@eve-horizon/cli 0.2.10 → 0.2.12
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 +26 -0
- package/dist/commands/agents.js +401 -34
- package/dist/commands/auth.js +30 -1
- package/dist/commands/chat.js +55 -0
- package/dist/commands/env.js +142 -3
- package/dist/commands/init.js +16 -26
- package/dist/commands/integrations.js +73 -0
- package/dist/commands/job.js +20 -0
- package/dist/commands/migrate.js +136 -0
- package/dist/commands/org.js +45 -3
- package/dist/commands/packs.js +197 -0
- package/dist/commands/project.js +38 -1
- package/dist/commands/skills.js +15 -98
- package/dist/commands/supervise.js +60 -0
- package/dist/commands/system.js +1 -1
- package/dist/commands/thread.js +153 -0
- package/dist/index.js +24 -0
- package/dist/lib/git.js +48 -1
- package/dist/lib/help.js +339 -18
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -175,6 +175,32 @@ eve job approve MyProj-abc123 --comment "LGTM"
|
|
|
175
175
|
eve job reject MyProj-abc123 --reason "Missing tests"
|
|
176
176
|
```
|
|
177
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
|
+
|
|
178
204
|
#### Job Results
|
|
179
205
|
|
|
180
206
|
Fetch and display completed job results:
|
package/dist/commands/agents.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
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
|
+
const shared_1 = require("@eve/shared");
|
|
7
9
|
const harness_capabilities_js_1 = require("../lib/harness-capabilities.js");
|
|
8
10
|
const args_1 = require("../lib/args");
|
|
9
11
|
const client_1 = require("../lib/client");
|
|
10
12
|
const output_1 = require("../lib/output");
|
|
13
|
+
const git_js_1 = require("../lib/git.js");
|
|
11
14
|
function readYamlFile(filePath) {
|
|
12
15
|
const raw = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
13
16
|
const parsed = (0, yaml_1.parse)(raw);
|
|
@@ -16,6 +19,41 @@ function readYamlFile(filePath) {
|
|
|
16
19
|
}
|
|
17
20
|
return parsed;
|
|
18
21
|
}
|
|
22
|
+
function resolveAgentsConfigPaths(repoRoot, manifest) {
|
|
23
|
+
const xEve = manifest['x-eve'] ||
|
|
24
|
+
manifest['x_eve'] ||
|
|
25
|
+
{};
|
|
26
|
+
const agentsBlock = xEve['agents'] || {};
|
|
27
|
+
const chatBlock = xEve['chat'] || {};
|
|
28
|
+
const manifestChat = manifest['chat'] || {};
|
|
29
|
+
const agentsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.config_path) ?? 'agents/agents.yaml');
|
|
30
|
+
const teamsPath = (0, node_path_1.resolve)(repoRoot, pickString(agentsBlock.teams_path) ?? 'agents/teams.yaml');
|
|
31
|
+
const chatPath = (0, node_path_1.resolve)(repoRoot, pickString(chatBlock.config_path) ?? pickString(manifestChat.config_path) ?? 'agents/chat.yaml');
|
|
32
|
+
return { agentsPath, teamsPath, chatPath };
|
|
33
|
+
}
|
|
34
|
+
function pickString(value) {
|
|
35
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
36
|
+
}
|
|
37
|
+
function ensureFileExists(path, label) {
|
|
38
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
39
|
+
throw new Error(`Missing ${label} at ${path}. Update manifest config_path or add the file.`);
|
|
40
|
+
}
|
|
41
|
+
return path;
|
|
42
|
+
}
|
|
43
|
+
function isLocalApiUrl(apiUrl) {
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(apiUrl);
|
|
46
|
+
const host = url.hostname.toLowerCase();
|
|
47
|
+
return (host === 'localhost' ||
|
|
48
|
+
host === '127.0.0.1' ||
|
|
49
|
+
host === '0.0.0.0' ||
|
|
50
|
+
host.endsWith('.lvh.me') ||
|
|
51
|
+
host.endsWith('.localhost'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
19
57
|
function loadAgentsConfig(repoRoot) {
|
|
20
58
|
const eveDir = (0, node_path_1.join)(repoRoot, '.eve');
|
|
21
59
|
const manifestPath = (0, node_path_1.join)(eveDir, 'manifest.yaml');
|
|
@@ -30,49 +68,378 @@ function loadAgentsConfig(repoRoot) {
|
|
|
30
68
|
}
|
|
31
69
|
return { source: { type: 'none' }, policy: null };
|
|
32
70
|
}
|
|
71
|
+
async function resolvePacksAndMerge(repoRoot, manifest, projectSlug) {
|
|
72
|
+
const xEve = (manifest['x-eve'] ?? manifest['x_eve']);
|
|
73
|
+
const packs = (xEve?.packs ?? []);
|
|
74
|
+
if (packs.length === 0)
|
|
75
|
+
return null;
|
|
76
|
+
console.log(`Resolving ${packs.length} pack(s)...`);
|
|
77
|
+
// 1. Resolve all packs
|
|
78
|
+
const resolvedPacks = [];
|
|
79
|
+
for (const entry of packs) {
|
|
80
|
+
const resolved = await (0, shared_1.resolvePack)(entry, projectSlug, repoRoot);
|
|
81
|
+
resolvedPacks.push(resolved);
|
|
82
|
+
console.log(` ✓ ${resolved.id} (${resolved.skillPaths.length} skills)`);
|
|
83
|
+
}
|
|
84
|
+
// 2. Check for agent ID collisions across packs
|
|
85
|
+
const seenAgentIds = new Map();
|
|
86
|
+
for (const pack of resolvedPacks) {
|
|
87
|
+
for (const agentId of Object.keys(pack.agents)) {
|
|
88
|
+
if (seenAgentIds.has(agentId)) {
|
|
89
|
+
throw new Error(`Agent ID collision: "${agentId}" defined in both pack "${seenAgentIds.get(agentId)}" and "${pack.id}". ` +
|
|
90
|
+
`Agent IDs must be unique across all packs.`);
|
|
91
|
+
}
|
|
92
|
+
seenAgentIds.set(agentId, pack.id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 3. Merge pack bases in listed order
|
|
96
|
+
let mergedAgents = {};
|
|
97
|
+
let mergedTeams = {};
|
|
98
|
+
let mergedChat = { routes: [] };
|
|
99
|
+
const xEveFragments = [];
|
|
100
|
+
for (const pack of resolvedPacks) {
|
|
101
|
+
mergedAgents = (0, shared_1.mergeMapConfig)(mergedAgents, pack.agents);
|
|
102
|
+
mergedTeams = (0, shared_1.mergeMapConfig)(mergedTeams, pack.teams);
|
|
103
|
+
if (pack.chat) {
|
|
104
|
+
mergedChat = (0, shared_1.mergeChatConfig)(mergedChat, pack.chat);
|
|
105
|
+
}
|
|
106
|
+
if (pack.xEve) {
|
|
107
|
+
xEveFragments.push(pack.xEve);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 4. Load project overlay files and merge on top
|
|
111
|
+
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
112
|
+
if ((0, node_fs_1.existsSync)(configPaths.agentsPath)) {
|
|
113
|
+
const projectAgents = (0, yaml_1.parse)((0, node_fs_1.readFileSync)(configPaths.agentsPath, 'utf-8')) ?? {};
|
|
114
|
+
mergedAgents = (0, shared_1.mergeMapConfig)(mergedAgents, projectAgents);
|
|
115
|
+
}
|
|
116
|
+
if ((0, node_fs_1.existsSync)(configPaths.teamsPath)) {
|
|
117
|
+
const projectTeams = (0, yaml_1.parse)((0, node_fs_1.readFileSync)(configPaths.teamsPath, 'utf-8')) ?? {};
|
|
118
|
+
mergedTeams = (0, shared_1.mergeMapConfig)(mergedTeams, projectTeams);
|
|
119
|
+
}
|
|
120
|
+
if ((0, node_fs_1.existsSync)(configPaths.chatPath)) {
|
|
121
|
+
const projectChat = (0, yaml_1.parse)((0, node_fs_1.readFileSync)(configPaths.chatPath, 'utf-8')) ?? {};
|
|
122
|
+
mergedChat = (0, shared_1.mergeChatConfig)(mergedChat, projectChat);
|
|
123
|
+
}
|
|
124
|
+
// 5. Merge x-eve (strip packs + install_agents from project overlay)
|
|
125
|
+
const projectXEve = (xEve ?? {});
|
|
126
|
+
const { packs: _p, install_agents: _ia, ...projectXEveRest } = projectXEve;
|
|
127
|
+
(0, shared_1.mergeXEve)(xEveFragments, projectXEveRest);
|
|
128
|
+
// 6. Validate effective config
|
|
129
|
+
validateEffectiveConfig(mergedAgents, mergedTeams, mergedChat);
|
|
130
|
+
// 7. Write lockfile
|
|
131
|
+
const lockfile = {
|
|
132
|
+
resolved_at: new Date().toISOString(),
|
|
133
|
+
project_slug: projectSlug,
|
|
134
|
+
packs: resolvedPacks.map((p) => ({
|
|
135
|
+
id: p.id,
|
|
136
|
+
source: p.source,
|
|
137
|
+
ref: p.ref,
|
|
138
|
+
pack_version: 1,
|
|
139
|
+
})),
|
|
140
|
+
effective: {
|
|
141
|
+
agents_count: Object.keys((0, shared_1.extractInnerMap)(mergedAgents, 'agents')).length,
|
|
142
|
+
teams_count: Object.keys((0, shared_1.extractInnerMap)(mergedTeams, 'teams')).length,
|
|
143
|
+
routes_count: (mergedChat.routes ?? []).length,
|
|
144
|
+
profiles_count: 0,
|
|
145
|
+
agents_hash: simpleHash(JSON.stringify(mergedAgents)),
|
|
146
|
+
teams_hash: simpleHash(JSON.stringify(mergedTeams)),
|
|
147
|
+
chat_hash: simpleHash(JSON.stringify(mergedChat)),
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
const eveDir = (0, node_path_1.join)(repoRoot, '.eve');
|
|
151
|
+
(0, node_fs_1.mkdirSync)(eveDir, { recursive: true });
|
|
152
|
+
const lockfilePath = (0, node_path_1.join)(eveDir, 'packs.lock.yaml');
|
|
153
|
+
(0, node_fs_1.writeFileSync)(lockfilePath, (0, yaml_1.stringify)(lockfile), 'utf-8');
|
|
154
|
+
console.log(` ✓ Lockfile written: .eve/packs.lock.yaml`);
|
|
155
|
+
// 8. Return effective config as YAML strings
|
|
156
|
+
const packRefs = resolvedPacks.map((p) => ({ id: p.id, source: p.source, ref: p.ref }));
|
|
157
|
+
return {
|
|
158
|
+
agentsYaml: (0, yaml_1.stringify)(mergedAgents),
|
|
159
|
+
teamsYaml: (0, yaml_1.stringify)(mergedTeams),
|
|
160
|
+
chatYaml: (0, yaml_1.stringify)(mergedChat),
|
|
161
|
+
packRefs,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function validateEffectiveConfig(agents, teams, chat) {
|
|
165
|
+
// Extract inner maps (handles both nested `{ version: 1, agents: { ... } }` and flat formats)
|
|
166
|
+
const agentsMap = (0, shared_1.extractInnerMap)(agents, 'agents');
|
|
167
|
+
const teamsMap = (0, shared_1.extractInnerMap)(teams, 'teams');
|
|
168
|
+
// Check slug uniqueness
|
|
169
|
+
const slugs = new Set();
|
|
170
|
+
for (const [, entry] of Object.entries(agentsMap)) {
|
|
171
|
+
if (typeof entry !== 'object' || entry === null)
|
|
172
|
+
continue;
|
|
173
|
+
const agent = entry;
|
|
174
|
+
const slug = agent.slug;
|
|
175
|
+
if (slug) {
|
|
176
|
+
if (slugs.has(slug)) {
|
|
177
|
+
throw new Error(`Duplicate agent slug "${slug}" in effective config. Slugs must be unique.`);
|
|
178
|
+
}
|
|
179
|
+
slugs.add(slug);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Check team references
|
|
183
|
+
const agentIds = new Set(Object.keys(agentsMap));
|
|
184
|
+
for (const [teamId, entry] of Object.entries(teamsMap)) {
|
|
185
|
+
if (typeof entry !== 'object' || entry === null)
|
|
186
|
+
continue;
|
|
187
|
+
const team = entry;
|
|
188
|
+
const lead = team.lead;
|
|
189
|
+
if (lead && !agentIds.has(lead)) {
|
|
190
|
+
throw new Error(`Team "${teamId}" references unknown agent "${lead}" as lead.`);
|
|
191
|
+
}
|
|
192
|
+
const members = (team.members ?? []);
|
|
193
|
+
for (const member of members) {
|
|
194
|
+
if (!agentIds.has(member)) {
|
|
195
|
+
throw new Error(`Team "${teamId}" references unknown agent "${member}" as member.`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Check route targets (supports "team:name" and "agent:name" prefixes)
|
|
200
|
+
const teamIds = new Set(Object.keys(teamsMap));
|
|
201
|
+
const routes = (chat.routes ?? []);
|
|
202
|
+
for (const route of routes) {
|
|
203
|
+
if (!route.target)
|
|
204
|
+
continue;
|
|
205
|
+
let targetId = route.target;
|
|
206
|
+
if (targetId.startsWith('team:')) {
|
|
207
|
+
targetId = targetId.slice(5);
|
|
208
|
+
if (!teamIds.has(targetId)) {
|
|
209
|
+
throw new Error(`Route "${route.id}" targets unknown team "${route.target}".`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (targetId.startsWith('agent:')) {
|
|
213
|
+
targetId = targetId.slice(6);
|
|
214
|
+
if (!agentIds.has(targetId)) {
|
|
215
|
+
throw new Error(`Route "${route.id}" targets unknown agent "${route.target}".`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else if (!agentIds.has(targetId) && !teamIds.has(targetId)) {
|
|
219
|
+
throw new Error(`Route "${route.id}" targets unknown agent/team "${route.target}".`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function simpleHash(input) {
|
|
224
|
+
let hash = 0;
|
|
225
|
+
for (let i = 0; i < input.length; i++) {
|
|
226
|
+
const char = input.charCodeAt(i);
|
|
227
|
+
hash = ((hash << 5) - hash) + char;
|
|
228
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
229
|
+
}
|
|
230
|
+
return Math.abs(hash).toString(16).padStart(8, '0');
|
|
231
|
+
}
|
|
33
232
|
async function handleAgents(subcommand, positionals, flags, context) {
|
|
34
233
|
const command = subcommand ?? 'config';
|
|
35
234
|
const json = Boolean(flags.json);
|
|
36
235
|
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
|
-
|
|
236
|
+
const repoRoot = (0, node_path_1.resolve)((0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir', 'path']) ?? process.cwd());
|
|
237
|
+
switch (command) {
|
|
238
|
+
case 'config': {
|
|
239
|
+
const result = loadAgentsConfig(repoRoot);
|
|
240
|
+
const response = {
|
|
241
|
+
repo_root: repoRoot,
|
|
242
|
+
source: result.source,
|
|
243
|
+
policy: result.policy,
|
|
244
|
+
};
|
|
245
|
+
if (result.manifest_defaults) {
|
|
246
|
+
response.manifest_defaults = result.manifest_defaults;
|
|
247
|
+
}
|
|
248
|
+
if (includeHarnesses) {
|
|
249
|
+
const harnesses = await (0, client_1.requestJson)(context, '/harnesses');
|
|
250
|
+
response.harnesses = harnesses.data;
|
|
251
|
+
response.capabilities = harness_capabilities_js_1.HARNESS_CAPABILITIES;
|
|
252
|
+
}
|
|
253
|
+
if (json) {
|
|
254
|
+
(0, output_1.outputJson)(response, json);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
console.log(`Agents config source: ${result.source.type}`);
|
|
258
|
+
if ('path' in result.source) {
|
|
259
|
+
console.log(`Path: ${result.source.path}`);
|
|
260
|
+
}
|
|
261
|
+
if (!result.policy) {
|
|
262
|
+
console.log('No policy found. Add x-eve.agents to .eve/manifest.yaml.');
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
const profiles = result.policy.profiles || {};
|
|
266
|
+
const profileNames = Object.keys(profiles);
|
|
267
|
+
console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
|
|
268
|
+
}
|
|
269
|
+
if (includeHarnesses) {
|
|
270
|
+
const harnesses = response.harnesses;
|
|
271
|
+
if (harnesses?.length) {
|
|
272
|
+
const ready = harnesses.filter((h) => h.auth.available).length;
|
|
273
|
+
console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
case 'sync': {
|
|
279
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
280
|
+
if (!projectId) {
|
|
281
|
+
throw new Error('Missing project id. Provide --project or set a profile default.');
|
|
282
|
+
}
|
|
283
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
284
|
+
const local = (0, args_1.getBooleanFlag)(flags, ['local']) ?? false;
|
|
285
|
+
const allowDirty = (0, args_1.getBooleanFlag)(flags, ['allow-dirty', 'allow_dirty']) ?? false;
|
|
286
|
+
const forceNonlocal = (0, args_1.getBooleanFlag)(flags, ['force-nonlocal', 'force_nonlocal']) ?? false;
|
|
287
|
+
if (local && ref) {
|
|
288
|
+
throw new Error('Use either --local or --ref, not both.');
|
|
289
|
+
}
|
|
290
|
+
if (!local && !ref) {
|
|
291
|
+
throw new Error('Missing --ref. Use --ref <sha|branch> or --local for dev sync.');
|
|
292
|
+
}
|
|
293
|
+
if (local && !forceNonlocal && !isLocalApiUrl(context.apiUrl)) {
|
|
294
|
+
throw new Error(`--local sync is only allowed for local API URLs (localhost or *.lvh.me). ` +
|
|
295
|
+
`Current API: ${context.apiUrl}. Use --force-nonlocal to override.`);
|
|
296
|
+
}
|
|
297
|
+
const gitRoot = (0, git_js_1.getGitRoot)(repoRoot);
|
|
298
|
+
if (!gitRoot) {
|
|
299
|
+
throw new Error('Not a git repository. Run from the repo root or pass --repo-dir <path>.');
|
|
300
|
+
}
|
|
301
|
+
const dirty = (0, git_js_1.isGitDirty)(gitRoot);
|
|
302
|
+
if (dirty && !allowDirty) {
|
|
303
|
+
throw new Error('Working tree is dirty. Commit changes or pass --allow-dirty to sync anyway.');
|
|
304
|
+
}
|
|
305
|
+
const manifestPath = (0, node_path_1.join)(repoRoot, '.eve', 'manifest.yaml');
|
|
306
|
+
if (!(0, node_fs_1.existsSync)(manifestPath)) {
|
|
307
|
+
throw new Error(`Missing manifest at ${manifestPath}. Expected .eve/manifest.yaml.`);
|
|
308
|
+
}
|
|
309
|
+
const manifest = readYamlFile(manifestPath);
|
|
310
|
+
// Pack resolution: if packs exist, resolve and merge before posting
|
|
311
|
+
const projectSlug = manifest.project ?? projectId.replace(/^proj_/, '');
|
|
312
|
+
const packResult = await resolvePacksAndMerge(repoRoot, manifest, projectSlug);
|
|
313
|
+
let agentsYaml;
|
|
314
|
+
let teamsYaml;
|
|
315
|
+
let chatYaml;
|
|
316
|
+
let packRefs;
|
|
317
|
+
if (packResult) {
|
|
318
|
+
agentsYaml = packResult.agentsYaml;
|
|
319
|
+
teamsYaml = packResult.teamsYaml;
|
|
320
|
+
chatYaml = packResult.chatYaml;
|
|
321
|
+
packRefs = packResult.packRefs;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
325
|
+
agentsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.agentsPath, 'agents config'), 'utf-8');
|
|
326
|
+
teamsYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.teamsPath, 'teams config'), 'utf-8');
|
|
327
|
+
chatYaml = (0, node_fs_1.readFileSync)(ensureFileExists(configPaths.chatPath, 'chat config'), 'utf-8');
|
|
328
|
+
}
|
|
329
|
+
let gitSha;
|
|
330
|
+
let branch;
|
|
331
|
+
let gitRef = ref ?? 'local';
|
|
332
|
+
if (ref) {
|
|
333
|
+
gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoRoot);
|
|
334
|
+
branch = (0, git_js_1.resolveGitBranch)(gitRoot, ref) ?? undefined;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
gitSha = (0, node_child_process_1.execSync)('git rev-parse HEAD', { cwd: gitRoot, encoding: 'utf-8' }).trim();
|
|
338
|
+
branch = (0, git_js_1.getGitBranch)(gitRoot) ?? undefined;
|
|
339
|
+
}
|
|
340
|
+
if (dirty) {
|
|
341
|
+
gitRef = `dirty:${gitRef}`;
|
|
342
|
+
}
|
|
343
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/agents/sync`, {
|
|
344
|
+
method: 'POST',
|
|
345
|
+
body: {
|
|
346
|
+
agents_yaml: agentsYaml,
|
|
347
|
+
teams_yaml: teamsYaml,
|
|
348
|
+
chat_yaml: chatYaml,
|
|
349
|
+
git_sha: gitSha,
|
|
350
|
+
branch,
|
|
351
|
+
git_ref: gitRef,
|
|
352
|
+
pack_refs: packRefs,
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
if (json) {
|
|
356
|
+
(0, output_1.outputJson)(response, json);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
console.log(`✓ Agents config synced to ${projectId}`);
|
|
360
|
+
if (packRefs) {
|
|
361
|
+
console.log(` Packs: ${packRefs.map((p) => p.id).join(', ')}`);
|
|
362
|
+
console.log(` Source: merged (packs + project overlay)`);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
366
|
+
console.log(` Agents: ${configPaths.agentsPath}`);
|
|
367
|
+
console.log(` Teams: ${configPaths.teamsPath}`);
|
|
368
|
+
console.log(` Chat: ${configPaths.chatPath}`);
|
|
369
|
+
}
|
|
370
|
+
if (gitSha)
|
|
371
|
+
console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
|
|
372
|
+
if (branch)
|
|
373
|
+
console.log(` Branch: ${branch}`);
|
|
374
|
+
if (dirty)
|
|
375
|
+
console.log(' Warning: working tree dirty — marked non-deployable');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
case 'runtime-status': {
|
|
379
|
+
const orgId = (0, args_1.getStringFlag)(flags, ['org']) ?? context.orgId;
|
|
380
|
+
if (!orgId) {
|
|
381
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
382
|
+
}
|
|
383
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}/agent-runtime/status`);
|
|
384
|
+
if (json) {
|
|
385
|
+
(0, output_1.outputJson)(response, json);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
formatAgentRuntimeStatus(response, orgId);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
default:
|
|
392
|
+
throw new Error('Usage: eve agents <config|sync|runtime-status>');
|
|
54
393
|
}
|
|
55
|
-
|
|
56
|
-
|
|
394
|
+
}
|
|
395
|
+
function formatAgentRuntimeStatus(response, orgId) {
|
|
396
|
+
console.log(`Agent Runtime Status: ${orgId}`);
|
|
397
|
+
if (response.pods.length === 0) {
|
|
398
|
+
console.log('');
|
|
399
|
+
console.log('No agent runtime pods found.');
|
|
57
400
|
return;
|
|
58
401
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
402
|
+
const nameWidth = Math.max(7, ...response.pods.map((pod) => pod.pod_name.length));
|
|
403
|
+
const statusWidth = Math.max(6, ...response.pods.map((pod) => pod.status.length));
|
|
404
|
+
const capacityWidth = Math.max(8, ...response.pods.map((pod) => String(pod.capacity).length));
|
|
405
|
+
const ageWidth = Math.max(3, ...response.pods.map((pod) => formatAgeSeconds(pod.last_heartbeat_at).length));
|
|
406
|
+
console.log('');
|
|
407
|
+
const header = [
|
|
408
|
+
padRight('Pod', nameWidth),
|
|
409
|
+
padRight('Status', statusWidth),
|
|
410
|
+
padRight('Capacity', capacityWidth),
|
|
411
|
+
padRight('Age', ageWidth),
|
|
412
|
+
'Last Heartbeat',
|
|
413
|
+
].join(' ');
|
|
414
|
+
console.log(header);
|
|
415
|
+
console.log('-'.repeat(header.length));
|
|
416
|
+
for (const pod of response.pods) {
|
|
417
|
+
const age = formatAgeSeconds(pod.last_heartbeat_at);
|
|
418
|
+
console.log([
|
|
419
|
+
padRight(pod.pod_name, nameWidth),
|
|
420
|
+
padRight(pod.status, statusWidth),
|
|
421
|
+
padRight(String(pod.capacity), capacityWidth),
|
|
422
|
+
padRight(age, ageWidth),
|
|
423
|
+
pod.last_heartbeat_at,
|
|
424
|
+
].join(' '));
|
|
62
425
|
}
|
|
63
|
-
|
|
64
|
-
|
|
426
|
+
}
|
|
427
|
+
function padRight(value, width) {
|
|
428
|
+
return value.length >= width ? value : `${value}${' '.repeat(width - value.length)}`;
|
|
429
|
+
}
|
|
430
|
+
function formatAgeSeconds(isoDate) {
|
|
431
|
+
const timestamp = Date.parse(isoDate);
|
|
432
|
+
if (!Number.isFinite(timestamp)) {
|
|
433
|
+
return '-';
|
|
65
434
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log(`Profiles: ${profileNames.length ? profileNames.join(', ') : 'none'}`);
|
|
435
|
+
const diffSeconds = Math.max(0, Math.floor((Date.now() - timestamp) / 1000));
|
|
436
|
+
if (diffSeconds < 60) {
|
|
437
|
+
return `${diffSeconds}s`;
|
|
70
438
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const ready = harnesses.filter((h) => h.auth.available).length;
|
|
75
|
-
console.log(`Harnesses: ${harnesses.length} (${ready} ready)`);
|
|
76
|
-
}
|
|
439
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
440
|
+
if (diffMinutes < 60) {
|
|
441
|
+
return `${diffMinutes}m`;
|
|
77
442
|
}
|
|
443
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
444
|
+
return `${diffHours}h`;
|
|
78
445
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -174,9 +174,38 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
174
174
|
(0, output_1.outputJson)(data, json, 'Auth disabled for this stack.');
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
|
+
if (!json && data.permissions && data.permissions.length > 0) {
|
|
178
|
+
console.log(`User: ${data.user_id ?? 'unknown'} (${data.email ?? 'no email'})`);
|
|
179
|
+
console.log(`Role: ${data.role ?? 'member'}`);
|
|
180
|
+
console.log(`Admin: ${data.is_admin ?? false}`);
|
|
181
|
+
console.log(`\nPermissions (${data.permissions.length}):`);
|
|
182
|
+
for (const perm of data.permissions) {
|
|
183
|
+
console.log(` ${perm}`);
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
177
187
|
(0, output_1.outputJson)(data, json);
|
|
178
188
|
return;
|
|
179
189
|
}
|
|
190
|
+
case 'permissions': {
|
|
191
|
+
const response = await (0, client_1.requestJson)(context, '/auth/permissions');
|
|
192
|
+
if (json) {
|
|
193
|
+
(0, output_1.outputJson)(response, json);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
console.log('Permission Matrix:');
|
|
197
|
+
console.log('');
|
|
198
|
+
const header = 'Permission'.padEnd(24) + 'Member'.padEnd(10) + 'Admin'.padEnd(10) + 'Owner';
|
|
199
|
+
console.log(header);
|
|
200
|
+
console.log('-'.repeat(header.length));
|
|
201
|
+
for (const row of response.matrix) {
|
|
202
|
+
const m = row.member ? '✓' : '-';
|
|
203
|
+
const a = row.admin ? '✓' : '-';
|
|
204
|
+
const o = row.owner ? '✓' : '-';
|
|
205
|
+
console.log(`${row.permission.padEnd(24)}${m.padEnd(10)}${a.padEnd(10)}${o}`);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
180
209
|
case 'bootstrap': {
|
|
181
210
|
const statusOnly = (0, args_1.getBooleanFlag)(flags, ['status']) ?? false;
|
|
182
211
|
// Check bootstrap status first
|
|
@@ -681,7 +710,7 @@ async function handleAuth(subcommand, flags, context, credentials) {
|
|
|
681
710
|
return;
|
|
682
711
|
}
|
|
683
712
|
default:
|
|
684
|
-
throw new Error('Usage: eve auth <login|logout|status|whoami|bootstrap|sync|creds|token>');
|
|
713
|
+
throw new Error('Usage: eve auth <login|logout|status|whoami|bootstrap|sync|creds|token|mint|permissions>');
|
|
685
714
|
}
|
|
686
715
|
}
|
|
687
716
|
function signNonceWithSsh(keyPath, nonce) {
|
|
@@ -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
|
+
}
|