@eve-horizon/cli 0.2.11 → 0.2.13

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.
@@ -1,397 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleBuild = handleBuild;
4
- const args_1 = require("../lib/args");
5
- const client_1 = require("../lib/client");
6
- const output_1 = require("../lib/output");
7
- const git_js_1 = require("../lib/git.js");
8
- const ERROR_CODES = {
9
- auth_error: { code: 'auth_error', label: 'Authentication Error', hint: "Check GITHUB_TOKEN via 'eve secrets set'" },
10
- clone_error: { code: 'clone_error', label: 'Git Clone Error', hint: "Verify repo URL and access. Check 'eve secrets list'" },
11
- build_error: { code: 'build_error', label: 'Build Error', hint: "Run 'eve build diagnose <build_id>' for full output" },
12
- timeout_error: { code: 'timeout_error', label: 'Timeout Error', hint: 'Consider increasing timeout or checking resources' },
13
- resource_error: { code: 'resource_error', label: 'Resource Error', hint: 'Check disk space and memory on build worker' },
14
- registry_error: { code: 'registry_error', label: 'Registry Error', hint: "Check registry credentials via 'eve secrets list'" },
15
- deploy_error: { code: 'deploy_error', label: 'Deploy Error', hint: "Run 'eve env diagnose <project> <env>'" },
16
- unknown_error: { code: 'unknown_error', label: 'Unknown Error', hint: "Run 'eve build diagnose <build_id>' or 'eve job diagnose <job_id>'" },
17
- };
18
- function getErrorCodeInfo(code) {
19
- return ERROR_CODES[code] ?? ERROR_CODES.unknown_error;
20
- }
21
- // ---------------------------------------------------------------------------
22
- // Entry point
23
- // ---------------------------------------------------------------------------
24
- async function handleBuild(subcommand, positionals, flags, context) {
25
- const json = Boolean(flags.json);
26
- switch (subcommand) {
27
- case 'create':
28
- return handleCreate(flags, context, json);
29
- case 'list':
30
- return handleList(positionals, flags, context, json);
31
- case 'show':
32
- return handleShow(positionals, flags, context, json);
33
- case 'run':
34
- return handleRun(positionals, flags, context, json);
35
- case 'runs':
36
- return handleRuns(positionals, flags, context, json);
37
- case 'logs':
38
- return handleLogs(positionals, flags, context, json);
39
- case 'artifacts':
40
- return handleArtifacts(positionals, flags, context, json);
41
- case 'diagnose':
42
- return handleDiagnose(positionals, flags, context, json);
43
- case 'cancel':
44
- return handleCancel(positionals, flags, context, json);
45
- default:
46
- throw new Error('Usage: eve build <create|list|show|run|runs|logs|artifacts|diagnose|cancel>\n' +
47
- ' create --project <id> --ref <sha> [--services <list>] - create a build spec\n' +
48
- ' list [--project <id>] - list build specs\n' +
49
- ' show <build_id> - show build spec details\n' +
50
- ' run <build_id> - start a build run\n' +
51
- ' runs <build_id> - list runs for a build\n' +
52
- ' logs <build_id> [--run <id>] - show build logs\n' +
53
- ' artifacts <build_id> - list build artifacts\n' +
54
- ' diagnose <build_id> - show full build state\n' +
55
- ' cancel <build_id> - cancel active build run');
56
- }
57
- }
58
- // ---------------------------------------------------------------------------
59
- // Subcommands
60
- // ---------------------------------------------------------------------------
61
- async function handleCreate(flags, context, json) {
62
- const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
63
- const ref = (0, args_1.getStringFlag)(flags, ['ref']);
64
- const manifestHash = (0, args_1.getStringFlag)(flags, ['manifest-hash', 'manifest']);
65
- const services = (0, args_1.getStringFlag)(flags, ['services']);
66
- const repoDir = (0, args_1.getStringFlag)(flags, ['repo-dir', 'repo_dir', 'dir']);
67
- if (!projectId || !ref || !manifestHash) {
68
- throw new Error('Usage: eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>] [--repo-dir <path>]');
69
- }
70
- // Resolve git ref to actual 40-char SHA
71
- const gitSha = await (0, git_js_1.resolveGitRef)(context, projectId, ref, repoDir);
72
- if (!json && ref !== gitSha) {
73
- console.log(`Resolved ref '${ref}' → ${gitSha.substring(0, 8)}...`);
74
- }
75
- const body = { git_sha: gitSha, manifest_hash: manifestHash };
76
- if (services) {
77
- body.services = services.split(',').map(s => s.trim());
78
- }
79
- const build = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds`, { method: 'POST', body });
80
- if (json) {
81
- (0, output_1.outputJson)(build, json);
82
- return;
83
- }
84
- console.log(`Build created: ${build.id}`);
85
- console.log(` Project: ${build.project_id}`);
86
- console.log(` SHA: ${build.git_sha}`);
87
- if (build.manifest_hash) {
88
- console.log(` Manifest: ${build.manifest_hash.substring(0, 12)}...`);
89
- }
90
- console.log(` Created: ${build.created_at}`);
91
- }
92
- async function handleList(positionals, flags, context, json) {
93
- const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
94
- if (!projectId) {
95
- throw new Error('Usage: eve build list [--project <id>]');
96
- }
97
- const query = buildQuery({
98
- limit: (0, args_1.getStringFlag)(flags, ['limit']),
99
- offset: (0, args_1.getStringFlag)(flags, ['offset']),
100
- });
101
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds${query}`);
102
- if (json) {
103
- (0, output_1.outputJson)(response, json);
104
- return;
105
- }
106
- if (response.data.length === 0) {
107
- console.log('No builds found.');
108
- return;
109
- }
110
- formatBuildList(response.data);
111
- }
112
- async function handleShow(positionals, flags, context, json) {
113
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
114
- if (!buildId) {
115
- throw new Error('Usage: eve build show <build_id>');
116
- }
117
- const build = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
118
- if (json) {
119
- (0, output_1.outputJson)(build, json);
120
- return;
121
- }
122
- formatBuildDetail(build);
123
- }
124
- async function handleRun(positionals, flags, context, json) {
125
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
126
- if (!buildId) {
127
- throw new Error('Usage: eve build run <build_id>');
128
- }
129
- const run = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs`, { method: 'POST' });
130
- if (json) {
131
- (0, output_1.outputJson)(run, json);
132
- return;
133
- }
134
- console.log(`Build run started: ${run.id}`);
135
- console.log(` Build: ${run.build_id}`);
136
- console.log(` Status: ${run.status}`);
137
- if (run.backend) {
138
- console.log(` Backend: ${run.backend}`);
139
- }
140
- }
141
- async function handleRuns(positionals, flags, context, json) {
142
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
143
- if (!buildId) {
144
- throw new Error('Usage: eve build runs <build_id>');
145
- }
146
- const query = buildQuery({
147
- limit: (0, args_1.getStringFlag)(flags, ['limit']),
148
- offset: (0, args_1.getStringFlag)(flags, ['offset']),
149
- });
150
- const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs${query}`);
151
- if (json) {
152
- (0, output_1.outputJson)(response, json);
153
- return;
154
- }
155
- if (response.data.length === 0) {
156
- console.log('No runs found for this build.');
157
- return;
158
- }
159
- formatRunList(response.data);
160
- }
161
- function formatLogTimestamp(timestamp) {
162
- if (!timestamp)
163
- return '';
164
- try {
165
- const d = new Date(timestamp);
166
- return `[${d.toLocaleTimeString('en-US', { hour12: false })}] `;
167
- }
168
- catch {
169
- return '';
170
- }
171
- }
172
- async function handleLogs(positionals, flags, context, json) {
173
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
174
- const runId = (0, args_1.getStringFlag)(flags, ['run']);
175
- if (!buildId) {
176
- throw new Error('Usage: eve build logs <build_id> [--run <run_id>]');
177
- }
178
- const query = runId ? `?run_id=${encodeURIComponent(runId)}` : '';
179
- const logs = await (0, client_1.requestJson)(context, `/builds/${buildId}/logs${query}`);
180
- if (json) {
181
- (0, output_1.outputJson)(logs, json);
182
- return;
183
- }
184
- if (logs.logs.length === 0) {
185
- console.log('No logs available.');
186
- return;
187
- }
188
- for (const entry of logs.logs) {
189
- const prefix = formatLogTimestamp(entry.timestamp);
190
- const line = entry.line;
191
- if (typeof line.message === 'string') {
192
- console.log(`${prefix}${line.message}`);
193
- }
194
- else if (Array.isArray(line.lines)) {
195
- for (const item of line.lines) {
196
- if (typeof item === 'string') {
197
- console.log(`${prefix}${item}`);
198
- }
199
- }
200
- }
201
- else {
202
- console.log(`${prefix}${JSON.stringify(line)}`);
203
- }
204
- if (line.level === 'error' && typeof line.error_code === 'string') {
205
- const info = getErrorCodeInfo(line.error_code);
206
- console.log(`${prefix} Type: ${info.label}`);
207
- console.log(`${prefix} Hint: ${info.hint}`);
208
- }
209
- }
210
- }
211
- async function handleArtifacts(positionals, flags, context, json) {
212
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
213
- if (!buildId) {
214
- throw new Error('Usage: eve build artifacts <build_id>');
215
- }
216
- const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
217
- if (json) {
218
- (0, output_1.outputJson)(response, json);
219
- return;
220
- }
221
- if (response.data.length === 0) {
222
- console.log('No artifacts found for this build.');
223
- return;
224
- }
225
- formatArtifactList(response.data);
226
- }
227
- async function handleCancel(positionals, flags, context, json) {
228
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
229
- if (!buildId) {
230
- throw new Error('Usage: eve build cancel <build_id>');
231
- }
232
- const result = await (0, client_1.requestJson)(context, `/builds/${buildId}/cancel`, { method: 'POST' });
233
- if (json) {
234
- (0, output_1.outputJson)(result, json);
235
- return;
236
- }
237
- console.log(`Build cancelled: ${buildId}`);
238
- }
239
- async function handleDiagnose(positionals, flags, context, json) {
240
- const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
241
- if (!buildId) {
242
- throw new Error('Usage: eve build diagnose <build_id>');
243
- }
244
- const spec = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
245
- const runs = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs?limit=20&offset=0`);
246
- const artifacts = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
247
- const latestRun = runs.data[0];
248
- const logs = latestRun
249
- ? await (0, client_1.requestJson)(context, `/builds/${buildId}/logs?run_id=${encodeURIComponent(latestRun.id)}`)
250
- : { logs: [] };
251
- const payload = { spec, runs: runs.data, artifacts: artifacts.data, logs: logs.logs };
252
- if (json) {
253
- (0, output_1.outputJson)(payload, json);
254
- return;
255
- }
256
- console.log(`Build diagnose: ${spec.id}`);
257
- formatBuildDetail(spec);
258
- console.log('');
259
- if (runs.data.length > 0) {
260
- formatRunList(runs.data);
261
- }
262
- else {
263
- console.log('No runs found.');
264
- }
265
- console.log('');
266
- if (artifacts.data.length > 0) {
267
- formatArtifactList(artifacts.data);
268
- }
269
- else {
270
- console.log('No artifacts found.');
271
- }
272
- console.log('');
273
- if (logs.logs.length > 0) {
274
- console.log('Recent logs:');
275
- for (const entry of logs.logs.slice(-50)) {
276
- const line = entry.line;
277
- if (typeof line.message === 'string') {
278
- console.log(line.message);
279
- }
280
- else if (Array.isArray(line.lines)) {
281
- for (const item of line.lines) {
282
- if (typeof item === 'string') {
283
- console.log(item);
284
- }
285
- }
286
- }
287
- else {
288
- console.log(JSON.stringify(line));
289
- }
290
- if (line.level === 'error' && typeof line.error_code === 'string') {
291
- const info = getErrorCodeInfo(line.error_code);
292
- console.log(` Type: ${info.label}`);
293
- console.log(` Hint: ${info.hint}`);
294
- }
295
- }
296
- }
297
- else {
298
- console.log('No logs found.');
299
- }
300
- }
301
- // ---------------------------------------------------------------------------
302
- // Formatters
303
- // ---------------------------------------------------------------------------
304
- function formatBuildList(builds) {
305
- console.log('Build ID'.padEnd(30) + 'SHA'.padEnd(12) + 'Created By'.padEnd(20) + 'Created');
306
- console.log('-'.repeat(90));
307
- for (const build of builds) {
308
- const id = build.id.padEnd(30);
309
- const sha = build.git_sha.substring(0, 8).padEnd(12);
310
- const creator = (build.created_by ?? '-').padEnd(20);
311
- const created = new Date(build.created_at).toLocaleString();
312
- console.log(`${id}${sha}${creator}${created}`);
313
- }
314
- console.log('');
315
- console.log(`Total: ${builds.length} builds`);
316
- }
317
- function formatBuildDetail(build) {
318
- console.log(`Build: ${build.id}`);
319
- console.log(` Project: ${build.project_id}`);
320
- console.log(` SHA: ${build.git_sha}`);
321
- console.log(` Manifest Hash: ${build.manifest_hash}`);
322
- console.log(` Created By: ${build.created_by ?? '-'}`);
323
- console.log(` Created: ${build.created_at}`);
324
- if (build.services_json) {
325
- console.log(` Services: ${JSON.stringify(build.services_json)}`);
326
- }
327
- if (build.inputs_json) {
328
- console.log(` Inputs: ${JSON.stringify(build.inputs_json)}`);
329
- }
330
- if (build.registry_json) {
331
- console.log(` Registry: ${JSON.stringify(build.registry_json)}`);
332
- }
333
- if (build.cache_json) {
334
- console.log(` Cache: ${JSON.stringify(build.cache_json)}`);
335
- }
336
- }
337
- function formatRunList(runs) {
338
- console.log('Run ID'.padEnd(30) + 'Status'.padEnd(15) + 'Backend'.padEnd(15) + 'Started');
339
- console.log('-'.repeat(80));
340
- for (const run of runs) {
341
- const id = run.id.padEnd(30);
342
- const status = run.status.padEnd(15);
343
- const backend = (run.backend ?? '-').padEnd(15);
344
- const started = run.started_at ? new Date(run.started_at).toLocaleString() : '-';
345
- console.log(`${id}${status}${backend}${started}`);
346
- if (run.error_message) {
347
- console.log(` Error: ${run.error_message}`);
348
- }
349
- if (run.error_code) {
350
- const info = getErrorCodeInfo(run.error_code);
351
- console.log(` Error Type: ${info.label}`);
352
- console.log(` Hint: ${info.hint}`);
353
- }
354
- }
355
- console.log('');
356
- console.log(`Total: ${runs.length} runs`);
357
- }
358
- function formatArtifactList(artifacts) {
359
- console.log('Service'.padEnd(25) + 'Image'.padEnd(50) + 'Size');
360
- console.log('-'.repeat(90));
361
- for (const artifact of artifacts) {
362
- const service = artifact.service_name.padEnd(25);
363
- const image = artifact.image_ref.padEnd(50);
364
- const size = artifact.size_bytes ? formatBytes(artifact.size_bytes) : '-';
365
- console.log(`${service}${image}${size}`);
366
- if (artifact.digest) {
367
- console.log(` Digest: ${artifact.digest}`);
368
- }
369
- if (artifact.platforms_json && artifact.platforms_json.length > 0) {
370
- console.log(` Platforms: ${artifact.platforms_json.join(', ')}`);
371
- }
372
- }
373
- console.log('');
374
- console.log(`Total: ${artifacts.length} artifacts`);
375
- }
376
- function formatBytes(bytes) {
377
- if (bytes < 1024)
378
- return `${bytes} B`;
379
- if (bytes < 1024 * 1024)
380
- return `${(bytes / 1024).toFixed(1)} KB`;
381
- if (bytes < 1024 * 1024 * 1024)
382
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
383
- return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
384
- }
385
- // ---------------------------------------------------------------------------
386
- // Utilities
387
- // ---------------------------------------------------------------------------
388
- function buildQuery(params) {
389
- const search = new URLSearchParams();
390
- Object.entries(params).forEach(([key, value]) => {
391
- if (value === undefined || value === '')
392
- return;
393
- search.set(key, value);
394
- });
395
- const query = search.toString();
396
- return query ? `?${query}` : '';
397
- }
@@ -1,55 +0,0 @@
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
- }
@@ -1,204 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleDb = handleDb;
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 MIGRATION_REGEX = /^(\d{14})_([a-z0-9_]+)\.sql$/;
10
- async function handleDb(subcommand, positionals, flags, context) {
11
- const jsonOutput = Boolean(flags.json);
12
- switch (subcommand) {
13
- case 'schema':
14
- return handleSchema(positionals, flags, context, jsonOutput);
15
- case 'rls':
16
- return handleRls(positionals, flags, context, jsonOutput);
17
- case 'sql':
18
- return handleSql(positionals, flags, context, jsonOutput);
19
- case 'migrate':
20
- return handleMigrate(positionals, flags, context, jsonOutput);
21
- case 'migrations':
22
- return handleMigrations(positionals, flags, context, jsonOutput);
23
- case 'new':
24
- return handleNew(positionals, flags);
25
- default:
26
- throw new Error('Usage: eve db <command> [options]\n\n' +
27
- 'Commands:\n' +
28
- ' schema --env <name> Show DB schema info\n' +
29
- ' rls --env <name> Show RLS policies and tables\n' +
30
- ' sql --env <name> --sql <stmt> Run parameterized SQL\n' +
31
- ' migrate --env <name> [--path <dir>] Apply pending migrations\n' +
32
- ' migrations --env <name> List applied migrations\n' +
33
- ' new <description> Create new migration file');
34
- }
35
- }
36
- async function handleSchema(positionals, flags, context, jsonOutput) {
37
- const { projectId, envName } = resolveProjectEnv(positionals, flags, context);
38
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/db/schema`);
39
- (0, output_1.outputJson)(response, jsonOutput);
40
- }
41
- async function handleRls(positionals, flags, context, jsonOutput) {
42
- const { projectId, envName } = resolveProjectEnv(positionals, flags, context);
43
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/db/rls`);
44
- (0, output_1.outputJson)(response, jsonOutput);
45
- }
46
- async function handleSql(positionals, flags, context, jsonOutput) {
47
- const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
48
- const envFromFlag = (0, args_1.getStringFlag)(flags, ['env']);
49
- const envName = envFromFlag ?? positionals[0];
50
- if (!projectId || !envName) {
51
- throw new Error('Missing project or env. Usage: --env <name> [--project <id>]');
52
- }
53
- const sqlPositionals = envFromFlag ? positionals : positionals.slice(1);
54
- const sqlInput = (0, args_1.getStringFlag)(flags, ['sql']) ?? sqlPositionals.join(' ');
55
- const fileInput = (0, args_1.getStringFlag)(flags, ['file']);
56
- const sqlText = fileInput ? (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(fileInput), 'utf-8') : sqlInput;
57
- if (!sqlText) {
58
- throw new Error('Usage: eve db sql --env <name> --sql <statement> [--params <json>] [--write]');
59
- }
60
- const paramsFlag = (0, args_1.getStringFlag)(flags, ['params']);
61
- const params = paramsFlag ? parseJson(paramsFlag, '--params') : undefined;
62
- const allowWrite = (0, args_1.toBoolean)(flags.write);
63
- if (allowWrite) {
64
- console.warn('⚠️ Write mode enabled - changes will be committed to the database');
65
- }
66
- const body = {
67
- sql: sqlText,
68
- ...(params !== undefined ? { params } : {}),
69
- ...(allowWrite !== undefined ? { allow_write: allowWrite } : {}),
70
- };
71
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/db/sql`, {
72
- method: 'POST',
73
- body,
74
- });
75
- (0, output_1.outputJson)(response, jsonOutput);
76
- }
77
- function resolveProjectEnv(positionals, flags, context) {
78
- const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
79
- const envName = (0, args_1.getStringFlag)(flags, ['env']) ?? positionals[0];
80
- if (!projectId || !envName) {
81
- throw new Error('Missing project or env. Usage: --env <name> [--project <id>]');
82
- }
83
- return { projectId, envName };
84
- }
85
- function parseJson(value, label) {
86
- try {
87
- return JSON.parse(value);
88
- }
89
- catch (error) {
90
- throw new Error(`${label} must be valid JSON: ${error.message}`);
91
- }
92
- }
93
- async function handleMigrate(positionals, flags, context, jsonOutput) {
94
- const { projectId, envName } = resolveProjectEnv(positionals, flags, context);
95
- const migrationsPath = (0, args_1.getStringFlag)(flags, ['path']) ?? 'db/migrations';
96
- const fullPath = (0, node_path_1.resolve)(migrationsPath);
97
- if (!(0, node_fs_1.existsSync)(fullPath)) {
98
- throw new Error(`Migrations directory not found: ${fullPath}`);
99
- }
100
- // Read and validate migration files
101
- const files = (0, node_fs_1.readdirSync)(fullPath)
102
- .filter((f) => f.endsWith('.sql'))
103
- .sort();
104
- if (files.length === 0) {
105
- console.log('No migration files found');
106
- return;
107
- }
108
- // Validate filenames
109
- for (const file of files) {
110
- if (!MIGRATION_REGEX.test(file)) {
111
- throw new Error(`Invalid migration filename: ${file}\n` +
112
- `Expected format: YYYYMMDDHHmmss_description.sql\n` +
113
- `Example: 20260128100000_create_users.sql`);
114
- }
115
- }
116
- // Read file contents
117
- const migrations = files.map((file) => ({
118
- name: file,
119
- sql: (0, node_fs_1.readFileSync)((0, node_path_1.join)(fullPath, file), 'utf-8'),
120
- }));
121
- console.log(`Found ${migrations.length} migration files`);
122
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/db/migrate`, {
123
- method: 'POST',
124
- body: { migrations },
125
- });
126
- if (jsonOutput) {
127
- (0, output_1.outputJson)(response, jsonOutput);
128
- }
129
- else {
130
- const applied = response.applied ?? [];
131
- if (applied.length === 0) {
132
- console.log('No new migrations to apply');
133
- }
134
- else {
135
- console.log(`Applied ${applied.length} migrations:`);
136
- for (const m of applied) {
137
- console.log(` ✓ ${m.name}`);
138
- }
139
- }
140
- }
141
- }
142
- async function handleMigrations(positionals, flags, context, jsonOutput) {
143
- const { projectId, envName } = resolveProjectEnv(positionals, flags, context);
144
- const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/db/migrations`);
145
- if (jsonOutput) {
146
- (0, output_1.outputJson)(response, jsonOutput);
147
- }
148
- else {
149
- const migrations = response.migrations ?? [];
150
- if (migrations.length === 0) {
151
- console.log('No migrations have been applied');
152
- }
153
- else {
154
- console.log(`Applied migrations (${migrations.length}):\n`);
155
- console.log(' Name Applied At');
156
- console.log(' ' + '-'.repeat(70));
157
- for (const m of migrations) {
158
- const date = new Date(m.applied_at).toLocaleString();
159
- console.log(` ${m.name.padEnd(40)} ${date}`);
160
- }
161
- }
162
- }
163
- }
164
- function handleNew(positionals, flags) {
165
- const description = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['name']);
166
- if (!description) {
167
- throw new Error('Usage: eve db new <description>\nExample: eve db new create_users');
168
- }
169
- // Validate description format
170
- const normalizedDescription = description.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '');
171
- if (!normalizedDescription || !/^[a-z0-9_]+$/.test(normalizedDescription)) {
172
- throw new Error('Description must contain only lowercase letters, numbers, and underscores');
173
- }
174
- // Generate timestamp (YYYYMMDDHHmmss in UTC)
175
- const now = new Date();
176
- const timestamp = [
177
- now.getUTCFullYear(),
178
- String(now.getUTCMonth() + 1).padStart(2, '0'),
179
- String(now.getUTCDate()).padStart(2, '0'),
180
- String(now.getUTCHours()).padStart(2, '0'),
181
- String(now.getUTCMinutes()).padStart(2, '0'),
182
- String(now.getUTCSeconds()).padStart(2, '0'),
183
- ].join('');
184
- const filename = `${timestamp}_${normalizedDescription}.sql`;
185
- const migrationsPath = (0, args_1.getStringFlag)(flags, ['path']) ?? 'db/migrations';
186
- const fullPath = (0, node_path_1.resolve)(migrationsPath);
187
- // Create directory if it doesn't exist
188
- if (!(0, node_fs_1.existsSync)(fullPath)) {
189
- (0, node_fs_1.mkdirSync)(fullPath, { recursive: true });
190
- }
191
- const filePath = (0, node_path_1.join)(fullPath, filename);
192
- // Check if file already exists
193
- if ((0, node_fs_1.existsSync)(filePath)) {
194
- throw new Error(`Migration file already exists: ${filePath}`);
195
- }
196
- const template = `-- Migration: ${normalizedDescription}
197
- -- Created: ${now.toISOString()}
198
-
199
- -- Write your SQL migration here
200
-
201
- `;
202
- (0, node_fs_1.writeFileSync)(filePath, template);
203
- console.log(`Created: ${filePath}`);
204
- }