higherdata 0.6.0 → 0.7.1

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/index.js CHANGED
@@ -20522,14 +20522,20 @@ var init_keychain = __esm({
20522
20522
  });
20523
20523
 
20524
20524
  // src/config.ts
20525
+ import { AsyncLocalStorage } from "async_hooks";
20525
20526
  import { chmod, mkdir, open as open2, readFile, rename, unlink } from "fs/promises";
20526
20527
  import { homedir } from "os";
20527
20528
  import { dirname, join } from "path";
20528
20529
  import { randomUUID } from "crypto";
20530
+ function withConfigWarn(warn, run) {
20531
+ return configWarn.run(warn, run);
20532
+ }
20529
20533
  function warnAboutFileKeychainFallback() {
20530
20534
  if (warnedAboutFileKeychainFallback) return;
20531
20535
  warnedAboutFileKeychainFallback = true;
20532
- process.stderr.write("HigherData could not access a system keychain. Credentials will be stored in the config file with 0600 permissions.\n");
20536
+ const warn = configWarn.getStore();
20537
+ if (warn) warn(KEYCHAIN_FILE_FALLBACK);
20538
+ else process.stderr.write(KEYCHAIN_FILE_FALLBACK);
20533
20539
  }
20534
20540
  function configPath(home = homedir()) {
20535
20541
  return join(home, ".config", "higherdata", "config.json");
@@ -20639,7 +20645,7 @@ function resolveSettings(config2, options = {}, env = process.env) {
20639
20645
  space: profile.space
20640
20646
  };
20641
20647
  }
20642
- var DEFAULT_API_URL, apiUrlSchema, profileSchema, configSchema, ConfigError, warnedAboutFileKeychainFallback;
20648
+ var DEFAULT_API_URL, apiUrlSchema, profileSchema, configSchema, ConfigError, KEYCHAIN_FILE_FALLBACK, warnedAboutFileKeychainFallback, configWarn;
20643
20649
  var init_config = __esm({
20644
20650
  "src/config.ts"() {
20645
20651
  "use strict";
@@ -20674,7 +20680,9 @@ var init_config = __esm({
20674
20680
  this.name = "ConfigError";
20675
20681
  }
20676
20682
  };
20683
+ KEYCHAIN_FILE_FALLBACK = "HigherData could not access a system keychain. Credentials will be stored in the config file with 0600 permissions.\n";
20677
20684
  warnedAboutFileKeychainFallback = false;
20685
+ configWarn = new AsyncLocalStorage();
20678
20686
  }
20679
20687
  });
20680
20688
 
