@aopslabs/aops 0.2.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.
Files changed (171) hide show
  1. package/LICENSE +19 -0
  2. package/NOTICE +7 -0
  3. package/README.md +203 -0
  4. package/assets/agent-assets/core/SKILL.md +49 -0
  5. package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
  6. package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
  7. package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
  8. package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
  9. package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
  10. package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
  11. package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
  12. package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
  13. package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
  14. package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
  15. package/assets/agent-assets/core/references/view/SKILL.md +58 -0
  16. package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
  17. package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
  18. package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
  19. package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
  20. package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
  21. package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
  22. package/assets/agent-assets/core/user-guides/docman.md +162 -0
  23. package/assets/agent-assets/core/user-guides/projectman.md +212 -0
  24. package/assets/agent-assets/core/user-guides/sys.md +94 -0
  25. package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
  26. package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
  27. package/assets/r6-remote-session-baseline.json +19 -0
  28. package/assets/skills/aops-install/SKILL.md +231 -0
  29. package/assets/skills/aops-install/agents/openai.yaml +4 -0
  30. package/dist/commands/activity.js +352 -0
  31. package/dist/commands/agent-profile.js +653 -0
  32. package/dist/commands/agent.js +710 -0
  33. package/dist/commands/agents-md.js +125 -0
  34. package/dist/commands/api.js +84 -0
  35. package/dist/commands/archive.js +873 -0
  36. package/dist/commands/artifact.js +535 -0
  37. package/dist/commands/assets.js +1414 -0
  38. package/dist/commands/auth/device-login.js +1 -0
  39. package/dist/commands/auth/legacy-export.js +177 -0
  40. package/dist/commands/auth/login.js +426 -0
  41. package/dist/commands/chat.js +1363 -0
  42. package/dist/commands/chatv3.js +1684 -0
  43. package/dist/commands/checkpoint.js +549 -0
  44. package/dist/commands/commercial-artifact.js +475 -0
  45. package/dist/commands/commercial-license.js +594 -0
  46. package/dist/commands/community-auth.js +62 -0
  47. package/dist/commands/community-cockpit.js +185 -0
  48. package/dist/commands/community-console.js +109 -0
  49. package/dist/commands/community-doctor.js +335 -0
  50. package/dist/commands/community-server.js +2062 -0
  51. package/dist/commands/community-setup.js +284 -0
  52. package/dist/commands/discuss.js +1448 -0
  53. package/dist/commands/doc.js +5219 -0
  54. package/dist/commands/experience.js +922 -0
  55. package/dist/commands/host.js +1127 -0
  56. package/dist/commands/init.js +118 -0
  57. package/dist/commands/memory.js +3129 -0
  58. package/dist/commands/mission.js +562 -0
  59. package/dist/commands/official-catalog.js +134 -0
  60. package/dist/commands/plan.js +84 -0
  61. package/dist/commands/playbook.js +415 -0
  62. package/dist/commands/pm/context.js +6 -0
  63. package/dist/commands/pm/index.js +701 -0
  64. package/dist/commands/pm/projectman.js +6266 -0
  65. package/dist/commands/project.js +1080 -0
  66. package/dist/commands/prompt.js +1024 -0
  67. package/dist/commands/repo-sync.js +1907 -0
  68. package/dist/commands/resource.js +527 -0
  69. package/dist/commands/skill.js +1211 -0
  70. package/dist/commands/start-disciplines.js +510 -0
  71. package/dist/commands/start.js +1343 -0
  72. package/dist/commands/target.js +309 -0
  73. package/dist/commands/version.js +42 -0
  74. package/dist/commands/view.js +1852 -0
  75. package/dist/launchers/cockpit.js +28 -0
  76. package/dist/launchers/options.js +74 -0
  77. package/dist/launchers/server.js +41 -0
  78. package/dist/lib/agent-assets/envelope.js +83 -0
  79. package/dist/lib/agent-assets/gateway.js +15 -0
  80. package/dist/lib/agent-assets/guards.js +23 -0
  81. package/dist/lib/agent-assets/hosted-discovery.js +148 -0
  82. package/dist/lib/agent-assets/hosted-package-input.js +154 -0
  83. package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
  84. package/dist/lib/agent-assets/native-fs.js +589 -0
  85. package/dist/lib/agent-assets/package-manifest.js +324 -0
  86. package/dist/lib/agent-assets/portable-path.js +67 -0
  87. package/dist/lib/agent-assets/release-input.js +177 -0
  88. package/dist/lib/agent-assets/roots.js +45 -0
  89. package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
  90. package/dist/lib/agent-assets/runtime-targets.js +50 -0
  91. package/dist/lib/agent-assets/store-reader.js +1212 -0
  92. package/dist/lib/agent-assets/store-types.js +1 -0
  93. package/dist/lib/agent-assets/store-writer.js +1484 -0
  94. package/dist/lib/agent-assets/types.js +1 -0
  95. package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
  96. package/dist/lib/commercial-final-readiness.js +504 -0
  97. package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
  98. package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
  99. package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
  100. package/dist/lib/community-client-contract.js +164 -0
  101. package/dist/lib/community-cockpit-lifecycle.js +601 -0
  102. package/dist/lib/community-command-abort.js +59 -0
  103. package/dist/lib/community-diagnostic.js +237 -0
  104. package/dist/lib/community-docker-adapter.js +498 -0
  105. package/dist/lib/community-home.js +31 -0
  106. package/dist/lib/community-instance-contract.js +148 -0
  107. package/dist/lib/community-instance-layout.js +97 -0
  108. package/dist/lib/community-lifecycle.js +1631 -0
  109. package/dist/lib/community-migration-snapshot.js +770 -0
  110. package/dist/lib/community-native-application-recovery.js +425 -0
  111. package/dist/lib/community-native-child.js +240 -0
  112. package/dist/lib/community-native-database-recovery.js +456 -0
  113. package/dist/lib/community-native-lifecycle.js +2458 -0
  114. package/dist/lib/community-native-migration.js +339 -0
  115. package/dist/lib/community-native-postgres.js +596 -0
  116. package/dist/lib/community-operation-journal.js +773 -0
  117. package/dist/lib/community-operation-lock.js +596 -0
  118. package/dist/lib/community-release-resolver.js +291 -0
  119. package/dist/lib/community-release-verifier.js +475 -0
  120. package/dist/lib/community-repo-discovery.js +252 -0
  121. package/dist/lib/community-setup-server-env.js +203 -0
  122. package/dist/lib/external-url.js +110 -0
  123. package/dist/lib/official-catalog-gateway.js +108 -0
  124. package/dist/lib/official-catalog.js +364 -0
  125. package/dist/lib/server-bootstrap.js +240 -0
  126. package/dist/lib/setup-agent-assets-bridge.js +176 -0
  127. package/dist/lib/setup-agent-assets-release.js +52 -0
  128. package/dist/lib/setup-external-postgres.js +108 -0
  129. package/dist/lib/setup-init-orchestrator.js +990 -0
  130. package/dist/lib/setup-install-guide.js +52 -0
  131. package/dist/lib/setup-installer-release.js +47 -0
  132. package/dist/lib/setup-local-postgres.js +329 -0
  133. package/dist/lib/setup-official-catalog-bridge.js +148 -0
  134. package/dist/lib/setup-readiness.js +888 -0
  135. package/dist/lib/tui-launcher.js +59 -0
  136. package/dist/main.js +150 -0
  137. package/dist/seeds/agents-md.js +54 -0
  138. package/dist/utils/agent-gateway.js +275 -0
  139. package/dist/utils/agents-md.js +281 -0
  140. package/dist/utils/api.js +255 -0
  141. package/dist/utils/chatv3-session-store.js +185 -0
  142. package/dist/utils/command.js +67 -0
  143. package/dist/utils/community-host-runtime.js +376 -0
  144. package/dist/utils/config.js +1015 -0
  145. package/dist/utils/discussion-workspace.js +552 -0
  146. package/dist/utils/experience-workspace.js +326 -0
  147. package/dist/utils/guide-paths.js +67 -0
  148. package/dist/utils/hosted-sugar.js +56 -0
  149. package/dist/utils/hosted-workspace.js +229 -0
  150. package/dist/utils/memory-workspace.js +537 -0
  151. package/dist/utils/operator-runtime.js +199 -0
  152. package/dist/utils/password-crypto.js +39 -0
  153. package/dist/utils/playbook-workspace.js +479 -0
  154. package/dist/utils/project-context.js +255 -0
  155. package/dist/utils/prompts.js +70 -0
  156. package/dist/utils/refresh-race.js +28 -0
  157. package/dist/utils/repo-config.js +109 -0
  158. package/dist/utils/repo-first-projectman.js +194 -0
  159. package/dist/utils/repo-first-storage.js +13 -0
  160. package/dist/utils/repo-first-view.js +305 -0
  161. package/dist/utils/session-guidance.js +278 -0
  162. package/dist/utils/session-state.js +137 -0
  163. package/dist/utils/table.js +39 -0
  164. package/dist/utils/transient-fs.js +49 -0
  165. package/dist/utils/workspace-root.js +1 -0
  166. package/launchers/aops-cockpit.ps1 +15 -0
  167. package/launchers/aops-cockpit.sh +16 -0
  168. package/launchers/aops-server.ps1 +15 -0
  169. package/launchers/aops-server.sh +16 -0
  170. package/native/tui/win32-x64/aops-tui.exe +0 -0
  171. package/package.json +81 -0
