@eve-horizon/cli 0.2.9 → 0.2.11
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 +54 -4
- package/dist/commands/agents.js +157 -39
- package/dist/commands/auth.js +20 -9
- package/dist/commands/build.js +6 -15
- package/dist/commands/chat.js +55 -0
- package/dist/commands/env.js +7 -31
- package/dist/commands/integrations.js +73 -0
- package/dist/commands/pipeline.js +6 -15
- package/dist/commands/profile.js +68 -120
- package/dist/index.js +10 -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 +158 -0
- package/dist/lib/help.js +119 -46
- 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 \
|
|
@@ -151,6 +175,32 @@ eve job approve MyProj-abc123 --comment "LGTM"
|
|
|
151
175
|
eve job reject MyProj-abc123 --reason "Missing tests"
|
|
152
176
|
```
|
|
153
177
|
|
|
178
|
+
### Agents
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Inspect agent policy + harness readiness
|
|
182
|
+
eve agents config --json
|
|
183
|
+
|
|
184
|
+
# Sync agents/teams/chat config from repo (deterministic)
|
|
185
|
+
eve agents sync --project proj_xxx --ref main
|
|
186
|
+
|
|
187
|
+
# Local dev sync (requires local API + allow dirty)
|
|
188
|
+
eve agents sync --project proj_xxx --local --allow-dirty
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Integrations + Chat
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Connect Slack workspace (stub OAuth)
|
|
195
|
+
eve integrations slack connect --org org_xxx --team-id T123 --token xoxb-...
|
|
196
|
+
|
|
197
|
+
# List integrations for org
|
|
198
|
+
eve integrations list --org org_xxx
|
|
199
|
+
|
|
200
|
+
# Simulate inbound Slack message
|
|
201
|
+
eve chat simulate --project proj_xxx --team-id T123 --channel-id C123 --user-id U123 --text "hello"
|
|
202
|
+
```
|
|
203
|
+
|
|
154
204
|
#### Job Results
|
|
155
205
|
|
|
156
206
|
Fetch and display completed job results:
|
package/dist/commands/agents.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleAgents = handleAgents;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
4
5
|
const node_fs_1 = require("node:fs");
|
|
5
6
|
const node_path_1 = require("node:path");
|
|
6
7
|
const yaml_1 = require("yaml");
|
|
@@ -8,6 +9,7 @@ const harness_capabilities_js_1 = require("../lib/harness-capabilities.js");
|
|
|
8
9
|
const args_1 = require("../lib/args");
|
|
9
10
|
const client_1 = require("../lib/client");
|
|
10
11
|
const output_1 = require("../lib/output");
|
|
12
|
+
const git_js_1 = require("../lib/git.js");
|
|
11
13
|
function readYamlFile(filePath) {
|
|
12
14
|
const raw = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
13
15
|
const parsed = (0, yaml_1.parse)(raw);
|
|
@@ -16,6 +18,41 @@ function readYamlFile(filePath) {
|
|
|
16
18
|
}
|
|
17
19
|
return parsed;
|
|
18
20
|
}
|
|
21
|
+
function resolveAgentsConfigPaths(repoRoot, manifest) {
|
|
22
|
+
const xEve = manifest['x-eve'] ||
|
|
23
|
+
manifest['x_eve'] ||
|
|
24
|
+
{};
|
|
25
|
+
const agentsBlock = xEve['agents'] || {};
|
|
26
|
+
const chatBlock = xEve['chat'] || {};
|
|
27
|
+
const manifestChat = manifest['chat'] || {};
|
|
28
|
+
const agentsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.config_path) ?? 'agents/agents.yaml');
|
|
29
|
+
const teamsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.teams_path) ?? 'agents/teams.yaml');
|
|
30
|
+
const chatPath = (0, node_path_1.resolve)(repoRoot, pickString(chatBlock.config_path) ?? pickString(manifestChat.config_path) ?? 'agents/chat.yaml');
|
|
31
|
+
return { agentsPath, teamsPath, chatPath };
|
|
32
|
+
}
|
|
33
|
+
function pickString(value) {
|
|
34
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
35
|
+
}
|
|
36
|
+
function ensureFileExists(path, label) {
|
|
37
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
38
|
+
throw new Error(`Missing ${label} at ${path}. Update manifest config_path or add the file.`);
|
|
39
|
+
}
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
function isLocalApiUrl(apiUrl) {
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(apiUrl);
|
|
45
|
+
const host = url.hostname.toLowerCase();
|
|
46
|
+
return (host === 'localhost' ||
|
|
47
|
+
host === '127.0.0.1' ||
|
|
48
|
+
host === '0.0.0.0' ||
|
|
49
|
+
host.endsWith('.lvh.me') ||
|
|
50
|
+
host.endsWith('.localhost'));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
19
56
|
function loadAgentsConfig(repoRoot) {
|
|
20
57
|
const eveDir = (0, node_path_1.join)(repoRoot, '.eve');
|
|
21
58
|
const manifestPath = (0, node_path_1.join)(eveDir, 'manifest.yaml');
|
|
@@ -34,45 +71,126 @@ async function handleAgents(subcommand, positionals, flags, context) {
|
|
|
34
71
|
const command = subcommand ?? 'config';
|
|
35
72
|
const json = Boolean(flags.json);
|
|
36
73
|
const includeHarnesses = !((0, args_1.getBooleanFlag)(flags, ['no-harnesses']) ?? false);
|
|
37
|
-
const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['path']) ?? process.cwd());
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir', 'path']) ?? process.cwd());
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'config': {
|
|
77
|
+
const result = loadAgentsConfig(repoRoot);
|
|
78
|
+
const response = {
|
|
79
|
+
repo_root: repoRoot,
|
|
80
|
+
source: result.source,
|
|
81
|
+
policy: result.policy,
|
|
82
|
+
};
|
|
83
|
+
if (result.manifest_defaults) {
|
|
84
|
+
response.manifest_defaults = result.manifest_defaults;
|
|
85
|
+
}
|
|
86
|
+
if (includeHarnesses) {
|
|
87
|
+
const harnesses = await (0, client_1.requestJson)(context, '/harnesses');
|
|
88
|
+
response.harnesses = harnesses.data;
|
|
89
|
+
response.capabilities = harness_capabilities_js_1.HARNESS_CAPABILITIES;
|
|
90
|
+
}
|
|
91
|
+
if (json) {
|
|
92
|
+
(0, output_1.outputJson)(response, json);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`Agents config source: ${result.source.type}`);
|
|
96
|
+
if ('path' in result.source) {
|
|
97
|
+
console.log(`Path: ${result.source.path}`);
|
|
98
|
+
}
|
|
99
|
+
if (!result.policy) {
|
|
100
|
+
console.log('No policy found. Add x-eve.agents to .eve/manifest.yaml.');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const profiles = result.policy.profiles || {};
|
|
104
|
+
const profileNames = Object.keys(profiles);
|
|
105
|
+
console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
|
|
106
|
+
}
|
|
107
|
+
if (includeHarnesses) {
|
|
108
|
+
const harnesses = response.harnesses;
|
|
109
|
+
if (harnesses?.length) {
|
|
110
|
+
const ready = harnesses.filter((h) => h.auth.available).length;
|
|
111
|
+
console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
case 'sync': {
|
|
117
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
118
|
+
if (!projectId) {
|
|
119
|
+
throw new Error('Missing project id. Provide --project or set a profile default.');
|
|
120
|
+
}
|
|
121
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
122
|
+
const local = (0, args_1.getBooleanFlag)(flags, ['local']) ?? false;
|
|
123
|
+
const allowDirty = (0, args_1.getBooleanFlag)(flags, ['allow-dirty', 'allow_dirty']) ?? false;
|
|
124
|
+
const forceNonlocal = (0, args_1.getBooleanFlag)(flags, ['force-nonlocal', 'force_nonlocal']) ?? false;
|
|
125
|
+
if (local && ref) {
|
|
126
|
+
throw new Error('Use either --local or --ref, not both.');
|
|
127
|
+
}
|
|
128
|
+
if (!local && !ref) {
|
|
129
|
+
throw new Error('Missing --ref. Use --ref <sha|branch> or --local for dev sync.');
|
|
130
|
+
}
|
|
131
|
+
if (local && !forceNonlocal && !isLocalApiUrl(context.apiUrl)) {
|
|
132
|
+
throw new Error(`--local sync is only allowed for local API URLs (localhost or *.lvh.me). ` +
|
|
133
|
+
`Current API: ${context.apiUrl}. Use --force-nonlocal to override.`);
|
|
134
|
+
}
|
|
135
|
+
const gitRoot = (0, git_js_1.getGitRoot)(repoRoot);
|
|
136
|
+
if (!gitRoot) {
|
|
137
|
+
throw new Error('Not a git repository. Run from the repo root or pass --repo-dir <path>.');
|
|
138
|
+
}
|
|
139
|
+
const dirty = (0, git_js_1.isGitDirty)(gitRoot);
|
|
140
|
+
if (dirty && !allowDirty) {
|
|
141
|
+
throw new Error('Working tree is dirty. Commit changes or pass --allow-dirty to sync anyway.');
|
|
142
|
+
}
|
|
143
|
+
const manifestPath = (0, node_path_1.join)(repoRoot, '.eve', 'manifest.yaml');
|
|
144
|
+
if (!(0, node_fs_1.existsSync)(manifestPath)) {
|
|
145
|
+
throw new Error(`Missing manifest at ${manifestPath}. Expected .eve/manifest.yaml.`);
|
|
146
|
+
}
|
|
147
|
+
const manifest = readYamlFile(manifestPath);
|
|
148
|
+
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
149
|
+
const agentsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.agentsPath, 'agents config'), 'utf-8');
|
|
150
|
+
const teamsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.teamsPath, 'teams config'), 'utf-8');
|
|
151
|
+
const chatYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.chatPath, 'chat config'), 'utf-8');
|
|
152
|
+
let gitSha;
|
|
153
|
+
let branch;
|
|
154
|
+
let gitRef = ref ?? 'local';
|
|
155
|
+
if (ref) {
|
|
156
|
+
gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoRoot);
|
|
157
|
+
branch = (0, git_js_1.resolveGitBranch)(gitRoot, ref) ?? undefined;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
gitSha = (0, node_child_process_1.execSync)('git rev-parse HEAD', { cwd: gitRoot, encoding: 'utf-8' }).trim();
|
|
161
|
+
branch = (0, git_js_1.getGitBranch)(gitRoot) ?? undefined;
|
|
162
|
+
}
|
|
163
|
+
if (dirty) {
|
|
164
|
+
gitRef = `dirty:${gitRef}`;
|
|
165
|
+
}
|
|
166
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/agents/sync`, {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
body: {
|
|
169
|
+
agents_yaml: agentsYaml,
|
|
170
|
+
teams_yaml: teamsYaml,
|
|
171
|
+
chat_yaml: chatYaml,
|
|
172
|
+
git_sha: gitSha,
|
|
173
|
+
branch,
|
|
174
|
+
git_ref: gitRef,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
if (json) {
|
|
178
|
+
(0, output_1.outputJson)(response, json);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
console.log(`✓ Agents config synced to ${projectId}`);
|
|
182
|
+
console.log(` Agents: ${configPaths.agentsPath}`);
|
|
183
|
+
console.log(` Teams: ${configPaths.teamsPath}`);
|
|
184
|
+
console.log(` Chat: ${configPaths.chatPath}`);
|
|
185
|
+
if (gitSha)
|
|
186
|
+
console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
|
|
187
|
+
if (branch)
|
|
188
|
+
console.log(` Branch: ${branch}`);
|
|
189
|
+
if (dirty)
|
|
190
|
+
console.log(' Warning: working tree dirty — marked non-deployable');
|
|
191
|
+
return;
|
|
76
192
|
}
|
|
193
|
+
default:
|
|
194
|
+
throw new Error('Usage: eve agents <config|sync>');
|
|
77
195
|
}
|
|
78
196
|
}
|
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) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleChat = handleChat;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handleChat(subcommand, _positionals, flags, context) {
|
|
8
|
+
const json = Boolean(flags.json);
|
|
9
|
+
switch (subcommand) {
|
|
10
|
+
case 'simulate': {
|
|
11
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
12
|
+
if (!projectId) {
|
|
13
|
+
throw new Error('Missing project id. Provide --project or set a profile default.');
|
|
14
|
+
}
|
|
15
|
+
const provider = (0, args_1.getStringFlag)(flags, ['provider']) ?? 'slack';
|
|
16
|
+
const teamId = (0, args_1.getStringFlag)(flags, ['team-id']);
|
|
17
|
+
const text = (0, args_1.getStringFlag)(flags, ['text']);
|
|
18
|
+
if (!teamId || !text) {
|
|
19
|
+
throw new Error('Usage: eve chat simulate --project <id> --team-id <team> --text <message>');
|
|
20
|
+
}
|
|
21
|
+
const channelId = (0, args_1.getStringFlag)(flags, ['channel-id']);
|
|
22
|
+
const userId = (0, args_1.getStringFlag)(flags, ['user-id']);
|
|
23
|
+
const threadKey = (0, args_1.getStringFlag)(flags, ['thread-key']);
|
|
24
|
+
const metadataFlag = (0, args_1.getStringFlag)(flags, ['metadata']);
|
|
25
|
+
let metadata;
|
|
26
|
+
if (metadataFlag) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(metadataFlag);
|
|
29
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
30
|
+
metadata = parsed;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error('Invalid --metadata (must be JSON object)');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/chat/simulate`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: {
|
|
40
|
+
provider,
|
|
41
|
+
team_id: teamId,
|
|
42
|
+
channel_id: channelId,
|
|
43
|
+
user_id: userId,
|
|
44
|
+
text,
|
|
45
|
+
thread_key: threadKey,
|
|
46
|
+
metadata,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
(0, output_1.outputJson)(response, json, `✓ Chat simulated (thread: ${response.thread_id})`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
throw new Error('Usage: eve chat <simulate>');
|
|
54
|
+
}
|
|
55
|
+
}
|
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
|
*/
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleIntegrations = handleIntegrations;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handleIntegrations(subcommand, positionals, flags, context) {
|
|
8
|
+
const json = Boolean(flags.json);
|
|
9
|
+
const orgId = (0, args_1.getStringFlag)(flags, ['org']) ?? context.orgId;
|
|
10
|
+
switch (subcommand) {
|
|
11
|
+
case 'list': {
|
|
12
|
+
if (!orgId) {
|
|
13
|
+
throw new Error('Usage: eve integrations list --org <org_id>');
|
|
14
|
+
}
|
|
15
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations`);
|
|
16
|
+
(0, output_1.outputJson)(response, json);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
case 'slack': {
|
|
20
|
+
const action = positionals[0];
|
|
21
|
+
if (action !== 'connect') {
|
|
22
|
+
throw new Error('Usage: eve integrations slack connect --org <org_id> --team-id <team_id> [--token <token>]');
|
|
23
|
+
}
|
|
24
|
+
if (!orgId) {
|
|
25
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
26
|
+
}
|
|
27
|
+
const teamId = (0, args_1.getStringFlag)(flags, ['team-id']);
|
|
28
|
+
if (!teamId) {
|
|
29
|
+
throw new Error('Missing --team-id');
|
|
30
|
+
}
|
|
31
|
+
const token = (0, args_1.getStringFlag)(flags, ['token']);
|
|
32
|
+
const tokensJsonFlag = (0, args_1.getStringFlag)(flags, ['tokens-json']);
|
|
33
|
+
const status = (0, args_1.getStringFlag)(flags, ['status']);
|
|
34
|
+
let tokensJson;
|
|
35
|
+
if (tokensJsonFlag) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(tokensJsonFlag);
|
|
38
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
39
|
+
tokensJson = parsed;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error('Invalid --tokens-json (must be JSON object)');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!tokensJson && token) {
|
|
47
|
+
tokensJson = { access_token: token };
|
|
48
|
+
}
|
|
49
|
+
const body = { team_id: teamId };
|
|
50
|
+
if (tokensJson)
|
|
51
|
+
body.tokens_json = tokensJson;
|
|
52
|
+
if (status)
|
|
53
|
+
body.status = status;
|
|
54
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations/slack/connect`, { method: 'POST', body });
|
|
55
|
+
(0, output_1.outputJson)(response, json, `✓ Slack integration connected: ${response.id}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
case 'test': {
|
|
59
|
+
const integrationId = positionals[0];
|
|
60
|
+
if (!integrationId) {
|
|
61
|
+
throw new Error('Usage: eve integrations test <integration_id>');
|
|
62
|
+
}
|
|
63
|
+
if (!orgId) {
|
|
64
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
65
|
+
}
|
|
66
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/integrations/${integrationId}/test`, { method: 'POST' });
|
|
67
|
+
(0, output_1.outputJson)(response, json, response.ok ? '✓ Integration test ok' : 'Integration test failed');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
throw new Error('Usage: eve integrations <list|slack|test>');
|
|
72
|
+
}
|
|
73
|
+
}
|