@madewithremy/admin 0.1.37 → 0.1.39

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/prod.js CHANGED
@@ -274,20 +274,20 @@ function seg(value) {
274
274
  return encodeURIComponent(String(value));
275
275
  }
276
276
  function qs(params) {
277
- const search2 = new URLSearchParams();
277
+ const search3 = new URLSearchParams();
278
278
  for (const [key, value] of Object.entries(params)) {
279
279
  if (value === void 0 || value === null) {
280
280
  continue;
281
281
  }
282
282
  if (Array.isArray(value)) {
283
283
  for (const entry of value) {
284
- search2.append(key, String(entry));
284
+ search3.append(key, String(entry));
285
285
  }
286
286
  continue;
287
287
  }
288
- search2.set(key, String(value));
288
+ search3.set(key, String(value));
289
289
  }
290
- const text = search2.toString();
290
+ const text = search3.toString();
291
291
  return text ? `?${text}` : "";
292
292
  }
293
293
  function authHeaders(ctx) {
@@ -1510,8 +1510,113 @@ async function customRetry(ctx, hostname) {
1510
1510
  `/_internal/v2/apps/${ctx.appId}/settings/custom-domains/${seg(id)}/retry`
1511
1511
  );
1512
1512
  }
1513
+ var REGISTERED = (ctx) => `/_internal/v2/apps/${ctx.appId}/settings/registered-domains`;
1514
+ function search(ctx, query3) {
1515
+ return call(
1516
+ ctx,
1517
+ "POST",
1518
+ `${REGISTERED(ctx)}/search`,
1519
+ { query: query3 }
1520
+ );
1521
+ }
1522
+ function quote(ctx, name) {
1523
+ return call(
1524
+ ctx,
1525
+ "POST",
1526
+ `${REGISTERED(ctx)}/quote`,
1527
+ { name }
1528
+ );
1529
+ }
1530
+ function buy(ctx, params) {
1531
+ return call(
1532
+ ctx,
1533
+ "POST",
1534
+ `${REGISTERED(ctx)}/purchase`,
1535
+ { name: params.name, expectedPrice: params.price, acceptTerms: true }
1536
+ );
1537
+ }
1538
+ function owned(ctx) {
1539
+ return call(ctx, "GET", REGISTERED(ctx));
1540
+ }
1541
+ async function findOwned(ctx, name) {
1542
+ const wanted = name.toLowerCase();
1543
+ const { domains } = await owned(ctx);
1544
+ const domain = domains.find(
1545
+ (d) => d.name === wanted && (d.status === "active" || d.status === "expired" || d.status === "pending")
1546
+ );
1547
+ if (!domain) {
1548
+ throw new Error(`No domain "${name}" in this workspace`);
1549
+ }
1550
+ return domain;
1551
+ }
1552
+ function contactGet(ctx) {
1553
+ return call(
1554
+ ctx,
1555
+ "GET",
1556
+ `${REGISTERED(ctx)}/contact`
1557
+ );
1558
+ }
1559
+ function contactSet(ctx, contact) {
1560
+ return call(
1561
+ ctx,
1562
+ "POST",
1563
+ `${REGISTERED(ctx)}/contact`,
1564
+ { ...contact }
1565
+ );
1566
+ }
1567
+ async function zone(ctx, name) {
1568
+ const domain = await findOwned(ctx, name);
1569
+ return call(
1570
+ ctx,
1571
+ "GET",
1572
+ `${REGISTERED(ctx)}/${seg(domain.id)}/records`
1573
+ );
1574
+ }
1575
+ async function zoneAdd(ctx, name, record) {
1576
+ const domain = await findOwned(ctx, name);
1577
+ return call(
1578
+ ctx,
1579
+ "POST",
1580
+ `${REGISTERED(ctx)}/${seg(domain.id)}/records`,
1581
+ { ...record }
1582
+ );
1583
+ }
1584
+ async function zoneUpdate(ctx, name, recordId, record) {
1585
+ const domain = await findOwned(ctx, name);
1586
+ return call(
1587
+ ctx,
1588
+ "POST",
1589
+ `${REGISTERED(ctx)}/${seg(domain.id)}/records/${seg(recordId)}`,
1590
+ { ...record }
1591
+ );
1592
+ }
1593
+ async function zoneDelete(ctx, name, recordId) {
1594
+ const domain = await findOwned(ctx, name);
1595
+ return call(
1596
+ ctx,
1597
+ "POST",
1598
+ `${REGISTERED(ctx)}/${seg(domain.id)}/records/${seg(recordId)}/delete`
1599
+ );
1600
+ }
1513
1601
 
1514
1602
  // src/commands/domains.ts
1603
+ var RECORD_TYPES = [
1604
+ "A",
1605
+ "AAAA",
1606
+ "ANAME",
1607
+ "CNAME",
1608
+ "MX",
1609
+ "NS",
1610
+ "SRV",
1611
+ "TXT"
1612
+ ];
1613
+ var ZONE_RECORD_FLAGS = {
1614
+ type: { type: "string", choices: RECORD_TYPES },
1615
+ host: { type: "string" },
1616
+ answer: { type: "string" },
1617
+ ttl: { type: "number", min: 300 },
1618
+ priority: { type: "number", min: 0 }
1619
+ };
1515
1620
  var domainsSpecs = {
1516
1621
  "domains get": {
1517
1622
  usage: "Usage: remy-admin domains get"
@@ -1550,6 +1655,79 @@ var domainsSpecs = {
1550
1655
  "domains custom retry": {
1551
1656
  usage: "Usage: remy-admin domains custom retry <hostname>",
1552
1657
  positionals: [{ name: "hostname", required: true }]
1658
+ },
1659
+ "domains search": {
1660
+ usage: "Usage: remy-admin domains search <keyword or name>",
1661
+ positionals: [{ name: "query", required: true, variadic: true }]
1662
+ },
1663
+ "domains quote": {
1664
+ usage: "Usage: remy-admin domains quote <name>",
1665
+ positionals: [{ name: "name", required: true }]
1666
+ },
1667
+ "domains buy": {
1668
+ usage: "Usage: remy-admin domains buy <name> --price <dollars>\nBuys the domain for the workspace at once: the price isn't refundable, and it renews yearly at its renewal price. Only run this after the user has explicitly confirmed the name, the price and the renewal price. --price is the quote's price.",
1669
+ positionals: [{ name: "name", required: true }],
1670
+ flags: {
1671
+ price: { type: "string" }
1672
+ },
1673
+ requireAnyOf: {
1674
+ flags: ["price"],
1675
+ message: "Pass --price with the quote's price the user confirmed (remy-admin domains quote <name>)."
1676
+ }
1677
+ },
1678
+ "domains owned": {
1679
+ usage: "Usage: remy-admin domains owned"
1680
+ },
1681
+ "domains contact get": {
1682
+ usage: "Usage: remy-admin domains contact get"
1683
+ },
1684
+ "domains contact set": {
1685
+ usage: "Usage: remy-admin domains contact set --first-name <n> --last-name <n> [--company <org>] --address1 <line> [--address2 <line>] --city <c> --state <s> --zip <z> --country <XX> --email <e> --phone <+E.164>\nThe registrant: the legal owner of every domain the workspace buys (the organization when --company is set). ICANN emails that address to verify it.",
1686
+ flags: {
1687
+ "first-name": { type: "string" },
1688
+ "last-name": { type: "string" },
1689
+ company: { type: "string" },
1690
+ address1: { type: "string" },
1691
+ address2: { type: "string" },
1692
+ city: { type: "string" },
1693
+ state: { type: "string" },
1694
+ zip: { type: "string" },
1695
+ country: { type: "string" },
1696
+ email: { type: "string" },
1697
+ phone: { type: "string" }
1698
+ }
1699
+ },
1700
+ "domains zone list": {
1701
+ usage: "Usage: remy-admin domains zone list <name>",
1702
+ positionals: [{ name: "name", required: true }]
1703
+ },
1704
+ "domains zone add": {
1705
+ usage: "Usage: remy-admin domains zone add <name> --type <type> --answer <value> [--host <host>] [--ttl <seconds>] [--priority <n>]",
1706
+ positionals: [{ name: "name", required: true }],
1707
+ flags: ZONE_RECORD_FLAGS,
1708
+ requireAnyOf: {
1709
+ flags: ["answer"],
1710
+ message: "Pass --type and --answer."
1711
+ }
1712
+ },
1713
+ "domains zone update": {
1714
+ usage: "Usage: remy-admin domains zone update <name> <recordId> --type <type> --answer <value> [--host <host>] [--ttl <seconds>] [--priority <n>]",
1715
+ positionals: [
1716
+ { name: "name", required: true },
1717
+ { name: "recordId", required: true }
1718
+ ],
1719
+ flags: ZONE_RECORD_FLAGS,
1720
+ requireAnyOf: {
1721
+ flags: ["answer"],
1722
+ message: "Pass --type and --answer."
1723
+ }
1724
+ },
1725
+ "domains zone delete": {
1726
+ usage: "Usage: remy-admin domains zone delete <name> <recordId>",
1727
+ positionals: [
1728
+ { name: "name", required: true },
1729
+ { name: "recordId", required: true }
1730
+ ]
1553
1731
  }
1554
1732
  };
1555
1733
  async function domainsGet(ctx) {
@@ -1594,6 +1772,72 @@ async function customDomainsRemove(ctx, a) {
1594
1772
  async function customDomainsRetry(ctx, a) {
1595
1773
  out(await customRetry(ctx, a.req("hostname")));
1596
1774
  }
1775
+ async function domainsSearch(ctx, a) {
1776
+ out(await search(ctx, a.rest("query").join(" ")));
1777
+ }
1778
+ async function domainsQuote(ctx, a) {
1779
+ out(await quote(ctx, a.req("name")));
1780
+ }
1781
+ async function domainsBuy(ctx, a) {
1782
+ out(
1783
+ await buy(ctx, {
1784
+ name: a.req("name"),
1785
+ price: Number(a.str("price"))
1786
+ })
1787
+ );
1788
+ }
1789
+ async function domainsOwned(ctx) {
1790
+ out(await owned(ctx));
1791
+ }
1792
+ async function domainsContactGet(ctx) {
1793
+ out(await contactGet(ctx));
1794
+ }
1795
+ async function domainsContactSet(ctx, a) {
1796
+ out(
1797
+ await contactSet(ctx, {
1798
+ firstName: a.str("first-name") ?? "",
1799
+ lastName: a.str("last-name") ?? "",
1800
+ companyName: a.str("company"),
1801
+ address1: a.str("address1") ?? "",
1802
+ address2: a.str("address2"),
1803
+ city: a.str("city") ?? "",
1804
+ state: a.str("state") ?? "",
1805
+ zip: a.str("zip") ?? "",
1806
+ country: a.str("country") ?? "",
1807
+ email: a.str("email") ?? "",
1808
+ phone: a.str("phone") ?? ""
1809
+ })
1810
+ );
1811
+ }
1812
+ function zoneRecord(a) {
1813
+ const host = a.str("host") ?? "";
1814
+ return {
1815
+ host: host === "@" ? "" : host,
1816
+ type: a.str("type") ?? "",
1817
+ answer: a.str("answer") ?? "",
1818
+ ttl: a.num("ttl"),
1819
+ priority: a.num("priority")
1820
+ };
1821
+ }
1822
+ async function domainsZoneList(ctx, a) {
1823
+ out(await zone(ctx, a.req("name")));
1824
+ }
1825
+ async function domainsZoneAdd(ctx, a) {
1826
+ out(await zoneAdd(ctx, a.req("name"), zoneRecord(a)));
1827
+ }
1828
+ async function domainsZoneUpdate(ctx, a) {
1829
+ out(
1830
+ await zoneUpdate(
1831
+ ctx,
1832
+ a.req("name"),
1833
+ Number(a.req("recordId")),
1834
+ zoneRecord(a)
1835
+ )
1836
+ );
1837
+ }
1838
+ async function domainsZoneDelete(ctx, a) {
1839
+ out(await zoneDelete(ctx, a.req("name"), Number(a.req("recordId"))));
1840
+ }
1597
1841
  var domainsHandlers = {
1598
1842
  "domains get": domainsGet,
1599
1843
  "domains set": domainsSet,
@@ -1604,9 +1848,19 @@ var domainsHandlers = {
1604
1848
  "domains custom records": customDomainsRecords,
1605
1849
  "domains custom status": customDomainsStatus,
1606
1850
  "domains custom remove": customDomainsRemove,
1607
- "domains custom retry": customDomainsRetry
1851
+ "domains custom retry": customDomainsRetry,
1852
+ "domains search": domainsSearch,
1853
+ "domains quote": domainsQuote,
1854
+ "domains buy": domainsBuy,
1855
+ "domains owned": domainsOwned,
1856
+ "domains contact get": domainsContactGet,
1857
+ "domains contact set": domainsContactSet,
1858
+ "domains zone list": domainsZoneList,
1859
+ "domains zone add": domainsZoneAdd,
1860
+ "domains zone update": domainsZoneUpdate,
1861
+ "domains zone delete": domainsZoneDelete
1608
1862
  };
1609
- var domainsHelp = `remy-admin domains \u2014 Manage your app's domains.
1863
+ var domainsHelp = `remy-admin domains \u2014 Manage your app's domains, and buy one.
1610
1864
 
1611
1865
  Platform subdomain (e.g. my-app.madewithremy.com):
1612
1866
  get Get current custom subdomain
@@ -1622,6 +1876,18 @@ Custom domains (customer-owned hostnames, CNAME/A records):
1622
1876
  custom remove <hostname> Remove a hostname (apex also removes paired www)
1623
1877
  custom retry <hostname> Re-trigger validation (after customer fixes DNS)
1624
1878
 
1879
+ Buying a domain (the workspace owns it; Remy hosts its DNS):
1880
+ search <keyword or name> Names for sale, with price and renewal price
1881
+ quote <name> The definitive price for one name
1882
+ buy <name> --price <dollars> Buy it (not refundable: confirm with the user first)
1883
+ owned The workspace's domains: status, expiry, auto-renew, apps
1884
+ contact get The registrant contact every domain is registered to
1885
+ contact set --first-name ... Set it (needed before the first purchase)
1886
+ zone list <name> The domain's DNS records (ours marked managed)
1887
+ zone add <name> ... Add a record (--type, --answer, --host, --ttl, --priority)
1888
+ zone update <name> <id> ... Replace one of the owner's records
1889
+ zone delete <name> <id> Delete one of the owner's records
1890
+
1625
1891
  Usage:
1626
1892
  remy-admin domains get
1627
1893
  remy-admin domains set my-app
@@ -1634,6 +1900,13 @@ Usage:
1634
1900
  remy-admin domains custom status app.acme.com
1635
1901
  remy-admin domains custom retry app.acme.com
1636
1902
  remy-admin domains custom remove app.acme.com
1903
+ remy-admin domains search acme bakery
1904
+ remy-admin domains quote acmebakery.com
1905
+ remy-admin domains buy acmebakery.com --price 11.99
1906
+ remy-admin domains owned
1907
+ remy-admin domains contact set --first-name Ada --last-name Lovelace --address1 "1 Main St" --city Austin --state TX --zip 78701 --country US --email ada@acme.com --phone +15125550123
1908
+ remy-admin domains zone list acmebakery.com
1909
+ remy-admin domains zone add acmebakery.com --type TXT --host @ --answer "google-site-verification=abc"
1637
1910
 
1638
1911
  Notes:
1639
1912
  - 'add' with an apex (e.g. acme.com) auto-creates the www.apex pair and
@@ -1641,7 +1914,29 @@ Notes:
1641
1914
  - 'list'/'records'/'status' read a CF-synced cache that can be up to ~5
1642
1915
  minutes stale. After the customer adds DNS, use 'retry' to force a
1643
1916
  synchronous re-check.
1644
- - 'uiStatus' values: waiting_for_dns | issuing_ssl | live | action_needed | reconnecting.`;
1917
+ - 'uiStatus' values: waiting_for_dns | issuing_ssl | live | action_needed | reconnecting.
1918
+ - 'buy' charges the workspace the price at once, and it isn't refundable; the
1919
+ domain renews yearly at its renewal price while auto-renew is on (the owner
1920
+ turns it off, renews early or transfers out in Workspace Settings > Domains).
1921
+ Never run 'buy' without the user's explicit confirmation of the name, the
1922
+ price and the renewal price. --price is the quote's; if the price has moved,
1923
+ 'buy' is refused with price_changed and the new price, to confirm again.
1924
+ - The first purchase needs the registrant contact ('contact set'): the
1925
+ domain's legal owner. ICANN emails that address to verify it within 15
1926
+ days, or the domain stops resolving; tell the user to watch for it.
1927
+ - Other refusals: registrant_contact_required, insufficient_credits/*,
1928
+ entitlement_required:registered_domains (the plan holds no more domains:
1929
+ a paid plan holds 20), domain_registration_rate (3 in any 30 days),
1930
+ domain_unavailable, tld_not_supported,
1931
+ trademark_claim, registrar_busy (try again), registrar_payment (temporarily
1932
+ unavailable), namecom_not_configured (this deployment doesn't sell domains).
1933
+ - A bought domain is usually active when 'buy' returns; 'owned' shows a
1934
+ pending one settle. It connects to nothing by itself: 'custom add',
1935
+ 'email domains add' and 'email inbound-domains add' on it write their DNS
1936
+ records automatically (the response's registeredDomain says what was
1937
+ written), so there's nothing to hand the user.
1938
+ - Records the platform wrote for a connection are 'managed' in 'zone list' and
1939
+ can't be changed; they go when the connection is removed.`;
1645
1940
 
1646
1941
  // src/ops/users.ts
1647
1942
  function list4(ctx, params = {}) {
@@ -3375,7 +3670,7 @@ async function allDocuments(ctx, params) {
3375
3670
  function list8(ctx) {
3376
3671
  return call(ctx, "GET", base(ctx.appId));
3377
3672
  }
3378
- function search(ctx, params) {
3673
+ function search2(ctx, params) {
3379
3674
  const {
3380
3675
  slug,
3381
3676
  query: query3,
@@ -6211,7 +6506,7 @@ async function dataSourcesSearch(ctx, a) {
6211
6506
  const filter = filterFromFlags(a);
6212
6507
  const maxPerDocument = a.num("max-per-document");
6213
6508
  out(
6214
- await search(ctx, {
6509
+ await search2(ctx, {
6215
6510
  slug: sourceOf(a),
6216
6511
  query: a.req("query"),
6217
6512
  ...topK ? { topK } : {},
@@ -8619,11 +8914,11 @@ function logs(ctx, params) {
8619
8914
  if (params.limit) {
8620
8915
  query3.set("limit", String(params.limit));
8621
8916
  }
8622
- const search2 = query3.toString();
8917
+ const search3 = query3.toString();
8623
8918
  return call(
8624
8919
  ctx,
8625
8920
  "GET",
8626
- `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
8921
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search3 ? `?${search3}` : ""}`
8627
8922
  );
8628
8923
  }
8629
8924
  function provision(ctx, params) {
@@ -9371,7 +9666,7 @@ Commands:
9371
9666
  analytics View traffic, top-N, geo, and AI-referral insights
9372
9667
  releases View and monitor releases
9373
9668
  diagnostics View the post-deploy Lighthouse audit (scores, issues, raw report)
9374
- domains Manage custom subdomain
9669
+ domains Subdomain, custom domains, and buying a domain
9375
9670
  users Manage app users and roles
9376
9671
  db Query the production database
9377
9672
  secrets Manage app secrets (env vars)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madewithremy/admin",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "Admin SDK + CLI for managing production Remy apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",