@@ -0,0 +1,1127 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawn } from 'node:child_process';
5
+ import { Command } from 'commander';
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';
8
+ import { getAopsServerEnvPath, inferAopsRepoDialect, readAopsServerEnvConfig, redactAopsRepoUrl, writeAopsServerEnvConfig, } from '@aopslabs/aops-runtime-config';
9
+ import { createCliApiClientFromOptions, fetchCliHealth } from '../utils/api.js';
10
+ import { applyCommonOptions, compactPayload } from '../utils/command.js';
11
+ import { ensureDestructiveWrite } from '../utils/hosted-sugar.js';
12
+ import { parseJsonInput, requireApiState } from '../utils/agent-gateway.js';
13
+ const HOST_LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'];
14
+ const HOST_DATABASE_RESET_CONFIRM_TEXT = 'I understand all data will be lost';
15
+ function normalizeNonEmpty(value) {
16
+ if (typeof value !== 'string')
17
+ return undefined;
18
+ const trimmed = value.trim();
19
+ return trimmed.length > 0 ? trimmed : undefined;
20
+ }
21
+ function expandHomePath(input) {
22
+ const trimmed = input.trim();
23
+ if (!trimmed)
24
+ return trimmed;
25
+ if (trimmed === '~')
26
+ return os.homedir();
27
+ if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
28
+ return path.join(os.homedir(), trimmed.slice(2));
29
+ }
30
+ return trimmed;
31
+ }
32
+ function resolveHostEnvPath(options) {
33
+ const envPath = normalizeNonEmpty(options.envPath);
34
+ if (!envPath)
35
+ return getAopsServerEnvPath(process.env);
36
+ return path.resolve(process.cwd(), expandHomePath(envPath));
37
+ }
38
+ function normalizeHostLogLevel(value) {
39
+ const normalized = normalizeNonEmpty(value)?.toLowerCase();
40
+ if (!normalized)
41
+ return undefined;
42
+ if (HOST_LOG_LEVELS.includes(normalized)) {
43
+ return normalized;
44
+ }
45
+ return undefined;
46
+ }
47
+ function resolveRepoUrlEnvName(value) {
48
+ const normalized = normalizeNonEmpty(value);
49
+ if (!normalized)
50
+ return undefined;
51
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) {
52
+ throw new Error('Invalid --repo-url-env name. Use a valid environment variable name.');
53
+ }
54
+ return normalized;
55
+ }
56
+ function printHostConfigResult(payload, options) {
57
+ if (options.json) {
58
+ console.log(JSON.stringify(payload, null, 2));
59
+ return;
60
+ }
61
+ console.log(JSON.stringify(payload, null, 2));
62
+ }
63
+ function printHostDatabaseResult(payload, options) {
64
+ if (options.json) {
65
+ console.log(JSON.stringify(payload, null, 2));
66
+ return;
67
+ }
68
+ console.log(JSON.stringify(payload, null, 2));
69
+ }
70
+ function collectRepeatedOption(value, previous = []) {
71
+ const normalized = normalizeNonEmpty(value);
72
+ return normalized ? [...previous, normalized] : previous;
73
+ }
74
+ function toNonEmptyStringArray(value) {
75
+ if (!Array.isArray(value))
76
+ return undefined;
77
+ const normalized = value
78
+ .map((entry) => normalizeNonEmpty(entry))
79
+ .filter((entry) => Boolean(entry));
80
+ return normalized.length > 0 ? normalized : undefined;
81
+ }
82
+ function normalizeHostDatabaseResetMode(value) {
83
+ const normalized = normalizeNonEmpty(value)?.toLowerCase();
84
+ if (!normalized)
85
+ return undefined;
86
+ if (normalized === 'drop-only')
87
+ return 'drop-only';
88
+ if (normalized === 'drop-and-recreate')
89
+ return 'drop-and-recreate';
90
+ return undefined;
91
+ }
92
+ function buildHostDatabaseResetInput(options) {
93
+ const mode = normalizeHostDatabaseResetMode(options.mode);
94
+ if (options.mode && !mode) {
95
+ throw new Error('Invalid --mode. Use one of: drop-only, drop-and-recreate');
96
+ }
97
+ return compactPayload({
98
+ confirmText: normalizeNonEmpty(options.confirmText) ??
99
+ HOST_DATABASE_RESET_CONFIRM_TEXT,
100
+ mode,
101
+ includeAuthTables: options.includeAuthTables === true,
102
+ });
103
+ }
104
+ function normalizeHostDatabaseBackupRestoreMode(value) {
105
+ const normalized = normalizeNonEmpty(value)?.toLowerCase();
106
+ if (!normalized)
107
+ return undefined;
108
+ if (normalized === 'truncate-and-insert')
109
+ return 'truncate-and-insert';
110
+ if (normalized === 'insert-only')
111
+ return 'insert-only';
112
+ return undefined;
113
+ }
114
+ function resolveHostOutputPath(value) {
115
+ const normalized = normalizeNonEmpty(value);
116
+ if (!normalized)
117
+ return undefined;
118
+ return path.resolve(process.cwd(), normalized);
119
+ }
120
+ function buildHostDatabaseBackupExportInput(options) {
121
+ return compactPayload({
122
+ includeAuthTables: options.includeAuthTables === true,
123
+ tables: toNonEmptyStringArray(options.table),
124
+ });
125
+ }
126
+ function resolveBackupPayloadFromInput(options) {
127
+ const inputPath = normalizeNonEmpty(options.input);
128
+ const inputJson = normalizeNonEmpty(options.inputJson);
129
+ if (inputPath && inputJson) {
130
+ throw new Error('Provide only one of --input or --input-json.');
131
+ }
132
+ if (inputPath) {
133
+ return JSON.parse(fs.readFileSync(path.resolve(process.cwd(), inputPath), 'utf8'));
134
+ }
135
+ if (inputJson) {
136
+ return parseJsonInput(inputJson, '--input-json');
137
+ }
138
+ throw new Error('Missing backup input. Use --input or --input-json.');
139
+ }
140
+ function buildHostDatabaseBackupRestoreInput(options) {
141
+ const mode = normalizeHostDatabaseBackupRestoreMode(options.mode);
142
+ if (options.mode && !mode) {
143
+ throw new Error('Invalid --mode. Use one of: truncate-and-insert, insert-only');
144
+ }
145
+ return compactPayload({
146
+ confirmText: normalizeNonEmpty(options.confirmText) ??
147
+ HOST_DATABASE_RESET_CONFIRM_TEXT,
148
+ mode,
149
+ backup: resolveBackupPayloadFromInput(options),
150
+ });
151
+ }
152
+ export const nativePgCommandRuntime = {
153
+ async runProcess(tool, args) {
154
+ return await new Promise((resolve, reject) => {
155
+ const child = spawn(tool, args, {
156
+ stdio: 'inherit',
157
+ shell: false,
158
+ });
159
+ child.on('error', (error) => reject(error));
160
+ child.on('close', (exitCode, signal) => {
161
+ resolve({
162
+ exitCode,
163
+ signal,
164
+ });
165
+ });
166
+ });
167
+ },
168
+ };
169
+ function resolvePgBinDir(value) {
170
+ const normalized = normalizeNonEmpty(value);
171
+ if (!normalized)
172
+ return undefined;
173
+ return path.resolve(process.cwd(), expandHomePath(normalized));
174
+ }
175
+ function resolveNativePgToolCommand(tool, pgBinDir) {
176
+ if (!pgBinDir)
177
+ return tool;
178
+ const executable = process.platform === 'win32' ? `${tool}.exe` : tool;
179
+ return path.join(pgBinDir, executable);
180
+ }
181
+ function redactNativePgArgs(args, redactedRepoUrl) {
182
+ return args.map((arg) => {
183
+ if (arg.startsWith('--dbname='))
184
+ return `--dbname=${redactedRepoUrl}`;
185
+ return arg;
186
+ });
187
+ }
188
+ function resolveHostPgRepoTarget(options) {
189
+ const explicitRepoUrl = normalizeNonEmpty(options.repoUrl);
190
+ if (explicitRepoUrl) {
191
+ if (inferAopsRepoDialect(explicitRepoUrl) !== 'pg') {
192
+ throw new Error('PG-native dump commands require a PostgreSQL repo URL.');
193
+ }
194
+ return {
195
+ repoUrl: explicitRepoUrl,
196
+ redactedRepoUrl: redactAopsRepoUrl(explicitRepoUrl),
197
+ repoUrlSource: 'option',
198
+ envPath: null,
199
+ };
200
+ }
201
+ const envPath = normalizeNonEmpty(options.envPath)
202
+ ? path.resolve(process.cwd(), expandHomePath(String(options.envPath)))
203
+ : undefined;
204
+ const hostEnv = readAopsServerEnvConfig(process.env, envPath);
205
+ if (!hostEnv.repoUrl) {
206
+ throw new Error('Missing PostgreSQL repo URL. Configure the selected host env with `aops-cli setup server-env` first.');
207
+ }
208
+ if (hostEnv.repoDialect !== 'pg') {
209
+ throw new Error('PG-native dump commands require the configured host repo URL to be PostgreSQL.');
210
+ }
211
+ return {
212
+ repoUrl: hostEnv.repoUrl,
213
+ redactedRepoUrl: hostEnv.redactedRepoUrl ?? redactAopsRepoUrl(hostEnv.repoUrl),
214
+ repoUrlSource: 'host-env',
215
+ envPath: hostEnv.path,
216
+ };
217
+ }
218
+ function assertMutuallyExclusiveDataShape(options) {
219
+ if (options.schemaOnly === true && options.dataOnly === true) {
220
+ throw new Error('Use only one of --schema-only or --data-only.');
221
+ }
222
+ }
223
+ function buildHostDatabaseDumpExportInvocation(options) {
224
+ assertMutuallyExclusiveDataShape(options);
225
+ const outputPath = resolveHostOutputPath(options.output);
226
+ if (!outputPath) {
227
+ throw new Error('Missing --output. PG-native dump export writes a .dump file to disk.');
228
+ }
229
+ const pgBinDir = resolvePgBinDir(options.pgBinDir);
230
+ const repoTarget = resolveHostPgRepoTarget(options);
231
+ const args = [
232
+ `--dbname=${repoTarget.repoUrl}`,
233
+ '--format=custom',
234
+ '--file',
235
+ outputPath,
236
+ '--no-owner',
237
+ '--no-privileges',
238
+ ];
239
+ if (options.schemaOnly === true)
240
+ args.push('--schema-only');
241
+ if (options.dataOnly === true)
242
+ args.push('--data-only');
243
+ for (const tableName of toNonEmptyStringArray(options.table) ?? []) {
244
+ args.push('--table', tableName);
245
+ }
246
+ return {
247
+ ...repoTarget,
248
+ tool: resolveNativePgToolCommand('pg_dump', pgBinDir),
249
+ args,
250
+ redactedArgs: redactNativePgArgs(args, repoTarget.redactedRepoUrl),
251
+ outputPath,
252
+ };
253
+ }
254
+ function buildHostDatabaseDumpRestoreInvocation(options) {
255
+ assertMutuallyExclusiveDataShape(options);
256
+ const inputPath = resolveHostOutputPath(options.input);
257
+ if (!inputPath) {
258
+ throw new Error('Missing --input. PG-native dump restore requires a dump file path.');
259
+ }
260
+ if (!fs.existsSync(inputPath)) {
261
+ throw new Error(`Dump input not found: ${inputPath}`);
262
+ }
263
+ const pgBinDir = resolvePgBinDir(options.pgBinDir);
264
+ const repoTarget = resolveHostPgRepoTarget(options);
265
+ const args = [
266
+ `--dbname=${repoTarget.repoUrl}`,
267
+ '--no-owner',
268
+ '--no-privileges',
269
+ ];
270
+ if (options.clean === true) {
271
+ args.push('--clean', '--if-exists');
272
+ }
273
+ if (options.schemaOnly === true)
274
+ args.push('--schema-only');
275
+ if (options.dataOnly === true)
276
+ args.push('--data-only');
277
+ for (const tableName of toNonEmptyStringArray(options.table) ?? []) {
278
+ args.push('--table', tableName);
279
+ }
280
+ args.push(inputPath);
281
+ return {
282
+ ...repoTarget,
283
+ tool: resolveNativePgToolCommand('pg_restore', pgBinDir),
284
+ args,
285
+ redactedArgs: redactNativePgArgs(args, repoTarget.redactedRepoUrl),
286
+ inputPath,
287
+ };
288
+ }
289
+ function formatNativePgRunnerError(error, tool) {
290
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
291
+ return `Required PostgreSQL client tool not found: ${tool}. Install PostgreSQL client tools or pass --pg-bin-dir.`;
292
+ }
293
+ return error instanceof Error ? error.message : String(error);
294
+ }
295
+ function resolveWarmupTimeoutMs(options, shouldWarmup, shouldReset) {
296
+ if (typeof options.timeoutMs === 'number' && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
297
+ return options.timeoutMs;
298
+ }
299
+ if (shouldWarmup || shouldReset)
300
+ return 120_000;
301
+ return options.timeoutMs;
302
+ }
303
+ function buildHostDiagnosticsPath(options) {
304
+ const query = new URLSearchParams();
305
+ if (options.reset === true)
306
+ query.set('reset', '1');
307
+ if (options.warmup === true)
308
+ query.set('warmup', '1');
309
+ const search = query.toString();
310
+ return search ? `/api/host-admin/plugins?${search}` : '/api/host-admin/plugins';
311
+ }
312
+ function applyHostRegistrationOptions(cmd) {
313
+ return cmd.option('--registrations-dir <path>', 'Host registrations directory (default: AOPS_HOST_REGISTRATIONS_DIR or ~/.aops/host-registrations)');
314
+ }
315
+ function resolveHostRegistrationsDir(options) {
316
+ return getHostRegistrationsDir({
317
+ registrationsDir: options.registrationsDir,
318
+ processEnv: process.env,
319
+ });
320
+ }
321
+ function printHostRegistrationResult(payload, options) {
322
+ if (options.json) {
323
+ console.log(JSON.stringify(payload, null, 2));
324
+ return;
325
+ }
326
+ console.log(JSON.stringify(payload, null, 2));
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
+ 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
+ }
393
+ }
394
+ export async function runHostRegistrations(options = {}) {
395
+ const interactive = !options.yes && !options.json;
396
+ const registrationsDir = resolveHostRegistrationsDir(options);
397
+ if (interactive) {
398
+ banner('AOPS Host Registrations');
399
+ logInfo(`Registry: ${registrationsDir}`);
400
+ }
401
+ try {
402
+ const registrations = listInstalledHostRegistrations({
403
+ registrationsDir,
404
+ processEnv: process.env,
405
+ });
406
+ if (!options.json)
407
+ logSuccess(`Loaded ${registrations.length} registration(s).`);
408
+ printHostRegistrationResult({
409
+ ok: true,
410
+ registrationsDir,
411
+ count: registrations.length,
412
+ registrations: registrations.map((entry) => ({
413
+ domain: entry.domain,
414
+ filePath: entry.filePath,
415
+ displayName: entry.manifest.displayName ?? null,
416
+ packageName: entry.manifest.packageName ?? null,
417
+ description: entry.manifest.description ?? null,
418
+ manifestProviderCount: entry.manifest.manifestProviders?.length ?? 0,
419
+ pluginCount: entry.manifest.plugins?.length ?? 0,
420
+ sourceCount: entry.manifest.sources?.length ?? 0,
421
+ })),
422
+ }, options);
423
+ }
424
+ catch (error) {
425
+ const message = error instanceof Error ? error.message : String(error);
426
+ logError(`Failed to load registrations: ${message}`);
427
+ process.exitCode = 1;
428
+ }
429
+ }
430
+ export async function runHostUnregister(options = {}) {
431
+ const interactive = !options.yes && !options.json;
432
+ const domain = options.domain?.trim().toLowerCase();
433
+ if (!domain) {
434
+ logError('Missing --domain.');
435
+ process.exitCode = 1;
436
+ return;
437
+ }
438
+ const registrationsDir = resolveHostRegistrationsDir(options);
439
+ if (interactive) {
440
+ banner('AOPS Host Unregister');
441
+ logInfo(`Registry: ${registrationsDir}`);
442
+ logInfo(`Domain: ${domain}`);
443
+ }
444
+ const removed = unregisterHostRegistration(domain, {
445
+ registrationsDir,
446
+ processEnv: process.env,
447
+ });
448
+ if (!removed) {
449
+ logError(`Registration not found: ${domain}`);
450
+ process.exitCode = 1;
451
+ return;
452
+ }
453
+ if (!options.json)
454
+ logSuccess(`Unregistered ${domain}.`);
455
+ printHostRegistrationResult({
456
+ ok: true,
457
+ domain,
458
+ registrationsDir,
459
+ removed: true,
460
+ }, options);
461
+ }
462
+ export async function runHostExplainRegistration(options = {}) {
463
+ const interactive = !options.yes && !options.json;
464
+ const domain = options.domain?.trim().toLowerCase();
465
+ if (!domain) {
466
+ logError('Missing --domain.');
467
+ process.exitCode = 1;
468
+ return;
469
+ }
470
+ const registrationsDir = resolveHostRegistrationsDir(options);
471
+ const registrations = listInstalledHostRegistrations({
472
+ registrationsDir,
473
+ processEnv: process.env,
474
+ });
475
+ const entry = registrations.find((item) => item.domain === domain);
476
+ if (!entry) {
477
+ logError(`Registration not found: ${domain}`);
478
+ process.exitCode = 1;
479
+ return;
480
+ }
481
+ if (interactive) {
482
+ banner('AOPS Host Registration');
483
+ logInfo(`Registry: ${registrationsDir}`);
484
+ logInfo(`Domain: ${domain}`);
485
+ }
486
+ const projected = mergeHostRegistrationsIntoConfig({}, [entry.manifest]);
487
+ if (!options.json)
488
+ logSuccess(`Explained ${domain} registration.`);
489
+ printHostRegistrationResult({
490
+ ok: true,
491
+ registrationsDir,
492
+ filePath: entry.filePath,
493
+ manifest: entry.manifest,
494
+ projectedConfig: projected,
495
+ }, options);
496
+ }
497
+ export async function runHostHealth(options = {}) {
498
+ const interactive = !options.yes && !options.json;
499
+ const apiState = await createCliApiClientFromOptions(options);
500
+ if (interactive) {
501
+ banner('AOPS Host Health');
502
+ logInfo(`API: ${apiState.baseUrl}`);
503
+ }
504
+ try {
505
+ const payload = await fetchCliHealth(apiState, { timeoutMs: options.timeoutMs });
506
+ if (!options.json)
507
+ logSuccess('Host is healthy.');
508
+ console.log(JSON.stringify(payload, null, 2));
509
+ }
510
+ catch (error) {
511
+ const message = error instanceof Error ? error.message : String(error);
512
+ logError(`Health check failed: ${message}`);
513
+ process.exitCode = 1;
514
+ }
515
+ }
516
+ export async function runHostConfigShow(options = {}) {
517
+ const interactive = !options.yes && !options.json;
518
+ const envPath = resolveHostEnvPath(options);
519
+ const snapshot = readAopsServerEnvConfig(process.env, envPath);
520
+ if (interactive) {
521
+ banner('AOPS Host Config');
522
+ logInfo(`Host env: ${snapshot.path}`);
523
+ }
524
+ if (!options.json) {
525
+ logSuccess(snapshot.exists ? 'Loaded local host env config.' : 'Local host env config is not created yet.');
526
+ }
527
+ printHostConfigResult({
528
+ ok: true,
529
+ action: 'host.config.show',
530
+ envPath: snapshot.path,
531
+ exists: snapshot.exists,
532
+ repoUrlPresent: Boolean(snapshot.repoUrl),
533
+ repoDialect: snapshot.repoDialect,
534
+ redactedRepoUrl: snapshot.redactedRepoUrl,
535
+ logLevel: snapshot.hostSettings.logLevel,
536
+ next: {
537
+ setRepo: 'aops-cli setup server-env',
538
+ setLogLevel: 'aops-cli host config set --log-level <level>',
539
+ },
540
+ }, options);
541
+ }
542
+ export async function runHostConfigSet(options = {}) {
543
+ const interactive = !options.yes && !options.json;
544
+ let repoUrlEnv;
545
+ try {
546
+ repoUrlEnv = resolveRepoUrlEnvName(options.repoUrlEnv);
547
+ }
548
+ catch (error) {
549
+ logError(error instanceof Error ? error.message : String(error));
550
+ process.exitCode = 1;
551
+ return;
552
+ }
553
+ if (normalizeNonEmpty(options.repoUrl) && repoUrlEnv) {
554
+ logError('Provide only one internal repo URL source.');
555
+ process.exitCode = 1;
556
+ return;
557
+ }
558
+ const repoUrl = normalizeNonEmpty(options.repoUrl) ??
559
+ (repoUrlEnv ? normalizeNonEmpty(process.env[repoUrlEnv]) : undefined);
560
+ const logLevel = normalizeHostLogLevel(options.logLevel);
561
+ if (repoUrlEnv && !repoUrl) {
562
+ logError(`Environment variable ${repoUrlEnv} is empty or missing.`);
563
+ process.exitCode = 1;
564
+ return;
565
+ }
566
+ if (!repoUrl && options.logLevel && !logLevel) {
567
+ logError(`Invalid --log-level. Use one of: ${HOST_LOG_LEVELS.join(', ')}`);
568
+ process.exitCode = 1;
569
+ return;
570
+ }
571
+ if (!repoUrl && !logLevel) {
572
+ logError('Nothing to update. Provide --repo-url-env and/or --log-level.');
573
+ process.exitCode = 1;
574
+ return;
575
+ }
576
+ const envPath = resolveHostEnvPath(options);
577
+ if (interactive) {
578
+ banner('AOPS Host Config Set');
579
+ logInfo(`Host env: ${envPath}`);
580
+ if (repoUrl)
581
+ logInfo(`Repo: ${redactAopsRepoUrl(repoUrl)}`);
582
+ if (logLevel)
583
+ logInfo(`Log level: ${logLevel}`);
584
+ }
585
+ const written = writeAopsServerEnvConfig({
586
+ ...(repoUrl ? { repoUrl } : {}),
587
+ ...(logLevel ? { logLevel } : {}),
588
+ }, process.env, envPath);
589
+ if (!options.json) {
590
+ logSuccess('Updated local host env config. Restart aops-server to apply changes.');
591
+ }
592
+ printHostConfigResult({
593
+ ok: true,
594
+ action: 'host.config.set',
595
+ envPath: written.path,
596
+ exists: written.exists,
597
+ repoUrlPresent: Boolean(written.repoUrl),
598
+ repoDialect: written.repoUrl ? inferAopsRepoDialect(written.repoUrl) : null,
599
+ redactedRepoUrl: written.redactedRepoUrl,
600
+ logLevel: written.hostSettings.logLevel,
601
+ restartRequired: true,
602
+ }, options);
603
+ }
604
+ export async function runHostDiagnostics(options = {}) {
605
+ const interactive = !options.yes && !options.json;
606
+ const apiState = await requireApiState(options);
607
+ if (!apiState)
608
+ return;
609
+ const shouldReset = options.reset === true;
610
+ const shouldWarmup = options.warmup === true;
611
+ const requestTimeoutMs = resolveWarmupTimeoutMs(options, shouldWarmup, shouldReset);
612
+ const path = buildHostDiagnosticsPath(options);
613
+ if (interactive) {
614
+ banner('AOPS Host Diagnostics');
615
+ logInfo(`API: ${apiState.baseUrl}`);
616
+ if (shouldReset)
617
+ logInfo('Reset: enabled');
618
+ if (shouldWarmup)
619
+ logInfo('Warmup: enabled');
620
+ if (requestTimeoutMs && (shouldWarmup || shouldReset))
621
+ logInfo(`Timeout: ${requestTimeoutMs}ms`);
622
+ }
623
+ try {
624
+ const payload = await apiState.client.fetchJson(path, {
625
+ method: 'GET',
626
+ timeoutMs: requestTimeoutMs,
627
+ });
628
+ if (!options.json)
629
+ logSuccess('Host diagnostics loaded.');
630
+ console.log(JSON.stringify(payload, null, 2));
631
+ }
632
+ catch (error) {
633
+ const message = error instanceof Error ? error.message : String(error);
634
+ logError(`Failed to load host diagnostics: ${message}`);
635
+ process.exitCode = 1;
636
+ }
637
+ }
638
+ export async function runHostDatabaseStatus(options = {}) {
639
+ const interactive = !options.yes && !options.json;
640
+ const apiState = await requireApiState(options);
641
+ if (!apiState)
642
+ return;
643
+ if (interactive) {
644
+ banner('AOPS Host Database Status');
645
+ logInfo(`API: ${apiState.baseUrl}`);
646
+ }
647
+ try {
648
+ const payload = await apiState.client.fetchJson('/api/aops/settings/database/status', {
649
+ method: 'GET',
650
+ timeoutMs: options.timeoutMs,
651
+ });
652
+ if (!options.json)
653
+ logSuccess('Host database status loaded.');
654
+ printHostDatabaseResult(payload, options);
655
+ }
656
+ catch (error) {
657
+ const message = error instanceof Error ? error.message : String(error);
658
+ logError(`Failed to load host database status: ${message}`);
659
+ process.exitCode = 1;
660
+ }
661
+ }
662
+ export async function runHostDatabaseReset(options = {}) {
663
+ const interactive = !options.yes && !options.json;
664
+ try {
665
+ ensureDestructiveWrite({
666
+ apply: options.apply,
667
+ confirm: options.confirm,
668
+ preview: options.preview,
669
+ }, 'This command resets hosted database tables.');
670
+ }
671
+ catch (error) {
672
+ logError(error instanceof Error ? error.message : String(error));
673
+ process.exitCode = 1;
674
+ return;
675
+ }
676
+ let input;
677
+ try {
678
+ input = buildHostDatabaseResetInput(options);
679
+ }
680
+ catch (error) {
681
+ logError(error instanceof Error ? error.message : String(error));
682
+ process.exitCode = 1;
683
+ return;
684
+ }
685
+ if (options.preview) {
686
+ const payload = {
687
+ ok: true,
688
+ action: 'host.database.reset.preview',
689
+ surface: '/api/aops/settings/database/reset',
690
+ destructive: true,
691
+ input,
692
+ next: {
693
+ apply: 'aops-cli host database reset --mode drop-and-recreate --apply --confirm',
694
+ },
695
+ };
696
+ if (!options.json)
697
+ logSuccess('Host database reset preview ready.');
698
+ printHostDatabaseResult(payload, options);
699
+ return;
700
+ }
701
+ const apiState = await requireApiState(options);
702
+ if (!apiState)
703
+ return;
704
+ if (interactive) {
705
+ banner('AOPS Host Database Reset');
706
+ logInfo(`API: ${apiState.baseUrl}`);
707
+ logInfo(`Mode: ${String(input.mode ?? 'drop-only')}`);
708
+ if (input.includeAuthTables === true) {
709
+ logInfo('Include auth tables: yes');
710
+ }
711
+ }
712
+ try {
713
+ const payload = await apiState.client.fetchJson('/api/aops/settings/database/reset', {
714
+ method: 'POST',
715
+ body: input,
716
+ timeoutMs: options.timeoutMs,
717
+ });
718
+ if (!options.json)
719
+ logSuccess('Host database reset completed.');
720
+ printHostDatabaseResult(payload, options);
721
+ }
722
+ catch (error) {
723
+ const message = error instanceof Error ? error.message : String(error);
724
+ logError(`Host database reset failed: ${message}`);
725
+ process.exitCode = 1;
726
+ }
727
+ }
728
+ export async function runHostDatabaseBackupExport(options = {}) {
729
+ const interactive = !options.yes && !options.json;
730
+ const apiState = await requireApiState(options);
731
+ if (!apiState)
732
+ return;
733
+ const input = buildHostDatabaseBackupExportInput(options);
734
+ const outputPath = resolveHostOutputPath(options.output);
735
+ if (interactive) {
736
+ banner('AOPS Host Database Backup Export');
737
+ logInfo(`API: ${apiState.baseUrl}`);
738
+ if (outputPath)
739
+ logInfo(`Output: ${outputPath}`);
740
+ }
741
+ try {
742
+ const payload = await apiState.client.fetchJson('/api/aops/settings/database/backup/export', {
743
+ method: 'POST',
744
+ body: input,
745
+ timeoutMs: options.timeoutMs,
746
+ });
747
+ const backup = payload?.data ?? payload;
748
+ if (outputPath) {
749
+ fs.writeFileSync(outputPath, `${JSON.stringify(backup, null, 2)}\n`, 'utf8');
750
+ const response = {
751
+ ok: true,
752
+ action: 'host.database.backup.export',
753
+ outputPath,
754
+ input,
755
+ };
756
+ if (!options.json)
757
+ logSuccess('Host database backup exported.');
758
+ printHostDatabaseResult(response, options);
759
+ return;
760
+ }
761
+ if (!options.json)
762
+ logSuccess('Host database backup exported.');
763
+ printHostDatabaseResult(backup, options);
764
+ }
765
+ catch (error) {
766
+ const message = error instanceof Error ? error.message : String(error);
767
+ logError(`Host database backup export failed: ${message}`);
768
+ process.exitCode = 1;
769
+ }
770
+ }
771
+ export async function runHostDatabaseBackupRestore(options = {}) {
772
+ const interactive = !options.yes && !options.json;
773
+ try {
774
+ ensureDestructiveWrite({
775
+ apply: options.apply,
776
+ confirm: options.confirm,
777
+ preview: options.preview,
778
+ }, 'This command restores hosted database state.');
779
+ }
780
+ catch (error) {
781
+ logError(error instanceof Error ? error.message : String(error));
782
+ process.exitCode = 1;
783
+ return;
784
+ }
785
+ let input;
786
+ try {
787
+ input = buildHostDatabaseBackupRestoreInput(options);
788
+ }
789
+ catch (error) {
790
+ logError(error instanceof Error ? error.message : String(error));
791
+ process.exitCode = 1;
792
+ return;
793
+ }
794
+ if (options.preview) {
795
+ const payload = {
796
+ ok: true,
797
+ action: 'host.database.backup.restore.preview',
798
+ surface: '/api/aops/settings/database/backup/restore',
799
+ destructive: true,
800
+ input,
801
+ next: {
802
+ apply: 'aops-cli host database backup restore --input <backup.json> --apply --confirm',
803
+ },
804
+ };
805
+ if (!options.json)
806
+ logSuccess('Host database backup restore preview ready.');
807
+ printHostDatabaseResult(payload, options);
808
+ return;
809
+ }
810
+ const apiState = await requireApiState(options);
811
+ if (!apiState)
812
+ return;
813
+ if (interactive) {
814
+ banner('AOPS Host Database Backup Restore');
815
+ logInfo(`API: ${apiState.baseUrl}`);
816
+ logInfo(`Mode: ${String(input.mode ?? 'truncate-and-insert')}`);
817
+ }
818
+ try {
819
+ const payload = await apiState.client.fetchJson('/api/aops/settings/database/backup/restore', {
820
+ method: 'POST',
821
+ body: input,
822
+ timeoutMs: options.timeoutMs,
823
+ });
824
+ if (!options.json)
825
+ logSuccess('Host database backup restore completed.');
826
+ printHostDatabaseResult(payload, options);
827
+ }
828
+ catch (error) {
829
+ const message = error instanceof Error ? error.message : String(error);
830
+ logError(`Host database backup restore failed: ${message}`);
831
+ process.exitCode = 1;
832
+ }
833
+ }
834
+ export async function runHostDatabaseDumpExport(options = {}) {
835
+ const interactive = !options.yes && !options.json;
836
+ let invocation;
837
+ try {
838
+ invocation = buildHostDatabaseDumpExportInvocation(options);
839
+ }
840
+ catch (error) {
841
+ logError(error instanceof Error ? error.message : String(error));
842
+ process.exitCode = 1;
843
+ return;
844
+ }
845
+ if (interactive) {
846
+ banner('AOPS Host Database Dump Export');
847
+ logInfo(`Repo: ${invocation.redactedRepoUrl}`);
848
+ logInfo(`Output: ${invocation.outputPath}`);
849
+ logInfo(`Tool: ${invocation.tool}`);
850
+ }
851
+ try {
852
+ const result = await nativePgCommandRuntime.runProcess(invocation.tool, invocation.args);
853
+ if (result.exitCode !== 0) {
854
+ throw new Error(`${path.basename(invocation.tool)} exited with code ${String(result.exitCode ?? 'null')}.`);
855
+ }
856
+ const payload = {
857
+ ok: true,
858
+ action: 'host.database.dump.export',
859
+ format: 'pg-custom',
860
+ tool: invocation.tool,
861
+ args: invocation.redactedArgs,
862
+ outputPath: invocation.outputPath,
863
+ repoUrl: invocation.redactedRepoUrl,
864
+ repoUrlSource: invocation.repoUrlSource,
865
+ envPath: invocation.envPath,
866
+ };
867
+ if (!options.json)
868
+ logSuccess('PG-native dump export completed.');
869
+ printHostDatabaseResult(payload, options);
870
+ }
871
+ catch (error) {
872
+ logError(`PG-native dump export failed: ${formatNativePgRunnerError(error, invocation.tool)}`);
873
+ process.exitCode = 1;
874
+ }
875
+ }
876
+ export async function runHostDatabaseDumpRestore(options = {}) {
877
+ const interactive = !options.yes && !options.json;
878
+ try {
879
+ ensureDestructiveWrite({
880
+ apply: options.apply,
881
+ confirm: options.confirm,
882
+ preview: options.preview,
883
+ }, 'This command restores database state using pg_restore.');
884
+ }
885
+ catch (error) {
886
+ logError(error instanceof Error ? error.message : String(error));
887
+ process.exitCode = 1;
888
+ return;
889
+ }
890
+ let invocation;
891
+ try {
892
+ invocation = buildHostDatabaseDumpRestoreInvocation(options);
893
+ }
894
+ catch (error) {
895
+ logError(error instanceof Error ? error.message : String(error));
896
+ process.exitCode = 1;
897
+ return;
898
+ }
899
+ if (options.preview) {
900
+ const payload = {
901
+ ok: true,
902
+ action: 'host.database.dump.restore.preview',
903
+ destructive: true,
904
+ tool: invocation.tool,
905
+ args: invocation.redactedArgs,
906
+ inputPath: invocation.inputPath,
907
+ repoUrl: invocation.redactedRepoUrl,
908
+ repoUrlSource: invocation.repoUrlSource,
909
+ envPath: invocation.envPath,
910
+ next: {
911
+ apply: 'aops-cli host database dump restore --input <backup.dump> --apply --confirm',
912
+ },
913
+ };
914
+ if (!options.json)
915
+ logSuccess('PG-native dump restore preview ready.');
916
+ printHostDatabaseResult(payload, options);
917
+ return;
918
+ }
919
+ if (interactive) {
920
+ banner('AOPS Host Database Dump Restore');
921
+ logInfo(`Repo: ${invocation.redactedRepoUrl}`);
922
+ logInfo(`Input: ${invocation.inputPath}`);
923
+ logInfo(`Tool: ${invocation.tool}`);
924
+ }
925
+ try {
926
+ const result = await nativePgCommandRuntime.runProcess(invocation.tool, invocation.args);
927
+ if (result.exitCode !== 0) {
928
+ throw new Error(`${path.basename(invocation.tool)} exited with code ${String(result.exitCode ?? 'null')}.`);
929
+ }
930
+ const payload = {
931
+ ok: true,
932
+ action: 'host.database.dump.restore',
933
+ destructive: true,
934
+ tool: invocation.tool,
935
+ args: invocation.redactedArgs,
936
+ inputPath: invocation.inputPath,
937
+ repoUrl: invocation.redactedRepoUrl,
938
+ repoUrlSource: invocation.repoUrlSource,
939
+ envPath: invocation.envPath,
940
+ };
941
+ if (!options.json)
942
+ logSuccess('PG-native dump restore completed.');
943
+ printHostDatabaseResult(payload, options);
944
+ }
945
+ catch (error) {
946
+ logError(`PG-native dump restore failed: ${formatNativePgRunnerError(error, invocation.tool)}`);
947
+ process.exitCode = 1;
948
+ }
949
+ }
950
+ export async function runHostHello(options = {}) {
951
+ const apiState = await createCliApiClientFromOptions(options);
952
+ try {
953
+ const payload = await apiState.client.fetchJson('/api/hello', {
954
+ method: 'GET',
955
+ auth: false,
956
+ retry401: false,
957
+ timeoutMs: options.timeoutMs,
958
+ });
959
+ console.log(JSON.stringify(payload, null, 2));
960
+ }
961
+ catch (error) {
962
+ const message = error instanceof Error ? error.message : String(error);
963
+ logError(`Hello endpoint failed: ${message}`);
964
+ process.exitCode = 1;
965
+ }
966
+ }
967
+ export function makeHostCommand() {
968
+ const cmd = new Command('host').description('Host inventory, registration, diagnostics, and admin commands');
969
+ const configCmd = cmd.command('config').description('Inspect or update the local host-owned env config');
970
+ const databaseCmd = cmd
971
+ .command('database')
972
+ .alias('db')
973
+ .description('Inspect or mutate the active host database via AOPS db-admin routes');
974
+ const backupCmd = databaseCmd
975
+ .command('backup')
976
+ .description('Export or restore hosted database backups');
977
+ const dumpCmd = databaseCmd
978
+ .command('dump')
979
+ .description('Run PG-native pg_dump / pg_restore against the configured PostgreSQL repo URL');
980
+ applyCommonOptions(configCmd
981
+ .command('show')
982
+ .description('Show repo target and host log level from the local host env file')
983
+ .option('--env-path <path>', 'Explicit host env file path (default: ~/.aops/aops.server.env)')
984
+ .action(async (options) => {
985
+ await runHostConfigShow(options);
986
+ }), { withAuth: false, withProject: false });
987
+ applyCommonOptions(configCmd
988
+ .command('set')
989
+ .description('Write repo target and/or host log level into the local host env file')
990
+ .option('--env-path <path>', 'Explicit host env file path (default: ~/.aops/aops.server.env)')
991
+ .option('--repo-url-env <name>', 'Read the shared host repository URL from this environment variable')
992
+ .option('--log-level <level>', `Host log level (${HOST_LOG_LEVELS.join(', ')})`)
993
+ .action(async (options) => {
994
+ await runHostConfigSet(options);
995
+ }), { withAuth: false, withProject: false });
996
+ applyHostRegistrationOptions(applyCommonOptions(cmd
997
+ .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')
1000
+ .action(async (options) => {
1001
+ await runHostRegister(options);
1002
+ }), { withAuth: false, withProject: false }));
1003
+ applyHostRegistrationOptions(applyCommonOptions(cmd.command('registrations').description('List installed host registrations in the operator registry').action(async (options) => {
1004
+ await runHostRegistrations(options);
1005
+ }), { withAuth: false, withProject: false }));
1006
+ applyHostRegistrationOptions(applyCommonOptions(cmd
1007
+ .command('unregister')
1008
+ .description('Remove an installed host registration')
1009
+ .requiredOption('--domain <domain>', 'Domain id')
1010
+ .action(async (options) => {
1011
+ await runHostUnregister(options);
1012
+ }), { withAuth: false, withProject: false }));
1013
+ applyHostRegistrationOptions(applyCommonOptions(cmd
1014
+ .command('explain-registration')
1015
+ .description('Explain the stored registration manifest and projected host-config fragment')
1016
+ .requiredOption('--domain <domain>', 'Domain id')
1017
+ .action(async (options) => {
1018
+ await runHostExplainRegistration(options);
1019
+ }), { withAuth: false, withProject: false }));
1020
+ applyCommonOptions(cmd.command('health').description('Smoke the runtime health endpoint (GET /api/health)').action(async (options) => {
1021
+ await runHostHealth(options);
1022
+ }), { withAuth: false, withProject: false });
1023
+ applyCommonOptions(cmd
1024
+ .command('diagnostics')
1025
+ .alias('plugins')
1026
+ .description('Inspect host runtime diagnostics (/api/host-admin/plugins)')
1027
+ .option('--reset', 'Reset plugin registry and agent gateway catalog snapshot before diagnostics')
1028
+ .option('--warmup', 'Warmup plugin registry before diagnostics')
1029
+ .action(async (options) => {
1030
+ await runHostDiagnostics(options);
1031
+ }), { withProject: false });
1032
+ applyCommonOptions(databaseCmd
1033
+ .command('status')
1034
+ .description('Load hosted database status (/api/aops/settings/database/status)')
1035
+ .action(async (options) => {
1036
+ await runHostDatabaseStatus(options);
1037
+ }), { withProject: false, withYes: false });
1038
+ applyCommonOptions(databaseCmd
1039
+ .command('reset')
1040
+ .description('Reset hosted database tables and optionally recreate schema')
1041
+ .option('--mode <mode>', 'Reset mode (drop-only or drop-and-recreate)')
1042
+ .option('--include-auth-tables', 'Also reset auth tables')
1043
+ .option('--preview', 'Show the reset payload without calling the server')
1044
+ .option('--apply', 'Execute the hosted database reset')
1045
+ .option('--confirm', 'Required with --apply because this command deletes hosted state')
1046
+ .option('--confirm-text <text>', 'Override the destructive confirmation text sent to the server')
1047
+ .action(async (options) => {
1048
+ await runHostDatabaseReset(options);
1049
+ }), { withProject: false, withYes: false });
1050
+ applyCommonOptions(backupCmd
1051
+ .command('export')
1052
+ .description('Export a hosted database backup through the AOPS backup route')
1053
+ .option('--include-auth-tables', 'Also export auth tables')
1054
+ .option('--table <name>', 'Limit export to a configured table', collectRepeatedOption, [])
1055
+ .option('--output <path>', 'Write backup JSON to a file instead of stdout')
1056
+ .action(async (options) => {
1057
+ await runHostDatabaseBackupExport(options);
1058
+ }), { withProject: false, withYes: false });
1059
+ applyCommonOptions(backupCmd
1060
+ .command('restore')
1061
+ .description('Restore a hosted database backup through the AOPS restore route')
1062
+ .option('--input <path>', 'Read backup JSON from a file')
1063
+ .option('--input-json <json>', 'Inline backup JSON or @file.json')
1064
+ .option('--mode <mode>', 'Restore mode (truncate-and-insert or insert-only)')
1065
+ .option('--preview', 'Show the restore payload without calling the server')
1066
+ .option('--apply', 'Execute the hosted backup restore')
1067
+ .option('--confirm', 'Required with --apply because this command mutates hosted state')
1068
+ .option('--confirm-text <text>', 'Override the destructive confirmation text sent to the server')
1069
+ .action(async (options) => {
1070
+ await runHostDatabaseBackupRestore(options);
1071
+ }), { withProject: false, withYes: false });
1072
+ applyCommonOptions(dumpCmd
1073
+ .command('export')
1074
+ .description('Write a native PostgreSQL custom dump file with pg_dump')
1075
+ .option('--env-path <path>', 'Path to host env file used to resolve repo URL')
1076
+ .option('--pg-bin-dir <path>', 'Directory containing pg_dump / pg_restore executables')
1077
+ .requiredOption('--output <path>', 'Write dump output to this file path')
1078
+ .option('--table <name>', 'Limit dump to a table', collectRepeatedOption, [])
1079
+ .option('--schema-only', 'Dump schema only')
1080
+ .option('--data-only', 'Dump data only')
1081
+ .action(async (options) => {
1082
+ await runHostDatabaseDumpExport(options);
1083
+ }), { withProject: false, withYes: false });
1084
+ applyCommonOptions(dumpCmd
1085
+ .command('restore')
1086
+ .description('Restore a native PostgreSQL custom dump file with pg_restore')
1087
+ .option('--env-path <path>', 'Path to host env file used to resolve repo URL')
1088
+ .option('--pg-bin-dir <path>', 'Directory containing pg_dump / pg_restore executables')
1089
+ .requiredOption('--input <path>', 'Path to a native PostgreSQL dump file')
1090
+ .option('--table <name>', 'Restore only a table from the dump', collectRepeatedOption, [])
1091
+ .option('--schema-only', 'Restore schema only')
1092
+ .option('--data-only', 'Restore data only')
1093
+ .option('--clean', 'Drop database objects before recreating them')
1094
+ .option('--preview', 'Show the pg_restore invocation without executing it')
1095
+ .option('--apply', 'Execute pg_restore')
1096
+ .option('--confirm', 'Required with --apply because this command mutates database state')
1097
+ .action(async (options) => {
1098
+ await runHostDatabaseDumpRestore(options);
1099
+ }), { withProject: false, withYes: false });
1100
+ applyCommonOptions(cmd.command('hello').description('Smoke the hello endpoint (GET /api/hello)').action(async (options) => {
1101
+ await runHostHello(options);
1102
+ }), { withAuth: false, withProject: false });
1103
+ cmd.addHelpText('after', `
1104
+ Examples:
1105
+ aops-cli host config show
1106
+ aops-cli setup server-env
1107
+ aops-cli host config set --log-level info
1108
+ aops-cli host config set --repo-url-env AOPS_REPO_URL
1109
+ aops-cli host database status
1110
+ aops-cli host database reset --mode drop-and-recreate --preview
1111
+ aops-cli host database reset --mode drop-and-recreate --include-auth-tables --apply --confirm
1112
+ aops-cli host database backup export --table prompts --table prompt-versions --output ./aops-backup.json
1113
+ aops-cli host database backup restore --input ./aops-backup.json --mode truncate-and-insert --apply --confirm
1114
+ aops-cli host database dump export --output ./aops.dump
1115
+ aops-cli host database dump restore --input ./aops.dump --preview
1116
+ aops-cli host database dump restore --input ./aops.dump --clean --apply --confirm
1117
+ aops-cli host register --from /path/to/host-registration.json
1118
+ aops-cli host registrations
1119
+ aops-cli host explain-registration --domain fileman
1120
+ aops-cli host unregister --domain fileman
1121
+ aops-cli host health
1122
+ aops-cli host diagnostics --warmup
1123
+ aops-cli host diagnostics --reset --warmup
1124
+ aops-cli host hello
1125
+ `);
1126
+ return cmd;
1127
+ }