Skip to content

Commit 8d0bb87

Browse files
Add searchJobs and fetchJob operations
1 parent 01af8ad commit 8d0bb87

4 files changed

Lines changed: 183 additions & 9 deletions

File tree

package-lock.json

Lines changed: 6 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@linkedapi/linkedin-cli",
3-
"version": "1.0.14",
3+
"version": "1.0.15",
44
"description": "AI-agent-friendly CLI for controlling LinkedIn accounts and retrieving real-time data.",
55
"author": "Linked API",
66
"license": "MIT",
@@ -43,7 +43,7 @@
4343
"ai-agent"
4444
],
4545
"dependencies": {
46-
"@linkedapi/node": "^2.0.1",
46+
"@linkedapi/node": "^2.0.4",
4747
"@oclif/core": "^4.2.10"
4848
},
4949
"devDependencies": {
@@ -79,6 +79,9 @@
7979
"company": {
8080
"description": "Fetch and search LinkedIn companies"
8181
},
82+
"jobs": {
83+
"description": "Fetch and search LinkedIn jobs"
84+
},
8285
"message": {
8386
"description": "Send and retrieve LinkedIn messages"
8487
},

src/commands/jobs/fetch.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Args } from '@oclif/core';
2+
3+
import { BaseCommand } from '@base-command';
4+
import { formatOutput } from '@core/output/formatter';
5+
import { runWorkflow } from '@core/workflow/workflow-runner';
6+
7+
export default class JobsFetch extends BaseCommand {
8+
static override description = 'Fetch a LinkedIn job';
9+
10+
static override args = {
11+
url: Args.string({
12+
description: 'LinkedIn job URL',
13+
required: true,
14+
}),
15+
};
16+
17+
static override flags = {
18+
...BaseCommand.baseFlags,
19+
};
20+
21+
static override examples = [
22+
'<%= config.bin %> jobs fetch https://www.linkedin.com/jobs/view/4416248954/',
23+
'<%= config.bin %> jobs fetch https://www.linkedin.com/jobs/view/4416248954/ --json',
24+
];
25+
26+
public async run(): Promise<void> {
27+
const { args, flags } = await this.parse(JobsFetch);
28+
29+
const client = await this.buildAuthenticatedClient();
30+
31+
const params: Record<string, unknown> = {
32+
jobUrl: args.url,
33+
};
34+
35+
try {
36+
const result = await runWorkflow(client.fetchJob, params, {
37+
isQuiet: flags.quiet,
38+
});
39+
40+
formatOutput({
41+
data: result.data,
42+
errors: result.errors,
43+
isJson: flags.json,
44+
fields: flags.fields,
45+
isQuiet: flags.quiet,
46+
});
47+
} catch (error) {
48+
this.handleError(error);
49+
}
50+
}
51+
}

src/commands/jobs/search.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { Flags } from '@oclif/core';
2+
3+
import { BaseCommand } from '@base-command';
4+
import { formatOutput } from '@core/output/formatter';
5+
import { runWorkflow } from '@core/workflow/workflow-runner';
6+
7+
export default class JobsSearch extends BaseCommand {
8+
static override description = 'Search for jobs on LinkedIn';
9+
10+
static override flags = {
11+
...BaseCommand.baseFlags,
12+
term: Flags.string({
13+
description: 'Search keyword or phrase',
14+
}),
15+
limit: Flags.integer({
16+
description: 'Max results to return',
17+
}),
18+
location: Flags.string({
19+
description: 'Filter by location',
20+
}),
21+
'date-posted': Flags.string({
22+
description: 'Filter by date posted',
23+
options: ['anyTime', 'past24Hours', 'pastWeek', 'pastMonth'],
24+
}),
25+
'experience-levels': Flags.string({
26+
description:
27+
'Filter by experience levels (comma-separated: internship, entryLevel, associate, midSeniorLevel, director, executive)',
28+
}),
29+
'employment-types': Flags.string({
30+
description:
31+
'Filter by employment types (comma-separated: fullTime, partTime, contract, temporary, volunteer, internship, other)',
32+
}),
33+
'workplace-types': Flags.string({
34+
description: 'Filter by workplace types (comma-separated: onSite, remote, hybrid)',
35+
}),
36+
companies: Flags.string({
37+
description: 'Filter by company names (comma-separated)',
38+
}),
39+
industries: Flags.string({
40+
description: 'Filter by industries (comma-separated)',
41+
}),
42+
'job-functions': Flags.string({
43+
description: 'Filter by job functions (comma-separated)',
44+
}),
45+
'easy-apply': Flags.boolean({
46+
description: 'Only jobs with Easy Apply',
47+
default: false,
48+
}),
49+
'has-verifications': Flags.boolean({
50+
description: 'Only jobs with verification signals',
51+
default: false,
52+
}),
53+
'under-10-applicants': Flags.boolean({
54+
description: 'Only jobs with fewer than 10 applicants',
55+
default: false,
56+
}),
57+
'in-your-network': Flags.boolean({
58+
description: 'Only jobs from your network',
59+
default: false,
60+
}),
61+
'fair-chance-employer': Flags.boolean({
62+
description: 'Only fair chance employer jobs',
63+
default: false,
64+
}),
65+
};
66+
67+
static override examples = [
68+
'<%= config.bin %> jobs search --term "product manager" --location "San Francisco" --limit 20',
69+
'<%= config.bin %> jobs search --term engineer --workplace-types "remote,hybrid" --easy-apply --json',
70+
];
71+
72+
public async run(): Promise<void> {
73+
const { flags } = await this.parse(JobsSearch);
74+
75+
const client = await this.buildAuthenticatedClient();
76+
77+
const params: Record<string, unknown> = {};
78+
if (flags.term) params.term = flags.term;
79+
if (flags.limit) params.limit = flags.limit;
80+
81+
const filter: Record<string, unknown> = {};
82+
if (flags.location) filter.location = flags.location;
83+
if (flags['date-posted']) filter.datePosted = flags['date-posted'];
84+
if (flags['experience-levels'])
85+
filter.experienceLevels = splitCsv(flags['experience-levels']);
86+
if (flags['employment-types']) filter.employmentTypes = splitCsv(flags['employment-types']);
87+
if (flags['workplace-types']) filter.workplaceTypes = splitCsv(flags['workplace-types']);
88+
if (flags.companies) filter.companies = splitCsv(flags.companies);
89+
if (flags.industries) filter.industries = splitCsv(flags.industries);
90+
if (flags['job-functions']) filter.jobFunctions = splitCsv(flags['job-functions']);
91+
if (flags['easy-apply']) filter.easyApply = true;
92+
if (flags['has-verifications']) filter.hasVerifications = true;
93+
if (flags['under-10-applicants']) filter.under10Applicants = true;
94+
if (flags['in-your-network']) filter.inYourNetwork = true;
95+
if (flags['fair-chance-employer']) filter.fairChanceEmployer = true;
96+
97+
if (Object.keys(filter).length > 0) {
98+
params.filter = filter;
99+
}
100+
101+
try {
102+
const result = await runWorkflow(client.searchJobs, params, {
103+
isQuiet: flags.quiet,
104+
});
105+
106+
formatOutput({
107+
data: result.data,
108+
errors: result.errors,
109+
isJson: flags.json,
110+
fields: flags.fields,
111+
isQuiet: flags.quiet,
112+
});
113+
} catch (error) {
114+
this.handleError(error);
115+
}
116+
}
117+
}
118+
119+
function splitCsv(value: string): Array<string> {
120+
return value.split(',').map((s) => s.trim());
121+
}

0 commit comments

Comments
 (0)