@curviate/cli 0.13.0 → 0.15.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/CHANGELOG.md +116 -0
- package/README.md +52 -79
- package/dist/{account-7AUDAMIK.js → account-VB52GIUA.js} +34 -369
- package/dist/{chunk-SSPILTKF.js → chunk-45KHSWCV.js} +56 -48
- package/dist/chunk-BGZW6B7G.js +59 -0
- package/dist/{chunk-GXTZION6.js → chunk-DNTRQZBT.js} +13 -1
- package/dist/chunk-QJZ3LWOX.js +24 -0
- package/dist/{chunk-HMAGEMF7.js → chunk-ZE7QGR3F.js} +4 -0
- package/dist/cli.js +75 -12
- package/dist/comment-Y5IU42CR.js +550 -0
- package/dist/{company-RU2CA5DY.js → company-LGID3SK3.js} +21 -72
- package/dist/{connect-QT6ZOS75.js → connect-A7HEKKEE.js} +86 -43
- package/dist/{exit-codes-ZTY2NY3X.js → exit-codes-SL3GQF7W.js} +1 -1
- package/dist/{inbox-DN7X7CM2.js → inbox-44JRTJAP.js} +50 -122
- package/dist/job-TFTTCP5B.js +561 -0
- package/dist/{message-6K5E3CUF.js → message-FOJTDPW3.js} +47 -47
- package/dist/{post-2JQZ3OSF.js → post-FC6NZXM5.js} +177 -219
- package/dist/profile-I6YMVXSS.js +799 -0
- package/dist/{recruiter-XHVMQWK6.js → recruiter-BHT5OJD7.js} +731 -224
- package/dist/{sales-nav-QTSTJMKA.js → sales-nav-6IYKQ4TC.js} +109 -75
- package/dist/{search-ZGTS45BT.js → search-6C72M563.js} +95 -37
- package/dist/{webhook-XPWBI675.js → webhook-NSXEIU44.js} +36 -39
- package/package.json +4 -3
- package/dist/chunk-Q43HZUN3.js +0 -29
- package/dist/job-FGGQEXSU.js +0 -105
- package/dist/profile-DD5GMGNL.js +0 -501
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
BinaryOutputError,
|
|
4
|
+
writeBinaryOutput
|
|
5
|
+
} from "./chunk-UWO2D4HW.js";
|
|
6
|
+
import {
|
|
7
|
+
buildPreviewOutput
|
|
8
|
+
} from "./chunk-R3VLWLVV.js";
|
|
9
|
+
import {
|
|
10
|
+
slimJob
|
|
11
|
+
} from "./chunk-45KHSWCV.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveJobIdentifier
|
|
14
|
+
} from "./chunk-DMQZEPQE.js";
|
|
15
|
+
import {
|
|
16
|
+
streamAll
|
|
17
|
+
} from "./chunk-DNTRQZBT.js";
|
|
18
|
+
import {
|
|
19
|
+
createClient,
|
|
20
|
+
renderError,
|
|
21
|
+
renderSuccess,
|
|
22
|
+
renderUnexpectedError,
|
|
23
|
+
resolveEffectiveConfig
|
|
24
|
+
} from "./chunk-JXF47TRY.js";
|
|
25
|
+
import {
|
|
26
|
+
GLOBAL_FLAGS,
|
|
27
|
+
READ_SINGLE_FLAGS,
|
|
28
|
+
WRITE_SINGLE_FLAGS
|
|
29
|
+
} from "./chunk-4JHGVY7R.js";
|
|
30
|
+
|
|
31
|
+
// src/commands/job.ts
|
|
32
|
+
import { defineCommand } from "citty";
|
|
33
|
+
var JOB_STATES = ["DRAFT", "OPEN", "CLOSED", "REVIEW", "SUSPENDED"];
|
|
34
|
+
var WORKPLACE_TYPES = ["ON_SITE", "HYBRID", "REMOTE"];
|
|
35
|
+
var EMPLOYMENT_STATUSES = ["FULL_TIME", "PART_TIME", "CONTRACT", "TEMPORARY", "OTHER", "VOLUNTEER", "INTERNSHIP"];
|
|
36
|
+
var APPLY_METHODS = ["linkedin", "external"];
|
|
37
|
+
var PUBLISH_MODES = ["FREE", "PROMOTED", "PROMOTED_PLUS"];
|
|
38
|
+
var BUDGET_SCOPES = ["DAILY", "TOTAL"];
|
|
39
|
+
var RATINGS = ["UNRATED", "NOT_A_FIT", "MAYBE", "GOOD_FIT"];
|
|
40
|
+
function requestStateToItemState(state) {
|
|
41
|
+
return state === "OPEN" ? "LISTED" : state;
|
|
42
|
+
}
|
|
43
|
+
function filterJobsByState(items, requestedState) {
|
|
44
|
+
const source = items ?? [];
|
|
45
|
+
const wantedState = requestStateToItemState(requestedState);
|
|
46
|
+
const filtered = source.filter((item) => item.state === wantedState);
|
|
47
|
+
return { items: filtered, dropped: source.length - filtered.length };
|
|
48
|
+
}
|
|
49
|
+
function stateFilterDroppedNote(dropped, total, requestedState) {
|
|
50
|
+
return `note: LinkedIn's --state filter is best-effort -- dropped ${dropped} of ${total} returned item(s) on this page whose own state was not "${requestedState}". The page walk itself is unaffected (upstream pages are fetched unfiltered).
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
function buildOutputStreams() {
|
|
54
|
+
return {
|
|
55
|
+
stdout: { write: (s) => process.stdout.write(s) },
|
|
56
|
+
stderr: { write: (s) => process.stderr.write(s) }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function requireAccount(account, out) {
|
|
60
|
+
if (!account) {
|
|
61
|
+
out.stderr.write("error: --account is required for this command. Set it via --account, CURVIATE_ACCOUNT, or `curviate config set-account`.\n");
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
return account;
|
|
65
|
+
}
|
|
66
|
+
function rejectPreviewOnRead(preview, out) {
|
|
67
|
+
if (preview) {
|
|
68
|
+
out.stderr.write("error: --preview is only valid on write commands (mutations). Reads just run.\n");
|
|
69
|
+
process.exit(2);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function resolveOutputOpts(flags) {
|
|
73
|
+
return {
|
|
74
|
+
json: (flags.json ?? false) || !process.stdout.isTTY,
|
|
75
|
+
isTTY: process.stdout.isTTY ?? false,
|
|
76
|
+
fields: flags.fields,
|
|
77
|
+
verbose: flags.verbose ?? false
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function requireFlag(value, flag, out) {
|
|
81
|
+
if (!value) {
|
|
82
|
+
out.stderr.write(`error: ${flag} is required.
|
|
83
|
+
`);
|
|
84
|
+
process.exit(2);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
function requireEnum(value, allowed, flag, out) {
|
|
89
|
+
if (!allowed.includes(value)) {
|
|
90
|
+
out.stderr.write(`error: ${flag} must be one of: ${allowed.join(", ")}. Got "${value}".
|
|
91
|
+
`);
|
|
92
|
+
process.exit(2);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function handleSdkError(err, outOpts, out) {
|
|
96
|
+
const { CurviateError } = await import("@curviate/sdk");
|
|
97
|
+
if (err instanceof CurviateError) {
|
|
98
|
+
const { getExitCode } = await import("./exit-codes-SL3GQF7W.js");
|
|
99
|
+
renderError(err, outOpts, out);
|
|
100
|
+
process.exit(getExitCode(err.code));
|
|
101
|
+
}
|
|
102
|
+
renderUnexpectedError(err, out);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
function buildRef(idValue, nameValue) {
|
|
106
|
+
if (idValue) return { id: idValue };
|
|
107
|
+
if (nameValue) return { name: nameValue };
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
function buildApplyMethod(flags, out) {
|
|
111
|
+
const method = flags["apply-method"] ?? "";
|
|
112
|
+
requireEnum(method, APPLY_METHODS, "--apply-method", out);
|
|
113
|
+
if (method === "linkedin") {
|
|
114
|
+
const email = requireFlag(flags["notification-email"], "--notification-email (required when --apply-method is linkedin)", out);
|
|
115
|
+
return { method, notification_email: email };
|
|
116
|
+
}
|
|
117
|
+
const url = requireFlag(flags["website-url"], "--website-url (required when --apply-method is external)", out);
|
|
118
|
+
return { method, website_url: url };
|
|
119
|
+
}
|
|
120
|
+
async function runJobGet(client, flags, out) {
|
|
121
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
122
|
+
const accountId = requireAccount(flags.account, out);
|
|
123
|
+
const resolvedId = resolveJobIdentifier(flags.id ?? "");
|
|
124
|
+
const ns = client.account(accountId);
|
|
125
|
+
const outOpts = resolveOutputOpts(flags);
|
|
126
|
+
try {
|
|
127
|
+
const result = await ns.jobs.get(resolvedId);
|
|
128
|
+
renderSuccess(result, { ...outOpts, slim: slimJob }, out);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
await handleSdkError(err, outOpts, out);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function runJobList(client, flags, out) {
|
|
134
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
135
|
+
const accountId = requireAccount(flags.account, out);
|
|
136
|
+
const state = requireFlag(flags.state, "--state", out);
|
|
137
|
+
requireEnum(state, JOB_STATES, "--state", out);
|
|
138
|
+
const ns = client.account(accountId);
|
|
139
|
+
const outOpts = resolveOutputOpts(flags);
|
|
140
|
+
const all = flags.all ?? false;
|
|
141
|
+
const maxPages = flags["max-pages"] ? parseInt(flags["max-pages"], 10) : 100;
|
|
142
|
+
const base = { state };
|
|
143
|
+
if (flags.limit) base.limit = parseInt(flags.limit, 10);
|
|
144
|
+
if (flags.cursor) base.cursor = flags.cursor;
|
|
145
|
+
try {
|
|
146
|
+
if (all) {
|
|
147
|
+
const fn = (p) => ns.jobs.list(p).then((page) => {
|
|
148
|
+
const { items: filtered, dropped } = filterJobsByState(page.items, state);
|
|
149
|
+
if (dropped > 0) {
|
|
150
|
+
out.stderr.write(stateFilterDroppedNote(dropped, page.items?.length ?? 0, state));
|
|
151
|
+
}
|
|
152
|
+
return { ...page, items: filtered };
|
|
153
|
+
});
|
|
154
|
+
for await (const item of streamAll(fn, base, {
|
|
155
|
+
maxPages,
|
|
156
|
+
out
|
|
157
|
+
})) {
|
|
158
|
+
out.stdout.write(JSON.stringify(item) + "\n");
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
const result = await ns.jobs.list(base);
|
|
162
|
+
const { items: filtered, dropped } = filterJobsByState(result.items, state);
|
|
163
|
+
if (dropped > 0) {
|
|
164
|
+
out.stderr.write(stateFilterDroppedNote(dropped, result.items?.length ?? 0, state));
|
|
165
|
+
}
|
|
166
|
+
renderSuccess({ ...result, items: filtered }, outOpts, out);
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
await handleSdkError(err, outOpts, out);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function runJobBudget(client, flags, out) {
|
|
173
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
174
|
+
const accountId = requireAccount(flags.account, out);
|
|
175
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
176
|
+
const ns = client.account(accountId);
|
|
177
|
+
const outOpts = resolveOutputOpts(flags);
|
|
178
|
+
try {
|
|
179
|
+
const result = await ns.jobs.getBudget(jobId);
|
|
180
|
+
renderSuccess(result, outOpts, out);
|
|
181
|
+
} catch (err) {
|
|
182
|
+
await handleSdkError(err, outOpts, out);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function runJobApplicants(client, flags, out) {
|
|
186
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
187
|
+
const accountId = requireAccount(flags.account, out);
|
|
188
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
189
|
+
const ns = client.account(accountId);
|
|
190
|
+
const outOpts = resolveOutputOpts(flags);
|
|
191
|
+
const all = flags.all ?? false;
|
|
192
|
+
const maxPages = flags["max-pages"] ? parseInt(flags["max-pages"], 10) : 100;
|
|
193
|
+
const base = {};
|
|
194
|
+
if (flags.ratings) {
|
|
195
|
+
const ratings = flags.ratings.split(",").map((r) => r.trim()).filter(Boolean);
|
|
196
|
+
for (const r of ratings) requireEnum(r, RATINGS, "--ratings", out);
|
|
197
|
+
base.ratings = ratings;
|
|
198
|
+
}
|
|
199
|
+
if (flags.limit) base.limit = parseInt(flags.limit, 10);
|
|
200
|
+
if (flags.cursor) base.cursor = flags.cursor;
|
|
201
|
+
try {
|
|
202
|
+
if (all) {
|
|
203
|
+
const fn = (p) => ns.jobs.listApplicants(jobId, p);
|
|
204
|
+
for await (const item of streamAll(fn, base, {
|
|
205
|
+
maxPages,
|
|
206
|
+
out
|
|
207
|
+
})) {
|
|
208
|
+
out.stdout.write(JSON.stringify(item) + "\n");
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
const result = await ns.jobs.listApplicants(jobId, base);
|
|
212
|
+
renderSuccess(result, outOpts, out);
|
|
213
|
+
}
|
|
214
|
+
} catch (err) {
|
|
215
|
+
await handleSdkError(err, outOpts, out);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function runJobApplicantGet(client, flags, out) {
|
|
219
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
220
|
+
const accountId = requireAccount(flags.account, out);
|
|
221
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
222
|
+
const applicantId = flags.applicantId ?? "";
|
|
223
|
+
const ns = client.account(accountId);
|
|
224
|
+
const outOpts = resolveOutputOpts(flags);
|
|
225
|
+
try {
|
|
226
|
+
const result = await ns.jobs.getApplicant(jobId, applicantId);
|
|
227
|
+
renderSuccess(result, outOpts, out);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
await handleSdkError(err, outOpts, out);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function runJobApplicantResume(client, flags, out, isTTY) {
|
|
233
|
+
rejectPreviewOnRead(flags.preview, out);
|
|
234
|
+
const accountId = requireAccount(flags.account, out);
|
|
235
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
236
|
+
const applicantId = flags.applicantId ?? "";
|
|
237
|
+
const ns = client.account(accountId);
|
|
238
|
+
try {
|
|
239
|
+
const data = await ns.jobs.downloadResume(jobId, applicantId);
|
|
240
|
+
await writeBinaryOutput(data, { outputPath: flags.output, isTTY, stdout: process.stdout });
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (err instanceof BinaryOutputError) {
|
|
243
|
+
out.stderr.write(`error: ${err.message}
|
|
244
|
+
`);
|
|
245
|
+
process.exit(err.exitCode);
|
|
246
|
+
}
|
|
247
|
+
await handleSdkError(err, resolveOutputOpts(flags), out);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function runJobCreate(client, flags, out) {
|
|
251
|
+
const accountId = requireAccount(flags.account, out);
|
|
252
|
+
if (!flags["job-title"] && !flags["job-title-id"]) {
|
|
253
|
+
out.stderr.write("error: --job-title (a free-text name) or --job-title-id is required.\n");
|
|
254
|
+
process.exit(2);
|
|
255
|
+
}
|
|
256
|
+
if (!flags.company && !flags["company-id"]) {
|
|
257
|
+
out.stderr.write("error: --company (a free-text name) or --company-id is required.\n");
|
|
258
|
+
process.exit(2);
|
|
259
|
+
}
|
|
260
|
+
const workplaceType = requireFlag(flags["workplace-type"], "--workplace-type", out);
|
|
261
|
+
requireEnum(workplaceType, WORKPLACE_TYPES, "--workplace-type", out);
|
|
262
|
+
const location = requireFlag(flags.location, "--location", out);
|
|
263
|
+
const employmentStatus = requireFlag(flags["employment-status"], "--employment-status", out);
|
|
264
|
+
requireEnum(employmentStatus, EMPLOYMENT_STATUSES, "--employment-status", out);
|
|
265
|
+
const description = requireFlag(flags.description, "--description", out);
|
|
266
|
+
requireFlag(flags["apply-method"], "--apply-method", out);
|
|
267
|
+
const applyMethod = buildApplyMethod(flags, out);
|
|
268
|
+
const body = {
|
|
269
|
+
job_title: buildRef(flags["job-title-id"], flags["job-title"]),
|
|
270
|
+
company: buildRef(flags["company-id"], flags.company),
|
|
271
|
+
workplace_type: workplaceType,
|
|
272
|
+
location,
|
|
273
|
+
employment_status: employmentStatus,
|
|
274
|
+
description,
|
|
275
|
+
apply_method: applyMethod
|
|
276
|
+
};
|
|
277
|
+
if (flags.skills) {
|
|
278
|
+
body.skills = flags.skills.split(",").map((s) => s.trim()).filter(Boolean);
|
|
279
|
+
}
|
|
280
|
+
if (flags.preview) {
|
|
281
|
+
const preview = buildPreviewOutput({ method: "jobs.create", args: {}, body, account: accountId });
|
|
282
|
+
out.stdout.write(JSON.stringify(preview) + "\n");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const ns = client.account(accountId);
|
|
286
|
+
const outOpts = resolveOutputOpts(flags);
|
|
287
|
+
try {
|
|
288
|
+
const result = await ns.jobs.create(body);
|
|
289
|
+
renderSuccess(result, outOpts, out);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
await handleSdkError(err, outOpts, out);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async function runJobUpdate(client, flags, out) {
|
|
295
|
+
const accountId = requireAccount(flags.account, out);
|
|
296
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
297
|
+
const body = {};
|
|
298
|
+
const jobTitle = buildRef(flags["job-title-id"], flags["job-title"]);
|
|
299
|
+
if (jobTitle) body.job_title = jobTitle;
|
|
300
|
+
const company = buildRef(flags["company-id"], flags.company);
|
|
301
|
+
if (company) body.company = company;
|
|
302
|
+
if (flags["workplace-type"]) {
|
|
303
|
+
requireEnum(flags["workplace-type"], WORKPLACE_TYPES, "--workplace-type", out);
|
|
304
|
+
body.workplace_type = flags["workplace-type"];
|
|
305
|
+
}
|
|
306
|
+
if (flags.location) body.location = flags.location;
|
|
307
|
+
if (flags["employment-status"]) {
|
|
308
|
+
requireEnum(flags["employment-status"], EMPLOYMENT_STATUSES, "--employment-status", out);
|
|
309
|
+
body.employment_status = flags["employment-status"];
|
|
310
|
+
}
|
|
311
|
+
if (flags.description) body.description = flags.description;
|
|
312
|
+
if (flags["apply-method"]) body.apply_method = buildApplyMethod(flags, out);
|
|
313
|
+
if (flags.skills) body.skills = flags.skills.split(",").map((s) => s.trim()).filter(Boolean);
|
|
314
|
+
if (flags.preview) {
|
|
315
|
+
const preview = buildPreviewOutput({ method: "jobs.update", args: { job_id: jobId }, body, account: accountId });
|
|
316
|
+
out.stdout.write(JSON.stringify(preview) + "\n");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const ns = client.account(accountId);
|
|
320
|
+
const outOpts = resolveOutputOpts(flags);
|
|
321
|
+
try {
|
|
322
|
+
const result = await ns.jobs.update(jobId, body);
|
|
323
|
+
renderSuccess(result, outOpts, out);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
await handleSdkError(err, outOpts, out);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function runJobPublish(client, flags, out) {
|
|
329
|
+
const accountId = requireAccount(flags.account, out);
|
|
330
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
331
|
+
const mode = requireFlag(flags.mode, "--mode", out);
|
|
332
|
+
requireEnum(mode, PUBLISH_MODES, "--mode", out);
|
|
333
|
+
let body = { mode };
|
|
334
|
+
if (mode === "PROMOTED" || mode === "PROMOTED_PLUS") {
|
|
335
|
+
const currency = requireFlag(flags["budget-currency"], "--budget-currency (required for a paid publish)", out);
|
|
336
|
+
const amountRaw = requireFlag(flags["budget-amount"], "--budget-amount (required for a paid publish)", out);
|
|
337
|
+
const scope = requireFlag(flags["budget-scope"], "--budget-scope (required for a paid publish)", out);
|
|
338
|
+
requireEnum(scope, BUDGET_SCOPES, "--budget-scope", out);
|
|
339
|
+
const amount = Number(amountRaw);
|
|
340
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
341
|
+
out.stderr.write("error: --budget-amount must be a non-negative number.\n");
|
|
342
|
+
process.exit(2);
|
|
343
|
+
}
|
|
344
|
+
body = { mode, budget: { currency, amount, scope } };
|
|
345
|
+
}
|
|
346
|
+
if (flags.preview) {
|
|
347
|
+
const preview = buildPreviewOutput({ method: "jobs.publish", args: { job_id: jobId }, body, account: accountId });
|
|
348
|
+
out.stdout.write(JSON.stringify(preview) + "\n");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const ns = client.account(accountId);
|
|
352
|
+
const outOpts = resolveOutputOpts(flags);
|
|
353
|
+
try {
|
|
354
|
+
const result = await ns.jobs.publish(jobId, body);
|
|
355
|
+
renderSuccess(result, outOpts, out);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
await handleSdkError(err, outOpts, out);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async function runJobClose(client, flags, out) {
|
|
361
|
+
const accountId = requireAccount(flags.account, out);
|
|
362
|
+
const jobId = resolveJobIdentifier(flags.id ?? "");
|
|
363
|
+
if (flags.preview) {
|
|
364
|
+
const preview = buildPreviewOutput({ method: "jobs.close", args: { job_id: jobId }, body: {}, account: accountId });
|
|
365
|
+
out.stdout.write(JSON.stringify(preview) + "\n");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const ns = client.account(accountId);
|
|
369
|
+
const outOpts = resolveOutputOpts(flags);
|
|
370
|
+
try {
|
|
371
|
+
const result = await ns.jobs.close(jobId);
|
|
372
|
+
renderSuccess(result, outOpts, out);
|
|
373
|
+
} catch (err) {
|
|
374
|
+
await handleSdkError(err, outOpts, out);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function withClient(flags, fn) {
|
|
378
|
+
const cfg = await resolveEffectiveConfig({
|
|
379
|
+
apiKey: flags["api-key"],
|
|
380
|
+
baseUrl: flags["base-url"],
|
|
381
|
+
timeout: flags.timeout,
|
|
382
|
+
account: flags.account,
|
|
383
|
+
profile: flags.profile
|
|
384
|
+
});
|
|
385
|
+
if (!cfg.apiKey) {
|
|
386
|
+
process.stderr.write("error: no API key \u2014 run `curviate login` or pass --api-key.\n");
|
|
387
|
+
process.exit(3);
|
|
388
|
+
}
|
|
389
|
+
const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
|
|
390
|
+
const out = buildOutputStreams();
|
|
391
|
+
await fn(client, { ...flags, account: flags.account ?? cfg.account }, out);
|
|
392
|
+
}
|
|
393
|
+
var JOB_BODY_FLAGS = {
|
|
394
|
+
"job-title": { type: "string", description: "Job title as free text (or use --job-title-id for an existing LinkedIn title)." },
|
|
395
|
+
"job-title-id": { type: "string", description: "Existing LinkedIn job-title id." },
|
|
396
|
+
company: { type: "string", description: "Hiring company as free text (or use --company-id)." },
|
|
397
|
+
"company-id": { type: "string", description: "Existing company id." },
|
|
398
|
+
"workplace-type": { type: "string", description: "Workplace arrangement: ON_SITE|HYBRID|REMOTE." },
|
|
399
|
+
location: { type: "string", description: "A LOCATION parameter id (from search parameters)." },
|
|
400
|
+
"employment-status": { type: "string", description: "Employment type: FULL_TIME|PART_TIME|CONTRACT|TEMPORARY|OTHER|VOLUNTEER|INTERNSHIP." },
|
|
401
|
+
description: { type: "string", description: "Full job description (min 200 chars)." },
|
|
402
|
+
"apply-method": { type: "string", description: "How candidates apply: linkedin|external." },
|
|
403
|
+
"notification-email": { type: "string", description: "Email notified of new applicants (required when --apply-method is linkedin)." },
|
|
404
|
+
"website-url": { type: "string", description: "External apply URL (required when --apply-method is external)." },
|
|
405
|
+
skills: { type: "string", description: "Comma-separated skill parameter ids (optional)." }
|
|
406
|
+
};
|
|
407
|
+
var jobGetCommand = defineCommand({
|
|
408
|
+
meta: { name: "get", description: "Retrieve one public LinkedIn job posting's full detail." },
|
|
409
|
+
args: {
|
|
410
|
+
...READ_SINGLE_FLAGS,
|
|
411
|
+
id: { type: "positional", description: "Job URL or a bare numeric job id." }
|
|
412
|
+
},
|
|
413
|
+
async run({ args }) {
|
|
414
|
+
await withClient(args, runJobGet);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
var jobListCommand = defineCommand({
|
|
418
|
+
meta: { name: "list", description: "List your own classic job postings by state." },
|
|
419
|
+
args: {
|
|
420
|
+
...GLOBAL_FLAGS,
|
|
421
|
+
state: { type: "string", description: "Filter by state (required): DRAFT|OPEN|CLOSED|REVIEW|SUSPENDED. Best-effort on LinkedIn's side -- verify item.state. The CLI re-filters returned items against their own state (dropped items are noted on stderr), but the upstream page walk itself is unfiltered, so --all may fetch more pages than the filtered item count implies.", required: true }
|
|
422
|
+
},
|
|
423
|
+
async run({ args }) {
|
|
424
|
+
await withClient(args, runJobList);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
var jobCreateCommand = defineCommand({
|
|
428
|
+
meta: { name: "create", description: "Create a classic job-posting draft (never publishes, never spends)." },
|
|
429
|
+
args: {
|
|
430
|
+
...WRITE_SINGLE_FLAGS,
|
|
431
|
+
...JOB_BODY_FLAGS
|
|
432
|
+
},
|
|
433
|
+
async run({ args }) {
|
|
434
|
+
await withClient(args, runJobCreate);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
var jobUpdateCommand = defineCommand({
|
|
438
|
+
meta: { name: "update", description: "Apply a partial update to a job posting you own." },
|
|
439
|
+
args: {
|
|
440
|
+
...WRITE_SINGLE_FLAGS,
|
|
441
|
+
id: { type: "positional", description: "Job id to update." },
|
|
442
|
+
...JOB_BODY_FLAGS
|
|
443
|
+
},
|
|
444
|
+
async run({ args }) {
|
|
445
|
+
await withClient(args, runJobUpdate);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
var jobBudgetCommand = defineCommand({
|
|
449
|
+
meta: { name: "budget", description: "Price a publish before committing any money." },
|
|
450
|
+
args: {
|
|
451
|
+
...READ_SINGLE_FLAGS,
|
|
452
|
+
id: { type: "positional", description: "Job id to price." }
|
|
453
|
+
},
|
|
454
|
+
async run({ args }) {
|
|
455
|
+
await withClient(args, runJobBudget);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
var jobPublishCommand = defineCommand({
|
|
459
|
+
meta: { name: "publish", description: "Publish a draft. PROMOTED/PROMOTED_PLUS spend real money and require --budget-*." },
|
|
460
|
+
args: {
|
|
461
|
+
...WRITE_SINGLE_FLAGS,
|
|
462
|
+
id: { type: "positional", description: "Job id to publish." },
|
|
463
|
+
mode: { type: "string", description: "Publish mode (required): FREE|PROMOTED|PROMOTED_PLUS.", required: true },
|
|
464
|
+
"budget-currency": { type: "string", description: "ISO-4217 currency (required for a paid publish), e.g. EUR." },
|
|
465
|
+
"budget-amount": { type: "string", description: "Budget amount (required for a paid publish)." },
|
|
466
|
+
"budget-scope": { type: "string", description: "Budget scope (required for a paid publish): DAILY|TOTAL." }
|
|
467
|
+
},
|
|
468
|
+
async run({ args }) {
|
|
469
|
+
await withClient(args, runJobPublish);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
var jobCloseCommand = defineCommand({
|
|
473
|
+
meta: { name: "close", description: "Stop a posting from accepting applications (irreversible once LISTED)." },
|
|
474
|
+
args: {
|
|
475
|
+
...WRITE_SINGLE_FLAGS,
|
|
476
|
+
id: { type: "positional", description: "Job id to close." }
|
|
477
|
+
},
|
|
478
|
+
async run({ args }) {
|
|
479
|
+
await withClient(args, runJobClose);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
var jobApplicantsCommand = defineCommand({
|
|
483
|
+
meta: { name: "applicants", description: "List applicants to a posting you own (POST-as-search)." },
|
|
484
|
+
args: {
|
|
485
|
+
...GLOBAL_FLAGS,
|
|
486
|
+
id: { type: "positional", description: "Job id to list applicants for." },
|
|
487
|
+
ratings: { type: "string", description: "Comma-separated rating filter: UNRATED|NOT_A_FIT|MAYBE|GOOD_FIT. Omit for the full funnel." }
|
|
488
|
+
},
|
|
489
|
+
async run({ args }) {
|
|
490
|
+
await withClient(args, runJobApplicants);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
var jobApplicantGetCommand = defineCommand({
|
|
494
|
+
meta: { name: "get", description: "Get one applicant's full detail, including contact info." },
|
|
495
|
+
args: {
|
|
496
|
+
...READ_SINGLE_FLAGS,
|
|
497
|
+
id: { type: "positional", description: "Job id the applicant applied to." },
|
|
498
|
+
applicantId: { type: "positional", description: "Applicant id." }
|
|
499
|
+
},
|
|
500
|
+
async run({ args }) {
|
|
501
|
+
await withClient(args, runJobApplicantGet);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
var jobApplicantResumeCommand = defineCommand({
|
|
505
|
+
meta: { name: "resume", description: "Download an applicant's r\xE9sum\xE9 (binary)." },
|
|
506
|
+
args: {
|
|
507
|
+
...READ_SINGLE_FLAGS,
|
|
508
|
+
id: { type: "positional", description: "Job id the applicant applied to." },
|
|
509
|
+
applicantId: { type: "positional", description: "Applicant id." },
|
|
510
|
+
output: { type: "string", alias: "o", description: "Path to write the r\xE9sum\xE9 file to." }
|
|
511
|
+
},
|
|
512
|
+
async run({ args }) {
|
|
513
|
+
await withClient(args, (c, f, o) => runJobApplicantResume(c, f, o, process.stdout.isTTY ?? false));
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
var jobApplicantCommand = defineCommand({
|
|
517
|
+
meta: { name: "applicant", description: "Applicant detail + r\xE9sum\xE9 for a posting you own." },
|
|
518
|
+
args: { ...READ_SINGLE_FLAGS },
|
|
519
|
+
subCommands: {
|
|
520
|
+
get: jobApplicantGetCommand,
|
|
521
|
+
resume: jobApplicantResumeCommand
|
|
522
|
+
},
|
|
523
|
+
async run() {
|
|
524
|
+
process.stderr.write(
|
|
525
|
+
"Usage: curviate job applicant get <job_id> <applicant_id>\n curviate job applicant resume <job_id> <applicant_id> -o <file>\n"
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
var jobCommand = defineCommand({
|
|
530
|
+
meta: { name: "job", description: "Classic LinkedIn job-posting operations." },
|
|
531
|
+
args: { ...READ_SINGLE_FLAGS },
|
|
532
|
+
subCommands: {
|
|
533
|
+
get: jobGetCommand,
|
|
534
|
+
list: jobListCommand,
|
|
535
|
+
create: jobCreateCommand,
|
|
536
|
+
update: jobUpdateCommand,
|
|
537
|
+
budget: jobBudgetCommand,
|
|
538
|
+
publish: jobPublishCommand,
|
|
539
|
+
close: jobCloseCommand,
|
|
540
|
+
applicants: jobApplicantsCommand,
|
|
541
|
+
applicant: jobApplicantCommand
|
|
542
|
+
},
|
|
543
|
+
async run() {
|
|
544
|
+
process.stderr.write(
|
|
545
|
+
"Usage: curviate job get <url|id>\n curviate job list --state <DRAFT|OPEN|CLOSED|REVIEW|SUSPENDED>\n curviate job create --job-title <t> --company <c> --workplace-type <w> --location <id> --employment-status <e> --description <d> --apply-method <linkedin|external>\n curviate job update <id> [<flags>]\n curviate job budget <id>\n curviate job publish <id> --mode <FREE|PROMOTED|PROMOTED_PLUS>\n curviate job close <id>\n curviate job applicants <id>\n curviate job applicant get <id> <applicant_id>\n curviate job applicant resume <id> <applicant_id> -o <file>\n"
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
export {
|
|
550
|
+
jobCommand,
|
|
551
|
+
runJobApplicantGet,
|
|
552
|
+
runJobApplicantResume,
|
|
553
|
+
runJobApplicants,
|
|
554
|
+
runJobBudget,
|
|
555
|
+
runJobClose,
|
|
556
|
+
runJobCreate,
|
|
557
|
+
runJobGet,
|
|
558
|
+
runJobList,
|
|
559
|
+
runJobPublish,
|
|
560
|
+
runJobUpdate
|
|
561
|
+
};
|