@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,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleWorkflow = handleWorkflow;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
async function handleWorkflow(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 'invoke':
|
|
17
|
+
return handleInvoke(positionals, flags, context, json);
|
|
18
|
+
case 'logs':
|
|
19
|
+
return handleLogs(positionals, flags, context, json);
|
|
20
|
+
default:
|
|
21
|
+
throw new Error('Usage: eve workflow <list|show|run|invoke|logs>\n' +
|
|
22
|
+
' list [project] - list workflows for a project\n' +
|
|
23
|
+
' show <project> <name> - show workflow definition\n' +
|
|
24
|
+
' run [project] <workflow-name> - fire-and-forget workflow invocation\n' +
|
|
25
|
+
' Options: --input <json>\n' +
|
|
26
|
+
' invoke [project] <workflow-name> - invoke workflow and wait for result\n' +
|
|
27
|
+
' Options: --input <json>, --no-wait\n' +
|
|
28
|
+
' logs <job-id> - show logs for a workflow job');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function handleList(positionals, flags, context, json) {
|
|
32
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
33
|
+
if (!projectId) {
|
|
34
|
+
throw new Error('Usage: eve workflow list [project] [--project=<id>]');
|
|
35
|
+
}
|
|
36
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/workflows`);
|
|
37
|
+
if (json) {
|
|
38
|
+
(0, output_1.outputJson)(response, json);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (response.data.length === 0) {
|
|
42
|
+
console.log('No workflows found.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
formatWorkflows(response.data);
|
|
46
|
+
}
|
|
47
|
+
async function handleShow(positionals, flags, context, json) {
|
|
48
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
49
|
+
const name = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['name']);
|
|
50
|
+
if (!projectId || !name) {
|
|
51
|
+
throw new Error('Usage: eve workflow show <project> <name> [--project=<id>] [--name=<name>]');
|
|
52
|
+
}
|
|
53
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/workflows/${name}`);
|
|
54
|
+
if (json) {
|
|
55
|
+
(0, output_1.outputJson)(response, json);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
formatWorkflow(response);
|
|
59
|
+
}
|
|
60
|
+
function formatWorkflows(workflows) {
|
|
61
|
+
console.log('Workflows:');
|
|
62
|
+
for (const workflow of workflows) {
|
|
63
|
+
const stepCount = Array.isArray(workflow.definition?.steps)
|
|
64
|
+
? workflow.definition.steps.length
|
|
65
|
+
: null;
|
|
66
|
+
const suffix = stepCount === null ? '' : ` (${stepCount} steps)`;
|
|
67
|
+
console.log(`- ${workflow.name}${suffix}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function formatWorkflow(workflow) {
|
|
71
|
+
console.log(`Workflow: ${workflow.name}`);
|
|
72
|
+
console.log('Definition:');
|
|
73
|
+
console.log(JSON.stringify(workflow.definition, null, 2));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* eve workflow run [project] <workflow-name> [--input=<json>]
|
|
77
|
+
* Fire-and-forget workflow invocation (no waiting)
|
|
78
|
+
*/
|
|
79
|
+
async function handleRun(positionals, flags, context, json) {
|
|
80
|
+
let projectId;
|
|
81
|
+
let workflowName;
|
|
82
|
+
// Parse positionals: either "workflow-name" or "project workflow-name"
|
|
83
|
+
if (positionals.length === 1) {
|
|
84
|
+
projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
85
|
+
workflowName = positionals[0];
|
|
86
|
+
}
|
|
87
|
+
else if (positionals.length >= 2) {
|
|
88
|
+
projectId = positionals[0];
|
|
89
|
+
workflowName = positionals[1];
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
93
|
+
workflowName = (0, args_1.getStringFlag)(flags, ['workflow', 'name']);
|
|
94
|
+
}
|
|
95
|
+
if (!projectId || !workflowName) {
|
|
96
|
+
throw new Error('Usage: eve workflow run [project] <workflow-name> [--input=<json>]');
|
|
97
|
+
}
|
|
98
|
+
// Parse input JSON
|
|
99
|
+
const inputRaw = (0, args_1.getStringFlag)(flags, ['input', 'i']);
|
|
100
|
+
let input;
|
|
101
|
+
if (inputRaw) {
|
|
102
|
+
try {
|
|
103
|
+
input = JSON.parse(inputRaw);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
throw new Error(`Invalid JSON for --input: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Call invoke endpoint with wait=false
|
|
110
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/workflows/${workflowName}/invoke?wait=false`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
body: input ?? {},
|
|
113
|
+
});
|
|
114
|
+
if (json) {
|
|
115
|
+
(0, output_1.outputJson)(response, json);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(`Workflow invoked: ${workflowName}`);
|
|
119
|
+
console.log(` Job ID: ${response.job_id}`);
|
|
120
|
+
console.log(` Status: ${response.status}`);
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(`Use 'eve workflow logs ${response.job_id}' to view logs`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* eve workflow invoke [project] <workflow-name> [--input=<json>] [--no-wait]
|
|
126
|
+
* Invoke workflow and wait for result (default behavior)
|
|
127
|
+
*/
|
|
128
|
+
async function handleInvoke(positionals, flags, context, json) {
|
|
129
|
+
let projectId;
|
|
130
|
+
let workflowName;
|
|
131
|
+
// Parse positionals: either "workflow-name" or "project workflow-name"
|
|
132
|
+
if (positionals.length === 1) {
|
|
133
|
+
projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
134
|
+
workflowName = positionals[0];
|
|
135
|
+
}
|
|
136
|
+
else if (positionals.length >= 2) {
|
|
137
|
+
projectId = positionals[0];
|
|
138
|
+
workflowName = positionals[1];
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
142
|
+
workflowName = (0, args_1.getStringFlag)(flags, ['workflow', 'name']);
|
|
143
|
+
}
|
|
144
|
+
if (!projectId || !workflowName) {
|
|
145
|
+
throw new Error('Usage: eve workflow invoke [project] <workflow-name> [--input=<json>] [--no-wait]');
|
|
146
|
+
}
|
|
147
|
+
// Parse input JSON
|
|
148
|
+
const inputRaw = (0, args_1.getStringFlag)(flags, ['input', 'i']);
|
|
149
|
+
let input;
|
|
150
|
+
if (inputRaw) {
|
|
151
|
+
try {
|
|
152
|
+
input = JSON.parse(inputRaw);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw new Error(`Invalid JSON for --input: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Check if --no-wait is set (inverse logic: wait by default)
|
|
159
|
+
const wait = !flags['no-wait'];
|
|
160
|
+
// Call invoke endpoint
|
|
161
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/workflows/${workflowName}/invoke?wait=${wait}`, {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
body: input ?? {},
|
|
164
|
+
});
|
|
165
|
+
if (json) {
|
|
166
|
+
(0, output_1.outputJson)(response, json);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
console.log(`Workflow invoked: ${workflowName}`);
|
|
170
|
+
console.log(` Job ID: ${response.job_id}`);
|
|
171
|
+
console.log(` Status: ${response.status}`);
|
|
172
|
+
if (response.result !== undefined) {
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log('Result:');
|
|
175
|
+
if (typeof response.result === 'string') {
|
|
176
|
+
console.log(response.result);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
console.log(JSON.stringify(response.result, null, 2));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else if (!wait) {
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log(`Use 'eve workflow logs ${response.job_id}' to view logs`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* eve workflow logs <job-id> [--attempt=N] [--after=N]
|
|
189
|
+
* Show logs for a workflow job
|
|
190
|
+
*/
|
|
191
|
+
async function handleLogs(positionals, flags, context, json) {
|
|
192
|
+
const jobId = positionals[0];
|
|
193
|
+
if (!jobId) {
|
|
194
|
+
throw new Error('Usage: eve workflow logs <job-id> [--attempt=N] [--after=N]');
|
|
195
|
+
}
|
|
196
|
+
// Get attempt number (default to latest)
|
|
197
|
+
const attemptStr = (0, args_1.getStringFlag)(flags, ['attempt']);
|
|
198
|
+
let attemptNum;
|
|
199
|
+
if (attemptStr) {
|
|
200
|
+
attemptNum = parseInt(attemptStr, 10);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// Find the latest attempt
|
|
204
|
+
const attemptsResponse = await (0, client_1.requestJson)(context, `/jobs/${jobId}/attempts`);
|
|
205
|
+
if (attemptsResponse.attempts.length === 0) {
|
|
206
|
+
console.log('No attempts found for this job.');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
attemptNum = Math.max(...attemptsResponse.attempts.map(a => a.attempt_number));
|
|
210
|
+
}
|
|
211
|
+
const afterStr = (0, args_1.getStringFlag)(flags, ['after']);
|
|
212
|
+
const afterQuery = afterStr ? `?after=${afterStr}` : '';
|
|
213
|
+
const response = await (0, client_1.requestJson)(context, `/jobs/${jobId}/attempts/${attemptNum}/logs${afterQuery}`);
|
|
214
|
+
if (json) {
|
|
215
|
+
(0, output_1.outputJson)(response, json);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (response.logs.length === 0) {
|
|
219
|
+
console.log(`No logs found for attempt #${attemptNum}.`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
console.log(`Logs for job ${jobId}, attempt #${attemptNum}:`);
|
|
223
|
+
console.log('');
|
|
224
|
+
for (const log of response.logs) {
|
|
225
|
+
formatLogEntry(log);
|
|
226
|
+
}
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log(`Total: ${response.logs.length} log entries`);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Format a single log entry for display
|
|
232
|
+
*/
|
|
233
|
+
function formatLogEntry(log) {
|
|
234
|
+
const line = log.line;
|
|
235
|
+
const timestamp = new Date(log.timestamp).toLocaleTimeString();
|
|
236
|
+
// Common log line formats from harnesses
|
|
237
|
+
const type = log.type || line.type || 'log';
|
|
238
|
+
// If type starts with 'lifecycle_', format as lifecycle event
|
|
239
|
+
if (type.startsWith('lifecycle_')) {
|
|
240
|
+
const content = line;
|
|
241
|
+
const phase = content.phase || 'unknown';
|
|
242
|
+
const action = content.action || 'unknown';
|
|
243
|
+
const duration = content.duration_ms;
|
|
244
|
+
const success = content.success;
|
|
245
|
+
const error = content.error;
|
|
246
|
+
const meta = content.meta || {};
|
|
247
|
+
if (action === 'start') {
|
|
248
|
+
const detail = formatLifecycleMeta(phase, meta);
|
|
249
|
+
console.log(`[${timestamp}] ${getLifecycleIcon(phase)} Starting ${phase}${detail}...`);
|
|
250
|
+
}
|
|
251
|
+
else if (action === 'end') {
|
|
252
|
+
const durationStr = duration ? ` (${duration}ms)` : '';
|
|
253
|
+
if (success === false && error) {
|
|
254
|
+
console.log(`[${timestamp}] ${getLifecycleIcon(phase)} ${capitalize(phase)} failed${durationStr}: ${error}`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
console.log(`[${timestamp}] ${getLifecycleIcon(phase)} ${capitalize(phase)} completed${durationStr}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (action === 'log') {
|
|
261
|
+
const msg = meta.message || JSON.stringify(meta);
|
|
262
|
+
console.log(`[${timestamp}] > ${msg}`);
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const message = line.message || line.text || '';
|
|
267
|
+
const tool = line.tool;
|
|
268
|
+
const toolInput = line.tool_input;
|
|
269
|
+
const toolResult = line.tool_result;
|
|
270
|
+
// Format based on log type
|
|
271
|
+
switch (type) {
|
|
272
|
+
case 'assistant':
|
|
273
|
+
case 'text':
|
|
274
|
+
console.log(`[${timestamp}] ${message || JSON.stringify(line)}`);
|
|
275
|
+
break;
|
|
276
|
+
case 'tool_use':
|
|
277
|
+
console.log(`[${timestamp}] Tool: ${tool || 'tool'}: ${toolInput || JSON.stringify(line)}`);
|
|
278
|
+
break;
|
|
279
|
+
case 'tool_result':
|
|
280
|
+
const resultPreview = (toolResult || '').substring(0, 100);
|
|
281
|
+
console.log(`[${timestamp}] → ${resultPreview}${(toolResult?.length || 0) > 100 ? '...' : ''}`);
|
|
282
|
+
break;
|
|
283
|
+
case 'error':
|
|
284
|
+
console.log(`[${timestamp}] Error: ${message || JSON.stringify(line)}`);
|
|
285
|
+
break;
|
|
286
|
+
case 'status':
|
|
287
|
+
console.log(`[${timestamp}] ${message || JSON.stringify(line)}`);
|
|
288
|
+
break;
|
|
289
|
+
default:
|
|
290
|
+
// Generic JSON output for unknown types
|
|
291
|
+
console.log(`[${timestamp}] ${JSON.stringify(line)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get icon for lifecycle phase
|
|
296
|
+
*/
|
|
297
|
+
function getLifecycleIcon(phase) {
|
|
298
|
+
switch (phase) {
|
|
299
|
+
case 'workspace': return '📁';
|
|
300
|
+
case 'hook': return '🪝';
|
|
301
|
+
case 'secrets': return '🔐';
|
|
302
|
+
case 'services': return '🐳';
|
|
303
|
+
case 'harness': return '🤖';
|
|
304
|
+
case 'runner': return '☸️';
|
|
305
|
+
default: return '⚙️';
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Capitalize first letter of string
|
|
310
|
+
*/
|
|
311
|
+
function capitalize(str) {
|
|
312
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Format lifecycle metadata for display
|
|
316
|
+
*/
|
|
317
|
+
function formatLifecycleMeta(phase, meta) {
|
|
318
|
+
switch (phase) {
|
|
319
|
+
case 'workspace':
|
|
320
|
+
const repoUrl = meta.repo_url || '';
|
|
321
|
+
const branch = meta.branch || '';
|
|
322
|
+
return branch ? ` (${repoUrl}@${branch})` : repoUrl ? ` (${repoUrl})` : '';
|
|
323
|
+
case 'hook':
|
|
324
|
+
return meta.hook_name ? ` "${meta.hook_name}"` : '';
|
|
325
|
+
case 'secrets':
|
|
326
|
+
return '';
|
|
327
|
+
case 'services':
|
|
328
|
+
const svcName = meta.service_name || '';
|
|
329
|
+
return svcName ? ` "${svcName}"` : '';
|
|
330
|
+
case 'harness':
|
|
331
|
+
return meta.harness ? ` ${meta.harness}` : '';
|
|
332
|
+
case 'runner':
|
|
333
|
+
return meta.pod_name ? ` (${meta.pod_name})` : '';
|
|
334
|
+
default:
|
|
335
|
+
return '';
|
|
336
|
+
}
|
|
337
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const args_1 = require("./lib/args");
|
|
5
|
+
const config_1 = require("./lib/config");
|
|
6
|
+
const context_1 = require("./lib/context");
|
|
7
|
+
const help_1 = require("./lib/help");
|
|
8
|
+
const org_1 = require("./commands/org");
|
|
9
|
+
const project_1 = require("./commands/project");
|
|
10
|
+
const job_1 = require("./commands/job");
|
|
11
|
+
const profile_1 = require("./commands/profile");
|
|
12
|
+
const auth_1 = require("./commands/auth");
|
|
13
|
+
const harness_1 = require("./commands/harness");
|
|
14
|
+
const secrets_1 = require("./commands/secrets");
|
|
15
|
+
const system_1 = require("./commands/system");
|
|
16
|
+
const env_1 = require("./commands/env");
|
|
17
|
+
const pipeline_1 = require("./commands/pipeline");
|
|
18
|
+
const workflow_1 = require("./commands/workflow");
|
|
19
|
+
const api_1 = require("./commands/api");
|
|
20
|
+
const db_1 = require("./commands/db");
|
|
21
|
+
const event_1 = require("./commands/event");
|
|
22
|
+
async function main() {
|
|
23
|
+
const { flags, positionals } = (0, args_1.parseArgs)(process.argv.slice(2));
|
|
24
|
+
const command = positionals[0];
|
|
25
|
+
const subcommand = positionals[1];
|
|
26
|
+
const rest = positionals.slice(2);
|
|
27
|
+
// Top-level help: no command or explicit --help without command
|
|
28
|
+
if (!command || command === '-h' || command === '--help') {
|
|
29
|
+
(0, help_1.showMainHelp)();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Command-level help: "eve profile --help" or "eve profile" with no subcommand
|
|
33
|
+
// flags.help means --help appeared somewhere; without subcommand = command help
|
|
34
|
+
if (flags.help && !subcommand) {
|
|
35
|
+
(0, help_1.showCommandHelp)(command);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Subcommand-level help: "eve profile set --help"
|
|
39
|
+
if (flags.help && subcommand) {
|
|
40
|
+
(0, help_1.showSubcommandHelp)(command, subcommand);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const config = (0, config_1.loadConfig)();
|
|
44
|
+
const credentials = (0, config_1.loadCredentials)();
|
|
45
|
+
const context = (0, context_1.resolveContext)(flags, config, credentials);
|
|
46
|
+
switch (command) {
|
|
47
|
+
case 'org':
|
|
48
|
+
await (0, org_1.handleOrg)(subcommand, rest, flags, context);
|
|
49
|
+
return;
|
|
50
|
+
case 'project':
|
|
51
|
+
await (0, project_1.handleProject)(subcommand, rest, flags, context);
|
|
52
|
+
return;
|
|
53
|
+
case 'job':
|
|
54
|
+
await (0, job_1.handleJob)(subcommand, rest, flags, context);
|
|
55
|
+
return;
|
|
56
|
+
case 'profile':
|
|
57
|
+
(0, profile_1.handleProfile)(subcommand, rest, flags, config);
|
|
58
|
+
return;
|
|
59
|
+
case 'auth':
|
|
60
|
+
await (0, auth_1.handleAuth)(subcommand, flags, context, credentials);
|
|
61
|
+
return;
|
|
62
|
+
case 'harness':
|
|
63
|
+
await (0, harness_1.handleHarness)(subcommand, rest, flags, context);
|
|
64
|
+
return;
|
|
65
|
+
case 'secrets':
|
|
66
|
+
await (0, secrets_1.handleSecrets)(subcommand, rest, flags, context);
|
|
67
|
+
return;
|
|
68
|
+
case 'system':
|
|
69
|
+
await (0, system_1.handleSystem)(subcommand, rest, flags, context);
|
|
70
|
+
return;
|
|
71
|
+
case 'env':
|
|
72
|
+
await (0, env_1.handleEnv)(subcommand, rest, flags, context);
|
|
73
|
+
return;
|
|
74
|
+
case 'pipeline':
|
|
75
|
+
await (0, pipeline_1.handlePipeline)(subcommand, rest, flags, context);
|
|
76
|
+
return;
|
|
77
|
+
case 'workflow':
|
|
78
|
+
await (0, workflow_1.handleWorkflow)(subcommand, rest, flags, context);
|
|
79
|
+
return;
|
|
80
|
+
case 'api':
|
|
81
|
+
await (0, api_1.handleApi)(subcommand, rest, flags, context);
|
|
82
|
+
return;
|
|
83
|
+
case 'db':
|
|
84
|
+
await (0, db_1.handleDb)(subcommand, rest, flags, context);
|
|
85
|
+
return;
|
|
86
|
+
case 'event':
|
|
87
|
+
await (0, event_1.handleEvent)(subcommand, rest, flags, context);
|
|
88
|
+
return;
|
|
89
|
+
default:
|
|
90
|
+
(0, help_1.showMainHelp)();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
main().catch((error) => {
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.error(error instanceof Error ? error.message : error);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
package/dist/lib/args.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseArgs = parseArgs;
|
|
4
|
+
exports.toBoolean = toBoolean;
|
|
5
|
+
exports.getStringFlag = getStringFlag;
|
|
6
|
+
exports.getBooleanFlag = getBooleanFlag;
|
|
7
|
+
function parseArgs(args) {
|
|
8
|
+
const flags = {};
|
|
9
|
+
const positionals = [];
|
|
10
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
11
|
+
const arg = args[i];
|
|
12
|
+
if (arg.startsWith('--')) {
|
|
13
|
+
const trimmed = arg.slice(2);
|
|
14
|
+
const eqIndex = trimmed.indexOf('=');
|
|
15
|
+
if (eqIndex >= 0) {
|
|
16
|
+
const key = trimmed.slice(0, eqIndex);
|
|
17
|
+
const value = trimmed.slice(eqIndex + 1);
|
|
18
|
+
flags[key] = value;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const next = args[i + 1];
|
|
22
|
+
if (next && !next.startsWith('--')) {
|
|
23
|
+
flags[trimmed] = next;
|
|
24
|
+
i += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
flags[trimmed] = true;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
positionals.push(arg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { flags, positionals };
|
|
34
|
+
}
|
|
35
|
+
function toBoolean(value) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (typeof value === 'boolean')
|
|
39
|
+
return value;
|
|
40
|
+
return ['true', '1', 'yes', 'y', 'on'].includes(value.toLowerCase());
|
|
41
|
+
}
|
|
42
|
+
function getStringFlag(flags, keys) {
|
|
43
|
+
for (const key of keys) {
|
|
44
|
+
const value = flags[key];
|
|
45
|
+
if (typeof value === 'string')
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function getBooleanFlag(flags, keys) {
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
if (flags[key] !== undefined) {
|
|
53
|
+
return toBoolean(flags[key]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestJson = requestJson;
|
|
4
|
+
exports.requestRaw = requestRaw;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
async function requestJson(context, path, options = {}) {
|
|
7
|
+
const response = await requestRaw(context, path, options);
|
|
8
|
+
if (response.status === 401 && options.tokenOverride === undefined) {
|
|
9
|
+
const refreshed = await attemptRefresh(context);
|
|
10
|
+
if (refreshed?.access_token) {
|
|
11
|
+
context.token = refreshed.access_token;
|
|
12
|
+
context.refreshToken = refreshed.refresh_token;
|
|
13
|
+
context.expiresAt = refreshed.expires_at;
|
|
14
|
+
const retry = await requestRaw(context, path, {
|
|
15
|
+
...options,
|
|
16
|
+
tokenOverride: refreshed.access_token,
|
|
17
|
+
});
|
|
18
|
+
if (!retry.ok && !options.allowError) {
|
|
19
|
+
const message = typeof retry.data === 'string' ? retry.data : retry.text;
|
|
20
|
+
throw new Error(`HTTP ${retry.status}: ${message}`);
|
|
21
|
+
}
|
|
22
|
+
return retry.data;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!response.ok && !options.allowError) {
|
|
26
|
+
const message = typeof response.data === 'string' ? response.data : response.text;
|
|
27
|
+
// Check for "Project not found" error and provide actionable guidance
|
|
28
|
+
if ((response.status === 404 || response.status === 500) && message.includes('Project not found')) {
|
|
29
|
+
const projectIdMatch = message.match(/Project not found: (proj_[a-z0-9]+)/);
|
|
30
|
+
const projectId = projectIdMatch?.[1] ?? 'unknown';
|
|
31
|
+
throw new Error(`Project not found: ${projectId}\n\n` +
|
|
32
|
+
`The configured project_id does not exist in the connected Eve API.\n` +
|
|
33
|
+
`This often happens when connecting to a different environment (e.g., K8s vs local).\n\n` +
|
|
34
|
+
`To fix this, either:\n` +
|
|
35
|
+
` 1. Clear the default project: eve profile set --project=""\n` +
|
|
36
|
+
` 2. Set a valid project: eve profile set --project=<valid-project-id>\n` +
|
|
37
|
+
` 3. Specify project per-command: eve job create --project=<id> ...\n\n` +
|
|
38
|
+
`To list available projects: eve project list`);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
41
|
+
}
|
|
42
|
+
return response.data;
|
|
43
|
+
}
|
|
44
|
+
async function requestRaw(context, path, options = {}) {
|
|
45
|
+
const headers = {
|
|
46
|
+
...options.headers,
|
|
47
|
+
};
|
|
48
|
+
if (options.body !== undefined) {
|
|
49
|
+
headers['Content-Type'] = 'application/json';
|
|
50
|
+
}
|
|
51
|
+
const token = options.tokenOverride ?? context.token;
|
|
52
|
+
if (token) {
|
|
53
|
+
headers.Authorization = `Bearer ${token}`;
|
|
54
|
+
}
|
|
55
|
+
const response = await fetch(`${context.apiUrl}${path}`, {
|
|
56
|
+
method: options.method ?? 'GET',
|
|
57
|
+
headers,
|
|
58
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
59
|
+
});
|
|
60
|
+
const text = await response.text();
|
|
61
|
+
let data = null;
|
|
62
|
+
if (text) {
|
|
63
|
+
try {
|
|
64
|
+
data = JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
data = text;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { status: response.status, ok: response.ok, data, text };
|
|
71
|
+
}
|
|
72
|
+
async function attemptRefresh(context) {
|
|
73
|
+
if (!context.refreshToken)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (!context.profile.supabase_url || !context.profile.supabase_anon_key)
|
|
76
|
+
return undefined;
|
|
77
|
+
const refreshResponse = await fetch(`${context.profile.supabase_url}/auth/v1/token?grant_type=refresh_token`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
apikey: context.profile.supabase_anon_key,
|
|
82
|
+
Authorization: `Bearer ${context.profile.supabase_anon_key}`,
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify({ refresh_token: context.refreshToken }),
|
|
85
|
+
});
|
|
86
|
+
const refreshText = await refreshResponse.text();
|
|
87
|
+
let refreshData = null;
|
|
88
|
+
if (refreshText) {
|
|
89
|
+
try {
|
|
90
|
+
refreshData = JSON.parse(refreshText);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
refreshData = refreshText;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!refreshResponse.ok || !refreshData || typeof refreshData !== 'object') {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const payload = refreshData;
|
|
100
|
+
if (!payload.access_token) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const expiresAt = payload.expires_in
|
|
104
|
+
? Math.floor(Date.now() / 1000) + payload.expires_in
|
|
105
|
+
: undefined;
|
|
106
|
+
const nextToken = {
|
|
107
|
+
access_token: payload.access_token,
|
|
108
|
+
refresh_token: payload.refresh_token ?? context.refreshToken,
|
|
109
|
+
expires_at: expiresAt,
|
|
110
|
+
token_type: payload.token_type,
|
|
111
|
+
};
|
|
112
|
+
const credentials = (0, config_1.loadCredentials)();
|
|
113
|
+
credentials.profiles[context.profileName] = nextToken;
|
|
114
|
+
(0, config_1.saveCredentials)(credentials);
|
|
115
|
+
return nextToken;
|
|
116
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
exports.saveConfig = saveConfig;
|
|
5
|
+
exports.loadCredentials = loadCredentials;
|
|
6
|
+
exports.saveCredentials = saveCredentials;
|
|
7
|
+
exports.getDefaultProfileName = getDefaultProfileName;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const DEFAULT_PROFILE = 'default';
|
|
12
|
+
const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.eve');
|
|
13
|
+
const CONFIG_PATH = (0, node_path_1.join)(CONFIG_DIR, 'config.json');
|
|
14
|
+
const CREDENTIALS_PATH = (0, node_path_1.join)(CONFIG_DIR, 'credentials.json');
|
|
15
|
+
function ensureConfigDir() {
|
|
16
|
+
if (!(0, node_fs_1.existsSync)(CONFIG_DIR)) {
|
|
17
|
+
(0, node_fs_1.mkdirSync)(CONFIG_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function readJsonFile(path, fallback) {
|
|
21
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
22
|
+
return fallback;
|
|
23
|
+
const raw = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
24
|
+
if (!raw.trim())
|
|
25
|
+
return fallback;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new Error(`Failed to parse ${path}: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function writeJsonFile(path, value) {
|
|
34
|
+
ensureConfigDir();
|
|
35
|
+
(0, node_fs_1.writeFileSync)(path, JSON.stringify(value, null, 2));
|
|
36
|
+
try {
|
|
37
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Best-effort for platforms that don't support chmod.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function loadConfig() {
|
|
44
|
+
const fallback = { active_profile: DEFAULT_PROFILE, profiles: { [DEFAULT_PROFILE]: {} } };
|
|
45
|
+
const config = readJsonFile(CONFIG_PATH, fallback);
|
|
46
|
+
config.profiles = config.profiles ?? {};
|
|
47
|
+
if (!config.active_profile) {
|
|
48
|
+
const [first] = Object.keys(config.profiles);
|
|
49
|
+
config.active_profile = first ?? DEFAULT_PROFILE;
|
|
50
|
+
}
|
|
51
|
+
if (!config.profiles[config.active_profile]) {
|
|
52
|
+
config.profiles[config.active_profile] = {};
|
|
53
|
+
}
|
|
54
|
+
return config;
|
|
55
|
+
}
|
|
56
|
+
function saveConfig(config) {
|
|
57
|
+
config.profiles = config.profiles ?? {};
|
|
58
|
+
if (!config.active_profile) {
|
|
59
|
+
const [first] = Object.keys(config.profiles);
|
|
60
|
+
config.active_profile = first ?? DEFAULT_PROFILE;
|
|
61
|
+
}
|
|
62
|
+
writeJsonFile(CONFIG_PATH, config);
|
|
63
|
+
}
|
|
64
|
+
function loadCredentials() {
|
|
65
|
+
const fallback = { profiles: {} };
|
|
66
|
+
const credentials = readJsonFile(CREDENTIALS_PATH, fallback);
|
|
67
|
+
credentials.profiles = credentials.profiles ?? {};
|
|
68
|
+
return credentials;
|
|
69
|
+
}
|
|
70
|
+
function saveCredentials(credentials) {
|
|
71
|
+
credentials.profiles = credentials.profiles ?? {};
|
|
72
|
+
writeJsonFile(CREDENTIALS_PATH, credentials);
|
|
73
|
+
}
|
|
74
|
+
function getDefaultProfileName(config) {
|
|
75
|
+
return config.active_profile ?? DEFAULT_PROFILE;
|
|
76
|
+
}
|