@eve-horizon/cli 0.2.11 → 0.2.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eve-horizon/cli",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,9 +28,11 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^22.0.0",
31
- "typescript": "^5.7.0"
31
+ "esbuild": "^0.27.3",
32
+ "typescript": "^5.7.0",
33
+ "@eve/shared": "0.0.1"
32
34
  },
33
35
  "scripts": {
34
- "build": "tsc -p tsconfig.json"
36
+ "build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node22 --outfile=dist/index.js --format=cjs --external:yaml"
35
37
  }
36
38
  }
@@ -1,81 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleAdmin = handleAdmin;
4
- const args_1 = require("../lib/args");
5
- const client_1 = require("../lib/client");
6
- const output_1 = require("../lib/output");
7
- async function handleAdmin(subcommand, positionals, flags, context) {
8
- const json = Boolean(flags.json);
9
- switch (subcommand) {
10
- case 'invite': {
11
- const githubUsername = (0, args_1.getStringFlag)(flags, ['github']);
12
- const email = (0, args_1.getStringFlag)(flags, ['email']);
13
- const role = (0, args_1.getStringFlag)(flags, ['role']) ?? 'member';
14
- const orgId = (0, args_1.getStringFlag)(flags, ['org']) ?? context.orgId;
15
- if (!email) {
16
- throw new Error('Usage: eve admin invite --email <email> [--github <username>] [--role <role>] [--org <org_id>]');
17
- }
18
- if (!['owner', 'admin', 'member'].includes(role)) {
19
- throw new Error(`Invalid role: ${role}. Must be one of: owner, admin, member`);
20
- }
21
- if (!orgId) {
22
- throw new Error('No org specified. Use --org <org_id> or set a default org in your profile.');
23
- }
24
- const results = {
25
- keys_registered: 0,
26
- identities: [],
27
- };
28
- // Add user to org first - this creates the user if they don't exist
29
- if (orgId) {
30
- const membership = await (0, client_1.requestJson)(context, `/orgs/${orgId}/members`, {
31
- method: 'POST',
32
- body: {
33
- email,
34
- role,
35
- },
36
- });
37
- results.membership = membership;
38
- }
39
- // Fetch and register GitHub SSH keys if username provided
40
- // Now that user exists (created via org membership), identity registration will work
41
- if (githubUsername) {
42
- const keys = await fetchGitHubKeys(githubUsername);
43
- if (keys.length === 0) {
44
- throw new Error(`No SSH keys found for GitHub user: ${githubUsername}`);
45
- }
46
- for (const publicKey of keys) {
47
- const identity = await (0, client_1.requestJson)(context, '/auth/identities', {
48
- method: 'POST',
49
- body: {
50
- email,
51
- public_key: publicKey,
52
- label: `github-${githubUsername}`,
53
- },
54
- });
55
- results.identities.push(identity);
56
- results.keys_registered += 1;
57
- }
58
- }
59
- const summary = [
60
- `Invited ${email}`,
61
- results.keys_registered > 0 ? `${results.keys_registered} SSH key(s) registered` : null,
62
- results.membership ? `Added to ${orgId} as ${role}` : null,
63
- ].filter(Boolean).join(', ');
64
- (0, output_1.outputJson)(results, json, `+ ${summary}`);
65
- return;
66
- }
67
- default:
68
- throw new Error('Usage: eve admin <invite>');
69
- }
70
- }
71
- async function fetchGitHubKeys(username) {
72
- const response = await fetch(`https://github.com/${username}.keys`);
73
- if (!response.ok) {
74
- if (response.status === 404) {
75
- throw new Error(`GitHub user not found: ${username}`);
76
- }
77
- throw new Error(`Failed to fetch GitHub keys: HTTP ${response.status}`);
78
- }
79
- const text = await response.text();
80
- return text.trim().split('\n').filter(k => k.length > 0);
81
- }
@@ -1,196 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleAgents = handleAgents;
4
- const node_child_process_1 = require("node:child_process");
5
- const node_fs_1 = require("node:fs");
6
- const node_path_1 = require("node:path");
7
- const yaml_1 = require("yaml");
8
- const harness_capabilities_js_1 = require("../lib/harness-capabilities.js");
9
- const args_1 = require("../lib/args");
10
- const client_1 = require("../lib/client");
11
- const output_1 = require("../lib/output");
12
- const git_js_1 = require("../lib/git.js");
13
- function readYamlFile(filePath) {
14
- const raw = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
15
- const parsed = (0, yaml_1.parse)(raw);
16
- if (!parsed || typeof parsed !== 'object') {
17
- throw new Error(`Invalid YAML in ${filePath}`);
18
- }
19
- return parsed;
20
- }
21
- function resolveAgentsConfigPaths(repoRoot, manifest) {
22
- const xEve = manifest['x-eve'] ||
23
- manifest['x_eve'] ||
24
- {};
25
- const agentsBlock = xEve['agents'] || {};
26
- const chatBlock = xEve['chat'] || {};
27
- const manifestChat = manifest['chat'] || {};
28
- const agentsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.config_path) ?? 'agents/agents.yaml');
29
- const teamsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.teams_path) ?? 'agents/teams.yaml');
30
- const chatPath = (0, node_path_1.resolve)(repoRoot, pickString(chatBlock.config_path) ?? pickString(manifestChat.config_path) ?? 'agents/chat.yaml');
31
- return { agentsPath, teamsPath, chatPath };
32
- }
33
- function pickString(value) {
34
- return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
35
- }
36
- function ensureFileExists(path, label) {
37
- if (!(0, node_fs_1.existsSync)(path)) {
38
- throw new Error(`Missing ${label} at ${path}. Update manifest config_path or add the file.`);
39
- }
40
- return path;
41
- }
42
- function isLocalApiUrl(apiUrl) {
43
- try {
44
- const url = new URL(apiUrl);
45
- const host = url.hostname.toLowerCase();
46
- return (host === 'localhost' ||
47
- host === '127.0.0.1' ||
48
- host === '0.0.0.0' ||
49
- host.endsWith('.lvh.me') ||
50
- host.endsWith('.localhost'));
51
- }
52
- catch {
53
- return false;
54
- }
55
- }
56
- function loadAgentsConfig(repoRoot) {
57
- const eveDir = (0, node_path_1.join)(repoRoot, '.eve');
58
- const manifestPath = (0, node_path_1.join)(eveDir, 'manifest.yaml');
59
- if ((0, node_fs_1.existsSync)(manifestPath)) {
60
- const manifest = readYamlFile(manifestPath);
61
- const xEve = manifest['x-eve'] ||
62
- manifest['x_eve'] ||
63
- {};
64
- const policy = xEve['agents'] || null;
65
- const defaults = xEve['defaults'] || null;
66
- return { source: { type: 'manifest', path: manifestPath }, policy, manifest_defaults: defaults };
67
- }
68
- return { source: { type: 'none' }, policy: null };
69
- }
70
- async function handleAgents(subcommand, positionals, flags, context) {
71
- const command = subcommand ?? 'config';
72
- const json = Boolean(flags.json);
73
- const includeHarnesses = !((0, args_1.getBooleanFlag)(flags, ['no-harnesses']) ?? false);
74
- const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir', 'path']) ?? process.cwd());
75
- switch (command) {
76
- case 'config': {
77
- const result = loadAgentsConfig(repoRoot);
78
- const response = {
79
- repo_root: repoRoot,
80
- source: result.source,
81
- policy: result.policy,
82
- };
83
- if (result.manifest_defaults) {
84
- response.manifest_defaults = result.manifest_defaults;
85
- }
86
- if (includeHarnesses) {
87
- const harnesses = await (0, client_1.requestJson)(context, '/harnesses');
88
- response.harnesses = harnesses.data;
89
- response.capabilities = harness_capabilities_js_1.HARNESS_CAPABILITIES;
90
- }
91
- if (json) {
92
- (0, output_1.outputJson)(response, json);
93
- return;
94
- }
95
- console.log(`Agents config source: ${result.source.type}`);
96
- if ('path' in result.source) {
97
- console.log(`Path: ${result.source.path}`);
98
- }
99
- if (!result.policy) {
100
- console.log('No policy found. Add x-eve.agents to .eve/manifest.yaml.');
101
- }
102
- else {
103
- const profiles = result.policy.profiles || {};
104
- const profileNames = Object.keys(profiles);
105
- console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
106
- }
107
- if (includeHarnesses) {
108
- const harnesses = response.harnesses;
109
- if (harnesses?.length) {
110
- const ready = harnesses.filter((h) => h.auth.available).length;
111
- console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
112
- }
113
- }
114
- return;
115
- }
116
- case 'sync': {
117
- const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
118
- if (!projectId) {
119
- throw new Error('Missing project id. Provide --project or set a profile default.');
120
- }
121
- const ref = (0, args_1.getStringFlag)(flags, ['ref']);
122
- const local = (0, args_1.getBooleanFlag)(flags, ['local']) ?? false;
123
- const allowDirty = (0, args_1.getBooleanFlag)(flags, ['allow-dirty', 'allow_dirty']) ?? false;
124
- const forceNonlocal = (0, args_1.getBooleanFlag)(flags, ['force-nonlocal', 'force_nonlocal']) ?? false;
125
- if (local && ref) {
126
- throw new Error('Use either --local or --ref, not both.');
127
- }
128
- if (!local && !ref) {
129
- throw new Error('Missing --ref. Use --ref <sha|branch> or --local for dev sync.');
130
- }
131
- if (local && !forceNonlocal && !isLocalApiUrl(context.apiUrl)) {
132
- throw new Error(`--local sync is only allowed for local API URLs (localhost or *.lvh.me). ` +
133
- `Current API: ${context.apiUrl}. Use --force-nonlocal to override.`);
134
- }
135
- const gitRoot = (0, git_js_1.getGitRoot)(repoRoot);
136
- if (!gitRoot) {
137
- throw new Error('Not a git repository. Run from the repo root or pass --repo-dir <path>.');
138
- }
139
- const dirty = (0, git_js_1.isGitDirty)(gitRoot);
140
- if (dirty && !allowDirty) {
141
- throw new Error('Working tree is dirty. Commit changes or pass --allow-dirty to sync anyway.');
142
- }
143
- const manifestPath = (0, node_path_1.join)(repoRoot, '.eve', 'manifest.yaml');
144
- if (!(0, node_fs_1.existsSync)(manifestPath)) {
145
- throw new Error(`Missing manifest at ${manifestPath}. Expected .eve/manifest.yaml.`);
146
- }
147
- const manifest = readYamlFile(manifestPath);
148
- const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
149
- const agentsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.agentsPath, 'agents config'), 'utf-8');
150
- const teamsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.teamsPath, 'teams config'), 'utf-8');
151
- const chatYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.chatPath, 'chat config'), 'utf-8');
152
- let gitSha;
153
- let branch;
154
- let gitRef = ref ?? 'local';
155
- if (ref) {
156
- gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoRoot);
157
- branch = (0, git_js_1.resolveGitBranch)(gitRoot, ref) ?? undefined;
158
- }
159
- else {
160
- gitSha = (0, node_child_process_1.execSync)('git rev-parse HEAD', { cwd: gitRoot, encoding: 'utf-8' }).trim();
161
- branch = (0, git_js_1.getGitBranch)(gitRoot) ?? undefined;
162
- }
163
- if (dirty) {
164
- gitRef = `dirty:${gitRef}`;
165
- }
166
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/agents/sync`, {
167
- method: 'POST',
168
- body: {
169
- agents_yaml: agentsYaml,
170
- teams_yaml: teamsYaml,
171
- chat_yaml: chatYaml,
172
- git_sha: gitSha,
173
- branch,
174
- git_ref: gitRef,
175
- },
176
- });
177
- if (json) {
178
- (0, output_1.outputJson)(response, json);
179
- return;
180
- }
181
- console.log(`✓ Agents config synced to ${projectId}`);
182
- console.log(` Agents: ${configPaths.agentsPath}`);
183
- console.log(` Teams: ${configPaths.teamsPath}`);
184
- console.log(` Chat: ${configPaths.chatPath}`);
185
- if (gitSha)
186
- console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
187
- if (branch)
188
- console.log(` Branch: ${branch}`);
189
- if (dirty)
190
- console.log(' Warning: working tree dirty — marked non-deployable');
191
- return;
192
- }
193
- default:
194
- throw new Error('Usage: eve agents <config|sync>');
195
- }
196
- }
@@ -1,350 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleApi = handleApi;
4
- exports.handleSpec = handleSpec;
5
- exports.fetchApiSpec = fetchApiSpec;
6
- const args_1 = require("../lib/args");
7
- const client_1 = require("../lib/client");
8
- const output_1 = require("../lib/output");
9
- const node_fs_1 = require("node:fs");
10
- const node_path_1 = require("node:path");
11
- const node_child_process_1 = require("node:child_process");
12
- async function handleApi(subcommand, positionals, flags, context) {
13
- const jsonOutput = Boolean(flags.json);
14
- switch (subcommand) {
15
- case 'list':
16
- return handleList(positionals, flags, context, jsonOutput);
17
- case 'show':
18
- return handleShow(positionals, flags, context, jsonOutput);
19
- case 'spec':
20
- return handleSpec(positionals, flags, context, jsonOutput);
21
- case 'refresh':
22
- return handleRefresh(positionals, flags, context, jsonOutput);
23
- case 'examples':
24
- return handleExamples(positionals, flags, context, jsonOutput);
25
- case 'call':
26
- return handleCall(positionals, flags, context);
27
- default:
28
- throw new Error('Usage: eve api <list|show|spec|refresh|examples|call>\n' +
29
- ' list [project] - list API sources\n' +
30
- ' show <name> [project] - show a single API source\n' +
31
- ' spec <name> [project] - show cached API spec\n' +
32
- ' refresh <name> [project] - refresh cached API spec\n' +
33
- ' examples <name> [project] - print curl examples from spec\n' +
34
- ' call <name> <method> <path> [options] - call an API with Eve auth');
35
- }
36
- }
37
- async function handleList(positionals, flags, context, jsonOutput) {
38
- const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
39
- if (!projectId) {
40
- throw new Error('Usage: eve api list [project] [--project=<id>] [--env=<name>]');
41
- }
42
- const query = buildQuery({
43
- env: (0, args_1.getStringFlag)(flags, ['env']),
44
- });
45
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis${query}`);
46
- if (jsonOutput) {
47
- (0, output_1.outputJson)(response, jsonOutput);
48
- return;
49
- }
50
- if (!response.data || response.data.length === 0) {
51
- console.log('No API sources found.');
52
- return;
53
- }
54
- formatApiSourcesTable(response.data);
55
- }
56
- async function handleShow(positionals, flags, context, jsonOutput) {
57
- const name = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
58
- const projectId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
59
- const env = (0, args_1.getStringFlag)(flags, ['env']);
60
- if (!name || !projectId) {
61
- throw new Error('Usage: eve api show <name> [project] [--project=<id>] [--env=<name>]');
62
- }
63
- const query = buildQuery({ env });
64
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis/${name}${query}`);
65
- (0, output_1.outputJson)(response, jsonOutput);
66
- }
67
- async function handleSpec(positionals, flags, context, jsonOutput) {
68
- const name = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
69
- const projectId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
70
- const env = (0, args_1.getStringFlag)(flags, ['env']);
71
- if (!name || !projectId) {
72
- throw new Error('Usage: eve api spec <name> [project] [--project=<id>] [--env=<name>]');
73
- }
74
- const spec = await fetchApiSpec(context, projectId, name, env);
75
- if (typeof spec.data === 'string') {
76
- (0, output_1.outputJson)(spec.data, jsonOutput, spec.raw);
77
- return;
78
- }
79
- (0, output_1.outputJson)(spec.data, jsonOutput);
80
- }
81
- async function handleRefresh(positionals, flags, context, jsonOutput) {
82
- const name = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
83
- const projectId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
84
- const env = (0, args_1.getStringFlag)(flags, ['env']);
85
- if (!name || !projectId) {
86
- throw new Error('Usage: eve api refresh <name> [project] [--project=<id>] [--env=<name>]');
87
- }
88
- const query = buildQuery({ env });
89
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis/${name}/refresh${query}`, {
90
- method: 'POST',
91
- });
92
- (0, output_1.outputJson)(response, jsonOutput);
93
- }
94
- async function handleExamples(positionals, flags, context, jsonOutput) {
95
- const name = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
96
- const projectId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
97
- const env = (0, args_1.getStringFlag)(flags, ['env']);
98
- if (!name || !projectId) {
99
- throw new Error('Usage: eve api examples <name> [project] [--project=<id>] [--env=<name>]');
100
- }
101
- const query = buildQuery({ env });
102
- const api = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis/${name}${query}`);
103
- const spec = await fetchApiSpec(context, projectId, name, env);
104
- if (jsonOutput) {
105
- (0, output_1.outputJson)({ api, spec: spec.data }, jsonOutput);
106
- return;
107
- }
108
- if (!spec.data || typeof spec.data !== 'object') {
109
- throw new Error('API spec is not JSON; examples require a JSON OpenAPI spec.');
110
- }
111
- const examples = buildCurlExamples(api, spec.data);
112
- if (examples.length === 0) {
113
- console.log('No endpoints found in spec.');
114
- return;
115
- }
116
- console.log(`API examples for ${api.name} (${api.type})`);
117
- console.log('');
118
- examples.forEach((example) => {
119
- console.log(example);
120
- });
121
- }
122
- async function handleCall(positionals, flags, context) {
123
- const name = positionals[0];
124
- const methodInput = positionals[1];
125
- const pathInput = positionals[2];
126
- if (!name || !methodInput || !pathInput) {
127
- throw new Error('Usage: eve api call <name> <method> <path> [--json <payload>] [--jq <expr>]');
128
- }
129
- const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
130
- const env = (0, args_1.getStringFlag)(flags, ['env']);
131
- if (!projectId) {
132
- throw new Error('Missing project id. Provide --project or set a profile default.');
133
- }
134
- const query = buildQuery({ env });
135
- const api = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis/${name}${query}`);
136
- const jqExpr = (0, args_1.getStringFlag)(flags, ['jq']);
137
- const printCurl = Boolean(flags['print-curl']);
138
- const jsonPayloadFlag = flags.json;
139
- if (jsonPayloadFlag === true) {
140
- throw new Error('Usage: --json <payload|@file>');
141
- }
142
- const graphqlFlag = flags.graphql;
143
- const graphqlQuery = graphqlFlag === true ? undefined : (0, args_1.getStringFlag)(flags, ['graphql']);
144
- if (graphqlFlag === true && !graphqlQuery) {
145
- throw new Error('Usage: --graphql <query|@file>');
146
- }
147
- if (graphqlQuery && jsonPayloadFlag) {
148
- throw new Error('Use --variables for GraphQL variables; --json is for JSON bodies.');
149
- }
150
- const variablesFlag = (0, args_1.getStringFlag)(flags, ['variables']);
151
- const variables = variablesFlag ? resolveJsonInput(variablesFlag) : undefined;
152
- const jsonBody = typeof jsonPayloadFlag === 'string' ? resolveJsonInput(jsonPayloadFlag) : undefined;
153
- const graphqlBody = graphqlQuery
154
- ? {
155
- query: resolveTextInput(graphqlQuery),
156
- ...(variables ? { variables } : {}),
157
- }
158
- : undefined;
159
- const body = graphqlBody ?? jsonBody;
160
- const method = methodInput.toUpperCase();
161
- const path = resolveApiPath(api.base_url, pathInput);
162
- const tokenOverride = (0, args_1.getStringFlag)(flags, ['token']);
163
- const authToken = tokenOverride ?? process.env.EVE_JOB_TOKEN ?? context.token;
164
- if (printCurl) {
165
- const curl = buildCurlCommand({
166
- method,
167
- url: path,
168
- authToken,
169
- jsonBody: body,
170
- authHint: tokenOverride ? 'override' : (process.env.EVE_JOB_TOKEN ? 'job' : 'user'),
171
- });
172
- console.log(curl);
173
- return;
174
- }
175
- const response = await fetch(path, {
176
- method,
177
- headers: buildApiHeaders(authToken, body),
178
- body: body ? JSON.stringify(body) : undefined,
179
- });
180
- const text = await response.text();
181
- if (!response.ok) {
182
- throw new Error(`HTTP ${response.status}: ${text}`);
183
- }
184
- const { data, jsonText } = parseResponse(text);
185
- if (jqExpr) {
186
- if (!jsonText) {
187
- throw new Error('Cannot apply --jq to a non-JSON response.');
188
- }
189
- const output = runJq(jqExpr, jsonText);
190
- process.stdout.write(output);
191
- return;
192
- }
193
- if (data !== undefined) {
194
- console.log(JSON.stringify(data, null, 2));
195
- return;
196
- }
197
- process.stdout.write(text);
198
- }
199
- function buildQuery(params) {
200
- const query = Object.entries(params)
201
- .filter(([, value]) => value !== undefined)
202
- .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
203
- .join('&');
204
- return query ? `?${query}` : '';
205
- }
206
- function formatApiSourcesTable(apis) {
207
- const nameWidth = Math.max(4, ...apis.map((api) => api.name.length));
208
- const typeWidth = Math.max(4, ...apis.map((api) => api.type.length));
209
- console.log(`${'NAME'.padEnd(nameWidth)} ${'TYPE'.padEnd(typeWidth)} BASE URL`);
210
- console.log(`${'-'.repeat(nameWidth)} ${'-'.repeat(typeWidth)} --------`);
211
- apis.forEach((api) => {
212
- console.log(`${api.name.padEnd(nameWidth)} ${api.type.padEnd(typeWidth)} ${api.base_url}`);
213
- });
214
- }
215
- async function fetchApiSpec(context, projectId, name, env) {
216
- const query = buildQuery({ env });
217
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/apis/${name}/spec${query}`);
218
- if (response && typeof response === 'object' && 'schema' in response) {
219
- const schema = response.schema;
220
- if (typeof schema === 'string') {
221
- return { data: schema.trim(), raw: schema };
222
- }
223
- return { data: schema, raw: JSON.stringify(schema) };
224
- }
225
- if (typeof response === 'string') {
226
- return { data: response.trim(), raw: response };
227
- }
228
- return { data: response, raw: JSON.stringify(response) };
229
- }
230
- function buildCurlExamples(api, spec) {
231
- const paths = spec.paths ?? {};
232
- const examples = [];
233
- Object.entries(paths).forEach(([path, methods]) => {
234
- Object.entries(methods).forEach(([method, operation]) => {
235
- const lower = method.toLowerCase();
236
- if (!['get', 'post', 'put', 'patch', 'delete'].includes(lower)) {
237
- return;
238
- }
239
- const requestBody = operation.requestBody?.content?.['application/json'];
240
- const examplePayload = extractExamplePayload(requestBody);
241
- const needsBody = ['post', 'put', 'patch'].includes(lower);
242
- const curl = buildCurlCommand({
243
- method: lower.toUpperCase(),
244
- url: resolveApiPath(api.base_url, path),
245
- authToken: api.auth_mode === 'eve' ? '$EVE_JOB_TOKEN' : undefined,
246
- jsonBody: needsBody ? (examplePayload ?? {}) : undefined,
247
- authHint: 'job',
248
- });
249
- const summary = operation.summary ?? operation.description;
250
- if (summary) {
251
- examples.push(`# ${summary}`);
252
- }
253
- examples.push(curl);
254
- examples.push('');
255
- });
256
- });
257
- return examples;
258
- }
259
- function extractExamplePayload(requestBody) {
260
- if (!requestBody)
261
- return undefined;
262
- if (requestBody.example !== undefined)
263
- return requestBody.example;
264
- const examples = requestBody.examples ? Object.values(requestBody.examples) : [];
265
- if (examples.length === 0)
266
- return undefined;
267
- return examples[0]?.value;
268
- }
269
- function resolveApiPath(baseUrl, path) {
270
- if (path.startsWith('http://') || path.startsWith('https://')) {
271
- return path;
272
- }
273
- if (!baseUrl) {
274
- return path;
275
- }
276
- const trimmedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
277
- const trimmedPath = path.startsWith('/') ? path : `/${path}`;
278
- return `${trimmedBase}${trimmedPath}`;
279
- }
280
- function buildApiHeaders(authToken, body) {
281
- const headers = {};
282
- if (authToken) {
283
- headers.Authorization = `Bearer ${authToken}`;
284
- }
285
- if (body !== undefined) {
286
- headers['Content-Type'] = 'application/json';
287
- }
288
- return headers;
289
- }
290
- function resolveJsonInput(value) {
291
- const text = resolveTextInput(value);
292
- try {
293
- return JSON.parse(text);
294
- }
295
- catch (error) {
296
- throw new Error(`Invalid JSON payload: ${error.message}`);
297
- }
298
- }
299
- function resolveTextInput(value) {
300
- if (value.startsWith('@')) {
301
- const filePath = (0, node_path_1.resolve)(value.slice(1));
302
- return (0, node_fs_1.readFileSync)(filePath, 'utf-8');
303
- }
304
- if (value.trim().startsWith('{') || value.trim().startsWith('[')) {
305
- return value;
306
- }
307
- if ((0, node_fs_1.existsSync)(value)) {
308
- return (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(value), 'utf-8');
309
- }
310
- return value;
311
- }
312
- function parseResponse(text) {
313
- if (!text)
314
- return { data: undefined, jsonText: undefined };
315
- try {
316
- const data = JSON.parse(text);
317
- return { data, jsonText: JSON.stringify(data) };
318
- }
319
- catch {
320
- return { data: undefined, jsonText: undefined };
321
- }
322
- }
323
- function runJq(expression, jsonText) {
324
- const result = (0, node_child_process_1.spawnSync)('jq', [expression], {
325
- input: jsonText,
326
- encoding: 'utf-8',
327
- });
328
- if (result.error) {
329
- if (result.error.code === 'ENOENT') {
330
- throw new Error('jq is not installed. Install jq to use --jq output filtering.');
331
- }
332
- throw result.error;
333
- }
334
- if (result.status !== 0) {
335
- throw new Error(result.stderr || 'jq failed to process the response.');
336
- }
337
- return result.stdout ?? '';
338
- }
339
- function buildCurlCommand(options) {
340
- const parts = ['curl', '-sS', '-X', options.method, `'${options.url}'`];
341
- if (options.authToken) {
342
- const tokenValue = options.authHint === 'job' ? '$EVE_JOB_TOKEN' : options.authToken;
343
- parts.push(`-H 'Authorization: Bearer ${tokenValue}'`);
344
- }
345
- if (options.jsonBody !== undefined) {
346
- parts.push("-H 'Content-Type: application/json'");
347
- parts.push(`-d '${JSON.stringify(options.jsonBody)}'`);
348
- }
349
- return parts.join(' ');
350
- }