@kud/gandi-cli 0.1.0

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 (50) hide show
  1. package/README.md +59 -0
  2. package/dist/commands/dns-delete.d.ts +9 -0
  3. package/dist/commands/dns-delete.d.ts.map +1 -0
  4. package/dist/commands/dns-delete.js +39 -0
  5. package/dist/commands/dns-delete.js.map +1 -0
  6. package/dist/commands/dns-list.d.ts +7 -0
  7. package/dist/commands/dns-list.d.ts.map +1 -0
  8. package/dist/commands/dns-list.js +46 -0
  9. package/dist/commands/dns-list.js.map +1 -0
  10. package/dist/commands/dns-set.d.ts +11 -0
  11. package/dist/commands/dns-set.d.ts.map +1 -0
  12. package/dist/commands/dns-set.js +42 -0
  13. package/dist/commands/dns-set.js.map +1 -0
  14. package/dist/commands/domain-list.d.ts +4 -0
  15. package/dist/commands/domain-list.d.ts.map +1 -0
  16. package/dist/commands/domain-list.js +48 -0
  17. package/dist/commands/domain-list.js.map +1 -0
  18. package/dist/commands/domain-renew.d.ts +8 -0
  19. package/dist/commands/domain-renew.d.ts.map +1 -0
  20. package/dist/commands/domain-renew.js +51 -0
  21. package/dist/commands/domain-renew.js.map +1 -0
  22. package/dist/components/error.d.ts +7 -0
  23. package/dist/components/error.d.ts.map +1 -0
  24. package/dist/components/error.js +7 -0
  25. package/dist/components/error.js.map +1 -0
  26. package/dist/components/spinner-action.d.ts +7 -0
  27. package/dist/components/spinner-action.d.ts.map +1 -0
  28. package/dist/components/spinner-action.js +17 -0
  29. package/dist/components/spinner-action.js.map +1 -0
  30. package/dist/components/table.d.ts +9 -0
  31. package/dist/components/table.d.ts.map +1 -0
  32. package/dist/components/table.js +18 -0
  33. package/dist/components/table.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +46 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/lib/api.d.ts +6 -0
  39. package/dist/lib/api.d.ts.map +1 -0
  40. package/dist/lib/api.js +30 -0
  41. package/dist/lib/api.js.map +1 -0
  42. package/dist/lib/config.d.ts +2 -0
  43. package/dist/lib/config.d.ts.map +1 -0
  44. package/dist/lib/config.js +21 -0
  45. package/dist/lib/config.js.map +1 -0
  46. package/dist/types/gandi.d.ts +31 -0
  47. package/dist/types/gandi.d.ts.map +1 -0
  48. package/dist/types/gandi.js +2 -0
  49. package/dist/types/gandi.js.map +1 -0
  50. package/package.json +32 -0
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @kud/gandi-cli
2
+
3
+ A modern CLI for the [Gandi v5 REST API](https://api.gandi.net/docs/).
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install -g @kud/gandi-cli
9
+ ```
10
+
11
+ ## Authentication
12
+
13
+ Generate a personal access token at **gandi.net → Account → Partage → Créer un jeton d'accès personnel**.
14
+
15
+ Token name: `gandi-cli`
16
+ Expiry: your preference
17
+ Resource access: **Restreint aux produits sélectionnés**
18
+
19
+ Required permissions (Domaines section only):
20
+
21
+ | Permission | Commands |
22
+ | ----------------------------------------------- | ----------------------------------- |
23
+ | Voir la liste de vos domaines | `gandi domain list` |
24
+ | Gérer le renouvellement de vos domaines | `gandi domain renew` |
25
+ | Accéder aux enregistrements DNS de vos domaines | `gandi dns list` |
26
+ | Gérer les enregistrements DNS de vos domaines | `gandi dns set`, `gandi dns delete` |
27
+
28
+ No Organisation, Facturation, Web Hosting, Cloud, or SSL permissions needed.
29
+
30
+ Then export the key in your shell:
31
+
32
+ ```sh
33
+ export GANDI_API_KEY="your-token-here"
34
+ ```
35
+
36
+ Or add it to `~/.config/gandi/config.toml`:
37
+
38
+ ```toml
39
+ api_key = "your-token-here"
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Domains
45
+
46
+ ```sh
47
+ gandi domain list
48
+ gandi domain renew example.com
49
+ gandi domain renew example.com --duration 2
50
+ ```
51
+
52
+ ### DNS
53
+
54
+ ```sh
55
+ gandi dns list example.com
56
+ gandi dns set example.com A www 1.2.3.4
57
+ gandi dns set example.com A www 1.2.3.4 --ttl 3600
58
+ gandi dns delete example.com A www
59
+ ```
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ interface DnsDeleteProps {
3
+ domain: string;
4
+ type: string;
5
+ name: string;
6
+ }
7
+ declare const DnsDelete: ({ domain, type, name }: DnsDeleteProps) => React.JSX.Element;
8
+ export default DnsDelete;
9
+ //# sourceMappingURL=dns-delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-delete.d.ts","sourceRoot":"","sources":["../../src/commands/dns-delete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAOlD,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,QAAA,MAAM,SAAS,GAAI,wBAAwB,cAAc,sBA6BxD,CAAA;AAED,eAAe,SAAS,CAAA"}
@@ -0,0 +1,39 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { deleteDnsRecord } from "../lib/api.js";
4
+ import { getApiKey } from "../lib/config.js";
5
+ import SpinnerAction from "../components/spinner-action.js";
6
+ import ErrorMessage from "../components/error.js";
7
+ const DnsDelete = ({ domain, type, name }) => {
8
+ const [done, setDone] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ useEffect(() => {
11
+ const run = async () => {
12
+ try {
13
+ const key = getApiKey();
14
+ await deleteDnsRecord(key, domain, type, name);
15
+ setDone(true);
16
+ }
17
+ catch (e) {
18
+ setError(e.message);
19
+ }
20
+ };
21
+ run();
22
+ }, []);
23
+ if (error)
24
+ return React.createElement(ErrorMessage, { message: error });
25
+ if (!done)
26
+ return React.createElement(SpinnerAction, { label: `Deleting ${type} record ${name}…` });
27
+ return (React.createElement(Box, null,
28
+ React.createElement(Text, { color: "green" }, "\u2714 "),
29
+ React.createElement(Text, null,
30
+ "Deleted ",
31
+ React.createElement(Text, { bold: true }, name),
32
+ " ",
33
+ React.createElement(Text, { color: "cyan" }, type),
34
+ " from",
35
+ " ",
36
+ domain)));
37
+ };
38
+ export default DnsDelete;
39
+ //# sourceMappingURL=dns-delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-delete.js","sourceRoot":"","sources":["../../src/commands/dns-delete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,aAAa,MAAM,iCAAiC,CAAA;AAC3D,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAQjD,MAAM,SAAS,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAkB,EAAE,EAAE;IAC3D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;gBACvB,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;gBAC9C,OAAO,CAAC,IAAI,CAAC,CAAA;YACf,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAE,CAAW,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAA;QACD,GAAG,EAAE,CAAA;IACP,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,KAAK;QAAE,OAAO,oBAAC,YAAY,IAAC,OAAO,EAAE,KAAK,GAAI,CAAA;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,oBAAC,aAAa,IAAC,KAAK,EAAE,YAAY,IAAI,WAAW,IAAI,GAAG,GAAI,CAAA;IAE9E,OAAO,CACL,oBAAC,GAAG;QACF,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,cAAU;QAC7B,oBAAC,IAAI;;YACK,oBAAC,IAAI,IAAC,IAAI,UAAE,IAAI,CAAQ;;YAAC,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,IAAE,IAAI,CAAQ;;YAAM,GAAG;YACzE,MAAM,CACF,CACH,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,SAAS,CAAA"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ interface DnsListProps {
3
+ domain: string;
4
+ }
5
+ declare const DnsList: ({ domain }: DnsListProps) => React.JSX.Element;
6
+ export default DnsList;
7
+ //# sourceMappingURL=dns-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-list.d.ts","sourceRoot":"","sources":["../../src/commands/dns-list.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AASlD,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,QAAA,MAAM,OAAO,GAAI,YAAY,YAAY,sBA4CxC,CAAA;AAED,eAAe,OAAO,CAAA"}
@@ -0,0 +1,46 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { listDnsRecords } from "../lib/api.js";
4
+ import { getApiKey } from "../lib/config.js";
5
+ import Table from "../components/table.js";
6
+ import SpinnerAction from "../components/spinner-action.js";
7
+ import ErrorMessage from "../components/error.js";
8
+ const DnsList = ({ domain }) => {
9
+ const [records, setRecords] = useState(null);
10
+ const [error, setError] = useState(null);
11
+ useEffect(() => {
12
+ const run = async () => {
13
+ try {
14
+ const key = getApiKey();
15
+ const data = await listDnsRecords(key, domain);
16
+ setRecords(data);
17
+ }
18
+ catch (e) {
19
+ setError(e.message);
20
+ }
21
+ };
22
+ run();
23
+ }, []);
24
+ if (error)
25
+ return React.createElement(ErrorMessage, { message: error });
26
+ if (!records)
27
+ return React.createElement(SpinnerAction, { label: `Fetching DNS records for ${domain}…` });
28
+ if (records.length === 0) {
29
+ return React.createElement(Text, { color: "gray" }, "No DNS records found.");
30
+ }
31
+ const rows = records.flatMap((r) => r.rrset_values.map((v) => ({
32
+ NAME: r.rrset_name,
33
+ TYPE: r.rrset_type,
34
+ TTL: String(r.rrset_ttl),
35
+ VALUE: v,
36
+ })));
37
+ return (React.createElement(Box, { flexDirection: "column" },
38
+ React.createElement(Table, { headers: ["NAME", "TYPE", "TTL", "VALUE"], rows: rows }),
39
+ React.createElement(Box, { marginTop: 1 },
40
+ React.createElement(Text, { color: "gray" },
41
+ records.length,
42
+ " record",
43
+ records.length !== 1 ? "s" : ""))));
44
+ };
45
+ export default DnsList;
46
+ //# sourceMappingURL=dns-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-list.js","sourceRoot":"","sources":["../../src/commands/dns-list.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAE5C,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,aAAa,MAAM,iCAAiC,CAAA;AAC3D,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAMjD,MAAM,OAAO,GAAG,CAAC,EAAE,MAAM,EAAgB,EAAE,EAAE;IAC3C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAA;IAChE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;gBACvB,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;gBAC9C,UAAU,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAE,CAAW,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAA;QACD,GAAG,EAAE,CAAA;IACP,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,KAAK;QAAE,OAAO,oBAAC,YAAY,IAAC,OAAO,EAAE,KAAK,GAAI,CAAA;IAClD,IAAI,CAAC,OAAO;QACV,OAAO,oBAAC,aAAa,IAAC,KAAK,EAAE,4BAA4B,MAAM,GAAG,GAAI,CAAA;IAExE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,4BAA6B,CAAA;IACxD,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACjC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,UAAU;QAClB,IAAI,EAAE,CAAC,CAAC,UAAU;QAClB,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACxB,KAAK,EAAE,CAAC;KACT,CAAC,CAAC,CACJ,CAAA;IAED,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,KAAK,IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,GAAI;QAChE,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;gBACf,OAAO,CAAC,MAAM;;gBAAS,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAClD,CACH,CACF,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,OAAO,CAAA"}
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ interface DnsSetProps {
3
+ domain: string;
4
+ type: string;
5
+ name: string;
6
+ value: string;
7
+ ttl?: number;
8
+ }
9
+ declare const DnsSet: ({ domain, type, name, value, ttl }: DnsSetProps) => React.JSX.Element;
10
+ export default DnsSet;
11
+ //# sourceMappingURL=dns-set.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-set.d.ts","sourceRoot":"","sources":["../../src/commands/dns-set.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAOlD,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,QAAA,MAAM,MAAM,GAAI,oCAAoC,WAAW,sBA6B9D,CAAA;AAED,eAAe,MAAM,CAAA"}
@@ -0,0 +1,42 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { setDnsRecord } from "../lib/api.js";
4
+ import { getApiKey } from "../lib/config.js";
5
+ import SpinnerAction from "../components/spinner-action.js";
6
+ import ErrorMessage from "../components/error.js";
7
+ const DnsSet = ({ domain, type, name, value, ttl }) => {
8
+ const [done, setDone] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ useEffect(() => {
11
+ const run = async () => {
12
+ try {
13
+ const key = getApiKey();
14
+ await setDnsRecord(key, domain, type, name, [value], ttl);
15
+ setDone(true);
16
+ }
17
+ catch (e) {
18
+ setError(e.message);
19
+ }
20
+ };
21
+ run();
22
+ }, []);
23
+ if (error)
24
+ return React.createElement(ErrorMessage, { message: error });
25
+ if (!done)
26
+ return React.createElement(SpinnerAction, { label: `Setting ${type} record…` });
27
+ return (React.createElement(Box, null,
28
+ React.createElement(Text, { color: "green" }, "\u2714 "),
29
+ React.createElement(Text, null,
30
+ "Set ",
31
+ React.createElement(Text, { bold: true }, name),
32
+ " ",
33
+ React.createElement(Text, { color: "cyan" }, type),
34
+ " \u2192 ",
35
+ value,
36
+ ttl !== undefined ? React.createElement(Text, { color: "gray" },
37
+ " (TTL ",
38
+ ttl,
39
+ ")") : null)));
40
+ };
41
+ export default DnsSet;
42
+ //# sourceMappingURL=dns-set.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-set.js","sourceRoot":"","sources":["../../src/commands/dns-set.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,aAAa,MAAM,iCAAiC,CAAA;AAC3D,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAUjD,MAAM,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAe,EAAE,EAAE;IACjE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;gBACvB,MAAM,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAA;gBACzD,OAAO,CAAC,IAAI,CAAC,CAAA;YACf,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAE,CAAW,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAA;QACD,GAAG,EAAE,CAAA;IACP,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,KAAK;QAAE,OAAO,oBAAC,YAAY,IAAC,OAAO,EAAE,KAAK,GAAI,CAAA;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,oBAAC,aAAa,IAAC,KAAK,EAAE,WAAW,IAAI,UAAU,GAAI,CAAA;IAErE,OAAO,CACL,oBAAC,GAAG;QACF,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,cAAU;QAC7B,oBAAC,IAAI;;YACC,oBAAC,IAAI,IAAC,IAAI,UAAE,IAAI,CAAQ;;YAAC,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,IAAE,IAAI,CAAQ;;YAAI,KAAK;YACrE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;;gBAAQ,GAAG;oBAAS,CAAC,CAAC,CAAC,IAAI,CAC7D,CACH,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,MAAM,CAAA"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const DomainList: () => React.JSX.Element;
3
+ export default DomainList;
4
+ //# sourceMappingURL=domain-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-list.d.ts","sourceRoot":"","sources":["../../src/commands/domain-list.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AASlD,QAAA,MAAM,UAAU,yBA8Cf,CAAA;AAED,eAAe,UAAU,CAAA"}
@@ -0,0 +1,48 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { listDomains } from "../lib/api.js";
4
+ import { getApiKey } from "../lib/config.js";
5
+ import Table from "../components/table.js";
6
+ import SpinnerAction from "../components/spinner-action.js";
7
+ import ErrorMessage from "../components/error.js";
8
+ const DomainList = () => {
9
+ const [domains, setDomains] = useState(null);
10
+ const [error, setError] = useState(null);
11
+ useEffect(() => {
12
+ const run = async () => {
13
+ try {
14
+ const key = getApiKey();
15
+ const data = await listDomains(key);
16
+ setDomains(data);
17
+ }
18
+ catch (e) {
19
+ setError(e.message);
20
+ }
21
+ };
22
+ run();
23
+ }, []);
24
+ if (error)
25
+ return React.createElement(ErrorMessage, { message: error });
26
+ if (!domains)
27
+ return React.createElement(SpinnerAction, { label: "Fetching domains\u2026" });
28
+ if (domains.length === 0) {
29
+ return React.createElement(Text, { color: "gray" }, "No domains found.");
30
+ }
31
+ const rows = domains.map((d) => ({
32
+ DOMAIN: d.fqdn_unicode,
33
+ EXPIRES: d.dates.registry_ends_at
34
+ ? new Date(d.dates.registry_ends_at).toISOString().slice(0, 10)
35
+ : "—",
36
+ STATUS: d.status.join(", ") || "active",
37
+ NAMESERVER: d.name_server.current,
38
+ }));
39
+ return (React.createElement(Box, { flexDirection: "column" },
40
+ React.createElement(Table, { headers: ["DOMAIN", "EXPIRES", "STATUS", "NAMESERVER"], rows: rows }),
41
+ React.createElement(Box, { marginTop: 1 },
42
+ React.createElement(Text, { color: "gray" },
43
+ domains.length,
44
+ " domain",
45
+ domains.length !== 1 ? "s" : ""))));
46
+ };
47
+ export default DomainList;
48
+ //# sourceMappingURL=domain-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-list.js","sourceRoot":"","sources":["../../src/commands/domain-list.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAE5C,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,aAAa,MAAM,iCAAiC,CAAA;AAC3D,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAEjD,MAAM,UAAU,GAAG,GAAG,EAAE;IACtB,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAA;IAC7D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;gBACvB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;gBACnC,UAAU,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAE,CAAW,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAA;QACD,GAAG,EAAE,CAAA;IACP,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,KAAK;QAAE,OAAO,oBAAC,YAAY,IAAC,OAAO,EAAE,KAAK,GAAI,CAAA;IAClD,IAAI,CAAC,OAAO;QAAE,OAAO,oBAAC,aAAa,IAAC,KAAK,EAAC,wBAAmB,GAAG,CAAA;IAEhE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,wBAAyB,CAAA;IACpD,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,YAAY;QACtB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB;YAC/B,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/D,CAAC,CAAC,GAAG;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ;QACvC,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO;KAClC,CAAC,CAAC,CAAA;IAEH,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,KAAK,IACJ,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,EACtD,IAAI,EAAE,IAAI,GACV;QACF,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;gBACf,OAAO,CAAC,MAAM;;gBAAS,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAClD,CACH,CACF,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,UAAU,CAAA"}
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ interface DomainRenewProps {
3
+ domain: string;
4
+ duration?: number;
5
+ }
6
+ declare const DomainRenew: ({ domain, duration }: DomainRenewProps) => React.JSX.Element;
7
+ export default DomainRenew;
8
+ //# sourceMappingURL=domain-renew.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-renew.d.ts","sourceRoot":"","sources":["../../src/commands/domain-renew.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAQlD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,QAAA,MAAM,WAAW,GAAI,sBAA0B,gBAAgB,sBA4C9D,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,51 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { getApiKey } from "../lib/config.js";
4
+ import ErrorMessage from "../components/error.js";
5
+ import SpinnerAction from "../components/spinner-action.js";
6
+ const BASE_URL = "https://api.gandi.net/v5";
7
+ const DomainRenew = ({ domain, duration = 1 }) => {
8
+ const [done, setDone] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ useEffect(() => {
11
+ const run = async () => {
12
+ try {
13
+ const key = getApiKey();
14
+ const res = await fetch(`${BASE_URL}/domain/domains/${domain}/renew`, {
15
+ method: "POST",
16
+ headers: {
17
+ Authorization: `Apikey ${key}`,
18
+ "Content-Type": "application/json",
19
+ },
20
+ body: JSON.stringify({ duration }),
21
+ });
22
+ if (!res.ok) {
23
+ const err = await res
24
+ .json()
25
+ .catch(() => ({ message: res.statusText }));
26
+ throw new Error(err.message ?? `HTTP ${res.status}`);
27
+ }
28
+ setDone(true);
29
+ }
30
+ catch (e) {
31
+ setError(e.message);
32
+ }
33
+ };
34
+ run();
35
+ }, []);
36
+ if (error)
37
+ return React.createElement(ErrorMessage, { message: error });
38
+ if (!done)
39
+ return React.createElement(SpinnerAction, { label: `Renewing ${domain}…` });
40
+ return (React.createElement(Box, null,
41
+ React.createElement(Text, { color: "green" }, "\u2714 "),
42
+ React.createElement(Text, null,
43
+ "Renewed ",
44
+ React.createElement(Text, { bold: true }, domain),
45
+ " for ",
46
+ duration,
47
+ " year",
48
+ duration !== 1 ? "s" : "")));
49
+ };
50
+ export default DomainRenew;
51
+ //# sourceMappingURL=domain-renew.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-renew.js","sourceRoot":"","sources":["../../src/commands/domain-renew.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,YAAY,MAAM,wBAAwB,CAAA;AACjD,OAAO,aAAa,MAAM,iCAAiC,CAAA;AAE3D,MAAM,QAAQ,GAAG,0BAA0B,CAAA;AAO3C,MAAM,WAAW,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,EAAoB,EAAE,EAAE;IACjE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;gBACvB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,mBAAmB,MAAM,QAAQ,EAAE;oBACpE,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,aAAa,EAAE,UAAU,GAAG,EAAE;wBAC9B,cAAc,EAAE,kBAAkB;qBACnC;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;iBACnC,CAAC,CAAA;gBACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,GAAG,GAAG,MAAM,GAAG;yBAClB,IAAI,EAAE;yBACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAA;oBAC7C,MAAM,IAAI,KAAK,CACZ,GAA2B,CAAC,OAAO,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAC7D,CAAA;gBACH,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,CAAA;YACf,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAE,CAAW,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC,CAAA;QACD,GAAG,EAAE,CAAA;IACP,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,KAAK;QAAE,OAAO,oBAAC,YAAY,IAAC,OAAO,EAAE,KAAK,GAAI,CAAA;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,oBAAC,aAAa,IAAC,KAAK,EAAE,YAAY,MAAM,GAAG,GAAI,CAAA;IAEjE,OAAO,CACL,oBAAC,GAAG;QACF,oBAAC,IAAI,IAAC,KAAK,EAAC,OAAO,cAAU;QAC7B,oBAAC,IAAI;;YACK,oBAAC,IAAI,IAAC,IAAI,UAAE,MAAM,CAAQ;;YAAM,QAAQ;;YAC/C,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CACrB,CACH,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ interface ErrorProps {
3
+ message: string;
4
+ }
5
+ declare const ErrorMessage: ({ message }: ErrorProps) => React.JSX.Element;
6
+ export default ErrorMessage;
7
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/components/error.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,QAAA,MAAM,YAAY,GAAI,aAAa,UAAU,sBAK5C,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ const ErrorMessage = ({ message }) => (React.createElement(Box, null,
4
+ React.createElement(Text, { color: "red" }, "\u2716 "),
5
+ React.createElement(Text, null, message)));
6
+ export default ErrorMessage;
7
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/components/error.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAM/B,MAAM,YAAY,GAAG,CAAC,EAAE,OAAO,EAAc,EAAE,EAAE,CAAC,CAChD,oBAAC,GAAG;IACF,oBAAC,IAAI,IAAC,KAAK,EAAC,KAAK,cAAU;IAC3B,oBAAC,IAAI,QAAE,OAAO,CAAQ,CAClB,CACP,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ interface SpinnerActionProps {
3
+ label: string;
4
+ }
5
+ declare const SpinnerAction: ({ label }: SpinnerActionProps) => React.JSX.Element;
6
+ export default SpinnerAction;
7
+ //# sourceMappingURL=spinner-action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner-action.d.ts","sourceRoot":"","sources":["../../src/components/spinner-action.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAKlD,UAAU,kBAAkB;IAC1B,KAAK,EAAE,MAAM,CAAA;CACd;AAED,QAAA,MAAM,aAAa,GAAI,WAAW,kBAAkB,sBAcnD,CAAA;AAED,eAAe,aAAa,CAAA"}
@@ -0,0 +1,17 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ const SpinnerAction = ({ label }) => {
5
+ const [frame, setFrame] = useState(0);
6
+ useEffect(() => {
7
+ const id = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), 80);
8
+ return () => clearInterval(id);
9
+ }, []);
10
+ return (React.createElement(Box, null,
11
+ React.createElement(Text, { color: "cyan" },
12
+ FRAMES[frame],
13
+ " "),
14
+ React.createElement(Text, { color: "gray" }, label)));
15
+ };
16
+ export default SpinnerAction;
17
+ //# sourceMappingURL=spinner-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner-action.js","sourceRoot":"","sources":["../../src/components/spinner-action.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAE/B,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;AAMjE,MAAM,aAAa,GAAG,CAAC,EAAE,KAAK,EAAsB,EAAE,EAAE;IACtD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAErC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;QAC1E,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,CACL,oBAAC,GAAG;QACF,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM;YAAE,MAAM,CAAC,KAAK,CAAC;gBAAS;QAC1C,oBAAC,IAAI,IAAC,KAAK,EAAC,MAAM,IAAE,KAAK,CAAQ,CAC7B,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,aAAa,CAAA"}
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ type Row = Record<string, string>;
3
+ interface TableProps {
4
+ headers: string[];
5
+ rows: Row[];
6
+ }
7
+ declare const Table: ({ headers, rows }: TableProps) => React.JSX.Element;
8
+ export default Table;
9
+ //# sourceMappingURL=table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEjC,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,GAAG,EAAE,CAAA;CACZ;AAID,QAAA,MAAM,KAAK,GAAI,mBAAmB,UAAU,sBAmC3C,CAAA;AAED,eAAe,KAAK,CAAA"}
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ const pad = (str, len) => str.padEnd(len);
4
+ const Table = ({ headers, rows }) => {
5
+ const widths = headers.map((h) => Math.max(h.length, ...rows.map((r) => (r[h] ?? "").length)));
6
+ return (React.createElement(Box, { flexDirection: "column" },
7
+ React.createElement(Box, null, headers.map((h, i) => (React.createElement(Text, { key: h, color: "cyan", bold: true },
8
+ pad(h, widths[i]),
9
+ " ")))),
10
+ React.createElement(Box, null, headers.map((h, i) => (React.createElement(Text, { key: h, color: "gray" },
11
+ pad("─".repeat(widths[i]), widths[i]),
12
+ " ")))),
13
+ rows.map((row, ri) => (React.createElement(Box, { key: ri }, headers.map((h, i) => (React.createElement(Text, { key: h },
14
+ pad(row[h] ?? "", widths[i]),
15
+ " "))))))));
16
+ };
17
+ export default Table;
18
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAS/B,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AAEzD,MAAM,KAAK,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAc,EAAE,EAAE;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAC5D,CAAA;IAED,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,GAAG,QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACrB,oBAAC,IAAI,IAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAC,MAAM,EAAC,IAAI;YAC5B,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YACjB,IAAI,CACA,CACR,CAAC,CACE;QACN,oBAAC,GAAG,QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACrB,oBAAC,IAAI,IAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAC,MAAM;YACvB,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,CACA,CACR,CAAC,CACE;QACL,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CACrB,oBAAC,GAAG,IAAC,GAAG,EAAE,EAAE,IACT,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACrB,oBAAC,IAAI,IAAC,GAAG,EAAE,CAAC;YACT,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,CACA,CACR,CAAC,CACE,CACP,CAAC,CACE,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,KAAK,CAAA"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import React from "react";
3
+ import { render } from "ink";
4
+ import { Command } from "commander";
5
+ import DomainList from "./commands/domain-list.js";
6
+ import DomainRenew from "./commands/domain-renew.js";
7
+ import DnsList from "./commands/dns-list.js";
8
+ import DnsSet from "./commands/dns-set.js";
9
+ import DnsDelete from "./commands/dns-delete.js";
10
+ const program = new Command();
11
+ program
12
+ .name("gandi")
13
+ .description("Modern CLI for the Gandi v5 REST API")
14
+ .version("0.1.0");
15
+ const domain = program.command("domain").description("Manage domains");
16
+ domain
17
+ .command("list")
18
+ .description("List all domains")
19
+ .action(() => void render(React.createElement(DomainList, null)));
20
+ domain
21
+ .command("renew <domain>")
22
+ .description("Renew a domain")
23
+ .option("-d, --duration <years>", "Number of years to renew", "1")
24
+ .action((d, opts) => {
25
+ void render(React.createElement(DomainRenew, { domain: d, duration: parseInt(opts.duration, 10) }));
26
+ });
27
+ const dns = program.command("dns").description("Manage LiveDNS records");
28
+ dns
29
+ .command("list <domain>")
30
+ .description("List DNS records for a domain")
31
+ .action((d) => void render(React.createElement(DnsList, { domain: d })));
32
+ dns
33
+ .command("set <domain> <type> <name> <value>")
34
+ .description("Create or replace a DNS record")
35
+ .option("-t, --ttl <seconds>", "TTL in seconds", "10800")
36
+ .action((d, type, name, value, opts) => {
37
+ void render(React.createElement(DnsSet, { domain: d, type: type, name: name, value: value, ttl: parseInt(opts.ttl, 10) }));
38
+ });
39
+ dns
40
+ .command("delete <domain> <type> <name>")
41
+ .description("Delete a DNS record")
42
+ .action((d, type, name) => {
43
+ void render(React.createElement(DnsDelete, { domain: d, type: type, name: name }));
44
+ });
45
+ program.parse();
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,UAAU,MAAM,2BAA2B,CAAA;AAClD,OAAO,WAAW,MAAM,4BAA4B,CAAA;AACpD,OAAO,OAAO,MAAM,wBAAwB,CAAA;AAC5C,OAAO,MAAM,MAAM,uBAAuB,CAAA;AAC1C,OAAO,SAAS,MAAM,0BAA0B,CAAA;AAEhD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,sCAAsC,CAAC;KACnD,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAA;AAEtE,MAAM;KACH,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,kBAAkB,CAAC;KAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,oBAAC,UAAU,OAAG,CAAC,CAAC,CAAA;AAE5C,MAAM;KACH,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,gBAAgB,CAAC;KAC7B,MAAM,CAAC,wBAAwB,EAAE,0BAA0B,EAAE,GAAG,CAAC;KACjE,MAAM,CAAC,CAAC,CAAS,EAAE,IAA0B,EAAE,EAAE;IAChD,KAAK,MAAM,CACT,oBAAC,WAAW,IAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAI,CAClE,CAAA;AACH,CAAC,CAAC,CAAA;AAEJ,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAA;AAExE,GAAG;KACA,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,oBAAC,OAAO,IAAC,MAAM,EAAE,CAAC,GAAI,CAAC,CAAC,CAAA;AAE7D,GAAG;KACA,OAAO,CAAC,oCAAoC,CAAC;KAC7C,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,qBAAqB,EAAE,gBAAgB,EAAE,OAAO,CAAC;KACxD,MAAM,CACL,CACE,CAAS,EACT,IAAY,EACZ,IAAY,EACZ,KAAa,EACb,IAAqB,EACrB,EAAE;IACF,KAAK,MAAM,CACT,oBAAC,MAAM,IACL,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,GAC3B,CACH,CAAA;AACH,CAAC,CACF,CAAA;AAEH,GAAG;KACA,OAAO,CAAC,+BAA+B,CAAC;KACxC,WAAW,CAAC,qBAAqB,CAAC;KAClC,MAAM,CAAC,CAAC,CAAS,EAAE,IAAY,EAAE,IAAY,EAAE,EAAE;IAChD,KAAK,MAAM,CAAC,oBAAC,SAAS,IAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAI,CAAC,CAAA;AAC/D,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,KAAK,EAAE,CAAA"}
@@ -0,0 +1,6 @@
1
+ import type { Domain, DnsRecord } from "../types/gandi.js";
2
+ export declare const listDomains: (apiKey: string) => Promise<Domain[]>;
3
+ export declare const listDnsRecords: (apiKey: string, domain: string) => Promise<DnsRecord[]>;
4
+ export declare const setDnsRecord: (apiKey: string, domain: string, type: string, name: string, values: string[], ttl?: number) => Promise<void>;
5
+ export declare const deleteDnsRecord: (apiKey: string, domain: string, type: string, name: string) => Promise<void>;
6
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAc,MAAM,mBAAmB,CAAA;AA6BtE,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CACf,CAAA;AAE9C,eAAO,MAAM,cAAc,GACzB,QAAQ,MAAM,EACd,QAAQ,MAAM,KACb,OAAO,CAAC,SAAS,EAAE,CAC8C,CAAA;AAEpE,eAAO,MAAM,YAAY,GACvB,QAAQ,MAAM,EACd,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,QAAQ,MAAM,EAAE,EAChB,YAAW,KACV,OAAO,CAAC,IAAI,CAIX,CAAA;AAEJ,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,MAAM,MAAM,KACX,OAAO,CAAC,IAAI,CAGX,CAAA"}
@@ -0,0 +1,30 @@
1
+ const BASE_URL = "https://api.gandi.net/v5";
2
+ const request = async (apiKey, path, options = {}) => {
3
+ const res = await fetch(`${BASE_URL}${path}`, {
4
+ ...options,
5
+ headers: {
6
+ Authorization: `Apikey ${apiKey}`,
7
+ "Content-Type": "application/json",
8
+ ...options.headers,
9
+ },
10
+ });
11
+ if (!res.ok) {
12
+ const err = (await res
13
+ .json()
14
+ .catch(() => ({ message: res.statusText })));
15
+ throw new Error(err.message ?? `HTTP ${res.status}`);
16
+ }
17
+ if (res.status === 204)
18
+ return undefined;
19
+ return res.json();
20
+ };
21
+ export const listDomains = (apiKey) => request(apiKey, "/domain/domains");
22
+ export const listDnsRecords = (apiKey, domain) => request(apiKey, `/livedns/domains/${domain}/records`);
23
+ export const setDnsRecord = (apiKey, domain, type, name, values, ttl = 10800) => request(apiKey, `/livedns/domains/${domain}/records/${name}/${type}`, {
24
+ method: "PUT",
25
+ body: JSON.stringify({ rrset_ttl: ttl, rrset_values: values }),
26
+ });
27
+ export const deleteDnsRecord = (apiKey, domain, type, name) => request(apiKey, `/livedns/domains/${domain}/records/${name}/${type}`, {
28
+ method: "DELETE",
29
+ });
30
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,0BAA0B,CAAA;AAE3C,MAAM,OAAO,GAAG,KAAK,EACnB,MAAc,EACd,IAAY,EACZ,UAAuB,EAAE,EACb,EAAE;IACd,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;QAC5C,GAAG,OAAO;QACV,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,MAAM,EAAE;YACjC,cAAc,EAAE,kBAAkB;YAClC,GAAG,OAAO,CAAC,OAAO;SACnB;KACF,CAAC,CAAA;IAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG;aACnB,IAAI,EAAE;aACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAe,CAAA;QAC5D,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,SAAc,CAAA;IAC7C,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAA;AACjC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAqB,EAAE,CAC/D,OAAO,CAAW,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAE9C,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAAc,EACd,MAAc,EACQ,EAAE,CACxB,OAAO,CAAc,MAAM,EAAE,oBAAoB,MAAM,UAAU,CAAC,CAAA;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY,EACZ,MAAgB,EAChB,GAAG,GAAG,KAAK,EACI,EAAE,CACjB,OAAO,CAAO,MAAM,EAAE,oBAAoB,MAAM,YAAY,IAAI,IAAI,IAAI,EAAE,EAAE;IAC1E,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;CAC/D,CAAC,CAAA;AAEJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY,EACG,EAAE,CACjB,OAAO,CAAO,MAAM,EAAE,oBAAoB,MAAM,YAAY,IAAI,IAAI,IAAI,EAAE,EAAE;IAC1E,MAAM,EAAE,QAAQ;CACjB,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const getApiKey: () => string;
2
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,SAAS,QAAO,MAAyB,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { parse } from "smol-toml";
5
+ const resolveApiKey = () => {
6
+ if (process.env.GANDI_API_KEY)
7
+ return process.env.GANDI_API_KEY;
8
+ const configPath = join(homedir(), ".config", "gandi", "config.toml");
9
+ try {
10
+ const raw = readFileSync(configPath, "utf8");
11
+ const config = parse(raw);
12
+ if (config.api_key)
13
+ return config.api_key;
14
+ }
15
+ catch {
16
+ // file absent or unreadable — fall through to error
17
+ }
18
+ throw new Error("No API key found. Set GANDI_API_KEY or add api_key to ~/.config/gandi/config.toml");
19
+ };
20
+ export const getApiKey = () => resolveApiKey();
21
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAMjC,MAAM,aAAa,GAAG,GAAW,EAAE;IACjC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IAE/D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;IAErE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAsB,CAAA;QAC9C,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC,OAAO,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IAED,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,aAAa,EAAE,CAAA"}
@@ -0,0 +1,31 @@
1
+ export interface Domain {
2
+ fqdn: string;
3
+ fqdn_unicode: string;
4
+ href: string;
5
+ id: string;
6
+ name_server: {
7
+ current: string;
8
+ };
9
+ dates: {
10
+ created_at: string;
11
+ registry_created_at: string;
12
+ registry_ends_at: string;
13
+ updated_at: string;
14
+ };
15
+ status: string[];
16
+ tld: string;
17
+ }
18
+ export interface DnsRecord {
19
+ rrset_name: string;
20
+ rrset_type: string;
21
+ rrset_ttl: number;
22
+ rrset_href: string;
23
+ rrset_values: string[];
24
+ }
25
+ export interface GandiError {
26
+ code: number;
27
+ message: string;
28
+ object: string;
29
+ cause: string;
30
+ }
31
+ //# sourceMappingURL=gandi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gandi.d.ts","sourceRoot":"","sources":["../../src/types/gandi.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAA;QAClB,mBAAmB,EAAE,MAAM,CAAA;QAC3B,gBAAgB,EAAE,MAAM,CAAA;QACxB,UAAU,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gandi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gandi.js","sourceRoot":"","sources":["../../src/types/gandi.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@kud/gandi-cli",
3
+ "version": "0.1.0",
4
+ "description": "A modern CLI for the Gandi v5 REST API",
5
+ "type": "module",
6
+ "bin": {
7
+ "gandi": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "start": "node dist/index.js"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/kud/gandi-cli"
20
+ },
21
+ "dependencies": {
22
+ "commander": "^12.1.0",
23
+ "ink": "^7.0.0",
24
+ "react": "^19.2.5",
25
+ "smol-toml": "^1.3.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "@types/react": "^19.0.0",
30
+ "typescript": "^5.5.4"
31
+ }
32
+ }