higherdata 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/index.js +350 -84
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ node packages/cli/dist/index.js --version
|
|
|
8
8
|
node packages/cli/dist/index.js --help
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
The package provides `higherdata`
|
|
11
|
+
The package provides the `higherdata` command. After the first approved npm release,
|
|
12
12
|
install with `npm install --global higherdata`, or run `npx higherdata --version`.
|
|
13
13
|
Global flags are `--json`, `--jsonl` and `--api-url <url>`.
|
|
14
14
|
`--help` and `--version` work without credentials or a config file.
|
|
@@ -39,7 +39,7 @@ Exit codes: 0 success, 2 usage or configuration, 3 authentication, 4 limit,
|
|
|
39
39
|
|
|
40
40
|
## Interactive app
|
|
41
41
|
|
|
42
|
-
Run `higherdata`
|
|
42
|
+
Run `higherdata` in a terminal to open the keyboard app. The yellow
|
|
43
43
|
wordmark and three bars form in under 1.5 seconds; any key skips it.
|
|
44
44
|
`--no-banner` opens the menu immediately. Piped output, `--json`, `--jsonl`
|
|
45
45
|
and `CI` never animate. `NO_COLOR` disables accent colors. Supports 80 columns
|
|
@@ -222,3 +222,5 @@ The metadata request times out after three seconds. On network failure the CLI
|
|
|
222
222
|
uses its last known policy; with no cached policy it allows the command. Failed
|
|
223
223
|
checks are cached for a day too. A read-only cache directory allows commands but
|
|
224
224
|
cannot persist the daily check, so subsequent invocations may check again.
|
|
225
|
+
|
|
226
|
+
Download the loaded search page to a new file with `higherdata search companies --country-code CA --out results.csv`. Add `--all` to download up to 5,000 rows, or use a `.jsonl` destination for JSON Lines. The interactive results screen offers "This page" and "All results" when you press `d`. For larger sets, save the search to a list and export the list. Existing files are never overwritten.
|
package/dist/index.js
CHANGED
|
@@ -28133,6 +28133,10 @@ function retryAfter(response, data, now) {
|
|
|
28133
28133
|
const date5 = Date.parse(value);
|
|
28134
28134
|
if (Number.isFinite(date5)) return Math.max(0, (date5 - now) / 1e3);
|
|
28135
28135
|
}
|
|
28136
|
+
if (response.status === 429) {
|
|
28137
|
+
const reset = response.headers.get("ratelimit-reset");
|
|
28138
|
+
if (reset !== null && /^\d+(?:\.\d+)?$/.test(reset.trim())) return Number(reset);
|
|
28139
|
+
}
|
|
28136
28140
|
const parsed = failure2.safeParse(data);
|
|
28137
28141
|
return parsed.success && typeof parsed.data.error !== "string" ? parsed.data.error.retry_after : void 0;
|
|
28138
28142
|
}
|
|
@@ -28340,8 +28344,16 @@ var init_api2 = __esm({
|
|
|
28340
28344
|
async request(path2, input2, options = {}) {
|
|
28341
28345
|
if (options.authenticated && !this.settings.apiKey) throw new CliError("Run higherdata login or set HIGHERDATA_API_KEY.", EXIT_CODES.AUTH);
|
|
28342
28346
|
try {
|
|
28343
|
-
|
|
28344
|
-
|
|
28347
|
+
for (let attempt = 0; ; attempt++) {
|
|
28348
|
+
try {
|
|
28349
|
+
return await this.transport.request(path2, input2, { ...options, idempotencyKey: this.settings.idempotencyKey });
|
|
28350
|
+
} catch (error63) {
|
|
28351
|
+
if (!(error63 instanceof HigherDataError) || error63.status !== 429 || !options.rateLimitRecovery || attempt >= 5) throw error63;
|
|
28352
|
+
const wait = (error63.retryAfter ?? 1) * 1e3;
|
|
28353
|
+
if (!Number.isFinite(wait) || wait > 3e5) throw error63;
|
|
28354
|
+
await options.rateLimitRecovery.sleep(Math.max(100, wait));
|
|
28355
|
+
}
|
|
28356
|
+
}
|
|
28345
28357
|
} catch (error63) {
|
|
28346
28358
|
if (error63 instanceof HigherDataError) {
|
|
28347
28359
|
if (error63.status) throw apiFailure(error63.status, { error: { code: error63.code, message: error63.message } });
|
|
@@ -31914,11 +31926,10 @@ var init_package = __esm({
|
|
|
31914
31926
|
"package.json"() {
|
|
31915
31927
|
package_default = {
|
|
31916
31928
|
name: "higherdata",
|
|
31917
|
-
version: "0.
|
|
31929
|
+
version: "0.3.0",
|
|
31918
31930
|
type: "module",
|
|
31919
31931
|
bin: {
|
|
31920
|
-
higherdata: "dist/index.js"
|
|
31921
|
-
hd: "dist/index.js"
|
|
31932
|
+
higherdata: "dist/index.js"
|
|
31922
31933
|
},
|
|
31923
31934
|
files: [
|
|
31924
31935
|
"dist",
|
|
@@ -34895,6 +34906,9 @@ function cell(value) {
|
|
|
34895
34906
|
const text2 = value == null ? "" : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
34896
34907
|
return text2.replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ");
|
|
34897
34908
|
}
|
|
34909
|
+
function lookupMatchColumns(row) {
|
|
34910
|
+
return { kind: cell(row.kind), name: cell(row.label), details: cell(row.detail) };
|
|
34911
|
+
}
|
|
34898
34912
|
function customerOutput(value) {
|
|
34899
34913
|
if (Array.isArray(value)) return value.map(customerOutput);
|
|
34900
34914
|
if (!value || typeof value !== "object") return value;
|
|
@@ -34904,10 +34918,10 @@ function renderOutput(value, mode) {
|
|
|
34904
34918
|
value = customerOutput(value);
|
|
34905
34919
|
if (mode === "json") return `${JSON.stringify(value)}
|
|
34906
34920
|
`;
|
|
34907
|
-
const records = Array.isArray(value.items) ? value.items : Array.isArray(value.candidates) ? value.candidates : [value];
|
|
34921
|
+
const records = Array.isArray(value.items) ? value.items : Array.isArray(value.candidates) ? value.candidates : Array.isArray(value.matches) ? value.matches : [value];
|
|
34908
34922
|
if (mode === "jsonl") return records.map((record2) => JSON.stringify(record2)).join("\n") + (records.length ? "\n" : "");
|
|
34909
34923
|
if (!records.length) return "No results.\n";
|
|
34910
|
-
const rows = records.map((record2) => record2);
|
|
34924
|
+
const rows = records.map((record2) => Array.isArray(value.matches) ? lookupMatchColumns(record2) : record2);
|
|
34911
34925
|
const columns = rows.some((row) => "companyName" in row) ? rows.some((row) => "fullName" in row) ? ["id", "fullName", "jobTitle", "companyName", "emailAddress"] : ["id", "companyName", "normalizedDomain", "headquartersCountryCode"] : Object.keys(rows[0]);
|
|
34912
34926
|
const headings = columns.map((column) => column.replace(/([a-z])([A-Z])/gu, "$1 $2").toUpperCase());
|
|
34913
34927
|
const widths = columns.map((column, index) => rows.reduce((width, row) => Math.min(64, Math.max(width, cell(row[column]).length)), headings[index].length));
|
|
@@ -34923,6 +34937,105 @@ var init_output = __esm({
|
|
|
34923
34937
|
}
|
|
34924
34938
|
});
|
|
34925
34939
|
|
|
34940
|
+
// ../../src/lib/csv/search-download.ts
|
|
34941
|
+
function csvCell(value) {
|
|
34942
|
+
let text2 = value === null || value === void 0 ? "" : String(value);
|
|
34943
|
+
if (/^[\s\u0000-\u001f]*[=+\-@]/.test(text2)) text2 = `'${text2}`;
|
|
34944
|
+
return `"${text2.replaceAll('"', '""')}"`;
|
|
34945
|
+
}
|
|
34946
|
+
function pageCsv(headers, rows) {
|
|
34947
|
+
const lines = [headers.map(csvCell).join(","), ...rows.map((row) => row.map(csvCell).join(","))];
|
|
34948
|
+
return `${lines.join("\r\n")}\r
|
|
34949
|
+
`;
|
|
34950
|
+
}
|
|
34951
|
+
function companyPageCsv(items) {
|
|
34952
|
+
return pageCsv(
|
|
34953
|
+
[...COMPANY_DOWNLOAD_FIELDS],
|
|
34954
|
+
items.map((item) => COMPANY_DOWNLOAD_FIELDS.map((field) => item[field]))
|
|
34955
|
+
);
|
|
34956
|
+
}
|
|
34957
|
+
function peoplePageCsv(items) {
|
|
34958
|
+
return pageCsv(
|
|
34959
|
+
[...PEOPLE_DOWNLOAD_FIELDS],
|
|
34960
|
+
items.map((item) => PEOPLE_DOWNLOAD_FIELDS.map((field) => item[field]))
|
|
34961
|
+
);
|
|
34962
|
+
}
|
|
34963
|
+
function searchPageJsonl(kind, items) {
|
|
34964
|
+
const fields = kind === "companies" ? COMPANY_DOWNLOAD_FIELDS : PEOPLE_DOWNLOAD_FIELDS;
|
|
34965
|
+
return items.map((item) => JSON.stringify(Object.fromEntries(fields.map((field) => [field, item[field] ?? null]))) + "\n").join("");
|
|
34966
|
+
}
|
|
34967
|
+
var COMPANY_DOWNLOAD_FIELDS, PEOPLE_DOWNLOAD_FIELDS;
|
|
34968
|
+
var init_search_download = __esm({
|
|
34969
|
+
"../../src/lib/csv/search-download.ts"() {
|
|
34970
|
+
"use strict";
|
|
34971
|
+
COMPANY_DOWNLOAD_FIELDS = ["id", "companyName", "normalizedDomain", "headquartersCountryCode", "industryLinkedin", "employeeCountEstimate"];
|
|
34972
|
+
PEOPLE_DOWNLOAD_FIELDS = ["id", "fullName", "jobTitle", "companyName", "emailAddress", "countryCode"];
|
|
34973
|
+
}
|
|
34974
|
+
});
|
|
34975
|
+
|
|
34976
|
+
// src/search-download.ts
|
|
34977
|
+
import { open as open5 } from "fs/promises";
|
|
34978
|
+
function searchDownloadFormat(path2) {
|
|
34979
|
+
if (!/\.(csv|jsonl)$/.test(path2)) throw new CliError("Choose a new .csv or .jsonl destination.");
|
|
34980
|
+
return path2.endsWith(".jsonl") ? "jsonl" : "csv";
|
|
34981
|
+
}
|
|
34982
|
+
async function downloadSearch(api, name, input2, path2, options) {
|
|
34983
|
+
const format2 = searchDownloadFormat(path2);
|
|
34984
|
+
if (options.all && api.settings.idempotencyKey) throw new CliError("An idempotency key applies to one page. Omit --all.");
|
|
34985
|
+
const file2 = await open5(path2, "wx", 384);
|
|
34986
|
+
let count = 0, first = true, cursor = input2.cursor, loaded = options.loadedPage;
|
|
34987
|
+
const seen = /* @__PURE__ */ new Set();
|
|
34988
|
+
if (typeof cursor === "string") seen.add(cursor);
|
|
34989
|
+
try {
|
|
34990
|
+
while (true) {
|
|
34991
|
+
let raw, headers;
|
|
34992
|
+
if (loaded) {
|
|
34993
|
+
raw = loaded;
|
|
34994
|
+
loaded = void 0;
|
|
34995
|
+
} else {
|
|
34996
|
+
const response = await api.request(`/api/v1/tools/${name}`, { ...input2, ...cursor === void 0 ? {} : { cursor } }, { authenticated: true, rateLimitRecovery: options });
|
|
34997
|
+
raw = response.data;
|
|
34998
|
+
headers = response.headers;
|
|
34999
|
+
}
|
|
35000
|
+
const page = pageSchema2.safeParse(raw);
|
|
35001
|
+
if (!page.success || page.data.items.some((item) => typeof item.id !== "string" || !(name === "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.");
|
|
35002
|
+
const items = page.data.items.slice(0, SEARCH_DOWNLOAD_CAP - count);
|
|
35003
|
+
if (format2 === "jsonl") await file2.writeFile(searchPageJsonl(name === "search.companies" ? "companies" : "people", items));
|
|
35004
|
+
else {
|
|
35005
|
+
const csv = (name === "search.companies" ? companyPageCsv : peoplePageCsv)(items);
|
|
35006
|
+
await file2.writeFile(first ? csv : csv.slice(csv.indexOf("\r\n") + 2));
|
|
35007
|
+
}
|
|
35008
|
+
first = false;
|
|
35009
|
+
count += items.length;
|
|
35010
|
+
options.progress?.(`Downloaded ${count.toLocaleString("en-US")} of 5,000`);
|
|
35011
|
+
const next = page.data.nextCursor;
|
|
35012
|
+
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) };
|
|
35013
|
+
if (seen.has(next)) throw new CliError("The API repeated a cursor. Pagination stopped.");
|
|
35014
|
+
seen.add(next);
|
|
35015
|
+
cursor = next;
|
|
35016
|
+
if (headers?.get("ratelimit-remaining") === "0") {
|
|
35017
|
+
const reset = headers.get("ratelimit-reset");
|
|
35018
|
+
if (reset !== null && /^\d+(?:\.\d+)?$/.test(reset.trim())) await options.sleep(Number(reset) * 1e3);
|
|
35019
|
+
}
|
|
35020
|
+
}
|
|
35021
|
+
} finally {
|
|
35022
|
+
await file2.close();
|
|
35023
|
+
}
|
|
35024
|
+
}
|
|
35025
|
+
var SEARCH_DOWNLOAD_CAP, SEARCH_DOWNLOAD_CAP_NOTICE, isSearchResults, pageSchema2;
|
|
35026
|
+
var init_search_download2 = __esm({
|
|
35027
|
+
"src/search-download.ts"() {
|
|
35028
|
+
"use strict";
|
|
35029
|
+
init_zod();
|
|
35030
|
+
init_search_download();
|
|
35031
|
+
init_api2();
|
|
35032
|
+
SEARCH_DOWNLOAD_CAP = 5e3;
|
|
35033
|
+
SEARCH_DOWNLOAD_CAP_NOTICE = "Download stopped at 5,000 rows. Save the search to a list and export the list for larger sets.";
|
|
35034
|
+
isSearchResults = (name) => name === "search.companies" || name === "search.people";
|
|
35035
|
+
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() });
|
|
35036
|
+
}
|
|
35037
|
+
});
|
|
35038
|
+
|
|
34926
35039
|
// ../../node_modules/smol-toml/dist/error.js
|
|
34927
35040
|
function getLineColFromPtr(string4, ptr) {
|
|
34928
35041
|
let lines = string4.slice(0, ptr).split(/\r?\n/);
|
|
@@ -37343,7 +37456,7 @@ var init_main = __esm({
|
|
|
37343
37456
|
|
|
37344
37457
|
// src/install.ts
|
|
37345
37458
|
import { constants } from "fs";
|
|
37346
|
-
import { access, chmod as chmod2, lstat, mkdir as mkdir3, open as
|
|
37459
|
+
import { access, chmod as chmod2, lstat, mkdir as mkdir3, open as open6, readFile as readFile3, readdir, rename as rename3, unlink as unlink3 } from "fs/promises";
|
|
37347
37460
|
import { execFile as execFile2 } from "child_process";
|
|
37348
37461
|
import { promisify, isDeepStrictEqual } from "util";
|
|
37349
37462
|
import { homedir as homedir2 } from "os";
|
|
@@ -37417,7 +37530,7 @@ async function safeParents(path2) {
|
|
|
37417
37530
|
await mkdir3(dirname3(path2), { recursive: true, mode: 448 });
|
|
37418
37531
|
}
|
|
37419
37532
|
async function privateFile(path2, text2) {
|
|
37420
|
-
const file2 = await
|
|
37533
|
+
const file2 = await open6(path2, "wx", 384);
|
|
37421
37534
|
try {
|
|
37422
37535
|
await file2.writeFile(text2, "utf8");
|
|
37423
37536
|
} finally {
|
|
@@ -38050,7 +38163,7 @@ var init_model = __esm({
|
|
|
38050
38163
|
["account", "Account", "Usage, limits and API keys"],
|
|
38051
38164
|
["icp", "ICP", "Build your ideal customer profile"]
|
|
38052
38165
|
];
|
|
38053
|
-
groupTools = (group) => TOOL_CATALOG.filter((tool) => tool.name.startsWith(`${group}.`));
|
|
38166
|
+
groupTools = (group) => TOOL_CATALOG.filter((tool) => tool.name.startsWith(`${group}.`) && tool.name !== "lookup.find");
|
|
38054
38167
|
toolByName = (name) => TOOL_CATALOG.find((tool) => tool.name === name);
|
|
38055
38168
|
clean = (value) => String(value ?? "").replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ");
|
|
38056
38169
|
title = (value) => value.replaceAll("_", " ").replace(/([a-z])([A-Z])/gu, "$1 $2").replace(/\b\w/gu, (c) => c.toUpperCase());
|
|
@@ -38138,19 +38251,43 @@ var init_install_menu = __esm({
|
|
|
38138
38251
|
});
|
|
38139
38252
|
|
|
38140
38253
|
// src/terminal/banner.ts
|
|
38141
|
-
function
|
|
38142
|
-
const
|
|
38143
|
-
|
|
38144
|
-
|
|
38145
|
-
|
|
38146
|
-
|
|
38147
|
-
const
|
|
38148
|
-
|
|
38149
|
-
|
|
38254
|
+
function markFrame(size, progress = [1, 1, 1]) {
|
|
38255
|
+
const scale = size === "large" ? 3 : 1;
|
|
38256
|
+
return [8, 4, 6].flatMap((units, index) => {
|
|
38257
|
+
const width = Math.round(units * scale * Math.max(0, Math.min(1, progress[index])));
|
|
38258
|
+
const indent = " ".repeat([0, 2, 1][index] * scale);
|
|
38259
|
+
const cap = width > 2 ? indent + " " + "\u2584".repeat(width - 2) : "";
|
|
38260
|
+
const base2 = width ? indent + "\u2580".repeat(width) : "";
|
|
38261
|
+
return size === "large" ? [cap, width ? indent + "\u2588".repeat(width) : "", cap.replaceAll("\u2584", "\u2580")] : [cap, base2];
|
|
38262
|
+
});
|
|
38263
|
+
}
|
|
38264
|
+
function wordmarkFrame(frame = BANNER_FRAMES - 1) {
|
|
38265
|
+
const visible = frame < 5 ? Math.ceil((frame + 1) / 5 * WORDMARK_WIDTH) : WORDMARK_WIDTH;
|
|
38266
|
+
return Array.from({ length: 6 }, (_, row) => {
|
|
38267
|
+
const higher = [..."HIGHER"].map((letter) => GLYPHS[letter][row]).join(" ") + " ";
|
|
38268
|
+
const data = [..."DATA"].map((letter) => GLYPHS[letter][row]).join(" ");
|
|
38269
|
+
return { higher: higher.slice(0, visible), data: data.slice(0, Math.max(0, visible - higher.length)) };
|
|
38150
38270
|
});
|
|
38151
|
-
return [...full.map((line) => " ".repeat(indent) + line.slice(0, visible)), "", ...logo, frame >= 10 ? " ".repeat(Math.max(0, Math.floor((columns - 23) / 2))) + "HIGHERDATA by HigherOps" : ""].map((line) => line.trimEnd());
|
|
38152
38271
|
}
|
|
38153
|
-
|
|
38272
|
+
function homeBrandLayout(columns, rows, extraRows = 0) {
|
|
38273
|
+
const sideBySide = columns - 4 >= WORDMARK_WIDTH + 3 + MARK_WIDTH;
|
|
38274
|
+
const headerRows = sideBySide ? 10 : 17;
|
|
38275
|
+
return { large: columns - 4 >= WORDMARK_WIDTH && rows >= headerRows + 14 + extraRows, sideBySide, headerRows };
|
|
38276
|
+
}
|
|
38277
|
+
function busyFrame(frame, size = "large") {
|
|
38278
|
+
return markFrame(size, [0, 1, 2].map((index) => 0.65 + 0.35 * (1 + Math.sin(frame * Math.PI / 4 - index * Math.PI / 2)) / 2));
|
|
38279
|
+
}
|
|
38280
|
+
function bannerFrame(frame, columns) {
|
|
38281
|
+
const wordmark = wordmarkFrame(frame).map((line) => line.higher + line.data);
|
|
38282
|
+
const indent = " ".repeat(Math.max(0, Math.floor((columns - WORDMARK_WIDTH) / 2)));
|
|
38283
|
+
const logo = markFrame("large", [0, 1, 2].map((index) => Math.max(0, Math.min(1, (frame - 5 - index) / 2))));
|
|
38284
|
+
const markIndent = " ".repeat(Math.max(0, Math.floor((columns - MARK_WIDTH) / 2)));
|
|
38285
|
+
return [...wordmark.map((line) => indent + line), "", ...logo.map((line) => markIndent + line), frame >= 10 ? " ".repeat(Math.max(0, Math.floor((columns - 23) / 2))) + "HIGHERDATA by HigherOps" : ""].map((line) => line.trimEnd());
|
|
38286
|
+
}
|
|
38287
|
+
function animationOptions(args = process.argv.slice(2)) {
|
|
38288
|
+
return { banner: !args.includes("--no-banner"), json: args.includes("--json"), jsonl: args.includes("--jsonl") };
|
|
38289
|
+
}
|
|
38290
|
+
var GLYPHS, BANNER_FRAMES, BANNER_DURATION_MS, MARK_WIDTH, WORDMARK_WIDTH, shouldAnimate;
|
|
38154
38291
|
var init_banner = __esm({
|
|
38155
38292
|
"src/terminal/banner.ts"() {
|
|
38156
38293
|
"use strict";
|
|
@@ -38166,13 +38303,15 @@ var init_banner = __esm({
|
|
|
38166
38303
|
};
|
|
38167
38304
|
BANNER_FRAMES = 11;
|
|
38168
38305
|
BANNER_DURATION_MS = 1210;
|
|
38169
|
-
|
|
38306
|
+
MARK_WIDTH = 24;
|
|
38307
|
+
WORDMARK_WIDTH = [..."HIGHERDATA"].reduce((width, letter) => width + GLYPHS[letter][0].length, "HIGHERDATA".length - 1);
|
|
38170
38308
|
shouldAnimate = (tty, options, env) => tty && options.banner !== false && !options.json && !options.jsonl && !Object.hasOwn(env, "CI");
|
|
38171
38309
|
}
|
|
38172
38310
|
});
|
|
38173
38311
|
|
|
38174
38312
|
// src/terminal/screens.tsx
|
|
38175
|
-
import {
|
|
38313
|
+
import { useEffect, useState } from "react";
|
|
38314
|
+
import { Box, Text, useStdout } from "ink";
|
|
38176
38315
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
38177
38316
|
function Menu({ items, selected, limit, color, width }) {
|
|
38178
38317
|
const start = Math.max(0, Math.min(selected - Math.floor(limit / 2), items.length - limit));
|
|
@@ -38195,17 +38334,54 @@ function Menu({ items, selected, limit, color, width }) {
|
|
|
38195
38334
|
] })
|
|
38196
38335
|
] });
|
|
38197
38336
|
}
|
|
38337
|
+
function Mark({ lines, color }) {
|
|
38338
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx(Text, { color, children: line || " " }, i)) });
|
|
38339
|
+
}
|
|
38340
|
+
function Wordmark({ color, frame }) {
|
|
38341
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: WORDMARK_WIDTH, children: wordmarkFrame(frame).map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
38342
|
+
line.higher,
|
|
38343
|
+
/* @__PURE__ */ jsx(Text, { color, children: line.data })
|
|
38344
|
+
] }, i)) });
|
|
38345
|
+
}
|
|
38346
|
+
function BusyMark({ color, size = "large", options = animationOptions(), env = process.env }) {
|
|
38347
|
+
const { stdout } = useStdout(), [frame, setFrame] = useState(0);
|
|
38348
|
+
const animate = shouldAnimate(Boolean(stdout.isTTY), options, env);
|
|
38349
|
+
useEffect(() => {
|
|
38350
|
+
if (!animate) return;
|
|
38351
|
+
const timer = setInterval(() => setFrame((value) => (value + 1) % 8), 120);
|
|
38352
|
+
return () => clearInterval(timer);
|
|
38353
|
+
}, [animate]);
|
|
38354
|
+
return /* @__PURE__ */ jsx(Mark, { lines: animate ? busyFrame(frame, size) : markFrame(size), color });
|
|
38355
|
+
}
|
|
38198
38356
|
function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "default", space = "Default", workspace = "HigherOps", remaining = "--" }) {
|
|
38199
38357
|
const width = Math.max(40, columns), color = colors ? "#f2b705" : void 0;
|
|
38200
|
-
const limit = Math.max(4, rows -
|
|
38358
|
+
const limit = Math.max(4, rows - 15);
|
|
38359
|
+
const extraRows = [state.error, state.notice].reduce((count, text2) => count + Math.ceil(clean(text2).length / (width - 4)), 0);
|
|
38360
|
+
const brand = homeBrandLayout(width, rows, extraRows), largeHome = state.screen === "home" && brand.large;
|
|
38201
38361
|
if (state.screen === "banner") return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, children: [
|
|
38202
|
-
|
|
38362
|
+
/* @__PURE__ */ jsx(Box, { paddingLeft: Math.max(0, Math.floor((width - WORDMARK_WIDTH) / 2)), children: /* @__PURE__ */ jsx(Wordmark, { color, frame: state.frame }) }),
|
|
38363
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
38364
|
+
/* @__PURE__ */ jsx(Mark, { lines: bannerFrame(state.frame, width).slice(7, 16), color }),
|
|
38365
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: bannerFrame(state.frame, width)[16] || " " }),
|
|
38203
38366
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press any key to start" })
|
|
38204
38367
|
] });
|
|
38205
38368
|
const fields = state.tool ? fieldsFor(state.tool.input, state.input, state.variant) : [];
|
|
38206
38369
|
let help = "Up/Down choose. Enter opens. Esc back. Ctrl+C quit.";
|
|
38207
38370
|
let content;
|
|
38208
|
-
if (state.screen === "home")
|
|
38371
|
+
if (state.screen === "home") help = "Up/Down choose. Enter opens. / Look up. Ctrl+C quit.";
|
|
38372
|
+
if (state.screen === "home") content = /* @__PURE__ */ jsx(Menu, { items: homeItems, selected: state.selected, limit: Math.max(4, rows - (largeHome ? brand.headerRows : 6) - 8 - extraRows), color, width: width - 4 });
|
|
38373
|
+
if (state.screen === "lookup") {
|
|
38374
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38375
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Look up" }),
|
|
38376
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Name, work email, international phone, domain, LinkedIn or record id" }),
|
|
38377
|
+
/* @__PURE__ */ jsxs(Text, { color, wrap: "wrap", children: [
|
|
38378
|
+
"> ",
|
|
38379
|
+
clean(state.text),
|
|
38380
|
+
"_"
|
|
38381
|
+
] })
|
|
38382
|
+
] });
|
|
38383
|
+
help = "Type or paste. Enter looks up. Esc home.";
|
|
38384
|
+
}
|
|
38209
38385
|
if (state.screen === "install") {
|
|
38210
38386
|
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38211
38387
|
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
@@ -38276,6 +38452,12 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
|
|
|
38276
38452
|
] });
|
|
38277
38453
|
help = "Enter edit/run. Arrays: comma separated. Blank clears. Esc back.";
|
|
38278
38454
|
}
|
|
38455
|
+
if (state.screen === "download-choice") {
|
|
38456
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38457
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Download search results" }),
|
|
38458
|
+
/* @__PURE__ */ jsx(Menu, { items: [{ label: "This page", hint: "Loaded rows, no new request" }, { label: "All results", hint: "Same search, up to 5,000 rows" }], selected: state.selected, limit: 2, color, width: width - 4 })
|
|
38459
|
+
] });
|
|
38460
|
+
}
|
|
38279
38461
|
if (["edit", "upload", "download", "space"].includes(state.screen)) {
|
|
38280
38462
|
const field = fields.find((field2) => field2.path === state.field);
|
|
38281
38463
|
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -38297,7 +38479,7 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
|
|
|
38297
38479
|
}
|
|
38298
38480
|
if (state.screen === "busy") {
|
|
38299
38481
|
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38300
|
-
|
|
38482
|
+
/* @__PURE__ */ jsx(BusyMark, { color, size: rows >= 28 ? "large" : "compact" }),
|
|
38301
38483
|
/* @__PURE__ */ jsx(Text, { children: state.notice || "Working..." })
|
|
38302
38484
|
] });
|
|
38303
38485
|
help = "The current operation is running. It is never retried automatically.";
|
|
@@ -38316,35 +38498,58 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
|
|
|
38316
38498
|
}
|
|
38317
38499
|
if (state.screen === "results") {
|
|
38318
38500
|
const items = rowsOf(state.result);
|
|
38319
|
-
|
|
38320
|
-
|
|
38321
|
-
|
|
38322
|
-
|
|
38323
|
-
|
|
38324
|
-
|
|
38325
|
-
|
|
38326
|
-
|
|
38327
|
-
|
|
38328
|
-
|
|
38329
|
-
|
|
38330
|
-
|
|
38331
|
-
|
|
38332
|
-
|
|
38333
|
-
|
|
38334
|
-
|
|
38335
|
-
|
|
38336
|
-
|
|
38337
|
-
|
|
38338
|
-
] }
|
|
38339
|
-
|
|
38340
|
-
|
|
38341
|
-
|
|
38342
|
-
|
|
38343
|
-
|
|
38344
|
-
|
|
38345
|
-
|
|
38346
|
-
|
|
38347
|
-
|
|
38501
|
+
if (state.tool?.name === "lookup.find") {
|
|
38502
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38503
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
38504
|
+
"Look up: ",
|
|
38505
|
+
clean(state.input.query)
|
|
38506
|
+
] }),
|
|
38507
|
+
items.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38508
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "KIND / NAME / DETAILS" }),
|
|
38509
|
+
/* @__PURE__ */ jsx(Menu, { items: items.map((row) => {
|
|
38510
|
+
const match = lookupMatchColumns(row);
|
|
38511
|
+
return { label: `${match.kind}: ${match.name}`, hint: match.details };
|
|
38512
|
+
}), selected: state.selected, limit: limit - 2, color, width: width - 4 })
|
|
38513
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38514
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
38515
|
+
"No match for ",
|
|
38516
|
+
clean(state.input.query)
|
|
38517
|
+
] }),
|
|
38518
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Try Search to find companies or people with filters." })
|
|
38519
|
+
] })
|
|
38520
|
+
] });
|
|
38521
|
+
help = "Enter detail. E edit lookup. Esc home.";
|
|
38522
|
+
} else {
|
|
38523
|
+
const list = state.tool?.name === "lists.get" && state.input.id && Array.isArray(state.result.lists) ? state.result.lists[0] : void 0;
|
|
38524
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38525
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: state.tool?.name ?? "Enrichment job" }),
|
|
38526
|
+
list && /* @__PURE__ */ jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [
|
|
38527
|
+
clean(list.name),
|
|
38528
|
+
" / ",
|
|
38529
|
+
clean(list.type),
|
|
38530
|
+
" / ",
|
|
38531
|
+
list.recurringEnabled ? "Each release" : "On demand",
|
|
38532
|
+
" / ",
|
|
38533
|
+
clean(list.maxRecords),
|
|
38534
|
+
" records / ",
|
|
38535
|
+
clean(list.maxNewRecordsPerRefresh),
|
|
38536
|
+
" new records per refresh"
|
|
38537
|
+
] }),
|
|
38538
|
+
items.length ? /* @__PURE__ */ jsx(Menu, { items: items.map((row) => ({ label: rowLabel(row), hint: row.matchesCriteria === false ? "No longer matches" : clean(row.status ?? row.jobTitle ?? row.normalizedDomain ?? row.domain ?? row.headquartersCountryCode) })), selected: state.selected, limit: limit - (list ? 3 : 2), color, width: width - 4 }) : recordFields(state.result).slice(state.selected, state.selected + limit).map(([key, value]) => /* @__PURE__ */ jsxs(Text, { wrap: "truncate-end", children: [
|
|
38539
|
+
key,
|
|
38540
|
+
": ",
|
|
38541
|
+
value
|
|
38542
|
+
] }, key)),
|
|
38543
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
38544
|
+
items.length ? `${items.length} on this page. ` : "",
|
|
38545
|
+
state.result.nextCursor ? "More records available." : "End of page."
|
|
38546
|
+
] })
|
|
38547
|
+
] });
|
|
38548
|
+
help = "Enter detail. N next page. R refresh. D download. E edit inputs. Esc back.";
|
|
38549
|
+
if (!state.tool) help = "E installation options. Esc home. ";
|
|
38550
|
+
if (state.tool?.name === "searches.list") help = "Enter reopen search. A create audience. N next page. E edit. Esc back.";
|
|
38551
|
+
if (state.tool?.name === "lists.get" && state.input.id) help += " P preview refresh.";
|
|
38552
|
+
}
|
|
38348
38553
|
}
|
|
38349
38554
|
if (state.screen === "detail") {
|
|
38350
38555
|
const row = detailRecord(state.row), isCore = coreRecord(state.row);
|
|
@@ -38391,26 +38596,45 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
|
|
|
38391
38596
|
help = "Enter sends. Ctrl+B step limit. Ctrl+N new conversation. Esc home.";
|
|
38392
38597
|
}
|
|
38393
38598
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, borderStyle: "double", paddingX: 1, children: [
|
|
38394
|
-
/* @__PURE__ */ jsxs(
|
|
38599
|
+
largeHome ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38600
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: brand.sideBySide ? "row" : "column", gap: brand.sideBySide ? 3 : 1, children: [
|
|
38601
|
+
/* @__PURE__ */ jsx(Wordmark, { color }),
|
|
38602
|
+
/* @__PURE__ */ jsx(Mark, { lines: markFrame("large"), color })
|
|
38603
|
+
] }),
|
|
38604
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [
|
|
38605
|
+
"by HigherOps / ",
|
|
38606
|
+
clean(profile),
|
|
38607
|
+
" / ",
|
|
38608
|
+
clean(workspace),
|
|
38609
|
+
" / ",
|
|
38610
|
+
clean(space),
|
|
38611
|
+
" / ",
|
|
38612
|
+
clean(remaining),
|
|
38613
|
+
" email verifications remaining"
|
|
38614
|
+
] })
|
|
38615
|
+
] }) : /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
38395
38616
|
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
38396
|
-
/* @__PURE__ */ jsx(
|
|
38617
|
+
/* @__PURE__ */ jsx(Mark, { lines: markFrame("compact"), color }),
|
|
38397
38618
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
38398
|
-
/* @__PURE__ */
|
|
38619
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
38620
|
+
"Higher",
|
|
38621
|
+
/* @__PURE__ */ jsx(Text, { color, children: "Data" })
|
|
38622
|
+
] }),
|
|
38399
38623
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "by HigherOps" }),
|
|
38400
38624
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: clean(profile) })
|
|
38401
38625
|
] })
|
|
38402
38626
|
] }),
|
|
38403
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "flex-end", children: [
|
|
38627
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "flex-end", flexShrink: 1, children: [
|
|
38404
38628
|
/* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: clean(workspace) }),
|
|
38405
38629
|
/* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "truncate-end", children: clean(space) }),
|
|
38406
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
38630
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [
|
|
38407
38631
|
clean(remaining),
|
|
38408
38632
|
" email verifications remaining"
|
|
38409
38633
|
] })
|
|
38410
38634
|
] })
|
|
38411
38635
|
] }),
|
|
38412
38636
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2550".repeat(Math.max(1, width - 4)) }),
|
|
38413
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", width: width - 4, minHeight: Math.min(rows -
|
|
38637
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", width: width - 4, minHeight: largeHome ? 0 : Math.max(0, Math.min(rows - 16, 13)), children: content }),
|
|
38414
38638
|
state.error && /* @__PURE__ */ jsx(Text, { color: colors ? "red" : void 0, children: clean(state.error) }),
|
|
38415
38639
|
state.notice && state.screen !== "busy" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: clean(state.notice) }),
|
|
38416
38640
|
/* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: help })
|
|
@@ -38420,6 +38644,7 @@ var initialState, accountItems, homeItems;
|
|
|
38420
38644
|
var init_screens = __esm({
|
|
38421
38645
|
"src/terminal/screens.tsx"() {
|
|
38422
38646
|
"use strict";
|
|
38647
|
+
init_output();
|
|
38423
38648
|
init_tool_commands();
|
|
38424
38649
|
init_banner();
|
|
38425
38650
|
init_install_menu();
|
|
@@ -38433,8 +38658,8 @@ var init_screens = __esm({
|
|
|
38433
38658
|
{ id: "skills-install", label: "Install skills", hint: "Five task guides for Claude Code or Codex" },
|
|
38434
38659
|
{ id: "quit", label: "Quit", hint: "" }
|
|
38435
38660
|
];
|
|
38436
|
-
homeItems = [["Find", ["search", "searches", "lookup", "icp", "ask"]], ["Work", ["lists", "enrich", "email", "jobs"]], ["Govern", ["suppress", "crm", "changes", "hygiene"]], ["Account", ["account"]]].flatMap(([section, ids]) => ids.map((id, index) => {
|
|
38437
|
-
const group = GROUPS.find(([name]) => name === id);
|
|
38661
|
+
homeItems = [["Find", ["find", "search", "searches", "lookup", "icp", "ask"]], ["Work", ["lists", "enrich", "email", "jobs"]], ["Govern", ["suppress", "crm", "changes", "hygiene"]], ["Account", ["account"]]].flatMap(([section, ids]) => ids.map((id, index) => {
|
|
38662
|
+
const group = id === "find" ? ["find", "Look up", "Name, email, phone, domain, LinkedIn or record id"] : GROUPS.find(([name]) => name === id);
|
|
38438
38663
|
return { id, label: group?.[1] ?? "Ask HigherData", hint: group?.[2] ?? "Plan a request, limit its steps", section: index === 0 ? section : "" };
|
|
38439
38664
|
}));
|
|
38440
38665
|
}
|
|
@@ -38449,25 +38674,25 @@ __export(app_exports, {
|
|
|
38449
38674
|
});
|
|
38450
38675
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
38451
38676
|
import { createReadStream as createReadStream2 } from "fs";
|
|
38452
|
-
import { open as
|
|
38453
|
-
import { useEffect, useRef, useState } from "react";
|
|
38677
|
+
import { open as open7 } from "fs/promises";
|
|
38678
|
+
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
38454
38679
|
import { render, useApp, useInput, useWindowSize } from "ink";
|
|
38455
38680
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
38456
38681
|
function InteractiveApp({ session }) {
|
|
38457
38682
|
const { exit } = useApp(), { columns, rows } = useWindowSize();
|
|
38458
38683
|
const settings = session.settings();
|
|
38459
|
-
const [chrome, setChrome] =
|
|
38460
|
-
const [state, setState] =
|
|
38684
|
+
const [chrome, setChrome] = useState2({ workspace: "Workspace", remaining: "--" });
|
|
38685
|
+
const [state, setState] = useState2(() => ({ ...initialState(), screen: shouldAnimate(true, session.options, session.runtime.env) ? "banner" : "home" }));
|
|
38461
38686
|
const current = useRef(state);
|
|
38462
38687
|
current.current = state;
|
|
38463
38688
|
const mounted = useRef(true), request = useRef(false), back = useRef(initialState());
|
|
38464
|
-
|
|
38689
|
+
useEffect2(() => {
|
|
38465
38690
|
mounted.current = true;
|
|
38466
38691
|
return () => {
|
|
38467
38692
|
mounted.current = false;
|
|
38468
38693
|
};
|
|
38469
38694
|
}, []);
|
|
38470
|
-
|
|
38695
|
+
useEffect2(() => {
|
|
38471
38696
|
let active = true;
|
|
38472
38697
|
if (!settings.apiKey || !settings.space) return;
|
|
38473
38698
|
void Promise.all([session.call("account.whoami", {}), session.call("account.usage", {})]).then(([identity, usage]) => {
|
|
@@ -38480,12 +38705,12 @@ function InteractiveApp({ session }) {
|
|
|
38480
38705
|
active = false;
|
|
38481
38706
|
};
|
|
38482
38707
|
}, [session, settings.apiKey, settings.space]);
|
|
38483
|
-
|
|
38708
|
+
useEffect2(() => {
|
|
38484
38709
|
if (state.screen !== "banner") return;
|
|
38485
38710
|
const timer = setInterval(() => setState((s) => s.frame >= BANNER_FRAMES - 1 ? { ...s, screen: "home" } : { ...s, frame: s.frame + 1 }), BANNER_DURATION_MS / BANNER_FRAMES);
|
|
38486
38711
|
return () => clearInterval(timer);
|
|
38487
38712
|
}, [state.screen]);
|
|
38488
|
-
|
|
38713
|
+
useEffect2(() => {
|
|
38489
38714
|
const job = state.result.job;
|
|
38490
38715
|
const id = job?.id ?? (state.tool?.name === "jobs.get" ? state.result.id : void 0);
|
|
38491
38716
|
const status = job?.status ?? state.result.status;
|
|
@@ -38543,6 +38768,7 @@ function InteractiveApp({ session }) {
|
|
|
38543
38768
|
function activate(item) {
|
|
38544
38769
|
const s = current.current;
|
|
38545
38770
|
if (item.id === "quit") exit();
|
|
38771
|
+
else if (item.id === "find") patch({ screen: "lookup", tool: toolByName("lookup.find"), input: {}, text: "", selected: 0 });
|
|
38546
38772
|
else if (item.id === "mcp-install" || item.id === "skills-install") patch({ screen: "install", group: item.id === "mcp-install" ? "mcp" : "skills", tool: null, input: { agent: "claude", scope: "user", profile: "default" }, selected: 0 });
|
|
38547
38773
|
else if (item.id === "space") {
|
|
38548
38774
|
back.current = s;
|
|
@@ -38569,12 +38795,27 @@ function InteractiveApp({ session }) {
|
|
|
38569
38795
|
return;
|
|
38570
38796
|
}
|
|
38571
38797
|
if (s.screen === "busy") return;
|
|
38798
|
+
if (s.screen === "home" && input2 === "/") {
|
|
38799
|
+
activate({ id: "find" });
|
|
38800
|
+
return;
|
|
38801
|
+
}
|
|
38572
38802
|
if (key.escape && s.screen === "filter-group") {
|
|
38573
38803
|
patch({ screen: "form", selected: searchGroups(s.tool.input, s.input).findIndex((group) => group.group === s.filterGroup) });
|
|
38574
38804
|
return;
|
|
38575
38805
|
}
|
|
38576
38806
|
if (key.escape) {
|
|
38577
|
-
patch(s.screen === "home" ? { screen: "home" } : ["edit", "download", "space", "upload"].includes(s.screen) ? { ...back.current } : { screen: s.screen === "detail" ? "results" : s.screen === "form" ? "tools" : "home", selected: 0 });
|
|
38807
|
+
patch(s.screen === "home" ? { screen: "home" } : ["edit", "download", "download-choice", "space", "upload"].includes(s.screen) ? { ...back.current } : { screen: s.screen === "detail" ? "results" : s.screen === "form" ? "tools" : "home", selected: 0 });
|
|
38808
|
+
return;
|
|
38809
|
+
}
|
|
38810
|
+
if (s.screen === "lookup") {
|
|
38811
|
+
if (key.return && s.text.trim()) void work("Looking up your record...", () => session.lookup(s.text));
|
|
38812
|
+
else if (key.backspace || key.delete) patch({ text: s.text.slice(0, -1) });
|
|
38813
|
+
else if (!key.return && !key.ctrl && !key.meta && !key.upArrow && !key.downArrow) patch({ text: s.text + input2 });
|
|
38814
|
+
return;
|
|
38815
|
+
}
|
|
38816
|
+
if (s.screen === "download-choice") {
|
|
38817
|
+
if (key.upArrow || key.downArrow) patch({ selected: s.selected === 0 ? 1 : 0 });
|
|
38818
|
+
else if (key.return) patch({ screen: "download", field: "New destination .csv or .jsonl file", text: "" });
|
|
38578
38819
|
return;
|
|
38579
38820
|
}
|
|
38580
38821
|
if (["edit", "download", "space", "upload"].includes(s.screen)) {
|
|
@@ -38588,8 +38829,10 @@ function InteractiveApp({ session }) {
|
|
|
38588
38829
|
}
|
|
38589
38830
|
if (s.screen === "download") {
|
|
38590
38831
|
void work("Downloading into a new file...", async () => {
|
|
38591
|
-
await session.download(back.current, s.text)
|
|
38592
|
-
|
|
38832
|
+
const notice = await session.download(back.current, s.text, { all: isSearchResults(back.current.tool?.name) && s.selected === 1, progress: (text2) => {
|
|
38833
|
+
if (mounted.current) patch({ notice: text2 });
|
|
38834
|
+
} });
|
|
38835
|
+
return { ...back.current, notice: `Saved ${s.text}${notice ? `. ${notice}` : ""}` };
|
|
38593
38836
|
});
|
|
38594
38837
|
return;
|
|
38595
38838
|
}
|
|
@@ -38698,15 +38941,15 @@ function InteractiveApp({ session }) {
|
|
|
38698
38941
|
patch({ input: { ...s.input, cursor: s.result.nextCursor } });
|
|
38699
38942
|
void work("Loading the next page...", async () => ({ screen: "results", selected: 0, input: { ...s.input, cursor: s.result.nextCursor }, result: await session.call(s.tool.name, { ...s.input, cursor: s.result.nextCursor }) }));
|
|
38700
38943
|
} else if (input2 === "r" && s.tool) runTool(!needsConfirmation(s.tool));
|
|
38701
|
-
else if (input2 === "e") patch({ screen: s.tool ? "form" : "install", selected: 0 });
|
|
38944
|
+
else if (input2 === "e") patch({ screen: s.tool?.name === "lookup.find" ? "lookup" : s.tool ? "form" : "install", text: s.tool?.name === "lookup.find" ? String(s.input.query ?? "") : s.text, selected: 0 });
|
|
38702
38945
|
else if (input2 === "d") {
|
|
38703
38946
|
back.current = s;
|
|
38704
|
-
patch({ screen: "download", field: "New destination .csv or .jsonl file", text: "" });
|
|
38947
|
+
patch({ screen: isSearchResults(s.tool?.name) ? "download-choice" : "download", selected: 0, field: "New destination .csv or .jsonl file", text: "" });
|
|
38705
38948
|
} else if (input2 === "a" && s.tool?.name === "searches.list") {
|
|
38706
38949
|
const row = rowsOf(s.result)[s.selected];
|
|
38707
38950
|
if (row) form("lists.create", { type: "audience", savedSearchId: row.id, recurringEnabled: true, maxRecords: 1e3, maxNewRecordsPerRefresh: 1e3, removeNonmatches: false });
|
|
38708
38951
|
} else if (input2 === "p" && s.tool?.name === "lists.get" && typeof s.input.id === "string") form("lists.refresh", { id: s.input.id, maxNewRecords: 1e3 });
|
|
38709
|
-
else if (key.return) {
|
|
38952
|
+
else if (key.return && (rowsOf(s.result).length || s.tool?.name !== "lookup.find")) {
|
|
38710
38953
|
void work("Opening the record...", () => session.openRow(s));
|
|
38711
38954
|
}
|
|
38712
38955
|
} else if (s.screen === "detail") {
|
|
@@ -38736,6 +38979,7 @@ var TerminalSession;
|
|
|
38736
38979
|
var init_app = __esm({
|
|
38737
38980
|
"src/terminal/app.tsx"() {
|
|
38738
38981
|
"use strict";
|
|
38982
|
+
init_search_download2();
|
|
38739
38983
|
init_tool_commands();
|
|
38740
38984
|
init_api2();
|
|
38741
38985
|
init_config();
|
|
@@ -38759,6 +39003,12 @@ var init_app = __esm({
|
|
|
38759
39003
|
async load() {
|
|
38760
39004
|
this.config = await this.runtime.readConfig();
|
|
38761
39005
|
}
|
|
39006
|
+
async lookup(query) {
|
|
39007
|
+
const result = await this.call("lookup.find", { query });
|
|
39008
|
+
const state = { ...initialState(), screen: "results", tool: toolByName("lookup.find"), input: { query }, result };
|
|
39009
|
+
if (result.exactMatch === true && rowsOf(result).length === 1) return { ...state, ...await this.openRow(state) };
|
|
39010
|
+
return state;
|
|
39011
|
+
}
|
|
38762
39012
|
settings() {
|
|
38763
39013
|
return resolveSettings(this.config, this.options, this.runtime.env);
|
|
38764
39014
|
}
|
|
@@ -38788,15 +39038,22 @@ var init_app = __esm({
|
|
|
38788
39038
|
this.config.profiles[this.config.activeProfile] = { apiUrl: settings.apiUrl, apiKey: key };
|
|
38789
39039
|
await this.runtime.writeConfig(this.config);
|
|
38790
39040
|
}
|
|
38791
|
-
async download(state, path2) {
|
|
39041
|
+
async download(state, path2, options = {}) {
|
|
38792
39042
|
if (!path2.trim()) throw new CliError("Enter a new destination file.");
|
|
38793
39043
|
path2 = path2.trim();
|
|
38794
39044
|
if (!/\.(csv|jsonl)$/.test(path2)) throw new CliError("Choose a new .csv or .jsonl destination.");
|
|
39045
|
+
if (isSearchResults(state.tool?.name)) {
|
|
39046
|
+
const input2 = { ...state.input };
|
|
39047
|
+
const loadedPage = !options.all || !input2.cursor ? state.result : void 0;
|
|
39048
|
+
if (options.all) delete input2.cursor;
|
|
39049
|
+
const result = await downloadSearch(this.api(), state.tool.name, input2, path2, { ...options, loadedPage, sleep: this.runtime.sleep });
|
|
39050
|
+
return result.capped ? SEARCH_DOWNLOAD_CAP_NOTICE : "";
|
|
39051
|
+
}
|
|
38795
39052
|
const job = state.result.job;
|
|
38796
39053
|
const jobId = state.result.jobId ?? job?.id ?? (state.tool?.name.startsWith("jobs.") ? state.result.id ?? state.input.jobId : void 0);
|
|
38797
39054
|
const exportTool = state.tool?.name === "lists.get" && state.input.id ? "lists.export" : state.tool?.name;
|
|
38798
39055
|
if (typeof jobId !== "string" && !["lists.export", "suppress.export"].includes(exportTool ?? "")) throw new CliError("Open a completed job or a list export to download a file.");
|
|
38799
|
-
const file2 = await
|
|
39056
|
+
const file2 = await open7(path2.trim(), "wx", 384);
|
|
38800
39057
|
try {
|
|
38801
39058
|
const format2 = path2.endsWith(".jsonl") ? "jsonl" : "csv";
|
|
38802
39059
|
if (typeof jobId === "string") await downloadBulkJob(this.api(), jobId, format2, async (text2) => {
|
|
@@ -38936,11 +39193,12 @@ async function buildFilters(api, options, kind) {
|
|
|
38936
39193
|
|
|
38937
39194
|
// src/commands.ts
|
|
38938
39195
|
init_output();
|
|
39196
|
+
init_search_download2();
|
|
38939
39197
|
|
|
38940
39198
|
// src/pagination.ts
|
|
38941
39199
|
init_zod();
|
|
38942
39200
|
init_api2();
|
|
38943
|
-
var
|
|
39201
|
+
var pageSchema3 = 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(), zeroResults: external_exports.boolean() });
|
|
38944
39202
|
var exportSchema = external_exports.object({ content: external_exports.string(), mimeType: external_exports.enum(["text/csv", "application/x-ndjson"]), nextCursor: external_exports.string().min(1).max(2048).nullable(), zeroResults: external_exports.boolean() });
|
|
38945
39203
|
async function pagedTool(api, name, input2, all, exporting = false) {
|
|
38946
39204
|
if (all && api.settings.idempotencyKey) throw new CliError("An idempotency key applies to one page. Omit --all.");
|
|
@@ -38953,7 +39211,7 @@ async function pagedTool(api, name, input2, all, exporting = false) {
|
|
|
38953
39211
|
let empty = true;
|
|
38954
39212
|
while (true) {
|
|
38955
39213
|
const raw = await api.tool(name, { ...input2, ...cursor === void 0 ? {} : { cursor } });
|
|
38956
|
-
const page = (exporting ? exportSchema :
|
|
39214
|
+
const page = (exporting ? exportSchema : pageSchema3).safeParse(raw);
|
|
38957
39215
|
if (!page.success) throw new CliError("The API returned an invalid cursor page.");
|
|
38958
39216
|
if ("items" in page.data) records.push(...page.data.items);
|
|
38959
39217
|
else {
|
|
@@ -39247,10 +39505,18 @@ function registerCommands(program2, runtime) {
|
|
|
39247
39505
|
emit(identity, mode);
|
|
39248
39506
|
});
|
|
39249
39507
|
const search = program2.command("search").description("Search HigherData Core");
|
|
39250
|
-
for (const kind of ["companies", "people"]) pageOptions(excludeList(addFilterOptions(search.command(kind).description(`Find ${kind}`), kind === "people"))).action(async (options, command) => {
|
|
39508
|
+
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) => {
|
|
39251
39509
|
const { api, mode } = await session(command);
|
|
39510
|
+
if (options.out) searchDownloadFormat(options.out);
|
|
39252
39511
|
const filters = await buildFilters(api, options, kind);
|
|
39253
|
-
|
|
39512
|
+
const input2 = { ...filters, limit: options.limit, ...options.cursor ? { cursor: options.cursor } : {}, ...excluded(options) };
|
|
39513
|
+
if (options.out) {
|
|
39514
|
+
const result = await downloadSearch(api, `search.${kind}`, input2, options.out, { all: Boolean(options.all), sleep: runtime.sleep, progress: (text2) => runtime.err(`${text2}
|
|
39515
|
+
`) });
|
|
39516
|
+
if (result.capped) runtime.err(`${SEARCH_DOWNLOAD_CAP_NOTICE}
|
|
39517
|
+
`);
|
|
39518
|
+
emit({ path: options.out, ...result }, mode);
|
|
39519
|
+
} else emit(await pagedTool(api, `search.${kind}`, input2, Boolean(options.all)), mode);
|
|
39254
39520
|
});
|
|
39255
39521
|
addFilterOptions(program2.command("count").description("Estimate companies and their contacts")).action(async (options, command) => {
|
|
39256
39522
|
const { api, mode } = await session(command);
|