@hasna/domains 0.0.12 → 0.0.14

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
@@ -19207,7 +19207,7 @@ var require_package = __commonJS((exports, module) => {
19207
19207
 
19208
19208
  // node_modules/@aws-sdk/util-user-agent-node/dist-cjs/index.js
19209
19209
  var require_dist_cjs41 = __commonJS((exports) => {
19210
- var __dirname = "/Users/hasna/Workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
19210
+ var __dirname = "/home/hasna/workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
19211
19211
  var node_os = __require("os");
19212
19212
  var node_process = __require("process");
19213
19213
  var utilConfigProvider = require_dist_cjs33();
@@ -25599,6 +25599,322 @@ var init_dist_es9 = __esm(() => {
25599
25599
  init_fromIni();
25600
25600
  });
25601
25601
 
25602
+ // src/lib/cloudflare-auth.ts
25603
+ function resolveCloudflareConfig(env = process.env) {
25604
+ const accountId = env["CLOUDFLARE_ACCOUNT_ID"] || env["HASNAXYZ_CLOUDFLARE_LIVE_ACCOUNT_ID"] || undefined;
25605
+ if (env["CLOUDFLARE_API_TOKEN"]) {
25606
+ return { apiToken: env["CLOUDFLARE_API_TOKEN"], accountId };
25607
+ }
25608
+ if (env["CLOUDFLARE_API_KEY"] && env["CLOUDFLARE_EMAIL"]) {
25609
+ return { apiKey: env["CLOUDFLARE_API_KEY"], email: env["CLOUDFLARE_EMAIL"], accountId };
25610
+ }
25611
+ if (env["HASNAXYZ_CLOUDFLARE_LIVE_API_KEY"] && env["HASNAXYZ_CLOUDFLARE_LIVE_EMAIL"]) {
25612
+ return {
25613
+ apiKey: env["HASNAXYZ_CLOUDFLARE_LIVE_API_KEY"],
25614
+ email: env["HASNAXYZ_CLOUDFLARE_LIVE_EMAIL"],
25615
+ accountId
25616
+ };
25617
+ }
25618
+ return accountId ? { accountId } : {};
25619
+ }
25620
+ function cloudflareAuthHeaders(cfg) {
25621
+ if (cfg.apiToken)
25622
+ return { Authorization: `Bearer ${cfg.apiToken}` };
25623
+ if (cfg.apiKey && cfg.email)
25624
+ return { "X-Auth-Key": cfg.apiKey, "X-Auth-Email": cfg.email };
25625
+ throw new Error("Cloudflare credentials not configured. Set CLOUDFLARE_API_TOKEN, or CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL.");
25626
+ }
25627
+
25628
+ // src/lib/brandsight.ts
25629
+ function resolveBrandsightConfig(env = process.env) {
25630
+ return {
25631
+ apiKey: env["BRANDSIGHT_API_KEY"] ?? env["HASNAXYZ_BRANDSIGHT_LIVE_API_KEY"] ?? "",
25632
+ apiSecret: env["BRANDSIGHT_API_SECRET"] ?? env["HASNAXYZ_BRANDSIGHT_LIVE_API_SECRET"],
25633
+ customerId: env["BRANDSIGHT_CUSTOMER_ID"] ?? env["HASNAXYZ_BRANDSIGHT_LIVE_CUSTOMER_ID"],
25634
+ shopperId: env["BRANDSIGHT_SHOPPER_ID"] ?? env["HASNAXYZ_BRANDSIGHT_LIVE_SHOPPER_ID"],
25635
+ accountId: env["BRANDSIGHT_ACCOUNT_ID"] ?? env["HASNAXYZ_BRANDSIGHT_LIVE_CUSTOMER_ID"]
25636
+ };
25637
+ }
25638
+ function getApiKey() {
25639
+ const key = process.env["BRANDSIGHT_API_KEY"];
25640
+ if (!key) {
25641
+ throw new BrandsightApiError("BRANDSIGHT_API_KEY environment variable is not set");
25642
+ }
25643
+ return key;
25644
+ }
25645
+ function getConfig4() {
25646
+ return {
25647
+ apiKey: process.env["BRANDSIGHT_API_KEY"] ?? "",
25648
+ accountId: process.env["BRANDSIGHT_ACCOUNT_ID"]
25649
+ };
25650
+ }
25651
+ async function apiRequest3(method, path, apiKey, body, baseUrl = BRANDSIGHT_BASE) {
25652
+ const fetchFn = _fetchFn || globalThis.fetch;
25653
+ const url = `${baseUrl}${path}`;
25654
+ const headers = {
25655
+ Authorization: `Bearer ${apiKey}`,
25656
+ "Content-Type": "application/json",
25657
+ Accept: "application/json",
25658
+ "User-Agent": USER_AGENT
25659
+ };
25660
+ try {
25661
+ const response = await fetchFn(url, {
25662
+ method,
25663
+ headers,
25664
+ body: body ? JSON.stringify(body) : undefined,
25665
+ signal: AbortSignal.timeout(15000)
25666
+ });
25667
+ if (!response.ok) {
25668
+ throw new BrandsightApiError(`Brandsight API ${method} ${path} failed with status ${response.status}`, response.status, await response.text());
25669
+ }
25670
+ const data = await response.json();
25671
+ return { data, stub: false };
25672
+ } catch (error) {
25673
+ if (error instanceof BrandsightApiError)
25674
+ throw error;
25675
+ return { data: null, stub: true };
25676
+ }
25677
+ }
25678
+ async function apiGet(path, apiKey, baseUrl = BRANDSIGHT_BASE) {
25679
+ const fetchFn = _fetchFn || globalThis.fetch;
25680
+ const url = `${baseUrl}${path}`;
25681
+ const headers = {
25682
+ Authorization: `Bearer ${apiKey}`,
25683
+ "Content-Type": "application/json",
25684
+ Accept: "application/json",
25685
+ "User-Agent": USER_AGENT
25686
+ };
25687
+ try {
25688
+ const response = await fetchFn(url, {
25689
+ method: "GET",
25690
+ headers,
25691
+ signal: AbortSignal.timeout(15000)
25692
+ });
25693
+ if (!response.ok) {
25694
+ throw new BrandsightApiError(`Brandsight API GET ${path} failed with status ${response.status}`, response.status, await response.text());
25695
+ }
25696
+ const data = await response.json();
25697
+ return { data, stub: false };
25698
+ } catch (error) {
25699
+ if (error instanceof BrandsightApiError)
25700
+ throw error;
25701
+ return { data: null, stub: true };
25702
+ }
25703
+ }
25704
+ function generateStubAlerts(brandName) {
25705
+ const now = new Date().toISOString();
25706
+ return [
25707
+ { domain: `${brandName}-deals.com`, type: "keyword", registered_at: now },
25708
+ { domain: `${brandName.replace(/a/gi, "4").replace(/e/gi, "3")}.com`, type: "homoglyph", registered_at: now },
25709
+ { domain: `${brandName}s.com`, type: "typosquat", registered_at: now }
25710
+ ];
25711
+ }
25712
+ function generateStubSimilarDomains(domain) {
25713
+ const base = domain.replace(/\.[^.]+$/, "");
25714
+ const tld = domain.slice(base.length);
25715
+ return [
25716
+ `${base}-online${tld}`,
25717
+ `${base}s${tld}`,
25718
+ `${base.replace(/a/gi, "4")}${tld}`,
25719
+ `${base}-app${tld}`,
25720
+ `get${base}${tld}`
25721
+ ];
25722
+ }
25723
+ function generateStubThreatAssessment(domain) {
25724
+ return {
25725
+ domain,
25726
+ risk_level: "low",
25727
+ threats: [],
25728
+ recommendation: "No immediate threats detected. Continue routine monitoring."
25729
+ };
25730
+ }
25731
+ async function monitorBrand(brandName) {
25732
+ const apiKey = getApiKey();
25733
+ const result = await apiGet(`/brands/${encodeURIComponent(brandName)}/monitor`, apiKey);
25734
+ if (result.stub) {
25735
+ return { brand: brandName, alerts: generateStubAlerts(brandName), stub: true };
25736
+ }
25737
+ return { brand: brandName, alerts: result.data.alerts, stub: false };
25738
+ }
25739
+ async function getSimilarDomains(domain) {
25740
+ const apiKey = getApiKey();
25741
+ const result = await apiGet(`/domains/${encodeURIComponent(domain)}/similar`, apiKey);
25742
+ if (result.stub) {
25743
+ return { domain, similar: generateStubSimilarDomains(domain), stub: true };
25744
+ }
25745
+ return { domain, similar: result.data.similar, stub: false };
25746
+ }
25747
+ async function getThreatAssessment(domain) {
25748
+ const apiKey = getApiKey();
25749
+ const result = await apiGet(`/domains/${encodeURIComponent(domain)}/threats`, apiKey);
25750
+ if (result.stub) {
25751
+ return { ...generateStubThreatAssessment(domain), stub: true };
25752
+ }
25753
+ return { ...result.data, stub: false };
25754
+ }
25755
+ async function listDomains2(config) {
25756
+ const cfg = config ?? getConfig4();
25757
+ const result = await apiGet("/portfolio/domains", cfg.apiKey);
25758
+ if (result.stub)
25759
+ return [];
25760
+ return result.data.domains ?? [];
25761
+ }
25762
+ async function getDomainInfo3(domain, config) {
25763
+ const cfg = config ?? getConfig4();
25764
+ const result = await apiGet(`/portfolio/domains/${encodeURIComponent(domain)}`, cfg.apiKey);
25765
+ if (result.stub)
25766
+ return null;
25767
+ return result.data;
25768
+ }
25769
+ async function checkAvailability4(domain, config) {
25770
+ const cfg = config ?? getConfig4();
25771
+ const result = await apiGet(`/domains/check?domain=${encodeURIComponent(domain)}`, cfg.apiKey);
25772
+ if (result.stub)
25773
+ return { domain, available: false };
25774
+ return result.data;
25775
+ }
25776
+ async function renewDomain3(domain, years = 1, config) {
25777
+ const cfg = config ?? getConfig4();
25778
+ const result = await apiRequest3("POST", `/portfolio/domains/${encodeURIComponent(domain)}/renew`, cfg.apiKey, { years });
25779
+ if (result.stub)
25780
+ return { success: false };
25781
+ return { success: true, orderId: result.data.orderId };
25782
+ }
25783
+ async function syncToLocalDb3(dbFns, config) {
25784
+ const domains = await listDomains2(config);
25785
+ let synced = 0, created = 0, updated = 0;
25786
+ const errors = [];
25787
+ for (const d3 of domains) {
25788
+ try {
25789
+ const existing = dbFns.getDomainByName(d3.domain);
25790
+ if (existing) {
25791
+ dbFns.updateDomain(existing.id, {
25792
+ registrar: "Brandsight",
25793
+ expires_at: d3.expires || undefined,
25794
+ auto_renew: d3.auto_renew,
25795
+ status: "active",
25796
+ nameservers: d3.nameservers
25797
+ });
25798
+ updated++;
25799
+ } else {
25800
+ dbFns.createDomain({
25801
+ name: d3.domain,
25802
+ registrar: "Brandsight",
25803
+ expires_at: d3.expires || undefined,
25804
+ auto_renew: d3.auto_renew,
25805
+ status: "active",
25806
+ nameservers: d3.nameservers
25807
+ });
25808
+ created++;
25809
+ }
25810
+ synced++;
25811
+ } catch (err) {
25812
+ errors.push(`${d3.domain}: ${err instanceof Error ? err.message : String(err)}`);
25813
+ }
25814
+ }
25815
+ return { synced, created, updated, errors };
25816
+ }
25817
+ function createBrandsightProvider(config) {
25818
+ const cfg = config ?? getConfig4();
25819
+ return {
25820
+ name: "brandsight",
25821
+ async listDomains() {
25822
+ const domains = await listDomains2(cfg);
25823
+ return domains.map((d3) => ({
25824
+ domain: d3.domain,
25825
+ registrar: "Brandsight",
25826
+ created: "",
25827
+ expires: d3.expires,
25828
+ nameservers: d3.nameservers,
25829
+ status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
25830
+ auto_renew: d3.auto_renew
25831
+ }));
25832
+ },
25833
+ async getDomainInfo(domain) {
25834
+ const d3 = await getDomainInfo3(domain, cfg);
25835
+ if (!d3)
25836
+ throw new Error(`Domain not found in Brandsight: ${domain}`);
25837
+ return {
25838
+ domain: d3.domain,
25839
+ registrar: "Brandsight",
25840
+ created: "",
25841
+ expires: d3.expires,
25842
+ nameservers: d3.nameservers,
25843
+ status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
25844
+ auto_renew: d3.auto_renew
25845
+ };
25846
+ },
25847
+ async renewDomain(domain) {
25848
+ const result = await renewDomain3(domain, 1, cfg);
25849
+ return { domain, success: result.success, orderId: result.orderId };
25850
+ },
25851
+ async checkAvailability(domain) {
25852
+ const result = await checkAvailability4(domain, cfg);
25853
+ return {
25854
+ domain: result.domain,
25855
+ available: result.available,
25856
+ standard_price: result.price,
25857
+ currency: result.currency
25858
+ };
25859
+ },
25860
+ async syncToLocalDb(dbFns) {
25861
+ return syncToLocalDb3(dbFns, cfg);
25862
+ }
25863
+ };
25864
+ }
25865
+ var BrandsightApiError, _fetchFn = null, BRANDSIGHT_BASE = "https://api.brandsight.com/v1";
25866
+ var init_brandsight = __esm(() => {
25867
+ init_version();
25868
+ BrandsightApiError = class BrandsightApiError extends Error {
25869
+ statusCode;
25870
+ responseBody;
25871
+ constructor(message, statusCode, responseBody) {
25872
+ super(message);
25873
+ this.statusCode = statusCode;
25874
+ this.responseBody = responseBody;
25875
+ this.name = "BrandsightApiError";
25876
+ }
25877
+ };
25878
+ });
25879
+
25880
+ // src/lib/creds-check.ts
25881
+ var exports_creds_check = {};
25882
+ __export(exports_creds_check, {
25883
+ checkProvisioningCredentials: () => checkProvisioningCredentials
25884
+ });
25885
+ function checkProvisioningCredentials(env = process.env) {
25886
+ const out = [];
25887
+ const hasAws = !!(env["AWS_ACCESS_KEY_ID"] && env["AWS_SECRET_ACCESS_KEY"]) || !!env["AWS_PROFILE"];
25888
+ out.push({
25889
+ provider: "route53",
25890
+ configured: hasAws,
25891
+ mode: env["AWS_PROFILE"] ? `profile:${env["AWS_PROFILE"]}` : hasAws ? "access-keys" : "none",
25892
+ detail: hasAws ? "AWS credentials present (region us-east-1 for Route53 Domains)" : "Set AWS_PROFILE or AWS_ACCESS_KEY_ID/SECRET"
25893
+ });
25894
+ const cf = resolveCloudflareConfig(env);
25895
+ const cfMode = cf.apiToken ? "token" : cf.apiKey && cf.email ? "global-key" : "none";
25896
+ out.push({
25897
+ provider: "cloudflare",
25898
+ configured: cfMode !== "none",
25899
+ mode: cfMode + (cf.accountId ? "+account" : ""),
25900
+ detail: cfMode === "none" ? "Set CLOUDFLARE_API_TOKEN or CLOUDFLARE_API_KEY+CLOUDFLARE_EMAIL" : cf.accountId ? "ok" : "missing CLOUDFLARE_ACCOUNT_ID (needed to create zones)"
25901
+ });
25902
+ const bs = resolveBrandsightConfig(env);
25903
+ const bsConfigured = !!(bs.apiKey && bs.apiSecret && bs.customerId);
25904
+ out.push({
25905
+ provider: "brandsight",
25906
+ configured: bsConfigured,
25907
+ mode: bsConfigured ? "full-creds" : "none",
25908
+ detail: "enterprise/contract-only (gated) \u2014 not used for automated purchase"
25909
+ });
25910
+ const gd = !!(env["GODADDY_API_KEY"] && env["GODADDY_API_SECRET"]);
25911
+ out.push({ provider: "godaddy", configured: gd, mode: gd ? "key+secret" : "none", detail: "retail API gated for purchase/DNS since 2024" });
25912
+ return out;
25913
+ }
25914
+ var init_creds_check = __esm(() => {
25915
+ init_brandsight();
25916
+ });
25917
+
25602
25918
  // src/db/domain-history.ts
25603
25919
  function rowToDomainHistory(row) {
25604
25920
  return {
@@ -31990,6 +32306,7 @@ var _Ex = "Expiry";
31990
32306
  var _F2 = "Fax";
31991
32307
  var _FC = "FilterCondition";
31992
32308
  var _FCi = "FilterConditions";
32309
+ var _FIAK = "FIAuthKey";
31993
32310
  var _FN = "FirstName";
31994
32311
  var _Fl2 = "Flags";
31995
32312
  var _GDD = "GetDomainDetail";
@@ -32063,6 +32380,9 @@ var _TTD = "TagsToDelete";
32063
32380
  var _Tl = "Tld";
32064
32381
  var _Ty = "Type";
32065
32382
  var _UD = "UpdatedDate";
32383
+ var _UDN = "UpdateDomainNameservers";
32384
+ var _UDNR = "UpdateDomainNameserversRequest";
32385
+ var _UDNRp = "UpdateDomainNameserversResponse";
32066
32386
  var _UTLD = "UnsupportedTLD";
32067
32387
  var _V2 = "Value";
32068
32388
  var _Va = "Values";
@@ -32153,6 +32473,7 @@ var ContactNumber = [0, n02, _CNo2, 8, 0];
32153
32473
  var CountryCode = [0, n02, _CC2, 8, 0];
32154
32474
  var Email = [0, n02, _E, 8, 0];
32155
32475
  var ExtraParamValue = [0, n02, _EPV, 8, 0];
32476
+ var FIAuthKey = [0, n02, _FIAK, 8, 0];
32156
32477
  var State = [0, n02, _S2, 8, 0];
32157
32478
  var ZipCode = [0, n02, _ZC, 8, 0];
32158
32479
  var CheckDomainAvailabilityRequest$ = [
@@ -32349,8 +32670,25 @@ var SortCondition$ = [
32349
32670
  [0, 0],
32350
32671
  2
32351
32672
  ];
32352
- var DnssecKeyList = [
32353
- 1,
32673
+ var UpdateDomainNameserversRequest$ = [
32674
+ 3,
32675
+ n02,
32676
+ _UDNR,
32677
+ 0,
32678
+ [_DN, _Na2, _FIAK],
32679
+ [0, () => NameserverList, [() => FIAuthKey, 0]],
32680
+ 2
32681
+ ];
32682
+ var UpdateDomainNameserversResponse$ = [
32683
+ 3,
32684
+ n02,
32685
+ _UDNRp,
32686
+ 0,
32687
+ [_OI],
32688
+ [0]
32689
+ ];
32690
+ var DnssecKeyList = [
32691
+ 1,
32354
32692
  n02,
32355
32693
  _DKL,
32356
32694
  0,
@@ -32448,6 +32786,14 @@ var RegisterDomain$ = [
32448
32786
  () => RegisterDomainRequest$,
32449
32787
  () => RegisterDomainResponse$
32450
32788
  ];
32789
+ var UpdateDomainNameservers$ = [
32790
+ 9,
32791
+ n02,
32792
+ _UDN,
32793
+ 0,
32794
+ () => UpdateDomainNameserversRequest$,
32795
+ () => UpdateDomainNameserversResponse$
32796
+ ];
32451
32797
 
32452
32798
  // node_modules/@aws-sdk/client-route-53-domains/dist-es/runtimeConfig.shared.js
32453
32799
  var getRuntimeConfig3 = (config) => {
@@ -32652,6 +32998,14 @@ class RegisterDomainCommand extends import_smithy_client23.Command.classBuilder(
32652
32998
  }).s("Route53Domains_v20140515", "RegisterDomain", {}).n("Route53DomainsClient", "RegisterDomainCommand").sc(RegisterDomain$).build() {
32653
32999
  }
32654
33000
 
33001
+ // node_modules/@aws-sdk/client-route-53-domains/dist-es/commands/UpdateDomainNameserversCommand.js
33002
+ var import_middleware_endpoint15 = __toESM(require_dist_cjs39(), 1);
33003
+ var import_smithy_client24 = __toESM(require_dist_cjs27(), 1);
33004
+ class UpdateDomainNameserversCommand extends import_smithy_client24.Command.classBuilder().ep(commonParams2).m(function(Command2, cs, config, o3) {
33005
+ return [import_middleware_endpoint15.getEndpointPlugin(config, Command2.getEndpointParameterInstructions())];
33006
+ }).s("Route53Domains_v20140515", "UpdateDomainNameservers", {}).n("Route53DomainsClient", "UpdateDomainNameserversCommand").sc(UpdateDomainNameservers$).build() {
33007
+ }
33008
+
32655
33009
  // src/lib/route53.ts
32656
33010
  function getConfig2() {
32657
33011
  return {
@@ -32751,6 +33105,17 @@ async function getDomainDetail(domain, config) {
32751
33105
  nameservers: (result.Nameservers ?? []).map((ns) => ns.Name ?? "").filter(Boolean)
32752
33106
  };
32753
33107
  }
33108
+ async function updateNameservers(domain, nameservers, config, client) {
33109
+ if (!nameservers.length) {
33110
+ throw new Error("updateNameservers requires at least one nameserver");
33111
+ }
33112
+ const domains = client ?? makeClients(config).domains;
33113
+ const result = await domains.send(new UpdateDomainNameserversCommand({
33114
+ DomainName: domain,
33115
+ Nameservers: nameservers.map((name) => ({ Name: name }))
33116
+ }));
33117
+ return { operationId: result.OperationId ?? "" };
33118
+ }
32754
33119
  async function listRegisteredDomains(config) {
32755
33120
  const { domains } = makeClients(config);
32756
33121
  const all = [];
@@ -33036,15 +33401,10 @@ function createRoute53Provider(config) {
33036
33401
 
33037
33402
  // src/lib/cloudflare.ts
33038
33403
  function getConfig3() {
33039
- return {
33040
- apiToken: process.env["CLOUDFLARE_API_TOKEN"],
33041
- accountId: process.env["CLOUDFLARE_ACCOUNT_ID"]
33042
- };
33404
+ return resolveCloudflareConfig();
33043
33405
  }
33044
33406
  function checkCredentials2(cfg) {
33045
- if (!cfg.apiToken) {
33046
- throw new Error("Cloudflare credentials not configured. Set CLOUDFLARE_API_TOKEN environment variable.");
33047
- }
33407
+ cloudflareAuthHeaders(cfg);
33048
33408
  }
33049
33409
  var CF_BASE = "https://api.cloudflare.com/client/v4";
33050
33410
  async function cfFetch(path, opts = {}) {
@@ -33053,7 +33413,7 @@ async function cfFetch(path, opts = {}) {
33053
33413
  const res = await fetch(`${CF_BASE}${path}`, {
33054
33414
  method: opts.method ?? "GET",
33055
33415
  headers: {
33056
- Authorization: `Bearer ${cfg.apiToken}`,
33416
+ ...cloudflareAuthHeaders(cfg),
33057
33417
  "Content-Type": "application/json"
33058
33418
  },
33059
33419
  body: opts.body ? JSON.stringify(opts.body) : undefined
@@ -33160,250 +33520,8 @@ function createCloudflareProvider(config) {
33160
33520
  };
33161
33521
  }
33162
33522
 
33163
- // src/lib/brandsight.ts
33164
- init_version();
33165
-
33166
- class BrandsightApiError extends Error {
33167
- statusCode;
33168
- responseBody;
33169
- constructor(message, statusCode, responseBody) {
33170
- super(message);
33171
- this.statusCode = statusCode;
33172
- this.responseBody = responseBody;
33173
- this.name = "BrandsightApiError";
33174
- }
33175
- }
33176
- var _fetchFn = null;
33177
- function getApiKey() {
33178
- const key = process.env["BRANDSIGHT_API_KEY"];
33179
- if (!key) {
33180
- throw new BrandsightApiError("BRANDSIGHT_API_KEY environment variable is not set");
33181
- }
33182
- return key;
33183
- }
33184
- function getConfig4() {
33185
- return {
33186
- apiKey: process.env["BRANDSIGHT_API_KEY"] ?? "",
33187
- accountId: process.env["BRANDSIGHT_ACCOUNT_ID"]
33188
- };
33189
- }
33190
- var BRANDSIGHT_BASE = "https://api.brandsight.com/v1";
33191
- async function apiRequest3(method, path, apiKey, body, baseUrl = BRANDSIGHT_BASE) {
33192
- const fetchFn = _fetchFn || globalThis.fetch;
33193
- const url = `${baseUrl}${path}`;
33194
- const headers = {
33195
- Authorization: `Bearer ${apiKey}`,
33196
- "Content-Type": "application/json",
33197
- Accept: "application/json",
33198
- "User-Agent": USER_AGENT
33199
- };
33200
- try {
33201
- const response = await fetchFn(url, {
33202
- method,
33203
- headers,
33204
- body: body ? JSON.stringify(body) : undefined,
33205
- signal: AbortSignal.timeout(15000)
33206
- });
33207
- if (!response.ok) {
33208
- throw new BrandsightApiError(`Brandsight API ${method} ${path} failed with status ${response.status}`, response.status, await response.text());
33209
- }
33210
- const data = await response.json();
33211
- return { data, stub: false };
33212
- } catch (error) {
33213
- if (error instanceof BrandsightApiError)
33214
- throw error;
33215
- return { data: null, stub: true };
33216
- }
33217
- }
33218
- async function apiGet(path, apiKey, baseUrl = BRANDSIGHT_BASE) {
33219
- const fetchFn = _fetchFn || globalThis.fetch;
33220
- const url = `${baseUrl}${path}`;
33221
- const headers = {
33222
- Authorization: `Bearer ${apiKey}`,
33223
- "Content-Type": "application/json",
33224
- Accept: "application/json",
33225
- "User-Agent": USER_AGENT
33226
- };
33227
- try {
33228
- const response = await fetchFn(url, {
33229
- method: "GET",
33230
- headers,
33231
- signal: AbortSignal.timeout(15000)
33232
- });
33233
- if (!response.ok) {
33234
- throw new BrandsightApiError(`Brandsight API GET ${path} failed with status ${response.status}`, response.status, await response.text());
33235
- }
33236
- const data = await response.json();
33237
- return { data, stub: false };
33238
- } catch (error) {
33239
- if (error instanceof BrandsightApiError)
33240
- throw error;
33241
- return { data: null, stub: true };
33242
- }
33243
- }
33244
- function generateStubAlerts(brandName) {
33245
- const now = new Date().toISOString();
33246
- return [
33247
- { domain: `${brandName}-deals.com`, type: "keyword", registered_at: now },
33248
- { domain: `${brandName.replace(/a/gi, "4").replace(/e/gi, "3")}.com`, type: "homoglyph", registered_at: now },
33249
- { domain: `${brandName}s.com`, type: "typosquat", registered_at: now }
33250
- ];
33251
- }
33252
- function generateStubSimilarDomains(domain) {
33253
- const base = domain.replace(/\.[^.]+$/, "");
33254
- const tld = domain.slice(base.length);
33255
- return [
33256
- `${base}-online${tld}`,
33257
- `${base}s${tld}`,
33258
- `${base.replace(/a/gi, "4")}${tld}`,
33259
- `${base}-app${tld}`,
33260
- `get${base}${tld}`
33261
- ];
33262
- }
33263
- function generateStubThreatAssessment(domain) {
33264
- return {
33265
- domain,
33266
- risk_level: "low",
33267
- threats: [],
33268
- recommendation: "No immediate threats detected. Continue routine monitoring."
33269
- };
33270
- }
33271
- async function monitorBrand(brandName) {
33272
- const apiKey = getApiKey();
33273
- const result = await apiGet(`/brands/${encodeURIComponent(brandName)}/monitor`, apiKey);
33274
- if (result.stub) {
33275
- return { brand: brandName, alerts: generateStubAlerts(brandName), stub: true };
33276
- }
33277
- return { brand: brandName, alerts: result.data.alerts, stub: false };
33278
- }
33279
- async function getSimilarDomains(domain) {
33280
- const apiKey = getApiKey();
33281
- const result = await apiGet(`/domains/${encodeURIComponent(domain)}/similar`, apiKey);
33282
- if (result.stub) {
33283
- return { domain, similar: generateStubSimilarDomains(domain), stub: true };
33284
- }
33285
- return { domain, similar: result.data.similar, stub: false };
33286
- }
33287
- async function getThreatAssessment(domain) {
33288
- const apiKey = getApiKey();
33289
- const result = await apiGet(`/domains/${encodeURIComponent(domain)}/threats`, apiKey);
33290
- if (result.stub) {
33291
- return { ...generateStubThreatAssessment(domain), stub: true };
33292
- }
33293
- return { ...result.data, stub: false };
33294
- }
33295
- async function listDomains2(config) {
33296
- const cfg = config ?? getConfig4();
33297
- const result = await apiGet("/portfolio/domains", cfg.apiKey);
33298
- if (result.stub)
33299
- return [];
33300
- return result.data.domains ?? [];
33301
- }
33302
- async function getDomainInfo3(domain, config) {
33303
- const cfg = config ?? getConfig4();
33304
- const result = await apiGet(`/portfolio/domains/${encodeURIComponent(domain)}`, cfg.apiKey);
33305
- if (result.stub)
33306
- return null;
33307
- return result.data;
33308
- }
33309
- async function checkAvailability4(domain, config) {
33310
- const cfg = config ?? getConfig4();
33311
- const result = await apiGet(`/domains/check?domain=${encodeURIComponent(domain)}`, cfg.apiKey);
33312
- if (result.stub)
33313
- return { domain, available: false };
33314
- return result.data;
33315
- }
33316
- async function renewDomain3(domain, years = 1, config) {
33317
- const cfg = config ?? getConfig4();
33318
- const result = await apiRequest3("POST", `/portfolio/domains/${encodeURIComponent(domain)}/renew`, cfg.apiKey, { years });
33319
- if (result.stub)
33320
- return { success: false };
33321
- return { success: true, orderId: result.data.orderId };
33322
- }
33323
- async function syncToLocalDb3(dbFns, config) {
33324
- const domains = await listDomains2(config);
33325
- let synced = 0, created = 0, updated = 0;
33326
- const errors = [];
33327
- for (const d3 of domains) {
33328
- try {
33329
- const existing = dbFns.getDomainByName(d3.domain);
33330
- if (existing) {
33331
- dbFns.updateDomain(existing.id, {
33332
- registrar: "Brandsight",
33333
- expires_at: d3.expires || undefined,
33334
- auto_renew: d3.auto_renew,
33335
- status: "active",
33336
- nameservers: d3.nameservers
33337
- });
33338
- updated++;
33339
- } else {
33340
- dbFns.createDomain({
33341
- name: d3.domain,
33342
- registrar: "Brandsight",
33343
- expires_at: d3.expires || undefined,
33344
- auto_renew: d3.auto_renew,
33345
- status: "active",
33346
- nameservers: d3.nameservers
33347
- });
33348
- created++;
33349
- }
33350
- synced++;
33351
- } catch (err) {
33352
- errors.push(`${d3.domain}: ${err instanceof Error ? err.message : String(err)}`);
33353
- }
33354
- }
33355
- return { synced, created, updated, errors };
33356
- }
33357
- function createBrandsightProvider(config) {
33358
- const cfg = config ?? getConfig4();
33359
- return {
33360
- name: "brandsight",
33361
- async listDomains() {
33362
- const domains = await listDomains2(cfg);
33363
- return domains.map((d3) => ({
33364
- domain: d3.domain,
33365
- registrar: "Brandsight",
33366
- created: "",
33367
- expires: d3.expires,
33368
- nameservers: d3.nameservers,
33369
- status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
33370
- auto_renew: d3.auto_renew
33371
- }));
33372
- },
33373
- async getDomainInfo(domain) {
33374
- const d3 = await getDomainInfo3(domain, cfg);
33375
- if (!d3)
33376
- throw new Error(`Domain not found in Brandsight: ${domain}`);
33377
- return {
33378
- domain: d3.domain,
33379
- registrar: "Brandsight",
33380
- created: "",
33381
- expires: d3.expires,
33382
- nameservers: d3.nameservers,
33383
- status: d3.status === "ACTIVE" ? "active" : d3.status.toLowerCase(),
33384
- auto_renew: d3.auto_renew
33385
- };
33386
- },
33387
- async renewDomain(domain) {
33388
- const result = await renewDomain3(domain, 1, cfg);
33389
- return { domain, success: result.success, orderId: result.orderId };
33390
- },
33391
- async checkAvailability(domain) {
33392
- const result = await checkAvailability4(domain, cfg);
33393
- return {
33394
- domain: result.domain,
33395
- available: result.available,
33396
- standard_price: result.price,
33397
- currency: result.currency
33398
- };
33399
- },
33400
- async syncToLocalDb(dbFns) {
33401
- return syncToLocalDb3(dbFns, cfg);
33402
- }
33403
- };
33404
- }
33405
-
33406
33523
  // src/lib/registrar.ts
33524
+ init_brandsight();
33407
33525
  function createNamecheapProvider() {
33408
33526
  return {
33409
33527
  name: "namecheap",
@@ -33732,6 +33850,16 @@ function resolveContact(opts) {
33732
33850
  return { first_name, last_name, email, phone, address_line_1, city, state, country_code, zip_code, organization_name };
33733
33851
  }
33734
33852
 
33853
+ // src/lib/delegate.ts
33854
+ async function delegateDomainToCloudflare(domain, deps) {
33855
+ const zone = await deps.createCloudflareZone(domain);
33856
+ if (!zone.nameservers || zone.nameservers.length === 0) {
33857
+ throw new Error(`Cloudflare zone for ${domain} returned no nameservers; cannot delegate`);
33858
+ }
33859
+ const { operationId } = await deps.updateNameservers(domain, zone.nameservers);
33860
+ return { zoneId: zone.id, nameservers: zone.nameservers, operationId };
33861
+ }
33862
+
33735
33863
  // src/cli/commands/domain.ts
33736
33864
  var DOMAIN_STATUS_HELP = DOMAIN_STATUSES.join("/");
33737
33865
  var DOMAIN_OFFER_STATUS_HELP = DOMAIN_OFFER_STATUSES.join("/");
@@ -34155,7 +34283,7 @@ WHOIS for ${result.domain} [${result.source}]:`);
34155
34283
  process.exit(1);
34156
34284
  }
34157
34285
  });
34158
- domain.command("buy <name>").description("Purchase a domain via Route 53 (contact defaults from: domains config set contact.*)").option("--provider <name>", "Registrar provider (default: config default-registrar or route53)").option("--registrar <name>", "Registrar/seller for recorded purchases (alias of --provider)").option("--email <email>", "Registrant email").option("--first-name <n>", "First name").option("--last-name <n>", "Last name").option("--phone <p>", "Phone").option("--address <a>", "Street address").option("--city <c>", "City").option("--state <s>", "State/province").option("--country <c>", "Country code").option("--zip <z>", "ZIP code").option("--org <o>", "Organization").option("--price <amount>", "Record a completed purchase instead of registering via Route 53").option("--expires <date>", "Expiry date for recorded purchases").option("--auto-renew <bool>", "Auto-renew for recorded purchases (true/false)").option("--years <n>", "Years", "1").option("--wait", "Poll until registration completes").action(async (name, opts) => {
34286
+ domain.command("buy <name>").description("Purchase a domain via Route 53 (contact defaults from: domains config set contact.*)").option("--provider <name>", "Registrar provider (default: config default-registrar or route53)").option("--registrar <name>", "Registrar/seller for recorded purchases (alias of --provider)").option("--email <email>", "Registrant email").option("--first-name <n>", "First name").option("--last-name <n>", "Last name").option("--phone <p>", "Phone").option("--address <a>", "Street address").option("--city <c>", "City").option("--state <s>", "State/province").option("--country <c>", "Country code").option("--zip <z>", "ZIP code").option("--org <o>", "Organization").option("--price <amount>", "Record a completed purchase instead of registering via Route 53").option("--expires <date>", "Expiry date for recorded purchases").option("--auto-renew <bool>", "Auto-renew for recorded purchases (true/false)").option("--years <n>", "Years", "1").option("--wait", "Poll until registration completes").option("--dns <provider>", "DNS provider to delegate to after purchase (always cloudflare)", "cloudflare").option("--no-delegate", "Skip delegating DNS to Cloudflare after purchase").action(async (name, opts) => {
34159
34287
  const recordedPrice = parseOptionalNumber(opts.price, "--price");
34160
34288
  if (recordedPrice !== undefined) {
34161
34289
  const registrarName = opts.registrar ?? opts.provider ?? "manual";
@@ -34238,6 +34366,30 @@ WHOIS for ${result.domain} [${result.source}]:`);
34238
34366
  });
34239
34367
  }
34240
34368
  console.log(`\u2713 Added to portfolio`);
34369
+ const dnsProvider = opts.dns ?? "cloudflare";
34370
+ if (opts.delegate !== false && dnsProvider === "cloudflare") {
34371
+ if (!opts.wait) {
34372
+ console.log(` DNS: run 'domains domain buy ${name} --wait' or delegate later \u2014 registration must finish before NS can change.`);
34373
+ } else {
34374
+ try {
34375
+ console.log(`Delegating DNS to Cloudflare...`);
34376
+ const del = await delegateDomainToCloudflare(name, {
34377
+ createCloudflareZone: async (d3) => {
34378
+ const z2 = await createZone(d3);
34379
+ return { id: z2.id, nameservers: z2.nameservers };
34380
+ },
34381
+ updateNameservers: (d3, ns) => updateNameservers(d3, ns)
34382
+ });
34383
+ const existing2 = getDomainByName(name);
34384
+ if (existing2)
34385
+ updateDomain(existing2.id, { nameservers: del.nameservers });
34386
+ console.log(`\u2713 Cloudflare zone ${del.zoneId}; nameservers \u2192 ${del.nameservers.join(", ")} (op ${del.operationId})`);
34387
+ } catch (e3) {
34388
+ console.error(`\u26A0 DNS delegation failed (domain is registered): ${e3 instanceof Error ? e3.message : String(e3)}`);
34389
+ console.error(` Retry: create the Cloudflare zone and point Route53 NS at it.`);
34390
+ }
34391
+ }
34392
+ }
34241
34393
  if (!opts.wait)
34242
34394
  console.log(` Check: domains r53 status ${reg.operationId}`);
34243
34395
  } catch (e3) {
@@ -34749,6 +34901,7 @@ function registerAlertCommands(program2) {
34749
34901
  }
34750
34902
 
34751
34903
  // src/cli/commands/monitor.ts
34904
+ init_brandsight();
34752
34905
  function registerMonitorCommand(program2) {
34753
34906
  const monitor = program2.command("monitor").description("Brand monitoring and threat detection (Brandsight)");
34754
34907
  monitor.command("watch <brand>").description("Monitor a brand for new lookalike domain registrations").option("--json", "Output JSON").action(async (brand, opts) => {
@@ -35193,6 +35346,14 @@ function registerDoctorCommand(program2) {
35193
35346
  ok("Registrant contact info complete");
35194
35347
  else
35195
35348
  fail(`Missing contact fields: ${missingContact.join(", ")}`, "domains config set contact.<field> <value>");
35349
+ section("Provisioning Credentials");
35350
+ const { checkProvisioningCredentials: checkProvisioningCredentials2 } = await Promise.resolve().then(() => (init_creds_check(), exports_creds_check));
35351
+ for (const c3 of checkProvisioningCredentials2()) {
35352
+ if (c3.configured)
35353
+ ok(`${c3.provider}: ${c3.mode} \u2014 ${c3.detail}`);
35354
+ else
35355
+ fail(`${c3.provider}: not configured (${c3.detail})`);
35356
+ }
35196
35357
  section("Providers");
35197
35358
  const providers = getAvailableProviders().filter((p3) => p3.name !== "brandsight");
35198
35359
  for (const p3 of providers) {
@@ -37250,6 +37411,67 @@ function registerWalletCommand(program2) {
37250
37411
  });
37251
37412
  }
37252
37413
 
37414
+ // src/lib/provision-state.ts
37415
+ var DOMAIN_FLOW = {
37416
+ requested: { action: "register", next: "registered" },
37417
+ registered: { action: "create_cf_zone", next: "cf_zone_ready" },
37418
+ cf_zone_ready: { action: "delegate_ns", next: "ns_delegated" },
37419
+ ns_delegated: { action: "check_ns_propagation", next: "ns_propagated" },
37420
+ ns_propagated: { action: "verify_dns", next: "dns_managed" },
37421
+ dns_managed: { action: "finalize", next: "ready" }
37422
+ };
37423
+ var TERMINAL = new Set(["ready", "failed"]);
37424
+ function domainHappyPath() {
37425
+ return [...Object.keys(DOMAIN_FLOW), "ready"];
37426
+ }
37427
+ function deriveDomainState(sig) {
37428
+ if (!sig.registered)
37429
+ return "requested";
37430
+ if (!sig.zoneExists)
37431
+ return "registered";
37432
+ if (!sig.registrarNsAreCloudflare)
37433
+ return "cf_zone_ready";
37434
+ if (!sig.publicNsAreCloudflare)
37435
+ return "ns_delegated";
37436
+ if (!sig.zoneActive)
37437
+ return "ns_propagated";
37438
+ return "ready";
37439
+ }
37440
+
37441
+ // src/cli/commands/provision.ts
37442
+ var CF_NS_SUFFIX = ".ns.cloudflare.com";
37443
+ function registerProvisionCommand(program2) {
37444
+ const provision = program2.command("provision").description("Domain provisioning lifecycle");
37445
+ provision.command("status <name>").description("Show the provisioning state of a domain (derived from live signals)").action(async (name) => {
37446
+ const signals = {};
37447
+ try {
37448
+ const detail = await getDomainDetail(name).catch(() => null);
37449
+ signals["registered"] = !!detail;
37450
+ const registrarNs = detail?.nameservers ?? [];
37451
+ signals["registrarNsAreCloudflare"] = registrarNs.length > 0 && registrarNs.every((n3) => n3.includes(CF_NS_SUFFIX));
37452
+ const zone = await getZone(name).catch(() => null);
37453
+ signals["zoneExists"] = !!zone;
37454
+ signals["zoneActive"] = zone?.status === "active";
37455
+ try {
37456
+ const resolved = await (await import("dns")).promises.resolveNs(name).catch(() => []);
37457
+ signals["publicNsAreCloudflare"] = resolved.length > 0 && resolved.every((n3) => n3.includes(CF_NS_SUFFIX));
37458
+ } catch {
37459
+ signals["publicNsAreCloudflare"] = false;
37460
+ }
37461
+ } catch (e3) {
37462
+ console.error(`Error gathering signals: ${e3 instanceof Error ? e3.message : String(e3)}`);
37463
+ }
37464
+ const state = deriveDomainState(signals);
37465
+ console.log(`
37466
+ Domain: ${name}`);
37467
+ console.log(`State: ${state}`);
37468
+ console.log(`Signals: ${JSON.stringify(signals)}`);
37469
+ const path = domainHappyPath();
37470
+ const idx = path.indexOf(state);
37471
+ console.log(`Progress: ${idx >= 0 ? idx + 1 : "?"}/${path.length} [${path.join(" \u2192 ")}]`);
37472
+ });
37473
+ }
37474
+
37253
37475
  // src/cli/index.ts
37254
37476
  init_version();
37255
37477
  var program2 = new Command;
@@ -37273,4 +37495,5 @@ registerResearchCommand(program2);
37273
37495
  registerOutreachCommand(program2);
37274
37496
  registerSedoCommand(program2);
37275
37497
  registerWalletCommand(program2);
37498
+ registerProvisionCommand(program2);
37276
37499
  program2.parse(process.argv);