greybull 0.2.2 → 0.2.4

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.
Files changed (2) hide show
  1. package/dist/index.js +49 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ var ApiError = class extends Error {
94
94
  this.status = status;
95
95
  }
96
96
  };
97
- var VERSION = "0.2.2";
97
+ var VERSION = "0.2.4";
98
98
  function buildRequest(opts) {
99
99
  const url = new URL(opts.apiUrl.replace(/\/+$/, "") + opts.path);
100
100
  if (opts.query) {
@@ -148,6 +148,17 @@ async function apiRequest(opts) {
148
148
  }
149
149
 
150
150
  // src/output.ts
151
+ var COLOR = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
152
+ function paint(code, s) {
153
+ return COLOR ? `\x1B[${code}m${s}\x1B[0m` : s;
154
+ }
155
+ var color = {
156
+ red: (s) => paint("31", s),
157
+ green: (s) => paint("32", s),
158
+ yellow: (s) => paint("33", s),
159
+ bold: (s) => paint("1", s),
160
+ dim: (s) => paint("2", s)
161
+ };
151
162
  function formatTable(headers, rows, opts = {}) {
152
163
  if (rows.length === 0) return "(none)";
153
164
  const max = opts.max ?? [];
@@ -175,7 +186,7 @@ Run 'greybull login' to authenticate (or set GREYBULL_API_KEY).`;
175
186
  Your API key is missing a required scope \u2014 create one with DNS/domain permissions at ${opts.apiUrl}/api-settings.`;
176
187
  }
177
188
  }
178
- process.stderr.write(`Error: ${message}
189
+ process.stderr.write(`${color.red(color.bold("Error:"))} ${color.red(message)}
179
190
  `);
180
191
  process.exit(1);
181
192
  }
@@ -478,7 +489,7 @@ function renderZoneFile(zone, records) {
478
489
  "; -*- mode: dns -*-",
479
490
  `; Zone: ${zone}`,
480
491
  "; Edit records below, then save and close your editor to apply.",
481
- "; Format: NAME TTL TYPE CONTENT (use @ for the zone apex)",
492
+ "; Format: NAME [TTL] TYPE CONTENT (@ = apex; TTL optional, default 3600)",
482
493
  "; TXT values keep their quotes; MX/SRV keep priority in CONTENT.",
483
494
  "; SOA and the apex NS records are managed for you and not shown.",
484
495
  ";"
@@ -494,12 +505,17 @@ function parseZoneFile(text) {
494
505
  for (let i = 0; i < lines.length; i++) {
495
506
  const trimmed = lines[i].trim();
496
507
  if (!trimmed || trimmed.startsWith(";")) continue;
497
- const m = trimmed.match(/^(\S+)\s+(\d+)\s+(?:IN\s+)?([A-Za-z][A-Za-z0-9]*)\s+(.+)$/i);
508
+ const m = trimmed.match(/^(\S+)\s+(?:(\d+)\s+)?(?:IN\s+)?([A-Za-z][A-Za-z0-9]*)\s+(.+)$/i);
498
509
  if (!m) {
499
- throw new Error(`line ${i + 1}: could not parse "${trimmed}" (expected: NAME TTL TYPE CONTENT)`);
510
+ throw new Error(`line ${i + 1}: could not parse "${trimmed}" (expected: NAME [TTL] TYPE CONTENT)`);
500
511
  }
501
512
  const [, name, ttlStr, typeRaw, content] = m;
502
- recs.push({ name, type: typeRaw.toUpperCase(), content: content.trim(), ttl: parseInt(ttlStr) });
513
+ recs.push({
514
+ name,
515
+ type: typeRaw.toUpperCase(),
516
+ content: content.trim(),
517
+ ttl: ttlStr ? parseInt(ttlStr) : 3600
518
+ });
503
519
  }
504
520
  return recs;
505
521
  }
@@ -527,11 +543,21 @@ async function prompt(question, valid, def, nonTtyDefault = def) {
527
543
  rl.close();
528
544
  }
529
545
  }
530
- function editorArgv(cmd, args, file) {
546
+ function isVimFamily(cmd) {
531
547
  const base = path2.basename(cmd).replace(/\.exe$/i, "").toLowerCase();
532
- if (["vim", "nvim", "gvim", "mvim"].includes(base)) {
533
- return [...args, "-c", "syntax enable", "-c", "set ft=bindzone", file];
548
+ if (["vim", "nvim", "gvim", "mvim", "vimdiff", "view"].includes(base)) return true;
549
+ if (base === "vi") {
550
+ try {
551
+ const r = spawnSync(cmd, ["--version"], { encoding: "utf8", timeout: 2e3 });
552
+ return /\bn?vim\b/i.test(`${r.stdout ?? ""}${r.stderr ?? ""}`);
553
+ } catch {
554
+ return false;
555
+ }
534
556
  }
557
+ return false;
558
+ }
559
+ function editorArgv(args, file, forceVim) {
560
+ if (forceVim) return [...args, "-c", "syntax enable", "-c", "set ft=bindzone", file];
535
561
  return [...args, file];
536
562
  }
537
563
  function openInEditor(zone, content) {
@@ -540,7 +566,7 @@ function openInEditor(zone, content) {
540
566
  const editor = process.env.VISUAL || process.env.EDITOR || (process.platform === "win32" ? "notepad" : "vi");
541
567
  const [cmd, ...args] = editor.split(" ");
542
568
  try {
543
- const r = spawnSync(cmd, editorArgv(cmd, args, file), { stdio: "inherit" });
569
+ const r = spawnSync(cmd, editorArgv(args, file, isVimFamily(cmd)), { stdio: "inherit" });
544
570
  if (r.error) return null;
545
571
  return fs2.readFileSync(file, "utf8");
546
572
  } finally {
@@ -567,9 +593,9 @@ async function editCmd(ctx2, zone) {
567
593
  try {
568
594
  records = parseZoneFile(edited);
569
595
  } catch (err) {
570
- process.stdout.write(`
571
- Error parsing the zone: ${err.message}
572
- `);
596
+ process.stdout.write(
597
+ "\n" + color.red(color.bold("Error parsing the zone: ") + err.message) + "\n"
598
+ );
573
599
  const c2 = await prompt(
574
600
  "Options are: (e)dit your changes, (r)etry with original zone, (a)bort? [e] ",
575
601
  ["e", "r", "a"],
@@ -584,11 +610,9 @@ Error parsing the zone: ${err.message}
584
610
  if (added.length === 0 && removed.length === 0) {
585
611
  return void process.stdout.write("No changes.\n");
586
612
  }
587
- process.stdout.write("\nChanges to apply:\n");
588
- removed.forEach((l) => process.stdout.write(` - ${l}
589
- `));
590
- added.forEach((l) => process.stdout.write(` + ${l}
591
- `));
613
+ process.stdout.write("\n" + color.bold("Changes to apply:") + "\n");
614
+ removed.forEach((l) => process.stdout.write(color.red(` - ${l}`) + "\n"));
615
+ added.forEach((l) => process.stdout.write(color.green(` + ${l}`) + "\n"));
592
616
  process.stdout.write("\n");
593
617
  const c = await prompt(
594
618
  "(a)pply these changes, (e)dit again, (r)etry with original zone, (q)uit? [a] ",
@@ -612,14 +636,15 @@ Error parsing the zone: ${err.message}
612
636
  path: `/api/v1/dns/zones/${encodeURIComponent(zone)}`,
613
637
  body: { records }
614
638
  });
615
- process.stdout.write(`Applied. ${zone} now has ${put.data.applied} record(s).
616
- `);
639
+ process.stdout.write(
640
+ color.green(`Applied. ${zone} now has ${put.data.applied} record(s).`) + "\n"
641
+ );
617
642
  return;
618
643
  } catch (err) {
619
644
  if (err instanceof ApiError && (err.status === 422 || err.status === 400)) {
620
- process.stdout.write(`
621
- The DNS server rejected the zone: ${err.message}
622
- `);
645
+ process.stdout.write(
646
+ "\n" + color.red(color.bold("The DNS server rejected the zone: ") + err.message) + "\n"
647
+ );
623
648
  const c2 = await prompt(
624
649
  "Options are: (e)dit your changes, (r)etry with original zone, (a)bort? [e] ",
625
650
  ["e", "r", "a"],
@@ -674,7 +699,7 @@ async function domainsCheckCmd(ctx2, query, opts) {
674
699
 
675
700
  // src/index.ts
676
701
  var program = new Command();
677
- program.name("greybull").description("Manage Greybull DNS records and domains from the command line").version("0.2.2").option("--json", "output raw JSON", false).option("--api-url <url>", "portal base URL (or set GREYBULL_API_URL)");
702
+ program.name("greybull").description("Manage Greybull DNS records and domains from the command line").version("0.2.4").option("--json", "output raw JSON", false).option("--api-url <url>", "portal base URL (or set GREYBULL_API_URL)");
678
703
  function ctx(requireAuth = true) {
679
704
  const opts = program.opts();
680
705
  const apiUrl = resolveApiUrl(opts.apiUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greybull",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Command-line tool for the Greybull portal — manage DNS records and domains",
5
5
  "type": "module",
6
6
  "bin": {