@@ -20878,7 +20886,7 @@ var init_generated = __esm({
20878
20886
  },
20879
20887
  {
20880
20888
  "name": "account.limits",
20881
- "description": "Returns the current plan rate limits, concurrency and monthly email verification allowance.",
20889
+ "description": "Returns the current plan rate limits, concurrency and available purchased verify credit balance.",
20882
20890
  "risk": "read",
20883
20891
  "scopes": [],
20884
20892
  "input": {
@@ -21063,7 +21071,7 @@ var init_generated = __esm({
21063
21071
  },
21064
21072
  {
21065
21073
  "name": "account.usage",
21066
- "description": "Returns monthly activity, the email verification allowance and its reset time.",
21074
+ "description": "Returns monthly activity, the available purchased verify credit balance.",
21067
21075
  "risk": "read",
21068
21076
  "scopes": [],
21069
21077
  "input": {
@@ -22526,7 +22534,7 @@ var init_generated = __esm({
22526
22534
  },
22527
22535
  {
22528
22536
  "name": "email.find",
22529
- "description": "Find a person's work email from company pattern evidence. Tries at most three candidates in order and stops at the first deliverable. Dry run only plans candidates. Use the returned requestId to read or continue pending work.",
22537
+ "description": "Find a person's work email from company pattern evidence. Tries at most three candidates in order and stops at the first deliverable. At zero verify credits, returns the best available pattern guess marked unverified. Dry run only plans candidates. Use the returned requestId to read or continue pending work.",
22530
22538
  "risk": "read",
22531
22539
  "scopes": [
22532
22540
  "data:read"
@@ -22537,6 +22545,11 @@ var init_generated = __esm({
22537
22545
  {
22538
22546
  "type": "object",
22539
22547
  "properties": {
22548
+ "personId": {
22549
+ "description": "Stable person id to attach the verified email to.",
22550
+ "type": "string",
22551
+ "pattern": "^hdp_[0-9a-f]{32}$"
22552
+ },
22540
22553
  "firstName": {
22541
22554
  "type": "string",
22542
22555
  "minLength": 1,
@@ -22575,6 +22588,11 @@ var init_generated = __esm({
22575
22588
  "minLength": 1,
22576
22589
  "maxLength": 240
22577
22590
  },
22591
+ "linkedinUrl": {
22592
+ "description": "LinkedIn company page URL.",
22593
+ "type": "string",
22594
+ "maxLength": 2048
22595
+ },
22578
22596
  "city": {
22579
22597
  "description": "City of the headquarters.",
22580
22598
  "type": "string",
@@ -23124,7 +23142,7 @@ var init_generated = __esm({
23124
23142
  }
23125
23143
  },
23126
23144
  "additionalProperties": false,
23127
- "description": "Company to match. Supply an id, a domain, or a name."
23145
+ "description": "Company to match. Supply an id, a domain, a LinkedIn URL, or a name."
23128
23146
  }
23129
23147
  },
23130
23148
  "required": [
@@ -23154,7 +23172,7 @@ var init_generated = __esm({
23154
23172
  },
23155
23173
  {
23156
23174
  "name": "email.verify",
23157
- "description": "Verify one email address. Dry run spends nothing. Returns a requestId for pending work; call this tool with requestId to read or continue it. Verification results use deliverable, undeliverable, risky or unknown.",
23175
+ "description": "Verify one email address not already held for a contact. API callers may explicitly pass force to verify an existing address. Provider answers use purchased verify credits; cached answers are free. Dry run spends nothing. Returns a requestId for pending work; call this tool with requestId to read or continue it. Verification results use deliverable, undeliverable, risky or unknown.",
23158
23176
  "risk": "read",
23159
23177
  "scopes": [
23160
23178
  "data:read"
@@ -23165,12 +23183,21 @@ var init_generated = __esm({
23165
23183
  {
23166
23184
  "type": "object",
23167
23185
  "properties": {
23186
+ "personId": {
23187
+ "description": "Stable person id to attach the verified email to.",
23188
+ "type": "string",
23189
+ "pattern": "^hdp_[0-9a-f]{32}$"
23190
+ },
23168
23191
  "email": {
23169
23192
  "type": "string",
23170
23193
  "maxLength": 320,
23171
23194
  "format": "email",
23172
23195
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
23173
23196
  "description": "Work email address to verify."
23197
+ },
23198
+ "force": {
23199
+ "description": "API override to verify an address already held for a contact. Not offered in the UI.",
23200
+ "type": "boolean"
23174
23201
  }
23175
23202
  },
23176
23203
  "required": [
@@ -23379,7 +23406,7 @@ var init_generated = __esm({
23379
23406
  },
23380
23407
  {
23381
23408
  "name": "enrich.company",
23382
- "description": "Compare a company record with HigherData Core, using stable id, exact domain, then exact name and location. Name matches always require review. Dry run returns comparisons without recording them. Insights fields and filters require the Insights or Unlimited plan.",
23409
+ "description": "Compare a company record with HigherData Core, using stable id, LinkedIn URL, exact domain, then exact name and location. Name matches always require review. Dry run returns comparisons without recording them. Insights fields and filters require the Insights or Complete plan.",
23383
23410
  "risk": "write_local",
23384
23411
  "scopes": [
23385
23412
  "data:read",
@@ -23406,6 +23433,11 @@ var init_generated = __esm({
23406
23433
  "minLength": 1,
23407
23434
  "maxLength": 240
23408
23435
  },
23436
+ "linkedinUrl": {
23437
+ "description": "LinkedIn company page URL.",
23438
+ "type": "string",
23439
+ "maxLength": 2048
23440
+ },
23409
23441
  "city": {
23410
23442
  "description": "City of the headquarters.",
23411
23443
  "type": "string",
@@ -23955,7 +23987,7 @@ var init_generated = __esm({
23955
23987
  }
23956
23988
  },
23957
23989
  "additionalProperties": false,
23958
- "description": "Company to match. Supply an id, a domain, or a name."
23990
+ "description": "Company to match. Supply an id, a domain, a LinkedIn URL, or a name."
23959
23991
  }
23960
23992
  },
23961
23993
  {
@@ -24029,8 +24061,7 @@ var init_generated = __esm({
24029
24061
  "linkedinUrl": {
24030
24062
  "description": "LinkedIn person profile URL.",
24031
24063
  "type": "string",
24032
- "maxLength": 2048,
24033
- "format": "uri"
24064
+ "maxLength": 2048
24034
24065
  },
24035
24066
  "name": {
24036
24067
  "description": "Person's full name.",
@@ -24039,7 +24070,7 @@ var init_generated = __esm({
24039
24070
  "maxLength": 240
24040
24071
  },
24041
24072
  "company": {
24042
- "description": "Company this person works at. Supply an id, a domain, or a name.",
24073
+ "description": "Company this person works at. Supply an id, a domain, a LinkedIn URL, or a name.",
24043
24074
  "type": "object",
24044
24075
  "properties": {
24045
24076
  "id": {
@@ -24059,6 +24090,11 @@ var init_generated = __esm({
24059
24090
  "minLength": 1,
24060
24091
  "maxLength": 240
24061
24092
  },
24093
+ "linkedinUrl": {
24094
+ "description": "LinkedIn company page URL.",
24095
+ "type": "string",
24096
+ "maxLength": 2048
24097
+ },
24062
24098
  "city": {
24063
24099
  "description": "City of the headquarters.",
24064
24100
  "type": "string",
@@ -26548,31 +26584,29 @@ var init_generated = __esm({
26548
26584
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
26549
26585
  },
26550
26586
  "titleKeywords": {
26551
- "description": "Job title keywords to match. A person can match any of them.",
26587
+ "description": "Job title keywords to match, at least 3 characters each after trimming. A person can match any of them.",
26552
26588
  "minItems": 1,
26553
26589
  "maxItems": 50,
26554
26590
  "type": "array",
26555
26591
  "items": {
26556
26592
  "type": "string",
26557
- "minLength": 1,
26558
- "maxLength": 120,
26559
- "pattern": "\\S"
26593
+ "minLength": 3,
26594
+ "maxLength": 120
26560
26595
  }
26561
26596
  },
26562
26597
  "requiredTitleKeywords": {
26563
- "description": "Job title keywords the person must match.",
26598
+ "description": "Job title keywords the person must match, at least 3 characters each after trimming.",
26564
26599
  "minItems": 1,
26565
26600
  "maxItems": 50,
26566
26601
  "type": "array",
26567
26602
  "items": {
26568
26603
  "type": "string",
26569
- "minLength": 1,
26570
- "maxLength": 120,
26571
- "pattern": "\\S"
26604
+ "minLength": 3,
26605
+ "maxLength": 120
26572
26606
  }
26573
26607
  },
26574
26608
  "requiredTitleKeywordGroups": {
26575
- "description": "Job title keyword groups. The person must match every group, and any keyword inside a group.",
26609
+ "description": "Job title keyword groups, at least 3 characters per keyword after trimming. The person must match every group, and any keyword inside a group.",
26576
26610
  "maxItems": 10,
26577
26611
  "type": "array",
26578
26612
  "items": {
@@ -26581,22 +26615,20 @@ var init_generated = __esm({
26581
26615
  "type": "array",
26582
26616
  "items": {
26583
26617
  "type": "string",
26584
- "minLength": 1,
26585
- "maxLength": 120,
26586
- "pattern": "\\S"
26618
+ "minLength": 3,
26619
+ "maxLength": 120
26587
26620
  }
26588
26621
  }
26589
26622
  },
26590
26623
  "excludeTitleKeywords": {
26591
- "description": "Job title keywords that remove a person.",
26624
+ "description": "Job title keywords that remove a person, at least 3 characters each after trimming.",
26592
26625
  "minItems": 1,
26593
26626
  "maxItems": 50,
26594
26627
  "type": "array",
26595
26628
  "items": {
26596
26629
  "type": "string",
26597
- "minLength": 1,
26598
- "maxLength": 120,
26599
- "pattern": "\\S"
26630
+ "minLength": 3,
26631
+ "maxLength": 120
26600
26632
  }
26601
26633
  },
26602
26634
  "jobFunctions": {
@@ -26713,6 +26745,14 @@ var init_generated = __esm({
26713
26745
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
26714
26746
  }
26715
26747
  },
26748
+ "sort": {
26749
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
26750
+ "type": "string",
26751
+ "enum": [
26752
+ "company_size_desc",
26753
+ "company_size_asc"
26754
+ ]
26755
+ },
26716
26756
  "firstName": {
26717
26757
  "description": "First name tokens, using accent-folded fuzzy matching. Requires companyId.",
26718
26758
  "type": "string",
@@ -26839,9 +26879,34 @@ var init_generated = __esm({
26839
26879
  "additionalProperties": false
26840
26880
  }
26841
26881
  },
26882
+ {
26883
+ "name": "lists.delete",
26884
+ "description": "Delete a saved list and its membership and refresh history. Records and CRM data are unchanged. Dry run previews the deletion.",
26885
+ "risk": "destructive",
26886
+ "scopes": [
26887
+ "data:read",
26888
+ "data:write"
26889
+ ],
26890
+ "input": {
26891
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
26892
+ "type": "object",
26893
+ "properties": {
26894
+ "id": {
26895
+ "type": "string",
26896
+ "format": "uuid",
26897
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
26898
+ "description": "Saved list id in the current space."
26899
+ }
26900
+ },
26901
+ "required": [
26902
+ "id"
26903
+ ],
26904
+ "additionalProperties": false
26905
+ }
26906
+ },
26842
26907
  {
26843
26908
  "name": "lists.export",
26844
- "description": "Export a saved list as paged CSV or JSONL with opaque ids. CSV escapes spreadsheet formulas. Pages contain at most 50 records.Insights fields and filters require the Insights or Unlimited plan.",
26909
+ "description": "Export a saved list as paged CSV or JSONL with opaque ids. CSV escapes spreadsheet formulas. Pages contain at most 50 records.Insights fields and filters require the Insights or Complete plan.",
26845
26910
  "risk": "read",
26846
26911
  "scopes": [
26847
26912
  "data:read"
@@ -26926,6 +26991,15 @@ var init_generated = __esm({
26926
26991
  "type": "string",
26927
26992
  "format": "uuid",
26928
26993
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
26994
+ },
26995
+ "sort": {
26996
+ "description": "List records sorted by name or date added.",
26997
+ "type": "string",
26998
+ "enum": [
26999
+ "name_asc",
27000
+ "name_desc",
27001
+ "added_desc"
27002
+ ]
26929
27003
  }
26930
27004
  },
26931
27005
  "additionalProperties": false
@@ -26963,6 +27037,75 @@ var init_generated = __esm({
26963
27037
  "additionalProperties": false
26964
27038
  }
26965
27039
  },
27040
+ {
27041
+ "name": "lists.remove",
27042
+ "description": "Remove up to 50 saved row ids from a list. A later dynamic refresh may add matching records again. Dry run previews the removal count.",
27043
+ "risk": "destructive",
27044
+ "scopes": [
27045
+ "data:read",
27046
+ "data:write"
27047
+ ],
27048
+ "input": {
27049
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
27050
+ "type": "object",
27051
+ "properties": {
27052
+ "id": {
27053
+ "type": "string",
27054
+ "format": "uuid",
27055
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
27056
+ "description": "Saved list id in the current space."
27057
+ },
27058
+ "ids": {
27059
+ "minItems": 1,
27060
+ "maxItems": 50,
27061
+ "type": "array",
27062
+ "items": {
27063
+ "type": "string",
27064
+ "format": "uuid",
27065
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27066
+ },
27067
+ "description": "Saved row ids returned by lists.get, at most 50 per call."
27068
+ }
27069
+ },
27070
+ "required": [
27071
+ "id",
27072
+ "ids"
27073
+ ],
27074
+ "additionalProperties": false
27075
+ }
27076
+ },
27077
+ {
27078
+ "name": "lists.rename",
27079
+ "description": "Rename a saved list. Dry run previews the name.",
27080
+ "risk": "write_local",
27081
+ "scopes": [
27082
+ "data:read",
27083
+ "data:write"
27084
+ ],
27085
+ "input": {
27086
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
27087
+ "type": "object",
27088
+ "properties": {
27089
+ "id": {
27090
+ "type": "string",
27091
+ "format": "uuid",
27092
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
27093
+ "description": "Saved list id in the current space."
27094
+ },
27095
+ "name": {
27096
+ "type": "string",
27097
+ "minLength": 1,
27098
+ "maxLength": 80,
27099
+ "description": "New name for the saved list."
27100
+ }
27101
+ },
27102
+ "required": [
27103
+ "id",
27104
+ "name"
27105
+ ],
27106
+ "additionalProperties": false
27107
+ }
27108
+ },
26966
27109
  {
26967
27110
  "name": "lists.run",
26968
27111
  "description": "Queue a background preview or refresh. Only one job runs per list. Owners and admins can refresh dynamic lists now, including the current release. Dry run reports the proposed release without queueing.",
@@ -27030,7 +27173,7 @@ var init_generated = __esm({
27030
27173
  },
27031
27174
  {
27032
27175
  "name": "lookup.company",
27033
- "description": "Look up a company by exactly one domain, exact or near name (optionally with location), or opaque hdo_ id. Returns bounded candidates and ambiguity metadata. Insights fields and filters require the Insights or Unlimited plan.",
27176
+ "description": "Look up a company by exactly one domain, exact or near name (optionally with location), LinkedIn company page URL, or opaque hdo_ id, or up to 100 ids. Returns bounded candidates and ambiguity metadata. Insights fields and filters require the Insights or Complete plan.",
27034
27177
  "risk": "read",
27035
27178
  "scopes": [
27036
27179
  "data:read"
@@ -27055,7 +27198,7 @@ var init_generated = __esm({
27055
27198
  "maximum": 5
27056
27199
  },
27057
27200
  "excludeListId": {
27058
- "description": "Saved list whose members are left out of the result.",
27201
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27059
27202
  "type": "string",
27060
27203
  "format": "uuid",
27061
27204
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27098,7 +27241,7 @@ var init_generated = __esm({
27098
27241
  "maximum": 5
27099
27242
  },
27100
27243
  "excludeListId": {
27101
- "description": "Saved list whose members are left out of the result.",
27244
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27102
27245
  "type": "string",
27103
27246
  "format": "uuid",
27104
27247
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27118,7 +27261,7 @@ var init_generated = __esm({
27118
27261
  "description": "Opaque company id (hdo_)."
27119
27262
  },
27120
27263
  "excludeListId": {
27121
- "description": "Saved list whose members are left out of the result.",
27264
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27122
27265
  "type": "string",
27123
27266
  "format": "uuid",
27124
27267
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27128,6 +27271,59 @@ var init_generated = __esm({
27128
27271
  "id"
27129
27272
  ],
27130
27273
  "additionalProperties": false
27274
+ },
27275
+ {
27276
+ "type": "object",
27277
+ "properties": {
27278
+ "ids": {
27279
+ "minItems": 1,
27280
+ "maxItems": 100,
27281
+ "type": "array",
27282
+ "items": {
27283
+ "type": "string",
27284
+ "pattern": "^hdo_[0-9a-f]{32}$",
27285
+ "description": "Opaque company id (hdo_)."
27286
+ },
27287
+ "description": "Look up up to 100 opaque company ids in one call."
27288
+ },
27289
+ "excludeListId": {
27290
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27291
+ "type": "string",
27292
+ "format": "uuid",
27293
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27294
+ }
27295
+ },
27296
+ "required": [
27297
+ "ids"
27298
+ ],
27299
+ "additionalProperties": false
27300
+ },
27301
+ {
27302
+ "type": "object",
27303
+ "properties": {
27304
+ "linkedinUrl": {
27305
+ "type": "string",
27306
+ "maxLength": 2048,
27307
+ "description": "LinkedIn company page URL."
27308
+ },
27309
+ "candidateLimit": {
27310
+ "default": 5,
27311
+ "description": "Maximum candidates to return.",
27312
+ "type": "integer",
27313
+ "minimum": 1,
27314
+ "maximum": 5
27315
+ },
27316
+ "excludeListId": {
27317
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27318
+ "type": "string",
27319
+ "format": "uuid",
27320
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27321
+ }
27322
+ },
27323
+ "required": [
27324
+ "linkedinUrl"
27325
+ ],
27326
+ "additionalProperties": false
27131
27327
  }
27132
27328
  ]
27133
27329
  }
@@ -27143,6 +27339,12 @@ var init_generated = __esm({
27143
27339
  "$schema": "https://json-schema.org/draft/2020-12/schema",
27144
27340
  "type": "object",
27145
27341
  "properties": {
27342
+ "excludeListId": {
27343
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27344
+ "type": "string",
27345
+ "format": "uuid",
27346
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27347
+ },
27146
27348
  "companyId": {
27147
27349
  "description": "Select a company from a previous ambiguous company-scoped lookup.",
27148
27350
  "type": "string",
@@ -27189,7 +27391,7 @@ var init_generated = __esm({
27189
27391
  "maximum": 5
27190
27392
  },
27191
27393
  "excludeListId": {
27192
- "description": "Saved list whose members are left out of the result.",
27394
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27193
27395
  "type": "string",
27194
27396
  "format": "uuid",
27195
27397
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27216,7 +27418,7 @@ var init_generated = __esm({
27216
27418
  "maximum": 5
27217
27419
  },
27218
27420
  "excludeListId": {
27219
- "description": "Saved list whose members are left out of the result.",
27421
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27220
27422
  "type": "string",
27221
27423
  "format": "uuid",
27222
27424
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27256,7 +27458,7 @@ var init_generated = __esm({
27256
27458
  "maximum": 20
27257
27459
  },
27258
27460
  "excludeListId": {
27259
- "description": "Saved list whose members are left out of the result.",
27461
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27260
27462
  "type": "string",
27261
27463
  "format": "uuid",
27262
27464
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27273,7 +27475,6 @@ var init_generated = __esm({
27273
27475
  "linkedinUrl": {
27274
27476
  "type": "string",
27275
27477
  "maxLength": 2048,
27276
- "format": "uri",
27277
27478
  "description": "LinkedIn person profile URL."
27278
27479
  },
27279
27480
  "candidateLimit": {
@@ -27284,7 +27485,7 @@ var init_generated = __esm({
27284
27485
  "maximum": 5
27285
27486
  },
27286
27487
  "excludeListId": {
27287
- "description": "Saved list whose members are left out of the result.",
27488
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27288
27489
  "type": "string",
27289
27490
  "format": "uuid",
27290
27491
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27311,10 +27512,14 @@ var init_generated = __esm({
27311
27512
  "maximum": 5
27312
27513
  },
27313
27514
  "excludeListId": {
27314
- "description": "Saved list whose members are left out of the result.",
27515
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27315
27516
  "type": "string",
27316
27517
  "format": "uuid",
27317
27518
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27519
+ },
27520
+ "includeEmployment": {
27521
+ "description": "Include current and past jobs for this person, with suppression applied to each job.",
27522
+ "type": "boolean"
27318
27523
  }
27319
27524
  },
27320
27525
  "required": [
@@ -27327,7 +27532,7 @@ var init_generated = __esm({
27327
27532
  },
27328
27533
  {
27329
27534
  "name": "search.companies",
27330
- "description": "Find companies in HigherData Core using explicit filters. Optional domain uses exact website-host matching and ranks root sites first. Pages contain at most 50 records and opaque ids. Insights fields and filters require the Insights or Unlimited plan.",
27535
+ "description": "Find companies in HigherData Core using explicit filters. Optional domain uses exact website-host matching and ranks root sites first. Interactive pages contain at most 50 records. A download (download: true) contains at most 500 records and opaque ids. Insights fields and filters require the Insights or Complete plan.",
27331
27536
  "risk": "read",
27332
27537
  "scopes": [
27333
27538
  "data:read"
@@ -27678,10 +27883,10 @@ var init_generated = __esm({
27678
27883
  },
27679
27884
  "limit": {
27680
27885
  "default": 50,
27681
- "description": "Maximum records in this page.",
27886
+ "description": "Maximum records in this page. Interactive search accepts at most 50. A download (download true) accepts at most 500.",
27682
27887
  "type": "integer",
27683
27888
  "minimum": 1,
27684
- "maximum": 50
27889
+ "maximum": 500
27685
27890
  },
27686
27891
  "cursor": {
27687
27892
  "description": "Cursor from the previous page. Omit it for the first page.",
@@ -28371,10 +28576,14 @@ var init_generated = __esm({
28371
28576
  "additionalProperties": false
28372
28577
  },
28373
28578
  "excludeListId": {
28374
- "description": "Saved list whose members are left out of the result.",
28579
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
28375
28580
  "type": "string",
28376
28581
  "format": "uuid",
28377
28582
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
28583
+ },
28584
+ "download": {
28585
+ "description": "Set true to download a page. Interactive search stays at 50 records. The server reads this flag from the request, not from a plan or role.",
28586
+ "type": "boolean"
28378
28587
  }
28379
28588
  },
28380
28589
  "additionalProperties": false
@@ -29418,7 +29627,7 @@ var init_generated = __esm({
29418
29627
  },
29419
29628
  {
29420
29629
  "name": "search.count_people",
29421
- "description": "Count people once by primary current job before suppression using release-built aggregates; search results may match other current jobs. Unsupported refinements return a qualified baseline. exact: true explicitly requests one capped detailed count of matching people; never use it for live filtering.",
29630
+ "description": "Count people once by primary current job before suppression using the search index; search results may match other current jobs. Title keywords, required titles, title groups or seniorities can be used without company filters. Empty and exclusion-only counts are rejected. Broad counts are approximate. exact: true explicitly requests one capped detailed count of matching people; never use it for live filtering.",
29422
29631
  "risk": "read",
29423
29632
  "scopes": [
29424
29633
  "data:read"
@@ -29902,31 +30111,29 @@ var init_generated = __esm({
29902
30111
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
29903
30112
  },
29904
30113
  "titleKeywords": {
29905
- "description": "Job title keywords to match. A person can match any of them.",
30114
+ "description": "Job title keywords to match, at least 3 characters each after trimming. A person can match any of them.",
29906
30115
  "minItems": 1,
29907
30116
  "maxItems": 50,
29908
30117
  "type": "array",
29909
30118
  "items": {
29910
30119
  "type": "string",
29911
- "minLength": 1,
29912
- "maxLength": 120,
29913
- "pattern": "\\S"
30120
+ "minLength": 3,
30121
+ "maxLength": 120
29914
30122
  }
29915
30123
  },
29916
30124
  "requiredTitleKeywords": {
29917
- "description": "Job title keywords the person must match.",
30125
+ "description": "Job title keywords the person must match, at least 3 characters each after trimming.",
29918
30126
  "minItems": 1,
29919
30127
  "maxItems": 50,
29920
30128
  "type": "array",
29921
30129
  "items": {
29922
30130
  "type": "string",
29923
- "minLength": 1,
29924
- "maxLength": 120,
29925
- "pattern": "\\S"
30131
+ "minLength": 3,
30132
+ "maxLength": 120
29926
30133
  }
29927
30134
  },
29928
30135
  "requiredTitleKeywordGroups": {
29929
- "description": "Job title keyword groups. The person must match every group, and any keyword inside a group.",
30136
+ "description": "Job title keyword groups, at least 3 characters per keyword after trimming. The person must match every group, and any keyword inside a group.",
29930
30137
  "maxItems": 10,
29931
30138
  "type": "array",
29932
30139
  "items": {
@@ -29935,22 +30142,20 @@ var init_generated = __esm({
29935
30142
  "type": "array",
29936
30143
  "items": {
29937
30144
  "type": "string",
29938
- "minLength": 1,
29939
- "maxLength": 120,
29940
- "pattern": "\\S"
30145
+ "minLength": 3,
30146
+ "maxLength": 120
29941
30147
  }
29942
30148
  }
29943
30149
  },
29944
30150
  "excludeTitleKeywords": {
29945
- "description": "Job title keywords that remove a person.",
30151
+ "description": "Job title keywords that remove a person, at least 3 characters each after trimming.",
29946
30152
  "minItems": 1,
29947
30153
  "maxItems": 50,
29948
30154
  "type": "array",
29949
30155
  "items": {
29950
30156
  "type": "string",
29951
- "minLength": 1,
29952
- "maxLength": 120,
29953
- "pattern": "\\S"
30157
+ "minLength": 3,
30158
+ "maxLength": 120
29954
30159
  }
29955
30160
  },
29956
30161
  "jobFunctions": {
@@ -30133,7 +30338,7 @@ var init_generated = __esm({
30133
30338
  },
30134
30339
  "exact": {
30135
30340
  "default": false,
30136
- "description": "Explicitly run a detailed capped count instead of a rollup estimate. Never enable for live filtering.",
30341
+ "description": "Explicitly request a detailed count when an indexed count is unavailable. Never enable for live filtering.",
30137
30342
  "type": "boolean"
30138
30343
  }
30139
30344
  },
@@ -30244,7 +30449,7 @@ var init_generated = __esm({
30244
30449
  },
30245
30450
  {
30246
30451
  "name": "search.people",
30247
- "description": "Find people in HigherData Core, with 1 to 5 positive company HQ country codes or industries required unless a company id, company name, domain or indexed email scopes the search. Unscoped person names use indexed tokens and bounded, ranked pages. Person, job and company location use distinct filters; each city filter requires its corresponding country. Company-scoped contacts also support legacy contactLocation filters. Pages contain at most 50 people and opaque ids. ",
30452
+ "description": "Find people in HigherData Core. A title keyword, required title, title group or seniority is enough to search without a company filter. Otherwise use 1 to 5 positive company HQ countries or industries, a company id, company name, domain, email or person name. Empty and exclusion-only searches are rejected. Unscoped person names use indexed tokens and bounded, ranked pages. Person, job and company location use distinct filters; each city filter requires its corresponding country. Company-scoped contacts also support legacy contactLocation filters. Interactive pages contain at most 50 people. A download (download: true) contains at most 500 people and opaque ids. ",
30248
30453
  "risk": "read",
30249
30454
  "scopes": [
30250
30455
  "data:read"
@@ -30725,31 +30930,29 @@ var init_generated = __esm({
30725
30930
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
30726
30931
  },
30727
30932
  "titleKeywords": {
30728
- "description": "Job title keywords to match. A person can match any of them.",
30933
+ "description": "Job title keywords to match, at least 3 characters each after trimming. A person can match any of them.",
30729
30934
  "minItems": 1,
30730
30935
  "maxItems": 50,
30731
30936
  "type": "array",
30732
30937
  "items": {
30733
30938
  "type": "string",
30734
- "minLength": 1,
30735
- "maxLength": 120,
30736
- "pattern": "\\S"
30939
+ "minLength": 3,
30940
+ "maxLength": 120
30737
30941
  }
30738
30942
  },
30739
30943
  "requiredTitleKeywords": {
30740
- "description": "Job title keywords the person must match.",
30944
+ "description": "Job title keywords the person must match, at least 3 characters each after trimming.",
30741
30945
  "minItems": 1,
30742
30946
  "maxItems": 50,
30743
30947
  "type": "array",
30744
30948
  "items": {
30745
30949
  "type": "string",
30746
- "minLength": 1,
30747
- "maxLength": 120,
30748
- "pattern": "\\S"
30950
+ "minLength": 3,
30951
+ "maxLength": 120
30749
30952
  }
30750
30953
  },
30751
30954
  "requiredTitleKeywordGroups": {
30752
- "description": "Job title keyword groups. The person must match every group, and any keyword inside a group.",
30955
+ "description": "Job title keyword groups, at least 3 characters per keyword after trimming. The person must match every group, and any keyword inside a group.",
30753
30956
  "maxItems": 10,
30754
30957
  "type": "array",
30755
30958
  "items": {
@@ -30758,22 +30961,20 @@ var init_generated = __esm({
30758
30961
  "type": "array",
30759
30962
  "items": {
30760
30963
  "type": "string",
30761
- "minLength": 1,
30762
- "maxLength": 120,
30763
- "pattern": "\\S"
30964
+ "minLength": 3,
30965
+ "maxLength": 120
30764
30966
  }
30765
30967
  }
30766
30968
  },
30767
30969
  "excludeTitleKeywords": {
30768
- "description": "Job title keywords that remove a person.",
30970
+ "description": "Job title keywords that remove a person, at least 3 characters each after trimming.",
30769
30971
  "minItems": 1,
30770
30972
  "maxItems": 50,
30771
30973
  "type": "array",
30772
30974
  "items": {
30773
30975
  "type": "string",
30774
- "minLength": 1,
30775
- "maxLength": 120,
30776
- "pattern": "\\S"
30976
+ "minLength": 3,
30977
+ "maxLength": 120
30777
30978
  }
30778
30979
  },
30779
30980
  "jobFunctions": {
@@ -30890,12 +31091,20 @@ var init_generated = __esm({
30890
31091
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
30891
31092
  }
30892
31093
  },
31094
+ "sort": {
31095
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
31096
+ "type": "string",
31097
+ "enum": [
31098
+ "company_size_desc",
31099
+ "company_size_asc"
31100
+ ]
31101
+ },
30893
31102
  "limit": {
30894
31103
  "default": 50,
30895
- "description": "Maximum records in this page.",
31104
+ "description": "Maximum records in this page. Interactive search accepts at most 50. A download (download true) accepts at most 500.",
30896
31105
  "type": "integer",
30897
31106
  "minimum": 1,
30898
- "maximum": 50
31107
+ "maximum": 500
30899
31108
  },
30900
31109
  "cursor": {
30901
31110
  "description": "Cursor from the previous page. Omit it for the first page.",
@@ -30964,10 +31173,14 @@ var init_generated = __esm({
30964
31173
  "additionalProperties": false
30965
31174
  },
30966
31175
  "excludeListId": {
30967
- "description": "Saved list whose members are left out of the result.",
31176
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
30968
31177
  "type": "string",
30969
31178
  "format": "uuid",
30970
31179
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
31180
+ },
31181
+ "download": {
31182
+ "description": "Set true to download a page. Interactive search stays at 50 records. The server reads this flag from the request, not from a plan or role.",
31183
+ "type": "boolean"
30971
31184
  }
30972
31185
  },
30973
31186
  "additionalProperties": false
@@ -33589,31 +33802,29 @@ var init_generated = __esm({
33589
33802
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
33590
33803
  },
33591
33804
  "titleKeywords": {
33592
- "description": "Job title keywords to match. A person can match any of them.",
33805
+ "description": "Job title keywords to match, at least 3 characters each after trimming. A person can match any of them.",
33593
33806
  "minItems": 1,
33594
33807
  "maxItems": 50,
33595
33808
  "type": "array",
33596
33809
  "items": {
33597
33810
  "type": "string",
33598
- "minLength": 1,
33599
- "maxLength": 120,
33600
- "pattern": "\\S"
33811
+ "minLength": 3,
33812
+ "maxLength": 120
33601
33813
  }
33602
33814
  },
33603
33815
  "requiredTitleKeywords": {
33604
- "description": "Job title keywords the person must match.",
33816
+ "description": "Job title keywords the person must match, at least 3 characters each after trimming.",
33605
33817
  "minItems": 1,
33606
33818
  "maxItems": 50,
33607
33819
  "type": "array",
33608
33820
  "items": {
33609
33821
  "type": "string",
33610
- "minLength": 1,
33611
- "maxLength": 120,
33612
- "pattern": "\\S"
33822
+ "minLength": 3,
33823
+ "maxLength": 120
33613
33824
  }
33614
33825
  },
33615
33826
  "requiredTitleKeywordGroups": {
33616
- "description": "Job title keyword groups. The person must match every group, and any keyword inside a group.",
33827
+ "description": "Job title keyword groups, at least 3 characters per keyword after trimming. The person must match every group, and any keyword inside a group.",
33617
33828
  "maxItems": 10,
33618
33829
  "type": "array",
33619
33830
  "items": {
@@ -33622,22 +33833,20 @@ var init_generated = __esm({
33622
33833
  "type": "array",
33623
33834
  "items": {
33624
33835
  "type": "string",
33625
- "minLength": 1,
33626
- "maxLength": 120,
33627
- "pattern": "\\S"
33836
+ "minLength": 3,
33837
+ "maxLength": 120
33628
33838
  }
33629
33839
  }
33630
33840
  },
33631
33841
  "excludeTitleKeywords": {
33632
- "description": "Job title keywords that remove a person.",
33842
+ "description": "Job title keywords that remove a person, at least 3 characters each after trimming.",
33633
33843
  "minItems": 1,
33634
33844
  "maxItems": 50,
33635
33845
  "type": "array",
33636
33846
  "items": {
33637
33847
  "type": "string",
33638
- "minLength": 1,
33639
- "maxLength": 120,
33640
- "pattern": "\\S"
33848
+ "minLength": 3,
33849
+ "maxLength": 120
33641
33850
  }
33642
33851
  },
33643
33852
  "jobFunctions": {
@@ -33754,6 +33963,14 @@ var init_generated = __esm({
33754
33963
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
33755
33964
  }
33756
33965
  },
33966
+ "sort": {
33967
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
33968
+ "type": "string",
33969
+ "enum": [
33970
+ "company_size_desc",
33971
+ "company_size_asc"
33972
+ ]
33973
+ },
33757
33974
  "firstName": {
33758
33975
  "description": "First name tokens, using accent-folded fuzzy matching. Requires companyId.",
33759
33976
  "type": "string",
@@ -34194,6 +34411,14 @@ var init_generated = __esm({
34194
34411
  });
34195
34412
 
34196
34413
  // ../sdk/src/transport.ts
34414
+ function responseByteLimit(path2, input2) {
34415
+ const name2 = path2.split("?")[0]?.split("/").at(-1);
34416
+ if (name2 !== "search.companies" && name2 !== "search.people") return MAX_RESPONSE;
34417
+ if (!input2 || typeof input2 !== "object" || Array.isArray(input2) || input2.download !== true) return MAX_RESPONSE;
34418
+ const limit = input2.limit;
34419
+ const rows = typeof limit === "number" && Number.isInteger(limit) && limit >= 1 ? Math.min(limit, DOWNLOAD_PAGE_ROWS) : INTERACTIVE_PAGE_ROWS;
34420
+ return MAX_RESPONSE * Math.ceil(rows / INTERACTIVE_PAGE_ROWS);
34421
+ }
34197
34422
  function retryAfter(response, data, now) {
34198
34423
  const value = response.headers.get("retry-after");
34199
34424
  if (value !== null) {
@@ -34210,9 +34435,9 @@ function retryAfter(response, data, now) {
34210
34435
  }
34211
34436
  function apiError(response, data, now) {
34212
34437
  const parsed = failure2.safeParse(data), error63 = parsed.success ? parsed.data.error : null;
34213
- return new HigherDataError(typeof error63 === "object" && error63?.message ? error63.message : "The HigherData API request failed.", typeof error63 === "string" ? error63 : error63?.code ?? "http_error", response.status, retryAfter(response, data, now), response.headers.get("x-request-id") ?? void 0);
34438
+ return new HigherDataError(typeof error63 === "object" && error63?.message ? error63.message : "The HigherData API request failed.", typeof error63 === "string" ? error63 : error63?.code ?? "http_error", response.status, retryAfter(response, data, now), response.headers.get("x-request-id") ?? void 0, typeof error63 === "object" ? error63?.billing_href : void 0);
34214
34439
  }
34215
- async function responseJson(response) {
34440
+ async function responseJson(response, maxBytes) {
34216
34441
  if (!response.body) throw new HigherDataError("The API returned an invalid response.", "invalid_response");
34217
34442
  const reader = response.body.getReader(), chunks = [];
34218
34443
  let length = 0;
@@ -34221,7 +34446,7 @@ async function responseJson(response) {
34221
34446
  const { done, value } = await reader.read();
34222
34447
  if (done) break;
34223
34448
  length += value.byteLength;
34224
- if (length > MAX_RESPONSE) {
34449
+ if (length > maxBytes) {
34225
34450
  await reader.cancel();
34226
34451
  throw new HigherDataError("The API response exceeded the size limit.", "response_too_large");
34227
34452
  }
@@ -34258,28 +34483,32 @@ function delay(ms, signal) {
34258
34483
  signal?.addEventListener("abort", abort, { once: true });
34259
34484
  });
34260
34485
  }
34261
- var failure2, HigherDataError, limits, MAX_RESPONSE, JsonTransport;
34486
+ var failure2, HigherDataError, limits, MAX_RESPONSE, DOWNLOAD_PAGE_ROWS, INTERACTIVE_PAGE_ROWS, JsonTransport;
34262
34487
  var init_transport = __esm({
34263
34488
  "../sdk/src/transport.ts"() {
34264
34489
  "use strict";
34265
34490
  init_zod();
34266
- failure2 = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string().max(128), message: external_exports.string().max(2048).optional(), retry_after: external_exports.number().nonnegative().finite().optional() })]) });
34491
+ failure2 = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string().max(128), message: external_exports.string().max(2048).optional(), billing_href: external_exports.string().max(2048).optional(), retry_after: external_exports.number().nonnegative().finite().optional() })]) });
34267
34492
  HigherDataError = class extends Error {
34268
- constructor(message, code, status = 0, retryAfter2, requestId) {
34493
+ constructor(message, code, status = 0, retryAfter2, requestId, billingHref) {
34269
34494
  super(message);
34270
34495
  this.code = code;
34271
34496
  this.status = status;
34272
34497
  this.retryAfter = retryAfter2;
34273
34498
  this.requestId = requestId;
34499
+ this.billingHref = billingHref;
34274
34500
  this.name = "HigherDataError";
34275
34501
  }
34276
34502
  code;
34277
34503
  status;
34278
34504
  retryAfter;
34279
34505
  requestId;
34506
+ billingHref;
34280
34507
  };
34281
34508
  limits = external_exports.object({ maxRetries: external_exports.number().int().min(0).max(5), timeoutMs: external_exports.number().int().min(1).max(3e5), maxRetryDelayMs: external_exports.number().int().min(0).max(3e5) });
34282
34509
  MAX_RESPONSE = 8 * 1024 * 1024;
34510
+ DOWNLOAD_PAGE_ROWS = 500;
34511
+ INTERACTIVE_PAGE_ROWS = 50;
34283
34512
  JsonTransport = class {
34284
34513
  constructor(options) {
34285
34514
  this.options = options;
@@ -34317,7 +34546,7 @@ var init_transport = __esm({
34317
34546
  const timeout = AbortSignal.timeout(options.timeoutMs ?? this.settings.timeoutMs);
34318
34547
  response = await (this.options.fetch ?? fetch)(url2.toString(), { method: "POST", headers, body, redirect: "manual", signal: options.signal ? AbortSignal.any([timeout, options.signal]) : timeout });
34319
34548
  try {
34320
- data = await responseJson(response);
34549
+ data = await responseJson(response, responseByteLimit(path2, input2));
34321
34550
  } catch (error64) {
34322
34551
  if (response.ok) throw error64;
34323
34552
  data = void 0;
@@ -34377,6 +34606,8 @@ var init_exit_codes = __esm({
34377
34606
  function apiFailure(status, data) {
34378
34607
  const parsed = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string(), message: external_exports.string().optional() })]) }).safeParse(data);
34379
34608
  const code = parsed.success ? (typeof parsed.data.error === "string" ? parsed.data.error : parsed.data.error.code).toLowerCase() : "";
34609
+ if (code === "credits_required") return new CliError("Email verification is paused for credit.", EXIT_CODES.LIMIT);
34610
+ if (code === "plan_required") return new CliError("Your plan does not include this feature.");
34380
34611
  if (status === 401 || status === 403 || ["unauthorized", "forbidden"].includes(code)) return new CliError("Authentication failed. Run higherdata login or check HIGHERDATA_API_KEY and the selected space.", EXIT_CODES.AUTH);
34381
34612
  const message = parsed.success && typeof parsed.data.error !== "string" ? parsed.data.error.message : void 0;
34382
34613
  if (code === "limit_exceeded" && message && /^This month's email verification allowance is reached\. Resets \d{4}-\d{2}-\d{2}\. Upgrade: (https:\/\/[^\s]+|\/docs\/plans-and-limits)$/.test(message)) return new CliError(message, EXIT_CODES.LIMIT);
@@ -34409,7 +34640,7 @@ var init_api2 = __esm({
34409
34640
  ApiClient = class {
34410
34641
  constructor(settings, fetchImpl = fetch) {
34411
34642
  this.settings = settings;
34412
- this.transport = new JsonTransport({ ...settings, fetch: fetchImpl, maxRetries: 0 });
34643
+ this.transport = new JsonTransport({ ...settings, fetch: (url2, init) => fetchImpl(url2, { ...init, headers: { ...init?.headers, "x-higherdata-client": "cli" } }), maxRetries: 0 });
34413
34644
  }
34414
34645
  settings;
34415
34646
  transport;
@@ -34431,7 +34662,7 @@ var init_api2 = __esm({
34431
34662
  if (error63.status) {
34432
34663
  const mapped = apiFailure(error63.status, { error: { code: error63.code, message: error63.message } });
34433
34664
  const plain = (value) => (this.settings.apiKey ? value.replaceAll(this.settings.apiKey, "[redacted]") : value).replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ");
34434
- throw new CliError(`${plain(error63.code)}: ${plain(error63.message === "The HigherData API request failed." ? mapped.message : error63.message)}${error63.requestId ? ` (request: ${plain(error63.requestId)})` : ""}`, mapped.exitCode);
34665
+ throw new CliError(`${plain(error63.code)}: ${plain(error63.message === "The HigherData API request failed." ? mapped.message : error63.message)}${error63.billingHref ? ` Buy verify credits: ${plain(error63.billingHref)}` : ""}${error63.requestId ? ` (request: ${plain(error63.requestId)})` : ""}`, mapped.exitCode);
34435
34666
  }
34436
34667
  if (error63.code === "response_too_large") throw new CliError("The API response exceeded the CLI size limit.");
34437
34668
  throw new CliError(error63.message);
@@ -38002,7 +38233,7 @@ var init_package = __esm({
38002
38233
  "package.json"() {
38003
38234
  package_default = {
38004
38235
  name: "higherdata",
38005
- version: "0.6.0",
38236
+ version: "0.7.1",
38006
38237
  type: "module",
38007
38238
  bin: {
38008
38239
  higherdata: "dist/index.js"
@@ -38052,7 +38283,7 @@ var init_package = __esm({
38052
38283
  directory: "packages/cli"
38053
38284
  },
38054
38285
  higherdata: {
38055
- minimumServerVersion: "0.2.0"
38286
+ minimumServerVersion: "0.7.0"
38056
38287
  },
38057
38288
  optionalDependencies: {
38058
38289
  "@napi-rs/keyring": "2.1.0"
@@ -39875,6 +40106,13 @@ var init_records = __esm({
39875
40106
  }
39876
40107
  });
39877
40108
 
40109
+ // ../../src/lib/lookup/linkedin.ts
40110
+ var init_linkedin = __esm({
40111
+ "../../src/lib/lookup/linkedin.ts"() {
40112
+ "use strict";
40113
+ }
40114
+ });
40115
+
39878
40116
  // ../../src/lib/lookup/phone.ts
39879
40117
  var init_phone = __esm({
39880
40118
  "../../src/lib/lookup/phone.ts"() {
@@ -40649,6 +40887,7 @@ var suppressionKeyTypeSchema;
40649
40887
  var init_normalize = __esm({
40650
40888
  "../../src/lib/suppression/normalize.ts"() {
40651
40889
  "use strict";
40890
+ init_linkedin();
40652
40891
  init_phone();
40653
40892
  init_es62();
40654
40893
  init_zod();
@@ -41051,28 +41290,9 @@ function pageCsv(headers, rows) {
41051
41290
  return `${lines.join("\r\n")}\r
41052
41291
  `;
41053
41292
  }
41054
- function companyPageCsv(items) {
41055
- return pageCsv(
41056
- [...COMPANY_DOWNLOAD_FIELDS],
41057
- items.map((item) => COMPANY_DOWNLOAD_FIELDS.map((field) => item[field]))
41058
- );
41059
- }
41060
- function peoplePageCsv(items) {
41061
- return pageCsv(
41062
- [...PEOPLE_DOWNLOAD_FIELDS],
41063
- items.map((item) => PEOPLE_DOWNLOAD_FIELDS.map((field) => item[field]))
41064
- );
41065
- }
41066
- function searchPageJsonl(kind, items) {
41067
- const fields = kind === "companies" ? COMPANY_DOWNLOAD_FIELDS : PEOPLE_DOWNLOAD_FIELDS;
41068
- return items.map((item) => JSON.stringify(Object.fromEntries(fields.map((field) => [field, item[field] ?? null]))) + "\n").join("");
41069
- }
41070
- var COMPANY_DOWNLOAD_FIELDS, PEOPLE_DOWNLOAD_FIELDS;
41071
41293
  var init_search_download = __esm({
41072
41294
  "../../src/lib/csv/search-download.ts"() {
41073
41295
  "use strict";
41074
- COMPANY_DOWNLOAD_FIELDS = ["id", "companyName", "normalizedDomain", "headquartersCountryCode", "industryLinkedin", "employeeCountEstimate"];
41075
- PEOPLE_DOWNLOAD_FIELDS = ["id", "fullName", "jobTitle", "companyName", "emailAddress", "countryCode"];
41076
41296
  }
41077
41297
  });
41078
41298
 
@@ -41082,60 +41302,168 @@ function searchDownloadFormat(path2) {
41082
41302
  if (!/\.(csv|jsonl)$/.test(path2)) throw new CliError("Choose a new .csv or .jsonl destination.");
41083
41303
  return path2.endsWith(".jsonl") ? "jsonl" : "csv";
41084
41304
  }
41305
+ function customerRecord(item) {
41306
+ const value = customerOutput(item);
41307
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new CliError("The API returned an invalid search page.");
41308
+ return value;
41309
+ }
41310
+ function csvCellValue(value) {
41311
+ return value !== null && typeof value === "object" ? JSON.stringify(value) : value;
41312
+ }
41313
+ function downloadRecordsJsonl(items) {
41314
+ return items.map((item) => `${JSON.stringify(customerRecord(item))}
41315
+ `).join("");
41316
+ }
41317
+ function downloadProgress(count, finishedAll) {
41318
+ const label = `${count.toLocaleString("en-US")} ${count === 1 ? "row" : "rows"}`;
41319
+ return finishedAll ? `${label} (all)` : label;
41320
+ }
41085
41321
  async function downloadSearch(api, name2, input2, path2, options) {
41086
41322
  const format2 = searchDownloadFormat(path2);
41087
41323
  if (options.all && api.settings.idempotencyKey) throw new CliError("An idempotency key applies to one page. Omit --all.");
41088
41324
  const file2 = await open5(path2, "wx", 384);
41089
- let count = 0, first = true, cursor = input2.cursor, loaded = options.loadedPage;
41090
- const seen = /* @__PURE__ */ new Set();
41091
- if (typeof cursor === "string") seen.add(cursor);
41092
- try {
41093
- while (true) {
41094
- let raw, headers;
41325
+ const seenCursors = /* @__PURE__ */ new Set();
41326
+ const seenIds = /* @__PURE__ */ new Set();
41327
+ const startCursor = typeof input2.cursor === "string" ? input2.cursor : void 0;
41328
+ if (startCursor) seenCursors.add(startCursor);
41329
+ const requestInput = { ...input2, ...options.all ? { limit: SEARCH_DOWNLOAD_PAGE_SIZE, download: true } : {} };
41330
+ delete requestInput.cursor;
41331
+ let loaded = options.loadedPage;
41332
+ let reserved = 0;
41333
+ let written = 0;
41334
+ let capped = false;
41335
+ let position = 0;
41336
+ const columns = [];
41337
+ const csvRows = [];
41338
+ const idPattern = name2 === "search.companies" ? /^hdo_[a-f0-9]{32}$/ : /^hdp_[a-f0-9]{32}$/;
41339
+ let ahead = { kind: "ready", cursor: startCursor };
41340
+ const queue = [];
41341
+ let failed = false;
41342
+ async function writeAt(text2, at) {
41343
+ const body = Buffer.from(text2);
41344
+ let offset = 0;
41345
+ while (offset < body.length) {
41346
+ const { bytesWritten } = await file2.write(body, offset, body.length - offset, at + offset);
41347
+ if (bytesWritten <= 0) throw new CliError("The download could not be written.");
41348
+ offset += bytesWritten;
41349
+ }
41350
+ return at + body.length;
41351
+ }
41352
+ function csvText(records) {
41353
+ return pageCsv(columns, records.map((record2) => columns.map((key) => csvCellValue(record2[key]))));
41354
+ }
41355
+ async function writeRecords(items) {
41356
+ if (!items.length) return;
41357
+ if (options.beforeWrite) await options.beforeWrite();
41358
+ if (format2 === "jsonl") {
41359
+ position = await writeAt(downloadRecordsJsonl(items), position);
41360
+ return;
41361
+ }
41362
+ const records = items.map(customerRecord);
41363
+ const seen = new Set(columns);
41364
+ const grew = columns.length > 0 && records.some((record2) => Object.keys(record2).some((key) => !seen.has(key)));
41365
+ for (const record2 of records) for (const key of Object.keys(record2)) if (!seen.has(key)) {
41366
+ seen.add(key);
41367
+ columns.push(key);
41368
+ }
41369
+ const first = csvRows.length === 0;
41370
+ csvRows.push(...records);
41371
+ if (first) position = await writeAt(csvText(records), position);
41372
+ else if (grew) {
41373
+ position = await writeAt(csvText(csvRows), 0);
41374
+ await file2.truncate(position);
41375
+ } else {
41376
+ const page = csvText(records);
41377
+ position = await writeAt(page.slice(page.indexOf("\r\n") + 2), position);
41378
+ }
41379
+ }
41380
+ async function pace(headers) {
41381
+ if (headers?.get("ratelimit-remaining") !== "0") return;
41382
+ const reset = headers.get("ratelimit-reset");
41383
+ if (reset !== null && reset !== void 0 && /^\d+(?:\.\d+)?$/.test(reset.trim())) await options.sleep(Number(reset) * 1e3);
41384
+ }
41385
+ function kick() {
41386
+ if (ahead.kind !== "ready" || queue.length >= SEARCH_DOWNLOAD_WINDOW || failed) return;
41387
+ const cursor = ahead.cursor;
41388
+ ahead = { kind: "wait" };
41389
+ const promise2 = fetchPage(cursor);
41390
+ void promise2.catch(() => void 0);
41391
+ queue.push(promise2);
41392
+ }
41393
+ async function fetchPage(cursor) {
41394
+ try {
41395
+ let raw;
41396
+ let headers;
41095
41397
  if (loaded) {
41096
41398
  raw = loaded;
41097
41399
  loaded = void 0;
41098
41400
  } else {
41099
- const response = await api.request(`/api/v1/tools/${name2}`, { ...input2, ...cursor === void 0 ? {} : { cursor } }, { authenticated: true, rateLimitRecovery: options });
41401
+ const response = await api.request(`/api/v1/tools/${name2}`, { ...requestInput, ...cursor === void 0 ? {} : { cursor } }, { authenticated: true, rateLimitRecovery: options });
41100
41402
  raw = response.data;
41101
41403
  headers = response.headers;
41102
41404
  }
41103
41405
  const page = pageSchema2.safeParse(raw);
41104
- if (!page.success || page.data.items.some((item) => typeof item.id !== "string" || !(name2 === "search.companies" ? /^hdo_[a-f0-9]{32}$/ : /^hdp_[a-f0-9]{32}$/).test(item.id))) throw new CliError("The API returned an invalid search page.");
41105
- const items = page.data.items.slice(0, SEARCH_DOWNLOAD_CAP - count);
41106
- if (format2 === "jsonl") await file2.writeFile(searchPageJsonl(name2 === "search.companies" ? "companies" : "people", items));
41107
- else {
41108
- const csv = (name2 === "search.companies" ? companyPageCsv : peoplePageCsv)(items);
41109
- await file2.writeFile(first ? csv : csv.slice(csv.indexOf("\r\n") + 2));
41110
- }
41111
- first = false;
41112
- count += items.length;
41113
- options.progress?.(`Downloaded ${count.toLocaleString("en-US")} of 5,000`);
41406
+ if (!page.success || page.data.items.some((item) => typeof item.id !== "string" || !idPattern.test(item.id))) throw new CliError("The API returned an invalid search page.");
41407
+ const fresh = [];
41408
+ for (const item of page.data.items) {
41409
+ const id = item.id;
41410
+ if (seenIds.has(id)) continue;
41411
+ seenIds.add(id);
41412
+ fresh.push(item);
41413
+ }
41414
+ const items = fresh.slice(0, Math.max(0, SEARCH_DOWNLOAD_CAP - reserved));
41415
+ reserved += items.length;
41114
41416
  const next = page.data.nextCursor;
41115
- if (!options.all || !next || count === SEARCH_DOWNLOAD_CAP) return { rows: count, capped: !!options.all && count === SEARCH_DOWNLOAD_CAP && (!!next || items.length < page.data.items.length) };
41116
- if (seen.has(next)) throw new CliError("The API repeated a cursor. Pagination stopped.");
41117
- seen.add(next);
41118
- cursor = next;
41119
- if (headers?.get("ratelimit-remaining") === "0") {
41120
- const reset = headers.get("ratelimit-reset");
41121
- if (reset !== null && /^\d+(?:\.\d+)?$/.test(reset.trim())) await options.sleep(Number(reset) * 1e3);
41122
- }
41417
+ const cappedPage = Boolean(options.all) && reserved === SEARCH_DOWNLOAD_CAP && (Boolean(next) || fresh.length > items.length);
41418
+ const stop = !options.all || !next || cappedPage;
41419
+ if (!stop && next) {
41420
+ if (seenCursors.has(next)) {
41421
+ ahead = { kind: "stop" };
41422
+ return { items, done: true, capped: cappedPage, error: new CliError("The API repeated a cursor. Pagination stopped.") };
41423
+ }
41424
+ seenCursors.add(next);
41425
+ await pace(headers);
41426
+ ahead = { kind: "ready", cursor: next };
41427
+ kick();
41428
+ } else ahead = { kind: "stop" };
41429
+ return { items, done: stop, capped: cappedPage };
41430
+ } catch (error63) {
41431
+ failed = true;
41432
+ ahead = { kind: "stop" };
41433
+ throw error63;
41123
41434
  }
41435
+ }
41436
+ try {
41437
+ kick();
41438
+ while (queue.length) {
41439
+ const planned = await queue[0];
41440
+ await writeRecords(planned.items);
41441
+ written += planned.items.length;
41442
+ if (planned.capped) capped = true;
41443
+ options.progress?.(downloadProgress(written, planned.done && Boolean(options.all) && !capped));
41444
+ queue.shift();
41445
+ kick();
41446
+ if (planned.error) throw planned.error;
41447
+ }
41448
+ return { rows: written, capped };
41124
41449
  } finally {
41125
41450
  await file2.close();
41126
41451
  }
41127
41452
  }
41128
- var SEARCH_DOWNLOAD_CAP, SEARCH_DOWNLOAD_CAP_NOTICE, isSearchResults, pageSchema2;
41453
+ var SEARCH_DOWNLOAD_CAP, SEARCH_DOWNLOAD_CAP_NOTICE, SEARCH_DOWNLOAD_PAGE_SIZE, SEARCH_DOWNLOAD_WINDOW, isSearchResults, pageSchema2;
41129
41454
  var init_search_download2 = __esm({
41130
41455
  "src/search-download.ts"() {
41131
41456
  "use strict";
41132
41457
  init_zod();
41133
41458
  init_search_download();
41134
41459
  init_api2();
41460
+ init_output();
41135
41461
  SEARCH_DOWNLOAD_CAP = 5e3;
41136
41462
  SEARCH_DOWNLOAD_CAP_NOTICE = "Download stopped at 5,000 rows. Save the search to a list and export the list for larger sets.";
41463
+ SEARCH_DOWNLOAD_PAGE_SIZE = 500;
41464
+ SEARCH_DOWNLOAD_WINDOW = 3;
41137
41465
  isSearchResults = (name2) => name2 === "search.companies" || name2 === "search.people";
41138
- pageSchema2 = external_exports.object({ items: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())).max(50), nextCursor: external_exports.string().min(1).max(2048).nullable() });
41466
+ pageSchema2 = external_exports.object({ items: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())).max(500), nextCursor: external_exports.string().min(1).max(2048).nullable() });
41139
41467
  }
41140
41468
  });
41141
41469
 
@@ -43893,7 +44221,7 @@ async function globalScript(runtime, env, script) {
43893
44221
  }
43894
44222
  }
43895
44223
  async function autoUpdate(input2) {
43896
- if (!interactiveUpdateAllowed(input2.args, input2.env, input2.runtime) || input2.env.HIGHERDATA_NO_AUTO_UPDATE === "1" || input2.env.HIGHERDATA_UPDATE_RERUN === "1" || ["update", "upgrade", "config"].includes(primaryCommand(input2.args) ?? "")) return null;
44224
+ if (!interactiveUpdateAllowed(input2.args, input2.env, input2.runtime) || input2.env.HIGHERDATA_NO_AUTO_UPDATE === "1" || input2.env.HIGHERDATA_UPDATE_RERUN === "1" || ["update", "upgrade", "config", "guide"].includes(primaryCommand(input2.args) ?? "")) return null;
43897
44225
  if ((await input2.readConfig()).autoUpdate === false) return null;
43898
44226
  const candidate = await possibleGlobalScript(input2.runtime, input2.env);
43899
44227
  if (!candidate) return null;
@@ -46497,13 +46825,14 @@ function fieldsFor(schema, input2 = {}, variant = 0, prefix = "") {
46497
46825
  const discriminant = Object.keys(branches[0].properties).find((key) => branches.every((s) => s.properties?.[key]?.const !== void 0));
46498
46826
  const matched = branches.findIndex((s) => (s.required ?? []).some((key) => Object.hasOwn(input2, key)));
46499
46827
  const index = discriminant ? Math.max(0, branches.findIndex((s) => s.properties[discriminant].const === input2[discriminant])) : matched >= 0 ? matched : Math.min(variant, branches.length - 1);
46500
- const choices = branches.map((s) => discriminant ? s.properties[discriminant].const : Object.keys(s.properties).map(title).join(" / "));
46828
+ const choices = branches.map((s) => discriminant ? s.properties[discriminant].const : Object.keys(s.properties).filter((key) => key !== "force").map(title).join(" / "));
46501
46829
  const selector = { path: prefix ? `${prefix}.$variant` : "$variant", label: "Input type", required: true, schema: { type: "string", enum: choices } };
46502
46830
  return [selector, ...fieldsFor(branches[index], input2, variant, prefix).filter((f) => !discriminant || f.path !== `${prefix ? `${prefix}.` : ""}${discriminant}`)];
46503
46831
  }
46504
46832
  return fieldsFor(branches[0] ?? {}, input2, variant, prefix);
46505
46833
  }
46506
46834
  return Object.entries(schema.properties ?? {}).flatMap(([key, raw]) => {
46835
+ if (key === "force") return [];
46507
46836
  const path2 = prefix ? `${prefix}.${key}` : key;
46508
46837
  const alternatives2 = raw.anyOf ?? raw.oneOf;
46509
46838
  const field = alternatives2?.length === 2 && alternatives2.some((s) => s.type === "null") ? alternatives2.find((s) => s.type !== "null") : raw;
@@ -46712,12 +47041,12 @@ async function finishEmailRequest(name2, result, poll, sleep) {
46712
47041
  }
46713
47042
  function registerToolCommands(program2, runtime) {
46714
47043
  const tools = program2.command("tools").description("Every HigherData tool, with schema-driven flags and JSON input");
46715
- for (const tool of TOOL_CATALOG) for (const parent of tool.name === "email.find" ? [tools, program2] : [tools]) {
46716
- const command = parent.command(parent === program2 ? "find-email" : tool.name).description(tool.description).option("--input <json>", "Tool input as a JSON object", inputObject, {}).option("--execute", "Execute an irreversible action or email request (default: preview)").option("--yes", "Confirm execution of this request");
47044
+ for (const tool of TOOL_CATALOG) for (const parent of tool.name.startsWith("email.") ? [tools, program2] : [tools]) {
47045
+ const command = parent.command(parent === program2 ? tool.name === "email.find" ? "find-email" : "verify-email" : tool.name).description(tool.description).option("--input <json>", "Tool input as a JSON object", inputObject, {}).option("--execute", "Execute an irreversible action or email request (default: preview)").option("--yes", "Confirm execution of this request");
46717
47046
  const flags = flagFields(tool.input).map((field, index) => {
46718
47047
  const flag2 = field.path.replace(/([a-z])([A-Z])/gu, "$1-$2").replaceAll(".", "-").replaceAll("_", "-").toLowerCase();
46719
47048
  const attribute = `field${index}`;
46720
- const option = new Option(`--${flag2} <value>`, `${field.schema.description ?? field.path}${field.schema.type === "array" ? " (comma separated strings or JSON array)" : ""}`);
47049
+ const option = new Option(field.path === "force" ? "--force" : `${field.path === "linkedinUrl" ? "--linkedin <value>, " : ""}--${flag2} <value>`, `${field.schema.description ?? field.path}${field.schema.type === "array" ? " (comma separated strings or JSON array)" : ""}`);
46721
47050
  option.attributeName = () => attribute;
46722
47051
  command.addOption(option);
46723
47052
  return { field, attribute };
@@ -46725,7 +47054,10 @@ function registerToolCommands(program2, runtime) {
46725
47054
  command.action(async (options, cmd) => {
46726
47055
  if (options.execute && needsConfirmation(tool) && !options.yes) throw new CliError("Review the dry run, then supply --execute --yes to confirm this request.");
46727
47056
  let input2 = options.input;
46728
- for (const { field, attribute } of flags) if (typeof options[attribute] === "string") input2 = putField(input2, field, options[attribute]);
47057
+ for (const { field, attribute } of flags) {
47058
+ if (typeof options[attribute] === "string") input2 = putField(input2, field, options[attribute]);
47059
+ else if (field.path === "force" && options[attribute] === true) input2 = { ...input2, force: true };
47060
+ }
46729
47061
  const globals = cmd.optsWithGlobals();
46730
47062
  const settings = resolveSettings(await runtime.readConfig(), globals, runtime.env);
46731
47063
  const api = new ApiClient({ ...settings, idempotencyKey: globals.idempotencyKey ?? randomUUID5() }, runtime.fetchImpl);
@@ -47187,7 +47519,7 @@ function BusyMark({ color, size = "large", options = animationOptions(), env = p
47187
47519
  }, [animate]);
47188
47520
  return /* @__PURE__ */ jsx5(Mark, { lines: animate ? busyFrame(frame, size) : markFrame(size), color });
47189
47521
  }
47190
- function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "default", space = "Default", workspace = "HigherOps", remaining = "--", profiles = [], fullScreen = false, identity = "API key", plan = "Plan unavailable", used = "--" }) {
47522
+ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "default", space = "Default", workspace = "HigherOps", remaining = "--", profiles = [], fullScreen = false, identity = "API key", plan = "Plan unavailable", used = "--", keychainNotice = "" }) {
47191
47523
  const width = Math.max(16, columns), color = colors ? "#f2b705" : void 0;
47192
47524
  const limit = Math.max(2, rows - (fullScreen ? 12 : 15));
47193
47525
  const extraRows = [state.error, state.notice].reduce((count, text2) => count + Math.ceil(clean(text2).length / (width - 4)), 0);
@@ -47413,7 +47745,7 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
47413
47745
  isCore && (fullScreen ? /* @__PURE__ */ jsx5(Box4, { children: ["Overview", "Contacts", "Insights"].map((label, i) => /* @__PURE__ */ jsx5(Hit, { action: { key: `record-${i}` }, children: /* @__PURE__ */ jsx5(Text, { color, children: ` [${label}] ` }) }, label)) }) : /* @__PURE__ */ jsx5(Text, { color, children: "[1 Overview] [2 Contacts] [3 Insights]" })),
47414
47746
  state.tab === 2 && !row.insights ? /* @__PURE__ */ jsx5(Text, { children: "Insights requires an Insights or Unlimited plan, and a release with Insights fields. Check Account / Limits." }) : fieldLines(state.tool?.name.startsWith("changes.") ? reviewFields(row) : detail, width - 4).slice(state.selected, state.selected + limit - 2).map((line, i) => /* @__PURE__ */ jsx5(Text, { children: line }, i))
47415
47747
  ] });
47416
- help = isCore ? `Up/Down scroll. ${fullScreen ? "O/C/I record tabs." : "1/2/3 tabs."} A add to list. ${String(row.id).startsWith("hdp_") ? String(row.emailAddress ?? row.email ?? "").trim() ? "V verify email. " : "F find email. " : ""}Esc back.` : "Up/Down scroll. Esc back.";
47748
+ help = isCore ? `Up/Down scroll. ${fullScreen ? "O/C/I record tabs." : "1/2/3 tabs."} A add to list. ${String(row.id).startsWith("hdp_") && !String(row.emailAddress ?? row.email ?? "").trim() ? "F find email. " : ""}Esc back.` : "Up/Down scroll. Esc back.";
47417
47749
  if (state.tool?.name.startsWith("changes.")) help += " A approve. X reject. P apply. U restore. Each opens a preview.";
47418
47750
  if (state.tool?.name === "hygiene.duplicates") help += " M propose merge. X dismiss.";
47419
47751
  if (state.tool?.name === "hygiene.orphans") help += " P propose repair.";
@@ -47470,6 +47802,7 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
47470
47802
  if (fullScreen) return /* @__PURE__ */ jsx5(PaletteContext.Provider, { value: colors, children: /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", width, height: rows, overflow: "hidden", backgroundColor: colors ? "#09090b" : void 0, children: [
47471
47803
  /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", flexGrow: 1, children: [
47472
47804
  /* @__PURE__ */ jsx5(AppBar, { columns: width, active: state.appTab ?? 0, colors, workspace, space, identity, plan, used, remaining }),
47805
+ keychainNotice && /* @__PURE__ */ jsx5(Box4, { flexShrink: 0, paddingX: 1, children: /* @__PURE__ */ jsx5(Text, { wrap: "wrap", children: clean(keychainNotice) }) }),
47473
47806
  /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", paddingX: 1, flexGrow: 1, overflow: "hidden", children: content })
47474
47807
  ] }),
47475
47808
  state.error && /* @__PURE__ */ jsx5(Text, { wrap: "truncate-end", color: colors ? "#ffe37a" : void 0, children: clean(state.error) }),
@@ -47525,6 +47858,7 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
47525
47858
  ] })
47526
47859
  ] }),
47527
47860
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2550".repeat(Math.max(1, width - 4)) }),
47861
+ keychainNotice && /* @__PURE__ */ jsx5(Text, { wrap: "wrap", children: clean(keychainNotice) }),
47528
47862
  /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", width: width - 4, minHeight: largeHome ? 0 : Math.max(0, Math.min(rows - 16, 13)), children: content }),
47529
47863
  state.error && /* @__PURE__ */ jsx5(Text, { color: colors ? "red" : void 0, children: clean(state.error) }),
47530
47864
  state.notice && state.screen !== "busy" && /* @__PURE__ */ jsx5(Text, { dimColor: true, children: clean(state.notice) }),
@@ -47539,7 +47873,7 @@ function screenButtons(state) {
47539
47873
  if (state.tool?.name === "hygiene.duplicates") return [["Merge", "m"], ["Dismiss", "x"], ["Back", "escape"]];
47540
47874
  if (coreRecord(state.row)) {
47541
47875
  const row = detailRecord(state.row);
47542
- const emailAction = String(row.id).startsWith("hdp_") ? String(row.emailAddress ?? row.email ?? "").trim() ? [["Verify email", "v"]] : [["Find email", "f"]] : [];
47876
+ const emailAction = String(row.id).startsWith("hdp_") && !String(row.emailAddress ?? row.email ?? "").trim() ? [["Find email", "f"]] : [];
47543
47877
  return [["Add to list", "a"], ...emailAction, ["Suppression", "s"], ["Back", "escape"]];
47544
47878
  }
47545
47879
  }
@@ -47592,10 +47926,22 @@ import { open as open7 } from "fs/promises";
47592
47926
  import { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
47593
47927
  import { render, useApp, useInput, useWindowSize } from "ink";
47594
47928
  import { jsx as jsx6 } from "react/jsx-runtime";
47929
+ function recordLabel(value, fallback) {
47930
+ if (!value || typeof value !== "object") return fallback;
47931
+ const record2 = value;
47932
+ if (typeof record2.name === "string" && record2.name.trim()) return record2.name.trim();
47933
+ if (typeof record2.slug === "string" && record2.slug.trim()) return record2.slug.trim();
47934
+ return fallback;
47935
+ }
47936
+ function principalLabel(principal) {
47937
+ const type = principal && typeof principal === "object" ? principal.type : void 0;
47938
+ return type === "api_key" ? "Signed in with API key" : "Signed in";
47939
+ }
47595
47940
  function InteractiveApp({ session, fullScreen = true, events }) {
47596
47941
  const { exit } = useApp(), { columns, rows } = useWindowSize();
47597
47942
  const settings = session.settings();
47598
- const [chrome, setChrome] = useState2({ workspace: "Workspace", remaining: "--", identity: "Signed out", plan: "Plan unavailable", used: "--" });
47943
+ const signedIn = Boolean(settings.apiKey);
47944
+ const [chrome, setChrome] = useState2({ workspace: "Workspace", space: "", remaining: "--", identity: "Signed in", plan: "Plan unavailable", used: "--" });
47599
47945
  const targets = useRef2(/* @__PURE__ */ new Map());
47600
47946
  const pages = useRef2([]);
47601
47947
  const pressed = useRef2(void 0);
@@ -47612,12 +47958,11 @@ function InteractiveApp({ session, fullScreen = true, events }) {
47612
47958
  }, []);
47613
47959
  useEffect3(() => {
47614
47960
  let active = true;
47615
- if (!settings.apiKey || !settings.space) return;
47961
+ if (!settings.apiKey) return;
47616
47962
  void Promise.all([session.call("account.whoami", {}), session.call("account.usage", {})]).then(([identity, usage]) => {
47617
- const tenant = identity.tenant;
47618
- if (active) setChrome({ workspace: tenant?.slug ?? "Workspace", remaining: typeof usage.verificationsRemaining === "number" ? usage.verificationsRemaining.toLocaleString("en-US") : "--", plan: typeof usage.plan === "string" ? usage.plan : "Plan unavailable", used: String(usage.verificationsUsed ?? "--"), identity: identity.principal?.type === "api_key" ? "Signed in with API key" : "Signed in" });
47963
+ if (active) setChrome({ workspace: recordLabel(identity.tenant, "Workspace"), space: recordLabel(identity.space, ""), remaining: typeof usage.verificationsRemaining === "number" ? usage.verificationsRemaining.toLocaleString("en-US") : "--", plan: typeof usage.plan === "string" ? usage.plan : "Plan unavailable", used: String(usage.verificationsUsed ?? "--"), identity: principalLabel(identity.principal) });
47619
47964
  }).catch(() => {
47620
- if (active) setChrome({ workspace: "Account unavailable", remaining: "--", identity: "Account unavailable", plan: "Plan unavailable", used: "--" });
47965
+ if (active) setChrome({ workspace: "Account unavailable", space: "Account unavailable", remaining: "--", identity: "Account unavailable", plan: "Plan unavailable", used: "--" });
47621
47966
  });
47622
47967
  return () => {
47623
47968
  active = false;
@@ -47713,7 +48058,7 @@ function InteractiveApp({ session, fullScreen = true, events }) {
47713
48058
  });
47714
48059
  else if (item.id === "login") void work("Waiting for browser approval...", async () => {
47715
48060
  await session.login((code, url2) => patch({ notice: `Approve ${code} at ${url2}` }));
47716
- return { screen: "home", notice: "Signed in. Choose a space to begin." };
48061
+ return { screen: "home", notice: "Signed in." };
47717
48062
  });
47718
48063
  else if (item.id === "ask") void work("Loading your private conversation...", async () => ({ screen: "ask", ask: await session.call("agent.get", {}), text: "" }));
47719
48064
  else patch({ screen: "tools", group: item.id, selected: 0, appTab: Math.max(0, TAB_GROUPS.indexOf(item.id)) });
@@ -47991,11 +48336,11 @@ function InteractiveApp({ session, fullScreen = true, events }) {
47991
48336
  } else if (isCore && input2 === "a") form("lists.add", { ids: [record2.id] });
47992
48337
  else if (isCore && String(record2.id).startsWith("hdp_") && !String(record2.emailAddress ?? record2.email ?? "").trim() && input2 === "f") form("email.find", { firstName: record2.firstName, lastName: record2.lastName, company: record2.companyId ? { id: record2.companyId } : {} });
47993
48338
  else if (isCore && input2 === "s") form("suppress.check", { keys: [{ keyType: String(record2.id).startsWith("hdo_") ? "company_id" : "person_id", value: record2.id }] });
47994
- else if (isCore && String(record2.id).startsWith("hdp_") && !!String(record2.emailAddress ?? record2.email ?? "").trim() && input2 === "v") form("email.verify", { email: record2.emailAddress ?? record2.email });
47995
48339
  }
47996
48340
  }
47997
48341
  useInput(handleInput);
47998
- return /* @__PURE__ */ jsx6(PointerContext.Provider, { value: targets.current, children: /* @__PURE__ */ jsx6(TerminalScreen, { state, columns, rows, colors: !Object.hasOwn(session.runtime.env, "NO_COLOR") && session.runtime.env.TERM !== "dumb", profile: settings.profile, profiles: Object.keys(session.config.profiles), space: settings.space ?? "Choose a space", workspace: settings.apiKey && settings.space ? chrome.workspace : "Sign in and choose a space", remaining: settings.apiKey && settings.space ? chrome.remaining : "--", fullScreen, identity: settings.apiKey ? chrome.identity : "Signed out", plan: settings.apiKey ? chrome.plan : "Sign in", used: chrome.used }) });
48342
+ const space = signedIn ? chrome.space || settings.space || "Choose a space" : "Signed out";
48343
+ return /* @__PURE__ */ jsx6(PointerContext.Provider, { value: targets.current, children: /* @__PURE__ */ jsx6(TerminalScreen, { state, columns, rows, colors: !Object.hasOwn(session.runtime.env, "NO_COLOR") && session.runtime.env.TERM !== "dumb", profile: settings.profile, profiles: Object.keys(session.config.profiles), space, workspace: signedIn ? chrome.workspace : "Signed out", remaining: signedIn ? chrome.remaining : "--", fullScreen, identity: signedIn ? chrome.identity : "Signed out", plan: signedIn ? chrome.plan : "Sign in", used: signedIn ? chrome.used : "--", keychainNotice: session.keychainNotice }) });
47999
48344
  }
48000
48345
  async function launchInteractive(runtime, options) {
48001
48346
  if (options.idempotencyKey) throw new CliError("Omit --idempotency-key in the interactive app. Each confirmed action gets its own key.");
@@ -48078,8 +48423,18 @@ var init_app = __esm({
48078
48423
  runtime;
48079
48424
  options;
48080
48425
  config = { activeProfile: "default", profiles: { default: {} } };
48426
+ keychainNotice = "";
48427
+ rememberKeychainWarning = (message) => {
48428
+ this.keychainNotice = message.replace(/\s+$/u, "");
48429
+ };
48430
+ loadConfig() {
48431
+ return withConfigWarn(this.rememberKeychainWarning, () => this.runtime.readConfig());
48432
+ }
48433
+ saveConfig(config2 = this.config) {
48434
+ return withConfigWarn(this.rememberKeychainWarning, () => this.runtime.writeConfig(config2));
48435
+ }
48081
48436
  async load() {
48082
- this.config = await this.runtime.readConfig();
48437
+ this.config = await this.loadConfig();
48083
48438
  }
48084
48439
  async lookup(query) {
48085
48440
  const result = await this.call("lookup.find", { query });
@@ -48102,19 +48457,20 @@ var init_app = __esm({
48102
48457
  async selectSpace(space) {
48103
48458
  await this.api(space).tool("account.whoami", {});
48104
48459
  this.config.profiles[this.config.activeProfile].space = space;
48105
- await this.runtime.writeConfig(this.config);
48460
+ await this.saveConfig();
48106
48461
  }
48107
48462
  async selectProfile(profile) {
48108
48463
  if (this.runtime.env.HIGHERDATA_API_KEY) throw new CliError("Unset HIGHERDATA_API_KEY to switch saved workspace profiles.");
48109
48464
  if (!Object.hasOwn(this.config.profiles, profile)) throw new CliError("Choose a saved profile.");
48110
48465
  const config2 = { ...this.config, activeProfile: profile };
48111
48466
  await new ApiClient(resolveSettings(config2, this.options, this.runtime.env), this.runtime.fetchImpl).tool("account.whoami", {});
48112
- await this.runtime.writeConfig(config2);
48467
+ await this.saveConfig(config2);
48113
48468
  this.config = config2;
48114
48469
  }
48115
48470
  async logout() {
48116
48471
  delete this.config.profiles[this.config.activeProfile].apiKey;
48117
- await this.runtime.writeConfig(this.config);
48472
+ await this.saveConfig();
48473
+ this.config = await this.loadConfig();
48118
48474
  }
48119
48475
  async install(kind, input2, print) {
48120
48476
  const options = { agent: input2.agent, scope: input2.scope, profile: input2.profile, print };
@@ -48122,8 +48478,11 @@ var init_app = __esm({
48122
48478
  }
48123
48479
  async login(prompt) {
48124
48480
  const settings = this.settings(), key = await deviceLogin(this.api(), { now: this.runtime.now, sleep: this.runtime.sleep, openBrowser: this.runtime.openBrowser, prompt });
48125
- this.config.profiles[this.config.activeProfile] = { apiUrl: settings.apiUrl, apiKey: key };
48126
- await this.runtime.writeConfig(this.config);
48481
+ const profile = this.config.profiles[this.config.activeProfile];
48482
+ this.config.profiles[this.config.activeProfile] = { ...profile, apiUrl: settings.apiUrl, apiKey: key };
48483
+ delete this.config.profiles[this.config.activeProfile].space;
48484
+ await this.saveConfig();
48485
+ this.config = await this.loadConfig();
48127
48486
  }
48128
48487
  async download(state, path2, options = {}) {
48129
48488
  if (!path2.trim()) throw new CliError("Enter a new destination file.");
@@ -48254,7 +48613,7 @@ var peopleArrayFlags = {
48254
48613
  excludeEmail: "excludeEmails"
48255
48614
  };
48256
48615
  var scalarFlags = ["employeeCountMin", "employeeCountMax", "revenueMinUsd", "revenueMaxUsd", "websiteAvailability", "linkedinAvailability", "fundingDateAfter", "fundingDateBefore"];
48257
- var peopleScalarFlags = ["companyFoundedYearMin", "companyFoundedYearMax", "jobStartDateAfter", "jobStartDateBefore", "emailAvailability", "phoneAvailability", "requireVerifiedEmail"];
48616
+ var peopleScalarFlags = ["sort", "companyFoundedYearMin", "companyFoundedYearMax", "jobStartDateAfter", "jobStartDateBefore", "emailAvailability", "phoneAvailability", "requireVerifiedEmail"];
48258
48617
  var flag = (name2) => name2.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`);
48259
48618
  var collect = (value, previous = []) => [...previous, value];
48260
48619
  function positiveLimit(value) {
@@ -48297,6 +48656,39 @@ async function buildFilters(api, options, kind) {
48297
48656
 
48298
48657
  // src/commands.ts
48299
48658
  init_output();
48659
+
48660
+ // src/guide.ts
48661
+ var AGENT_GUIDE = `HigherData agent guide
48662
+
48663
+ Count first, then download.
48664
+
48665
+ higherdata count --country-code CA --json
48666
+ That estimates companies and their contacts. For a people count:
48667
+ higherdata tools search.count_people --input '{"filters":{"countryCodes":["CA"]}}' --json
48668
+
48669
+ higherdata search people --country-code CA --title-keyword Operations --all --out people.jsonl
48670
+ --all requests the largest page the API allows (500) and writes every matching row.
48671
+ Progress prints "344 rows" while it runs and "453 rows (all)" when the download finishes.
48672
+ The 5,000 row cap is mentioned only when a download hits it. Save the search to a list and export the list for a larger set.
48673
+ --out people.jsonl has the same fields as --jsonl. A .csv file has those same fields. Do not download twice.
48674
+
48675
+ Filters and allowed values.
48676
+
48677
+ higherdata search people --help
48678
+ higherdata search companies --help
48679
+ higherdata tools search.facets --json
48680
+ Facets list country, state, industry, NAICS, funding, department, seniority and role family values, plus city suggestions.
48681
+ --where "Canadian software companies" proposes filters before the search.
48682
+ A people search needs a country or an industry.
48683
+
48684
+ Company lookup.
48685
+
48686
+ Do not call lookup.company once per person.
48687
+ When batch company lookup is available, pass up to 100 hdo_ ids in one lookup.company call.
48688
+ Until that call accepts ids, lookup.company takes one domain, name, LinkedIn URL, or hdo_ id.
48689
+ `;
48690
+
48691
+ // src/commands.ts
48300
48692
  init_search_download2();
48301
48693
 
48302
48694
  // src/pagination.ts
@@ -48366,11 +48758,11 @@ function wholeNumber(value) {
48366
48758
  }
48367
48759
  var collectScope = (value, previous = []) => [...previous, value];
48368
48760
  function registerAccountCommands(program2, deps) {
48369
- program2.command("usage").description("Show monthly activity and email verification allowance").action(async (_options, command) => {
48761
+ program2.command("usage").description("Show monthly activity and purchased verify credits").action(async (_options, command) => {
48370
48762
  const { api, mode } = await deps.session(command);
48371
48763
  deps.emit(await api.tool("account.usage", {}), mode);
48372
48764
  });
48373
- program2.command("limits").description("Show rate limits, the plan allowance, and the reset date").action(async (_options, command) => {
48765
+ program2.command("limits").description("Show plan rate limits, concurrency and purchased verify credits").action(async (_options, command) => {
48374
48766
  const { api, mode } = await deps.session(command);
48375
48767
  deps.emit(await api.tool("account.limits", {}), mode);
48376
48768
  });
@@ -48609,6 +49001,9 @@ function registerCommands(program2, runtime) {
48609
49001
  await runtime.writeConfig(config2);
48610
49002
  emit(identity, mode);
48611
49003
  });
49004
+ program2.command("guide").description("Print the agent quick reference").action(() => {
49005
+ runtime.out(AGENT_GUIDE);
49006
+ });
48612
49007
  const search = program2.command("search").description("Search HigherData Core");
48613
49008
  for (const kind of ["companies", "people"]) pageOptions(excludeList(addFilterOptions(search.command(kind).description(`Find ${kind}`), kind === "people"))).option("--out <path>", "Download to a new .csv or .jsonl file (up to 5,000 rows with --all)").action(async (options, command) => {
48614
49009
  const { api, mode } = await session(command);
@@ -48632,14 +49027,16 @@ function registerCommands(program2, runtime) {
48632
49027
  const { api, mode } = await session(command);
48633
49028
  emit(await api.tool("lookup.find", { query: query.join(" "), ...options.company ? { companyId: options.company } : {} }), mode);
48634
49029
  });
48635
- excludeList(lookup.command("company <domain>")).action(async (domain2, options, command) => {
48636
- const { api, mode } = await session(command);
48637
- emit(await api.tool("lookup.company", { domain: domain2, ...excluded(options) }), mode);
48638
- });
48639
- excludeList(lookup.command("person <email>")).action(async (email3, options, command) => {
48640
- const { api, mode } = await session(command);
48641
- emit(await api.tool("lookup.person", { email: email3, ...excluded(options) }), mode);
48642
- });
49030
+ for (const kind of ["company", "person"]) {
49031
+ excludeList(lookup.command(`${kind} [identity]`).option("--linkedin <url>", `Look up a ${kind} by LinkedIn URL`).option("--ids <ids>", "Company ids, comma-separated (up to 100)")).action(async (identity, options, command) => {
49032
+ if (Number(Boolean(identity)) + Number(Boolean(options.linkedin)) + Number(Boolean(options.ids)) !== 1) throw new CliError("Supply one identity, --linkedin URL or --ids list.");
49033
+ if (options.ids && kind !== "company") throw new CliError("--ids is available for company lookup only.");
49034
+ const ids = options.ids?.split(",").map((id) => id.trim());
49035
+ if (ids && (!ids.length || ids.length > 100 || ids.some((id) => !/^hdo_[0-9a-f]{32}$/.test(id)))) throw new CliError("Use 1 to 100 opaque hdo_ company ids.");
49036
+ const { api, mode } = await session(command);
49037
+ emit(await api.tool(`lookup.${kind}`, { ...ids ? { ids } : options.linkedin ? { linkedinUrl: options.linkedin } : kind === "company" ? { domain: identity } : { email: identity }, ...excluded(options) }), mode);
49038
+ });
49039
+ }
48643
49040
  const lists = program2.command("lists").description("Create, refresh and export saved lists");
48644
49041
  addFilterOptions(lists.command("create <name>").option("--kind <kind>", "companies or people", "companies").option("--recurring", "Refresh automatically on each data release").option("--dry-run", "Preview without creating a list"), true).action(async (name2, options, command) => {
48645
49042
  if (options.kind !== "companies" && options.kind !== "people") throw new CliError("Choose --kind companies or people.");
@@ -48712,7 +49109,7 @@ function createProgram(overrides = {}) {
48712
49109
  } else program2.outputHelp();
48713
49110
  });
48714
49111
  program2.hook("preAction", async (_program, command) => {
48715
- if (["login", "update", "upgrade"].includes(command.name()) || ["mcp", "skills", "config"].includes(command.parent?.name() ?? "")) return;
49112
+ if (["login", "update", "upgrade", "guide"].includes(command.name()) || ["mcp", "skills", "config"].includes(command.parent?.name() ?? "")) return;
48716
49113
  if (command === program2 && (!runtime.updater.stdinTTY || !runtime.updater.stdoutTTY || Object.hasOwn(runtime.env, "CI") || program2.opts().json || program2.opts().jsonl)) return;
48717
49114
  const settings = resolveSettings(await runtime.readConfig(), command.optsWithGlobals(), runtime.env);
48718
49115
  await checkCliVersion({ apiUrl: settings.apiUrl, version: package_default.version, cache: runtime.versionCache, fetchImpl: runtime.fetchImpl, now: runtime.now, warn: runtime.err });