@eve-horizon/cli 0.2.1 → 0.2.6
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/dist/commands/auth.js +40 -0
- package/dist/commands/build.js +366 -0
- package/dist/commands/env.js +24 -4
- package/dist/commands/org.js +2 -1
- package/dist/commands/pipeline.js +19 -3
- package/dist/commands/profile.js +48 -19
- package/dist/commands/system.js +124 -3
- package/dist/index.js +4 -0
- package/dist/lib/help.js +180 -17
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -271,6 +271,46 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
271
271
|
console.log(tokenEntry.access_token);
|
|
272
272
|
return;
|
|
273
273
|
}
|
|
274
|
+
case 'mint': {
|
|
275
|
+
const email = (0, args_1.getStringFlag)(flags, ['email']);
|
|
276
|
+
const orgId = (0, args_1.getStringFlag)(flags, ['org']);
|
|
277
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']);
|
|
278
|
+
const role = (0, args_1.getStringFlag)(flags, ['role']) ?? 'member';
|
|
279
|
+
const ttlStr = (0, args_1.getStringFlag)(flags, ['ttl']);
|
|
280
|
+
const ttlDays = ttlStr ? parseInt(ttlStr, 10) : undefined;
|
|
281
|
+
if (ttlDays !== undefined && (isNaN(ttlDays) || ttlDays < 1 || ttlDays > 90)) {
|
|
282
|
+
throw new Error('--ttl must be between 1 and 90 days');
|
|
283
|
+
}
|
|
284
|
+
if (!email) {
|
|
285
|
+
throw new Error('Usage: eve auth mint --email <email> [--org <org_id> | --project <project_id>] [--role <role>]');
|
|
286
|
+
}
|
|
287
|
+
if (!orgId && !projectId) {
|
|
288
|
+
throw new Error('Usage: eve auth mint --email <email> [--org <org_id> | --project <project_id>] [--role <role>]');
|
|
289
|
+
}
|
|
290
|
+
if (!['admin', 'member'].includes(role)) {
|
|
291
|
+
throw new Error(`Invalid role: ${role}. Must be one of: admin, member`);
|
|
292
|
+
}
|
|
293
|
+
const response = await (0, client_1.requestJson)(context, '/auth/mint', {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
body: {
|
|
296
|
+
email,
|
|
297
|
+
org_id: orgId,
|
|
298
|
+
project_id: projectId,
|
|
299
|
+
role,
|
|
300
|
+
ttl_days: ttlDays,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
if (json) {
|
|
304
|
+
(0, output_1.outputJson)(response, json);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (!response.access_token) {
|
|
308
|
+
throw new Error('Mint response missing access_token');
|
|
309
|
+
}
|
|
310
|
+
// Print only the token to stdout, nothing else
|
|
311
|
+
console.log(response.access_token);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
274
314
|
case 'sync': {
|
|
275
315
|
const claudeOnly = (0, args_1.getBooleanFlag)(flags, ['claude']) ?? false;
|
|
276
316
|
const codexOnly = (0, args_1.getBooleanFlag)(flags, ['codex']) ?? false;
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleBuild = handleBuild;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Entry point
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
async function handleBuild(subcommand, positionals, flags, context) {
|
|
12
|
+
const json = Boolean(flags.json);
|
|
13
|
+
switch (subcommand) {
|
|
14
|
+
case 'create':
|
|
15
|
+
return handleCreate(flags, context, json);
|
|
16
|
+
case 'list':
|
|
17
|
+
return handleList(positionals, flags, context, json);
|
|
18
|
+
case 'show':
|
|
19
|
+
return handleShow(positionals, flags, context, json);
|
|
20
|
+
case 'run':
|
|
21
|
+
return handleRun(positionals, flags, context, json);
|
|
22
|
+
case 'runs':
|
|
23
|
+
return handleRuns(positionals, flags, context, json);
|
|
24
|
+
case 'logs':
|
|
25
|
+
return handleLogs(positionals, flags, context, json);
|
|
26
|
+
case 'artifacts':
|
|
27
|
+
return handleArtifacts(positionals, flags, context, json);
|
|
28
|
+
case 'diagnose':
|
|
29
|
+
return handleDiagnose(positionals, flags, context, json);
|
|
30
|
+
case 'cancel':
|
|
31
|
+
return handleCancel(positionals, flags, context, json);
|
|
32
|
+
default:
|
|
33
|
+
throw new Error('Usage: eve build <create|list|show|run|runs|logs|artifacts|diagnose|cancel>\n' +
|
|
34
|
+
' create --project <id> --ref <sha> [--services <list>] - create a build spec\n' +
|
|
35
|
+
' list [--project <id>] - list build specs\n' +
|
|
36
|
+
' show <build_id> - show build spec details\n' +
|
|
37
|
+
' run <build_id> - start a build run\n' +
|
|
38
|
+
' runs <build_id> - list runs for a build\n' +
|
|
39
|
+
' logs <build_id> [--run <id>] - show build logs\n' +
|
|
40
|
+
' artifacts <build_id> - list build artifacts\n' +
|
|
41
|
+
' diagnose <build_id> - show full build state\n' +
|
|
42
|
+
' cancel <build_id> - cancel active build run');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Subcommands
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
async function handleCreate(flags, context, json) {
|
|
49
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
50
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
51
|
+
const manifestHash = (0, args_1.getStringFlag)(flags, ['manifest-hash', 'manifest']);
|
|
52
|
+
const services = (0, args_1.getStringFlag)(flags, ['services']);
|
|
53
|
+
if (!projectId || !ref || !manifestHash) {
|
|
54
|
+
throw new Error('Usage: eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>]');
|
|
55
|
+
}
|
|
56
|
+
// Resolve git ref to actual 40-char SHA
|
|
57
|
+
let gitSha;
|
|
58
|
+
try {
|
|
59
|
+
gitSha = (0, node_child_process_1.execSync)(`git rev-parse ${ref}`, {
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
62
|
+
}).trim();
|
|
63
|
+
if (!json) {
|
|
64
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
69
|
+
'Make sure you are in a git repository and the ref exists.');
|
|
70
|
+
}
|
|
71
|
+
const body = { git_sha: gitSha, manifest_hash: manifestHash };
|
|
72
|
+
if (services) {
|
|
73
|
+
body.services = services.split(',').map(s => s.trim());
|
|
74
|
+
}
|
|
75
|
+
const build = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds`, { method: 'POST', body });
|
|
76
|
+
if (json) {
|
|
77
|
+
(0, output_1.outputJson)(build, json);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.log(`Build created: ${build.id}`);
|
|
81
|
+
console.log(` Project: ${build.project_id}`);
|
|
82
|
+
console.log(` SHA: ${build.git_sha}`);
|
|
83
|
+
if (build.manifest_hash) {
|
|
84
|
+
console.log(` Manifest: ${build.manifest_hash.substring(0, 12)}...`);
|
|
85
|
+
}
|
|
86
|
+
console.log(` Created: ${build.created_at}`);
|
|
87
|
+
}
|
|
88
|
+
async function handleList(positionals, flags, context, json) {
|
|
89
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
90
|
+
if (!projectId) {
|
|
91
|
+
throw new Error('Usage: eve build list [--project <id>]');
|
|
92
|
+
}
|
|
93
|
+
const query = buildQuery({
|
|
94
|
+
limit: (0, args_1.getStringFlag)(flags, ['limit']),
|
|
95
|
+
offset: (0, args_1.getStringFlag)(flags, ['offset']),
|
|
96
|
+
});
|
|
97
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds${query}`);
|
|
98
|
+
if (json) {
|
|
99
|
+
(0, output_1.outputJson)(response, json);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (response.data.length === 0) {
|
|
103
|
+
console.log('No builds found.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
formatBuildList(response.data);
|
|
107
|
+
}
|
|
108
|
+
async function handleShow(positionals, flags, context, json) {
|
|
109
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
110
|
+
if (!buildId) {
|
|
111
|
+
throw new Error('Usage: eve build show <build_id>');
|
|
112
|
+
}
|
|
113
|
+
const build = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
|
|
114
|
+
if (json) {
|
|
115
|
+
(0, output_1.outputJson)(build, json);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
formatBuildDetail(build);
|
|
119
|
+
}
|
|
120
|
+
async function handleRun(positionals, flags, context, json) {
|
|
121
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
122
|
+
if (!buildId) {
|
|
123
|
+
throw new Error('Usage: eve build run <build_id>');
|
|
124
|
+
}
|
|
125
|
+
const run = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs`, { method: 'POST' });
|
|
126
|
+
if (json) {
|
|
127
|
+
(0, output_1.outputJson)(run, json);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.log(`Build run started: ${run.id}`);
|
|
131
|
+
console.log(` Build: ${run.build_id}`);
|
|
132
|
+
console.log(` Status: ${run.status}`);
|
|
133
|
+
if (run.backend) {
|
|
134
|
+
console.log(` Backend: ${run.backend}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function handleRuns(positionals, flags, context, json) {
|
|
138
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
139
|
+
if (!buildId) {
|
|
140
|
+
throw new Error('Usage: eve build runs <build_id>');
|
|
141
|
+
}
|
|
142
|
+
const query = buildQuery({
|
|
143
|
+
limit: (0, args_1.getStringFlag)(flags, ['limit']),
|
|
144
|
+
offset: (0, args_1.getStringFlag)(flags, ['offset']),
|
|
145
|
+
});
|
|
146
|
+
const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs${query}`);
|
|
147
|
+
if (json) {
|
|
148
|
+
(0, output_1.outputJson)(response, json);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (response.data.length === 0) {
|
|
152
|
+
console.log('No runs found for this build.');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
formatRunList(response.data);
|
|
156
|
+
}
|
|
157
|
+
async function handleLogs(positionals, flags, context, json) {
|
|
158
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
159
|
+
const runId = (0, args_1.getStringFlag)(flags, ['run']);
|
|
160
|
+
if (!buildId) {
|
|
161
|
+
throw new Error('Usage: eve build logs <build_id> [--run <run_id>]');
|
|
162
|
+
}
|
|
163
|
+
const query = runId ? `?run_id=${encodeURIComponent(runId)}` : '';
|
|
164
|
+
const logs = await (0, client_1.requestJson)(context, `/builds/${buildId}/logs${query}`);
|
|
165
|
+
if (json) {
|
|
166
|
+
(0, output_1.outputJson)(logs, json);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (logs.logs.length === 0) {
|
|
170
|
+
console.log('No logs available.');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const entry of logs.logs) {
|
|
174
|
+
const line = entry.line;
|
|
175
|
+
if (typeof line.message === 'string') {
|
|
176
|
+
console.log(line.message);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(line.lines)) {
|
|
180
|
+
for (const item of line.lines) {
|
|
181
|
+
if (typeof item === 'string') {
|
|
182
|
+
console.log(item);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
console.log(JSON.stringify(line));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function handleArtifacts(positionals, flags, context, json) {
|
|
191
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
192
|
+
if (!buildId) {
|
|
193
|
+
throw new Error('Usage: eve build artifacts <build_id>');
|
|
194
|
+
}
|
|
195
|
+
const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
|
|
196
|
+
if (json) {
|
|
197
|
+
(0, output_1.outputJson)(response, json);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (response.data.length === 0) {
|
|
201
|
+
console.log('No artifacts found for this build.');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
formatArtifactList(response.data);
|
|
205
|
+
}
|
|
206
|
+
async function handleCancel(positionals, flags, context, json) {
|
|
207
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
208
|
+
if (!buildId) {
|
|
209
|
+
throw new Error('Usage: eve build cancel <build_id>');
|
|
210
|
+
}
|
|
211
|
+
const result = await (0, client_1.requestJson)(context, `/builds/${buildId}/cancel`, { method: 'POST' });
|
|
212
|
+
if (json) {
|
|
213
|
+
(0, output_1.outputJson)(result, json);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(`Build cancelled: ${buildId}`);
|
|
217
|
+
}
|
|
218
|
+
async function handleDiagnose(positionals, flags, context, json) {
|
|
219
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
220
|
+
if (!buildId) {
|
|
221
|
+
throw new Error('Usage: eve build diagnose <build_id>');
|
|
222
|
+
}
|
|
223
|
+
const spec = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
|
|
224
|
+
const runs = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs?limit=20&offset=0`);
|
|
225
|
+
const artifacts = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
|
|
226
|
+
const latestRun = runs.data[0];
|
|
227
|
+
const logs = latestRun
|
|
228
|
+
? await (0, client_1.requestJson)(context, `/builds/${buildId}/logs?run_id=${encodeURIComponent(latestRun.id)}`)
|
|
229
|
+
: { logs: [] };
|
|
230
|
+
const payload = { spec, runs: runs.data, artifacts: artifacts.data, logs: logs.logs };
|
|
231
|
+
if (json) {
|
|
232
|
+
(0, output_1.outputJson)(payload, json);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
console.log(`Build diagnose: ${spec.id}`);
|
|
236
|
+
formatBuildDetail(spec);
|
|
237
|
+
console.log('');
|
|
238
|
+
if (runs.data.length > 0) {
|
|
239
|
+
formatRunList(runs.data);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
console.log('No runs found.');
|
|
243
|
+
}
|
|
244
|
+
console.log('');
|
|
245
|
+
if (artifacts.data.length > 0) {
|
|
246
|
+
formatArtifactList(artifacts.data);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
console.log('No artifacts found.');
|
|
250
|
+
}
|
|
251
|
+
console.log('');
|
|
252
|
+
if (logs.logs.length > 0) {
|
|
253
|
+
console.log('Recent logs:');
|
|
254
|
+
for (const entry of logs.logs.slice(-50)) {
|
|
255
|
+
const line = entry.line;
|
|
256
|
+
if (typeof line.message === 'string') {
|
|
257
|
+
console.log(line.message);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (Array.isArray(line.lines)) {
|
|
261
|
+
for (const item of line.lines) {
|
|
262
|
+
if (typeof item === 'string') {
|
|
263
|
+
console.log(item);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
console.log(JSON.stringify(line));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
console.log('No logs found.');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Formatters
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
function formatBuildList(builds) {
|
|
279
|
+
console.log('Build ID'.padEnd(30) + 'SHA'.padEnd(12) + 'Created By'.padEnd(20) + 'Created');
|
|
280
|
+
console.log('-'.repeat(90));
|
|
281
|
+
for (const build of builds) {
|
|
282
|
+
const id = build.id.padEnd(30);
|
|
283
|
+
const sha = build.git_sha.substring(0, 8).padEnd(12);
|
|
284
|
+
const creator = (build.created_by ?? '-').padEnd(20);
|
|
285
|
+
const created = new Date(build.created_at).toLocaleString();
|
|
286
|
+
console.log(`${id}${sha}${creator}${created}`);
|
|
287
|
+
}
|
|
288
|
+
console.log('');
|
|
289
|
+
console.log(`Total: ${builds.length} builds`);
|
|
290
|
+
}
|
|
291
|
+
function formatBuildDetail(build) {
|
|
292
|
+
console.log(`Build: ${build.id}`);
|
|
293
|
+
console.log(` Project: ${build.project_id}`);
|
|
294
|
+
console.log(` SHA: ${build.git_sha}`);
|
|
295
|
+
console.log(` Manifest Hash: ${build.manifest_hash}`);
|
|
296
|
+
console.log(` Created By: ${build.created_by ?? '-'}`);
|
|
297
|
+
console.log(` Created: ${build.created_at}`);
|
|
298
|
+
if (build.services_json) {
|
|
299
|
+
console.log(` Services: ${JSON.stringify(build.services_json)}`);
|
|
300
|
+
}
|
|
301
|
+
if (build.inputs_json) {
|
|
302
|
+
console.log(` Inputs: ${JSON.stringify(build.inputs_json)}`);
|
|
303
|
+
}
|
|
304
|
+
if (build.registry_json) {
|
|
305
|
+
console.log(` Registry: ${JSON.stringify(build.registry_json)}`);
|
|
306
|
+
}
|
|
307
|
+
if (build.cache_json) {
|
|
308
|
+
console.log(` Cache: ${JSON.stringify(build.cache_json)}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function formatRunList(runs) {
|
|
312
|
+
console.log('Run ID'.padEnd(30) + 'Status'.padEnd(15) + 'Backend'.padEnd(15) + 'Started');
|
|
313
|
+
console.log('-'.repeat(80));
|
|
314
|
+
for (const run of runs) {
|
|
315
|
+
const id = run.id.padEnd(30);
|
|
316
|
+
const status = run.status.padEnd(15);
|
|
317
|
+
const backend = (run.backend ?? '-').padEnd(15);
|
|
318
|
+
const started = run.started_at ? new Date(run.started_at).toLocaleString() : '-';
|
|
319
|
+
console.log(`${id}${status}${backend}${started}`);
|
|
320
|
+
if (run.error_message) {
|
|
321
|
+
console.log(` Error: ${run.error_message}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
console.log('');
|
|
325
|
+
console.log(`Total: ${runs.length} runs`);
|
|
326
|
+
}
|
|
327
|
+
function formatArtifactList(artifacts) {
|
|
328
|
+
console.log('Service'.padEnd(25) + 'Image'.padEnd(50) + 'Size');
|
|
329
|
+
console.log('-'.repeat(90));
|
|
330
|
+
for (const artifact of artifacts) {
|
|
331
|
+
const service = artifact.service_name.padEnd(25);
|
|
332
|
+
const image = artifact.image_ref.padEnd(50);
|
|
333
|
+
const size = artifact.size_bytes ? formatBytes(artifact.size_bytes) : '-';
|
|
334
|
+
console.log(`${service}${image}${size}`);
|
|
335
|
+
if (artifact.digest) {
|
|
336
|
+
console.log(` Digest: ${artifact.digest}`);
|
|
337
|
+
}
|
|
338
|
+
if (artifact.platforms_json && artifact.platforms_json.length > 0) {
|
|
339
|
+
console.log(` Platforms: ${artifact.platforms_json.join(', ')}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
console.log('');
|
|
343
|
+
console.log(`Total: ${artifacts.length} artifacts`);
|
|
344
|
+
}
|
|
345
|
+
function formatBytes(bytes) {
|
|
346
|
+
if (bytes < 1024)
|
|
347
|
+
return `${bytes} B`;
|
|
348
|
+
if (bytes < 1024 * 1024)
|
|
349
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
350
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
351
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
352
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
353
|
+
}
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
// Utilities
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
function buildQuery(params) {
|
|
358
|
+
const search = new URLSearchParams();
|
|
359
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
360
|
+
if (value === undefined || value === '')
|
|
361
|
+
return;
|
|
362
|
+
search.set(key, value);
|
|
363
|
+
});
|
|
364
|
+
const query = search.toString();
|
|
365
|
+
return query ? `?${query}` : '';
|
|
366
|
+
}
|
package/dist/commands/env.js
CHANGED
|
@@ -161,7 +161,7 @@ async function handleCreate(positionals, flags, context, json) {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
|
-
* eve env deploy [project] <name> --ref <sha> [--direct] [--inputs <json>]
|
|
164
|
+
* eve env deploy [project] <name> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>]
|
|
165
165
|
* Deploy to an environment
|
|
166
166
|
* If project is in profile, can use: eve env deploy <name> --ref <sha>
|
|
167
167
|
*/
|
|
@@ -194,13 +194,28 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
194
194
|
envName = flagName;
|
|
195
195
|
}
|
|
196
196
|
if (!projectId || !envName) {
|
|
197
|
-
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project=<id>]');
|
|
197
|
+
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--project=<id>]');
|
|
198
198
|
}
|
|
199
199
|
// --ref flag is now required
|
|
200
|
-
const
|
|
201
|
-
if (!
|
|
200
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
201
|
+
if (!ref) {
|
|
202
202
|
throw new Error('Usage: eve env deploy <env> --ref <sha> [options]\n\nThe --ref flag is required (git SHA or commit reference)');
|
|
203
203
|
}
|
|
204
|
+
// Resolve git ref to actual 40-char SHA
|
|
205
|
+
let gitSha;
|
|
206
|
+
try {
|
|
207
|
+
gitSha = (0, child_process_1.execSync)(`git rev-parse ${ref}`, {
|
|
208
|
+
encoding: 'utf-8',
|
|
209
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
210
|
+
}).trim();
|
|
211
|
+
if (!json) {
|
|
212
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
217
|
+
'Make sure you are in a git repository and the ref exists.');
|
|
218
|
+
}
|
|
204
219
|
if (!json) {
|
|
205
220
|
console.log(`Deploying commit ${gitSha.substring(0, 8)} to ${envName}...`);
|
|
206
221
|
}
|
|
@@ -226,6 +241,8 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
226
241
|
'Example: --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'');
|
|
227
242
|
}
|
|
228
243
|
}
|
|
244
|
+
// Parse --image-tag flag (optional)
|
|
245
|
+
const imageTag = (0, args_1.getStringFlag)(flags, ['image-tag', 'image_tag', 'imageTag']);
|
|
229
246
|
// POST deployment
|
|
230
247
|
const body = {
|
|
231
248
|
git_sha: gitSha,
|
|
@@ -237,6 +254,9 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
237
254
|
if (inputs) {
|
|
238
255
|
body.inputs = inputs;
|
|
239
256
|
}
|
|
257
|
+
if (imageTag) {
|
|
258
|
+
body.image_tag = imageTag;
|
|
259
|
+
}
|
|
240
260
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/deploy`, {
|
|
241
261
|
method: 'POST',
|
|
242
262
|
body,
|
package/dist/commands/org.js
CHANGED
|
@@ -12,6 +12,7 @@ async function handleOrg(subcommand, positionals, flags, context) {
|
|
|
12
12
|
case 'ensure': {
|
|
13
13
|
let orgId = typeof flags.id === 'string' ? flags.id : '';
|
|
14
14
|
let orgName = typeof flags.name === 'string' ? flags.name : '';
|
|
15
|
+
let orgSlug = typeof flags.slug === 'string' ? flags.slug : '';
|
|
15
16
|
const nameOrId = positionals[0];
|
|
16
17
|
if (!orgId && nameOrId) {
|
|
17
18
|
if (/^org_[a-zA-Z0-9]+$/.test(nameOrId)) {
|
|
@@ -25,7 +26,7 @@ async function handleOrg(subcommand, positionals, flags, context) {
|
|
|
25
26
|
}
|
|
26
27
|
orgId = orgId || context.orgId || DEFAULT_ORG_ID;
|
|
27
28
|
orgName = orgName || DEFAULT_ORG_NAME;
|
|
28
|
-
const body = { id: orgId, name: orgName };
|
|
29
|
+
const body = { id: orgId, name: orgName, ...(orgSlug ? { slug: orgSlug } : {}) };
|
|
29
30
|
const org = await (0, client_1.requestJson)(context, '/orgs/ensure', {
|
|
30
31
|
method: 'POST',
|
|
31
32
|
body,
|
|
@@ -4,6 +4,7 @@ exports.handlePipeline = handlePipeline;
|
|
|
4
4
|
const args_1 = require("../lib/args");
|
|
5
5
|
const client_1 = require("../lib/client");
|
|
6
6
|
const output_1 = require("../lib/output");
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
8
|
async function handlePipeline(subcommand, positionals, flags, context) {
|
|
8
9
|
const json = Boolean(flags.json);
|
|
9
10
|
switch (subcommand) {
|
|
@@ -165,6 +166,21 @@ async function handleRun(positionals, flags, context, json) {
|
|
|
165
166
|
if (!pipelineName || !projectId || !ref) {
|
|
166
167
|
throw new Error('Usage: eve pipeline run <name> --ref <sha> [--env <env>] [--project <id>] [--wait] [--inputs <json>] [--only <step>]');
|
|
167
168
|
}
|
|
169
|
+
// Resolve git ref to actual 40-char SHA
|
|
170
|
+
let gitSha;
|
|
171
|
+
try {
|
|
172
|
+
gitSha = (0, node_child_process_1.execSync)(`git rev-parse ${ref}`, {
|
|
173
|
+
encoding: 'utf-8',
|
|
174
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
175
|
+
}).trim();
|
|
176
|
+
if (!json) {
|
|
177
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
182
|
+
'Make sure you are in a git repository and the ref exists.');
|
|
183
|
+
}
|
|
168
184
|
let inputs;
|
|
169
185
|
if (inputsRaw) {
|
|
170
186
|
try {
|
|
@@ -175,11 +191,11 @@ async function handleRun(positionals, flags, context, json) {
|
|
|
175
191
|
}
|
|
176
192
|
}
|
|
177
193
|
// Emit event before creating the pipeline run
|
|
178
|
-
await emitPipelineRunEvent(context, projectId, pipelineName, { env, ref, inputs });
|
|
194
|
+
await emitPipelineRunEvent(context, projectId, pipelineName, { env, ref: gitSha, inputs });
|
|
179
195
|
if (only) {
|
|
180
196
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/pipelines/${pipelineName}/runs`, {
|
|
181
197
|
method: 'POST',
|
|
182
|
-
body: { git_sha:
|
|
198
|
+
body: { git_sha: gitSha, env_name: env, inputs, only },
|
|
183
199
|
});
|
|
184
200
|
if (json) {
|
|
185
201
|
(0, output_1.outputJson)(response, json);
|
|
@@ -203,7 +219,7 @@ async function handleRun(positionals, flags, context, json) {
|
|
|
203
219
|
const url = `/projects/${projectId}/pipelines/${pipelineName}/run${queryString ? `?${queryString}` : ''}`;
|
|
204
220
|
const response = await (0, client_1.requestJson)(context, url, {
|
|
205
221
|
method: 'POST',
|
|
206
|
-
body: { ref, env, inputs },
|
|
222
|
+
body: { ref: gitSha, env, inputs },
|
|
207
223
|
});
|
|
208
224
|
if (json) {
|
|
209
225
|
(0, output_1.outputJson)(response, json);
|
package/dist/commands/profile.js
CHANGED
|
@@ -64,10 +64,10 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
case 'use': {
|
|
67
|
-
const
|
|
67
|
+
const isGlobal = Boolean(flags.global);
|
|
68
68
|
const isClear = Boolean(flags.clear);
|
|
69
|
-
// Handle --
|
|
70
|
-
if (
|
|
69
|
+
// Handle --clear: remove local profile
|
|
70
|
+
if (isClear) {
|
|
71
71
|
const removed = (0, context_1.removeRepoProfile)();
|
|
72
72
|
if (removed) {
|
|
73
73
|
(0, output_1.outputJson)({ cleared: true, path: (0, context_1.getRepoProfilePath)() }, json, `✓ Removed local profile`);
|
|
@@ -79,15 +79,21 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
79
79
|
}
|
|
80
80
|
const name = positionals[0];
|
|
81
81
|
if (!name) {
|
|
82
|
-
throw new Error('Usage: eve profile use <name> [--
|
|
82
|
+
throw new Error('Usage: eve profile use <name> [--global]');
|
|
83
83
|
}
|
|
84
|
-
// Validate profile exists in global config
|
|
84
|
+
// Validate profile exists in global config (auto-create if needed)
|
|
85
85
|
if (!config.profiles[name]) {
|
|
86
86
|
config.profiles[name] = {};
|
|
87
87
|
(0, config_1.saveConfig)(config);
|
|
88
88
|
}
|
|
89
|
-
if (
|
|
90
|
-
//
|
|
89
|
+
if (isGlobal) {
|
|
90
|
+
// Explicit: set global active profile in ~/.eve/config.json
|
|
91
|
+
config.active_profile = name;
|
|
92
|
+
(0, config_1.saveConfig)(config);
|
|
93
|
+
(0, output_1.outputJson)({ active_profile: name }, json, `✓ Active profile: ${name} (global)`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Default: write to .eve/profile.yaml (local project mode)
|
|
91
97
|
const repoProfile = { profile: name };
|
|
92
98
|
// Apply any override flags
|
|
93
99
|
if (typeof flags['api-url'] === 'string')
|
|
@@ -99,12 +105,6 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
99
105
|
(0, context_1.saveRepoProfile)(repoProfile);
|
|
100
106
|
(0, output_1.outputJson)({ profile: name, local: true, path: (0, context_1.getRepoProfilePath)(), ...repoProfile }, json, `✓ Local profile set: ${name} (${(0, context_1.getRepoProfilePath)()})`);
|
|
101
107
|
}
|
|
102
|
-
else {
|
|
103
|
-
// Set global active profile
|
|
104
|
-
config.active_profile = name;
|
|
105
|
-
(0, config_1.saveConfig)(config);
|
|
106
|
-
(0, output_1.outputJson)({ active_profile: name }, json, `✓ Active profile: ${name}`);
|
|
107
|
-
}
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
case 'create': {
|
|
@@ -121,13 +121,42 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
case 'set': {
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
126
|
-
config.
|
|
124
|
+
const isGlobal = Boolean(flags.global);
|
|
125
|
+
if (isGlobal) {
|
|
126
|
+
// Explicit: write to global ~/.eve/config.json
|
|
127
|
+
const name = positionals[0] ?? config.active_profile;
|
|
128
|
+
if (!config.profiles[name]) {
|
|
129
|
+
config.profiles[name] = {};
|
|
130
|
+
}
|
|
131
|
+
config.profiles[name] = applyProfileFlags(config.profiles[name], flags);
|
|
132
|
+
config.active_profile = name;
|
|
133
|
+
(0, config_1.saveConfig)(config);
|
|
134
|
+
(0, output_1.outputJson)({ active_profile: name, ...config.profiles[name] }, json, `✓ Profile set: ${name} (global)`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Default: write to local .eve/profile.yaml
|
|
138
|
+
const globalOnlyFields = ['supabase-url', 'supabase-anon-key', 'default-email', 'default-ssh-key'];
|
|
139
|
+
const usedGlobalFields = globalOnlyFields.filter(f => typeof flags[f] === 'string');
|
|
140
|
+
if (usedGlobalFields.length > 0) {
|
|
141
|
+
throw new Error(`${usedGlobalFields.map(f => `--${f}`).join(', ')} are user-level settings.\n` +
|
|
142
|
+
`Use --global to write them to ~/.eve/config.json`);
|
|
143
|
+
}
|
|
144
|
+
const existing = (0, context_1.loadRepoProfile)() ?? {};
|
|
145
|
+
const repoProfile = { ...existing };
|
|
146
|
+
const name = positionals[0];
|
|
147
|
+
if (name)
|
|
148
|
+
repoProfile.profile = name;
|
|
149
|
+
if (typeof flags['api-url'] === 'string')
|
|
150
|
+
repoProfile.api_url = flags['api-url'];
|
|
151
|
+
if (typeof flags.org === 'string')
|
|
152
|
+
repoProfile.org_id = flags.org;
|
|
153
|
+
if (typeof flags.project === 'string')
|
|
154
|
+
repoProfile.project_id = flags.project;
|
|
155
|
+
if (typeof flags.harness === 'string')
|
|
156
|
+
repoProfile.default_harness = flags.harness;
|
|
157
|
+
(0, context_1.saveRepoProfile)(repoProfile);
|
|
158
|
+
(0, output_1.outputJson)({ ...repoProfile, path: (0, context_1.getRepoProfilePath)() }, json, `✓ Profile set: ${repoProfile.profile ?? 'local'} (${(0, context_1.getRepoProfilePath)()})`);
|
|
127
159
|
}
|
|
128
|
-
config.profiles[name] = applyProfileFlags(config.profiles[name], flags);
|
|
129
|
-
(0, config_1.saveConfig)(config);
|
|
130
|
-
(0, output_1.outputJson)({ name, ...config.profiles[name] }, json, `✓ Profile updated: ${name}`);
|
|
131
160
|
return;
|
|
132
161
|
}
|
|
133
162
|
case 'remove': {
|
package/dist/commands/system.js
CHANGED
|
@@ -34,8 +34,10 @@ async function handleSystem(subcommand, positionals, flags, context) {
|
|
|
34
34
|
return handleConfig(context, json);
|
|
35
35
|
case 'settings':
|
|
36
36
|
return handleSettings(positionals, flags, context, json);
|
|
37
|
+
case 'orchestrator':
|
|
38
|
+
return handleOrchestrator(positionals, flags, context, json);
|
|
37
39
|
default:
|
|
38
|
-
throw new Error('Usage: eve system <status|health|jobs|envs|logs|pods|events|config|settings>');
|
|
40
|
+
throw new Error('Usage: eve system <status|health|jobs|envs|logs|pods|events|config|settings|orchestrator>');
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
// ============================================================================
|
|
@@ -61,10 +63,10 @@ async function handleStatus(context, json) {
|
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
catch (error) {
|
|
64
|
-
// If /system/status
|
|
66
|
+
// If /system/status is missing, fall back to basic health check
|
|
65
67
|
const err = error;
|
|
66
68
|
if (err.message?.includes('HTTP 404')) {
|
|
67
|
-
console.log('Note:
|
|
69
|
+
console.log('Note: /system/status not available on this API version.');
|
|
68
70
|
console.log('Falling back to basic health check...');
|
|
69
71
|
console.log('');
|
|
70
72
|
return handleHealth(context, json);
|
|
@@ -318,6 +320,47 @@ async function handleConfig(context, json) {
|
|
|
318
320
|
response.deployments.forEach((deployment) => console.log(` - ${deployment}`));
|
|
319
321
|
}
|
|
320
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* eve system orchestrator <status|set-concurrency>
|
|
325
|
+
* Manage orchestrator concurrency settings
|
|
326
|
+
*/
|
|
327
|
+
async function handleOrchestrator(positionals, flags, context, json) {
|
|
328
|
+
const subcommand = positionals[0];
|
|
329
|
+
if (subcommand === 'status') {
|
|
330
|
+
const response = await requestOrchestratorJson(context, '/system/orchestrator/status');
|
|
331
|
+
if (json) {
|
|
332
|
+
(0, output_1.outputJson)(response, json);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
formatOrchestratorStatus(response);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (subcommand === 'set-concurrency') {
|
|
340
|
+
const limitStr = positionals[1];
|
|
341
|
+
if (!limitStr) {
|
|
342
|
+
throw new Error('Usage: eve system orchestrator set-concurrency <n>');
|
|
343
|
+
}
|
|
344
|
+
const limit = parseInt(limitStr, 10);
|
|
345
|
+
if (isNaN(limit) || limit < 1) {
|
|
346
|
+
throw new Error('Concurrency limit must be a positive integer');
|
|
347
|
+
}
|
|
348
|
+
const response = await requestOrchestratorJson(context, '/system/orchestrator/concurrency', {
|
|
349
|
+
method: 'POST',
|
|
350
|
+
body: JSON.stringify({ limit }),
|
|
351
|
+
});
|
|
352
|
+
if (json) {
|
|
353
|
+
(0, output_1.outputJson)(response, json);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
console.log(`Concurrency limit updated to ${limit}`);
|
|
357
|
+
console.log('');
|
|
358
|
+
formatOrchestratorStatus(response);
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
throw new Error('Usage: eve system orchestrator <status|set-concurrency>');
|
|
363
|
+
}
|
|
321
364
|
/**
|
|
322
365
|
* eve system settings [get <key>] [set <key> <value>]
|
|
323
366
|
* Admin only: Get or set system settings
|
|
@@ -526,3 +569,81 @@ function padRight(str, width) {
|
|
|
526
569
|
return str;
|
|
527
570
|
return str + ' '.repeat(width - str.length);
|
|
528
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Get orchestrator URL from context.
|
|
574
|
+
* Derives from API URL by replacing the port with the orchestrator port.
|
|
575
|
+
*/
|
|
576
|
+
function getOrchestratorUrl(context) {
|
|
577
|
+
const envUrl = process.env.EVE_ORCHESTRATOR_URL;
|
|
578
|
+
if (envUrl) {
|
|
579
|
+
return envUrl;
|
|
580
|
+
}
|
|
581
|
+
// Derive from API URL by changing the port
|
|
582
|
+
const orchPort = process.env.EVE_ORCHESTRATOR_PORT || '4802';
|
|
583
|
+
const apiUrl = new URL(context.apiUrl);
|
|
584
|
+
apiUrl.port = orchPort;
|
|
585
|
+
return apiUrl.toString().replace(/\/$/, '');
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Request JSON from the orchestrator service.
|
|
589
|
+
* Similar to requestJson but targets the orchestrator directly.
|
|
590
|
+
*/
|
|
591
|
+
async function requestOrchestratorJson(context, path, options) {
|
|
592
|
+
const orchUrl = getOrchestratorUrl(context);
|
|
593
|
+
const url = `${orchUrl}${path}`;
|
|
594
|
+
const headers = {};
|
|
595
|
+
if (options?.body) {
|
|
596
|
+
headers['Content-Type'] = 'application/json';
|
|
597
|
+
}
|
|
598
|
+
if (context.token) {
|
|
599
|
+
headers.Authorization = `Bearer ${context.token}`;
|
|
600
|
+
}
|
|
601
|
+
const response = await fetch(url, {
|
|
602
|
+
method: options?.method ?? 'GET',
|
|
603
|
+
headers,
|
|
604
|
+
body: options?.body,
|
|
605
|
+
});
|
|
606
|
+
const text = await response.text();
|
|
607
|
+
let data = null;
|
|
608
|
+
if (text) {
|
|
609
|
+
try {
|
|
610
|
+
data = JSON.parse(text);
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
data = text;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (!response.ok) {
|
|
617
|
+
const message = typeof data === 'string' ? data : text;
|
|
618
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
619
|
+
}
|
|
620
|
+
return data;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Format orchestrator status for human-readable output
|
|
624
|
+
*/
|
|
625
|
+
function formatOrchestratorStatus(status) {
|
|
626
|
+
console.log('Orchestrator Concurrency Status');
|
|
627
|
+
console.log('═══════════════════════════════════════');
|
|
628
|
+
console.log('');
|
|
629
|
+
console.log(` Limit: ${status.limit}`);
|
|
630
|
+
console.log(` In-Flight: ${status.inFlight}`);
|
|
631
|
+
console.log(` Uptime: ${formatUptime(status.uptimeSeconds)}`);
|
|
632
|
+
console.log(` Last Change: ${status.lastChange}`);
|
|
633
|
+
console.log('');
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Format uptime seconds to human-readable format
|
|
637
|
+
*/
|
|
638
|
+
function formatUptime(seconds) {
|
|
639
|
+
const hours = Math.floor(seconds / 3600);
|
|
640
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
641
|
+
const secs = seconds % 60;
|
|
642
|
+
if (hours > 0) {
|
|
643
|
+
return `${hours}h ${minutes}m ${secs}s`;
|
|
644
|
+
}
|
|
645
|
+
if (minutes > 0) {
|
|
646
|
+
return `${minutes}m ${secs}s`;
|
|
647
|
+
}
|
|
648
|
+
return `${secs}s`;
|
|
649
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ const agents_1 = require("./commands/agents");
|
|
|
25
25
|
const init_1 = require("./commands/init");
|
|
26
26
|
const release_1 = require("./commands/release");
|
|
27
27
|
const manifest_1 = require("./commands/manifest");
|
|
28
|
+
const build_1 = require("./commands/build");
|
|
28
29
|
async function main() {
|
|
29
30
|
const { flags, positionals } = (0, args_1.parseArgs)(process.argv.slice(2));
|
|
30
31
|
const command = positionals[0];
|
|
@@ -111,6 +112,9 @@ async function main() {
|
|
|
111
112
|
case 'manifest':
|
|
112
113
|
await (0, manifest_1.handleManifest)(subcommand, rest, flags, context);
|
|
113
114
|
return;
|
|
115
|
+
case 'build':
|
|
116
|
+
await (0, build_1.handleBuild)(subcommand, rest, flags, context);
|
|
117
|
+
return;
|
|
114
118
|
default:
|
|
115
119
|
(0, help_1.showMainHelp)();
|
|
116
120
|
}
|
package/dist/lib/help.js
CHANGED
|
@@ -530,7 +530,11 @@ Jobs default to 'ready' phase, making them immediately schedulable.`,
|
|
|
530
530
|
},
|
|
531
531
|
profile: {
|
|
532
532
|
description: `Manage CLI profiles. Profiles store defaults (API URL, org, project) so you don't
|
|
533
|
-
have to specify them on every command.
|
|
533
|
+
have to specify them on every command.
|
|
534
|
+
|
|
535
|
+
By default, set and use write to .eve/profile.yaml (project-local). Use --global to
|
|
536
|
+
write to ~/.eve/config.json instead. This lets you work on multiple projects without
|
|
537
|
+
clobbering your global config.`,
|
|
534
538
|
usage: 'eve profile <subcommand> [options]',
|
|
535
539
|
subcommands: {
|
|
536
540
|
list: {
|
|
@@ -538,17 +542,28 @@ have to specify them on every command. Useful when working with multiple environ
|
|
|
538
542
|
usage: 'eve profile list',
|
|
539
543
|
},
|
|
540
544
|
show: {
|
|
541
|
-
description: 'Show profile details',
|
|
545
|
+
description: 'Show profile details (local overrides applied)',
|
|
542
546
|
usage: 'eve profile show [name]',
|
|
543
547
|
examples: ['eve profile show', 'eve profile show prod'],
|
|
544
548
|
},
|
|
545
549
|
use: {
|
|
546
|
-
description: 'Switch active profile',
|
|
547
|
-
usage: 'eve profile use <name>',
|
|
548
|
-
|
|
550
|
+
description: 'Switch active profile (defaults to local .eve/profile.yaml)',
|
|
551
|
+
usage: 'eve profile use <name> [--global] [--org <id>] [--project <id>]',
|
|
552
|
+
options: [
|
|
553
|
+
'--global Write to ~/.eve/config.json instead of local project',
|
|
554
|
+
'--org <id> Set org override',
|
|
555
|
+
'--project <id> Set project override',
|
|
556
|
+
'--api-url <url> Set API URL override',
|
|
557
|
+
'--clear Remove local .eve/profile.yaml',
|
|
558
|
+
],
|
|
559
|
+
examples: [
|
|
560
|
+
'eve profile use staging --org org_xxx --project proj_yyy',
|
|
561
|
+
'eve profile use staging --global',
|
|
562
|
+
'eve profile use --clear',
|
|
563
|
+
],
|
|
549
564
|
},
|
|
550
565
|
create: {
|
|
551
|
-
description: 'Create a new profile',
|
|
566
|
+
description: 'Create a new named profile (always global)',
|
|
552
567
|
usage: 'eve profile create <name> [--api-url <url>] [--org <id>] [--project <id>]',
|
|
553
568
|
options: [
|
|
554
569
|
'--api-url <url> API base URL',
|
|
@@ -566,13 +581,16 @@ have to specify them on every command. Useful when working with multiple environ
|
|
|
566
581
|
],
|
|
567
582
|
},
|
|
568
583
|
set: {
|
|
569
|
-
description: 'Update profile settings',
|
|
570
|
-
usage: 'eve profile set [name] [--
|
|
584
|
+
description: 'Update profile settings (defaults to local .eve/profile.yaml)',
|
|
585
|
+
usage: 'eve profile set [name] [--org <id>] [--project <id>] [--global]',
|
|
571
586
|
options: [
|
|
572
|
-
'--api-url <url> API base URL',
|
|
573
587
|
'--org <id> Default organization ID',
|
|
574
588
|
'--project <id> Default project ID',
|
|
589
|
+
'--api-url <url> API base URL',
|
|
575
590
|
'--harness <name> Default harness',
|
|
591
|
+
'--global Write to ~/.eve/config.json instead of local project',
|
|
592
|
+
'',
|
|
593
|
+
'Global-only (require --global):',
|
|
576
594
|
'--supabase-url <url> Supabase URL',
|
|
577
595
|
'--supabase-anon-key <key> Supabase anon key',
|
|
578
596
|
'--default-email <email> Default email for auth login',
|
|
@@ -580,25 +598,25 @@ have to specify them on every command. Useful when working with multiple environ
|
|
|
580
598
|
],
|
|
581
599
|
examples: [
|
|
582
600
|
'eve profile set --org org_xxx --project proj_yyy',
|
|
583
|
-
'eve profile set
|
|
584
|
-
'eve profile set --default-email user@example.com
|
|
601
|
+
'eve profile set staging --org org_xxx --project proj_yyy',
|
|
602
|
+
'eve profile set --global --default-email user@example.com',
|
|
585
603
|
],
|
|
586
604
|
},
|
|
587
605
|
remove: {
|
|
588
|
-
description: 'Remove a profile',
|
|
606
|
+
description: 'Remove a named profile (global)',
|
|
589
607
|
usage: 'eve profile remove <name>',
|
|
590
608
|
},
|
|
591
609
|
},
|
|
592
610
|
examples: [
|
|
593
|
-
'eve profile
|
|
594
|
-
'eve profile use
|
|
595
|
-
'eve profile set --
|
|
611
|
+
'eve profile set --org org_xxx --project proj_yyy # writes .eve/profile.yaml',
|
|
612
|
+
'eve profile use staging --org org_xxx # writes .eve/profile.yaml',
|
|
613
|
+
'eve profile set --global --default-email me@dev.com # writes ~/.eve/config.json',
|
|
596
614
|
],
|
|
597
615
|
},
|
|
598
616
|
auth: {
|
|
599
617
|
description: `Authenticate with Eve Horizon. Auth is optional for local development but required
|
|
600
618
|
for cloud deployments. Credentials are stored per-profile.`,
|
|
601
|
-
usage: 'eve auth <login|logout|status|whoami|bootstrap|sync|creds|token>',
|
|
619
|
+
usage: 'eve auth <login|logout|status|whoami|bootstrap|sync|creds|token|mint>',
|
|
602
620
|
subcommands: {
|
|
603
621
|
login: {
|
|
604
622
|
description: 'Login via GitHub SSH challenge (default) or Supabase (legacy)',
|
|
@@ -643,6 +661,23 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
643
661
|
'eve auth token # Share with reviewers for PR preview access',
|
|
644
662
|
],
|
|
645
663
|
},
|
|
664
|
+
mint: {
|
|
665
|
+
description: 'Mint a user token (admin-only, no SSH login required)',
|
|
666
|
+
usage: 'eve auth mint --email <email> [--org <org_id> | --project <project_id>] [--role <role>] [--ttl <days>]',
|
|
667
|
+
options: [
|
|
668
|
+
'--email <email> Target user email (created if missing)',
|
|
669
|
+
'--org <org_id> Org scope for membership and permission checks',
|
|
670
|
+
'--project <id> Project scope for membership and permission checks',
|
|
671
|
+
'--role <role> Role to assign (member|admin), default member',
|
|
672
|
+
'--ttl <days> Token TTL in days (1-90, default: server configured)',
|
|
673
|
+
],
|
|
674
|
+
examples: [
|
|
675
|
+
'eve auth mint --email app-bot@example.com --org org_xxx',
|
|
676
|
+
'eve auth mint --email app-bot@example.com --project proj_xxx',
|
|
677
|
+
'eve auth mint --email app-bot@example.com --project proj_xxx --role admin',
|
|
678
|
+
'eve auth mint --email bot@example.com --org org_xxx --ttl 90',
|
|
679
|
+
],
|
|
680
|
+
},
|
|
646
681
|
bootstrap: {
|
|
647
682
|
description: 'Bootstrap the first admin user with flexible security modes',
|
|
648
683
|
usage: 'eve auth bootstrap --email <email> [--token <token>] [options]',
|
|
@@ -736,12 +771,13 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
736
771
|
},
|
|
737
772
|
deploy: {
|
|
738
773
|
description: 'Deploy to an environment',
|
|
739
|
-
usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project <id>]',
|
|
774
|
+
usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--project <id>]',
|
|
740
775
|
options: [
|
|
741
776
|
'<env> Environment name (staging, production, test)',
|
|
742
777
|
'--ref <sha> Git SHA or commit reference (required)',
|
|
743
778
|
'--direct Bypass pipeline and do direct deploy',
|
|
744
779
|
'--inputs <json> JSON inputs for the deployment (e.g., \'{"release_id":"rel_xxx"}\')',
|
|
780
|
+
'--image-tag <tag> Use a specific image tag for deploy (direct only)',
|
|
745
781
|
'--project <id> Project ID or slug (uses profile default if omitted)',
|
|
746
782
|
'--watch Poll deployment status until ready (default: true)',
|
|
747
783
|
'--timeout <seconds> Watch timeout in seconds (default: 120)',
|
|
@@ -751,6 +787,7 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
751
787
|
'eve env deploy staging --ref abc123 --direct',
|
|
752
788
|
'eve env deploy staging --ref abc123 --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'',
|
|
753
789
|
'eve env deploy staging --ref abc123 --direct --inputs \'{"release_id":"rel_xxx"}\'',
|
|
790
|
+
'eve env deploy staging --ref abc123 --direct --image-tag demo-abc123',
|
|
754
791
|
],
|
|
755
792
|
},
|
|
756
793
|
diagnose: {
|
|
@@ -1091,6 +1128,105 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
1091
1128
|
'eve release resolve v1.2.3 --json',
|
|
1092
1129
|
],
|
|
1093
1130
|
},
|
|
1131
|
+
build: {
|
|
1132
|
+
description: 'Manage builds. Builds are first-class primitives for container image creation (specs, runs, artifacts).',
|
|
1133
|
+
usage: 'eve build <subcommand> [options]',
|
|
1134
|
+
subcommands: {
|
|
1135
|
+
create: {
|
|
1136
|
+
description: 'Create a new build spec',
|
|
1137
|
+
usage: 'eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>]',
|
|
1138
|
+
options: [
|
|
1139
|
+
'--project <id> Project ID (uses profile default)',
|
|
1140
|
+
'--ref <sha> Git SHA or commit reference (required)',
|
|
1141
|
+
'--manifest-hash <h> Manifest hash (required)',
|
|
1142
|
+
'--services <list> Comma-separated service names to build',
|
|
1143
|
+
],
|
|
1144
|
+
examples: [
|
|
1145
|
+
'eve build create --ref abc123 --manifest-hash mfst_123',
|
|
1146
|
+
'eve build create --project proj_xxx --ref abc123 --manifest-hash mfst_123 --services api,web',
|
|
1147
|
+
],
|
|
1148
|
+
},
|
|
1149
|
+
list: {
|
|
1150
|
+
description: 'List build specs for a project',
|
|
1151
|
+
usage: 'eve build list [--project <id>] [--limit <n>] [--offset <n>]',
|
|
1152
|
+
options: [
|
|
1153
|
+
'--project <id> Project ID (uses profile default)',
|
|
1154
|
+
'--limit <n> Number of results',
|
|
1155
|
+
'--offset <n> Skip first n results',
|
|
1156
|
+
],
|
|
1157
|
+
examples: [
|
|
1158
|
+
'eve build list',
|
|
1159
|
+
'eve build list --project proj_xxx --limit 20',
|
|
1160
|
+
],
|
|
1161
|
+
},
|
|
1162
|
+
show: {
|
|
1163
|
+
description: 'Show build spec details',
|
|
1164
|
+
usage: 'eve build show <build_id>',
|
|
1165
|
+
examples: [
|
|
1166
|
+
'eve build show build_xxx',
|
|
1167
|
+
],
|
|
1168
|
+
},
|
|
1169
|
+
run: {
|
|
1170
|
+
description: 'Start a build run for an existing build spec',
|
|
1171
|
+
usage: 'eve build run <build_id>',
|
|
1172
|
+
examples: [
|
|
1173
|
+
'eve build run build_xxx',
|
|
1174
|
+
],
|
|
1175
|
+
},
|
|
1176
|
+
runs: {
|
|
1177
|
+
description: 'List runs for a build spec',
|
|
1178
|
+
usage: 'eve build runs <build_id> [--limit <n>]',
|
|
1179
|
+
options: [
|
|
1180
|
+
'--limit <n> Number of results',
|
|
1181
|
+
'--offset <n> Skip first n results',
|
|
1182
|
+
],
|
|
1183
|
+
examples: [
|
|
1184
|
+
'eve build runs build_xxx',
|
|
1185
|
+
],
|
|
1186
|
+
},
|
|
1187
|
+
logs: {
|
|
1188
|
+
description: 'Show build logs',
|
|
1189
|
+
usage: 'eve build logs <build_id> [--run <run_id>]',
|
|
1190
|
+
options: [
|
|
1191
|
+
'--run <id> Specific run ID (default: latest)',
|
|
1192
|
+
],
|
|
1193
|
+
examples: [
|
|
1194
|
+
'eve build logs build_xxx',
|
|
1195
|
+
'eve build logs build_xxx --run brun_yyy',
|
|
1196
|
+
],
|
|
1197
|
+
},
|
|
1198
|
+
artifacts: {
|
|
1199
|
+
description: 'List build artifacts (images produced)',
|
|
1200
|
+
usage: 'eve build artifacts <build_id>',
|
|
1201
|
+
examples: [
|
|
1202
|
+
'eve build artifacts build_xxx',
|
|
1203
|
+
],
|
|
1204
|
+
},
|
|
1205
|
+
diagnose: {
|
|
1206
|
+
description: 'Show full build state (spec, runs, artifacts, logs)',
|
|
1207
|
+
usage: 'eve build diagnose <build_id>',
|
|
1208
|
+
examples: [
|
|
1209
|
+
'eve build diagnose build_xxx',
|
|
1210
|
+
],
|
|
1211
|
+
},
|
|
1212
|
+
cancel: {
|
|
1213
|
+
description: 'Cancel an active build run',
|
|
1214
|
+
usage: 'eve build cancel <build_id>',
|
|
1215
|
+
examples: [
|
|
1216
|
+
'eve build cancel build_xxx',
|
|
1217
|
+
],
|
|
1218
|
+
},
|
|
1219
|
+
},
|
|
1220
|
+
examples: [
|
|
1221
|
+
'eve build create --ref abc123 --manifest-hash mfst_123 --services api,web',
|
|
1222
|
+
'eve build list',
|
|
1223
|
+
'eve build show build_xxx',
|
|
1224
|
+
'eve build run build_xxx',
|
|
1225
|
+
'eve build logs build_xxx',
|
|
1226
|
+
'eve build artifacts build_xxx',
|
|
1227
|
+
'eve build diagnose build_xxx',
|
|
1228
|
+
],
|
|
1229
|
+
},
|
|
1094
1230
|
init: {
|
|
1095
1231
|
description: `Initialize a new Eve Horizon project from a template.
|
|
1096
1232
|
|
|
@@ -1196,6 +1332,31 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1196
1332
|
usage: 'eve system config',
|
|
1197
1333
|
examples: ['eve system config'],
|
|
1198
1334
|
},
|
|
1335
|
+
settings: {
|
|
1336
|
+
description: 'Get or set system settings (admin only)',
|
|
1337
|
+
usage: 'eve system settings [get <key>] [set <key> <value>]',
|
|
1338
|
+
options: [
|
|
1339
|
+
'get <key> Get specific setting',
|
|
1340
|
+
'set <key> <value> Update setting value',
|
|
1341
|
+
],
|
|
1342
|
+
examples: [
|
|
1343
|
+
'eve system settings',
|
|
1344
|
+
'eve system settings get some-key',
|
|
1345
|
+
'eve system settings set some-key some-value',
|
|
1346
|
+
],
|
|
1347
|
+
},
|
|
1348
|
+
orchestrator: {
|
|
1349
|
+
description: 'Manage orchestrator concurrency settings',
|
|
1350
|
+
usage: 'eve system orchestrator <status|set-concurrency>',
|
|
1351
|
+
options: [
|
|
1352
|
+
'status Show concurrency status',
|
|
1353
|
+
'set-concurrency <n> Set concurrency limit',
|
|
1354
|
+
],
|
|
1355
|
+
examples: [
|
|
1356
|
+
'eve system orchestrator status',
|
|
1357
|
+
'eve system orchestrator set-concurrency 8',
|
|
1358
|
+
],
|
|
1359
|
+
},
|
|
1199
1360
|
},
|
|
1200
1361
|
examples: [
|
|
1201
1362
|
'eve system health',
|
|
@@ -1203,6 +1364,7 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1203
1364
|
'eve system jobs',
|
|
1204
1365
|
'eve system envs',
|
|
1205
1366
|
'eve system logs api',
|
|
1367
|
+
'eve system orchestrator status',
|
|
1206
1368
|
],
|
|
1207
1369
|
},
|
|
1208
1370
|
};
|
|
@@ -1220,6 +1382,7 @@ function showMainHelp() {
|
|
|
1220
1382
|
console.log(' manifest Validate manifests (schema, secrets)');
|
|
1221
1383
|
console.log(' job Manage jobs (create, list, show, update, claim, etc.)');
|
|
1222
1384
|
console.log(' env Manage environments (list, show, deploy)');
|
|
1385
|
+
console.log(' build Manage builds (create, run, logs, artifacts)');
|
|
1223
1386
|
console.log(' release Manage and inspect releases');
|
|
1224
1387
|
console.log(' api Explore API sources and call endpoints');
|
|
1225
1388
|
console.log(' db Inspect env DB schema, RLS, and SQL');
|