@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.
@@ -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) {
@@ -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 <project> <name> [--tag=<tag>] [--release-tag=<tag>] - deploy to an environment\n' +
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 [project] <name> [--tag=<tag>] [--release-tag=<tag>] [--project=<id>]');
157
- }
158
- // Get git SHA from current directory
159
- const releaseTag = (0, args_1.getStringFlag)(flags, ['release-tag', 'release_tag']);
160
- let gitSha = null;
161
- let manifest = null;
162
- if (!releaseTag) {
163
- gitSha = getGitSha();
164
- if (!gitSha) {
165
- throw new Error('Failed to get git SHA. Are you in a git repository?');
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
- if (!json) {
168
- console.log(`Deploying commit ${gitSha.substring(0, 8)} to ${envName}...`);
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
- if (releaseTag) {
184
- body.release_tag = releaseTag;
185
- }
186
- else if (gitSha && manifest) {
187
- body.git_sha = gitSha;
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 (imageTag) {
191
- body.image_tag = imageTag;
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
  // ============================================================================
@@ -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 response = await (0, client_1.requestJson)(context, '/harnesses');
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 response = await (0, client_1.requestJson)(context, `/harnesses/${name}`);
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);
@@ -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) {
@@ -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 project/org/user scope. Values are never returned in plaintext.',
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 <project> <name> [--tag=<tag>] [--release-tag=<tag>]',
700
+ usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project <id>]',
658
701
  options: [
659
- '<project> Project ID or slug',
660
- '<name> Environment name (staging, production, test)',
661
- '--tag <tag> Image tag (e.g., "local" for local images, "sha-abc" for CI)',
662
- '--release-tag <tag> Deploy an existing release by tag (e.g., v1.2.3)',
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 proj_xxx staging',
666
- 'eve env deploy my-project test --tag local',
667
- 'eve env deploy proj_xxx staging --release-tag v1.2.3',
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 proj_xxx staging',
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)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eve-horizon/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {