@eve-horizon/cli 0.1.3 → 0.1.4
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 +12 -1
- package/dist/commands/env.js +130 -36
- package/dist/commands/harness.js +16 -2
- package/dist/commands/job.js +22 -0
- package/dist/commands/org.js +9 -1
- package/dist/commands/pipeline.js +7 -0
- package/dist/commands/release.js +69 -0
- package/dist/index.js +4 -0
- package/dist/lib/help.js +100 -16
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -260,6 +260,17 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
260
260
|
(0, output_1.outputJson)({ profile: context.profileName, user_id: payload.user_id, token_type: payload.token_type }, json, `✓ Bootstrapped admin user (user_id: ${payload.user_id})`);
|
|
261
261
|
return;
|
|
262
262
|
}
|
|
263
|
+
case 'token': {
|
|
264
|
+
// Print the current access token to stdout for use in scripts
|
|
265
|
+
const tokenEntry = credentials.profiles[context.profileName];
|
|
266
|
+
if (!tokenEntry || !tokenEntry.access_token) {
|
|
267
|
+
console.error('No valid token found. Please login first with: eve auth login');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
// Print only the token to stdout, nothing else
|
|
271
|
+
console.log(tokenEntry.access_token);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
263
274
|
case 'sync': {
|
|
264
275
|
const claudeOnly = (0, args_1.getBooleanFlag)(flags, ['claude']) ?? false;
|
|
265
276
|
const codexOnly = (0, args_1.getBooleanFlag)(flags, ['codex']) ?? false;
|
|
@@ -384,7 +395,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
384
395
|
return;
|
|
385
396
|
}
|
|
386
397
|
default:
|
|
387
|
-
throw new Error('Usage: eve auth <login|logout|status|whoami|bootstrap|sync>');
|
|
398
|
+
throw new Error('Usage: eve auth <login|logout|status|whoami|bootstrap|sync|token>');
|
|
388
399
|
}
|
|
389
400
|
}
|
|
390
401
|
function signNonceWithSsh(keyPath, nonce) {
|
package/dist/commands/env.js
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.handleEnv = handleEnv;
|
|
4
37
|
const args_1 = require("../lib/args");
|
|
5
38
|
const client_1 = require("../lib/client");
|
|
6
39
|
const output_1 = require("../lib/output");
|
|
7
40
|
const child_process_1 = require("child_process");
|
|
41
|
+
const readline = __importStar(require("node:readline/promises"));
|
|
8
42
|
// ============================================================================
|
|
9
43
|
// Main Handler
|
|
10
44
|
// ============================================================================
|
|
@@ -21,13 +55,16 @@ async function handleEnv(subcommand, positionals, flags, context) {
|
|
|
21
55
|
return handleDeploy(positionals, flags, context, json);
|
|
22
56
|
case 'logs':
|
|
23
57
|
return handleLogs(positionals, flags, context, json);
|
|
58
|
+
case 'delete':
|
|
59
|
+
return handleDelete(positionals, flags, context, json);
|
|
24
60
|
default:
|
|
25
|
-
throw new Error('Usage: eve env <list|show|create|deploy|logs>\n' +
|
|
61
|
+
throw new Error('Usage: eve env <list|show|create|deploy|logs|delete>\n' +
|
|
26
62
|
' list [project] - list environments for a project\n' +
|
|
27
63
|
' show <project> <name> - show details of an environment\n' +
|
|
28
64
|
' create <name> --type=<type> [options] - create an environment\n' +
|
|
29
|
-
' deploy <
|
|
30
|
-
' logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] - get service logs'
|
|
65
|
+
' deploy <env> --ref <sha> [--direct] [--inputs <json>] - deploy to an environment\n' +
|
|
66
|
+
' logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] - get service logs\n' +
|
|
67
|
+
' delete <name> [--project=<id>] [--force] - delete an environment');
|
|
31
68
|
}
|
|
32
69
|
}
|
|
33
70
|
// ============================================================================
|
|
@@ -120,9 +157,9 @@ async function handleCreate(positionals, flags, context, json) {
|
|
|
120
157
|
}
|
|
121
158
|
}
|
|
122
159
|
/**
|
|
123
|
-
* eve env deploy [project] <name>
|
|
160
|
+
* eve env deploy [project] <name> --ref <sha> [--direct] [--inputs <json>]
|
|
124
161
|
* Deploy to an environment
|
|
125
|
-
* If project is in profile, can use: eve env deploy <name>
|
|
162
|
+
* If project is in profile, can use: eve env deploy <name> --ref <sha>
|
|
126
163
|
*/
|
|
127
164
|
async function handleDeploy(positionals, flags, context, json) {
|
|
128
165
|
// Smart positional parsing:
|
|
@@ -153,42 +190,48 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
153
190
|
envName = flagName;
|
|
154
191
|
}
|
|
155
192
|
if (!projectId || !envName) {
|
|
156
|
-
throw new Error('Usage: eve env deploy
|
|
157
|
-
}
|
|
158
|
-
//
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
193
|
+
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project=<id>]');
|
|
194
|
+
}
|
|
195
|
+
// --ref flag is now required
|
|
196
|
+
const gitSha = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
197
|
+
if (!gitSha) {
|
|
198
|
+
throw new Error('Usage: eve env deploy <env> --ref <sha> [options]\n\nThe --ref flag is required (git SHA or commit reference)');
|
|
199
|
+
}
|
|
200
|
+
if (!json) {
|
|
201
|
+
console.log(`Deploying commit ${gitSha.substring(0, 8)} to ${envName}...`);
|
|
202
|
+
}
|
|
203
|
+
// Get manifest hash from server
|
|
204
|
+
const manifest = await (0, client_1.requestJson)(context, `/projects/${projectId}/manifest`);
|
|
205
|
+
if (!json) {
|
|
206
|
+
console.log(`Using manifest ${manifest.manifest_hash.substring(0, 8)}...`);
|
|
207
|
+
}
|
|
208
|
+
// Parse --direct flag (optional boolean)
|
|
209
|
+
const direct = Boolean(flags.direct);
|
|
210
|
+
// Parse --inputs flag (optional JSON)
|
|
211
|
+
let inputs;
|
|
212
|
+
const inputsString = (0, args_1.getStringFlag)(flags, ['inputs']);
|
|
213
|
+
if (inputsString) {
|
|
214
|
+
try {
|
|
215
|
+
inputs = JSON.parse(inputsString);
|
|
216
|
+
if (typeof inputs !== 'object' || inputs === null || Array.isArray(inputs)) {
|
|
217
|
+
throw new Error('Inputs must be a JSON object');
|
|
218
|
+
}
|
|
166
219
|
}
|
|
167
|
-
|
|
168
|
-
|
|
220
|
+
catch (error) {
|
|
221
|
+
throw new Error(`Failed to parse --inputs JSON: ${error instanceof Error ? error.message : String(error)}\n` +
|
|
222
|
+
'Example: --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'');
|
|
169
223
|
}
|
|
170
|
-
// Get manifest hash from server
|
|
171
|
-
manifest = await (0, client_1.requestJson)(context, `/projects/${projectId}/manifest`);
|
|
172
|
-
if (!json) {
|
|
173
|
-
console.log(`Using manifest ${manifest.manifest_hash.substring(0, 8)}...`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
else if (!json) {
|
|
177
|
-
console.log(`Deploying release tag ${releaseTag} to ${envName}...`);
|
|
178
224
|
}
|
|
179
|
-
// Get optional image tag for local testing
|
|
180
|
-
const imageTag = (0, args_1.getStringFlag)(flags, ['tag']);
|
|
181
225
|
// POST deployment
|
|
182
|
-
const body = {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
body.
|
|
188
|
-
body.manifest_hash = manifest.manifest_hash;
|
|
226
|
+
const body = {
|
|
227
|
+
git_sha: gitSha,
|
|
228
|
+
manifest_hash: manifest.manifest_hash,
|
|
229
|
+
};
|
|
230
|
+
if (direct) {
|
|
231
|
+
body.direct = true;
|
|
189
232
|
}
|
|
190
|
-
if (
|
|
191
|
-
body.
|
|
233
|
+
if (inputs) {
|
|
234
|
+
body.inputs = inputs;
|
|
192
235
|
}
|
|
193
236
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/deploy`, {
|
|
194
237
|
method: 'POST',
|
|
@@ -234,6 +277,57 @@ async function handleLogs(positionals, flags, context, json) {
|
|
|
234
277
|
console.log(`[${entry.timestamp}] ${entry.line}`);
|
|
235
278
|
}
|
|
236
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* eve env delete <name> [--project=<id>] [--force]
|
|
282
|
+
* Delete an environment
|
|
283
|
+
*/
|
|
284
|
+
async function handleDelete(positionals, flags, context, json) {
|
|
285
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
286
|
+
const envName = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
287
|
+
const force = Boolean(flags.force);
|
|
288
|
+
if (!projectId || !envName) {
|
|
289
|
+
throw new Error('Usage: eve env delete <name> [--project=<id>] [--force]');
|
|
290
|
+
}
|
|
291
|
+
// Prompt for confirmation unless --force is set
|
|
292
|
+
if (!force) {
|
|
293
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
294
|
+
try {
|
|
295
|
+
const answer = await rl.question(`Are you sure you want to delete environment "${envName}"? [y/N]: `);
|
|
296
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
297
|
+
console.log('Deletion cancelled.');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
rl.close();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}`, {
|
|
307
|
+
method: 'DELETE',
|
|
308
|
+
});
|
|
309
|
+
if (json) {
|
|
310
|
+
(0, output_1.outputJson)({ success: true, environment: envName }, json);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
console.log(`Environment "${envName}" deleted successfully.`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
const err = error;
|
|
318
|
+
const message = err.message;
|
|
319
|
+
// Handle specific error cases
|
|
320
|
+
if (message.includes('404')) {
|
|
321
|
+
throw new Error(`Environment "${envName}" not found in project ${projectId}`);
|
|
322
|
+
}
|
|
323
|
+
else if (message.includes('409')) {
|
|
324
|
+
throw new Error(`Cannot delete environment "${envName}": environment has active deployments or resources`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
237
331
|
// ============================================================================
|
|
238
332
|
// Helper Functions
|
|
239
333
|
// ============================================================================
|
package/dist/commands/harness.js
CHANGED
|
@@ -7,9 +7,14 @@ const output_1 = require("../lib/output");
|
|
|
7
7
|
async function handleHarness(subcommand, positionals, flags, context) {
|
|
8
8
|
const json = Boolean(flags.json);
|
|
9
9
|
const includeCapabilities = (0, args_1.getBooleanFlag)(flags, ['capabilities']) ?? false;
|
|
10
|
+
const orgId = (0, args_1.getStringFlag)(flags, ['org']);
|
|
11
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']);
|
|
12
|
+
// Build query params for scope-aware auth checks
|
|
13
|
+
const scopeParams = buildScopeParams(orgId, projectId);
|
|
10
14
|
switch (subcommand) {
|
|
11
15
|
case 'list': {
|
|
12
|
-
const
|
|
16
|
+
const url = scopeParams ? `/harnesses?${scopeParams}` : '/harnesses';
|
|
17
|
+
const response = await (0, client_1.requestJson)(context, url);
|
|
13
18
|
if (json) {
|
|
14
19
|
(0, output_1.outputJson)(response, json);
|
|
15
20
|
return;
|
|
@@ -21,7 +26,8 @@ async function handleHarness(subcommand, positionals, flags, context) {
|
|
|
21
26
|
const name = positionals[0];
|
|
22
27
|
if (!name)
|
|
23
28
|
throw new Error('Usage: eve harness get <name>');
|
|
24
|
-
const
|
|
29
|
+
const url = scopeParams ? `/harnesses/${name}?${scopeParams}` : `/harnesses/${name}`;
|
|
30
|
+
const response = await (0, client_1.requestJson)(context, url);
|
|
25
31
|
if (json) {
|
|
26
32
|
(0, output_1.outputJson)(response, json);
|
|
27
33
|
return;
|
|
@@ -33,6 +39,14 @@ async function handleHarness(subcommand, positionals, flags, context) {
|
|
|
33
39
|
throw new Error('Usage: eve harness <list|get>');
|
|
34
40
|
}
|
|
35
41
|
}
|
|
42
|
+
function buildScopeParams(orgId, projectId) {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
if (projectId)
|
|
45
|
+
params.set('project_id', projectId);
|
|
46
|
+
else if (orgId)
|
|
47
|
+
params.set('org_id', orgId);
|
|
48
|
+
return params.toString() || null;
|
|
49
|
+
}
|
|
36
50
|
function renderHarnessList(harnesses, includeCapabilities) {
|
|
37
51
|
if (includeCapabilities) {
|
|
38
52
|
renderHarnessCapabilities(harnesses);
|
package/dist/commands/job.js
CHANGED
|
@@ -1735,6 +1735,14 @@ async function handleResult(positionals, flags, context) {
|
|
|
1735
1735
|
* Format result as plain text (default)
|
|
1736
1736
|
*/
|
|
1737
1737
|
function formatResultText(response) {
|
|
1738
|
+
// Display preview URL if present in result_json.pipeline_output
|
|
1739
|
+
if (response.resultJson) {
|
|
1740
|
+
const pipelineOutput = response.resultJson.pipeline_output;
|
|
1741
|
+
if (pipelineOutput?.deploy?.preview_url) {
|
|
1742
|
+
console.log(`Preview: ${pipelineOutput.deploy.preview_url}`);
|
|
1743
|
+
console.log('');
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1738
1746
|
if (response.resultText) {
|
|
1739
1747
|
console.log(response.resultText);
|
|
1740
1748
|
}
|
|
@@ -1746,6 +1754,12 @@ function formatResultText(response) {
|
|
|
1746
1754
|
* Format result as JSON
|
|
1747
1755
|
*/
|
|
1748
1756
|
function formatResultJson(response) {
|
|
1757
|
+
// Extract preview URL if present in result_json.pipeline_output
|
|
1758
|
+
let previewUrl;
|
|
1759
|
+
if (response.resultJson) {
|
|
1760
|
+
const pipelineOutput = response.resultJson.pipeline_output;
|
|
1761
|
+
previewUrl = pipelineOutput?.deploy?.preview_url;
|
|
1762
|
+
}
|
|
1749
1763
|
const output = {
|
|
1750
1764
|
success: response.status === 'succeeded',
|
|
1751
1765
|
exitCode: response.exitCode,
|
|
@@ -1755,6 +1769,7 @@ function formatResultJson(response) {
|
|
|
1755
1769
|
tokenUsage: response.tokenInput !== null || response.tokenOutput !== null
|
|
1756
1770
|
? { input: response.tokenInput, output: response.tokenOutput }
|
|
1757
1771
|
: null,
|
|
1772
|
+
...(previewUrl ? { previewUrl } : {}),
|
|
1758
1773
|
};
|
|
1759
1774
|
console.log(JSON.stringify(output, null, 2));
|
|
1760
1775
|
}
|
|
@@ -1774,6 +1789,13 @@ function formatResultFull(response) {
|
|
|
1774
1789
|
const outputStr = response.tokenOutput !== null ? formatNumber(response.tokenOutput) : '0';
|
|
1775
1790
|
console.log(`Tokens: ${inputStr} in / ${outputStr} out`);
|
|
1776
1791
|
}
|
|
1792
|
+
// Display preview URL if present in result_json.pipeline_output
|
|
1793
|
+
if (response.resultJson) {
|
|
1794
|
+
const pipelineOutput = response.resultJson.pipeline_output;
|
|
1795
|
+
if (pipelineOutput?.deploy?.preview_url) {
|
|
1796
|
+
console.log(`Preview: ${pipelineOutput.deploy.preview_url}`);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1777
1799
|
console.log('');
|
|
1778
1800
|
console.log('Result:');
|
|
1779
1801
|
if (response.resultText) {
|
package/dist/commands/org.js
CHANGED
|
@@ -71,8 +71,16 @@ async function handleOrg(subcommand, positionals, flags, context) {
|
|
|
71
71
|
(0, output_1.outputJson)(response, json);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
|
+
case 'delete': {
|
|
75
|
+
const orgId = positionals[0];
|
|
76
|
+
if (!orgId)
|
|
77
|
+
throw new Error('Usage: eve org delete <org_id> [--force]');
|
|
78
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}`, { method: 'PATCH', body: { deleted: true } });
|
|
79
|
+
(0, output_1.outputJson)(response, json, `✓ Organization deleted: ${orgId}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
74
82
|
default:
|
|
75
|
-
throw new Error('Usage: eve org <ensure|list|get|update>');
|
|
83
|
+
throw new Error('Usage: eve org <ensure|list|get|update|delete>');
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
function normalizeOrgId(raw) {
|
|
@@ -108,6 +108,13 @@ function formatPipelineRunDetail(detail) {
|
|
|
108
108
|
if (run.git_sha) {
|
|
109
109
|
console.log(`Git SHA: ${run.git_sha}`);
|
|
110
110
|
}
|
|
111
|
+
// Display preview URL if present in step_outputs
|
|
112
|
+
if ('step_outputs' in run && run.step_outputs) {
|
|
113
|
+
const deployOutput = run.step_outputs.deploy;
|
|
114
|
+
if (deployOutput?.preview_url) {
|
|
115
|
+
console.log(`Preview: ${deployOutput.preview_url}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
111
118
|
if (run.started_at) {
|
|
112
119
|
console.log(`Started: ${run.started_at}`);
|
|
113
120
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleRelease = handleRelease;
|
|
4
|
+
const client_1 = require("../lib/client");
|
|
5
|
+
const output_1 = require("../lib/output");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
async function handleRelease(subcommand, positionals, flags, context) {
|
|
9
|
+
const json = Boolean(flags.json);
|
|
10
|
+
switch (subcommand) {
|
|
11
|
+
case 'resolve': {
|
|
12
|
+
const tag = positionals[0];
|
|
13
|
+
if (!tag) {
|
|
14
|
+
throw new Error('Usage: eve release resolve <tag> [--project <id>]');
|
|
15
|
+
}
|
|
16
|
+
// Determine project ID
|
|
17
|
+
let projectId = typeof flags.project === 'string' ? flags.project : context.projectId;
|
|
18
|
+
// If no project ID provided, try to detect from local .eve/manifest.yaml
|
|
19
|
+
if (!projectId) {
|
|
20
|
+
const dir = typeof flags.dir === 'string' ? flags.dir : process.cwd();
|
|
21
|
+
const manifestPath = (0, node_path_1.join)(dir, '.eve', 'manifest.yaml');
|
|
22
|
+
try {
|
|
23
|
+
const yaml = (0, node_fs_1.readFileSync)(manifestPath, 'utf-8');
|
|
24
|
+
const projectMatch = yaml.match(/^project:\s*(\S+)/m);
|
|
25
|
+
if (projectMatch) {
|
|
26
|
+
projectId = projectMatch[1];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
// Manifest file not found or not readable, continue without it
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!projectId) {
|
|
34
|
+
throw new Error('Missing project id. Provide --project, set a profile default, or add "project: proj_xxx" to .eve/manifest.yaml');
|
|
35
|
+
}
|
|
36
|
+
// Call the API endpoint
|
|
37
|
+
try {
|
|
38
|
+
const release = await (0, client_1.requestJson)(context, `/projects/${projectId}/releases/by-tag/${encodeURIComponent(tag)}`);
|
|
39
|
+
if (json) {
|
|
40
|
+
// Output full JSON
|
|
41
|
+
(0, output_1.outputJson)(release, json);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Human-readable format
|
|
45
|
+
const displayTag = release.tag || tag;
|
|
46
|
+
const displayVersion = release.version || 'N/A';
|
|
47
|
+
const shortSha = release.git_sha.substring(0, 8);
|
|
48
|
+
const shortHash = release.manifest_hash.substring(0, 12);
|
|
49
|
+
console.log(`Release ${displayTag}`);
|
|
50
|
+
console.log(` ID: ${release.id}`);
|
|
51
|
+
console.log(` SHA: ${shortSha}...`);
|
|
52
|
+
console.log(` Manifest: ${shortHash}...`);
|
|
53
|
+
console.log(` Version: ${displayVersion}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Enhance 404 errors with more context
|
|
58
|
+
if (error instanceof Error && error.message.includes('HTTP 404')) {
|
|
59
|
+
throw new Error(`Release with tag "${tag}" not found for project ${projectId}\n\n` +
|
|
60
|
+
`Make sure the release exists and the tag is correct.`);
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
throw new Error('Usage: eve release <resolve>');
|
|
68
|
+
}
|
|
69
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const skills_1 = require("./commands/skills");
|
|
|
23
23
|
const admin_1 = require("./commands/admin");
|
|
24
24
|
const agents_1 = require("./commands/agents");
|
|
25
25
|
const init_1 = require("./commands/init");
|
|
26
|
+
const release_1 = require("./commands/release");
|
|
26
27
|
async function main() {
|
|
27
28
|
const { flags, positionals } = (0, args_1.parseArgs)(process.argv.slice(2));
|
|
28
29
|
const command = positionals[0];
|
|
@@ -103,6 +104,9 @@ async function main() {
|
|
|
103
104
|
// init doesn't use subcommands - first positional is directory name
|
|
104
105
|
await (0, init_1.handleInit)(subcommand ? [subcommand, ...rest] : rest, flags);
|
|
105
106
|
return;
|
|
107
|
+
case 'release':
|
|
108
|
+
await (0, release_1.handleRelease)(subcommand, rest, flags, context);
|
|
109
|
+
return;
|
|
106
110
|
default:
|
|
107
111
|
(0, help_1.showMainHelp)();
|
|
108
112
|
}
|
package/dist/lib/help.js
CHANGED
|
@@ -40,10 +40,16 @@ exports.HELP = {
|
|
|
40
40
|
description: 'Update organization',
|
|
41
41
|
usage: 'eve org update <org_id> [--name <name>] [--deleted <bool>]',
|
|
42
42
|
},
|
|
43
|
+
delete: {
|
|
44
|
+
description: 'Soft-delete an organization',
|
|
45
|
+
usage: 'eve org delete <org_id>',
|
|
46
|
+
examples: ['eve org delete org_xxx'],
|
|
47
|
+
},
|
|
43
48
|
},
|
|
44
49
|
examples: [
|
|
45
50
|
'eve org ensure "Acme Corp"',
|
|
46
51
|
'eve org list --limit 20',
|
|
52
|
+
'eve org delete org_xxx',
|
|
47
53
|
],
|
|
48
54
|
},
|
|
49
55
|
project: {
|
|
@@ -101,13 +107,14 @@ exports.HELP = {
|
|
|
101
107
|
},
|
|
102
108
|
},
|
|
103
109
|
secrets: {
|
|
104
|
-
description: 'Manage secrets at
|
|
110
|
+
description: 'Manage secrets at system/org/user/project scope. Values are never returned in plaintext.',
|
|
105
111
|
usage: 'eve secrets <subcommand> [options]',
|
|
106
112
|
subcommands: {
|
|
107
113
|
set: {
|
|
108
114
|
description: 'Create or update a secret value',
|
|
109
|
-
usage: 'eve secrets set <key> <value> [--project <id>|--org <id>|--user <id>] [--type <type>]',
|
|
115
|
+
usage: 'eve secrets set <key> <value> [--system|--project <id>|--org <id>|--user <id>] [--type <type>]',
|
|
110
116
|
options: [
|
|
117
|
+
'--system System scope (admin only)',
|
|
111
118
|
'--project <id> Project ID (uses profile default)',
|
|
112
119
|
'--org <id> Organization ID',
|
|
113
120
|
'--user <id> User ID',
|
|
@@ -116,21 +123,43 @@ exports.HELP = {
|
|
|
116
123
|
},
|
|
117
124
|
list: {
|
|
118
125
|
description: 'List secrets (metadata only)',
|
|
119
|
-
usage: 'eve secrets list [--project <id>|--org <id>|--user <id>]',
|
|
126
|
+
usage: 'eve secrets list [--system|--project <id>|--org <id>|--user <id>]',
|
|
127
|
+
options: [
|
|
128
|
+
'--system System scope (admin only)',
|
|
129
|
+
'--project <id> Project ID (uses profile default)',
|
|
130
|
+
'--org <id> Organization ID',
|
|
131
|
+
'--user <id> User ID',
|
|
132
|
+
],
|
|
120
133
|
},
|
|
121
134
|
show: {
|
|
122
135
|
description: 'Show a masked secret value',
|
|
123
|
-
usage: 'eve secrets show <key> [--project <id>|--org <id>|--user <id>]',
|
|
136
|
+
usage: 'eve secrets show <key> [--system|--project <id>|--org <id>|--user <id>]',
|
|
137
|
+
options: [
|
|
138
|
+
'--system System scope (admin only)',
|
|
139
|
+
'--project <id> Project ID (uses profile default)',
|
|
140
|
+
'--org <id> Organization ID',
|
|
141
|
+
'--user <id> User ID',
|
|
142
|
+
],
|
|
124
143
|
},
|
|
125
144
|
delete: {
|
|
126
145
|
description: 'Delete a secret',
|
|
127
|
-
usage: 'eve secrets delete <key> [--project <id>|--org <id>|--user <id>]',
|
|
146
|
+
usage: 'eve secrets delete <key> [--system|--project <id>|--org <id>|--user <id>]',
|
|
147
|
+
options: [
|
|
148
|
+
'--system System scope (admin only)',
|
|
149
|
+
'--project <id> Project ID (uses profile default)',
|
|
150
|
+
'--org <id> Organization ID',
|
|
151
|
+
'--user <id> User ID',
|
|
152
|
+
],
|
|
128
153
|
},
|
|
129
154
|
import: {
|
|
130
155
|
description: 'Import env entries from an env file',
|
|
131
|
-
usage: 'eve secrets import [--file <path>] [--project <id>|--org <id>|--user <id>]',
|
|
156
|
+
usage: 'eve secrets import [--file <path>] [--system|--project <id>|--org <id>|--user <id>]',
|
|
132
157
|
options: [
|
|
133
158
|
'--file <path> Defaults to .env',
|
|
159
|
+
'--system System scope (admin only)',
|
|
160
|
+
'--project <id> Project ID (uses profile default)',
|
|
161
|
+
'--org <id> Organization ID',
|
|
162
|
+
'--user <id> User ID',
|
|
134
163
|
],
|
|
135
164
|
},
|
|
136
165
|
validate: {
|
|
@@ -547,7 +576,7 @@ have to specify them on every command. Useful when working with multiple environ
|
|
|
547
576
|
auth: {
|
|
548
577
|
description: `Authenticate with Eve Horizon. Auth is optional for local development but required
|
|
549
578
|
for cloud deployments. Credentials are stored per-profile.`,
|
|
550
|
-
usage: 'eve auth <login|logout|status|whoami|bootstrap|sync>',
|
|
579
|
+
usage: 'eve auth <login|logout|status|whoami|bootstrap|sync|token>',
|
|
551
580
|
subcommands: {
|
|
552
581
|
login: {
|
|
553
582
|
description: 'Login via GitHub SSH challenge (default) or Supabase (legacy)',
|
|
@@ -578,6 +607,20 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
578
607
|
description: 'Show current user info',
|
|
579
608
|
usage: 'eve auth whoami',
|
|
580
609
|
},
|
|
610
|
+
token: {
|
|
611
|
+
description: 'Print the current access token to stdout for sharing with reviewers or use in scripts',
|
|
612
|
+
usage: 'eve auth token [--print]',
|
|
613
|
+
options: [
|
|
614
|
+
'--print Explicitly request token print (default behavior)',
|
|
615
|
+
],
|
|
616
|
+
examples: [
|
|
617
|
+
'eve auth token',
|
|
618
|
+
'TOKEN=$(eve auth token)',
|
|
619
|
+
'curl -H "Authorization: Bearer $(eve auth token)" https://api.example.com',
|
|
620
|
+
'eve auth token | pbcopy # Copy to clipboard',
|
|
621
|
+
'eve auth token # Share with reviewers for PR preview access',
|
|
622
|
+
],
|
|
623
|
+
},
|
|
581
624
|
bootstrap: {
|
|
582
625
|
description: 'Bootstrap the first admin user with flexible security modes',
|
|
583
626
|
usage: 'eve auth bootstrap --email <email> [--token <token>] [options]',
|
|
@@ -654,17 +697,19 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
654
697
|
},
|
|
655
698
|
deploy: {
|
|
656
699
|
description: 'Deploy to an environment',
|
|
657
|
-
usage: 'eve env deploy <
|
|
700
|
+
usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project <id>]',
|
|
658
701
|
options: [
|
|
659
|
-
'<
|
|
660
|
-
'<
|
|
661
|
-
'--
|
|
662
|
-
'--
|
|
702
|
+
'<env> Environment name (staging, production, test)',
|
|
703
|
+
'--ref <sha> Git SHA or commit reference (required)',
|
|
704
|
+
'--direct Bypass pipeline and do direct deploy',
|
|
705
|
+
'--inputs <json> JSON inputs for the deployment (e.g., \'{"release_id":"rel_xxx"}\')',
|
|
706
|
+
'--project <id> Project ID or slug (uses profile default if omitted)',
|
|
663
707
|
],
|
|
664
708
|
examples: [
|
|
665
|
-
'eve env deploy
|
|
666
|
-
'eve env deploy
|
|
667
|
-
'eve env deploy
|
|
709
|
+
'eve env deploy staging --ref abc123',
|
|
710
|
+
'eve env deploy staging --ref abc123 --direct',
|
|
711
|
+
'eve env deploy staging --ref abc123 --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'',
|
|
712
|
+
'eve env deploy staging --ref abc123 --direct --inputs \'{"release_id":"rel_xxx"}\'',
|
|
668
713
|
],
|
|
669
714
|
},
|
|
670
715
|
logs: {
|
|
@@ -683,11 +728,25 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
683
728
|
'eve env logs proj_xxx staging api --since 3600 --grep ERROR',
|
|
684
729
|
],
|
|
685
730
|
},
|
|
731
|
+
delete: {
|
|
732
|
+
description: 'Delete an environment',
|
|
733
|
+
usage: 'eve env delete <name> [--project=<id>] [--force]',
|
|
734
|
+
options: [
|
|
735
|
+
'<name> Environment name to delete',
|
|
736
|
+
'--project <id> Project ID (uses profile default if omitted)',
|
|
737
|
+
'--force Skip confirmation prompt',
|
|
738
|
+
],
|
|
739
|
+
examples: [
|
|
740
|
+
'eve env delete test',
|
|
741
|
+
'eve env delete staging --project=proj_xxx',
|
|
742
|
+
'eve env delete old-env --force',
|
|
743
|
+
],
|
|
744
|
+
},
|
|
686
745
|
},
|
|
687
746
|
examples: [
|
|
688
747
|
'eve env list',
|
|
689
748
|
'eve env create test --type=persistent',
|
|
690
|
-
'eve env deploy
|
|
749
|
+
'eve env deploy staging --ref abc123',
|
|
691
750
|
'eve env logs proj_xxx staging api --tail 200',
|
|
692
751
|
],
|
|
693
752
|
},
|
|
@@ -953,6 +1012,30 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
953
1012
|
'eve admin invite --email user@example.com --github octocat --org org_xxx',
|
|
954
1013
|
],
|
|
955
1014
|
},
|
|
1015
|
+
release: {
|
|
1016
|
+
description: 'Manage and inspect releases.',
|
|
1017
|
+
usage: 'eve release <subcommand> [options]',
|
|
1018
|
+
subcommands: {
|
|
1019
|
+
resolve: {
|
|
1020
|
+
description: 'Look up a release by tag and output its details',
|
|
1021
|
+
usage: 'eve release resolve <tag> [--project <id>]',
|
|
1022
|
+
options: [
|
|
1023
|
+
'<tag> Release tag (e.g., v1.2.3)',
|
|
1024
|
+
'--project <id> Project ID (uses profile default or .eve/manifest.yaml if omitted)',
|
|
1025
|
+
'--json Output as JSON',
|
|
1026
|
+
],
|
|
1027
|
+
examples: [
|
|
1028
|
+
'eve release resolve v1.2.3',
|
|
1029
|
+
'eve release resolve v1.2.3 --project proj_xxx',
|
|
1030
|
+
'eve release resolve v1.2.3 --json',
|
|
1031
|
+
],
|
|
1032
|
+
},
|
|
1033
|
+
},
|
|
1034
|
+
examples: [
|
|
1035
|
+
'eve release resolve v1.2.3',
|
|
1036
|
+
'eve release resolve v1.2.3 --json',
|
|
1037
|
+
],
|
|
1038
|
+
},
|
|
956
1039
|
init: {
|
|
957
1040
|
description: `Initialize a new Eve Horizon project from a template.
|
|
958
1041
|
|
|
@@ -1081,6 +1164,7 @@ function showMainHelp() {
|
|
|
1081
1164
|
console.log(' project Manage projects');
|
|
1082
1165
|
console.log(' job Manage jobs (create, list, show, update, claim, etc.)');
|
|
1083
1166
|
console.log(' env Manage environments (list, show, deploy)');
|
|
1167
|
+
console.log(' release Manage and inspect releases');
|
|
1084
1168
|
console.log(' api Explore API sources and call endpoints');
|
|
1085
1169
|
console.log(' db Inspect env DB schema, RLS, and SQL');
|
|
1086
1170
|
console.log(' pipeline Inspect manifest pipelines (list, show)');
|