@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,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleOrg = handleOrg;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
const DEFAULT_ORG_NAME = process.env.EVE_ORG_NAME || 'default-test-org';
|
|
8
|
+
const DEFAULT_ORG_ID = process.env.EVE_ORG_ID || 'org_defaulttestorg';
|
|
9
|
+
async function handleOrg(subcommand, positionals, flags, context) {
|
|
10
|
+
const json = Boolean(flags.json);
|
|
11
|
+
switch (subcommand) {
|
|
12
|
+
case 'ensure': {
|
|
13
|
+
let orgId = typeof flags.id === 'string' ? flags.id : '';
|
|
14
|
+
let orgName = typeof flags.name === 'string' ? flags.name : '';
|
|
15
|
+
const nameOrId = positionals[0];
|
|
16
|
+
if (!orgId && nameOrId) {
|
|
17
|
+
if (/^org_[a-zA-Z0-9]+$/.test(nameOrId)) {
|
|
18
|
+
orgId = nameOrId;
|
|
19
|
+
orgName = orgName || nameOrId;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
orgName = orgName || nameOrId;
|
|
23
|
+
orgId = normalizeOrgId(nameOrId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
orgId = orgId || context.orgId || DEFAULT_ORG_ID;
|
|
27
|
+
orgName = orgName || DEFAULT_ORG_NAME;
|
|
28
|
+
const body = { id: orgId, name: orgName };
|
|
29
|
+
const org = await (0, client_1.requestJson)(context, '/orgs/ensure', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body,
|
|
32
|
+
});
|
|
33
|
+
(0, output_1.outputJson)(org, json, `✓ Organization ready: ${org.id} (${org.name})`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case 'list': {
|
|
37
|
+
const includeDeletedFlag = flags.include_deleted ?? flags['include-deleted'];
|
|
38
|
+
const query = buildQuery({
|
|
39
|
+
limit: typeof flags.limit === 'string' ? flags.limit : undefined,
|
|
40
|
+
offset: typeof flags.offset === 'string' ? flags.offset : undefined,
|
|
41
|
+
include_deleted: (0, args_1.toBoolean)(includeDeletedFlag) ? 'true' : undefined,
|
|
42
|
+
name: typeof flags.name === 'string' ? flags.name : undefined,
|
|
43
|
+
});
|
|
44
|
+
const response = await (0, client_1.requestJson)(context, `/orgs${query}`);
|
|
45
|
+
(0, output_1.outputJson)(response, json);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
case 'get': {
|
|
49
|
+
const orgId = positionals[0];
|
|
50
|
+
if (!orgId)
|
|
51
|
+
throw new Error('Usage: eve org get <org_id>');
|
|
52
|
+
const includeDeletedFlag = flags.include_deleted ?? flags['include-deleted'];
|
|
53
|
+
const query = buildQuery({
|
|
54
|
+
include_deleted: (0, args_1.toBoolean)(includeDeletedFlag) ? 'true' : undefined,
|
|
55
|
+
});
|
|
56
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}${query}`);
|
|
57
|
+
(0, output_1.outputJson)(response, json);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
case 'update': {
|
|
61
|
+
const orgId = positionals[0];
|
|
62
|
+
if (!orgId)
|
|
63
|
+
throw new Error('Usage: eve org update <org_id> [--name <name>] [--deleted <bool>]');
|
|
64
|
+
const body = {};
|
|
65
|
+
if (typeof flags.name === 'string')
|
|
66
|
+
body.name = flags.name;
|
|
67
|
+
const deleted = (0, args_1.toBoolean)(flags.deleted);
|
|
68
|
+
if (deleted !== undefined)
|
|
69
|
+
body.deleted = deleted;
|
|
70
|
+
const response = await (0, client_1.requestJson)(context, `/orgs/${orgId}`, { method: 'PATCH', body });
|
|
71
|
+
(0, output_1.outputJson)(response, json);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
default:
|
|
75
|
+
throw new Error('Usage: eve org <ensure|list|get|update>');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function normalizeOrgId(raw) {
|
|
79
|
+
if (/^org_[a-zA-Z0-9]+$/.test(raw)) {
|
|
80
|
+
return raw;
|
|
81
|
+
}
|
|
82
|
+
const stripped = raw.replace(/[^a-zA-Z0-9]/g, '');
|
|
83
|
+
if (!stripped) {
|
|
84
|
+
throw new Error('Organization id must contain alphanumeric characters');
|
|
85
|
+
}
|
|
86
|
+
return `org_${stripped}`;
|
|
87
|
+
}
|
|
88
|
+
function buildQuery(params) {
|
|
89
|
+
const search = new URLSearchParams();
|
|
90
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
91
|
+
if (value === undefined || value === '')
|
|
92
|
+
return;
|
|
93
|
+
search.set(key, String(value));
|
|
94
|
+
});
|
|
95
|
+
const query = search.toString();
|
|
96
|
+
return query ? `?${query}` : '';
|
|
97
|
+
}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handlePipeline = handlePipeline;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handlePipeline(subcommand, positionals, flags, context) {
|
|
8
|
+
const json = Boolean(flags.json);
|
|
9
|
+
switch (subcommand) {
|
|
10
|
+
case 'list':
|
|
11
|
+
return handleList(positionals, flags, context, json);
|
|
12
|
+
case 'show':
|
|
13
|
+
return handleShow(positionals, flags, context, json);
|
|
14
|
+
case 'run':
|
|
15
|
+
return handleRun(positionals, flags, context, json);
|
|
16
|
+
case 'runs':
|
|
17
|
+
return handleRuns(positionals, flags, context, json);
|
|
18
|
+
case 'show-run':
|
|
19
|
+
return handleShowRun(positionals, flags, context, json);
|
|
20
|
+
case 'approve':
|
|
21
|
+
return handleApprove(positionals, flags, context, json);
|
|
22
|
+
case 'cancel':
|
|
23
|
+
return handleCancel(positionals, flags, context, json);
|
|
24
|
+
case 'logs':
|
|
25
|
+
return handleLogs(positionals, flags, context, json);
|
|
26
|
+
default:
|
|
27
|
+
throw new Error('Usage: eve pipeline <list|show|run|runs|show-run|approve|cancel|logs>\n' +
|
|
28
|
+
' list [project] - list pipelines for a project\n' +
|
|
29
|
+
' show <project> <name> - show pipeline definition\n' +
|
|
30
|
+
' run <name> --ref <sha> - create and run a new pipeline\n' +
|
|
31
|
+
' Options: --env, --inputs <json>\n' +
|
|
32
|
+
' runs [project] - list recent pipeline runs\n' +
|
|
33
|
+
' Options: --limit, --status, --name <pipeline>\n' +
|
|
34
|
+
' show-run <pipeline> <run-id> - show pipeline run status and steps\n' +
|
|
35
|
+
' approve <run-id> - approve a blocked pipeline run\n' +
|
|
36
|
+
' cancel <run-id> [--reason <text>] - cancel pipeline run\n' +
|
|
37
|
+
' logs <pipeline> <run-id> [--step <name>] - show logs for pipeline run');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function handleList(positionals, flags, context, json) {
|
|
41
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
42
|
+
if (!projectId) {
|
|
43
|
+
throw new Error('Usage: eve pipeline list [project] [--project=<id>]');
|
|
44
|
+
}
|
|
45
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/pipelines`);
|
|
46
|
+
if (json) {
|
|
47
|
+
(0, output_1.outputJson)(response, json);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (response.data.length === 0) {
|
|
51
|
+
console.log('No pipelines found.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
formatPipelines(response.data);
|
|
55
|
+
}
|
|
56
|
+
async function handleShow(positionals, flags, context, json) {
|
|
57
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
58
|
+
const name = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
59
|
+
if (!projectId || !name) {
|
|
60
|
+
throw new Error('Usage: eve pipeline show <project> <name> [--project=<id>] [--name=<name>]');
|
|
61
|
+
}
|
|
62
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/pipelines/${name}`);
|
|
63
|
+
if (json) {
|
|
64
|
+
(0, output_1.outputJson)(response, json);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
formatPipeline(response);
|
|
68
|
+
}
|
|
69
|
+
function formatPipelines(pipelines) {
|
|
70
|
+
console.log('Pipelines:');
|
|
71
|
+
for (const pipeline of pipelines) {
|
|
72
|
+
const stepCount = Array.isArray(pipeline.definition?.steps)
|
|
73
|
+
? pipeline.definition.steps.length
|
|
74
|
+
: null;
|
|
75
|
+
const suffix = stepCount === null ? '' : ` (${stepCount} steps)`;
|
|
76
|
+
console.log(`- ${pipeline.name}${suffix}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function formatPipeline(pipeline) {
|
|
80
|
+
console.log(`Pipeline: ${pipeline.name}`);
|
|
81
|
+
console.log('Definition:');
|
|
82
|
+
console.log(JSON.stringify(pipeline.definition, null, 2));
|
|
83
|
+
}
|
|
84
|
+
function formatPipelineRunList(runs) {
|
|
85
|
+
console.log('Recent pipeline runs:');
|
|
86
|
+
console.log('');
|
|
87
|
+
// Table header
|
|
88
|
+
console.log('Run ID'.padEnd(30) + 'Pipeline'.padEnd(20) + 'Status'.padEnd(20) + 'Created');
|
|
89
|
+
console.log('-'.repeat(100));
|
|
90
|
+
for (const run of runs) {
|
|
91
|
+
const runId = run.id.padEnd(30);
|
|
92
|
+
const pipeline = run.pipeline_name.padEnd(20);
|
|
93
|
+
const status = run.status.padEnd(20);
|
|
94
|
+
const created = new Date(run.created_at).toLocaleString();
|
|
95
|
+
console.log(`${runId}${pipeline}${status}${created}`);
|
|
96
|
+
}
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(`Total: ${runs.length} runs`);
|
|
99
|
+
}
|
|
100
|
+
function formatPipelineRunDetail(detail) {
|
|
101
|
+
const { run, steps } = detail;
|
|
102
|
+
console.log(`Run ID: ${run.id}`);
|
|
103
|
+
console.log(`Pipeline: ${run.pipeline_name}`);
|
|
104
|
+
console.log(`Status: ${run.status}`);
|
|
105
|
+
if (run.env_name) {
|
|
106
|
+
console.log(`Environment: ${run.env_name}`);
|
|
107
|
+
}
|
|
108
|
+
if (run.git_sha) {
|
|
109
|
+
console.log(`Git SHA: ${run.git_sha}`);
|
|
110
|
+
}
|
|
111
|
+
if (run.started_at) {
|
|
112
|
+
console.log(`Started: ${run.started_at}`);
|
|
113
|
+
}
|
|
114
|
+
if (run.completed_at) {
|
|
115
|
+
console.log(`Completed: ${run.completed_at}`);
|
|
116
|
+
}
|
|
117
|
+
if (run.error_message) {
|
|
118
|
+
console.log(`Error: ${run.error_message}`);
|
|
119
|
+
}
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log('Steps:');
|
|
122
|
+
console.log('');
|
|
123
|
+
if (steps.length === 0) {
|
|
124
|
+
console.log('No steps found.');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Table header
|
|
128
|
+
console.log('Step'.padEnd(25) + 'Type'.padEnd(12) + 'Status'.padEnd(15) + 'Duration');
|
|
129
|
+
console.log('-'.repeat(80));
|
|
130
|
+
for (const step of steps) {
|
|
131
|
+
const name = step.step_name.padEnd(25);
|
|
132
|
+
const type = step.step_type.padEnd(12);
|
|
133
|
+
const status = step.status.padEnd(15);
|
|
134
|
+
let duration = '-';
|
|
135
|
+
if (step.duration_ms !== null) {
|
|
136
|
+
duration = `${step.duration_ms}ms`;
|
|
137
|
+
}
|
|
138
|
+
else if (step.started_at && !step.completed_at) {
|
|
139
|
+
duration = 'running...';
|
|
140
|
+
}
|
|
141
|
+
console.log(`${name}${type}${status}${duration}`);
|
|
142
|
+
if (step.error_message) {
|
|
143
|
+
console.log(` Error: ${step.error_message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log(`Total: ${steps.length} steps`);
|
|
148
|
+
}
|
|
149
|
+
async function handleRun(positionals, flags, context, json) {
|
|
150
|
+
const pipelineName = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
151
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
152
|
+
const env = (0, args_1.getStringFlag)(flags, ['env']);
|
|
153
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
154
|
+
const wait = Boolean(flags.wait);
|
|
155
|
+
const timeout = (0, args_1.getStringFlag)(flags, ['timeout']);
|
|
156
|
+
const inputsRaw = (0, args_1.getStringFlag)(flags, ['inputs']);
|
|
157
|
+
if (!pipelineName || !projectId || !ref) {
|
|
158
|
+
throw new Error('Usage: eve pipeline run <name> --ref <sha> [--env <env>] [--project <id>] [--wait] [--inputs <json>]');
|
|
159
|
+
}
|
|
160
|
+
let inputs;
|
|
161
|
+
if (inputsRaw) {
|
|
162
|
+
try {
|
|
163
|
+
inputs = JSON.parse(inputsRaw);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
throw new Error(`Invalid JSON for --inputs: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Emit event before creating the pipeline run
|
|
170
|
+
await emitPipelineRunEvent(context, projectId, pipelineName, { env, ref, inputs });
|
|
171
|
+
const query = new URLSearchParams();
|
|
172
|
+
if (wait)
|
|
173
|
+
query.set('wait', 'true');
|
|
174
|
+
if (timeout)
|
|
175
|
+
query.set('timeout', timeout);
|
|
176
|
+
const queryString = query.toString();
|
|
177
|
+
const url = `/projects/${projectId}/pipelines/${pipelineName}/run${queryString ? `?${queryString}` : ''}`;
|
|
178
|
+
const response = await (0, client_1.requestJson)(context, url, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
body: { ref, env, inputs },
|
|
181
|
+
});
|
|
182
|
+
if (json) {
|
|
183
|
+
(0, output_1.outputJson)(response, json);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log('Pipeline run created.');
|
|
187
|
+
console.log('');
|
|
188
|
+
formatPipelineRunDetail(response);
|
|
189
|
+
}
|
|
190
|
+
async function handleRuns(positionals, flags, context, json) {
|
|
191
|
+
// Support both: eve pipeline runs [project] OR eve pipeline runs <pipeline_name> [project]
|
|
192
|
+
// If first arg looks like a project ID, treat as project filter
|
|
193
|
+
// Otherwise treat as pipeline name for backward compat
|
|
194
|
+
const firstArg = positionals[0];
|
|
195
|
+
const secondArg = positionals[1];
|
|
196
|
+
let projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
197
|
+
let pipelineName = (0, args_1.getStringFlag)(flags, ['name']);
|
|
198
|
+
// Determine if first arg is project or pipeline name
|
|
199
|
+
if (firstArg) {
|
|
200
|
+
if (firstArg.startsWith('proj_') || firstArg.startsWith('org_')) {
|
|
201
|
+
projectId = firstArg;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
pipelineName = firstArg;
|
|
205
|
+
if (secondArg) {
|
|
206
|
+
projectId = secondArg;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (!projectId) {
|
|
211
|
+
throw new Error('Usage: eve pipeline runs [project] [--project <id>] [--name <pipeline>] [--limit N] [--status <status>]');
|
|
212
|
+
}
|
|
213
|
+
const query = buildQuery({
|
|
214
|
+
limit: (0, args_1.getStringFlag)(flags, ['limit']),
|
|
215
|
+
offset: (0, args_1.getStringFlag)(flags, ['offset']),
|
|
216
|
+
status: (0, args_1.getStringFlag)(flags, ['status']),
|
|
217
|
+
});
|
|
218
|
+
// If pipeline name specified, use per-pipeline endpoint
|
|
219
|
+
// Otherwise list all runs for the project (when that endpoint exists)
|
|
220
|
+
const url = pipelineName
|
|
221
|
+
? `/projects/${projectId}/pipelines/${pipelineName}/runs${query}`
|
|
222
|
+
: `/projects/${projectId}/pipelines/${pipelineName || '__all'}/runs${query}`;
|
|
223
|
+
const response = await (0, client_1.requestJson)(context, url);
|
|
224
|
+
if (json) {
|
|
225
|
+
(0, output_1.outputJson)(response, json);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (response.data.length === 0) {
|
|
229
|
+
console.log('No pipeline runs found.');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
formatPipelineRunList(response.data);
|
|
233
|
+
}
|
|
234
|
+
async function handleShowRun(positionals, flags, context, json) {
|
|
235
|
+
// Signature: eve pipeline show-run <pipeline_name> <run-id>
|
|
236
|
+
const pipelineName = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
237
|
+
const runId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['run']);
|
|
238
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
239
|
+
if (!pipelineName || !runId || !projectId) {
|
|
240
|
+
throw new Error('Usage: eve pipeline show-run <pipeline_name> <run-id> [--project <id>]');
|
|
241
|
+
}
|
|
242
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/pipelines/${pipelineName}/runs/${runId}`);
|
|
243
|
+
if (json) {
|
|
244
|
+
(0, output_1.outputJson)(response, json);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
formatPipelineRunDetail(response);
|
|
248
|
+
}
|
|
249
|
+
async function handleApprove(positionals, flags, context, json) {
|
|
250
|
+
const runId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['run']);
|
|
251
|
+
if (!runId) {
|
|
252
|
+
throw new Error('Usage: eve pipeline approve <run-id>');
|
|
253
|
+
}
|
|
254
|
+
const response = await (0, client_1.requestJson)(context, `/pipeline-runs/${runId}/approve`, { method: 'POST' });
|
|
255
|
+
if (json) {
|
|
256
|
+
(0, output_1.outputJson)(response, json);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
console.log(`Approved pipeline run ${runId}.`);
|
|
260
|
+
console.log('');
|
|
261
|
+
formatPipelineRunDetail(response);
|
|
262
|
+
}
|
|
263
|
+
async function handleCancel(positionals, flags, context, json) {
|
|
264
|
+
const runId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['run']);
|
|
265
|
+
const reason = (0, args_1.getStringFlag)(flags, ['reason']);
|
|
266
|
+
if (!runId) {
|
|
267
|
+
throw new Error('Usage: eve pipeline cancel <run-id> [--reason <text>]');
|
|
268
|
+
}
|
|
269
|
+
const response = await (0, client_1.requestJson)(context, `/pipeline-runs/${runId}/cancel`, {
|
|
270
|
+
method: 'POST',
|
|
271
|
+
body: reason ? { reason } : {},
|
|
272
|
+
});
|
|
273
|
+
if (json) {
|
|
274
|
+
(0, output_1.outputJson)(response, json);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
console.log(`Cancelled pipeline run ${runId}.`);
|
|
278
|
+
console.log('');
|
|
279
|
+
formatPipelineRunDetail(response);
|
|
280
|
+
}
|
|
281
|
+
async function handleLogs(positionals, flags, context, json) {
|
|
282
|
+
const pipelineName = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
283
|
+
const runId = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['run']);
|
|
284
|
+
const stepName = (0, args_1.getStringFlag)(flags, ['step']);
|
|
285
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
286
|
+
if (!pipelineName || !runId || !projectId) {
|
|
287
|
+
throw new Error('Usage: eve pipeline logs <pipeline_name> <run-id> [--step <step_name>] [--project <id>]');
|
|
288
|
+
}
|
|
289
|
+
// First get the run details to know which steps exist
|
|
290
|
+
const runDetail = await (0, client_1.requestJson)(context, `/projects/${projectId}/pipelines/${pipelineName}/runs/${runId}`);
|
|
291
|
+
if (json) {
|
|
292
|
+
(0, output_1.outputJson)(runDetail, json);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log(`Pipeline run: ${runDetail.run.id}`);
|
|
296
|
+
console.log(`Pipeline: ${runDetail.run.pipeline_name}`);
|
|
297
|
+
console.log(`Status: ${runDetail.run.status}`);
|
|
298
|
+
console.log('');
|
|
299
|
+
// Filter steps if specific step requested
|
|
300
|
+
const stepsToShow = stepName
|
|
301
|
+
? runDetail.steps.filter(s => s.step_name === stepName)
|
|
302
|
+
: runDetail.steps;
|
|
303
|
+
if (stepsToShow.length === 0) {
|
|
304
|
+
if (stepName) {
|
|
305
|
+
console.log(`No step found with name: ${stepName}`);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
console.log('No steps found for this pipeline run.');
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// Show logs for each step
|
|
313
|
+
for (const step of stepsToShow) {
|
|
314
|
+
console.log(`Step: ${step.step_name} (${step.step_type})`);
|
|
315
|
+
console.log(`Status: ${step.status}`);
|
|
316
|
+
if (step.started_at) {
|
|
317
|
+
console.log(`Started: ${step.started_at}`);
|
|
318
|
+
}
|
|
319
|
+
if (step.completed_at) {
|
|
320
|
+
console.log(`Completed: ${step.completed_at}`);
|
|
321
|
+
}
|
|
322
|
+
if (step.duration_ms !== null) {
|
|
323
|
+
console.log(`Duration: ${step.duration_ms}ms`);
|
|
324
|
+
}
|
|
325
|
+
if (step.error_message) {
|
|
326
|
+
console.log(`Error: ${step.error_message}`);
|
|
327
|
+
}
|
|
328
|
+
if (step.result_text) {
|
|
329
|
+
console.log('');
|
|
330
|
+
console.log('Result:');
|
|
331
|
+
console.log(step.result_text);
|
|
332
|
+
}
|
|
333
|
+
if (step.result_json) {
|
|
334
|
+
console.log('');
|
|
335
|
+
console.log('Result JSON:');
|
|
336
|
+
console.log(JSON.stringify(step.result_json, null, 2));
|
|
337
|
+
}
|
|
338
|
+
console.log('');
|
|
339
|
+
console.log('---');
|
|
340
|
+
console.log('');
|
|
341
|
+
}
|
|
342
|
+
if (!stepName) {
|
|
343
|
+
console.log(`Total steps: ${stepsToShow.length}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function buildQuery(params) {
|
|
347
|
+
const search = new URLSearchParams();
|
|
348
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
349
|
+
if (value === undefined || value === '')
|
|
350
|
+
return;
|
|
351
|
+
search.set(key, String(value));
|
|
352
|
+
});
|
|
353
|
+
const query = search.toString();
|
|
354
|
+
return query ? `?${query}` : '';
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Emit an event before creating a pipeline run.
|
|
358
|
+
* Fire-and-forget operation - logs warning on failure but doesn't block the command.
|
|
359
|
+
*/
|
|
360
|
+
async function emitPipelineRunEvent(context, projectId, pipelineName, options) {
|
|
361
|
+
try {
|
|
362
|
+
const eventBody = {
|
|
363
|
+
type: 'pipeline.run',
|
|
364
|
+
source: 'manual',
|
|
365
|
+
actor_type: 'user',
|
|
366
|
+
actor_id: 'cli-user',
|
|
367
|
+
};
|
|
368
|
+
// Add optional fields
|
|
369
|
+
if (options.env) {
|
|
370
|
+
eventBody.env_name = options.env;
|
|
371
|
+
}
|
|
372
|
+
if (options.ref) {
|
|
373
|
+
// Heuristic: if ref looks like a SHA (40 hex chars), treat as SHA; otherwise as branch
|
|
374
|
+
const isSha = /^[0-9a-f]{40}$/i.test(options.ref);
|
|
375
|
+
if (isSha) {
|
|
376
|
+
eventBody.ref_sha = options.ref;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Could be a branch name, short SHA, or tag - store in both for maximum compatibility
|
|
380
|
+
eventBody.ref_sha = options.ref;
|
|
381
|
+
eventBody.ref_branch = options.ref;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Build payload with pipeline name and inputs
|
|
385
|
+
const payload = {
|
|
386
|
+
pipeline_name: pipelineName,
|
|
387
|
+
};
|
|
388
|
+
if (options.inputs) {
|
|
389
|
+
payload.inputs = options.inputs;
|
|
390
|
+
}
|
|
391
|
+
eventBody.payload_json = payload;
|
|
392
|
+
// Fire-and-forget: emit event but don't block on failure
|
|
393
|
+
await (0, client_1.requestJson)(context, `/projects/${projectId}/events`, {
|
|
394
|
+
method: 'POST',
|
|
395
|
+
body: eventBody,
|
|
396
|
+
allowError: true,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
// Log warning but don't fail the command
|
|
401
|
+
console.warn('Warning: Failed to emit pipeline.run event:', error instanceof Error ? error.message : 'unknown error');
|
|
402
|
+
}
|
|
403
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleProfile = handleProfile;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const output_1 = require("../lib/output");
|
|
6
|
+
function handleProfile(subcommand, positionals, flags, config) {
|
|
7
|
+
const json = Boolean(flags.json);
|
|
8
|
+
switch (subcommand) {
|
|
9
|
+
case 'list': {
|
|
10
|
+
const profiles = Object.entries(config.profiles).map(([name, profile]) => ({
|
|
11
|
+
name,
|
|
12
|
+
active: name === config.active_profile,
|
|
13
|
+
...profile,
|
|
14
|
+
}));
|
|
15
|
+
(0, output_1.outputJson)({ active_profile: config.active_profile, profiles }, json);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
case 'show': {
|
|
19
|
+
const name = positionals[0] ?? config.active_profile;
|
|
20
|
+
const profile = config.profiles[name];
|
|
21
|
+
if (!profile) {
|
|
22
|
+
throw new Error(`Profile ${name} not found`);
|
|
23
|
+
}
|
|
24
|
+
(0, output_1.outputJson)({ name, ...profile }, json);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
case 'use': {
|
|
28
|
+
const name = positionals[0];
|
|
29
|
+
if (!name) {
|
|
30
|
+
throw new Error('Usage: eve profile use <name>');
|
|
31
|
+
}
|
|
32
|
+
if (!config.profiles[name]) {
|
|
33
|
+
config.profiles[name] = {};
|
|
34
|
+
}
|
|
35
|
+
config.active_profile = name;
|
|
36
|
+
(0, config_1.saveConfig)(config);
|
|
37
|
+
(0, output_1.outputJson)({ active_profile: name }, json, `✓ Active profile: ${name}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case 'create': {
|
|
41
|
+
const name = positionals[0];
|
|
42
|
+
if (!name) {
|
|
43
|
+
throw new Error('Usage: eve profile create <name> [--api-url <url> ...]');
|
|
44
|
+
}
|
|
45
|
+
if (config.profiles[name]) {
|
|
46
|
+
throw new Error(`Profile ${name} already exists`);
|
|
47
|
+
}
|
|
48
|
+
config.profiles[name] = applyProfileFlags({}, flags);
|
|
49
|
+
(0, config_1.saveConfig)(config);
|
|
50
|
+
(0, output_1.outputJson)({ name, ...config.profiles[name] }, json, `✓ Profile created: ${name}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
case 'set': {
|
|
54
|
+
const name = positionals[0] ?? config.active_profile;
|
|
55
|
+
if (!config.profiles[name]) {
|
|
56
|
+
config.profiles[name] = {};
|
|
57
|
+
}
|
|
58
|
+
config.profiles[name] = applyProfileFlags(config.profiles[name], flags);
|
|
59
|
+
(0, config_1.saveConfig)(config);
|
|
60
|
+
(0, output_1.outputJson)({ name, ...config.profiles[name] }, json, `✓ Profile updated: ${name}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
case 'remove': {
|
|
64
|
+
const name = positionals[0];
|
|
65
|
+
if (!name) {
|
|
66
|
+
throw new Error('Usage: eve profile remove <name>');
|
|
67
|
+
}
|
|
68
|
+
if (!config.profiles[name]) {
|
|
69
|
+
throw new Error(`Profile ${name} not found`);
|
|
70
|
+
}
|
|
71
|
+
delete config.profiles[name];
|
|
72
|
+
if (config.active_profile === name) {
|
|
73
|
+
const [first] = Object.keys(config.profiles);
|
|
74
|
+
config.active_profile = first ?? 'default';
|
|
75
|
+
if (!config.profiles[config.active_profile]) {
|
|
76
|
+
config.profiles[config.active_profile] = {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
(0, config_1.saveConfig)(config);
|
|
80
|
+
(0, output_1.outputJson)({ removed: name, active_profile: config.active_profile }, json, `✓ Removed profile ${name}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
default:
|
|
84
|
+
throw new Error('Usage: eve profile <list|show|use|create|set|remove>');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function applyProfileFlags(profile, flags) {
|
|
88
|
+
const updated = { ...profile };
|
|
89
|
+
if (typeof flags['api-url'] === 'string')
|
|
90
|
+
updated.api_url = flags['api-url'];
|
|
91
|
+
if (typeof flags.org === 'string')
|
|
92
|
+
updated.org_id = flags.org;
|
|
93
|
+
if (typeof flags.project === 'string')
|
|
94
|
+
updated.project_id = flags.project;
|
|
95
|
+
if (typeof flags['supabase-url'] === 'string')
|
|
96
|
+
updated.supabase_url = flags['supabase-url'];
|
|
97
|
+
if (typeof flags['supabase-anon-key'] === 'string')
|
|
98
|
+
updated.supabase_anon_key = flags['supabase-anon-key'];
|
|
99
|
+
// --harness accepts "harness" or "harness:variant" format
|
|
100
|
+
if (typeof flags.harness === 'string')
|
|
101
|
+
updated.default_harness = flags.harness;
|
|
102
|
+
return updated;
|
|
103
|
+
}
|