@develemail/cli 0.1.4 → 0.1.6
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/dist/index.js +342 -17
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command11 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/send.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -91,7 +91,10 @@ var COL_STATUS = 10;
|
|
|
91
91
|
function col(s, w) {
|
|
92
92
|
return s.slice(0, w).padEnd(w);
|
|
93
93
|
}
|
|
94
|
-
var logsCommand = new Command2("logs").description("List recent emails").option("--limit <n>", "Number of emails to show", "20").option(
|
|
94
|
+
var logsCommand = new Command2("logs").description("List recent emails").option("--limit <n>", "Number of emails to show", "20").option(
|
|
95
|
+
"--status <status>",
|
|
96
|
+
"Filter by status (sent, delivered, queued, deferred, bounced, failed, \u2026)"
|
|
97
|
+
).action(async (opts) => {
|
|
95
98
|
const cfg = loadConfig();
|
|
96
99
|
const params = new URLSearchParams({ limit: opts.limit });
|
|
97
100
|
if (opts.status) params.set("status", opts.status);
|
|
@@ -139,6 +142,12 @@ var tailCommand = new Command3("tail").description("Poll and print new email eve
|
|
|
139
142
|
const cfg = loadConfig();
|
|
140
143
|
const seen = /* @__PURE__ */ new Set();
|
|
141
144
|
const startedAt = opts.since ? new Date(opts.since) : /* @__PURE__ */ new Date();
|
|
145
|
+
if (opts.since && isNaN(startedAt.getTime())) {
|
|
146
|
+
console.error(
|
|
147
|
+
`Error: invalid --since value "${opts.since}" \u2014 use ISO 8601 (e.g. 2026-07-20) or other valid date format`
|
|
148
|
+
);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
142
151
|
console.log("Tailing email events\u2026 (Ctrl-C to stop)");
|
|
143
152
|
const poll = async () => {
|
|
144
153
|
try {
|
|
@@ -173,15 +182,39 @@ import { Command as Command4 } from "commander";
|
|
|
173
182
|
import {
|
|
174
183
|
createServer
|
|
175
184
|
} from "http";
|
|
176
|
-
|
|
185
|
+
|
|
186
|
+
// src/open-browser.ts
|
|
187
|
+
import { execFile } from "child_process";
|
|
177
188
|
import { platform } from "os";
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
189
|
+
function isHttpUrl(url) {
|
|
190
|
+
try {
|
|
191
|
+
const { protocol } = new URL(url);
|
|
192
|
+
return protocol === "http:" || protocol === "https:";
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function commandFor(os, url) {
|
|
198
|
+
if (os === "darwin") return ["open", [url]];
|
|
199
|
+
if (os === "win32") return ["rundll32", ["url.dll,FileProtocolHandler", url]];
|
|
200
|
+
return ["xdg-open", [url]];
|
|
201
|
+
}
|
|
202
|
+
function openBrowser(url, deps = {}) {
|
|
203
|
+
if (!isHttpUrl(url)) return false;
|
|
204
|
+
const spawn = deps.spawn ?? ((file2, args2) => {
|
|
205
|
+
execFile(file2, args2, () => {
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
const [file, args] = commandFor(deps.platform ?? platform(), url);
|
|
209
|
+
spawn(file, args);
|
|
210
|
+
return true;
|
|
181
211
|
}
|
|
182
|
-
|
|
212
|
+
|
|
213
|
+
// src/commands/login.ts
|
|
214
|
+
var loginCommand = new Command4("login").description("Authenticate with your DevelEmail instance via browser").option(
|
|
183
215
|
"--url <url>",
|
|
184
|
-
"Base URL of your DevelEmail
|
|
216
|
+
"Base URL of your DevelEmail API",
|
|
217
|
+
"https://develemail.com/api"
|
|
185
218
|
).action(async (opts) => {
|
|
186
219
|
const apiUrl = opts.url.replace(/\/$/, "");
|
|
187
220
|
const baseUrl = apiUrl.replace(/\/api$/, "");
|
|
@@ -277,7 +310,22 @@ var listCommand = new Command5("list").description("List all projects").action(a
|
|
|
277
310
|
);
|
|
278
311
|
}
|
|
279
312
|
});
|
|
280
|
-
var
|
|
313
|
+
var getCommand = new Command5("get").description("Get project details").argument("<id>", "Project ID").action(async (id) => {
|
|
314
|
+
const { url, token } = loadOwnerSession();
|
|
315
|
+
const project = await apiRequest(`${url}/projects/${id}`, token);
|
|
316
|
+
console.log(`ID: ${project.id}`);
|
|
317
|
+
console.log(`Name: ${project.name}`);
|
|
318
|
+
console.log(`Environment: ${project.environment}`);
|
|
319
|
+
console.log(`From Address: ${project.defaultFromAddress ?? "\u2014"}`);
|
|
320
|
+
console.log(
|
|
321
|
+
`Rate Limit: ${project.rateLimitPerDay ?? "Unlimited"} per day`
|
|
322
|
+
);
|
|
323
|
+
if (project.domainId) {
|
|
324
|
+
console.log(`Domain ID: ${project.domainId}`);
|
|
325
|
+
}
|
|
326
|
+
console.log(`Created At: ${new Date(project.createdAt).toISOString()}`);
|
|
327
|
+
});
|
|
328
|
+
var createCommand = new Command5("create").description("Create a new project").requiredOption("--name <name>", "Project name (slug, e.g. my-app)").option("--domain <domainId>", "Domain ID for this project").option(
|
|
281
329
|
"--env <env>",
|
|
282
330
|
"Environment: development | staging | production",
|
|
283
331
|
"production"
|
|
@@ -288,6 +336,7 @@ var createCommand = new Command5("create").description("Create a new project").r
|
|
|
288
336
|
method: "POST",
|
|
289
337
|
body: JSON.stringify({
|
|
290
338
|
name: opts.name,
|
|
339
|
+
domainId: opts.domain,
|
|
291
340
|
environment: opts.env,
|
|
292
341
|
defaultFromAddress: opts.from,
|
|
293
342
|
rateLimitPerDay: opts.rateLimit ? Number(opts.rateLimit) : void 0,
|
|
@@ -297,10 +346,284 @@ var createCommand = new Command5("create").description("Create a new project").r
|
|
|
297
346
|
console.log(`Created project: ${project.name} (${project.id})`);
|
|
298
347
|
}
|
|
299
348
|
);
|
|
300
|
-
var projectCommand = new Command5("project").description("Manage projects").addCommand(listCommand).addCommand(createCommand);
|
|
349
|
+
var projectCommand = new Command5("project").description("Manage projects").addCommand(listCommand).addCommand(getCommand).addCommand(createCommand);
|
|
301
350
|
|
|
302
|
-
// src/commands/
|
|
351
|
+
// src/commands/domains.ts
|
|
352
|
+
import { Command as Command7 } from "commander";
|
|
353
|
+
|
|
354
|
+
// src/owner-api.ts
|
|
355
|
+
var OwnerApiError = class extends Error {
|
|
356
|
+
constructor(message, status) {
|
|
357
|
+
super(message);
|
|
358
|
+
this.status = status;
|
|
359
|
+
}
|
|
360
|
+
status;
|
|
361
|
+
};
|
|
362
|
+
async function ownerRequest(path, init) {
|
|
363
|
+
const { url, token } = loadOwnerSession();
|
|
364
|
+
const res = await fetch(`${url}${path}`, {
|
|
365
|
+
...init,
|
|
366
|
+
headers: {
|
|
367
|
+
Cookie: `session=${token}`,
|
|
368
|
+
"Content-Type": "application/json",
|
|
369
|
+
...init?.headers ?? {}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
if (res.status === 401) {
|
|
373
|
+
throw new OwnerApiError(
|
|
374
|
+
"Session expired or missing. Run `devel login` first.",
|
|
375
|
+
401
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
if (!res.ok) {
|
|
379
|
+
const body = await res.json().catch(() => ({}));
|
|
380
|
+
throw new OwnerApiError(body.message ?? res.statusText, res.status);
|
|
381
|
+
}
|
|
382
|
+
return res.json();
|
|
383
|
+
}
|
|
384
|
+
function exitOnError(fn) {
|
|
385
|
+
return async (...args) => {
|
|
386
|
+
try {
|
|
387
|
+
await fn(...args);
|
|
388
|
+
} catch (err) {
|
|
389
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/commands/dns-records.ts
|
|
396
|
+
var BIND_TTL = 3600;
|
|
397
|
+
function formatBind(records) {
|
|
398
|
+
return records.map((r) => {
|
|
399
|
+
const value = r.type === "TXT" ? `"${r.value.replace(/"/g, '\\"')}"` : r.value;
|
|
400
|
+
return `${r.host}. ${BIND_TTL} IN ${r.type} ${value}`;
|
|
401
|
+
}).join("\n");
|
|
402
|
+
}
|
|
403
|
+
function formatTable(records) {
|
|
404
|
+
return records.map(
|
|
405
|
+
(r) => `${r.priority.padEnd(11)} ${r.type.padEnd(5)} ${r.host}
|
|
406
|
+
${r.value}
|
|
407
|
+
(${r.purpose})`
|
|
408
|
+
).join("\n");
|
|
409
|
+
}
|
|
410
|
+
function printRecords(records, format, json) {
|
|
411
|
+
if (json) console.log(JSON.stringify(records, null, 2));
|
|
412
|
+
else if (format === "bind") console.log(formatBind(records));
|
|
413
|
+
else console.log(formatTable(records));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/commands/publish-dns.ts
|
|
303
417
|
import { Command as Command6 } from "commander";
|
|
418
|
+
import { createInterface } from "readline/promises";
|
|
419
|
+
function pickZone(domainName, zones) {
|
|
420
|
+
const matches = zones.filter(
|
|
421
|
+
(z) => domainName === z.name || domainName.endsWith(`.${z.name}`)
|
|
422
|
+
);
|
|
423
|
+
if (matches.length === 0) {
|
|
424
|
+
throw new Error(`No Cloudflare zone matches ${domainName}.`);
|
|
425
|
+
}
|
|
426
|
+
const longest = Math.max(...matches.map((z) => z.name.length));
|
|
427
|
+
const best = matches.filter((z) => z.name.length === longest);
|
|
428
|
+
if (best.length > 1) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Several zones match ${domainName}: ${best.map((z) => z.id).join(", ")}. Pass --zone.`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
return best[0];
|
|
434
|
+
}
|
|
435
|
+
function printPlan(plan) {
|
|
436
|
+
for (const w of plan.warnings) console.log(`warning: ${w}`);
|
|
437
|
+
for (const c of plan.changes) {
|
|
438
|
+
const from = c.existingContent ? ` (was: ${c.existingContent})` : "";
|
|
439
|
+
console.log(
|
|
440
|
+
`${c.action.padEnd(6)} ${c.type} ${c.name} -> ${c.content}${from}`
|
|
441
|
+
);
|
|
442
|
+
for (const w of c.warnings) console.log(` warning: ${w}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function confirm() {
|
|
446
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
447
|
+
try {
|
|
448
|
+
const answer = await rl.question("Apply these changes? [y/N] ");
|
|
449
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
450
|
+
} finally {
|
|
451
|
+
rl.close();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
var publishDnsCommand = new Command6("publish-dns").description("Publish a domain\u2019s DNS records to Cloudflare").argument("<id>", "Domain ID").option("--zone <zoneId>", "Cloudflare zone ID (default: auto-pick)").option("--yes", "Apply without prompting").option("--json", "Output JSON").action(
|
|
455
|
+
exitOnError(async (id, opts) => {
|
|
456
|
+
let zoneId = opts.zone;
|
|
457
|
+
if (!zoneId) {
|
|
458
|
+
const domain = await ownerRequest(`/domains/${id}`);
|
|
459
|
+
const zones = await ownerRequest("/cloudflare/zones");
|
|
460
|
+
zoneId = pickZone(domain.name, zones).id;
|
|
461
|
+
}
|
|
462
|
+
const plan = await ownerRequest(`/cloudflare/domains/${id}/plan`, {
|
|
463
|
+
method: "POST",
|
|
464
|
+
body: JSON.stringify({ zoneId })
|
|
465
|
+
});
|
|
466
|
+
if (!opts.json) printPlan(plan);
|
|
467
|
+
const apply = opts.yes || process.stdin.isTTY === true && await confirm();
|
|
468
|
+
if (!apply) {
|
|
469
|
+
if (opts.json) console.log(JSON.stringify({ plan, applied: false }));
|
|
470
|
+
else
|
|
471
|
+
console.log("Dry run: nothing applied. Re-run with --yes to apply.");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const result = await ownerRequest("/cloudflare/apply", {
|
|
475
|
+
method: "POST",
|
|
476
|
+
body: JSON.stringify(plan)
|
|
477
|
+
});
|
|
478
|
+
if (opts.json)
|
|
479
|
+
console.log(JSON.stringify({ plan, applied: true, result }));
|
|
480
|
+
else console.log("Applied.");
|
|
481
|
+
})
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
// src/commands/domains.ts
|
|
485
|
+
var COL_ID3 = 40;
|
|
486
|
+
var COL_NAME2 = 30;
|
|
487
|
+
var COL_STATUS2 = 15;
|
|
488
|
+
function col3(s, w) {
|
|
489
|
+
return (s ?? "").slice(0, w).padEnd(w);
|
|
490
|
+
}
|
|
491
|
+
function printStatuses(d) {
|
|
492
|
+
console.log(`SPF Status: ${d.spfStatus ?? "\u2014"}`);
|
|
493
|
+
console.log(`DKIM Status: ${d.dkimStatus ?? "\u2014"}`);
|
|
494
|
+
console.log(`DMARC Status: ${d.dmarcStatus ?? "\u2014"}`);
|
|
495
|
+
}
|
|
496
|
+
var listCommand2 = new Command7("list").description("List all domains").action(
|
|
497
|
+
exitOnError(async () => {
|
|
498
|
+
const domains = await ownerRequest("/domains");
|
|
499
|
+
if (domains.length === 0) {
|
|
500
|
+
console.log("No domains found.");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const header = `${col3("ID", COL_ID3)} ${col3("NAME", COL_NAME2)} ${col3("STATUS", COL_STATUS2)} CREATED`;
|
|
504
|
+
console.log(header);
|
|
505
|
+
console.log("-".repeat(header.length + 20));
|
|
506
|
+
for (const d of domains) {
|
|
507
|
+
const createdAt = new Date(d.createdAt).toLocaleDateString();
|
|
508
|
+
console.log(
|
|
509
|
+
`${col3(d.id, COL_ID3)} ${col3(d.name, COL_NAME2)} ${col3(d.verificationStatus, COL_STATUS2)} ${createdAt}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
);
|
|
514
|
+
var getCommand2 = new Command7("get").description("Get domain details").argument("<id>", "Domain ID").action(
|
|
515
|
+
exitOnError(async (id) => {
|
|
516
|
+
const domain = await ownerRequest(`/domains/${id}`);
|
|
517
|
+
console.log(`ID: ${domain.id}`);
|
|
518
|
+
console.log(`Name: ${domain.name}`);
|
|
519
|
+
console.log(`Verification Status: ${domain.verificationStatus}`);
|
|
520
|
+
printStatuses(domain);
|
|
521
|
+
console.log(`MX Status: ${domain.mxStatus ?? "\u2014"}`);
|
|
522
|
+
console.log(`Return Path Status: ${domain.returnPathStatus ?? "\u2014"}`);
|
|
523
|
+
console.log(`PTR Status: ${domain.ptrStatus ?? "\u2014"}`);
|
|
524
|
+
console.log(`DKIM Selector: ${domain.dkimSelector}`);
|
|
525
|
+
console.log(`DKIM Public Key:
|
|
526
|
+
${domain.dkimPublicKey}`);
|
|
527
|
+
console.log(`Created At: ${new Date(domain.createdAt).toISOString()}`);
|
|
528
|
+
})
|
|
529
|
+
);
|
|
530
|
+
var addCommand = new Command7("add").description("Create a domain and print its required DNS records").argument("<name>", "Domain name, e.g. example.com").option("--dkim-selector <selector>", "DKIM selector").option("--json", "Output JSON").action(
|
|
531
|
+
exitOnError(
|
|
532
|
+
async (name, opts) => {
|
|
533
|
+
const domain = await ownerRequest("/domains", {
|
|
534
|
+
method: "POST",
|
|
535
|
+
body: JSON.stringify({ name, dkimSelector: opts.dkimSelector })
|
|
536
|
+
});
|
|
537
|
+
const records = await ownerRequest(
|
|
538
|
+
`/domains/${domain.id}/dns-records`
|
|
539
|
+
);
|
|
540
|
+
if (opts.json) {
|
|
541
|
+
console.log(JSON.stringify({ domain, records }, null, 2));
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
console.log(`Created domain: ${domain.name} (${domain.id})`);
|
|
545
|
+
console.log("\nRequired DNS records:");
|
|
546
|
+
printRecords(records, "table", false);
|
|
547
|
+
}
|
|
548
|
+
)
|
|
549
|
+
);
|
|
550
|
+
var recordsCommand = new Command7("records").description("Print the DNS records a domain requires").argument("<id>", "Domain ID").option("--json", "Output JSON").option("--format <format>", "table | bind", "table").action(
|
|
551
|
+
exitOnError(
|
|
552
|
+
async (id, opts) => {
|
|
553
|
+
const records = await ownerRequest(
|
|
554
|
+
`/domains/${id}/dns-records`
|
|
555
|
+
);
|
|
556
|
+
printRecords(records, opts.format, opts.json === true);
|
|
557
|
+
}
|
|
558
|
+
)
|
|
559
|
+
);
|
|
560
|
+
var POLL_INTERVAL_MS2 = 1e4;
|
|
561
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
562
|
+
var verifyCommand = new Command7("verify").description("Trigger domain verification").argument("<id>", "Domain ID").option("--wait", "Poll until verified or timed out").option("--timeout <seconds>", "Max seconds to wait with --wait", "600").action(
|
|
563
|
+
exitOnError(
|
|
564
|
+
async (id, opts) => {
|
|
565
|
+
const deadline = Date.now() + Number(opts.timeout) * 1e3;
|
|
566
|
+
for (; ; ) {
|
|
567
|
+
const domain = await ownerRequest(
|
|
568
|
+
`/domains/${id}/verify`,
|
|
569
|
+
{ method: "POST" }
|
|
570
|
+
);
|
|
571
|
+
console.log(`Domain verification: ${domain.name}`);
|
|
572
|
+
console.log(`Current status: ${domain.verificationStatus}`);
|
|
573
|
+
printStatuses(domain);
|
|
574
|
+
if (!opts.wait || domain.verificationStatus === "verified") return;
|
|
575
|
+
if (Date.now() + POLL_INTERVAL_MS2 > deadline) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`Timed out after ${opts.timeout}s; not yet verified.`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
)
|
|
584
|
+
);
|
|
585
|
+
var domainsCommand = new Command7("domains").description("Manage domains").addCommand(listCommand2).addCommand(getCommand2).addCommand(addCommand).addCommand(recordsCommand).addCommand(publishDnsCommand).addCommand(verifyCommand);
|
|
586
|
+
|
|
587
|
+
// src/commands/cloudflare.ts
|
|
588
|
+
import { Command as Command8 } from "commander";
|
|
589
|
+
var connectCommand = new Command8("connect").description("Connect a Cloudflare API token to your organization").requiredOption(
|
|
590
|
+
"--token-env <VAR>",
|
|
591
|
+
"Environment variable holding the Cloudflare API token"
|
|
592
|
+
).action(
|
|
593
|
+
exitOnError(async (opts) => {
|
|
594
|
+
const apiToken = process.env[opts.tokenEnv];
|
|
595
|
+
if (!apiToken) {
|
|
596
|
+
throw new Error(`Environment variable ${opts.tokenEnv} is not set.`);
|
|
597
|
+
}
|
|
598
|
+
await ownerRequest("/cloudflare/connect", {
|
|
599
|
+
method: "POST",
|
|
600
|
+
body: JSON.stringify({ apiToken })
|
|
601
|
+
});
|
|
602
|
+
console.log("Cloudflare connected.");
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
var statusCommand = new Command8("status").description("Show whether Cloudflare is connected").action(
|
|
606
|
+
exitOnError(async () => {
|
|
607
|
+
const { connected } = await ownerRequest(
|
|
608
|
+
"/cloudflare/status"
|
|
609
|
+
);
|
|
610
|
+
console.log(connected ? "Connected" : "Not connected");
|
|
611
|
+
})
|
|
612
|
+
);
|
|
613
|
+
var zonesCommand = new Command8("zones").description("List Cloudflare zones").option("--json", "Output JSON").action(
|
|
614
|
+
exitOnError(async (opts) => {
|
|
615
|
+
const zones = await ownerRequest("/cloudflare/zones");
|
|
616
|
+
if (opts.json) {
|
|
617
|
+
console.log(JSON.stringify(zones, null, 2));
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
for (const z of zones) console.log(`${z.id} ${z.name} ${z.status}`);
|
|
621
|
+
})
|
|
622
|
+
);
|
|
623
|
+
var cloudflareCommand = new Command8("cloudflare").description("Manage the Cloudflare integration").addCommand(connectCommand).addCommand(statusCommand).addCommand(zonesCommand);
|
|
624
|
+
|
|
625
|
+
// src/commands/key.ts
|
|
626
|
+
import { Command as Command9 } from "commander";
|
|
304
627
|
function ownerHeaders2(token) {
|
|
305
628
|
return { Cookie: `session=${token}`, "Content-Type": "application/json" };
|
|
306
629
|
}
|
|
@@ -316,7 +639,7 @@ async function apiRequest2(url, token, init) {
|
|
|
316
639
|
}
|
|
317
640
|
return res.json();
|
|
318
641
|
}
|
|
319
|
-
var
|
|
642
|
+
var listCommand3 = new Command9("list").description("List API keys for a project").requiredOption("--project <id>", "Project ID").action(async (opts) => {
|
|
320
643
|
const { url, token } = loadOwnerSession();
|
|
321
644
|
const keys = await apiRequest2(
|
|
322
645
|
`${url}/projects/${opts.project}/api-keys`,
|
|
@@ -331,7 +654,7 @@ var listCommand2 = new Command6("list").description("List API keys for a project
|
|
|
331
654
|
console.log(`${status} ${k.keyPrefix}\u2026 ${k.name} (${k.id})`);
|
|
332
655
|
}
|
|
333
656
|
});
|
|
334
|
-
var createCommand2 = new
|
|
657
|
+
var createCommand2 = new Command9("create").description("Create a new API key for a project").requiredOption("--project <id>", "Project ID").requiredOption("--name <name>", "Key name (e.g. production, ci)").option("--expires <date>", "Expiry date (ISO-8601, e.g. 2027-01-01)").option("--rate-limit <n>", "Max requests per minute").action(
|
|
335
658
|
async (opts) => {
|
|
336
659
|
const { url, token } = loadOwnerSession();
|
|
337
660
|
const result = await apiRequest2(
|
|
@@ -353,10 +676,10 @@ var createCommand2 = new Command6("create").description("Create a new API key fo
|
|
|
353
676
|
console.log("Store the secret now \u2014 it will not be shown again.");
|
|
354
677
|
}
|
|
355
678
|
);
|
|
356
|
-
var keyCommand = new
|
|
679
|
+
var keyCommand = new Command9("key").description("Manage API keys").addCommand(listCommand3).addCommand(createCommand2);
|
|
357
680
|
|
|
358
681
|
// src/commands/init.ts
|
|
359
|
-
import { Command as
|
|
682
|
+
import { Command as Command10 } from "commander";
|
|
360
683
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
361
684
|
import { resolve } from "path";
|
|
362
685
|
function ownerHeaders3(token) {
|
|
@@ -386,7 +709,7 @@ function upsertEnvFile(filePath, vars) {
|
|
|
386
709
|
const content = lines.join("\n").trimEnd() + (lines.some((l) => l.trim()) ? "\n" : "") + appended + "\n";
|
|
387
710
|
writeFileSync2(filePath, content, "utf-8");
|
|
388
711
|
}
|
|
389
|
-
var initCommand = new
|
|
712
|
+
var initCommand = new Command10("init").description(
|
|
390
713
|
"Provision a project + API key and write credentials to an env file"
|
|
391
714
|
).requiredOption(
|
|
392
715
|
"--project <name>",
|
|
@@ -440,10 +763,12 @@ var initCommand = new Command7("init").description(
|
|
|
440
763
|
);
|
|
441
764
|
|
|
442
765
|
// src/index.ts
|
|
443
|
-
var program = new
|
|
766
|
+
var program = new Command11().name("devel").description("DevelEmail CLI \u2014 interact with your DevelEmail instance").version("0.1.0");
|
|
444
767
|
program.addCommand(loginCommand);
|
|
445
768
|
program.addCommand(logoutCommand);
|
|
446
769
|
program.addCommand(projectCommand);
|
|
770
|
+
program.addCommand(domainsCommand);
|
|
771
|
+
program.addCommand(cloudflareCommand);
|
|
447
772
|
program.addCommand(keyCommand);
|
|
448
773
|
program.addCommand(initCommand);
|
|
449
774
|
program.addCommand(sendCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@develemail/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"devel": "dist/index.js"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"commander": "^12.1.0",
|
|
18
18
|
"dotenv": "^16.4.5",
|
|
19
|
-
"@develemail/sdk": "0.
|
|
19
|
+
"@develemail/sdk": "0.4.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsup": "^8.3.5",
|