Skip to content

Commit e053447

Browse files
Admin API added
1 parent d016daf commit e053447

26 files changed

Lines changed: 961 additions & 6 deletions

package-lock.json

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

package.json

Lines changed: 14 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.8",
3+
"version": "1.0.9",
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": "^1.2.17",
46+
"@linkedapi/node": "^1.2.18",
4747
"@oclif/core": "^4.2.10"
4848
},
4949
"devDependencies": {
@@ -105,6 +105,18 @@
105105
},
106106
"workflow": {
107107
"description": "Execute custom workflows"
108+
},
109+
"admin": {
110+
"description": "Manage subscription, accounts, and limits"
111+
},
112+
"admin:subscription": {
113+
"description": "Manage subscription and billing"
114+
},
115+
"admin:accounts": {
116+
"description": "Manage connected LinkedIn accounts"
117+
},
118+
"admin:limits": {
119+
"description": "Configure and monitor rate limits"
108120
}
109121
}
110122
},

src/admin-base-command.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Command, Flags } from '@oclif/core';
2+
import { LinkedApiAdmin, LinkedApiError } from '@linkedapi/node';
3+
4+
import { resolveAdminToken } from '@core/auth/auth-manager';
5+
import { buildAdminClient } from '@core/client/build-admin-client';
6+
import { mapLinkedApiErrorToCliError, writeErrorToStderr } from '@core/errors/error-handler';
7+
import { EXIT_CODE } from '@core/errors/exit-codes';
8+
9+
export abstract class AdminBaseCommand extends Command {
10+
static override baseFlags = {
11+
json: Flags.boolean({
12+
description: 'Output as JSON',
13+
default: false,
14+
}),
15+
fields: Flags.string({
16+
description: 'Comma-separated list of fields to include in output',
17+
}),
18+
quiet: Flags.boolean({
19+
char: 'q',
20+
description: 'Suppress progress output on stderr',
21+
default: false,
22+
}),
23+
'no-color': Flags.boolean({
24+
description: 'Disable colored output',
25+
default: false,
26+
}),
27+
};
28+
29+
public override async init(): Promise<void> {
30+
await super.init();
31+
const { flags } = await this.parse(this.constructor as typeof AdminBaseCommand);
32+
33+
if (flags['no-color']) {
34+
process.env.NO_COLOR = '1';
35+
}
36+
}
37+
38+
protected async buildAdminClient(): Promise<LinkedApiAdmin> {
39+
try {
40+
const linkedApiToken = resolveAdminToken();
41+
return buildAdminClient(linkedApiToken);
42+
} catch (error) {
43+
if (error instanceof Error) {
44+
process.stderr.write(error.message + '\n');
45+
}
46+
47+
this.exit(EXIT_CODE.AUTH);
48+
throw error;
49+
}
50+
}
51+
52+
protected handleError(error: unknown): never {
53+
if (error instanceof LinkedApiError) {
54+
const cliError = mapLinkedApiErrorToCliError(error);
55+
writeErrorToStderr(cliError);
56+
this.exit(cliError.exitCode);
57+
}
58+
59+
if (error instanceof Error) {
60+
writeErrorToStderr({
61+
exitCode: EXIT_CODE.GENERAL,
62+
error: 'unexpectedError',
63+
message: error.message,
64+
});
65+
this.exit(EXIT_CODE.GENERAL);
66+
}
67+
68+
writeErrorToStderr({
69+
exitCode: EXIT_CODE.GENERAL,
70+
error: 'unexpectedError',
71+
message: 'An unexpected error occurred',
72+
});
73+
this.exit(EXIT_CODE.GENERAL);
74+
75+
throw error;
76+
}
77+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Args } from '@oclif/core';
2+
3+
import { AdminBaseCommand } from '@admin-base-command';
4+
import { formatAdminVoidOutput } from '@core/output/admin-formatter';
5+
6+
export default class AdminAccountsCancelSession extends AdminBaseCommand {
7+
static override description = 'Cancel a pending connection session';
8+
9+
static override args = {
10+
sessionId: Args.string({
11+
description: 'Session UUID',
12+
required: true,
13+
}),
14+
};
15+
16+
static override flags = {
17+
...AdminBaseCommand.baseFlags,
18+
};
19+
20+
static override examples = ['<%= config.bin %> admin accounts cancel-session 990eef7a-...'];
21+
22+
public async run(): Promise<void> {
23+
const { args, flags } = await this.parse(AdminAccountsCancelSession);
24+
const admin = await this.buildAdminClient();
25+
26+
try {
27+
await admin.accounts.cancelConnectionSession({
28+
sessionId: args.sessionId,
29+
});
30+
31+
formatAdminVoidOutput({
32+
isJson: flags.json,
33+
isQuiet: flags.quiet,
34+
successMessage: 'Connection session cancelled.',
35+
});
36+
} catch (error) {
37+
this.handleError(error);
38+
}
39+
}
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AdminBaseCommand } from '@admin-base-command';
2+
import { formatAdminOutput } from '@core/output/admin-formatter';
3+
4+
export default class AdminAccountsConnect extends AdminBaseCommand {
5+
static override description = 'Create a connection session to connect a new LinkedIn account';
6+
7+
static override flags = {
8+
...AdminBaseCommand.baseFlags,
9+
};
10+
11+
static override examples = ['<%= config.bin %> admin accounts connect'];
12+
13+
public async run(): Promise<void> {
14+
const { flags } = await this.parse(AdminAccountsConnect);
15+
const admin = await this.buildAdminClient();
16+
17+
try {
18+
const result = await admin.accounts.createConnectionSession();
19+
20+
formatAdminOutput({
21+
data: result,
22+
isJson: flags.json,
23+
fields: flags.fields,
24+
});
25+
} catch (error) {
26+
this.handleError(error);
27+
}
28+
}
29+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Args } from '@oclif/core';
2+
3+
import { AdminBaseCommand } from '@admin-base-command';
4+
import { formatAdminVoidOutput } from '@core/output/admin-formatter';
5+
6+
export default class AdminAccountsDisconnect extends AdminBaseCommand {
7+
static override description = 'Disconnect a LinkedIn account (irreversible)';
8+
9+
static override args = {
10+
accountId: Args.string({
11+
description: 'Account UUID',
12+
required: true,
13+
}),
14+
};
15+
16+
static override flags = {
17+
...AdminBaseCommand.baseFlags,
18+
};
19+
20+
static override examples = ['<%= config.bin %> admin accounts disconnect f9b4346a-...'];
21+
22+
public async run(): Promise<void> {
23+
const { args, flags } = await this.parse(AdminAccountsDisconnect);
24+
const admin = await this.buildAdminClient();
25+
26+
try {
27+
await admin.accounts.disconnect({ accountId: args.accountId });
28+
29+
formatAdminVoidOutput({
30+
isJson: flags.json,
31+
isQuiet: flags.quiet,
32+
successMessage: 'Account disconnected.',
33+
});
34+
} catch (error) {
35+
this.handleError(error);
36+
}
37+
}
38+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AdminBaseCommand } from '@admin-base-command';
2+
import { formatAdminOutput } from '@core/output/admin-formatter';
3+
4+
export default class AdminAccountsList extends AdminBaseCommand {
5+
static override description = 'List all connected accounts and pending sessions';
6+
7+
static override flags = {
8+
...AdminBaseCommand.baseFlags,
9+
};
10+
11+
static override examples = ['<%= config.bin %> admin accounts list'];
12+
13+
public async run(): Promise<void> {
14+
const { flags } = await this.parse(AdminAccountsList);
15+
const admin = await this.buildAdminClient();
16+
17+
try {
18+
const result = await admin.accounts.getAll();
19+
20+
formatAdminOutput({
21+
data: result,
22+
isJson: flags.json,
23+
fields: flags.fields,
24+
});
25+
} catch (error) {
26+
this.handleError(error);
27+
}
28+
}
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Args } from '@oclif/core';
2+
3+
import { AdminBaseCommand } from '@admin-base-command';
4+
import { formatAdminOutput } from '@core/output/admin-formatter';
5+
6+
export default class AdminAccountsRegenerateToken extends AdminBaseCommand {
7+
static override description = 'Regenerate identification token for an account';
8+
9+
static override args = {
10+
accountId: Args.string({
11+
description: 'Account UUID',
12+
required: true,
13+
}),
14+
};
15+
16+
static override flags = {
17+
...AdminBaseCommand.baseFlags,
18+
};
19+
20+
static override examples = ['<%= config.bin %> admin accounts regenerate-token f9b4346a-...'];
21+
22+
public async run(): Promise<void> {
23+
const { args, flags } = await this.parse(AdminAccountsRegenerateToken);
24+
const admin = await this.buildAdminClient();
25+
26+
try {
27+
const result = await admin.accounts.regenerateIdentificationToken({
28+
accountId: args.accountId,
29+
});
30+
31+
formatAdminOutput({
32+
data: result,
33+
isJson: flags.json,
34+
fields: flags.fields,
35+
});
36+
} catch (error) {
37+
this.handleError(error);
38+
}
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Args } from '@oclif/core';
2+
3+
import { AdminBaseCommand } from '@admin-base-command';
4+
import { formatAdminOutput } from '@core/output/admin-formatter';
5+
6+
export default class AdminAccountsSession extends AdminBaseCommand {
7+
static override description = 'Check connection session status';
8+
9+
static override args = {
10+
sessionId: Args.string({
11+
description: 'Session UUID',
12+
required: true,
13+
}),
14+
};
15+
16+
static override flags = {
17+
...AdminBaseCommand.baseFlags,
18+
};
19+
20+
static override examples = ['<%= config.bin %> admin accounts session 990eef7a-...'];
21+
22+
public async run(): Promise<void> {
23+
const { args, flags } = await this.parse(AdminAccountsSession);
24+
const admin = await this.buildAdminClient();
25+
26+
try {
27+
const result = await admin.accounts.getConnectionSession({
28+
sessionId: args.sessionId,
29+
});
30+
31+
formatAdminOutput({
32+
data: result.session,
33+
isJson: flags.json,
34+
fields: flags.fields,
35+
});
36+
} catch (error) {
37+
this.handleError(error);
38+
}
39+
}
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AdminBaseCommand } from '@admin-base-command';
2+
import { formatAdminOutput } from '@core/output/admin-formatter';
3+
4+
export default class AdminLimitsDefaults extends AdminBaseCommand {
5+
static override description = 'View system default limits';
6+
7+
static override flags = {
8+
...AdminBaseCommand.baseFlags,
9+
};
10+
11+
static override examples = ['<%= config.bin %> admin limits defaults'];
12+
13+
public async run(): Promise<void> {
14+
const { flags } = await this.parse(AdminLimitsDefaults);
15+
const admin = await this.buildAdminClient();
16+
17+
try {
18+
const { limits } = await admin.limits.getDefaults();
19+
20+
formatAdminOutput({
21+
data: limits,
22+
isJson: flags.json,
23+
fields: flags.fields,
24+
});
25+
} catch (error) {
26+
this.handleError(error);
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)