@eve-horizon/cli 0.2.9 → 0.2.10
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/README.md +28 -4
- package/dist/commands/auth.js +20 -9
- package/dist/commands/build.js +6 -15
- package/dist/commands/env.js +7 -31
- package/dist/commands/pipeline.js +6 -15
- package/dist/commands/profile.js +68 -120
- package/dist/index.js +2 -3
- package/dist/lib/client.js +1 -1
- package/dist/lib/config.js +3 -30
- package/dist/lib/context.js +84 -25
- package/dist/lib/git.js +111 -0
- package/dist/lib/help.js +45 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,18 +6,38 @@ Published CLI (npx) for interacting with the Eve Horizon API. Dev/ops tooling li
|
|
|
6
6
|
npx @eve/cli --help
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
## Local Development & Global Parity
|
|
10
|
+
|
|
11
|
+
When testing CLI changes locally, you have two options:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Option 1: run the repo-local build directly
|
|
15
|
+
pnpm -C packages/cli build
|
|
16
|
+
node packages/cli/bin/eve.js --help
|
|
17
|
+
|
|
18
|
+
# Option 2: link the CLI so the global `eve` binary matches your local build
|
|
19
|
+
pnpm -C packages/cli build
|
|
20
|
+
cd packages/cli && npm link
|
|
21
|
+
eve --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To update global installs for others, publish a new CLI version via the `cli-v*` tag flow
|
|
25
|
+
(see root AGENTS.md for release instructions).
|
|
26
|
+
|
|
9
27
|
## Philosophy
|
|
10
28
|
|
|
11
29
|
The CLI is the **primary interface** for Eve Horizon, designed for humans and AI agents. The REST API is the substrate: every operation is exposed via HTTP, and the CLI is a thin wrapper that handles argument parsing and output formatting. It does not bypass the API or contain business logic.
|
|
12
30
|
|
|
13
31
|
See [API Philosophy](../../docs/system/api-philosophy.md) and [OpenAPI](../../docs/system/openapi.md).
|
|
14
32
|
|
|
15
|
-
## Profiles (defaults
|
|
33
|
+
## Profiles (repo-local defaults)
|
|
16
34
|
|
|
17
35
|
Profiles store API URL, default org/project IDs, default harness, and Supabase auth config.
|
|
36
|
+
Profiles are **repo-local** and live in `.eve/profile.yaml` so each project keeps its own
|
|
37
|
+
defaults without impacting other checkouts.
|
|
18
38
|
|
|
19
39
|
```bash
|
|
20
|
-
# Create or update a profile
|
|
40
|
+
# Create or update a profile (repo-local)
|
|
21
41
|
eve profile set local \
|
|
22
42
|
--api-url http://localhost:4801 \
|
|
23
43
|
--org org_defaulttestorg \
|
|
@@ -27,7 +47,7 @@ eve profile set local \
|
|
|
27
47
|
eve profile set --harness mclaude
|
|
28
48
|
eve profile set --harness mclaude:fast # harness with variant
|
|
29
49
|
|
|
30
|
-
# Set active profile
|
|
50
|
+
# Set active profile (repo-local)
|
|
31
51
|
eve profile use local
|
|
32
52
|
|
|
33
53
|
# Show active profile
|
|
@@ -54,12 +74,16 @@ eve profile set prod \
|
|
|
54
74
|
Auth is **required** for cloud stacks. The default flow uses GitHub SSH keys; Supabase remains an
|
|
55
75
|
optional adapter for legacy deployments.
|
|
56
76
|
|
|
57
|
-
|
|
77
|
+
Credentials are stored globally in `~/.eve/credentials.json`, keyed by API URL. The CLI will
|
|
78
|
+
refresh Supabase access tokens automatically if a refresh token is available.
|
|
58
79
|
|
|
59
80
|
```bash
|
|
60
81
|
# SSH login (default)
|
|
61
82
|
eve auth login --email you@example.com --ssh-key ~/.ssh/id_ed25519
|
|
62
83
|
|
|
84
|
+
# SSH login with custom token TTL (1-90 days)
|
|
85
|
+
eve auth login --email you@example.com --ttl 30
|
|
86
|
+
|
|
63
87
|
# Supabase login (optional)
|
|
64
88
|
eve auth login --email you@example.com --password '...' \
|
|
65
89
|
--supabase-url https://your-project.supabase.co \
|
package/dist/commands/auth.js
CHANGED
|
@@ -59,6 +59,11 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
59
59
|
const email = (0, args_1.getStringFlag)(flags, ['email']) ?? process.env.EVE_AUTH_EMAIL ?? context.profile.default_email;
|
|
60
60
|
const userId = (0, args_1.getStringFlag)(flags, ['user-id']);
|
|
61
61
|
const password = (0, args_1.getStringFlag)(flags, ['password']) ?? process.env.EVE_AUTH_PASSWORD;
|
|
62
|
+
const ttlStr = (0, args_1.getStringFlag)(flags, ['ttl']);
|
|
63
|
+
const ttlDays = ttlStr ? parseInt(ttlStr, 10) : undefined;
|
|
64
|
+
if (ttlDays !== undefined && (isNaN(ttlDays) || ttlDays < 1 || ttlDays > 90)) {
|
|
65
|
+
throw new Error('--ttl must be between 1 and 90 days');
|
|
66
|
+
}
|
|
62
67
|
const supabaseUrl = (0, args_1.getStringFlag)(flags, ['supabase-url']) ||
|
|
63
68
|
process.env.EVE_SUPABASE_URL ||
|
|
64
69
|
context.profile.supabase_url;
|
|
@@ -102,7 +107,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
102
107
|
const expiresAt = payload.expires_in
|
|
103
108
|
? Math.floor(Date.now() / 1000) + payload.expires_in
|
|
104
109
|
: undefined;
|
|
105
|
-
credentials.
|
|
110
|
+
credentials.tokens[context.authKey] = {
|
|
106
111
|
access_token: payload.access_token,
|
|
107
112
|
refresh_token: payload.refresh_token,
|
|
108
113
|
expires_at: expiresAt,
|
|
@@ -116,7 +121,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
116
121
|
throw new Error('Usage: eve auth login --email <email> or --user-id <id>');
|
|
117
122
|
}
|
|
118
123
|
// Attempt SSH key login with GitHub key auto-discovery on failure
|
|
119
|
-
const loginResult = await attemptSshLogin(context, credentials, flags, email, userId);
|
|
124
|
+
const loginResult = await attemptSshLogin(context, credentials, flags, email, userId, ttlDays);
|
|
120
125
|
if (loginResult.success) {
|
|
121
126
|
(0, output_1.outputJson)({ profile: context.profileName, token_type: loginResult.tokenType }, json, '✓ Logged in');
|
|
122
127
|
return;
|
|
@@ -136,7 +141,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
136
141
|
}
|
|
137
142
|
// Retry login after key registration
|
|
138
143
|
console.log('\nRetrying login with registered keys...');
|
|
139
|
-
const retryResult = await attemptSshLogin(context, credentials, flags, email, userId);
|
|
144
|
+
const retryResult = await attemptSshLogin(context, credentials, flags, email, userId, ttlDays);
|
|
140
145
|
if (retryResult.success) {
|
|
141
146
|
(0, output_1.outputJson)({ profile: context.profileName, token_type: retryResult.tokenType }, json, '✓ Logged in');
|
|
142
147
|
return;
|
|
@@ -144,8 +149,14 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
144
149
|
throw new Error(retryResult.error ?? 'Auth verify failed after key registration');
|
|
145
150
|
}
|
|
146
151
|
case 'logout': {
|
|
147
|
-
|
|
152
|
+
const hadToken = Boolean(credentials.tokens[context.authKey] || credentials.profiles?.[context.profileName]);
|
|
153
|
+
if (credentials.tokens[context.authKey]) {
|
|
154
|
+
delete credentials.tokens[context.authKey];
|
|
155
|
+
}
|
|
156
|
+
if (credentials.profiles?.[context.profileName]) {
|
|
148
157
|
delete credentials.profiles[context.profileName];
|
|
158
|
+
}
|
|
159
|
+
if (hadToken) {
|
|
149
160
|
(0, config_1.saveCredentials)(credentials);
|
|
150
161
|
}
|
|
151
162
|
(0, output_1.outputJson)({ profile: context.profileName }, json, '✓ Logged out');
|
|
@@ -251,7 +262,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
251
262
|
if (!payload.access_token) {
|
|
252
263
|
throw new Error('Bootstrap response missing access_token');
|
|
253
264
|
}
|
|
254
|
-
credentials.
|
|
265
|
+
credentials.tokens[context.authKey] = {
|
|
255
266
|
access_token: payload.access_token,
|
|
256
267
|
expires_at: payload.expires_at,
|
|
257
268
|
token_type: payload.token_type,
|
|
@@ -262,7 +273,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
262
273
|
}
|
|
263
274
|
case 'token': {
|
|
264
275
|
// Print the current access token to stdout for use in scripts
|
|
265
|
-
const tokenEntry = credentials.profiles[context.profileName];
|
|
276
|
+
const tokenEntry = credentials.tokens[context.authKey] || credentials.profiles?.[context.profileName];
|
|
266
277
|
if (!tokenEntry || !tokenEntry.access_token) {
|
|
267
278
|
console.error('No valid token found. Please login first with: eve auth login');
|
|
268
279
|
process.exit(1);
|
|
@@ -691,7 +702,7 @@ function signNonceWithSsh(keyPath, nonce) {
|
|
|
691
702
|
(0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
|
|
692
703
|
}
|
|
693
704
|
}
|
|
694
|
-
async function attemptSshLogin(context, credentials, flags, email, userId) {
|
|
705
|
+
async function attemptSshLogin(context, credentials, flags, email, userId, ttlDays) {
|
|
695
706
|
const challengeResponse = await (0, client_1.requestRaw)(context, '/auth/challenge', {
|
|
696
707
|
method: 'POST',
|
|
697
708
|
body: {
|
|
@@ -723,7 +734,7 @@ async function attemptSshLogin(context, credentials, flags, email, userId) {
|
|
|
723
734
|
}
|
|
724
735
|
const verifyResponse = await (0, client_1.requestRaw)(context, '/auth/verify', {
|
|
725
736
|
method: 'POST',
|
|
726
|
-
body: { challenge_id: challenge.challenge_id, signature },
|
|
737
|
+
body: { challenge_id: challenge.challenge_id, signature, ...(ttlDays !== undefined && { ttl_days: ttlDays }) },
|
|
727
738
|
});
|
|
728
739
|
if (!verifyResponse.ok) {
|
|
729
740
|
const message = typeof verifyResponse.data === 'string'
|
|
@@ -735,7 +746,7 @@ async function attemptSshLogin(context, credentials, flags, email, userId) {
|
|
|
735
746
|
if (!payload.access_token) {
|
|
736
747
|
return { success: false, error: 'Auth verify response missing access_token' };
|
|
737
748
|
}
|
|
738
|
-
credentials.
|
|
749
|
+
credentials.tokens[context.authKey] = {
|
|
739
750
|
access_token: payload.access_token,
|
|
740
751
|
expires_at: payload.expires_at,
|
|
741
752
|
token_type: payload.token_type,
|
package/dist/commands/build.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.handleBuild = handleBuild;
|
|
|
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
|
|
7
|
+
const git_js_1 = require("../lib/git.js");
|
|
8
8
|
const ERROR_CODES = {
|
|
9
9
|
auth_error: { code: 'auth_error', label: 'Authentication Error', hint: "Check GITHUB_TOKEN via 'eve secrets set'" },
|
|
10
10
|
clone_error: { code: 'clone_error', label: 'Git Clone Error', hint: "Verify repo URL and access. Check 'eve secrets list'" },
|
|
@@ -63,23 +63,14 @@ async function handleCreate(flags, context, json) {
|
|
|
63
63
|
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
64
64
|
const manifestHash = (0, args_1.getStringFlag)(flags, ['manifest-hash', 'manifest']);
|
|
65
65
|
const services = (0, args_1.getStringFlag)(flags, ['services']);
|
|
66
|
+
const repoDir = (0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir']);
|
|
66
67
|
if (!projectId || !ref || !manifestHash) {
|
|
67
|
-
throw new Error('Usage: eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>]');
|
|
68
|
+
throw new Error('Usage: eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>] [--repo-dir <path>]');
|
|
68
69
|
}
|
|
69
70
|
// Resolve git ref to actual 40-char SHA
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
encoding: 'utf-8',
|
|
74
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
75
|
-
}).trim();
|
|
76
|
-
if (!json) {
|
|
77
|
-
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
82
|
-
'Make sure you are in a git repository and the ref exists.');
|
|
71
|
+
const gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoDir);
|
|
72
|
+
if (!json && ref !== gitSha) {
|
|
73
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
83
74
|
}
|
|
84
75
|
const body = { git_sha: gitSha, manifest_hash: manifestHash };
|
|
85
76
|
if (services) {
|
package/dist/commands/env.js
CHANGED
|
@@ -37,7 +37,7 @@ exports.handleEnv = handleEnv;
|
|
|
37
37
|
const args_1 = require("../lib/args");
|
|
38
38
|
const client_1 = require("../lib/client");
|
|
39
39
|
const output_1 = require("../lib/output");
|
|
40
|
-
const
|
|
40
|
+
const git_js_1 = require("../lib/git.js");
|
|
41
41
|
const readline = __importStar(require("node:readline/promises"));
|
|
42
42
|
// ============================================================================
|
|
43
43
|
// Main Handler
|
|
@@ -64,7 +64,7 @@ async function handleEnv(subcommand, positionals, flags, context) {
|
|
|
64
64
|
' list [project] - list environments for a project\n' +
|
|
65
65
|
' show <project> <name> - show details of an environment\n' +
|
|
66
66
|
' create <name> --type=<type> [options] - create an environment\n' +
|
|
67
|
-
' deploy <env> --ref <sha> [--direct] [--inputs <json>] - deploy to an environment\n' +
|
|
67
|
+
' deploy <env> --ref <sha> [--direct] [--inputs <json>] [--repo-dir <path>] - deploy to an environment\n' +
|
|
68
68
|
' logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] - get service logs\n' +
|
|
69
69
|
' diagnose <project> <env> - diagnose deployment health and events\n' +
|
|
70
70
|
' delete <name> [--project=<id>] [--force] - delete an environment');
|
|
@@ -194,27 +194,18 @@ 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>] [--image-tag <tag>] [--project=<id>]');
|
|
197
|
+
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--repo-dir <path>] [--project=<id>]');
|
|
198
198
|
}
|
|
199
199
|
// --ref flag is now required
|
|
200
200
|
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
201
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
|
+
const repoDir = (0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir']);
|
|
204
205
|
// Resolve git ref to actual 40-char SHA
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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.');
|
|
206
|
+
const gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoDir);
|
|
207
|
+
if (!json && ref !== gitSha) {
|
|
208
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
218
209
|
}
|
|
219
210
|
if (!json) {
|
|
220
211
|
console.log(`Deploying commit ${gitSha.substring(0, 8)} to ${envName}...`);
|
|
@@ -434,21 +425,6 @@ function buildQuery(params) {
|
|
|
434
425
|
const query = search.toString();
|
|
435
426
|
return query ? `?${query}` : '';
|
|
436
427
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Get current git SHA from working directory
|
|
439
|
-
*/
|
|
440
|
-
function getGitSha() {
|
|
441
|
-
try {
|
|
442
|
-
const sha = (0, child_process_1.execSync)('git rev-parse HEAD', {
|
|
443
|
-
encoding: 'utf8',
|
|
444
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
445
|
-
}).trim();
|
|
446
|
-
return sha;
|
|
447
|
-
}
|
|
448
|
-
catch {
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
428
|
/**
|
|
453
429
|
* Format environments as a human-readable table
|
|
454
430
|
*/
|
|
@@ -4,7 +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
|
|
7
|
+
const git_js_1 = require("../lib/git.js");
|
|
8
8
|
async function handlePipeline(subcommand, positionals, flags, context) {
|
|
9
9
|
const json = Boolean(flags.json);
|
|
10
10
|
switch (subcommand) {
|
|
@@ -182,23 +182,14 @@ async function handleRun(positionals, flags, context, json) {
|
|
|
182
182
|
const wait = Boolean(flags.wait);
|
|
183
183
|
const timeout = (0, args_1.getStringFlag)(flags, ['timeout']);
|
|
184
184
|
const inputsRaw = (0, args_1.getStringFlag)(flags, ['inputs']);
|
|
185
|
+
const repoDir = (0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir']);
|
|
185
186
|
if (!pipelineName || !projectId || !ref) {
|
|
186
|
-
throw new Error('Usage: eve pipeline run <name> --ref <sha> [--env <env>] [--project <id>] [--wait] [--inputs <json>] [--only <step>]');
|
|
187
|
+
throw new Error('Usage: eve pipeline run <name> --ref <sha> [--env <env>] [--project <id>] [--repo-dir <path>] [--wait] [--inputs <json>] [--only <step>]');
|
|
187
188
|
}
|
|
188
189
|
// Resolve git ref to actual 40-char SHA
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
encoding: 'utf-8',
|
|
193
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
194
|
-
}).trim();
|
|
195
|
-
if (!json) {
|
|
196
|
-
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
201
|
-
'Make sure you are in a git repository and the ref exists.');
|
|
190
|
+
const gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoDir);
|
|
191
|
+
if (!json && ref !== gitSha) {
|
|
192
|
+
console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
|
|
202
193
|
}
|
|
203
194
|
let inputs;
|
|
204
195
|
if (inputsRaw) {
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,76 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleProfile = handleProfile;
|
|
4
|
-
const config_1 = require("../lib/config");
|
|
5
4
|
const context_1 = require("../lib/context");
|
|
6
5
|
const output_1 = require("../lib/output");
|
|
7
|
-
function handleProfile(subcommand, positionals, flags
|
|
6
|
+
function handleProfile(subcommand, positionals, flags) {
|
|
8
7
|
const json = Boolean(flags.json);
|
|
8
|
+
if (flags.global) {
|
|
9
|
+
throw new Error('Global profiles have been removed. Profiles are repo-local; remove --global.');
|
|
10
|
+
}
|
|
9
11
|
switch (subcommand) {
|
|
10
12
|
case 'list': {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const profiles = Object.entries(config.profiles).map(([name, profile]) => ({
|
|
13
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
14
|
+
const profiles = Object.entries(repoProfiles.profiles).map(([name, profile]) => ({
|
|
14
15
|
name,
|
|
15
|
-
active: name ===
|
|
16
|
-
local: name === localProfileName,
|
|
16
|
+
active: name === repoProfiles.activeProfile,
|
|
17
17
|
...profile,
|
|
18
18
|
}));
|
|
19
|
-
(0, output_1.outputJson)({
|
|
20
|
-
active_profile: config.active_profile,
|
|
21
|
-
local_profile: localProfileName ?? null,
|
|
22
|
-
profiles,
|
|
23
|
-
}, json);
|
|
19
|
+
(0, output_1.outputJson)({ active_profile: repoProfiles.activeProfile ?? null, profiles }, json);
|
|
24
20
|
return;
|
|
25
21
|
}
|
|
26
22
|
case 'show': {
|
|
27
|
-
// Check for local profile first
|
|
28
|
-
const repoProfile = (0, context_1.loadRepoProfile)();
|
|
29
|
-
const localProfileName = repoProfile?.profile;
|
|
30
|
-
// Determine which profile to show
|
|
31
23
|
const requestedName = positionals[0];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
name = requestedName;
|
|
37
|
-
source = localProfileName === requestedName ? 'local' : 'global';
|
|
38
|
-
}
|
|
39
|
-
else if (localProfileName) {
|
|
40
|
-
// No name specified, use local profile if exists
|
|
41
|
-
name = localProfileName;
|
|
42
|
-
source = 'local';
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
// Fall back to global active profile
|
|
46
|
-
name = config.active_profile;
|
|
47
|
-
source = 'global';
|
|
24
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
25
|
+
const name = resolveProfileName(repoProfiles, requestedName);
|
|
26
|
+
if (!name) {
|
|
27
|
+
throw new Error('No local profiles configured. Use `eve profile set` to create one.');
|
|
48
28
|
}
|
|
49
|
-
const profile =
|
|
29
|
+
const profile = repoProfiles.profiles[name];
|
|
50
30
|
if (!profile) {
|
|
51
31
|
throw new Error(`Profile ${name} not found`);
|
|
52
32
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
? {
|
|
56
|
-
...profile,
|
|
57
|
-
...(repoProfile.api_url && { api_url: repoProfile.api_url }),
|
|
58
|
-
...(repoProfile.org_id && { org_id: repoProfile.org_id }),
|
|
59
|
-
...(repoProfile.project_id && { project_id: repoProfile.project_id }),
|
|
60
|
-
}
|
|
61
|
-
: profile;
|
|
62
|
-
const sourceLabel = source === 'local' ? ` (local: .eve/profile.yaml)` : ' (global)';
|
|
63
|
-
(0, output_1.outputJson)({ name, source, ...effectiveProfile }, json, `${name}${sourceLabel}`);
|
|
33
|
+
const active = repoProfiles.activeProfile === name;
|
|
34
|
+
(0, output_1.outputJson)({ name, active, ...profile }, json, `${name} (local)`);
|
|
64
35
|
return;
|
|
65
36
|
}
|
|
66
37
|
case 'use': {
|
|
67
|
-
const isGlobal = Boolean(flags.global);
|
|
68
38
|
const isClear = Boolean(flags.clear);
|
|
69
39
|
// Handle --clear: remove local profile
|
|
70
40
|
if (isClear) {
|
|
71
41
|
const removed = (0, context_1.removeRepoProfile)();
|
|
72
42
|
if (removed) {
|
|
73
|
-
(0, output_1.outputJson)({ cleared: true, path: (0, context_1.getRepoProfilePath)() }, json, `✓ Removed local profile`);
|
|
43
|
+
(0, output_1.outputJson)({ cleared: true, path: (0, context_1.getRepoProfilePath)() }, json, `✓ Removed local profile store (${(0, context_1.getRepoProfilePath)()})`);
|
|
74
44
|
}
|
|
75
45
|
else {
|
|
76
46
|
(0, output_1.outputJson)({ cleared: false }, json, `No local profile to remove`);
|
|
@@ -79,32 +49,16 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
79
49
|
}
|
|
80
50
|
const name = positionals[0];
|
|
81
51
|
if (!name) {
|
|
82
|
-
throw new Error('Usage: eve profile use <name>
|
|
83
|
-
}
|
|
84
|
-
// Validate profile exists in global config (auto-create if needed)
|
|
85
|
-
if (!config.profiles[name]) {
|
|
86
|
-
config.profiles[name] = {};
|
|
87
|
-
(0, config_1.saveConfig)(config);
|
|
52
|
+
throw new Error('Usage: eve profile use <name>');
|
|
88
53
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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)
|
|
97
|
-
const repoProfile = { profile: name };
|
|
98
|
-
// Apply any override flags
|
|
99
|
-
if (typeof flags['api-url'] === 'string')
|
|
100
|
-
repoProfile.api_url = flags['api-url'];
|
|
101
|
-
if (typeof flags.org === 'string')
|
|
102
|
-
repoProfile.org_id = flags.org;
|
|
103
|
-
if (typeof flags.project === 'string')
|
|
104
|
-
repoProfile.project_id = flags.project;
|
|
105
|
-
(0, context_1.saveRepoProfile)(repoProfile);
|
|
106
|
-
(0, output_1.outputJson)({ profile: name, local: true, path: (0, context_1.getRepoProfilePath)(), ...repoProfile }, json, `✓ Local profile set: ${name} (${(0, context_1.getRepoProfilePath)()})`);
|
|
54
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
55
|
+
if (!repoProfiles.profiles[name]) {
|
|
56
|
+
repoProfiles.profiles[name] = {};
|
|
107
57
|
}
|
|
58
|
+
repoProfiles.profiles[name] = applyProfileFlags(repoProfiles.profiles[name], flags);
|
|
59
|
+
repoProfiles.activeProfile = name;
|
|
60
|
+
(0, context_1.saveRepoProfiles)(repoProfiles);
|
|
61
|
+
(0, output_1.outputJson)({ active_profile: name, path: (0, context_1.getRepoProfilePath)(), ...repoProfiles.profiles[name] }, json, `✓ Active profile: ${name} (${(0, context_1.getRepoProfilePath)()})`);
|
|
108
62
|
return;
|
|
109
63
|
}
|
|
110
64
|
case 'create': {
|
|
@@ -112,51 +66,28 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
112
66
|
if (!name) {
|
|
113
67
|
throw new Error('Usage: eve profile create <name> [--api-url <url> ...]');
|
|
114
68
|
}
|
|
115
|
-
|
|
69
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
70
|
+
if (repoProfiles.profiles[name]) {
|
|
116
71
|
throw new Error(`Profile ${name} already exists`);
|
|
117
72
|
}
|
|
118
|
-
|
|
119
|
-
(
|
|
120
|
-
|
|
73
|
+
repoProfiles.profiles[name] = applyProfileFlags({}, flags);
|
|
74
|
+
if (!repoProfiles.activeProfile) {
|
|
75
|
+
repoProfiles.activeProfile = name;
|
|
76
|
+
}
|
|
77
|
+
(0, context_1.saveRepoProfiles)(repoProfiles);
|
|
78
|
+
(0, output_1.outputJson)({ name, ...repoProfiles.profiles[name] }, json, `✓ Profile created: ${name}`);
|
|
121
79
|
return;
|
|
122
80
|
}
|
|
123
81
|
case 'set': {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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)()})`);
|
|
159
|
-
}
|
|
82
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
83
|
+
const name = resolveProfileName(repoProfiles, positionals[0]) ?? 'default';
|
|
84
|
+
if (!repoProfiles.profiles[name]) {
|
|
85
|
+
repoProfiles.profiles[name] = {};
|
|
86
|
+
}
|
|
87
|
+
repoProfiles.profiles[name] = applyProfileFlags(repoProfiles.profiles[name], flags);
|
|
88
|
+
repoProfiles.activeProfile = name;
|
|
89
|
+
(0, context_1.saveRepoProfiles)(repoProfiles);
|
|
90
|
+
(0, output_1.outputJson)({ active_profile: name, ...repoProfiles.profiles[name], path: (0, context_1.getRepoProfilePath)() }, json, `✓ Profile set: ${name} (${(0, context_1.getRepoProfilePath)()})`);
|
|
160
91
|
return;
|
|
161
92
|
}
|
|
162
93
|
case 'remove': {
|
|
@@ -164,19 +95,22 @@ function handleProfile(subcommand, positionals, flags, config) {
|
|
|
164
95
|
if (!name) {
|
|
165
96
|
throw new Error('Usage: eve profile remove <name>');
|
|
166
97
|
}
|
|
167
|
-
|
|
98
|
+
const repoProfiles = (0, context_1.loadRepoProfiles)();
|
|
99
|
+
if (!repoProfiles.profiles[name]) {
|
|
168
100
|
throw new Error(`Profile ${name} not found`);
|
|
169
101
|
}
|
|
170
|
-
delete
|
|
171
|
-
if (
|
|
172
|
-
const [first] = Object.keys(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
102
|
+
delete repoProfiles.profiles[name];
|
|
103
|
+
if (repoProfiles.activeProfile === name) {
|
|
104
|
+
const [first] = Object.keys(repoProfiles.profiles);
|
|
105
|
+
repoProfiles.activeProfile = first;
|
|
106
|
+
}
|
|
107
|
+
if (!repoProfiles.activeProfile && Object.keys(repoProfiles.profiles).length === 0) {
|
|
108
|
+
(0, context_1.removeRepoProfile)();
|
|
109
|
+
(0, output_1.outputJson)({ removed: name, cleared: true }, json, `✓ Removed profile ${name} (store cleared)`);
|
|
110
|
+
return;
|
|
177
111
|
}
|
|
178
|
-
(0,
|
|
179
|
-
(0, output_1.outputJson)({ removed: name, active_profile:
|
|
112
|
+
(0, context_1.saveRepoProfiles)(repoProfiles);
|
|
113
|
+
(0, output_1.outputJson)({ removed: name, active_profile: repoProfiles.activeProfile ?? null }, json, `✓ Removed profile ${name}`);
|
|
180
114
|
return;
|
|
181
115
|
}
|
|
182
116
|
default:
|
|
@@ -205,3 +139,17 @@ function applyProfileFlags(profile, flags) {
|
|
|
205
139
|
updated.default_ssh_key = flags['default-ssh-key'];
|
|
206
140
|
return updated;
|
|
207
141
|
}
|
|
142
|
+
function resolveProfileName(repoProfiles, requestedName) {
|
|
143
|
+
if (requestedName)
|
|
144
|
+
return requestedName;
|
|
145
|
+
if (repoProfiles.activeProfile)
|
|
146
|
+
return repoProfiles.activeProfile;
|
|
147
|
+
const names = Object.keys(repoProfiles.profiles);
|
|
148
|
+
if (names.length === 0)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (names.length === 1)
|
|
151
|
+
return names[0];
|
|
152
|
+
if (names.includes('default'))
|
|
153
|
+
return 'default';
|
|
154
|
+
return names[0];
|
|
155
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -47,9 +47,8 @@ async function main() {
|
|
|
47
47
|
(0, help_1.showSubcommandHelp)(command, subcommand);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
const config = (0, config_1.loadConfig)();
|
|
51
50
|
const credentials = (0, config_1.loadCredentials)();
|
|
52
|
-
const context = (0, context_1.resolveContext)(flags,
|
|
51
|
+
const context = (0, context_1.resolveContext)(flags, credentials);
|
|
53
52
|
switch (command) {
|
|
54
53
|
case 'org':
|
|
55
54
|
await (0, org_1.handleOrg)(subcommand, rest, flags, context);
|
|
@@ -61,7 +60,7 @@ async function main() {
|
|
|
61
60
|
await (0, job_1.handleJob)(subcommand, rest, flags, context);
|
|
62
61
|
return;
|
|
63
62
|
case 'profile':
|
|
64
|
-
(0, profile_1.handleProfile)(subcommand, rest, flags
|
|
63
|
+
(0, profile_1.handleProfile)(subcommand, rest, flags);
|
|
65
64
|
return;
|
|
66
65
|
case 'auth':
|
|
67
66
|
await (0, auth_1.handleAuth)(subcommand, flags, context, credentials);
|
package/dist/lib/client.js
CHANGED
|
@@ -110,7 +110,7 @@ async function attemptRefresh(context) {
|
|
|
110
110
|
token_type: payload.token_type,
|
|
111
111
|
};
|
|
112
112
|
const credentials = (0, config_1.loadCredentials)();
|
|
113
|
-
credentials.
|
|
113
|
+
credentials.tokens[context.authKey] = nextToken;
|
|
114
114
|
(0, config_1.saveCredentials)(credentials);
|
|
115
115
|
return nextToken;
|
|
116
116
|
}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadConfig = loadConfig;
|
|
4
|
-
exports.saveConfig = saveConfig;
|
|
5
3
|
exports.loadCredentials = loadCredentials;
|
|
6
4
|
exports.saveCredentials = saveCredentials;
|
|
7
|
-
exports.getDefaultProfileName = getDefaultProfileName;
|
|
8
5
|
const node_fs_1 = require("node:fs");
|
|
9
6
|
const node_os_1 = require("node:os");
|
|
10
7
|
const node_path_1 = require("node:path");
|
|
11
|
-
const DEFAULT_PROFILE = 'default';
|
|
12
8
|
const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.eve');
|
|
13
|
-
const CONFIG_PATH = (0, node_path_1.join)(CONFIG_DIR, 'config.json');
|
|
14
9
|
const CREDENTIALS_PATH = (0, node_path_1.join)(CONFIG_DIR, 'credentials.json');
|
|
15
10
|
function ensureConfigDir() {
|
|
16
11
|
if (!(0, node_fs_1.existsSync)(CONFIG_DIR)) {
|
|
@@ -40,37 +35,15 @@ function writeJsonFile(path, value) {
|
|
|
40
35
|
// Best-effort for platforms that don't support chmod.
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
|
-
function loadConfig() {
|
|
44
|
-
const fallback = { active_profile: DEFAULT_PROFILE, profiles: { [DEFAULT_PROFILE]: {} } };
|
|
45
|
-
const config = readJsonFile(CONFIG_PATH, fallback);
|
|
46
|
-
config.profiles = config.profiles ?? {};
|
|
47
|
-
if (!config.active_profile) {
|
|
48
|
-
const [first] = Object.keys(config.profiles);
|
|
49
|
-
config.active_profile = first ?? DEFAULT_PROFILE;
|
|
50
|
-
}
|
|
51
|
-
if (!config.profiles[config.active_profile]) {
|
|
52
|
-
config.profiles[config.active_profile] = {};
|
|
53
|
-
}
|
|
54
|
-
return config;
|
|
55
|
-
}
|
|
56
|
-
function saveConfig(config) {
|
|
57
|
-
config.profiles = config.profiles ?? {};
|
|
58
|
-
if (!config.active_profile) {
|
|
59
|
-
const [first] = Object.keys(config.profiles);
|
|
60
|
-
config.active_profile = first ?? DEFAULT_PROFILE;
|
|
61
|
-
}
|
|
62
|
-
writeJsonFile(CONFIG_PATH, config);
|
|
63
|
-
}
|
|
64
38
|
function loadCredentials() {
|
|
65
|
-
const fallback = {
|
|
39
|
+
const fallback = { tokens: {} };
|
|
66
40
|
const credentials = readJsonFile(CREDENTIALS_PATH, fallback);
|
|
41
|
+
credentials.tokens = credentials.tokens ?? {};
|
|
67
42
|
credentials.profiles = credentials.profiles ?? {};
|
|
68
43
|
return credentials;
|
|
69
44
|
}
|
|
70
45
|
function saveCredentials(credentials) {
|
|
46
|
+
credentials.tokens = credentials.tokens ?? {};
|
|
71
47
|
credentials.profiles = credentials.profiles ?? {};
|
|
72
48
|
writeJsonFile(CREDENTIALS_PATH, credentials);
|
|
73
49
|
}
|
|
74
|
-
function getDefaultProfileName(config) {
|
|
75
|
-
return config.active_profile ?? DEFAULT_PROFILE;
|
|
76
|
-
}
|
package/dist/lib/context.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRepoProfilePath = getRepoProfilePath;
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
4
|
+
exports.loadRepoProfiles = loadRepoProfiles;
|
|
5
|
+
exports.saveRepoProfiles = saveRepoProfiles;
|
|
6
6
|
exports.removeRepoProfile = removeRepoProfile;
|
|
7
7
|
exports.resolveContext = resolveContext;
|
|
8
8
|
exports.parseHarnessSpec = parseHarnessSpec;
|
|
@@ -10,6 +10,7 @@ const node_fs_1 = require("node:fs");
|
|
|
10
10
|
const node_path_1 = require("node:path");
|
|
11
11
|
const yaml_1 = require("yaml");
|
|
12
12
|
const args_1 = require("./args");
|
|
13
|
+
const DEFAULT_PROFILE = 'default';
|
|
13
14
|
// Default to Ingress URL for k8s stack (works via lvh.me → 127.0.0.1)
|
|
14
15
|
// Override with EVE_API_URL for other environments (docker compose, local dev)
|
|
15
16
|
const DEFAULT_API_URL = 'http://api.eve.lvh.me';
|
|
@@ -20,34 +21,37 @@ function getRepoProfilePath() {
|
|
|
20
21
|
return (0, node_path_1.join)(process.cwd(), '.eve', 'profile.yaml');
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
|
-
* Load repository
|
|
24
|
-
* Returns null if file not found
|
|
24
|
+
* Load repository profiles from .eve/profile.yaml if it exists
|
|
25
25
|
*/
|
|
26
|
-
function
|
|
26
|
+
function loadRepoProfiles() {
|
|
27
27
|
const profilePath = getRepoProfilePath();
|
|
28
28
|
if (!(0, node_fs_1.existsSync)(profilePath)) {
|
|
29
|
-
return
|
|
29
|
+
return { profiles: {} };
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
32
|
const content = (0, node_fs_1.readFileSync)(profilePath, 'utf-8');
|
|
33
33
|
const parsed = (0, yaml_1.parse)(content);
|
|
34
|
-
return parsed
|
|
34
|
+
return normalizeRepoProfiles(parsed);
|
|
35
35
|
}
|
|
36
36
|
catch {
|
|
37
37
|
// Silently ignore parse errors
|
|
38
|
-
return
|
|
38
|
+
return { profiles: {} };
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Save repository
|
|
42
|
+
* Save repository profiles to .eve/profile.yaml
|
|
43
43
|
*/
|
|
44
|
-
function
|
|
44
|
+
function saveRepoProfiles(repoProfiles) {
|
|
45
45
|
const profilePath = getRepoProfilePath();
|
|
46
46
|
const dir = (0, node_path_1.dirname)(profilePath);
|
|
47
47
|
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
48
48
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
const payload = {
|
|
51
|
+
active_profile: repoProfiles.activeProfile,
|
|
52
|
+
profiles: repoProfiles.profiles,
|
|
53
|
+
};
|
|
54
|
+
(0, node_fs_1.writeFileSync)(profilePath, (0, yaml_1.stringify)(payload));
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Remove repository profile file
|
|
@@ -60,15 +64,16 @@ function removeRepoProfile() {
|
|
|
60
64
|
(0, node_fs_1.unlinkSync)(profilePath);
|
|
61
65
|
return true;
|
|
62
66
|
}
|
|
63
|
-
function resolveContext(flags,
|
|
64
|
-
const
|
|
67
|
+
function resolveContext(flags, credentials) {
|
|
68
|
+
const repoProfiles = loadRepoProfiles();
|
|
69
|
+
const profileNames = Object.keys(repoProfiles.profiles);
|
|
65
70
|
// Determine profile name and source
|
|
66
|
-
// Priority: flag > env > local (.eve/profile.yaml) >
|
|
71
|
+
// Priority: flag > env > local (.eve/profile.yaml) > default
|
|
67
72
|
let profileName;
|
|
68
73
|
let profileSource;
|
|
69
74
|
const flagProfile = (0, args_1.getStringFlag)(flags, ['profile']);
|
|
70
75
|
const envProfile = process.env.EVE_PROFILE;
|
|
71
|
-
const localProfile =
|
|
76
|
+
const localProfile = repoProfiles.activeProfile;
|
|
72
77
|
if (flagProfile) {
|
|
73
78
|
profileName = flagProfile;
|
|
74
79
|
profileSource = 'flag';
|
|
@@ -81,34 +86,43 @@ function resolveContext(flags, config, credentials) {
|
|
|
81
86
|
profileName = localProfile;
|
|
82
87
|
profileSource = 'local';
|
|
83
88
|
}
|
|
89
|
+
else if (profileNames.length === 1) {
|
|
90
|
+
profileName = profileNames[0];
|
|
91
|
+
profileSource = 'local';
|
|
92
|
+
}
|
|
93
|
+
else if (profileNames.includes(DEFAULT_PROFILE)) {
|
|
94
|
+
profileName = DEFAULT_PROFILE;
|
|
95
|
+
profileSource = 'local';
|
|
96
|
+
}
|
|
97
|
+
else if (profileNames.length > 0) {
|
|
98
|
+
profileName = profileNames[0];
|
|
99
|
+
profileSource = 'local';
|
|
100
|
+
}
|
|
84
101
|
else {
|
|
85
|
-
profileName =
|
|
86
|
-
profileSource = '
|
|
102
|
+
profileName = DEFAULT_PROFILE;
|
|
103
|
+
profileSource = 'default';
|
|
87
104
|
}
|
|
88
|
-
// Get base profile config from
|
|
89
|
-
const profile =
|
|
90
|
-
// Apply overrides from repo profile on top of base profile
|
|
105
|
+
// Get base profile config from local profiles
|
|
106
|
+
const profile = repoProfiles.profiles[profileName] ?? {};
|
|
91
107
|
const apiUrl = (0, args_1.getStringFlag)(flags, ['api', 'api-url']) ||
|
|
92
|
-
repoProfile?.api_url ||
|
|
93
108
|
process.env.EVE_API_URL ||
|
|
94
109
|
profile.api_url ||
|
|
95
110
|
DEFAULT_API_URL;
|
|
96
111
|
const orgId = (0, args_1.getStringFlag)(flags, ['org']) ||
|
|
97
|
-
repoProfile?.org_id ||
|
|
98
112
|
process.env.EVE_ORG_ID ||
|
|
99
113
|
profile.org_id;
|
|
100
114
|
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ||
|
|
101
|
-
repoProfile?.project_id ||
|
|
102
115
|
process.env.EVE_PROJECT_ID ||
|
|
103
116
|
profile.project_id;
|
|
104
|
-
|
|
105
|
-
const tokenEntry = credentials.profiles[profileName];
|
|
117
|
+
const authKey = toAuthKey(apiUrl);
|
|
118
|
+
const tokenEntry = credentials.tokens[authKey] || credentials.profiles?.[profileName];
|
|
106
119
|
return {
|
|
107
120
|
apiUrl,
|
|
108
121
|
orgId,
|
|
109
122
|
projectId,
|
|
110
123
|
profileName,
|
|
111
124
|
profile,
|
|
125
|
+
authKey,
|
|
112
126
|
token: tokenEntry?.access_token,
|
|
113
127
|
refreshToken: tokenEntry?.refresh_token,
|
|
114
128
|
expiresAt: tokenEntry?.expires_at,
|
|
@@ -126,3 +140,48 @@ function parseHarnessSpec(spec) {
|
|
|
126
140
|
return [spec, undefined];
|
|
127
141
|
return [spec.slice(0, colonIdx), spec.slice(colonIdx + 1) || undefined];
|
|
128
142
|
}
|
|
143
|
+
function normalizeRepoProfiles(parsed) {
|
|
144
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
145
|
+
return { profiles: {} };
|
|
146
|
+
}
|
|
147
|
+
if (parsed.profiles && typeof parsed.profiles === 'object') {
|
|
148
|
+
return {
|
|
149
|
+
activeProfile: parsed.active_profile,
|
|
150
|
+
profiles: parsed.profiles,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const legacyConfig = extractProfileConfig(parsed);
|
|
154
|
+
const hasLegacyFields = Object.keys(legacyConfig).length > 0;
|
|
155
|
+
const legacyName = parsed.profile ?? parsed.active_profile;
|
|
156
|
+
if (!hasLegacyFields && !legacyName) {
|
|
157
|
+
return { profiles: {} };
|
|
158
|
+
}
|
|
159
|
+
const name = legacyName ?? DEFAULT_PROFILE;
|
|
160
|
+
return {
|
|
161
|
+
activeProfile: name,
|
|
162
|
+
profiles: { [name]: legacyConfig },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function extractProfileConfig(raw) {
|
|
166
|
+
const config = {};
|
|
167
|
+
if (raw.api_url)
|
|
168
|
+
config.api_url = raw.api_url;
|
|
169
|
+
if (raw.org_id)
|
|
170
|
+
config.org_id = raw.org_id;
|
|
171
|
+
if (raw.project_id)
|
|
172
|
+
config.project_id = raw.project_id;
|
|
173
|
+
if (raw.default_harness)
|
|
174
|
+
config.default_harness = raw.default_harness;
|
|
175
|
+
if (raw.supabase_url)
|
|
176
|
+
config.supabase_url = raw.supabase_url;
|
|
177
|
+
if (raw.supabase_anon_key)
|
|
178
|
+
config.supabase_anon_key = raw.supabase_anon_key;
|
|
179
|
+
if (raw.default_email)
|
|
180
|
+
config.default_email = raw.default_email;
|
|
181
|
+
if (raw.default_ssh_key)
|
|
182
|
+
config.default_ssh_key = raw.default_ssh_key;
|
|
183
|
+
return config;
|
|
184
|
+
}
|
|
185
|
+
function toAuthKey(apiUrl) {
|
|
186
|
+
return apiUrl.trim().replace(/\/+$/, '');
|
|
187
|
+
}
|
package/dist/lib/git.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isGitSha = isGitSha;
|
|
7
|
+
exports.resolveGitRef = resolveGitRef;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const client_js_1 = require("./client.js");
|
|
11
|
+
const GIT_SHA_REGEX = /^[0-9a-f]{40}$/;
|
|
12
|
+
function isGitSha(ref) {
|
|
13
|
+
return GIT_SHA_REGEX.test(ref);
|
|
14
|
+
}
|
|
15
|
+
async function resolveGitRef(context, projectId, ref, repoDir) {
|
|
16
|
+
if (isGitSha(ref)) {
|
|
17
|
+
return ref;
|
|
18
|
+
}
|
|
19
|
+
const resolvedRepoDir = repoDir ?? getGitRoot();
|
|
20
|
+
if (!resolvedRepoDir) {
|
|
21
|
+
throw new Error(`Failed to resolve git ref '${ref}': not in a git repository.\n` +
|
|
22
|
+
'Run the command from the project repository, pass --repo-dir <path>, or use a 40-character SHA.');
|
|
23
|
+
}
|
|
24
|
+
if (projectId) {
|
|
25
|
+
const project = await (0, client_js_1.requestJson)(context, `/projects/${projectId}`);
|
|
26
|
+
const expected = normalizeRepoIdentity(project.repo_url);
|
|
27
|
+
const actual = normalizeRepoIdentity(getGitOriginUrl(resolvedRepoDir));
|
|
28
|
+
const repoDirIdentity = normalizeRepoIdentity(resolvedRepoDir);
|
|
29
|
+
if (expected && actual && expected !== actual) {
|
|
30
|
+
throw new Error(`Failed to resolve git ref '${ref}': current repo does not match project repo.\n` +
|
|
31
|
+
` Project repo: ${project.repo_url}\n` +
|
|
32
|
+
` Current repo: ${getGitOriginUrl(resolvedRepoDir)}\n` +
|
|
33
|
+
'Run the command from the project repository, pass --repo-dir <path>, or use a 40-character SHA.');
|
|
34
|
+
}
|
|
35
|
+
if (expected && !actual && (!repoDirIdentity || repoDirIdentity !== expected)) {
|
|
36
|
+
throw new Error(`Failed to resolve git ref '${ref}': current repo has no origin remote to validate against project repo.\n` +
|
|
37
|
+
` Project repo: ${project.repo_url}\n` +
|
|
38
|
+
'Run the command from the project repository, pass --repo-dir <path>, or use a 40-character SHA.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return (0, node_child_process_1.execSync)(`git rev-parse ${ref}`, {
|
|
43
|
+
cwd: resolvedRepoDir,
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
}).trim();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(`Failed to resolve git ref '${ref}': ${error instanceof Error ? error.message : String(error)}\n` +
|
|
50
|
+
'Make sure the ref exists in the repository, or use a 40-character SHA.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function getGitRoot() {
|
|
54
|
+
try {
|
|
55
|
+
return (0, node_child_process_1.execSync)('git rev-parse --show-toplevel', {
|
|
56
|
+
encoding: 'utf-8',
|
|
57
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
58
|
+
}).trim();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getGitOriginUrl(repoDir) {
|
|
65
|
+
try {
|
|
66
|
+
const url = (0, node_child_process_1.execSync)('git config --get remote.origin.url', {
|
|
67
|
+
cwd: repoDir,
|
|
68
|
+
encoding: 'utf-8',
|
|
69
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
70
|
+
}).trim();
|
|
71
|
+
return url || null;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function normalizeRepoIdentity(repoUrl) {
|
|
78
|
+
if (!repoUrl) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (repoUrl.startsWith('file://')) {
|
|
82
|
+
return stripFilePath(node_path_1.default.resolve(repoUrl.replace('file://', '')));
|
|
83
|
+
}
|
|
84
|
+
if (repoUrl.startsWith('/') || repoUrl.startsWith('./') || repoUrl.startsWith('../')) {
|
|
85
|
+
return stripFilePath(node_path_1.default.resolve(repoUrl));
|
|
86
|
+
}
|
|
87
|
+
if (repoUrl.startsWith('git@')) {
|
|
88
|
+
const match = repoUrl.match(/^git@([^:]+):(.+)$/);
|
|
89
|
+
if (!match) {
|
|
90
|
+
return repoUrl;
|
|
91
|
+
}
|
|
92
|
+
const host = match[1].toLowerCase();
|
|
93
|
+
const pathname = stripGitSuffix(match[2]).toLowerCase();
|
|
94
|
+
return `${host}/${pathname}`;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const parsed = new URL(repoUrl);
|
|
98
|
+
const host = parsed.host.toLowerCase();
|
|
99
|
+
const pathname = stripGitSuffix(parsed.pathname.replace(/^\//, '')).toLowerCase();
|
|
100
|
+
return `${host}/${pathname}`;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return repoUrl;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function stripGitSuffix(pathname) {
|
|
107
|
+
return pathname.replace(/\.git$/i, '');
|
|
108
|
+
}
|
|
109
|
+
function stripFilePath(filePath) {
|
|
110
|
+
return filePath.replace(/\.git$/i, '');
|
|
111
|
+
}
|
package/dist/lib/help.js
CHANGED
|
@@ -529,12 +529,11 @@ Jobs default to 'ready' phase, making them immediately schedulable.`,
|
|
|
529
529
|
examples: ['eve agents config --json'],
|
|
530
530
|
},
|
|
531
531
|
profile: {
|
|
532
|
-
description: `Manage CLI profiles. Profiles store defaults (API URL, org, project) so you don't
|
|
532
|
+
description: `Manage repo-local CLI profiles. Profiles store defaults (API URL, org, project) so you don't
|
|
533
533
|
have to specify them on every command.
|
|
534
534
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
clobbering your global config.`,
|
|
535
|
+
Profiles live in .eve/profile.yaml inside the repo, so each project keeps its own defaults
|
|
536
|
+
and switching profiles won't affect other checkouts.`,
|
|
538
537
|
usage: 'eve profile <subcommand> [options]',
|
|
539
538
|
subcommands: {
|
|
540
539
|
list: {
|
|
@@ -542,28 +541,26 @@ clobbering your global config.`,
|
|
|
542
541
|
usage: 'eve profile list',
|
|
543
542
|
},
|
|
544
543
|
show: {
|
|
545
|
-
description: 'Show profile details
|
|
544
|
+
description: 'Show profile details',
|
|
546
545
|
usage: 'eve profile show [name]',
|
|
547
546
|
examples: ['eve profile show', 'eve profile show prod'],
|
|
548
547
|
},
|
|
549
548
|
use: {
|
|
550
|
-
description: 'Switch active profile (
|
|
551
|
-
usage: 'eve profile use <name> [--
|
|
549
|
+
description: 'Switch active profile (repo-local)',
|
|
550
|
+
usage: 'eve profile use <name> [--org <id>] [--project <id>]',
|
|
552
551
|
options: [
|
|
553
|
-
'--global Write to ~/.eve/config.json instead of local project',
|
|
554
552
|
'--org <id> Set org override',
|
|
555
553
|
'--project <id> Set project override',
|
|
556
554
|
'--api-url <url> Set API URL override',
|
|
557
|
-
'--clear Remove local .eve/profile.yaml',
|
|
555
|
+
'--clear Remove local .eve/profile.yaml (clears all profiles)',
|
|
558
556
|
],
|
|
559
557
|
examples: [
|
|
560
558
|
'eve profile use staging --org org_xxx --project proj_yyy',
|
|
561
|
-
'eve profile use staging --global',
|
|
562
559
|
'eve profile use --clear',
|
|
563
560
|
],
|
|
564
561
|
},
|
|
565
562
|
create: {
|
|
566
|
-
description: 'Create a new named profile (
|
|
563
|
+
description: 'Create a new named profile (repo-local)',
|
|
567
564
|
usage: 'eve profile create <name> [--api-url <url>] [--org <id>] [--project <id>]',
|
|
568
565
|
options: [
|
|
569
566
|
'--api-url <url> API base URL',
|
|
@@ -581,16 +578,13 @@ clobbering your global config.`,
|
|
|
581
578
|
],
|
|
582
579
|
},
|
|
583
580
|
set: {
|
|
584
|
-
description: 'Update profile settings (
|
|
585
|
-
usage: 'eve profile set [name] [--org <id>] [--project <id>]
|
|
581
|
+
description: 'Update profile settings (repo-local)',
|
|
582
|
+
usage: 'eve profile set [name] [--org <id>] [--project <id>]',
|
|
586
583
|
options: [
|
|
587
584
|
'--org <id> Default organization ID',
|
|
588
585
|
'--project <id> Default project ID',
|
|
589
586
|
'--api-url <url> API base URL',
|
|
590
587
|
'--harness <name> Default harness',
|
|
591
|
-
'--global Write to ~/.eve/config.json instead of local project',
|
|
592
|
-
'',
|
|
593
|
-
'Global-only (require --global):',
|
|
594
588
|
'--supabase-url <url> Supabase URL',
|
|
595
589
|
'--supabase-anon-key <key> Supabase anon key',
|
|
596
590
|
'--default-email <email> Default email for auth login',
|
|
@@ -599,38 +593,40 @@ clobbering your global config.`,
|
|
|
599
593
|
examples: [
|
|
600
594
|
'eve profile set --org org_xxx --project proj_yyy',
|
|
601
595
|
'eve profile set staging --org org_xxx --project proj_yyy',
|
|
602
|
-
'eve profile set --
|
|
596
|
+
'eve profile set --default-email user@example.com',
|
|
603
597
|
],
|
|
604
598
|
},
|
|
605
599
|
remove: {
|
|
606
|
-
description: 'Remove a named profile (
|
|
600
|
+
description: 'Remove a named profile (repo-local)',
|
|
607
601
|
usage: 'eve profile remove <name>',
|
|
608
602
|
},
|
|
609
603
|
},
|
|
610
604
|
examples: [
|
|
611
605
|
'eve profile set --org org_xxx --project proj_yyy # writes .eve/profile.yaml',
|
|
612
606
|
'eve profile use staging --org org_xxx # writes .eve/profile.yaml',
|
|
613
|
-
'eve profile set --
|
|
607
|
+
'eve profile set --default-email me@dev.com # writes .eve/profile.yaml',
|
|
614
608
|
],
|
|
615
609
|
},
|
|
616
610
|
auth: {
|
|
617
611
|
description: `Authenticate with Eve Horizon. Auth is optional for local development but required
|
|
618
|
-
for cloud deployments. Credentials are stored per
|
|
612
|
+
for cloud deployments. Credentials are stored globally per API URL.`,
|
|
619
613
|
usage: 'eve auth <login|logout|status|whoami|bootstrap|sync|creds|token|mint>',
|
|
620
614
|
subcommands: {
|
|
621
615
|
login: {
|
|
622
616
|
description: 'Login via GitHub SSH challenge (default) or Supabase (legacy)',
|
|
623
|
-
usage: 'eve auth login [--email <email>] [--ssh-key <path>]',
|
|
617
|
+
usage: 'eve auth login [--email <email>] [--ssh-key <path>] [--ttl <days>]',
|
|
624
618
|
options: [
|
|
625
619
|
'--email <email> Email address for SSH login (uses profile default_email if not provided)',
|
|
626
620
|
'--user-id <id> User id for SSH login',
|
|
627
621
|
'--ssh-key <path> Path to SSH private key (uses profile default_ssh_key, then ~/.ssh/id_ed25519)',
|
|
622
|
+
'--ttl <days> Token TTL in days (1-90, default: server configured)',
|
|
628
623
|
'--password <pass> Supabase password (triggers Supabase login)',
|
|
629
624
|
'--supabase-url <url> Supabase URL',
|
|
630
625
|
'--supabase-anon-key <key> Supabase anon key',
|
|
631
626
|
],
|
|
632
627
|
examples: [
|
|
633
628
|
'eve auth login --email user@example.com',
|
|
629
|
+
'eve auth login --email user@example.com --ttl 30',
|
|
634
630
|
'eve auth login # uses profile defaults if set',
|
|
635
631
|
'eve auth login --ssh-key ~/.ssh/id_rsa',
|
|
636
632
|
],
|
|
@@ -771,23 +767,24 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
771
767
|
},
|
|
772
768
|
deploy: {
|
|
773
769
|
description: 'Deploy to an environment',
|
|
774
|
-
usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--project <id>]',
|
|
770
|
+
usage: 'eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--repo-dir <path>] [--project <id>]',
|
|
775
771
|
options: [
|
|
776
772
|
'<env> Environment name (staging, production, test)',
|
|
777
|
-
'--ref <sha> Git SHA
|
|
773
|
+
'--ref <sha> Git SHA (required). Non-SHA refs resolve against the repo in --repo-dir or cwd.',
|
|
778
774
|
'--direct Bypass pipeline and do direct deploy',
|
|
779
775
|
'--inputs <json> JSON inputs for the deployment (e.g., \'{"release_id":"rel_xxx"}\')',
|
|
780
776
|
'--image-tag <tag> Use a specific image tag for deploy (direct only)',
|
|
777
|
+
'--repo-dir <path> Resolve --ref against this repo instead of cwd',
|
|
781
778
|
'--project <id> Project ID or slug (uses profile default if omitted)',
|
|
782
779
|
'--watch Poll deployment status until ready (default: true)',
|
|
783
780
|
'--timeout <seconds> Watch timeout in seconds (default: 120)',
|
|
784
781
|
],
|
|
785
782
|
examples: [
|
|
786
|
-
'eve env deploy staging --ref
|
|
787
|
-
'eve env deploy staging --ref
|
|
788
|
-
'eve env deploy staging --ref
|
|
789
|
-
'eve env deploy staging --ref
|
|
790
|
-
'eve env deploy staging --ref
|
|
783
|
+
'eve env deploy staging --ref 0123456789abcdef0123456789abcdef01234567',
|
|
784
|
+
'eve env deploy staging --ref 0123456789abcdef0123456789abcdef01234567 --direct',
|
|
785
|
+
'eve env deploy staging --ref 0123456789abcdef0123456789abcdef01234567 --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'',
|
|
786
|
+
'eve env deploy staging --ref 0123456789abcdef0123456789abcdef01234567 --direct --inputs \'{"release_id":"rel_xxx"}\'',
|
|
787
|
+
'eve env deploy staging --ref main --repo-dir ./my-app',
|
|
791
788
|
],
|
|
792
789
|
},
|
|
793
790
|
diagnose: {
|
|
@@ -952,19 +949,21 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
952
949
|
},
|
|
953
950
|
run: {
|
|
954
951
|
description: 'Run a pipeline',
|
|
955
|
-
usage: 'eve pipeline run <name> --ref <sha> [--env <env>] [--wait] [--only <step>]',
|
|
952
|
+
usage: 'eve pipeline run <name> --ref <sha> [--env <env>] [--repo-dir <path>] [--wait] [--only <step>]',
|
|
956
953
|
options: [
|
|
957
|
-
'--ref <sha> Git SHA
|
|
954
|
+
'--ref <sha> Git SHA (required). Non-SHA refs resolve against the repo in --repo-dir or cwd.',
|
|
958
955
|
'--env <env> Target environment',
|
|
959
956
|
'--project <id> Project ID (uses profile default)',
|
|
960
957
|
'--wait Wait for completion',
|
|
961
958
|
'--timeout <n> Max wait time (seconds)',
|
|
962
959
|
'--inputs <json> JSON inputs for the pipeline',
|
|
963
960
|
'--only <step> Run a single step (includes dependencies)',
|
|
961
|
+
'--repo-dir <path> Resolve --ref against this repo instead of cwd',
|
|
964
962
|
],
|
|
965
963
|
examples: [
|
|
966
|
-
'eve pipeline run deploy-test --ref
|
|
967
|
-
'eve pipeline run deploy-test --ref
|
|
964
|
+
'eve pipeline run deploy-test --ref 0123456789abcdef0123456789abcdef01234567 --env test',
|
|
965
|
+
'eve pipeline run deploy-test --ref 0123456789abcdef0123456789abcdef01234567 --env test --wait --timeout 120',
|
|
966
|
+
'eve pipeline run deploy-test --ref main --repo-dir ./my-app --env test',
|
|
968
967
|
],
|
|
969
968
|
},
|
|
970
969
|
runs: {
|
|
@@ -1006,7 +1005,7 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
1006
1005
|
],
|
|
1007
1006
|
},
|
|
1008
1007
|
},
|
|
1009
|
-
examples: ['eve pipeline list', 'eve pipeline run deploy-test --ref
|
|
1008
|
+
examples: ['eve pipeline list', 'eve pipeline run deploy-test --ref 0123456789abcdef0123456789abcdef01234567 --env test', 'eve pipeline logs deploy-test prun_xxx --follow'],
|
|
1010
1009
|
},
|
|
1011
1010
|
workflow: {
|
|
1012
1011
|
description: 'Inspect workflows defined in the project manifest (read-only in Phase 1).',
|
|
@@ -1148,16 +1147,18 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
1148
1147
|
subcommands: {
|
|
1149
1148
|
create: {
|
|
1150
1149
|
description: 'Create a new build spec',
|
|
1151
|
-
usage: 'eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>]',
|
|
1150
|
+
usage: 'eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>] [--repo-dir <path>]',
|
|
1152
1151
|
options: [
|
|
1153
1152
|
'--project <id> Project ID (uses profile default)',
|
|
1154
|
-
'--ref <sha> Git SHA
|
|
1153
|
+
'--ref <sha> Git SHA (required). Non-SHA refs resolve against the repo in --repo-dir or cwd.',
|
|
1155
1154
|
'--manifest-hash <h> Manifest hash (required)',
|
|
1156
1155
|
'--services <list> Comma-separated service names to build',
|
|
1156
|
+
'--repo-dir <path> Resolve --ref against this repo instead of cwd',
|
|
1157
1157
|
],
|
|
1158
1158
|
examples: [
|
|
1159
|
-
'eve build create --ref
|
|
1160
|
-
'eve build create --project proj_xxx --ref
|
|
1159
|
+
'eve build create --ref 0123456789abcdef0123456789abcdef01234567 --manifest-hash mfst_123',
|
|
1160
|
+
'eve build create --project proj_xxx --ref 0123456789abcdef0123456789abcdef01234567 --manifest-hash mfst_123 --services api,web',
|
|
1161
|
+
'eve build create --project proj_xxx --ref main --repo-dir ./my-app --manifest-hash mfst_123',
|
|
1161
1162
|
],
|
|
1162
1163
|
},
|
|
1163
1164
|
list: {
|
|
@@ -1232,7 +1233,7 @@ for cloud deployments. Credentials are stored per-profile.`,
|
|
|
1232
1233
|
},
|
|
1233
1234
|
},
|
|
1234
1235
|
examples: [
|
|
1235
|
-
'eve build create --ref
|
|
1236
|
+
'eve build create --ref 0123456789abcdef0123456789abcdef01234567 --manifest-hash mfst_123 --services api,web',
|
|
1236
1237
|
'eve build list',
|
|
1237
1238
|
'eve build show build_xxx',
|
|
1238
1239
|
'eve build run build_xxx',
|
|
@@ -1274,7 +1275,7 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1274
1275
|
],
|
|
1275
1276
|
},
|
|
1276
1277
|
system: {
|
|
1277
|
-
description: 'System administration and health checks.',
|
|
1278
|
+
description: 'System administration and health checks (admin scope required for most commands).',
|
|
1278
1279
|
usage: 'eve system <subcommand> [options]',
|
|
1279
1280
|
subcommands: {
|
|
1280
1281
|
health: {
|
|
@@ -1283,7 +1284,7 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1283
1284
|
examples: ['eve system health', 'eve system health --json'],
|
|
1284
1285
|
},
|
|
1285
1286
|
status: {
|
|
1286
|
-
description: 'Show comprehensive system status (
|
|
1287
|
+
description: 'Show comprehensive system status (admin only)',
|
|
1287
1288
|
usage: 'eve system status',
|
|
1288
1289
|
examples: ['eve system status', 'eve system status --json'],
|
|
1289
1290
|
},
|
|
@@ -1318,7 +1319,7 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1318
1319
|
],
|
|
1319
1320
|
},
|
|
1320
1321
|
logs: {
|
|
1321
|
-
description: 'Fetch recent logs for a system service (
|
|
1322
|
+
description: 'Fetch recent logs for a system service (admin only)',
|
|
1322
1323
|
usage: 'eve system logs <service> [--tail <n>]',
|
|
1323
1324
|
options: [
|
|
1324
1325
|
'--tail <n> Number of log lines (default: 100)',
|
|
@@ -1329,12 +1330,12 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
1329
1330
|
],
|
|
1330
1331
|
},
|
|
1331
1332
|
pods: {
|
|
1332
|
-
description: 'List pods across the cluster (admin
|
|
1333
|
+
description: 'List pods across the cluster (admin only)',
|
|
1333
1334
|
usage: 'eve system pods',
|
|
1334
1335
|
examples: ['eve system pods'],
|
|
1335
1336
|
},
|
|
1336
1337
|
events: {
|
|
1337
|
-
description: 'List recent cluster events (admin
|
|
1338
|
+
description: 'List recent cluster events (admin only)',
|
|
1338
1339
|
usage: 'eve system events [--limit <n>]',
|
|
1339
1340
|
options: [
|
|
1340
1341
|
'--limit <n> Max number of events (default: 50)',
|
|
@@ -1415,7 +1416,7 @@ function showMainHelp() {
|
|
|
1415
1416
|
console.log('Global options:');
|
|
1416
1417
|
console.log(' --help Show help for command');
|
|
1417
1418
|
console.log(' --api-url <url> Override API URL');
|
|
1418
|
-
console.log(' --profile <name> Use named profile');
|
|
1419
|
+
console.log(' --profile <name> Use named repo profile');
|
|
1419
1420
|
console.log(' --org <id> Override default org');
|
|
1420
1421
|
console.log(' --project <id> Override default project');
|
|
1421
1422
|
console.log(' --json Output as JSON');
|