@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,281 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { AOPS_AGENTS_MD_TEMPLATE_SEEDS, DISCUSS_AGENTS_MD_TEMPLATE_SLUG, DEFAULT_AGENTS_MD_TEMPLATE_SLUG, } from '../seeds/agents-md.js';
6
+ import { findWorkspaceRoot } from './workspace-root.js';
7
+ export const AGENTS_MD_MANAGED_BEGIN = '<!-- aops:agents-md-templates BEGIN version=1 managed-by=aops-cli -->';
8
+ export const AGENTS_MD_MANAGED_END = '<!-- aops:agents-md-templates END -->';
9
+ function normalizeBoolean(value) {
10
+ return value === true;
11
+ }
12
+ function uniqueSeeds(seeds) {
13
+ const seen = new Set();
14
+ return seeds.filter((seed) => {
15
+ if (seen.has(seed.slug))
16
+ return false;
17
+ seen.add(seed.slug);
18
+ return true;
19
+ });
20
+ }
21
+ export function selectAgentsMdTemplates(options = {}) {
22
+ const all = normalizeBoolean(options.all);
23
+ const noDefaultTask = normalizeBoolean(options.noDefaultTask);
24
+ const discuss = normalizeBoolean(options.discuss);
25
+ let selected = all ? [...AOPS_AGENTS_MD_TEMPLATE_SEEDS] : [];
26
+ if (!all && !noDefaultTask) {
27
+ const seed = AOPS_AGENTS_MD_TEMPLATE_SEEDS.find((entry) => entry.slug === DEFAULT_AGENTS_MD_TEMPLATE_SLUG);
28
+ if (seed)
29
+ selected.push(seed);
30
+ }
31
+ if (discuss) {
32
+ const seed = AOPS_AGENTS_MD_TEMPLATE_SEEDS.find((entry) => entry.slug === DISCUSS_AGENTS_MD_TEMPLATE_SLUG);
33
+ if (seed)
34
+ selected.push(seed);
35
+ }
36
+ if (noDefaultTask) {
37
+ selected = selected.filter((entry) => entry.slug !== DEFAULT_AGENTS_MD_TEMPLATE_SLUG);
38
+ }
39
+ selected = uniqueSeeds(selected);
40
+ if (selected.length === 0) {
41
+ throw new Error('No AGENTS.md prompt templates selected. Drop --no-default-task or add --discuss/--all.');
42
+ }
43
+ return selected;
44
+ }
45
+ export function resolveAgentsMdTarget(root) {
46
+ const startDir = process.cwd();
47
+ const rootDir = root ? path.resolve(startDir, root) : findWorkspaceRoot(startDir);
48
+ return {
49
+ rootDir,
50
+ filePath: path.join(rootDir, 'AGENTS.md'),
51
+ };
52
+ }
53
+ function findInitSectionEnd(content) {
54
+ const match = content.match(/^#\s*INIT\b.*$/im);
55
+ if (!match || match.index === undefined)
56
+ return undefined;
57
+ const afterInitHeader = match.index + match[0].length;
58
+ const rest = content.slice(afterInitHeader);
59
+ const nextTopLevelHeader = rest.match(/^\s*#(?!\s*INIT\b)/m);
60
+ if (!nextTopLevelHeader || nextTopLevelHeader.index === undefined)
61
+ return content.length;
62
+ return afterInitHeader + nextTopLevelHeader.index;
63
+ }
64
+ function managedBlockPattern() {
65
+ const begin = AGENTS_MD_MANAGED_BEGIN.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
+ const end = AGENTS_MD_MANAGED_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
67
+ return new RegExp(`\\n*${begin}[\\s\\S]*?${end}\\n*`, 'm');
68
+ }
69
+ export function removeAgentsMdManagedBlock(content) {
70
+ const pattern = managedBlockPattern();
71
+ if (!pattern.test(content))
72
+ return { content, changed: false };
73
+ const next = content.replace(pattern, '\n\n').replace(/\n{3,}/g, '\n\n').trimEnd();
74
+ return { content: `${next}\n`, changed: true };
75
+ }
76
+ export function mergeAgentsMdManagedBlock(content, block) {
77
+ const normalizedBlock = `${block.trimEnd()}\n`;
78
+ const pattern = managedBlockPattern();
79
+ let next;
80
+ if (pattern.test(content)) {
81
+ next = content.replace(pattern, `\n\n${normalizedBlock}\n`);
82
+ }
83
+ else if (content.trim().length === 0) {
84
+ next = `${normalizedBlock}\n`;
85
+ }
86
+ else {
87
+ const insertAt = findInitSectionEnd(content);
88
+ if (insertAt !== undefined) {
89
+ const prefix = content.slice(0, insertAt).trimEnd();
90
+ const suffix = content.slice(insertAt).trimStart();
91
+ next = `${prefix}\n\n${normalizedBlock}\n${suffix}`;
92
+ }
93
+ else {
94
+ next = `${normalizedBlock}\n${content.replace(/^\uFEFF/, '').trimStart()}`;
95
+ }
96
+ }
97
+ next = `${next.trimEnd()}\n`;
98
+ return { content: next, changed: next !== content };
99
+ }
100
+ function promptMirrorPath(rootDir, seed) {
101
+ return path.join(rootDir, ...seed.mirrorPath.split('/'));
102
+ }
103
+ async function readPromptBody(rootDir, seed) {
104
+ const filePath = promptMirrorPath(rootDir, seed);
105
+ try {
106
+ const raw = await fs.readFile(filePath, 'utf-8');
107
+ return raw.trim();
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ }
113
+ function markdownFenceFor(content) {
114
+ let fence = '~~~';
115
+ while (content.includes(fence)) {
116
+ fence += '~';
117
+ }
118
+ return fence;
119
+ }
120
+ async function renderEmbeddedPrompt(rootDir, seed) {
121
+ const body = (await readPromptBody(rootDir, seed)) ?? seed.snippet.map((line) => `- ${line}`).join('\n');
122
+ const fence = markdownFenceFor(body);
123
+ return [
124
+ `<details><summary>${seed.title} body</summary>`,
125
+ '',
126
+ `${fence}markdown`,
127
+ body,
128
+ fence,
129
+ '',
130
+ '</details>',
131
+ ];
132
+ }
133
+ export async function buildAgentsMdManagedBlock(rootDir, selectedTemplates, options = {}) {
134
+ const warnings = [];
135
+ const lines = [
136
+ AGENTS_MD_MANAGED_BEGIN,
137
+ '## AOPS Prompt Template Bootstrap',
138
+ '',
139
+ 'This managed block is generated by `aops-cli agents-md update`.',
140
+ 'Keep project-specific agent rules outside this block.',
141
+ '',
142
+ 'Refresh hosted prompt mirrors when needed:',
143
+ '',
144
+ '```bash',
145
+ 'aops-cli sync pull --apply --hosted-project-slug aops --json',
146
+ '```',
147
+ '',
148
+ 'Use `aops-cli agents-md preview --discuss` before adding the standalone discuss / decision-ritual guidance.',
149
+ 'Use `aops-cli agents-md update --discuss --apply` to inject it.',
150
+ '',
151
+ 'Selected templates:',
152
+ '',
153
+ ];
154
+ for (const seed of selectedTemplates) {
155
+ lines.push(`### ${seed.title}`);
156
+ lines.push('');
157
+ lines.push(`- Slug: \`${seed.slug}\``);
158
+ lines.push(`- Ref: \`${seed.promptRef}\``);
159
+ lines.push(`- Mirror: \`${seed.mirrorPath}\``);
160
+ lines.push(`- Purpose: ${seed.description}`);
161
+ lines.push('- Runtime reminder:');
162
+ seed.snippet.forEach((entry) => lines.push(` - ${entry}`));
163
+ lines.push('');
164
+ if (options.embedPrompts === true) {
165
+ const embedded = await renderEmbeddedPrompt(rootDir, seed);
166
+ lines.push(...embedded, '');
167
+ }
168
+ }
169
+ if (options.embedPrompts === true) {
170
+ warnings.push('Embedded prompt bodies can make AGENTS.md large; prefer mirrors unless offline bootstrap requires embedding.');
171
+ }
172
+ else {
173
+ lines.push('Prompt bodies stay in hosted mirrors by default. Use `--embed-prompts` only for explicit offline bootstrap needs.');
174
+ lines.push('');
175
+ }
176
+ lines.push(AGENTS_MD_MANAGED_END);
177
+ return {
178
+ block: `${lines.join('\n').trimEnd()}\n`,
179
+ warnings,
180
+ };
181
+ }
182
+ async function readAgentsFile(filePath) {
183
+ try {
184
+ return await fs.readFile(filePath, 'utf-8');
185
+ }
186
+ catch (error) {
187
+ const code = error.code;
188
+ if (code === 'ENOENT')
189
+ return '';
190
+ throw error;
191
+ }
192
+ }
193
+ function resolveCliInvocation() {
194
+ if (process.env.AOPS_CLI_BIN)
195
+ return { command: process.env.AOPS_CLI_BIN, argsPrefix: [] };
196
+ const entrypoint = process.argv[1];
197
+ if (entrypoint && entrypoint.endsWith('.js')) {
198
+ return { command: process.execPath, argsPrefix: [entrypoint] };
199
+ }
200
+ return { command: 'aops-cli', argsPrefix: [] };
201
+ }
202
+ async function runHostedSync(rootDir) {
203
+ const cli = resolveCliInvocation();
204
+ const args = ['sync', 'pull', '--apply', '--hosted-project-slug', 'aops', '--json'];
205
+ const spawnArgs = [...cli.argsPrefix, ...args];
206
+ return await new Promise((resolve, reject) => {
207
+ const child = spawn(cli.command, spawnArgs, {
208
+ cwd: rootDir,
209
+ shell: process.platform === 'win32',
210
+ windowsHide: true,
211
+ stdio: ['ignore', 'pipe', 'pipe'],
212
+ });
213
+ let stdout = '';
214
+ let stderr = '';
215
+ child.stdout.on('data', (chunk) => {
216
+ stdout += String(chunk);
217
+ });
218
+ child.stderr.on('data', (chunk) => {
219
+ stderr += String(chunk);
220
+ });
221
+ child.on('error', (error) => {
222
+ reject(error);
223
+ });
224
+ child.on('close', (code) => {
225
+ const command = [cli.command, ...spawnArgs].join(' ');
226
+ if (code === 0) {
227
+ resolve({ command, stdout: stdout.trim(), stderr: stderr.trim() });
228
+ return;
229
+ }
230
+ reject(new Error(`${command} failed with exit code ${code}: ${stderr.trim() || stdout.trim()}`));
231
+ });
232
+ });
233
+ }
234
+ export async function updateAgentsMdFile(options = {}) {
235
+ if (options.apply !== true && options.preview !== true) {
236
+ throw new Error('This command updates AGENTS.md. Retry with --apply or use --preview.');
237
+ }
238
+ if (options.preview === true && options.syncHosted === true) {
239
+ throw new Error('--sync-hosted mutates .aops/hosted mirrors. Use `aops-cli agents-md update --sync-hosted --apply`.');
240
+ }
241
+ const target = resolveAgentsMdTarget(options.root);
242
+ const selectedTemplates = selectAgentsMdTemplates(options);
243
+ const syncHosted = options.syncHosted === true ? await runHostedSync(target.rootDir) : undefined;
244
+ const { block, warnings } = await buildAgentsMdManagedBlock(target.rootDir, selectedTemplates, {
245
+ embedPrompts: options.embedPrompts,
246
+ });
247
+ const current = await readAgentsFile(target.filePath);
248
+ const merged = mergeAgentsMdManagedBlock(current, block);
249
+ if (options.apply === true) {
250
+ await fs.mkdir(path.dirname(target.filePath), { recursive: true });
251
+ await fs.writeFile(target.filePath, merged.content, 'utf-8');
252
+ }
253
+ return {
254
+ action: 'update',
255
+ ...target,
256
+ changed: merged.changed,
257
+ selectedTemplates,
258
+ block,
259
+ content: options.preview === true ? merged.content : undefined,
260
+ warnings,
261
+ syncHosted,
262
+ };
263
+ }
264
+ export async function resetAgentsMdFile(options = {}) {
265
+ if (options.preview !== true && !(options.apply === true && options.confirm === true)) {
266
+ throw new Error('This command removes the managed AGENTS.md block. Retry with --apply --confirm or use --preview.');
267
+ }
268
+ const target = resolveAgentsMdTarget(options.root);
269
+ const current = existsSync(target.filePath) ? await readAgentsFile(target.filePath) : '';
270
+ const removed = removeAgentsMdManagedBlock(current);
271
+ if (options.apply === true && options.confirm === true && removed.changed) {
272
+ await fs.writeFile(target.filePath, removed.content, 'utf-8');
273
+ }
274
+ return {
275
+ action: 'reset',
276
+ ...target,
277
+ changed: removed.changed,
278
+ content: options.preview === true ? removed.content : undefined,
279
+ warnings: [],
280
+ };
281
+ }
@@ -0,0 +1,255 @@
1
+ import { createAopsApiClient } from '@aopslabs/api-client';
2
+ import { ensureApiTokensLoaded, findApiTargetByBaseUrl, findApiTargetByBaseUrlReadOnly, getActiveApiTarget, getActiveApiTargetReadOnly, getApiTarget, getApiTargetReadOnly, getCachedApiTokens, normalizeApiTargetBaseUrl, readApiTokensFromConfigFile, readApiTokensFromConfigFileReadOnly, setApiTokensInConfig, validateApiTarget, } from './config.js';
3
+ import { createRefreshRaceRecovery } from './refresh-race.js';
4
+ function normalizeNonEmpty(value) {
5
+ if (typeof value !== 'string')
6
+ return undefined;
7
+ const trimmed = value.trim();
8
+ return trimmed.length > 0 ? trimmed : undefined;
9
+ }
10
+ function resolveAccessToken(input, allowEnvironment = true) {
11
+ return (normalizeNonEmpty(input) ??
12
+ (allowEnvironment ? normalizeNonEmpty(process.env.AOPS_API_ACCESS_TOKEN) : undefined) ??
13
+ (allowEnvironment ? normalizeNonEmpty(process.env.AOPS_API_TOKEN) : undefined));
14
+ }
15
+ function resolveRefreshToken(input, allowEnvironment = true) {
16
+ return normalizeNonEmpty(input) ??
17
+ (allowEnvironment ? normalizeNonEmpty(process.env.AOPS_API_REFRESH_TOKEN) : undefined);
18
+ }
19
+ function resolveDefaultTimeoutMs() {
20
+ const raw = normalizeNonEmpty(process.env.AOPS_API_TIMEOUT_MS);
21
+ if (!raw)
22
+ return 15_000;
23
+ const parsed = Number.parseInt(raw, 10);
24
+ if (!Number.isFinite(parsed) || parsed <= 0)
25
+ return 15_000;
26
+ return parsed;
27
+ }
28
+ export function resolveCliApiTarget(input, targetName) {
29
+ const explicit = normalizeNonEmpty(input);
30
+ const selectedTarget = normalizeNonEmpty(targetName);
31
+ if (selectedTarget) {
32
+ const target = getApiTarget(selectedTarget);
33
+ if (!target)
34
+ throw new Error(`aops_target_not_found:${selectedTarget}`);
35
+ if (explicit && normalizeApiTargetBaseUrl(explicit) !== target.apiBaseUrl) {
36
+ throw new Error('aops_target_api_base_url_mismatch');
37
+ }
38
+ return { baseUrl: target.apiBaseUrl, targetName: target.name, endpointSource: 'target' };
39
+ }
40
+ if (explicit) {
41
+ const baseUrl = validateApiTarget({ apiBaseUrl: explicit }).apiBaseUrl;
42
+ return {
43
+ baseUrl,
44
+ targetName: findApiTargetByBaseUrl(baseUrl)?.name,
45
+ endpointSource: 'option',
46
+ };
47
+ }
48
+ const env = normalizeNonEmpty(process.env.AOPS_API_BASE_URL) ?? normalizeNonEmpty(process.env.AOPS_API_SERVER);
49
+ if (env) {
50
+ const baseUrl = validateApiTarget({ apiBaseUrl: env }).apiBaseUrl;
51
+ return {
52
+ baseUrl,
53
+ targetName: findApiTargetByBaseUrl(baseUrl)?.name,
54
+ endpointSource: 'environment',
55
+ };
56
+ }
57
+ const activeTarget = getActiveApiTarget();
58
+ if (activeTarget) {
59
+ return {
60
+ baseUrl: activeTarget.apiBaseUrl,
61
+ targetName: activeTarget.name,
62
+ endpointSource: 'target',
63
+ };
64
+ }
65
+ return { baseUrl: validateApiTarget({ apiBaseUrl: 'http://localhost:5900' }).apiBaseUrl, endpointSource: 'default' };
66
+ }
67
+ function resolveCliApiTargetReadOnly(input, targetName) {
68
+ const explicit = normalizeNonEmpty(input);
69
+ const selectedTarget = normalizeNonEmpty(targetName);
70
+ if (selectedTarget) {
71
+ const target = getApiTargetReadOnly(selectedTarget);
72
+ if (!target)
73
+ throw new Error(`aops_target_not_found:${selectedTarget}`);
74
+ if (explicit && normalizeApiTargetBaseUrl(explicit) !== target.apiBaseUrl) {
75
+ throw new Error('aops_target_api_base_url_mismatch');
76
+ }
77
+ return { baseUrl: target.apiBaseUrl, targetName: target.name, endpointSource: 'target' };
78
+ }
79
+ if (explicit) {
80
+ const baseUrl = validateApiTarget({ apiBaseUrl: explicit }).apiBaseUrl;
81
+ return {
82
+ baseUrl,
83
+ targetName: findApiTargetByBaseUrlReadOnly(baseUrl)?.name,
84
+ endpointSource: 'option',
85
+ };
86
+ }
87
+ const env = normalizeNonEmpty(process.env.AOPS_API_BASE_URL) ?? normalizeNonEmpty(process.env.AOPS_API_SERVER);
88
+ if (env) {
89
+ const baseUrl = validateApiTarget({ apiBaseUrl: env }).apiBaseUrl;
90
+ return {
91
+ baseUrl,
92
+ targetName: findApiTargetByBaseUrlReadOnly(baseUrl)?.name,
93
+ endpointSource: 'environment',
94
+ };
95
+ }
96
+ const activeTarget = getActiveApiTargetReadOnly();
97
+ if (activeTarget) {
98
+ return {
99
+ baseUrl: activeTarget.apiBaseUrl,
100
+ targetName: activeTarget.name,
101
+ endpointSource: 'target',
102
+ };
103
+ }
104
+ return { baseUrl: validateApiTarget({ apiBaseUrl: 'http://localhost:5900' }).apiBaseUrl, endpointSource: 'default' };
105
+ }
106
+ export function resolveCliApiBaseUrl(input, targetName) {
107
+ return resolveCliApiTarget(input, targetName).baseUrl;
108
+ }
109
+ export async function createCliApiClient(options = {}) {
110
+ const resolution = options.readOnly
111
+ ? resolveCliApiTargetReadOnly(options.apiBaseUrl, options.targetName)
112
+ : resolveCliApiTarget(options.apiBaseUrl, options.targetName);
113
+ if (!options.readOnly)
114
+ await ensureApiTokensLoaded(resolution.targetName);
115
+ const baseUrl = resolution.baseUrl;
116
+ const cached = resolution.targetName
117
+ ? options.readOnly
118
+ ? await readApiTokensFromConfigFileReadOnly(resolution.targetName)
119
+ : getCachedApiTokens(resolution.targetName)
120
+ : {};
121
+ const allowEnvironmentTokens = resolution.targetName === undefined;
122
+ let accessToken = resolveAccessToken(options.accessToken, allowEnvironmentTokens) ?? cached.accessToken;
123
+ let refreshToken = options.readOnly
124
+ ? undefined
125
+ : resolveRefreshToken(options.refreshToken, allowEnvironmentTokens) ?? cached.refreshToken;
126
+ let credentialRevision = cached.credentialRevision;
127
+ const client = createAopsApiClient({
128
+ baseUrl,
129
+ defaultTimeoutMs: options.timeoutMs ?? resolveDefaultTimeoutMs(),
130
+ getAccessToken: () => accessToken,
131
+ getRefreshToken: () => refreshToken,
132
+ onTokenRefresh: options.readOnly ? undefined : async ({ accessToken: nextAccess, refreshToken: nextRefresh, userId }) => {
133
+ const expectedCredentialRevision = credentialRevision;
134
+ accessToken = nextAccess;
135
+ refreshToken = nextRefresh;
136
+ if (resolution.targetName) {
137
+ credentialRevision = await setApiTokensInConfig({
138
+ accessToken: nextAccess,
139
+ refreshToken: nextRefresh,
140
+ userId,
141
+ apiServer: baseUrl,
142
+ targetName: resolution.targetName,
143
+ expectedCredentialRevision,
144
+ });
145
+ }
146
+ await options.onTokenRefresh?.({ accessToken: nextAccess, refreshToken: nextRefresh, userId });
147
+ },
148
+ onRefreshRace: !options.readOnly && resolution.targetName ? createRefreshRaceRecovery({
149
+ getCurrentRefreshToken: () => refreshToken,
150
+ readLatestTokens: async () => {
151
+ const latest = await readApiTokensFromConfigFile(resolution.targetName);
152
+ if (latest.accessToken && latest.refreshToken) {
153
+ credentialRevision = latest.credentialRevision;
154
+ return { accessToken: latest.accessToken, refreshToken: latest.refreshToken, userId: latest.userId };
155
+ }
156
+ return null;
157
+ },
158
+ applyTokens: ({ accessToken: nextAccess, refreshToken: nextRefresh }) => {
159
+ accessToken = nextAccess;
160
+ refreshToken = nextRefresh;
161
+ },
162
+ }) : async () => false,
163
+ });
164
+ return {
165
+ client,
166
+ baseUrl,
167
+ targetName: resolution.targetName,
168
+ endpointSource: resolution.endpointSource,
169
+ getAccessToken: () => accessToken,
170
+ getRefreshToken: () => refreshToken,
171
+ };
172
+ }
173
+ function normalizeAuthProvider(value) {
174
+ if (typeof value !== 'string')
175
+ return undefined;
176
+ const trimmed = value.trim().toLowerCase();
177
+ if (trimmed === 'trusted-local' || trimmed === 'trusted_local' || trimmed === 'trusted') {
178
+ return 'trusted-local';
179
+ }
180
+ if (trimmed === 'authv2-jwt-session' ||
181
+ trimmed === 'remote-session' ||
182
+ trimmed === 'remote_session' ||
183
+ trimmed === 'authv2_jwt_session' ||
184
+ trimmed === 'jwt-session' ||
185
+ trimmed === 'jwt_session' ||
186
+ trimmed === 'session') {
187
+ return 'remote-session';
188
+ }
189
+ return undefined;
190
+ }
191
+ export async function probeCliRuntimeMode(apiState, options = {}) {
192
+ try {
193
+ const response = await apiState.client.requestXfJson('/api/auth/me', { method: 'GET' }, { auth: false, retry401: false, timeoutMs: options.timeoutMs });
194
+ const authProvider = normalizeAuthProvider(response.result?.data?.authProvider) ??
195
+ (response.status === 401 ? 'remote-session' : undefined) ??
196
+ 'remote-session';
197
+ const authRequired = typeof response.result?.data?.authRequired === 'boolean'
198
+ ? response.result.data.authRequired
199
+ : authProvider === 'remote-session';
200
+ const principalUserId = normalizeNonEmpty(response.result?.data?.principal?.userId);
201
+ return {
202
+ authProvider,
203
+ authRequired,
204
+ hasPrincipal: response.status === 200 && response.result?.ok === true && Boolean(principalUserId),
205
+ principalUserId,
206
+ };
207
+ }
208
+ catch {
209
+ return {
210
+ authProvider: 'remote-session',
211
+ authRequired: true,
212
+ hasPrincipal: false,
213
+ };
214
+ }
215
+ }
216
+ export function buildCliApiClientOptions(options = {}) {
217
+ return {
218
+ apiBaseUrl: options.apiBaseUrl,
219
+ targetName: options.targetName,
220
+ accessToken: options.accessToken,
221
+ refreshToken: options.refreshToken,
222
+ timeoutMs: options.timeoutMs,
223
+ readOnly: options.readOnly,
224
+ };
225
+ }
226
+ export async function createCliApiClientFromOptions(options = {}) {
227
+ return createCliApiClient(buildCliApiClientOptions(options));
228
+ }
229
+ export async function fetchCliPublicJson(apiState, path, options = {}) {
230
+ return apiState.client.fetchJson(path, {
231
+ method: 'GET',
232
+ auth: false,
233
+ retry401: false,
234
+ timeoutMs: options.timeoutMs,
235
+ });
236
+ }
237
+ export async function fetchCliHealth(apiState, options = {}) {
238
+ return fetchCliPublicJson(apiState, '/api/health', options);
239
+ }
240
+ export async function fetchCliBootstrapHealth(apiState, options = {}) {
241
+ const payload = await fetchCliHealth(apiState, options);
242
+ return (payload?.data?.bootstrap ?? {});
243
+ }
244
+ export async function isCliAuthRequired(apiState, options = {}) {
245
+ return (await probeCliRuntimeMode(apiState, { timeoutMs: options.timeoutMs })).authRequired;
246
+ }
247
+ export async function isCliHostReachable(apiState, options = {}) {
248
+ try {
249
+ await fetchCliHealth(apiState, options);
250
+ return true;
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ }