@hasna/domains 0.0.21 → 0.0.22

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/cli/index.js CHANGED
@@ -3664,7 +3664,7 @@ function godaddyCapability(env = process.env) {
3664
3664
  return {
3665
3665
  configured,
3666
3666
  gated: true,
3667
- notes: configured ? "Credentials present; availability checks may work, but purchase + DNS writes require a qualifying account (>=10 domains / DDC). Prefer Route53." : "Not configured; and retail Domains API is gated for purchase/DNS since 2024."
3667
+ notes: configured ? "Credentials present; DNS/domain management may work with qualifying account access, but availability remains threshold-gated and direct automated purchase is not exposed here. Prefer Route53 for purchases." : "Not configured; GoDaddy production availability remains threshold-gated and DNS/domain management requires qualifying account access."
3668
3668
  };
3669
3669
  }
3670
3670
  var GoDaddyApiError, _overriddenFetch = null, GODADDY_API_BASE = "https://api.godaddy.com";
@@ -34425,11 +34425,32 @@ var init_dist_es14 = __esm(() => {
34425
34425
  });
34426
34426
 
34427
34427
  // src/lib/route53.ts
34428
+ var exports_route53 = {};
34429
+ __export(exports_route53, {
34430
+ upsertRecords: () => upsertRecords,
34431
+ upsertRecord: () => upsertRecord,
34432
+ updateNameservers: () => updateNameservers2,
34433
+ registerDomain: () => registerDomain2,
34434
+ listRegisteredDomains: () => listRegisteredDomains,
34435
+ listRecords: () => listRecords,
34436
+ listHostedZones: () => listHostedZones,
34437
+ getRegistrationStatus: () => getRegistrationStatus,
34438
+ getHostedZone: () => getHostedZone,
34439
+ getDomainDetail: () => getDomainDetail,
34440
+ getConfig: () => getConfig2,
34441
+ findHostedZoneByDomain: () => findHostedZoneByDomain,
34442
+ deleteRecord: () => deleteRecord,
34443
+ deleteHostedZone: () => deleteHostedZone,
34444
+ createRoute53Provider: () => createRoute53Provider,
34445
+ createHostedZone: () => createHostedZone,
34446
+ checkAvailability: () => checkAvailability3
34447
+ });
34428
34448
  function getConfig2() {
34429
34449
  return {
34430
34450
  region: process.env["AWS_REGION"] || "us-east-1",
34431
34451
  accessKeyId: process.env["AWS_ACCESS_KEY_ID"],
34432
- secretAccessKey: process.env["AWS_SECRET_ACCESS_KEY"]
34452
+ secretAccessKey: process.env["AWS_SECRET_ACCESS_KEY"],
34453
+ sessionToken: process.env["AWS_SESSION_TOKEN"]
34433
34454
  };
34434
34455
  }
34435
34456
  function checkCredentials(cfg) {
@@ -34445,7 +34466,7 @@ function makeClients(config) {
34445
34466
  const cfg = config ?? getConfig2();
34446
34467
  checkCredentials(cfg);
34447
34468
  const region = cfg.region || "us-east-1";
34448
- const credentials = cfg.accessKeyId && cfg.secretAccessKey ? { accessKeyId: cfg.accessKeyId, secretAccessKey: cfg.secretAccessKey } : undefined;
34469
+ const credentials = cfg.accessKeyId && cfg.secretAccessKey ? { accessKeyId: cfg.accessKeyId, secretAccessKey: cfg.secretAccessKey, sessionToken: cfg.sessionToken } : undefined;
34449
34470
  return {
34450
34471
  route53: new Route53Client({ region, credentials }),
34451
34472
  domains: new Route53DomainsClient({ region: "us-east-1", credentials })
@@ -34585,7 +34606,8 @@ async function listHostedZones(config) {
34585
34606
  id: cleanZoneId(z2.Id ?? ""),
34586
34607
  name: z2.Name ?? "",
34587
34608
  record_count: z2.ResourceRecordSetCount ?? 0,
34588
- comment: z2.Config?.Comment
34609
+ comment: z2.Config?.Comment,
34610
+ private_zone: z2.Config?.PrivateZone
34589
34611
  });
34590
34612
  }
34591
34613
  marker = result.IsTruncated ? result.NextMarker : undefined;
@@ -34600,7 +34622,8 @@ async function getHostedZone(hostedZoneId, config) {
34600
34622
  name: result.HostedZone?.Name ?? "",
34601
34623
  record_count: result.HostedZone?.ResourceRecordSetCount ?? 0,
34602
34624
  comment: result.HostedZone?.Config?.Comment,
34603
- name_servers: result.DelegationSet?.NameServers ?? []
34625
+ name_servers: result.DelegationSet?.NameServers ?? [],
34626
+ private_zone: result.HostedZone?.Config?.PrivateZone
34604
34627
  };
34605
34628
  }
34606
34629
  async function deleteHostedZone(hostedZoneId, config) {
@@ -34610,7 +34633,15 @@ async function deleteHostedZone(hostedZoneId, config) {
34610
34633
  async function findHostedZoneByDomain(domain, config) {
34611
34634
  const zones = await listHostedZones(config);
34612
34635
  const normalized = domain.endsWith(".") ? domain : `${domain}.`;
34613
- return zones.find((z2) => z2.name === normalized) ?? null;
34636
+ const matches = zones.filter((z2) => z2.name === normalized);
34637
+ if (matches.length === 0)
34638
+ return null;
34639
+ const publicMatches = matches.filter((z2) => !z2.private_zone);
34640
+ const candidates = publicMatches.length > 0 ? publicMatches : matches;
34641
+ if (candidates.length > 1) {
34642
+ throw new Error(`Multiple Route 53 hosted zones found for ${domain}; specify hosted zone id`);
34643
+ }
34644
+ return candidates[0] ?? null;
34614
34645
  }
34615
34646
  function rrsToRecord(rrs) {
34616
34647
  if (rrs.AliasTarget) {
@@ -34710,19 +34741,53 @@ function createRoute53Provider(config) {
34710
34741
  const cfg = config ?? getConfig2();
34711
34742
  const registerWithRoute53 = registerDomain2;
34712
34743
  const updateRoute53Nameservers = updateNameservers2;
34744
+ async function listDomainInventory() {
34745
+ const byDomain = new Map;
34746
+ try {
34747
+ const registered = await listRegisteredDomains(cfg);
34748
+ for (const d3 of registered) {
34749
+ byDomain.set(d3.domain, {
34750
+ domain: d3.domain,
34751
+ registrar: "AWS Route 53",
34752
+ created: "",
34753
+ expires: d3.expiry,
34754
+ nameservers: [],
34755
+ status: "active",
34756
+ auto_renew: d3.auto_renew
34757
+ });
34758
+ }
34759
+ } catch (error) {
34760
+ const message = error instanceof Error ? error.message : String(error);
34761
+ if (!message.includes("route53domains:ListDomains") && !message.includes("AccessDenied")) {
34762
+ throw error;
34763
+ }
34764
+ }
34765
+ const zones = await listHostedZones(cfg);
34766
+ for (const z2 of zones) {
34767
+ const zone = z2.name_servers?.length ? z2 : await getHostedZone(z2.id, cfg).catch(() => z2);
34768
+ const domain = zone.name.replace(/\.$/, "");
34769
+ const nameservers = zone.name_servers ?? [];
34770
+ const existing = byDomain.get(domain);
34771
+ if (existing) {
34772
+ existing.nameservers = nameservers.length > 0 ? nameservers : existing.nameservers;
34773
+ continue;
34774
+ }
34775
+ byDomain.set(domain, {
34776
+ domain,
34777
+ registrar: "AWS Route 53 DNS",
34778
+ created: "",
34779
+ expires: "",
34780
+ nameservers,
34781
+ status: "active",
34782
+ auto_renew: false
34783
+ });
34784
+ }
34785
+ return Array.from(byDomain.values());
34786
+ }
34713
34787
  return {
34714
34788
  name: "route53",
34715
34789
  async listDomains() {
34716
- const domains = await listRegisteredDomains(cfg);
34717
- return domains.map((d3) => ({
34718
- domain: d3.domain,
34719
- registrar: "AWS Route 53",
34720
- created: "",
34721
- expires: d3.expiry,
34722
- nameservers: [],
34723
- status: "active",
34724
- auto_renew: d3.auto_renew
34725
- }));
34790
+ return listDomainInventory();
34726
34791
  },
34727
34792
  async getDomainInfo(domain) {
34728
34793
  const detail = await getDomainDetail(domain, cfg);
@@ -34791,7 +34856,7 @@ function createRoute53Provider(config) {
34791
34856
  };
34792
34857
  },
34793
34858
  async syncToLocalDb(dbFns) {
34794
- const domains = await listRegisteredDomains(cfg);
34859
+ const domains = await listDomainInventory();
34795
34860
  let synced = 0;
34796
34861
  let created = 0;
34797
34862
  let updated = 0;
@@ -34800,20 +34865,39 @@ function createRoute53Provider(config) {
34800
34865
  try {
34801
34866
  const existing = dbFns.getDomainByName(d3.domain);
34802
34867
  if (existing) {
34868
+ const existingRoute53 = existing.metadata["route53"];
34869
+ const staleDnsOnlyRegistrar = d3.registrar !== "AWS Route 53" && (existing.registrar === "AWS Route 53 DNS" || existing.registrar === "AWS Route 53" && existingRoute53?.source === "route53:hosted_zones");
34803
34870
  dbFns.updateDomain(existing.id, {
34804
- registrar: "AWS Route 53",
34805
- expires_at: d3.expiry || undefined,
34871
+ ...d3.registrar === "AWS Route 53" ? { registrar: "AWS Route 53" } : {},
34872
+ ...staleDnsOnlyRegistrar ? { registrar: null } : {},
34873
+ expires_at: d3.expires || undefined,
34806
34874
  auto_renew: d3.auto_renew,
34875
+ nameservers: d3.nameservers.length > 0 ? d3.nameservers : existing.nameservers,
34876
+ metadata: {
34877
+ ...existing.metadata,
34878
+ route53: {
34879
+ source: d3.registrar === "AWS Route 53" ? "route53domains+hosted_zones" : "route53:hosted_zones",
34880
+ synced_at: new Date().toISOString()
34881
+ }
34882
+ },
34807
34883
  status: "active"
34808
34884
  });
34809
34885
  updated++;
34810
34886
  } else {
34811
34887
  dbFns.createDomain({
34812
34888
  name: d3.domain,
34813
- registrar: "AWS Route 53",
34814
- expires_at: d3.expiry || undefined,
34889
+ ...d3.registrar === "AWS Route 53" ? { registrar: "AWS Route 53" } : {},
34890
+ expires_at: d3.expires || undefined,
34815
34891
  auto_renew: d3.auto_renew,
34816
- status: "active"
34892
+ nameservers: d3.nameservers,
34893
+ status: "active",
34894
+ notes: d3.registrar === "AWS Route 53 DNS" ? "Discovered from Route 53 hosted zones; registrar ownership was not inferred." : undefined,
34895
+ metadata: {
34896
+ route53: {
34897
+ source: d3.registrar === "AWS Route 53" ? "route53domains+hosted_zones" : "route53:hosted_zones",
34898
+ synced_at: new Date().toISOString()
34899
+ }
34900
+ }
34817
34901
  });
34818
34902
  created++;
34819
34903
  }
@@ -35055,26 +35139,111 @@ async function listRecords2(zoneId, config) {
35055
35139
  }
35056
35140
  return records;
35057
35141
  }
35058
- async function upsertRecord2(zoneId, record, config) {
35059
- const existing = await cfFetch(`/zones/${zoneId}/dns_records?type=${record.type}&name=${encodeURIComponent(record.name)}`, { config });
35060
- const body = {
35061
- type: record.type,
35062
- name: record.name,
35063
- content: record.content,
35064
- ttl: record.ttl ?? 1,
35065
- priority: record.priority,
35066
- proxied: record.proxied ?? false
35067
- };
35068
- if (existing && existing.length > 0) {
35069
- await cfFetch(`/zones/${zoneId}/dns_records/${existing[0].id}`, { method: "PUT", body, config });
35070
- } else {
35071
- await cfFetch(`/zones/${zoneId}/dns_records`, { method: "POST", body, config });
35142
+ async function listRecordsByNameType(zoneId, type, name, config) {
35143
+ const result = await cfFetch(`/zones/${zoneId}/dns_records?type=${encodeURIComponent(type)}&name=${encodeURIComponent(name)}`, { config });
35144
+ return (result ?? []).map((r3) => ({
35145
+ id: r3.id,
35146
+ type: r3.type,
35147
+ name: r3.name,
35148
+ content: r3.content,
35149
+ ttl: r3.ttl,
35150
+ priority: r3.priority,
35151
+ proxied: r3.proxied
35152
+ }));
35153
+ }
35154
+ async function replaceRecordsByNameType(zoneId, records, config) {
35155
+ if (records.length === 0)
35156
+ return;
35157
+ const { type, name } = records[0];
35158
+ const existing = await listRecordsByNameType(zoneId, type, name, config);
35159
+ for (const record of existing) {
35160
+ if (record.id)
35161
+ await deleteRecord2(zoneId, record.id, config);
35162
+ }
35163
+ for (const record of records) {
35164
+ await cfFetch(`/zones/${zoneId}/dns_records`, {
35165
+ method: "POST",
35166
+ body: {
35167
+ type: record.type,
35168
+ name: record.name,
35169
+ content: record.content,
35170
+ ttl: record.ttl ?? 1,
35171
+ priority: record.priority,
35172
+ proxied: record.proxied ?? false
35173
+ },
35174
+ config
35175
+ });
35072
35176
  }
35073
35177
  }
35178
+ async function deleteRecord2(zoneId, recordId, config) {
35179
+ await cfFetch(`/zones/${zoneId}/dns_records/${recordId}`, { method: "DELETE", config });
35180
+ }
35181
+ function zoneToDomainInfo(zone) {
35182
+ return {
35183
+ domain: zone.name,
35184
+ registrar: "Cloudflare DNS",
35185
+ created: "",
35186
+ expires: "",
35187
+ nameservers: zone.nameservers,
35188
+ status: zone.status === "active" ? "active" : "discovered",
35189
+ auto_renew: false
35190
+ };
35191
+ }
35192
+ function withCloudflareMetadata(existing, zone) {
35193
+ return {
35194
+ ...existing,
35195
+ cloudflare: {
35196
+ zone_id: zone.id,
35197
+ zone_status: zone.status,
35198
+ source: "cloudflare:zones",
35199
+ synced_at: new Date().toISOString()
35200
+ }
35201
+ };
35202
+ }
35074
35203
  function createCloudflareProvider(config) {
35075
35204
  const cfg = config ?? getConfig3();
35076
35205
  return {
35077
35206
  name: "cloudflare",
35207
+ async listDomains() {
35208
+ const zones = await listZones(cfg);
35209
+ return zones.map(zoneToDomainInfo);
35210
+ },
35211
+ async syncToLocalDb(dbFns) {
35212
+ const zones = await listZones(cfg);
35213
+ let synced = 0;
35214
+ let created = 0;
35215
+ let updated = 0;
35216
+ const errors3 = [];
35217
+ for (const zone of zones) {
35218
+ try {
35219
+ const info = zoneToDomainInfo(zone);
35220
+ const existing = dbFns.getDomainByName(zone.name);
35221
+ if (existing) {
35222
+ dbFns.updateDomain(existing.id, {
35223
+ ...existing.registrar === "Cloudflare DNS" ? { registrar: null } : {},
35224
+ status: existing.status === "discovered" && info.status === "active" ? "active" : existing.status,
35225
+ nameservers: zone.nameservers,
35226
+ metadata: withCloudflareMetadata(existing.metadata, zone)
35227
+ });
35228
+ updated++;
35229
+ } else {
35230
+ dbFns.createDomain({
35231
+ name: zone.name,
35232
+ status: info.status === "active" ? "active" : "discovered",
35233
+ auto_renew: false,
35234
+ nameservers: zone.nameservers,
35235
+ notes: "Discovered from Cloudflare zones; registrar ownership was not inferred.",
35236
+ metadata: withCloudflareMetadata({}, zone)
35237
+ });
35238
+ created++;
35239
+ }
35240
+ synced++;
35241
+ } catch (err) {
35242
+ errors3.push(`${zone.name}: ${err instanceof Error ? err.message : String(err)}`);
35243
+ }
35244
+ }
35245
+ return { synced, created, updated, errors: errors3 };
35246
+ },
35078
35247
  async getDnsRecords(domain) {
35079
35248
  const zone = await getZone(domain, cfg);
35080
35249
  if (!zone)
@@ -35092,8 +35261,15 @@ function createCloudflareProvider(config) {
35092
35261
  const zone = await getZone(domain, cfg);
35093
35262
  if (!zone)
35094
35263
  throw new Error(`No Cloudflare zone found for ${domain}`);
35264
+ const grouped = new Map;
35095
35265
  for (const r3 of records) {
35096
- await upsertRecord2(zone.id, { type: r3.type, name: r3.name, content: r3.value, ttl: r3.ttl || 1, priority: r3.priority }, cfg);
35266
+ const key = `${r3.type}|${r3.name}`;
35267
+ const existing = grouped.get(key) ?? [];
35268
+ existing.push({ type: r3.type, name: r3.name, content: r3.value, ttl: r3.ttl || 1, priority: r3.priority });
35269
+ grouped.set(key, existing);
35270
+ }
35271
+ for (const group of grouped.values()) {
35272
+ await replaceRecordsByNameType(zone.id, group, cfg);
35097
35273
  }
35098
35274
  return true;
35099
35275
  }
@@ -35139,7 +35315,7 @@ function getApiKey() {
35139
35315
  function getConfig4() {
35140
35316
  return resolveBrandsightConfig();
35141
35317
  }
35142
- async function apiRequest3(method, path, apiKey, body, baseUrl = BRANDSIGHT_BASE) {
35318
+ async function apiGet(path, apiKey, baseUrl = BRANDSIGHT_BASE) {
35143
35319
  const fetchFn = _fetchFn || globalThis.fetch;
35144
35320
  const url = `${baseUrl}${path}`;
35145
35321
  const headers = {
@@ -35150,13 +35326,12 @@ async function apiRequest3(method, path, apiKey, body, baseUrl = BRANDSIGHT_BASE
35150
35326
  };
35151
35327
  try {
35152
35328
  const response = await fetchFn(url, {
35153
- method,
35329
+ method: "GET",
35154
35330
  headers,
35155
- body: body ? JSON.stringify(body) : undefined,
35156
35331
  signal: AbortSignal.timeout(15000)
35157
35332
  });
35158
35333
  if (!response.ok) {
35159
- throw new BrandsightApiError(`Brandsight API ${method} ${path} failed with status ${response.status}`, response.status, await response.text());
35334
+ throw new BrandsightApiError(`Brandsight API GET ${path} failed with status ${response.status}`, response.status, await response.text());
35160
35335
  }
35161
35336
  const data = await response.json();
35162
35337
  return { data, stub: false };
@@ -35166,31 +35341,102 @@ async function apiRequest3(method, path, apiKey, body, baseUrl = BRANDSIGHT_BASE
35166
35341
  return { data: null, stub: true };
35167
35342
  }
35168
35343
  }
35169
- async function apiGet(path, apiKey, baseUrl = BRANDSIGHT_BASE) {
35170
- const fetchFn = _fetchFn || globalThis.fetch;
35171
- const url = `${baseUrl}${path}`;
35172
- const headers = {
35173
- Authorization: `Bearer ${apiKey}`,
35344
+ function requireDomainConfig(config) {
35345
+ const cfg = config ?? getConfig4();
35346
+ if (!cfg.apiKey || !cfg.apiSecret || !cfg.customerId) {
35347
+ throw new BrandsightApiError("Brandsight Domain API credentials are not configured. Set BRANDSIGHT_API_KEY, BRANDSIGHT_API_SECRET, and BRANDSIGHT_CUSTOMER_ID (or HASNAXYZ_BRANDSIGHT_LIVE_* aliases).");
35348
+ }
35349
+ return cfg;
35350
+ }
35351
+ function domainBaseUrl(cfg) {
35352
+ return cfg.baseUrl ?? BRANDSIGHT_DOMAIN_BASE;
35353
+ }
35354
+ function domainHeaders(cfg) {
35355
+ return {
35356
+ Authorization: `sso-key ${cfg.apiKey}:${cfg.apiSecret}`,
35174
35357
  "Content-Type": "application/json",
35175
35358
  Accept: "application/json",
35176
35359
  "User-Agent": USER_AGENT
35177
35360
  };
35178
- try {
35179
- const response = await fetchFn(url, {
35180
- method: "GET",
35181
- headers,
35182
- signal: AbortSignal.timeout(15000)
35183
- });
35184
- if (!response.ok) {
35185
- throw new BrandsightApiError(`Brandsight API GET ${path} failed with status ${response.status}`, response.status, await response.text());
35186
- }
35187
- const data = await response.json();
35188
- return { data, stub: false };
35189
- } catch (error) {
35190
- if (error instanceof BrandsightApiError)
35191
- throw error;
35192
- return { data: null, stub: true };
35361
+ }
35362
+ async function domainApiRequest(method, path, config, body) {
35363
+ const cfg = requireDomainConfig(config);
35364
+ const fetchFn = _fetchFn || globalThis.fetch;
35365
+ const response = await fetchFn(`${domainBaseUrl(cfg)}${path}`, {
35366
+ method,
35367
+ headers: domainHeaders(cfg),
35368
+ body: body ? JSON.stringify(body) : undefined,
35369
+ signal: AbortSignal.timeout(30000)
35370
+ });
35371
+ const text = await response.text();
35372
+ if (!response.ok) {
35373
+ throw new BrandsightApiError(`Brandsight Domain API ${method} ${path} failed with status ${response.status}`, response.status, text);
35193
35374
  }
35375
+ if (!text.trim())
35376
+ return {};
35377
+ return JSON.parse(text);
35378
+ }
35379
+ function normalizeBrandsightDomain(raw) {
35380
+ const nameServers = raw.nameServers ?? raw.nameservers ?? [];
35381
+ const expiresAt = raw.expiresAt ?? raw.expires ?? "";
35382
+ const createdAt = raw.createdAt ?? raw.created ?? "";
35383
+ const renewAuto = raw.renewAuto ?? raw.auto_renew ?? false;
35384
+ return {
35385
+ ...raw,
35386
+ domain: String(raw.domain ?? ""),
35387
+ status: String(raw.status ?? "UNKNOWN"),
35388
+ created: createdAt,
35389
+ expires: expiresAt,
35390
+ auto_renew: renewAuto,
35391
+ locked: Boolean(raw.locked),
35392
+ nameservers: nameServers,
35393
+ createdAt,
35394
+ expiresAt,
35395
+ nameServers,
35396
+ renewAuto
35397
+ };
35398
+ }
35399
+ function customerPath(cfg, path) {
35400
+ return `/customers/${encodeURIComponent(cfg.customerId)}${path}`;
35401
+ }
35402
+ function domainTld(domain) {
35403
+ const parts = domain.split(".").filter(Boolean);
35404
+ if (parts.length < 2)
35405
+ throw new BrandsightApiError(`Invalid domain name: ${domain}`);
35406
+ return parts.slice(1).join(".");
35407
+ }
35408
+ function brandsightContact(contact) {
35409
+ return {
35410
+ addressMailing: {
35411
+ address1: contact.address_line_1,
35412
+ city: contact.city,
35413
+ country: contact.country_code,
35414
+ postalCode: contact.zip_code,
35415
+ state: contact.state
35416
+ },
35417
+ email: contact.email,
35418
+ encoding: "ASCII",
35419
+ nameFirst: contact.first_name,
35420
+ nameLast: contact.last_name,
35421
+ organization: contact.organization_name,
35422
+ phone: contact.phone
35423
+ };
35424
+ }
35425
+ async function domainAvailability(domain, cfg, type, period = 1) {
35426
+ const params = new URLSearchParams({
35427
+ domain,
35428
+ period: String(period),
35429
+ type,
35430
+ optimizeFor: "ACCURACY"
35431
+ });
35432
+ const result = await domainApiRequest("GET", `/domains/available?${params.toString()}`, cfg);
35433
+ return {
35434
+ domain: result.domain ?? domain,
35435
+ available: Boolean(result.available),
35436
+ price: result.price,
35437
+ currency: result.currency,
35438
+ registryPremiumPricing: result.registryPremiumPricing
35439
+ };
35194
35440
  }
35195
35441
  function generateStubAlerts(brandName) {
35196
35442
  const now2 = new Date().toISOString();
@@ -35244,32 +35490,160 @@ async function getThreatAssessment(domain) {
35244
35490
  return { ...result.data, stub: false };
35245
35491
  }
35246
35492
  async function listDomains2(config) {
35247
- const cfg = config ?? getConfig4();
35248
- const result = await apiGet("/portfolio/domains", cfg.apiKey);
35249
- if (result.stub)
35250
- return [];
35251
- return result.data.domains ?? [];
35493
+ const cfg = requireDomainConfig(config);
35494
+ const domains = [];
35495
+ const seenMarkers = new Set;
35496
+ let marker;
35497
+ while (true) {
35498
+ const params = new URLSearchParams({ limit: "500" });
35499
+ if (marker)
35500
+ params.set("marker", marker);
35501
+ const batch = await domainApiRequest("GET", customerPath(cfg, `/domains?${params.toString()}`), cfg);
35502
+ if (!Array.isArray(batch) || batch.length === 0)
35503
+ break;
35504
+ domains.push(...batch.map(normalizeBrandsightDomain).filter((d3) => d3.domain));
35505
+ if (batch.length < 500)
35506
+ break;
35507
+ const nextMarker = String(batch[batch.length - 1]?.domain ?? "");
35508
+ if (!nextMarker || seenMarkers.has(nextMarker))
35509
+ break;
35510
+ seenMarkers.add(nextMarker);
35511
+ marker = nextMarker;
35512
+ }
35513
+ return domains;
35252
35514
  }
35253
35515
  async function getDomainInfo3(domain, config) {
35254
- const cfg = config ?? getConfig4();
35255
- const result = await apiGet(`/portfolio/domains/${encodeURIComponent(domain)}`, cfg.apiKey);
35256
- if (result.stub)
35257
- return null;
35258
- return result.data;
35516
+ const cfg = requireDomainConfig(config);
35517
+ const result = await domainApiRequest("GET", customerPath(cfg, `/domains/${encodeURIComponent(domain)}`), cfg);
35518
+ return normalizeBrandsightDomain(result);
35259
35519
  }
35260
35520
  async function checkAvailability4(domain, config) {
35261
- const cfg = config ?? getConfig4();
35262
- const result = await apiGet(`/domains/check?domain=${encodeURIComponent(domain)}`, cfg.apiKey);
35263
- if (result.stub)
35264
- return { domain, available: false };
35265
- return result.data;
35521
+ const cfg = requireDomainConfig(config);
35522
+ return domainAvailability(domain, cfg, "REGISTRATION", 1);
35266
35523
  }
35267
35524
  async function renewDomain3(domain, years = 1, config) {
35268
- const cfg = config ?? getConfig4();
35269
- const result = await apiRequest3("POST", `/portfolio/domains/${encodeURIComponent(domain)}/renew`, cfg.apiKey, { years });
35270
- if (result.stub)
35271
- return { success: false };
35272
- return { success: true, orderId: result.data.orderId };
35525
+ const cfg = requireDomainConfig(config);
35526
+ const current = await getDomainInfo3(domain, cfg);
35527
+ if (!current?.expires)
35528
+ throw new BrandsightApiError(`Cannot renew ${domain}: current expiry is unavailable`);
35529
+ const quote = await domainAvailability(domain, cfg, "RENEWAL", years);
35530
+ const price = quote.price ?? current.renewal?.price;
35531
+ const currency = quote.currency ?? current.renewal?.currency;
35532
+ if (price == null || !currency) {
35533
+ throw new BrandsightApiError(`Cannot renew ${domain}: renewal quote did not include an exact price and currency`);
35534
+ }
35535
+ const result = await domainApiRequest("POST", customerPath(cfg, `/domains/${encodeURIComponent(domain)}/renew`), cfg, {
35536
+ consent: {
35537
+ agreedAt: new Date().toISOString(),
35538
+ agreedBy: cfg.shopperId ?? "domains-cli",
35539
+ currency,
35540
+ price,
35541
+ registryPremiumPricing: quote.registryPremiumPricing ?? false
35542
+ },
35543
+ expires: current.expires,
35544
+ period: years
35545
+ });
35546
+ return { success: true, orderId: result.orderId ?? result.id };
35547
+ }
35548
+ async function getLegalAgreements(tld, privacy = false, config) {
35549
+ const cfg = requireDomainConfig(config);
35550
+ const params = new URLSearchParams({
35551
+ privacy: String(privacy),
35552
+ tlds: tld
35553
+ });
35554
+ return domainApiRequest("GET", customerPath(cfg, `/domains/agreements?${params.toString()}`), cfg);
35555
+ }
35556
+ async function getRegistrationSchema(tld, config) {
35557
+ const cfg = requireDomainConfig(config);
35558
+ return domainApiRequest("GET", customerPath(cfg, `/domains/register/schema/${encodeURIComponent(tld)}`), cfg);
35559
+ }
35560
+ async function validateRegistrationRequest(payload, config) {
35561
+ const cfg = requireDomainConfig(config);
35562
+ await domainApiRequest("POST", customerPath(cfg, "/domains/register/validate"), cfg, payload);
35563
+ return true;
35564
+ }
35565
+ async function registerBrandsightDomain(domain, contact, options = {}, config) {
35566
+ const cfg = requireDomainConfig(config);
35567
+ const period = options.years ?? 1;
35568
+ const availability = await domainAvailability(domain, cfg, "REGISTRATION", period);
35569
+ if (!availability.available)
35570
+ throw new BrandsightApiError(`${domain} is not available for registration`);
35571
+ const price = options.premiumPrice ?? availability.price;
35572
+ if (price == null || !availability.currency) {
35573
+ throw new BrandsightApiError(`Cannot register ${domain}: availability quote did not include an exact price and currency`);
35574
+ }
35575
+ const tld = domainTld(domain);
35576
+ const schema = await getRegistrationSchema(tld, cfg);
35577
+ const required = Array.isArray(schema["required"]) ? schema["required"] : [];
35578
+ if (required.includes("metadata") && !options.metadata) {
35579
+ throw new BrandsightApiError(`Cannot register ${domain}: ${tld} requires TLD-specific metadata; pass registration metadata before validation`);
35580
+ }
35581
+ const privacy = options.privacy ?? false;
35582
+ const agreements = await getLegalAgreements(tld, privacy, cfg);
35583
+ const agreementKeys = agreements.map((a3) => a3.agreementKey).filter(Boolean);
35584
+ const c3 = brandsightContact(contact);
35585
+ const payload = {
35586
+ consent: {
35587
+ agreedAt: new Date().toISOString(),
35588
+ agreedBy: contact.email || cfg.shopperId || "domains-cli",
35589
+ agreementKeys,
35590
+ currency: availability.currency,
35591
+ price,
35592
+ registryPremiumPricing: availability.registryPremiumPricing ?? !!options.premiumPrice
35593
+ },
35594
+ contacts: {
35595
+ admin: c3,
35596
+ billing: c3,
35597
+ registrant: c3,
35598
+ tech: c3
35599
+ },
35600
+ domain,
35601
+ metadata: options.metadata ?? {},
35602
+ nameServers: options.nameservers ?? [],
35603
+ period,
35604
+ privacy,
35605
+ renewAuto: options.autoRenew ?? true
35606
+ };
35607
+ await validateRegistrationRequest(payload, cfg);
35608
+ const result = await domainApiRequest("POST", customerPath(cfg, "/domains/register"), cfg, payload);
35609
+ return {
35610
+ success: true,
35611
+ orderId: result.orderId ?? result.id,
35612
+ operationId: result.operationId ?? result.id,
35613
+ chargedAmount: String(price)
35614
+ };
35615
+ }
35616
+ async function updateNameservers3(domain, nameservers, config) {
35617
+ const cfg = requireDomainConfig(config);
35618
+ const result = await domainApiRequest("PUT", customerPath(cfg, `/domains/${encodeURIComponent(domain)}/nameServers`), cfg, { nameServers: nameservers });
35619
+ return { success: true, operationId: result.operationId ?? result.id };
35620
+ }
35621
+ async function getDnsRecords3(domain, config) {
35622
+ const cfg = requireDomainConfig(config);
35623
+ const records = [];
35624
+ let offset = 0;
35625
+ const limit = 1000;
35626
+ while (true) {
35627
+ const batch = await domainApiRequest("GET", customerPath(cfg, `/domains/${encodeURIComponent(domain)}/records?offset=${offset}&limit=${limit}`), cfg);
35628
+ if (!Array.isArray(batch) || batch.length === 0)
35629
+ break;
35630
+ records.push(...batch);
35631
+ if (batch.length < limit)
35632
+ break;
35633
+ offset++;
35634
+ }
35635
+ return records;
35636
+ }
35637
+ function normalizeBrandsightDnsRecord(record) {
35638
+ return {
35639
+ ...record,
35640
+ ttl: Math.max(record.ttl || 600, 600)
35641
+ };
35642
+ }
35643
+ async function setDnsRecords3(domain, records, config) {
35644
+ const cfg = requireDomainConfig(config);
35645
+ await domainApiRequest("PUT", customerPath(cfg, `/domains/${encodeURIComponent(domain)}/records`), cfg, records.map(normalizeBrandsightDnsRecord));
35646
+ return true;
35273
35647
  }
35274
35648
  async function syncToLocalDb3(dbFns, config) {
35275
35649
  const domains = await listDomains2(config);
@@ -35314,7 +35688,7 @@ function createBrandsightProvider(config) {
35314
35688
  return domains.map((d3) => ({
35315
35689
  domain: d3.domain,
35316
35690
  registrar: "Brandsight",
35317
- created: "",
35691
+ created: d3.created ?? "",
35318
35692
  expires: d3.expires,
35319
35693
  nameservers: d3.nameservers,
35320
35694
  status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
@@ -35328,7 +35702,7 @@ function createBrandsightProvider(config) {
35328
35702
  return {
35329
35703
  domain: d3.domain,
35330
35704
  registrar: "Brandsight",
35331
- created: "",
35705
+ created: d3.created ?? "",
35332
35706
  expires: d3.expires,
35333
35707
  nameservers: d3.nameservers,
35334
35708
  status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
@@ -35339,12 +35713,47 @@ function createBrandsightProvider(config) {
35339
35713
  const result = await renewDomain3(domain, years, cfg);
35340
35714
  return { domain, success: result.success, orderId: result.orderId };
35341
35715
  },
35716
+ async registerDomain(domain, contact, options) {
35717
+ const result = await registerBrandsightDomain(domain, contact, options, cfg);
35718
+ return {
35719
+ domain,
35720
+ success: result.success,
35721
+ orderId: result.orderId,
35722
+ operationId: result.operationId,
35723
+ chargedAmount: result.chargedAmount
35724
+ };
35725
+ },
35726
+ async updateNameservers(domain, nameservers) {
35727
+ const result = await updateNameservers3(domain, nameservers, cfg);
35728
+ return { domain, success: result.success, operationId: result.operationId };
35729
+ },
35730
+ async getDnsRecords(domain) {
35731
+ const records = await getDnsRecords3(domain, cfg);
35732
+ return records.map((r3) => ({
35733
+ type: r3.type,
35734
+ name: r3.name,
35735
+ value: r3.data,
35736
+ ttl: r3.ttl,
35737
+ priority: r3.priority
35738
+ }));
35739
+ },
35740
+ async setDnsRecords(domain, records) {
35741
+ return setDnsRecords3(domain, records.map((r3) => ({
35742
+ type: r3.type,
35743
+ name: r3.name,
35744
+ data: r3.value,
35745
+ ttl: r3.ttl,
35746
+ priority: r3.priority
35747
+ })), cfg);
35748
+ },
35342
35749
  async checkAvailability(domain) {
35343
35750
  const result = await checkAvailability4(domain, cfg);
35344
35751
  return {
35345
35752
  domain: result.domain,
35346
35753
  available: result.available,
35347
- standard_price: result.price,
35754
+ is_premium: result.registryPremiumPricing,
35755
+ premium_price: result.registryPremiumPricing ? result.price : undefined,
35756
+ standard_price: result.registryPremiumPricing ? undefined : result.price,
35348
35757
  currency: result.currency
35349
35758
  };
35350
35759
  },
@@ -35353,7 +35762,7 @@ function createBrandsightProvider(config) {
35353
35762
  }
35354
35763
  };
35355
35764
  }
35356
- var BrandsightApiError, _fetchFn = null, BRANDSIGHT_BASE = "https://api.brandsight.com/v1";
35765
+ var BrandsightApiError, _fetchFn = null, BRANDSIGHT_BASE = "https://api.brandsight.com/v1", BRANDSIGHT_DOMAIN_BASE = "https://api.godaddy.com/v2";
35357
35766
  var init_brandsight = __esm(() => {
35358
35767
  init_env_aliases();
35359
35768
  init_version();
@@ -42048,6 +42457,7 @@ var providerRegistry = new Map([
42048
42457
  configured: false,
42049
42458
  envVars: providerEnvNames("namecheap")
42050
42459
  },
42460
+ createInventory: createNamecheapProvider,
42051
42461
  createRegistrar: createNamecheapProvider,
42052
42462
  createDns: createNamecheapProvider
42053
42463
  }],
@@ -42058,6 +42468,7 @@ var providerRegistry = new Map([
42058
42468
  configured: false,
42059
42469
  envVars: providerEnvNames("godaddy")
42060
42470
  },
42471
+ createInventory: createGoDaddyProvider,
42061
42472
  createRegistrar: createGoDaddyProvider,
42062
42473
  createDns: createGoDaddyProvider
42063
42474
  }],
@@ -42068,6 +42479,7 @@ var providerRegistry = new Map([
42068
42479
  configured: false,
42069
42480
  envVars: providerEnvNames("route53")
42070
42481
  },
42482
+ createInventory: () => createRoute53Provider(),
42071
42483
  createRegistrar: () => createRoute53Provider(),
42072
42484
  createDns: () => createRoute53Provider()
42073
42485
  }],
@@ -42078,16 +42490,19 @@ var providerRegistry = new Map([
42078
42490
  configured: false,
42079
42491
  envVars: providerEnvNames("cloudflare")
42080
42492
  },
42493
+ createInventory: () => createCloudflareProvider(),
42081
42494
  createDns: createCloudflareProvider
42082
42495
  }],
42083
42496
  ["brandsight", {
42084
42497
  info: {
42085
42498
  name: "brandsight",
42086
- type: "registrar",
42499
+ type: "full",
42087
42500
  configured: false,
42088
42501
  envVars: providerEnvNames("brandsight")
42089
42502
  },
42090
- createRegistrar: createBrandsightProvider
42503
+ createInventory: createBrandsightProvider,
42504
+ createRegistrar: createBrandsightProvider,
42505
+ createDns: createBrandsightProvider
42091
42506
  }],
42092
42507
  ["sedo", {
42093
42508
  info: {
@@ -42104,18 +42519,28 @@ function isConfigured(providerName) {
42104
42519
  function getAvailableProviders() {
42105
42520
  return Array.from(providerRegistry.values()).map((e3) => ({
42106
42521
  ...e3.info,
42107
- configured: isConfigured(e3.info.name)
42522
+ configured: isConfigured(e3.info.name),
42523
+ inventory: !!e3.createInventory
42108
42524
  }));
42109
42525
  }
42110
42526
  function getProviderInfo(name) {
42111
42527
  const entry = providerRegistry.get(name.toLowerCase());
42112
42528
  if (!entry)
42113
42529
  return null;
42114
- return { ...entry.info, configured: isConfigured(entry.info.name) };
42530
+ return { ...entry.info, configured: isConfigured(entry.info.name), inventory: !!entry.createInventory };
42115
42531
  }
42116
42532
  function providerHasRegistrar(name) {
42117
42533
  return !!providerRegistry.get(name.toLowerCase())?.createRegistrar;
42118
42534
  }
42535
+ function providerHasInventory(name) {
42536
+ return !!providerRegistry.get(name.toLowerCase())?.createInventory;
42537
+ }
42538
+ function getDomainInventoryProvider(name) {
42539
+ const entry = providerRegistry.get(name.toLowerCase());
42540
+ if (!entry?.createInventory)
42541
+ throw new Error(`No domain inventory provider: ${name}`);
42542
+ return entry.createInventory();
42543
+ }
42119
42544
  function getRegistrarProvider(name) {
42120
42545
  const entry = providerRegistry.get(name.toLowerCase());
42121
42546
  if (!entry?.createRegistrar)
@@ -42129,11 +42554,11 @@ function getDnsProvider(name) {
42129
42554
  return entry.createDns();
42130
42555
  }
42131
42556
  async function syncAll(dbFns) {
42132
- const available = getAvailableProviders().filter((p3) => p3.configured && providerHasRegistrar(p3.name));
42557
+ const available = getAvailableProviders().filter((p3) => p3.configured && providerHasInventory(p3.name));
42133
42558
  const result = { providers: [], totalSynced: 0, totalErrors: [] };
42134
42559
  for (const info of available) {
42135
42560
  try {
42136
- const provider = getRegistrarProvider(info.name);
42561
+ const provider = getDomainInventoryProvider(info.name);
42137
42562
  const syncResult = await provider.syncToLocalDb(dbFns);
42138
42563
  result.providers.push({ name: info.name, result: syncResult });
42139
42564
  result.totalSynced += syncResult.synced;
@@ -42151,14 +42576,16 @@ function autoDetectRegistrar(domain, getDomainByName2) {
42151
42576
  if (!dbDomain?.registrar)
42152
42577
  return null;
42153
42578
  const r3 = dbDomain.registrar.toLowerCase();
42579
+ if (r3.includes("cloudflare dns") || r3.includes("route 53 dns") || r3.includes("route53 dns"))
42580
+ return null;
42154
42581
  if (r3.includes("namecheap"))
42155
42582
  return "namecheap";
42156
42583
  if (r3.includes("godaddy"))
42157
42584
  return "godaddy";
42158
- if (r3.includes("route 53") || r3.includes("route53") || r3.includes("aws"))
42585
+ if (r3.includes("route 53") || r3.includes("route53"))
42159
42586
  return "route53";
42160
42587
  if (r3.includes("cloudflare"))
42161
- return "cloudflare";
42588
+ return null;
42162
42589
  if (r3.includes("brandsight"))
42163
42590
  return "brandsight";
42164
42591
  return null;
@@ -42498,10 +42925,10 @@ WHOIS for ${result.domain} [${result.source}]:`);
42498
42925
  process.exit(1);
42499
42926
  });
42500
42927
  domain.command("sync").description("Sync domains from a provider to the local DB").option("--provider <name>", "Provider name (default: all configured)").action(async (opts) => {
42501
- const providers = opts.provider ? [opts.provider] : getAvailableProviders().filter((p3) => p3.configured && (p3.type === "registrar" || p3.type === "full")).map((p3) => p3.name);
42928
+ const providers = opts.provider ? [opts.provider] : getAvailableProviders().filter((p3) => p3.configured && providerHasInventory(p3.name)).map((p3) => p3.name);
42502
42929
  for (const name of providers) {
42503
42930
  try {
42504
- const provider = getRegistrarProvider(name);
42931
+ const provider = getDomainInventoryProvider(name);
42505
42932
  const result = await provider.syncToLocalDb({ getDomainByName, createDomain, updateDomain });
42506
42933
  console.log(`\u2713 [${name}] Synced ${result.synced} (${result.created} new, ${result.updated} updated)`);
42507
42934
  if (result.errors.length > 0)
@@ -43354,6 +43781,10 @@ function registerMonitorCommand(program2) {
43354
43781
  }
43355
43782
 
43356
43783
  // src/cli/commands/provider.ts
43784
+ function isAwsAccessDenied(error) {
43785
+ const msg = error instanceof Error ? error.message : String(error);
43786
+ return msg.includes("AccessDenied") || msg.includes("route53domains:ListDomains");
43787
+ }
43357
43788
  function registerProviderCommand(program2) {
43358
43789
  const provider = program2.command("provider").description("Configure and test registrar/DNS providers");
43359
43790
  provider.command("list").description("Show all providers and their configuration status").option("-j, --json", "Output JSON").action((opts) => {
@@ -43367,7 +43798,8 @@ Registrar / DNS Providers:`);
43367
43798
  for (const p3 of providers) {
43368
43799
  const status = p3.configured ? "\u2713 configured" : "\u2717 not configured";
43369
43800
  const type = p3.type === "full" ? "registrar + dns" : p3.type;
43370
- console.log(` ${p3.name.padEnd(12)} [${type}] ${status}`);
43801
+ const capabilities = [type, p3.inventory ? "inventory" : null].filter(Boolean).join(", ");
43802
+ console.log(` ${p3.name.padEnd(12)} [${capabilities}] ${status}`);
43371
43803
  if (!p3.configured) {
43372
43804
  console.log(` Missing: ${p3.envVars.join(", ")}`);
43373
43805
  }
@@ -43408,20 +43840,45 @@ Registrar / DNS Providers:`);
43408
43840
  let registrarOk = null;
43409
43841
  let dnsOk = null;
43410
43842
  let marketplaceOk = null;
43843
+ const notes = [];
43411
43844
  try {
43412
43845
  if (info.type === "registrar" || info.type === "full") {
43413
- const reg = getRegistrarProvider(providerName);
43414
- await reg.listDomains();
43415
- registrarOk = true;
43416
- if (!opts.json)
43417
- console.log("\u2713 Registrar connection OK");
43846
+ if (providerName === "route53") {
43847
+ try {
43848
+ const { listRegisteredDomains: listRegisteredDomains2 } = await Promise.resolve().then(() => (init_route53(), exports_route53));
43849
+ await listRegisteredDomains2();
43850
+ registrarOk = true;
43851
+ if (!opts.json)
43852
+ console.log("\u2713 Registrar connection OK");
43853
+ } catch (error) {
43854
+ if (!isAwsAccessDenied(error))
43855
+ throw error;
43856
+ registrarOk = false;
43857
+ notes.push("route53domains-listdomains-access-denied");
43858
+ if (!opts.json)
43859
+ console.log("\u2022 Route53 Domains registrar API is not available for these AWS credentials");
43860
+ }
43861
+ } else {
43862
+ const reg = getRegistrarProvider(providerName);
43863
+ await reg.listDomains();
43864
+ registrarOk = true;
43865
+ if (!opts.json)
43866
+ console.log("\u2713 Registrar connection OK");
43867
+ }
43418
43868
  }
43419
43869
  if (info.type === "dns" || info.type === "full") {
43420
- const dns = getDnsProvider(providerName);
43421
- await dns.getDnsRecords("__test_nonexistent_domain__.invalid");
43422
- dnsOk = true;
43423
- if (!opts.json)
43424
- console.log("\u2713 DNS connection OK");
43870
+ if (providerName === "brandsight" && registrarOk) {
43871
+ dnsOk = null;
43872
+ notes.push("dns-not-probed-with-synthetic-invalid-domain");
43873
+ if (!opts.json)
43874
+ console.log("\u2022 DNS not probed with synthetic invalid domain");
43875
+ } else {
43876
+ const dns = getDnsProvider(providerName);
43877
+ await dns.getDnsRecords("__test_nonexistent_domain__.invalid");
43878
+ dnsOk = true;
43879
+ if (!opts.json)
43880
+ console.log("\u2713 DNS connection OK");
43881
+ }
43425
43882
  }
43426
43883
  if (info.type === "marketplace") {
43427
43884
  if (providerName !== "sedo")
@@ -43440,7 +43897,8 @@ Registrar / DNS Providers:`);
43440
43897
  type: info.type,
43441
43898
  registrar_ok: registrarOk,
43442
43899
  dns_ok: dnsOk,
43443
- marketplace_ok: marketplaceOk
43900
+ marketplace_ok: marketplaceOk,
43901
+ ...notes.length > 0 ? { notes } : {}
43444
43902
  }, null, 2));
43445
43903
  }
43446
43904
  } catch (e3) {
@@ -43455,7 +43913,8 @@ Registrar / DNS Providers:`);
43455
43913
  registrar_ok: registrarOk,
43456
43914
  dns_ok: true,
43457
43915
  marketplace_ok: marketplaceOk,
43458
- note: "connection-ok-no-domains-yet"
43916
+ note: "connection-ok-no-domains-yet",
43917
+ ...notes.length > 0 ? { notes } : {}
43459
43918
  }, null, 2));
43460
43919
  } else {
43461
43920
  console.log("\u2713 Connection OK (no domains yet)");
@@ -43487,6 +43946,18 @@ init_domains();
43487
43946
  function registrarProviderNames() {
43488
43947
  return getAvailableProviders().filter((p3) => providerHasRegistrar(p3.name)).map((p3) => p3.name).join(", ");
43489
43948
  }
43949
+ function inventoryProviderNames() {
43950
+ return getAvailableProviders().filter((p3) => providerHasInventory(p3.name)).map((p3) => p3.name).join(", ");
43951
+ }
43952
+ function requireInventoryProvider(name) {
43953
+ const info = getProviderInfo(name);
43954
+ if (!info) {
43955
+ throw new Error(`Unknown provider: ${name}. Supported domain inventory providers: ${inventoryProviderNames()}`);
43956
+ }
43957
+ if (!providerHasInventory(name)) {
43958
+ throw new Error(`${name} is a ${info.type} provider without domain inventory sync. Supported domain inventory providers: ${inventoryProviderNames()}`);
43959
+ }
43960
+ }
43490
43961
  function requireRegistrarProvider(name) {
43491
43962
  const info = getProviderInfo(name);
43492
43963
  if (!info) {
@@ -43505,14 +43976,15 @@ function registerProviderCommands(program2) {
43505
43976
  console.log("Providers:");
43506
43977
  for (const p3 of providers) {
43507
43978
  const status = p3.configured ? "CONFIGURED" : "not configured";
43508
- console.log(` ${p3.name} [${p3.type}]: ${status}`);
43979
+ const capabilities = [p3.type, p3.inventory ? "inventory" : null].filter(Boolean).join(", ");
43980
+ console.log(` ${p3.name} [${capabilities}]: ${status}`);
43509
43981
  if (!p3.configured) {
43510
43982
  console.log(` Accepted env: ${p3.envVars.join(", ")}`);
43511
43983
  }
43512
43984
  }
43513
43985
  }
43514
43986
  });
43515
- program2.command("sync").description("Sync domains from a registrar provider to local DB").option("--provider <provider>", "Provider name").option("--all", "Sync from all configured registrar providers").option("--json", "Output as JSON", false).action(async (opts) => {
43987
+ program2.command("sync").description("Sync domains from a domain inventory provider to local DB").option("--provider <provider>", "Provider name").option("--all", "Sync from all configured domain inventory providers").option("--json", "Output as JSON", false).action(async (opts) => {
43516
43988
  if (opts.all) {
43517
43989
  try {
43518
43990
  const result = await syncAll({
@@ -43546,8 +44018,8 @@ function registerProviderCommands(program2) {
43546
44018
  process.exit(1);
43547
44019
  }
43548
44020
  try {
43549
- requireRegistrarProvider(provider);
43550
- const result = await getRegistrarProvider(provider).syncToLocalDb({
44021
+ requireInventoryProvider(provider);
44022
+ const result = await getDomainInventoryProvider(provider).syncToLocalDb({
43551
44023
  getDomainByName,
43552
44024
  createDomain,
43553
44025
  updateDomain