@curviate/cli 0.14.0 → 0.15.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/CHANGELOG.md +161 -0
- package/README.md +52 -79
- package/dist/{account-7AUDAMIK.js → account-FM473HEK.js} +38 -371
- package/dist/{chunk-SSPILTKF.js → chunk-45KHSWCV.js} +56 -48
- package/dist/chunk-BGZW6B7G.js +59 -0
- package/dist/chunk-EEMJDJ4W.js +80 -0
- package/dist/{chunk-JXF47TRY.js → chunk-M33MI53G.js} +33 -1
- package/dist/chunk-QJZ3LWOX.js +24 -0
- package/dist/{chunk-4JHGVY7R.js → chunk-TMU3CSPR.js} +4 -0
- package/dist/{chunk-HMAGEMF7.js → chunk-ZE7QGR3F.js} +4 -0
- package/dist/cli.js +127 -15
- package/dist/comment-NCDXERSI.js +555 -0
- package/dist/{company-RU2CA5DY.js → company-IYTMW64G.js} +27 -74
- package/dist/{config-Q2AEWSEC.js → config-P5CPF5WC.js} +1 -1
- package/dist/{connect-QT6ZOS75.js → connect-TTJHMBUP.js} +91 -45
- package/dist/{exit-codes-ZTY2NY3X.js → exit-codes-SL3GQF7W.js} +1 -1
- package/dist/{inbox-DN7X7CM2.js → inbox-EOOGJ3ZN.js} +55 -124
- package/dist/job-YARGTHUY.js +631 -0
- package/dist/{login-BBVQLXM7.js → login-MFB45PVZ.js} +1 -1
- package/dist/{message-6K5E3CUF.js → message-2DLIQIE6.js} +61 -51
- package/dist/{post-2JQZ3OSF.js → post-GRIJ7X52.js} +188 -221
- package/dist/profile-57RJEL7H.js +809 -0
- package/dist/{recruiter-XHVMQWK6.js → recruiter-CTYH7AYG.js} +740 -226
- package/dist/{sales-nav-QTSTJMKA.js → sales-nav-NUMEYOEO.js} +119 -77
- package/dist/{search-ZGTS45BT.js → search-RD4TXNV7.js} +103 -39
- package/dist/{webhook-EIY5WWTE.js → webhook-CEP7SGP5.js} +8 -45
- package/package.json +4 -3
- package/dist/chunk-GXTZION6.js +0 -45
- package/dist/chunk-Q43HZUN3.js +0 -29
- package/dist/job-FGGQEXSU.js +0 -105
- package/dist/profile-DD5GMGNL.js +0 -501
|
@@ -21,59 +21,65 @@ function synthesizeHeadquarters(locations) {
|
|
|
21
21
|
if (!hq) return null;
|
|
22
22
|
return {
|
|
23
23
|
city: hq["city"] ?? null,
|
|
24
|
-
|
|
24
|
+
country_code: hq["country_code"] ?? null,
|
|
25
|
+
postal_code: hq["postal_code"] ?? null,
|
|
25
26
|
area: hq["area"] ?? null
|
|
26
27
|
};
|
|
27
28
|
}
|
|
29
|
+
function getSpecifics(d) {
|
|
30
|
+
return d["specifics"] !== null && d["specifics"] !== void 0 && typeof d["specifics"] === "object" ? d["specifics"] : null;
|
|
31
|
+
}
|
|
32
|
+
function extractExperience(specifics) {
|
|
33
|
+
return Array.isArray(specifics?.["experience"]) ? specifics["experience"] : [];
|
|
34
|
+
}
|
|
28
35
|
function slimProfileMe(data) {
|
|
29
36
|
const d = data !== null && data !== void 0 && typeof data === "object" ? data : {};
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
id: org["id"] ?? null,
|
|
33
|
-
mailbox_id: org["mailbox_id"] ?? null,
|
|
34
|
-
name: org["name"] ?? null
|
|
35
|
-
}));
|
|
36
|
-
const rawWE = Array.isArray(d["work_experience"]) ? d["work_experience"] : [];
|
|
37
|
-
const currentPosition = synthesizeCurrentPosition(rawWE);
|
|
37
|
+
const specifics = getSpecifics(d);
|
|
38
|
+
const currentPosition = synthesizeCurrentPosition(extractExperience(specifics));
|
|
38
39
|
return {
|
|
39
|
-
provider_id: d["
|
|
40
|
+
provider_id: d["id"] ?? null,
|
|
40
41
|
first_name: d["first_name"] ?? null,
|
|
41
42
|
last_name: d["last_name"] ?? null,
|
|
43
|
+
// LinkedIn serves the profile headline in the `description` wire field
|
|
44
|
+
// on reads, not a field named `headline` — see JSDoc above.
|
|
45
|
+
headline: d["description"] ?? null,
|
|
42
46
|
public_identifier: d["public_identifier"] ?? null,
|
|
43
47
|
location: d["location"] ?? null,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
is_premium: d["is_premium"] ?? null,
|
|
47
|
-
organizations,
|
|
48
|
+
emails: Array.isArray(d["emails"]) ? d["emails"] : [],
|
|
49
|
+
is_premium: specifics?.["is_premium"] ?? null,
|
|
48
50
|
current_position: currentPosition
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
function slimProfile(data) {
|
|
52
54
|
const d = data !== null && data !== void 0 && typeof data === "object" ? data : {};
|
|
53
|
-
const
|
|
54
|
-
const currentPosition = synthesizeCurrentPosition(
|
|
55
|
+
const specifics = getSpecifics(d);
|
|
56
|
+
const currentPosition = synthesizeCurrentPosition(extractExperience(specifics));
|
|
55
57
|
return {
|
|
56
|
-
provider_id: d["
|
|
58
|
+
provider_id: d["id"] ?? null,
|
|
57
59
|
first_name: d["first_name"] ?? null,
|
|
58
60
|
last_name: d["last_name"] ?? null,
|
|
59
|
-
|
|
61
|
+
// LinkedIn serves the profile headline in the `description` wire field
|
|
62
|
+
// on reads, not a field named `headline` — see slimProfileMe's JSDoc.
|
|
63
|
+
headline: d["description"] ?? null,
|
|
60
64
|
location: d["location"] ?? null,
|
|
61
|
-
|
|
62
|
-
network_distance: d["network_distance"] ?? null,
|
|
65
|
+
network_distance: specifics?.["network_distance"] ?? null,
|
|
63
66
|
public_identifier: d["public_identifier"] ?? null,
|
|
64
67
|
current_position: currentPosition
|
|
65
68
|
};
|
|
66
69
|
}
|
|
67
70
|
function slimInviteSentItem(item) {
|
|
71
|
+
const rawUser = item["user"] !== null && item["user"] !== void 0 && typeof item["user"] === "object" ? item["user"] : null;
|
|
72
|
+
const user = rawUser !== null ? {
|
|
73
|
+
id: rawUser["id"] ?? null,
|
|
74
|
+
display_name: rawUser["display_name"] ?? null,
|
|
75
|
+
first_name: rawUser["first_name"] ?? null,
|
|
76
|
+
last_name: rawUser["last_name"] ?? null
|
|
77
|
+
} : null;
|
|
68
78
|
return {
|
|
69
79
|
id: item["id"] ?? null,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
invited_user_description: item["invited_user_description"] ?? null,
|
|
74
|
-
date: item["date"] ?? null,
|
|
75
|
-
parsed_datetime: item["parsed_datetime"] ?? null,
|
|
76
|
-
invitation_text: item["invitation_text"] ?? null
|
|
80
|
+
created_at: item["created_at"] ?? null,
|
|
81
|
+
message: item["message"] ?? null,
|
|
82
|
+
user
|
|
77
83
|
};
|
|
78
84
|
}
|
|
79
85
|
function slimInviteSent(data) {
|
|
@@ -86,15 +92,18 @@ function slimInviteSent(data) {
|
|
|
86
92
|
};
|
|
87
93
|
}
|
|
88
94
|
function slimInviteReceivedItem(item) {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
95
|
+
const rawUser = item["user"] !== null && item["user"] !== void 0 && typeof item["user"] === "object" ? item["user"] : null;
|
|
96
|
+
const user = rawUser !== null ? {
|
|
97
|
+
id: rawUser["id"] ?? null,
|
|
98
|
+
display_name: rawUser["display_name"] ?? null,
|
|
99
|
+
first_name: rawUser["first_name"] ?? null,
|
|
100
|
+
last_name: rawUser["last_name"] ?? null,
|
|
101
|
+
public_identifier: rawUser["public_identifier"] ?? null
|
|
102
|
+
} : null;
|
|
91
103
|
return {
|
|
92
104
|
id: item["id"] ?? null,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parsed_datetime: item["parsed_datetime"] ?? null,
|
|
96
|
-
invitation_text: item["invitation_text"] ?? null,
|
|
97
|
-
specifics
|
|
105
|
+
created_at: item["created_at"] ?? null,
|
|
106
|
+
user
|
|
98
107
|
};
|
|
99
108
|
}
|
|
100
109
|
function slimInviteReceived(data) {
|
|
@@ -166,8 +175,7 @@ function slimSearchPostsItem(item) {
|
|
|
166
175
|
text = s.length > 200 ? s.slice(0, 200) : s;
|
|
167
176
|
}
|
|
168
177
|
return {
|
|
169
|
-
|
|
170
|
-
posted_at: item["posted_at"] ?? null,
|
|
178
|
+
id: item["id"] ?? null,
|
|
171
179
|
author,
|
|
172
180
|
text,
|
|
173
181
|
reaction_count: item["reaction_count"] ?? null,
|
|
@@ -211,25 +219,26 @@ function slimAccountGet(data) {
|
|
|
211
219
|
}
|
|
212
220
|
function slimJob(data) {
|
|
213
221
|
const d = data !== null && data !== void 0 && typeof data === "object" ? data : {};
|
|
222
|
+
const rawCompany = d["company"] !== null && d["company"] !== void 0 && typeof d["company"] === "object" ? d["company"] : null;
|
|
223
|
+
const companyId = rawCompany?.["id"] ?? null;
|
|
214
224
|
return {
|
|
215
225
|
object: d["object"] ?? null,
|
|
216
226
|
id: d["id"] ?? null,
|
|
217
227
|
title: d["title"] ?? null,
|
|
218
228
|
company: d["company"] ?? null,
|
|
219
|
-
company_id:
|
|
229
|
+
company_id: companyId,
|
|
220
230
|
location: d["location"] ?? null,
|
|
221
231
|
state: d["state"] ?? null,
|
|
222
|
-
|
|
223
|
-
published_at: d["published_at"] ?? null,
|
|
232
|
+
applications_count: d["applications_count"] ?? null,
|
|
233
|
+
published_at: d["published_at"] ?? d["created_at"] ?? null,
|
|
224
234
|
description: d["description"] ?? null
|
|
225
235
|
};
|
|
226
236
|
}
|
|
227
237
|
function slimCompany(data) {
|
|
228
238
|
const d = data !== null && data !== void 0 && typeof data === "object" ? data : {};
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
};
|
|
239
|
+
const rawInsights = d["insights"] !== null && d["insights"] !== void 0 && typeof d["insights"] === "object" ? d["insights"] : null;
|
|
240
|
+
const rawHeadcountRange = rawInsights?.["headcount_range"] !== null && rawInsights?.["headcount_range"] !== void 0 && typeof rawInsights?.["headcount_range"] === "object" ? rawInsights["headcount_range"] : null;
|
|
241
|
+
const employeeCountRange = rawHeadcountRange !== null ? { from: rawHeadcountRange["from"] ?? null } : null;
|
|
233
242
|
const rawLocations = Array.isArray(d["locations"]) ? d["locations"] : [];
|
|
234
243
|
const headquarters = synthesizeHeadquarters(rawLocations);
|
|
235
244
|
return {
|
|
@@ -238,13 +247,12 @@ function slimCompany(data) {
|
|
|
238
247
|
public_identifier: d["public_identifier"] ?? null,
|
|
239
248
|
profile_url: d["profile_url"] ?? null,
|
|
240
249
|
industry: d["industry"] ?? null,
|
|
241
|
-
employee_count:
|
|
242
|
-
employee_count_range:
|
|
250
|
+
employee_count: rawInsights?.["headcount"] ?? null,
|
|
251
|
+
employee_count_range: employeeCountRange,
|
|
243
252
|
website: d["website"] ?? null,
|
|
244
|
-
|
|
245
|
-
messaging,
|
|
253
|
+
establishment_year: d["establishment_year"] ?? null,
|
|
246
254
|
headquarters,
|
|
247
|
-
|
|
255
|
+
follower_count: d["follower_count"] ?? null
|
|
248
256
|
};
|
|
249
257
|
}
|
|
250
258
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/attach.ts
|
|
4
|
+
import { readFile } from "fs/promises";
|
|
5
|
+
import { basename, extname } from "path";
|
|
6
|
+
var AttachError = class extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.exitCode = 2;
|
|
10
|
+
this.name = "AttachError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
async function readAttachment(filePath) {
|
|
14
|
+
try {
|
|
15
|
+
const buf = await readFile(filePath);
|
|
16
|
+
return buf;
|
|
17
|
+
} catch (err) {
|
|
18
|
+
const filename = basename(filePath);
|
|
19
|
+
const reason = err instanceof Error ? err.message : "unknown error";
|
|
20
|
+
throw new AttachError(
|
|
21
|
+
`Cannot read attachment "${filename}": ${reason}. Pass a valid file path.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function describeAttachment(filePath, buf) {
|
|
26
|
+
return `${basename(filePath)} (${buf.byteLength} bytes)`;
|
|
27
|
+
}
|
|
28
|
+
var MIME_BY_EXT = {
|
|
29
|
+
".jpg": "image/jpeg",
|
|
30
|
+
".jpeg": "image/jpeg",
|
|
31
|
+
".png": "image/png",
|
|
32
|
+
".gif": "image/gif",
|
|
33
|
+
".webp": "image/webp",
|
|
34
|
+
".pdf": "application/pdf",
|
|
35
|
+
".mp4": "video/mp4",
|
|
36
|
+
".mov": "video/quicktime",
|
|
37
|
+
".webm": "video/webm",
|
|
38
|
+
".mp3": "audio/mpeg",
|
|
39
|
+
".m4a": "audio/mp4",
|
|
40
|
+
".wav": "audio/wav",
|
|
41
|
+
".ogg": "audio/ogg"
|
|
42
|
+
};
|
|
43
|
+
function guessContentType(filePath) {
|
|
44
|
+
return MIME_BY_EXT[extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
45
|
+
}
|
|
46
|
+
function toAttachmentPayload(filePath, buf) {
|
|
47
|
+
return {
|
|
48
|
+
content: buf.toString("base64"),
|
|
49
|
+
content_type: guessContentType(filePath),
|
|
50
|
+
filename: basename(filePath)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
AttachError,
|
|
56
|
+
readAttachment,
|
|
57
|
+
describeAttachment,
|
|
58
|
+
toAttachmentPayload
|
|
59
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/paginate.ts
|
|
4
|
+
var PaginateError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.exitCode = 2;
|
|
8
|
+
this.name = "PaginateError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function truncationProseNote(pagesFetched) {
|
|
12
|
+
return `Streaming truncated at ${pagesFetched} page(s). Use --all --max-pages or --cursor for manual paging.
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
function ndjsonModeNotice() {
|
|
16
|
+
return "--all streams NDJSON: one object per line; the {items,cursor} envelope is not used\n";
|
|
17
|
+
}
|
|
18
|
+
var DEFAULT_PAGE_DELAY_MS = 400;
|
|
19
|
+
function pageDelayFrom(raw) {
|
|
20
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
21
|
+
const n = Number(raw);
|
|
22
|
+
if (!Number.isInteger(n) || n < 0) return void 0;
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
function pageDelayFromFlags(flags) {
|
|
26
|
+
return pageDelayFrom(flags["page-delay"]);
|
|
27
|
+
}
|
|
28
|
+
var realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
|
+
function truncationSentinelLine(pagesFetched, hasMore) {
|
|
30
|
+
return JSON.stringify({ object: "stream_truncated", pages_fetched: pagesFetched, has_more: hasMore }) + "\n";
|
|
31
|
+
}
|
|
32
|
+
async function* streamAll(fn, params, opts = {}) {
|
|
33
|
+
const maxPages = opts.maxPages ?? 100;
|
|
34
|
+
const pageDelayMs = opts.pageDelayMs ?? DEFAULT_PAGE_DELAY_MS;
|
|
35
|
+
const sleep = opts.sleep ?? realSleep;
|
|
36
|
+
let cursor = void 0;
|
|
37
|
+
let pageCount = 0;
|
|
38
|
+
let firstPage = true;
|
|
39
|
+
while (true) {
|
|
40
|
+
const pageParams = cursor !== void 0 && cursor !== null ? { ...params, cursor } : params;
|
|
41
|
+
const page = await fn(pageParams);
|
|
42
|
+
pageCount++;
|
|
43
|
+
const items = page.items ?? page.data;
|
|
44
|
+
if (firstPage && !Array.isArray(items)) {
|
|
45
|
+
throw new PaginateError(
|
|
46
|
+
"--all requires a paginated method (response must have `items` or `data` array). Remove --all for non-list commands."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (firstPage) {
|
|
50
|
+
if (opts.out) opts.out.stderr.write(ndjsonModeNotice());
|
|
51
|
+
firstPage = false;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(items)) {
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
yield item;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
cursor = page.cursor;
|
|
59
|
+
if (!cursor) break;
|
|
60
|
+
if (pageCount >= maxPages) {
|
|
61
|
+
const hasMore = cursor !== null && cursor !== void 0;
|
|
62
|
+
if (opts.out) {
|
|
63
|
+
opts.out.stdout.write(truncationSentinelLine(pageCount, hasMore));
|
|
64
|
+
opts.out.stderr.write(truncationProseNote(pageCount));
|
|
65
|
+
}
|
|
66
|
+
if (opts.onTruncated) {
|
|
67
|
+
opts.onTruncated(pageCount, hasMore);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
if (pageDelayMs > 0) await sleep(pageDelayMs);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
ndjsonModeNotice,
|
|
77
|
+
DEFAULT_PAGE_DELAY_MS,
|
|
78
|
+
pageDelayFromFlags,
|
|
79
|
+
streamAll
|
|
80
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
readConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TMU3CSPR.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/resolve.ts
|
|
7
7
|
var DEFAULT_BASE_URL = "https://api.curviate.com";
|
|
@@ -73,10 +73,42 @@ function applyProjection(data, fields) {
|
|
|
73
73
|
}
|
|
74
74
|
return data;
|
|
75
75
|
}
|
|
76
|
+
function firstProjectableItem(data) {
|
|
77
|
+
const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
78
|
+
if (Array.isArray(data)) {
|
|
79
|
+
return data.length > 0 && isPlainObject(data[0]) ? data[0] : null;
|
|
80
|
+
}
|
|
81
|
+
if (isPlainObject(data)) {
|
|
82
|
+
const items = data["items"];
|
|
83
|
+
if (Array.isArray(items)) {
|
|
84
|
+
return items.length > 0 && isPlainObject(items[0]) ? items[0] : null;
|
|
85
|
+
}
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function detectUnknownFields(data, fields) {
|
|
91
|
+
if (fields.length === 0) return null;
|
|
92
|
+
const first = firstProjectableItem(data);
|
|
93
|
+
if (first === null) return null;
|
|
94
|
+
const available = Object.keys(first);
|
|
95
|
+
const unknown = fields.filter((f) => {
|
|
96
|
+
const topKey = f.split(".")[0];
|
|
97
|
+
return !Object.prototype.hasOwnProperty.call(first, topKey);
|
|
98
|
+
});
|
|
99
|
+
return unknown.length > 0 ? { unknown, available } : null;
|
|
100
|
+
}
|
|
76
101
|
function renderSuccess(data, opts, out) {
|
|
77
102
|
const json = isJsonMode(opts);
|
|
78
103
|
const fields = opts.fields ? opts.fields.split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
79
104
|
const slimmed = !opts.verbose && opts.slim ? opts.slim(data) : data;
|
|
105
|
+
const unknownFields = detectUnknownFields(slimmed, fields);
|
|
106
|
+
if (unknownFields) {
|
|
107
|
+
out.stderr.write(
|
|
108
|
+
`warning: --fields not present on the response: ${unknownFields.unknown.join(", ")}. Available keys: ${unknownFields.available.join(", ")}.
|
|
109
|
+
`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
80
112
|
const projected = applyProjection(slimmed, fields);
|
|
81
113
|
if (json) {
|
|
82
114
|
out.stdout.write(JSON.stringify(projected) + "\n");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveIdentifier
|
|
4
|
+
} from "./chunk-DMQZEPQE.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/member-id.ts
|
|
7
|
+
var MEMBER_PROVIDER_ID_RE = /^A[CDE][A-Za-z0-9_-]{4,}$/;
|
|
8
|
+
async function resolveMemberProviderId(ns, raw) {
|
|
9
|
+
const normalized = resolveIdentifier(raw);
|
|
10
|
+
if (MEMBER_PROVIDER_ID_RE.test(normalized)) return normalized;
|
|
11
|
+
const profile = await ns.users.get(normalized, {});
|
|
12
|
+
return profile.id;
|
|
13
|
+
}
|
|
14
|
+
async function resolveMemberOrMeProviderId(ns, raw) {
|
|
15
|
+
const normalized = resolveIdentifier(raw);
|
|
16
|
+
if (normalized === "me" || MEMBER_PROVIDER_ID_RE.test(normalized)) return normalized;
|
|
17
|
+
const profile = await ns.users.get(normalized, {});
|
|
18
|
+
return profile.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
resolveMemberProviderId,
|
|
23
|
+
resolveMemberOrMeProviderId
|
|
24
|
+
};
|
|
@@ -176,6 +176,10 @@ var GLOBAL_FLAGS = {
|
|
|
176
176
|
type: "string",
|
|
177
177
|
description: "Maximum number of pages to fetch when --all is used."
|
|
178
178
|
},
|
|
179
|
+
"page-delay": {
|
|
180
|
+
type: "string",
|
|
181
|
+
description: "Milliseconds to pause between pages when --all is used (default 400; pass 0 to disable). A modest delay keeps a long stream under the platform rate gate."
|
|
182
|
+
},
|
|
179
183
|
preview: {
|
|
180
184
|
type: "boolean",
|
|
181
185
|
description: "Render the request that would be sent without calling the API.",
|
|
@@ -21,6 +21,7 @@ var EXIT_CODE_MAP = {
|
|
|
21
21
|
RATE_LIMIT_TENANT: 6,
|
|
22
22
|
PLATFORM_RATE_LIMIT: 6,
|
|
23
23
|
LINKEDIN_RATE_LIMITED: 6,
|
|
24
|
+
RATE_LIMITED: 6,
|
|
24
25
|
// Transient platform (7)
|
|
25
26
|
PLATFORM_ERROR: 7,
|
|
26
27
|
LINKEDIN_SERVICE_UNAVAILABLE: 7,
|
|
@@ -30,6 +31,9 @@ var EXIT_CODE_MAP = {
|
|
|
30
31
|
LINKEDIN_AUTH_FAILED: 8,
|
|
31
32
|
LINKEDIN_COOKIE_INVALID: 8,
|
|
32
33
|
CONNECTION_IN_PROGRESS: 8,
|
|
34
|
+
ACCOUNT_ALREADY_LINKED: 8,
|
|
35
|
+
LINKEDIN_OPERATION_NOT_SUPPORTED: 8,
|
|
36
|
+
CONNECTION_REQUEST_CONFLICT: 8,
|
|
33
37
|
// Checkpoint flow (9)
|
|
34
38
|
CHECKPOINT_NOT_FOUND: 9,
|
|
35
39
|
CHECKPOINT_EXPIRED: 9,
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,47 @@ import { createRequire } from "module";
|
|
|
9
9
|
|
|
10
10
|
// src/dispatch.ts
|
|
11
11
|
import { runCommand } from "citty";
|
|
12
|
+
var REMOVED_COMMANDS = {
|
|
13
|
+
post: {
|
|
14
|
+
list: "`post list` was removed \u2014 use `post user-posts <user_id>` (accepts `me`).",
|
|
15
|
+
comment: "post comments are their own group now \u2014 use `comment add <post_id> <text>`.",
|
|
16
|
+
comments: "post comments are their own group now \u2014 use `comment list <post_id>`."
|
|
17
|
+
},
|
|
18
|
+
connect: {
|
|
19
|
+
respond: "`connect respond` was split \u2014 use `connect accept <id>` or `connect decline <id>`."
|
|
20
|
+
},
|
|
21
|
+
profile: {
|
|
22
|
+
connections: "`profile connections` was renamed \u2014 use `profile relations`."
|
|
23
|
+
},
|
|
24
|
+
account: {
|
|
25
|
+
"connect-link": "`account connect-link` was removed \u2014 use `account link [--account-id <id>]`.",
|
|
26
|
+
"reconnect-link": "`account reconnect-link` was removed \u2014 use `account link [--account-id <id>]`.",
|
|
27
|
+
reconnect: "`account reconnect` was removed \u2014 use `account link [--account-id <id>]`."
|
|
28
|
+
},
|
|
29
|
+
inbox: {
|
|
30
|
+
sync: "`inbox sync` was removed \u2014 history syncs automatically; just read `inbox messages <chat_id>`.",
|
|
31
|
+
"sync-chat": "`inbox sync-chat` was removed \u2014 history syncs automatically; just read `inbox messages <chat_id>`."
|
|
32
|
+
},
|
|
33
|
+
recruiter: {
|
|
34
|
+
"add-candidate": "`recruiter add-candidate` was renamed \u2014 use `recruiter save-candidate <project_id> --stage-id <id> --candidate-id <id>`.",
|
|
35
|
+
"project-jobs": "`recruiter project-jobs` was renamed \u2014 use `recruiter project-job get <project_id>`.",
|
|
36
|
+
sync: "`recruiter sync` was removed \u2014 Recruiter data syncs automatically now.",
|
|
37
|
+
"add-applicant": "`recruiter add-applicant` was removed with no replacement.",
|
|
38
|
+
"reject-applicant": "`recruiter reject-applicant` was removed with no replacement."
|
|
39
|
+
},
|
|
40
|
+
"sales-nav": {
|
|
41
|
+
sync: "`sales-nav sync` was removed \u2014 Sales Navigator data syncs automatically now."
|
|
42
|
+
},
|
|
43
|
+
webhook: {
|
|
44
|
+
"state-diff": "`webhook state-diff` was removed with no replacement."
|
|
45
|
+
},
|
|
46
|
+
company: {
|
|
47
|
+
followers: "`company followers` was removed with no replacement."
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function successorHint(group, token) {
|
|
51
|
+
return REMOVED_COMMANDS[group]?.[token] ?? null;
|
|
52
|
+
}
|
|
12
53
|
async function resolveValue(input) {
|
|
13
54
|
return typeof input === "function" ? input() : input;
|
|
14
55
|
}
|
|
@@ -19,6 +60,52 @@ async function nodeHasPositional(cmd) {
|
|
|
19
60
|
const argsDef = await resolveValue(cmd.args ?? {});
|
|
20
61
|
return Object.values(argsDef).some((def) => def?.type === "positional");
|
|
21
62
|
}
|
|
63
|
+
async function nodePositionalCount(cmd) {
|
|
64
|
+
const argsDef = await resolveValue(cmd.args ?? {});
|
|
65
|
+
return Object.values(argsDef).filter((def) => def?.type === "positional").length;
|
|
66
|
+
}
|
|
67
|
+
async function booleanFlagNames(cmd) {
|
|
68
|
+
const names = /* @__PURE__ */ new Set();
|
|
69
|
+
const argsDef = await resolveValue(cmd.args ?? {});
|
|
70
|
+
for (const [name, def] of Object.entries(argsDef)) {
|
|
71
|
+
if (def?.type !== "boolean") continue;
|
|
72
|
+
names.add(name);
|
|
73
|
+
const alias = def.alias;
|
|
74
|
+
if (typeof alias === "string") names.add(alias);
|
|
75
|
+
else if (Array.isArray(alias)) for (const a of alias) names.add(a);
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
async function nodeName(cmd) {
|
|
80
|
+
const meta = await resolveValue(cmd.meta ?? {});
|
|
81
|
+
return meta.name ?? "this command";
|
|
82
|
+
}
|
|
83
|
+
function positionalTokens(rawArgs, booleanFlags) {
|
|
84
|
+
const out = [];
|
|
85
|
+
let afterDoubleDash = false;
|
|
86
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
87
|
+
const arg = rawArgs[i];
|
|
88
|
+
if (afterDoubleDash) {
|
|
89
|
+
out.push({ token: arg, index: i });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === "--") {
|
|
93
|
+
afterDoubleDash = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (arg.startsWith("-") && arg !== "-") {
|
|
97
|
+
const body = arg.replace(/^-+/, "");
|
|
98
|
+
if (body.includes("=")) continue;
|
|
99
|
+
const isBoolean = booleanFlags.has(body) || body.startsWith("no-") && booleanFlags.has(body.slice(3));
|
|
100
|
+
if (isBoolean) continue;
|
|
101
|
+
const next = rawArgs[i + 1];
|
|
102
|
+
if (next !== void 0 && !(next.startsWith("-") && next !== "-")) i++;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
out.push({ token: arg, index: i });
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
22
109
|
async function declaredArgNames(cmd) {
|
|
23
110
|
const names = /* @__PURE__ */ new Set();
|
|
24
111
|
const argsDef = await resolveValue(cmd.args ?? {});
|
|
@@ -62,8 +149,10 @@ function hasEmptyFields(rawArgs) {
|
|
|
62
149
|
}
|
|
63
150
|
return false;
|
|
64
151
|
}
|
|
65
|
-
function usageError(message) {
|
|
152
|
+
function usageError(message, hint) {
|
|
66
153
|
process.stderr.write(`error: ${message}
|
|
154
|
+
`);
|
|
155
|
+
if (hint) process.stderr.write(`hint: ${hint}
|
|
67
156
|
`);
|
|
68
157
|
process.stderr.write("Run `curviate --help` for usage.\n");
|
|
69
158
|
process.exit(2);
|
|
@@ -78,7 +167,29 @@ async function resolveLeaf(cmd, rawArgs) {
|
|
|
78
167
|
const sub = await resolveValue(subCommands[token]);
|
|
79
168
|
return resolveLeaf(sub, rawArgs.slice(idx + 1));
|
|
80
169
|
}
|
|
170
|
+
if (token !== void 0) {
|
|
171
|
+
const hint = successorHint(await nodeName(cmd), token);
|
|
172
|
+
if (hint) {
|
|
173
|
+
usageError(`unknown command \`${token}\``, hint);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
81
176
|
if (token !== void 0 && hasBarePositional) {
|
|
177
|
+
const booleanFlags = await booleanFlagNames(cmd);
|
|
178
|
+
const positionals = positionalTokens(rawArgs, booleanFlags);
|
|
179
|
+
const declaredCount = await nodePositionalCount(cmd);
|
|
180
|
+
const extras = positionals.slice(declaredCount);
|
|
181
|
+
if (extras.length > 0) {
|
|
182
|
+
const first = extras[0];
|
|
183
|
+
if (extras.length === 1 && Object.prototype.hasOwnProperty.call(subCommands, first.token)) {
|
|
184
|
+
const sub = await resolveValue(subCommands[first.token]);
|
|
185
|
+
const remaining = rawArgs.filter((_, i) => i !== first.index);
|
|
186
|
+
return resolveLeaf(sub, remaining);
|
|
187
|
+
}
|
|
188
|
+
const name = await nodeName(cmd);
|
|
189
|
+
usageError(
|
|
190
|
+
`unexpected argument \`${first.token}\` after \`${name}\`. It is neither a positional \`${name}\` accepts nor one of its subcommands. Run \`curviate ${name} --help\` for the available subcommands.`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
82
193
|
return { leaf: cmd, leafArgs: rawArgs };
|
|
83
194
|
}
|
|
84
195
|
if (token !== void 0) {
|
|
@@ -139,23 +250,24 @@ var main = defineCommand({
|
|
|
139
250
|
// Subcommand registry — names and descriptions are static for help rendering;
|
|
140
251
|
// the handler implementation is loaded lazily on first invocation.
|
|
141
252
|
subCommands: {
|
|
142
|
-
login: () => import("./login-
|
|
143
|
-
config: () => import("./config-
|
|
253
|
+
login: () => import("./login-MFB45PVZ.js").then((m) => m.loginCommand),
|
|
254
|
+
config: () => import("./config-P5CPF5WC.js").then((m) => m.configCommand),
|
|
144
255
|
// ---------------------------------------------------------------------------
|
|
145
256
|
// Noun groups — lazy-loaded on first invocation.
|
|
146
257
|
// ---------------------------------------------------------------------------
|
|
147
|
-
profile: () => import("./profile-
|
|
148
|
-
company: () => import("./company-
|
|
149
|
-
job: () => import("./job-
|
|
150
|
-
connect: () => import("./connect-
|
|
151
|
-
search: () => import("./search-
|
|
152
|
-
inbox: () => import("./inbox-
|
|
153
|
-
message: () => import("./message-
|
|
154
|
-
post: () => import("./post-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
258
|
+
profile: () => import("./profile-57RJEL7H.js").then((m) => m.profileCommand),
|
|
259
|
+
company: () => import("./company-IYTMW64G.js").then((m) => m.companyCommand),
|
|
260
|
+
job: () => import("./job-YARGTHUY.js").then((m) => m.jobCommand),
|
|
261
|
+
connect: () => import("./connect-TTJHMBUP.js").then((m) => m.connectCommand),
|
|
262
|
+
search: () => import("./search-RD4TXNV7.js").then((m) => m.searchCommand),
|
|
263
|
+
inbox: () => import("./inbox-EOOGJ3ZN.js").then((m) => m.inboxCommand),
|
|
264
|
+
message: () => import("./message-2DLIQIE6.js").then((m) => m.messageCommand),
|
|
265
|
+
post: () => import("./post-GRIJ7X52.js").then((m) => m.postCommand),
|
|
266
|
+
comment: () => import("./comment-NCDXERSI.js").then((m) => m.commentCommand),
|
|
267
|
+
account: () => import("./account-FM473HEK.js").then((m) => m.accountCommand),
|
|
268
|
+
webhook: () => import("./webhook-CEP7SGP5.js").then((m) => m.webhookCommand),
|
|
269
|
+
"sales-nav": () => import("./sales-nav-NUMEYOEO.js").then((m) => m.salesNavCommand),
|
|
270
|
+
recruiter: () => import("./recruiter-CTYH7AYG.js").then((m) => m.recruiterCommand)
|
|
159
271
|
},
|
|
160
272
|
async run() {
|
|
161
273
|
const { runMain } = await import("citty");
|