@eve-horizon/cli 0.0.1
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 +275 -0
- package/bin/eve.js +2 -0
- package/dist/commands/api.js +350 -0
- package/dist/commands/auth.js +99 -0
- package/dist/commands/db.js +149 -0
- package/dist/commands/env.js +303 -0
- package/dist/commands/event.js +273 -0
- package/dist/commands/harness.js +96 -0
- package/dist/commands/job.js +2266 -0
- package/dist/commands/org.js +97 -0
- package/dist/commands/pipeline.js +403 -0
- package/dist/commands/profile.js +103 -0
- package/dist/commands/project.js +185 -0
- package/dist/commands/secrets.js +147 -0
- package/dist/commands/system.js +457 -0
- package/dist/commands/workflow.js +337 -0
- package/dist/index.js +97 -0
- package/dist/lib/args.js +57 -0
- package/dist/lib/client.js +116 -0
- package/dist/lib/config.js +76 -0
- package/dist/lib/context.js +45 -0
- package/dist/lib/help.js +938 -0
- package/dist/lib/logs.js +51 -0
- package/dist/lib/output.js +14 -0
- package/package.json +36 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleProject = handleProject;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
async function handleProject(subcommand, positionals, flags, context) {
|
|
11
|
+
const json = Boolean(flags.json);
|
|
12
|
+
switch (subcommand) {
|
|
13
|
+
case 'ensure': {
|
|
14
|
+
const name = typeof flags.name === 'string' ? flags.name : '';
|
|
15
|
+
const repoUrl = typeof flags['repo-url'] === 'string' ? flags['repo-url'] : '';
|
|
16
|
+
const branch = typeof flags.branch === 'string' ? flags.branch : 'main';
|
|
17
|
+
const slug = typeof flags.slug === 'string' ? flags.slug : undefined;
|
|
18
|
+
const orgIdRaw = typeof flags.org === 'string' ? flags.org : context.orgId;
|
|
19
|
+
const force = (0, args_1.toBoolean)(flags.force) ?? false;
|
|
20
|
+
if (!name || !repoUrl) {
|
|
21
|
+
throw new Error('Usage: eve project ensure --name <name> --repo-url <url> [--branch <branch>] [--slug <slug>]');
|
|
22
|
+
}
|
|
23
|
+
if (!orgIdRaw) {
|
|
24
|
+
throw new Error('Missing org id. Provide --org or set a profile default.');
|
|
25
|
+
}
|
|
26
|
+
const body = { org_id: orgIdRaw, name, repo_url: repoUrl, branch, force };
|
|
27
|
+
if (slug)
|
|
28
|
+
body.slug = slug;
|
|
29
|
+
const project = await (0, client_1.requestJson)(context, '/projects/ensure', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body,
|
|
32
|
+
});
|
|
33
|
+
(0, output_1.outputJson)(project, json, `✓ Project ready: ${project.id} (${name})`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case 'list': {
|
|
37
|
+
const all = Boolean(flags.all);
|
|
38
|
+
const includeDeletedFlag = flags.include_deleted ?? flags['include-deleted'];
|
|
39
|
+
// When --all is set, don't require org context
|
|
40
|
+
const orgId = all
|
|
41
|
+
? (typeof flags.org === 'string' ? flags.org : undefined)
|
|
42
|
+
: (typeof flags.org === 'string' ? flags.org : context.orgId);
|
|
43
|
+
const query = buildQuery({
|
|
44
|
+
limit: typeof flags.limit === 'string' ? flags.limit : (all ? '50' : undefined),
|
|
45
|
+
offset: typeof flags.offset === 'string' ? flags.offset : undefined,
|
|
46
|
+
include_deleted: (0, args_1.toBoolean)(includeDeletedFlag) ? 'true' : undefined,
|
|
47
|
+
org_id: orgId,
|
|
48
|
+
name: typeof flags.name === 'string' ? flags.name : undefined,
|
|
49
|
+
});
|
|
50
|
+
const response = await (0, client_1.requestJson)(context, `/projects${query}`);
|
|
51
|
+
if (json) {
|
|
52
|
+
(0, output_1.outputJson)(response, json);
|
|
53
|
+
}
|
|
54
|
+
else if (all) {
|
|
55
|
+
console.log('All projects (admin view):');
|
|
56
|
+
console.log('');
|
|
57
|
+
(0, output_1.outputJson)(response, json);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
(0, output_1.outputJson)(response, json);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
case 'get': {
|
|
65
|
+
const projectId = positionals[0];
|
|
66
|
+
if (!projectId)
|
|
67
|
+
throw new Error('Usage: eve project get <project_id>');
|
|
68
|
+
const includeDeletedFlag = flags.include_deleted ?? flags['include-deleted'];
|
|
69
|
+
const query = buildQuery({
|
|
70
|
+
include_deleted: (0, args_1.toBoolean)(includeDeletedFlag) ? 'true' : undefined,
|
|
71
|
+
});
|
|
72
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}${query}`);
|
|
73
|
+
(0, output_1.outputJson)(response, json);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
case 'update': {
|
|
77
|
+
const projectId = positionals[0];
|
|
78
|
+
if (!projectId) {
|
|
79
|
+
throw new Error('Usage: eve project update <project_id> [--name <name>] [--repo-url <url>]');
|
|
80
|
+
}
|
|
81
|
+
const body = {};
|
|
82
|
+
if (typeof flags.name === 'string')
|
|
83
|
+
body.name = flags.name;
|
|
84
|
+
if (typeof flags['repo-url'] === 'string')
|
|
85
|
+
body.repo_url = flags['repo-url'];
|
|
86
|
+
if (typeof flags.branch === 'string')
|
|
87
|
+
body.branch = flags.branch;
|
|
88
|
+
const deleted = (0, args_1.toBoolean)(flags.deleted);
|
|
89
|
+
if (deleted !== undefined)
|
|
90
|
+
body.deleted = deleted;
|
|
91
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}`, { method: 'PATCH', body });
|
|
92
|
+
(0, output_1.outputJson)(response, json);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
case 'sync': {
|
|
96
|
+
const dir = typeof flags.dir === 'string' ? flags.dir : process.cwd();
|
|
97
|
+
const manifestPath = (0, node_path_1.join)(dir, '.eve', 'manifest.yaml');
|
|
98
|
+
// Read manifest file
|
|
99
|
+
let yaml;
|
|
100
|
+
try {
|
|
101
|
+
yaml = (0, node_fs_1.readFileSync)(manifestPath, 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw new Error(`Failed to read manifest at ${manifestPath}: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
// Get git info
|
|
107
|
+
let gitSha;
|
|
108
|
+
let branch;
|
|
109
|
+
try {
|
|
110
|
+
gitSha = (0, node_child_process_1.execSync)('git rev-parse HEAD', { cwd: dir, encoding: 'utf-8' }).trim();
|
|
111
|
+
branch = (0, node_child_process_1.execSync)('git branch --show-current', { cwd: dir, encoding: 'utf-8' }).trim();
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// Git info is optional, continue without it
|
|
115
|
+
console.warn('Warning: Could not get git info:', error.message);
|
|
116
|
+
}
|
|
117
|
+
// Determine project ID
|
|
118
|
+
const projectIdRaw = typeof flags.project === 'string' ? flags.project : context.projectId;
|
|
119
|
+
if (!projectIdRaw) {
|
|
120
|
+
// Try to parse project ID from manifest
|
|
121
|
+
const projectMatch = yaml.match(/^project:\s*(\S+)/m);
|
|
122
|
+
if (projectMatch) {
|
|
123
|
+
const projectIdFromManifest = projectMatch[1];
|
|
124
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectIdFromManifest}/manifest`, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
body: { yaml, git_sha: gitSha, branch },
|
|
127
|
+
});
|
|
128
|
+
if (json) {
|
|
129
|
+
(0, output_1.outputJson)(response, json);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log(`✓ Manifest synced to ${projectIdFromManifest}`);
|
|
133
|
+
console.log(` Hash: ${response.manifest_hash.substring(0, 12)}...`);
|
|
134
|
+
if (gitSha)
|
|
135
|
+
console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
|
|
136
|
+
if (branch)
|
|
137
|
+
console.log(` Branch: ${branch}`);
|
|
138
|
+
if (response.parsed_defaults && Object.keys(response.parsed_defaults).length > 0) {
|
|
139
|
+
console.log('\nParsed defaults.env:');
|
|
140
|
+
Object.entries(response.parsed_defaults).forEach(([key, value]) => {
|
|
141
|
+
console.log(` ${key}: ${JSON.stringify(value)}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
throw new Error('Missing project id. Provide --project or set a profile default, or add "project: proj_xxx" to manifest.');
|
|
148
|
+
}
|
|
149
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectIdRaw}/manifest`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
body: { yaml, git_sha: gitSha, branch },
|
|
152
|
+
});
|
|
153
|
+
if (json) {
|
|
154
|
+
(0, output_1.outputJson)(response, json);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.log(`✓ Manifest synced to ${projectIdRaw}`);
|
|
158
|
+
console.log(` Hash: ${response.manifest_hash.substring(0, 12)}...`);
|
|
159
|
+
if (gitSha)
|
|
160
|
+
console.log(` Git SHA: ${gitSha.substring(0, 8)}`);
|
|
161
|
+
if (branch)
|
|
162
|
+
console.log(` Branch: ${branch}`);
|
|
163
|
+
if (response.parsed_defaults && Object.keys(response.parsed_defaults).length > 0) {
|
|
164
|
+
console.log('\nParsed defaults.env:');
|
|
165
|
+
Object.entries(response.parsed_defaults).forEach(([key, value]) => {
|
|
166
|
+
console.log(` ${key}: ${JSON.stringify(value)}`);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
throw new Error('Usage: eve project <ensure|list|get|update|sync>');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function buildQuery(params) {
|
|
177
|
+
const search = new URLSearchParams();
|
|
178
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
179
|
+
if (value === undefined || value === '')
|
|
180
|
+
return;
|
|
181
|
+
search.set(key, String(value));
|
|
182
|
+
});
|
|
183
|
+
const query = search.toString();
|
|
184
|
+
return query ? `?${query}` : '';
|
|
185
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleSecrets = handleSecrets;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handleSecrets(subcommand, positionals, flags, context) {
|
|
8
|
+
const json = Boolean(flags.json);
|
|
9
|
+
switch (subcommand) {
|
|
10
|
+
case 'set': {
|
|
11
|
+
const scope = resolveScope(flags, context);
|
|
12
|
+
const key = positionals[0] ?? (typeof flags.key === 'string' ? flags.key : undefined);
|
|
13
|
+
const value = positionals[1] ?? (typeof flags.value === 'string' ? flags.value : undefined);
|
|
14
|
+
const type = typeof flags.type === 'string' ? flags.type : undefined;
|
|
15
|
+
if (!key || !value) {
|
|
16
|
+
throw new Error('Usage: eve secrets set <key> <value> [--project <id>|--org <id>|--user <id>|--system] [--type <type>]');
|
|
17
|
+
}
|
|
18
|
+
const body = { key, value };
|
|
19
|
+
if (type)
|
|
20
|
+
body.type = type;
|
|
21
|
+
const response = await (0, client_1.requestJson)(context, `${scopeBasePath(scope)}`, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
body,
|
|
24
|
+
});
|
|
25
|
+
(0, output_1.outputJson)(response, json, `✓ Secret set: ${key}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
case 'list': {
|
|
29
|
+
const scope = resolveScope(flags, context);
|
|
30
|
+
const query = buildQuery({
|
|
31
|
+
limit: typeof flags.limit === 'string' ? flags.limit : undefined,
|
|
32
|
+
offset: typeof flags.offset === 'string' ? flags.offset : undefined,
|
|
33
|
+
});
|
|
34
|
+
const response = await (0, client_1.requestJson)(context, `${scopeBasePath(scope)}${query}`);
|
|
35
|
+
(0, output_1.outputJson)(response, json);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
case 'show': {
|
|
39
|
+
const scope = resolveScope(flags, context);
|
|
40
|
+
const key = positionals[0] ?? (typeof flags.key === 'string' ? flags.key : undefined);
|
|
41
|
+
if (!key) {
|
|
42
|
+
throw new Error('Usage: eve secrets show <key> [--project <id>|--org <id>|--user <id>|--system]');
|
|
43
|
+
}
|
|
44
|
+
const response = await (0, client_1.requestJson)(context, `${scopeBasePath(scope)}/${encodeURIComponent(key)}`);
|
|
45
|
+
(0, output_1.outputJson)(response, json);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
case 'delete': {
|
|
49
|
+
const scope = resolveScope(flags, context);
|
|
50
|
+
const key = positionals[0] ?? (typeof flags.key === 'string' ? flags.key : undefined);
|
|
51
|
+
if (!key) {
|
|
52
|
+
throw new Error('Usage: eve secrets delete <key> [--project <id>|--org <id>|--user <id>|--system]');
|
|
53
|
+
}
|
|
54
|
+
await (0, client_1.requestJson)(context, `${scopeBasePath(scope)}/${encodeURIComponent(key)}`, { method: 'DELETE' });
|
|
55
|
+
(0, output_1.outputJson)({ ok: true }, json, `✓ Secret deleted: ${key}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
case 'import': {
|
|
59
|
+
const scope = resolveScope(flags, context);
|
|
60
|
+
const filePath = typeof flags.file === 'string' ? flags.file : '.env';
|
|
61
|
+
const fileContents = (0, node_fs_1.readFileSync)(filePath, 'utf8');
|
|
62
|
+
const entries = parseEnvFile(fileContents);
|
|
63
|
+
const filtered = entries.filter(([key]) => key.startsWith('EVE_SECRET_'));
|
|
64
|
+
if (filtered.length === 0) {
|
|
65
|
+
throw new Error(`No EVE_SECRET_* entries found in ${filePath}`);
|
|
66
|
+
}
|
|
67
|
+
for (const [rawKey, rawValue] of filtered) {
|
|
68
|
+
const key = rawKey.slice('EVE_SECRET_'.length);
|
|
69
|
+
await (0, client_1.requestJson)(context, `${scopeBasePath(scope)}`, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
body: { key, value: rawValue },
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
(0, output_1.outputJson)({ imported: filtered.length }, json, `✓ Imported ${filtered.length} secrets`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
throw new Error('Usage: eve secrets <set|list|show|delete|import>');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function resolveScope(flags, context) {
|
|
82
|
+
// Explicit flags take priority in the order: system > project > org > user
|
|
83
|
+
const explicitSystem = Boolean(flags.system);
|
|
84
|
+
const explicitProject = typeof flags.project === 'string' ? flags.project : undefined;
|
|
85
|
+
const explicitOrg = typeof flags.org === 'string' ? flags.org : undefined;
|
|
86
|
+
const explicitUser = typeof flags.user === 'string' ? flags.user : undefined;
|
|
87
|
+
// Check for mutual exclusivity
|
|
88
|
+
const explicitFlags = [explicitSystem, explicitProject, explicitOrg, explicitUser].filter(Boolean);
|
|
89
|
+
if (explicitFlags.length > 1) {
|
|
90
|
+
throw new Error('Cannot specify multiple scope flags. Use only one of: --system, --project, --org, or --user');
|
|
91
|
+
}
|
|
92
|
+
// If explicit flags are provided, use them in priority order
|
|
93
|
+
if (explicitSystem)
|
|
94
|
+
return { scopeType: 'system', scopeId: 'system' };
|
|
95
|
+
if (explicitProject)
|
|
96
|
+
return { scopeType: 'project', scopeId: explicitProject };
|
|
97
|
+
if (explicitOrg)
|
|
98
|
+
return { scopeType: 'org', scopeId: explicitOrg };
|
|
99
|
+
if (explicitUser)
|
|
100
|
+
return { scopeType: 'user', scopeId: explicitUser };
|
|
101
|
+
// Fall back to profile defaults
|
|
102
|
+
if (context.projectId)
|
|
103
|
+
return { scopeType: 'project', scopeId: context.projectId };
|
|
104
|
+
if (context.orgId)
|
|
105
|
+
return { scopeType: 'org', scopeId: context.orgId };
|
|
106
|
+
throw new Error('Missing scope. Provide --system, --project, --org, or --user (or set profile defaults).');
|
|
107
|
+
}
|
|
108
|
+
function scopeBasePath(scope) {
|
|
109
|
+
switch (scope.scopeType) {
|
|
110
|
+
case 'system':
|
|
111
|
+
return `/system/secrets`;
|
|
112
|
+
case 'project':
|
|
113
|
+
return `/projects/${scope.scopeId}/secrets`;
|
|
114
|
+
case 'org':
|
|
115
|
+
return `/orgs/${scope.scopeId}/secrets`;
|
|
116
|
+
case 'user':
|
|
117
|
+
return `/users/${scope.scopeId}/secrets`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function buildQuery(params) {
|
|
121
|
+
const search = new URLSearchParams();
|
|
122
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
123
|
+
if (value === undefined || value === '')
|
|
124
|
+
return;
|
|
125
|
+
search.set(key, String(value));
|
|
126
|
+
});
|
|
127
|
+
const query = search.toString();
|
|
128
|
+
return query ? `?${query}` : '';
|
|
129
|
+
}
|
|
130
|
+
function parseEnvFile(contents) {
|
|
131
|
+
const lines = contents.split(/\r?\n/);
|
|
132
|
+
const entries = [];
|
|
133
|
+
for (const line of lines) {
|
|
134
|
+
const trimmed = line.trim();
|
|
135
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
136
|
+
continue;
|
|
137
|
+
const eqIndex = trimmed.indexOf('=');
|
|
138
|
+
if (eqIndex === -1)
|
|
139
|
+
continue;
|
|
140
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
141
|
+
const value = trimmed.slice(eqIndex + 1).trim();
|
|
142
|
+
if (!key)
|
|
143
|
+
continue;
|
|
144
|
+
entries.push([key, value]);
|
|
145
|
+
}
|
|
146
|
+
return entries;
|
|
147
|
+
}
|