linkedin-agent 1.0.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/dist/cli.js ADDED
@@ -0,0 +1,514 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const scraper_1 = require("./scraper");
40
+ const auth_1 = require("./auth");
41
+ const poster_1 = require("./poster");
42
+ const dev_app_1 = require("./dev-app");
43
+ // ---------------------------------------------------------------------------
44
+ // Help
45
+ // ---------------------------------------------------------------------------
46
+ function printHelp() {
47
+ console.log(`
48
+ Usage: linkedin-agent <command> [options]
49
+
50
+ LinkedIn 자동화 도구 - 게시글 수집, 작성, 수정, 삭제
51
+
52
+ Commands:
53
+ get 게시글 수집
54
+ post 게시글 작성
55
+ auth OAuth 인증 설정
56
+ edit 게시글 수정
57
+ delete 게시글 삭제
58
+
59
+ Global Options:
60
+ --json JSON 출력 (agent/프로그램 연동용)
61
+
62
+ Run 'linkedin-agent <command> --help' for command-specific options.
63
+ `);
64
+ }
65
+ function printGetHelp() {
66
+ console.log(`
67
+ Usage: linkedin-agent get [options]
68
+
69
+ 게시글과 반응 데이터를 수집합니다.
70
+
71
+ Options:
72
+ -p, --profile <url> 대상 프로필 URL (기본: 내 프로필)
73
+ -l, --limit <n> 최근 게시글 N개만 수집
74
+ -o, --output <dir> 출력 디렉토리 (기본: 현재 디렉토리)
75
+ -m, --max-scrolls <n> 최대 스크롤 횟수 (기본: 100)
76
+ -h, --help 도움말 출력
77
+
78
+ Examples:
79
+ linkedin-agent get 내 게시글 수집
80
+ linkedin-agent get -p https://www.linkedin.com/in/someone 특정 프로필 게시글 수집
81
+ linkedin-agent get -l 10 최근 10개만 수집
82
+ `);
83
+ }
84
+ function printPostHelp() {
85
+ console.log(`
86
+ Usage: linkedin-agent post [options]
87
+
88
+ LinkedIn에 새 게시글을 작성합니다.
89
+
90
+ Options:
91
+ -t, --text <text> 게시글 내용
92
+ -f, --file <path> 파일에서 게시글 내용 읽기
93
+ --link <url> 링크 첨부
94
+ --json JSON 출력
95
+ -h, --help 도움말 출력
96
+
97
+ Examples:
98
+ linkedin-agent post -t "오늘의 게시글입니다."
99
+ linkedin-agent post -f ./post.md --json
100
+ linkedin-agent post -t "내용" --link https://example.com
101
+ `);
102
+ }
103
+ function printAuthHelp() {
104
+ console.log(`
105
+ Usage: linkedin-agent auth [options]
106
+
107
+ LinkedIn OAuth 인증을 설정합니다.
108
+
109
+ 인자 없이 실행하면 Developer App 자동 생성 → OAuth 인증까지 한번에 진행합니다.
110
+ 이미 Developer App이 있다면 --client-id, --client-secret 옵션으로 바로 인증합니다.
111
+
112
+ Options:
113
+ --client-id <id> LinkedIn App Client ID (수동 모드)
114
+ --client-secret <secret> LinkedIn App Client Secret (수동 모드)
115
+ --company-page <url> LinkedIn Company Page URL (자동 모드, 기본: https://www.linkedin.com/company/103290544/)
116
+ -h, --help 도움말 출력
117
+
118
+ Examples:
119
+ linkedin-agent auth 자동: App 생성 + OAuth
120
+ linkedin-agent auth --client-id 86xxx --client-secret WPL_xxx 수동: 기존 App으로 OAuth
121
+ linkedin-agent auth --company-page https://www.linkedin.com/company/12345/
122
+ `);
123
+ }
124
+ function printEditHelp() {
125
+ console.log(`
126
+ Usage: linkedin-agent edit [options]
127
+
128
+ 기존 LinkedIn 게시글의 텍스트를 수정합니다.
129
+ (링크, 이미지 등 첨부는 수정 불가 — LinkedIn API 제한)
130
+
131
+ Options:
132
+ --id <post-id> 수정할 게시글 ID (urn:li:share:... 형식)
133
+ -t, --text <text> 새 게시글 내용
134
+ -f, --file <path> 파일에서 새 게시글 내용 읽기
135
+ --json JSON 출력
136
+ -h, --help 도움말 출력
137
+
138
+ Examples:
139
+ linkedin-agent edit --id "urn:li:share:123456" -t "수정된 내용"
140
+ linkedin-agent edit --id "urn:li:share:123456" -f ./updated.md
141
+ `);
142
+ }
143
+ function printDeleteHelp() {
144
+ console.log(`
145
+ Usage: linkedin-agent delete [options]
146
+
147
+ LinkedIn 게시글을 삭제합니다.
148
+
149
+ Options:
150
+ --id <post-id> 삭제할 게시글 ID (urn:li:share:... 형식)
151
+ --json JSON 출력
152
+ -h, --help 도움말 출력
153
+
154
+ Examples:
155
+ linkedin-agent delete --id "urn:li:share:123456"
156
+ `);
157
+ }
158
+ // ---------------------------------------------------------------------------
159
+ // JSON output helper
160
+ // ---------------------------------------------------------------------------
161
+ function outputResult(result, json, action) {
162
+ if (json) {
163
+ console.log(JSON.stringify(result));
164
+ if (!result.success)
165
+ process.exit(1);
166
+ return;
167
+ }
168
+ if (result.success) {
169
+ switch (action) {
170
+ case "post":
171
+ console.log(`\n✅ Posted successfully!`);
172
+ if (result.postId)
173
+ console.log(` Post ID: ${result.postId}`);
174
+ break;
175
+ case "edit":
176
+ console.log(`\n✅ Post updated successfully!`);
177
+ break;
178
+ case "delete":
179
+ console.log(`\n✅ Post deleted successfully!`);
180
+ break;
181
+ }
182
+ }
183
+ else {
184
+ console.error(`\n❌ Failed to ${action}: ${result.error}`);
185
+ process.exit(1);
186
+ }
187
+ }
188
+ function fail(message, json) {
189
+ if (json) {
190
+ console.log(JSON.stringify({ success: false, error: message }));
191
+ }
192
+ else {
193
+ console.error(message);
194
+ }
195
+ process.exit(1);
196
+ }
197
+ // ---------------------------------------------------------------------------
198
+ // Parsers
199
+ // ---------------------------------------------------------------------------
200
+ function parseGetArgs(args) {
201
+ let output = process.cwd();
202
+ let maxScrolls = 100;
203
+ let profile;
204
+ let limit;
205
+ for (let i = 0; i < args.length; i++) {
206
+ switch (args[i]) {
207
+ case "-h":
208
+ case "--help":
209
+ printGetHelp();
210
+ process.exit(0);
211
+ case "-p":
212
+ case "--profile":
213
+ profile = args[++i];
214
+ if (!profile) {
215
+ console.error("Error: --profile requires a LinkedIn profile URL");
216
+ process.exit(1);
217
+ }
218
+ break;
219
+ case "-l":
220
+ case "--limit":
221
+ limit = parseInt(args[++i], 10);
222
+ if (isNaN(limit) || limit <= 0) {
223
+ console.error("Error: --limit requires a positive number");
224
+ process.exit(1);
225
+ }
226
+ break;
227
+ case "-o":
228
+ case "--output":
229
+ output = args[++i];
230
+ if (!output) {
231
+ console.error("Error: --output requires a directory path");
232
+ process.exit(1);
233
+ }
234
+ break;
235
+ case "-m":
236
+ case "--max-scrolls":
237
+ maxScrolls = parseInt(args[++i], 10);
238
+ if (isNaN(maxScrolls) || maxScrolls <= 0) {
239
+ console.error("Error: --max-scrolls requires a positive number");
240
+ process.exit(1);
241
+ }
242
+ break;
243
+ default:
244
+ console.error(`Unknown option: ${args[i]}`);
245
+ printGetHelp();
246
+ process.exit(1);
247
+ }
248
+ }
249
+ return { output, maxScrolls, profile, limit };
250
+ }
251
+ function parsePostArgs(args) {
252
+ let text;
253
+ let file;
254
+ let link;
255
+ let json = false;
256
+ for (let i = 0; i < args.length; i++) {
257
+ switch (args[i]) {
258
+ case "-h":
259
+ case "--help":
260
+ printPostHelp();
261
+ process.exit(0);
262
+ case "-t":
263
+ case "--text":
264
+ text = args[++i];
265
+ if (!text) {
266
+ console.error("Error: --text requires content");
267
+ process.exit(1);
268
+ }
269
+ break;
270
+ case "-f":
271
+ case "--file":
272
+ file = args[++i];
273
+ if (!file) {
274
+ console.error("Error: --file requires a file path");
275
+ process.exit(1);
276
+ }
277
+ break;
278
+ case "--link":
279
+ link = args[++i];
280
+ if (!link) {
281
+ console.error("Error: --link requires a URL");
282
+ process.exit(1);
283
+ }
284
+ break;
285
+ case "--json":
286
+ json = true;
287
+ break;
288
+ default:
289
+ console.error(`Unknown option: ${args[i]}`);
290
+ printPostHelp();
291
+ process.exit(1);
292
+ }
293
+ }
294
+ return { text, file, link, json };
295
+ }
296
+ function parseAuthArgs(args) {
297
+ let clientId;
298
+ let clientSecret;
299
+ let companyPage;
300
+ for (let i = 0; i < args.length; i++) {
301
+ switch (args[i]) {
302
+ case "-h":
303
+ case "--help":
304
+ printAuthHelp();
305
+ process.exit(0);
306
+ case "--client-id":
307
+ clientId = args[++i];
308
+ if (!clientId) {
309
+ console.error("Error: --client-id requires a value");
310
+ process.exit(1);
311
+ }
312
+ break;
313
+ case "--client-secret":
314
+ clientSecret = args[++i];
315
+ if (!clientSecret) {
316
+ console.error("Error: --client-secret requires a value");
317
+ process.exit(1);
318
+ }
319
+ break;
320
+ case "--company-page":
321
+ companyPage = args[++i];
322
+ if (!companyPage) {
323
+ console.error("Error: --company-page requires a LinkedIn Company Page URL");
324
+ process.exit(1);
325
+ }
326
+ break;
327
+ default:
328
+ console.error(`Unknown option: ${args[i]}`);
329
+ printAuthHelp();
330
+ process.exit(1);
331
+ }
332
+ }
333
+ return { clientId, clientSecret, companyPage };
334
+ }
335
+ function parseEditArgs(args) {
336
+ let postId;
337
+ let text;
338
+ let file;
339
+ let json = false;
340
+ for (let i = 0; i < args.length; i++) {
341
+ switch (args[i]) {
342
+ case "-h":
343
+ case "--help":
344
+ printEditHelp();
345
+ process.exit(0);
346
+ case "--id":
347
+ postId = args[++i];
348
+ if (!postId) {
349
+ console.error("Error: --id requires a post ID");
350
+ process.exit(1);
351
+ }
352
+ break;
353
+ case "-t":
354
+ case "--text":
355
+ text = args[++i];
356
+ if (!text) {
357
+ console.error("Error: --text requires content");
358
+ process.exit(1);
359
+ }
360
+ break;
361
+ case "-f":
362
+ case "--file":
363
+ file = args[++i];
364
+ if (!file) {
365
+ console.error("Error: --file requires a file path");
366
+ process.exit(1);
367
+ }
368
+ break;
369
+ case "--json":
370
+ json = true;
371
+ break;
372
+ default:
373
+ console.error(`Unknown option: ${args[i]}`);
374
+ printEditHelp();
375
+ process.exit(1);
376
+ }
377
+ }
378
+ return { postId, text, file, json };
379
+ }
380
+ function parseDeleteArgs(args) {
381
+ let postId;
382
+ let json = false;
383
+ for (let i = 0; i < args.length; i++) {
384
+ switch (args[i]) {
385
+ case "-h":
386
+ case "--help":
387
+ printDeleteHelp();
388
+ process.exit(0);
389
+ case "--id":
390
+ postId = args[++i];
391
+ if (!postId) {
392
+ console.error("Error: --id requires a post ID");
393
+ process.exit(1);
394
+ }
395
+ break;
396
+ case "--json":
397
+ json = true;
398
+ break;
399
+ default:
400
+ console.error(`Unknown option: ${args[i]}`);
401
+ printDeleteHelp();
402
+ process.exit(1);
403
+ }
404
+ }
405
+ return { postId, json };
406
+ }
407
+ // ---------------------------------------------------------------------------
408
+ // Handlers
409
+ // ---------------------------------------------------------------------------
410
+ async function handleAuth(args) {
411
+ const opts = parseAuthArgs(args);
412
+ if (opts.clientId && opts.clientSecret) {
413
+ await (0, auth_1.authenticate)(opts.clientId, opts.clientSecret);
414
+ console.log("\n✅ Auth successful. Credentials saved.");
415
+ return;
416
+ }
417
+ console.log("\n🚀 Auto-creating LinkedIn Developer App...\n");
418
+ const { launchBrowser } = await Promise.resolve().then(() => __importStar(require("./browser")));
419
+ const logoPath = path.join(__dirname, "..", "assets", "default-logo.png");
420
+ const context = await launchBrowser();
421
+ try {
422
+ const devApp = await (0, dev_app_1.setupDevApp)(context, logoPath, opts.companyPage);
423
+ console.log("🔐 Starting OAuth flow...\n");
424
+ const page = context.pages()[0] || (await context.newPage());
425
+ await (0, auth_1.authenticate)(devApp.clientId, devApp.clientSecret, async (url) => {
426
+ await page.goto(url, { waitUntil: "domcontentloaded" });
427
+ });
428
+ console.log("\n✅ Auth successful. Credentials saved.");
429
+ console.log(` App ID: ${devApp.appId}`);
430
+ console.log(` Client ID: ${devApp.clientId}`);
431
+ }
432
+ finally {
433
+ await context.close();
434
+ }
435
+ }
436
+ function resolveText(opts, json) {
437
+ if (opts.file) {
438
+ if (!fs.existsSync(opts.file))
439
+ fail(`Error: File not found: ${opts.file}`, json);
440
+ return fs.readFileSync(opts.file, "utf-8").trim();
441
+ }
442
+ if (opts.text)
443
+ return opts.text;
444
+ fail("Error: Provide content with -t or -f.", json);
445
+ }
446
+ async function handlePost(args) {
447
+ const opts = parsePostArgs(args);
448
+ if (!(0, auth_1.loadCredentials)()) {
449
+ fail("❌ Authentication required. Run 'linkedin-agent auth' first.", opts.json);
450
+ }
451
+ const text = resolveText(opts, opts.json);
452
+ if (!opts.json) {
453
+ console.log(`\n📝 Posting to LinkedIn (${text.length} chars)...`);
454
+ if (opts.link)
455
+ console.log(`🔗 Link: ${opts.link}`);
456
+ }
457
+ const result = await (0, poster_1.postToLinkedIn)({ text, linkUrl: opts.link });
458
+ outputResult(result, opts.json, "post");
459
+ }
460
+ async function handleEdit(args) {
461
+ const opts = parseEditArgs(args);
462
+ if (!(0, auth_1.loadCredentials)())
463
+ fail("❌ Authentication required. Run 'linkedin-agent auth' first.", opts.json);
464
+ if (!opts.postId)
465
+ fail("Error: --id is required.", opts.json);
466
+ const text = resolveText(opts, opts.json);
467
+ if (!opts.json)
468
+ console.log(`\n✏️ Editing post ${opts.postId} (${text.length} chars)...`);
469
+ const result = await (0, poster_1.editLinkedInPost)({ postId: opts.postId, text });
470
+ outputResult(result, opts.json, "edit");
471
+ }
472
+ async function handleDelete(args) {
473
+ const opts = parseDeleteArgs(args);
474
+ if (!(0, auth_1.loadCredentials)())
475
+ fail("❌ Authentication required. Run 'linkedin-agent auth' first.", opts.json);
476
+ if (!opts.postId)
477
+ fail("Error: --id is required.", opts.json);
478
+ if (!opts.json)
479
+ console.log(`\n🗑️ Deleting post ${opts.postId}...`);
480
+ const result = await (0, poster_1.deleteLinkedInPost)(opts.postId);
481
+ outputResult(result, opts.json, "delete");
482
+ }
483
+ // ---------------------------------------------------------------------------
484
+ // Main
485
+ // ---------------------------------------------------------------------------
486
+ const command = process.argv[2];
487
+ const commandArgs = process.argv.slice(3);
488
+ switch (command) {
489
+ case "get":
490
+ const getOpts = parseGetArgs(commandArgs);
491
+ (0, scraper_1.getLinkedInPosts)(getOpts).catch((err) => { console.error("❌ Error:", err); process.exit(1); });
492
+ break;
493
+ case "post":
494
+ handlePost(commandArgs).catch((err) => { console.error("❌ Error:", err); process.exit(1); });
495
+ break;
496
+ case "auth":
497
+ handleAuth(commandArgs).catch((err) => { console.error("❌ Error:", err); process.exit(1); });
498
+ break;
499
+ case "edit":
500
+ handleEdit(commandArgs).catch((err) => { console.error("❌ Error:", err); process.exit(1); });
501
+ break;
502
+ case "delete":
503
+ handleDelete(commandArgs).catch((err) => { console.error("❌ Error:", err); process.exit(1); });
504
+ break;
505
+ case "-h":
506
+ case "--help":
507
+ case undefined:
508
+ printHelp();
509
+ break;
510
+ default:
511
+ console.error(`Unknown command: ${command}`);
512
+ printHelp();
513
+ process.exit(1);
514
+ }