@blinkdotnew/cli 0.2.5 → 0.2.7

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 CHANGED
@@ -1305,20 +1305,26 @@ async function getPersonId(agentId) {
1305
1305
  return id;
1306
1306
  }
1307
1307
  function registerLinkedInCommands(program2) {
1308
- const li = program2.command("linkedin").description("LinkedIn connector \u2014 post content, manage comments, and view your profile").addHelpText("after", `
1308
+ const li = program2.command("linkedin").description("LinkedIn connector \u2014 publish posts and manage your profile").addHelpText("after", `
1309
1309
  LinkedIn must be linked to your agent via the Integrations tab at blink.new/claw.
1310
1310
  Agent ID defaults to BLINK_AGENT_ID (automatically set on Claw Fly machines).
1311
1311
 
1312
+ What works today (w_member_social scope):
1313
+ \u2705 blink linkedin me Show your LinkedIn profile
1314
+ \u2705 blink linkedin post "Excited to announce..." Publish a text post
1315
+ \u2705 blink linkedin delete <postUrn> Delete one of your posts
1316
+
1317
+ Not yet available (requires LinkedIn Partner API approval):
1318
+ \u2717 Reading posts, comments, likes (needs r_member_social \u2014 restricted scope)
1319
+ \u2717 Adding comments (needs Community Management API)
1320
+
1312
1321
  Examples:
1313
- $ blink linkedin me Show your LinkedIn profile
1314
- $ blink linkedin posts List your 10 most recent posts
1315
- $ blink linkedin post "Excited to announce..." Publish a text post
1316
- $ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
1317
- $ blink linkedin comment "urn:li:ugcPost:123" "Great post!" Add a comment
1318
- $ blink linkedin like "urn:li:ugcPost:123" Like a post
1319
- $ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
1322
+ $ blink linkedin me
1323
+ $ blink linkedin post "Our product just launched!"
1324
+ $ blink linkedin post "Team update" --visibility CONNECTIONS
1325
+ $ blink linkedin delete "urn:li:ugcPost:1234567890"
1320
1326
  `);
1321
- li.command("me").description("Show your LinkedIn profile (name, ID, vanity URL)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1327
+ li.command("me").description("Show your LinkedIn profile (name, ID, email)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1322
1328
  Examples:
1323
1329
  $ blink linkedin me
1324
1330
  $ blink linkedin me --json
@@ -1338,176 +1344,58 @@ Examples:
1338
1344
  if (name) console.log(` ${chalk7.dim("Name:")} ${name}`);
1339
1345
  if (data?.email) console.log(` ${chalk7.dim("Email:")} ${data.email}`);
1340
1346
  });
1341
- li.command("posts").description("List your most recent LinkedIn posts (last 10)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").option("--limit <n>", "Number of posts to fetch (default: 10)", "10").addHelpText("after", `
1342
- Examples:
1343
- $ blink linkedin posts
1344
- $ blink linkedin posts --limit 5
1345
- $ blink linkedin posts --json | jq '.[].id'
1346
- `).action(async (opts) => {
1347
- requireToken();
1348
- const agentId = requireAgentId(opts.agent);
1349
- const personId = await withSpinner(
1350
- "Resolving your LinkedIn identity...",
1351
- () => getPersonId(agentId)
1352
- );
1353
- const urn = encodeURIComponent(`urn:li:person:${personId}`);
1354
- const data = await withSpinner(
1355
- "Fetching posts...",
1356
- () => liExec(
1357
- `ugcPosts?q=authors&authors=List(${urn})&sortBy=LAST_MODIFIED&count=${opts.limit}`,
1358
- "GET",
1359
- {},
1360
- agentId
1361
- )
1362
- );
1363
- const posts = data?.elements ?? (Array.isArray(data) ? data : []);
1364
- if (isJsonMode()) return printJson(posts);
1365
- if (!posts.length) {
1366
- console.log(chalk7.dim("No posts found."));
1367
- return;
1368
- }
1369
- for (const post of posts) {
1370
- const id = post.id ?? "\u2014";
1371
- const content = post.specificContent;
1372
- const share = content?.["com.linkedin.ugc.ShareContent"];
1373
- const commentary = share?.shareCommentary;
1374
- const text = commentary?.text ?? post.text?.text ?? "(no text)";
1375
- const preview = text.length > 120 ? text.slice(0, 120) + "\u2026" : text;
1376
- console.log(chalk7.bold(id));
1377
- console.log(` ${chalk7.dim(preview)}`);
1378
- console.log();
1379
- }
1380
- console.log(chalk7.dim(`${posts.length} post(s)`));
1381
- });
1382
- li.command("post <text>").description("Publish a text post to LinkedIn").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").option("--visibility <vis>", "Post visibility: PUBLIC | CONNECTIONS (default: PUBLIC)", "PUBLIC").addHelpText("after", `
1347
+ li.command("post <text>").description("Publish a text post to LinkedIn").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").option("--visibility <vis>", "PUBLIC | CONNECTIONS (default: PUBLIC)", "PUBLIC").addHelpText("after", `
1383
1348
  Examples:
1384
1349
  $ blink linkedin post "Excited to share our latest update!"
1385
- $ blink linkedin post "Internal update" --visibility CONNECTIONS
1350
+ $ blink linkedin post "Internal team update" --visibility CONNECTIONS
1386
1351
  $ blink linkedin post "Hello LinkedIn" --json
1387
1352
  `).action(async (text, opts) => {
1388
- requireToken();
1389
- const agentId = requireAgentId(opts.agent);
1390
- const data = await withSpinner(
1391
- "Publishing post...",
1392
- () => liExec("/ugcPosts", "POST", { text, visibility: opts.visibility }, agentId)
1393
- );
1394
- if (isJsonMode()) return printJson(data);
1395
- console.log(chalk7.green("\u2713 Post published"));
1396
- if (data?.id) console.log(chalk7.dim(` URN: ${data.id}`));
1397
- });
1398
- li.command("comments <postUrn>").description("Read comments on a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1399
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
1400
- Use "blink linkedin posts" to find your post URNs.
1401
-
1402
- Examples:
1403
- $ blink linkedin comments "urn:li:ugcPost:1234567890"
1404
- $ blink linkedin comments "urn:li:ugcPost:1234567890" --json
1405
- `).action(async (postUrn, opts) => {
1406
- requireToken();
1407
- const agentId = requireAgentId(opts.agent);
1408
- const encoded = encodeURIComponent(postUrn);
1409
- const data = await withSpinner(
1410
- "Fetching comments...",
1411
- () => liExec(`rest/socialActions/${encoded}/comments`, "GET", {}, agentId)
1412
- );
1413
- const comments = data?.elements ?? (Array.isArray(data) ? data : []);
1414
- if (isJsonMode()) return printJson(comments);
1415
- if (!comments.length) {
1416
- console.log(chalk7.dim("No comments."));
1417
- return;
1418
- }
1419
- for (const c of comments) {
1420
- const author = c.actor ?? "\u2014";
1421
- const msg = c.message;
1422
- const text = msg?.text ?? "(no text)";
1423
- console.log(chalk7.bold(author));
1424
- console.log(` ${text}`);
1425
- console.log();
1426
- }
1427
- console.log(chalk7.dim(`${comments.length} comment(s)`));
1428
- });
1429
- li.command("comment <postUrn> <text>").description("Add a comment to a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1430
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
1431
-
1432
- Examples:
1433
- $ blink linkedin comment "urn:li:ugcPost:1234567890" "Great post!"
1434
- $ blink linkedin comment "urn:li:ugcPost:1234567890" "Thanks for sharing" --json
1435
- `).action(async (postUrn, text, opts) => {
1436
1353
  requireToken();
1437
1354
  const agentId = requireAgentId(opts.agent);
1438
1355
  const personId = await withSpinner(
1439
- "Resolving your LinkedIn identity...",
1356
+ "Resolving LinkedIn identity...",
1440
1357
  () => getPersonId(agentId)
1441
1358
  );
1442
- const actor = `urn:li:person:${personId}`;
1443
- const encoded = encodeURIComponent(postUrn);
1359
+ const visibilityMap = {
1360
+ PUBLIC: "PUBLIC",
1361
+ CONNECTIONS: "CONNECTIONS_ONLY"
1362
+ };
1444
1363
  const data = await withSpinner(
1445
- "Adding comment...",
1446
- () => liExec(
1447
- `rest/socialActions/${encoded}/comments`,
1448
- "POST",
1449
- { actor, object: postUrn, message: { text } },
1450
- agentId
1451
- )
1364
+ "Publishing post...",
1365
+ () => liExec("ugcPosts", "POST", {
1366
+ author: `urn:li:person:${personId}`,
1367
+ lifecycleState: "PUBLISHED",
1368
+ specificContent: {
1369
+ "com.linkedin.ugc.ShareContent": {
1370
+ shareCommentary: { text },
1371
+ shareMediaCategory: "NONE"
1372
+ }
1373
+ },
1374
+ visibility: {
1375
+ "com.linkedin.ugc.MemberNetworkVisibility": visibilityMap[opts.visibility] ?? "PUBLIC"
1376
+ }
1377
+ }, agentId)
1452
1378
  );
1453
1379
  if (isJsonMode()) return printJson(data);
1454
- console.log(chalk7.green("\u2713 Comment added"));
1455
- if (data?.id) console.log(chalk7.dim(` ID: ${data.id}`));
1380
+ console.log(chalk7.green("\u2713 Post published"));
1381
+ if (data?.id) console.log(chalk7.dim(` URN: ${data.id}`));
1456
1382
  });
1457
- li.command("like <postUrn>").description("Like a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1458
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
1383
+ li.command("delete <postUrn>").description("Delete one of your LinkedIn posts").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1384
+ <postUrn> is the LinkedIn post URN returned when the post was created.
1385
+ e.g. urn:li:ugcPost:1234567890
1459
1386
 
1460
1387
  Examples:
1461
- $ blink linkedin like "urn:li:ugcPost:1234567890"
1462
- $ blink linkedin like "urn:li:ugcPost:1234567890" --json
1388
+ $ blink linkedin delete "urn:li:ugcPost:1234567890"
1463
1389
  `).action(async (postUrn, opts) => {
1464
1390
  requireToken();
1465
1391
  const agentId = requireAgentId(opts.agent);
1466
- const personId = await withSpinner(
1467
- "Resolving your LinkedIn identity...",
1468
- () => getPersonId(agentId)
1469
- );
1470
- const actor = `urn:li:person:${personId}`;
1471
1392
  const encoded = encodeURIComponent(postUrn);
1472
- const data = await withSpinner(
1473
- "Liking post...",
1474
- () => liExec(
1475
- `rest/socialActions/${encoded}/likes`,
1476
- "POST",
1477
- { actor, object: postUrn },
1478
- agentId
1479
- )
1480
- );
1481
- if (isJsonMode()) return printJson(data);
1482
- console.log(chalk7.green("\u2713 Post liked"));
1483
- });
1484
- li.command("unlike <postUrn>").description("Unlike a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
1485
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
1486
-
1487
- Examples:
1488
- $ blink linkedin unlike "urn:li:ugcPost:1234567890"
1489
- $ blink linkedin unlike "urn:li:ugcPost:1234567890" --json
1490
- `).action(async (postUrn, opts) => {
1491
- requireToken();
1492
- const agentId = requireAgentId(opts.agent);
1493
- const personId = await withSpinner(
1494
- "Resolving your LinkedIn identity...",
1495
- () => getPersonId(agentId)
1496
- );
1497
- const actor = `urn:li:person:${personId}`;
1498
- const encodedPost = encodeURIComponent(postUrn);
1499
- const encodedActor = encodeURIComponent(actor);
1500
- const data = await withSpinner(
1501
- "Unliking post...",
1502
- () => liExec(
1503
- `rest/socialActions/${encodedPost}/likes/${encodedActor}?actor=${encodedActor}`,
1504
- "DELETE",
1505
- {},
1506
- agentId
1507
- )
1393
+ await withSpinner(
1394
+ "Deleting post...",
1395
+ () => liExec(`ugcPosts/${encoded}`, "DELETE", {}, agentId)
1508
1396
  );
1509
- if (isJsonMode()) return printJson(data);
1510
- console.log(chalk7.green("\u2713 Post unliked"));
1397
+ if (isJsonMode()) return printJson({ deleted: true, urn: postUrn });
1398
+ console.log(chalk7.green("\u2713 Post deleted"));
1511
1399
  });
1512
1400
  }
1513
1401
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Blink platform CLI — deploy apps, manage databases, generate AI content",
5
5
  "bin": {
6
6
  "blink": "dist/cli.js"
@@ -1,8 +1,8 @@
1
1
  import { Command } from 'commander'
2
- import { resourcesRequest } from '../lib/api-resources.js'
3
2
  import { requireToken } from '../lib/auth.js'
4
3
  import { requireAgentId } from '../lib/agent.js'
5
4
  import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
5
+ import { resourcesRequest } from '../lib/api-resources.js'
6
6
  import chalk from 'chalk'
7
7
 
8
8
  const NOT_LINKED = 'LinkedIn not linked. Link it in the Agent Integrations tab at blink.new/claw'
@@ -22,7 +22,6 @@ async function liExec(
22
22
  }
23
23
 
24
24
  async function getPersonId(agentId: string): Promise<string> {
25
- // Use OpenID Connect userinfo — more reliable than deprecated /v2/me
26
25
  const data = await liExec('v2/userinfo', 'GET', {}, agentId)
27
26
  const id = data?.sub ?? data?.id
28
27
  if (!id) throw new Error('Could not resolve LinkedIn person ID')
@@ -31,24 +30,30 @@ async function getPersonId(agentId: string): Promise<string> {
31
30
 
32
31
  export function registerLinkedInCommands(program: Command) {
33
32
  const li = program.command('linkedin')
34
- .description('LinkedIn connector — post content, manage comments, and view your profile')
33
+ .description('LinkedIn connector — publish posts and manage your profile')
35
34
  .addHelpText('after', `
36
35
  LinkedIn must be linked to your agent via the Integrations tab at blink.new/claw.
37
36
  Agent ID defaults to BLINK_AGENT_ID (automatically set on Claw Fly machines).
38
37
 
38
+ What works today (w_member_social scope):
39
+ ✅ blink linkedin me Show your LinkedIn profile
40
+ ✅ blink linkedin post "Excited to announce..." Publish a text post
41
+ ✅ blink linkedin delete <postUrn> Delete one of your posts
42
+
43
+ Not yet available (requires LinkedIn Partner API approval):
44
+ ✗ Reading posts, comments, likes (needs r_member_social — restricted scope)
45
+ ✗ Adding comments (needs Community Management API)
46
+
39
47
  Examples:
40
- $ blink linkedin me Show your LinkedIn profile
41
- $ blink linkedin posts List your 10 most recent posts
42
- $ blink linkedin post "Excited to announce..." Publish a text post
43
- $ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
44
- $ blink linkedin comment "urn:li:ugcPost:123" "Great post!" Add a comment
45
- $ blink linkedin like "urn:li:ugcPost:123" Like a post
46
- $ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
48
+ $ blink linkedin me
49
+ $ blink linkedin post "Our product just launched!"
50
+ $ blink linkedin post "Team update" --visibility CONNECTIONS
51
+ $ blink linkedin delete "urn:li:ugcPost:1234567890"
47
52
  `)
48
53
 
49
54
  // blink linkedin me
50
55
  li.command('me')
51
- .description('Show your LinkedIn profile (name, ID, vanity URL)')
56
+ .description('Show your LinkedIn profile (name, ID, email)')
52
57
  .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
53
58
  .addHelpText('after', `
54
59
  Examples:
@@ -59,7 +64,6 @@ Examples:
59
64
  .action(async (opts) => {
60
65
  requireToken()
61
66
  const agentId = requireAgentId(opts.agent)
62
- // Use OpenID Connect userinfo endpoint — more reliable than /v2/me
63
67
  const data = await withSpinner('Fetching LinkedIn profile...', () =>
64
68
  liExec('v2/userinfo', 'GET', {}, agentId)
65
69
  )
@@ -67,202 +71,71 @@ Examples:
67
71
  const name = data?.name ?? [data?.given_name, data?.family_name].filter(Boolean).join(' ')
68
72
  const personId = data?.sub ?? data?.id
69
73
  console.log(chalk.bold('LinkedIn Profile'))
70
- if (personId) console.log(` ${chalk.dim('ID:')} ${personId}`)
71
- if (name) console.log(` ${chalk.dim('Name:')} ${name}`)
74
+ if (personId) console.log(` ${chalk.dim('ID:')} ${personId}`)
75
+ if (name) console.log(` ${chalk.dim('Name:')} ${name}`)
72
76
  if (data?.email) console.log(` ${chalk.dim('Email:')} ${data.email}`)
73
77
  })
74
78
 
75
- // blink linkedin posts
76
- li.command('posts')
77
- .description('List your most recent LinkedIn posts (last 10)')
78
- .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
79
- .option('--limit <n>', 'Number of posts to fetch (default: 10)', '10')
80
- .addHelpText('after', `
81
- Examples:
82
- $ blink linkedin posts
83
- $ blink linkedin posts --limit 5
84
- $ blink linkedin posts --json | jq '.[].id'
85
- `)
86
- .action(async (opts) => {
87
- requireToken()
88
- const agentId = requireAgentId(opts.agent)
89
- const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
90
- getPersonId(agentId)
91
- )
92
- const urn = encodeURIComponent(`urn:li:person:${personId}`)
93
- const data = await withSpinner('Fetching posts...', () =>
94
- liExec(
95
- `ugcPosts?q=authors&authors=List(${urn})&sortBy=LAST_MODIFIED&count=${opts.limit}`,
96
- 'GET',
97
- {},
98
- agentId
99
- )
100
- )
101
- const posts: Array<Record<string, unknown>> = data?.elements ?? (Array.isArray(data) ? data : [])
102
- if (isJsonMode()) return printJson(posts)
103
- if (!posts.length) { console.log(chalk.dim('No posts found.')); return }
104
- for (const post of posts) {
105
- const id = (post.id ?? '—') as string
106
- const content = post.specificContent as Record<string, unknown> | undefined
107
- const share = content?.['com.linkedin.ugc.ShareContent'] as Record<string, unknown> | undefined
108
- const commentary = share?.shareCommentary as Record<string, unknown> | undefined
109
- const text = (commentary?.text ?? (post.text as Record<string, unknown>)?.text ?? '(no text)') as string
110
- const preview = text.length > 120 ? text.slice(0, 120) + '…' : text
111
- console.log(chalk.bold(id))
112
- console.log(` ${chalk.dim(preview)}`)
113
- console.log()
114
- }
115
- console.log(chalk.dim(`${posts.length} post(s)`))
116
- })
117
-
118
79
  // blink linkedin post "text"
119
80
  li.command('post <text>')
120
81
  .description('Publish a text post to LinkedIn')
121
82
  .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
122
- .option('--visibility <vis>', 'Post visibility: PUBLIC | CONNECTIONS (default: PUBLIC)', 'PUBLIC')
83
+ .option('--visibility <vis>', 'PUBLIC | CONNECTIONS (default: PUBLIC)', 'PUBLIC')
123
84
  .addHelpText('after', `
124
85
  Examples:
125
86
  $ blink linkedin post "Excited to share our latest update!"
126
- $ blink linkedin post "Internal update" --visibility CONNECTIONS
87
+ $ blink linkedin post "Internal team update" --visibility CONNECTIONS
127
88
  $ blink linkedin post "Hello LinkedIn" --json
128
89
  `)
129
90
  .action(async (text: string, opts) => {
130
91
  requireToken()
131
92
  const agentId = requireAgentId(opts.agent)
93
+ const personId = await withSpinner('Resolving LinkedIn identity...', () =>
94
+ getPersonId(agentId)
95
+ )
96
+ const visibilityMap: Record<string, string> = {
97
+ PUBLIC: 'PUBLIC',
98
+ CONNECTIONS: 'CONNECTIONS_ONLY',
99
+ }
132
100
  const data = await withSpinner('Publishing post...', () =>
133
- liExec('/ugcPosts', 'POST', { text, visibility: opts.visibility }, agentId)
101
+ liExec('ugcPosts', 'POST', {
102
+ author: `urn:li:person:${personId}`,
103
+ lifecycleState: 'PUBLISHED',
104
+ specificContent: {
105
+ 'com.linkedin.ugc.ShareContent': {
106
+ shareCommentary: { text },
107
+ shareMediaCategory: 'NONE',
108
+ },
109
+ },
110
+ visibility: {
111
+ 'com.linkedin.ugc.MemberNetworkVisibility': visibilityMap[opts.visibility] ?? 'PUBLIC',
112
+ },
113
+ }, agentId)
134
114
  )
135
115
  if (isJsonMode()) return printJson(data)
136
116
  console.log(chalk.green('✓ Post published'))
137
117
  if (data?.id) console.log(chalk.dim(` URN: ${data.id}`))
138
118
  })
139
119
 
140
- // blink linkedin comments <postUrn>
141
- li.command('comments <postUrn>')
142
- .description('Read comments on a LinkedIn post')
120
+ // blink linkedin delete <postUrn>
121
+ li.command('delete <postUrn>')
122
+ .description('Delete one of your LinkedIn posts')
143
123
  .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
144
124
  .addHelpText('after', `
145
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
146
- Use "blink linkedin posts" to find your post URNs.
125
+ <postUrn> is the LinkedIn post URN returned when the post was created.
126
+ e.g. urn:li:ugcPost:1234567890
147
127
 
148
128
  Examples:
149
- $ blink linkedin comments "urn:li:ugcPost:1234567890"
150
- $ blink linkedin comments "urn:li:ugcPost:1234567890" --json
129
+ $ blink linkedin delete "urn:li:ugcPost:1234567890"
151
130
  `)
152
131
  .action(async (postUrn: string, opts) => {
153
132
  requireToken()
154
133
  const agentId = requireAgentId(opts.agent)
155
134
  const encoded = encodeURIComponent(postUrn)
156
- const data = await withSpinner('Fetching comments...', () =>
157
- liExec(`rest/socialActions/${encoded}/comments`, 'GET', {}, agentId)
158
- )
159
- const comments: Array<Record<string, unknown>> = data?.elements ?? (Array.isArray(data) ? data : [])
160
- if (isJsonMode()) return printJson(comments)
161
- if (!comments.length) { console.log(chalk.dim('No comments.')); return }
162
- for (const c of comments) {
163
- const author = (c.actor ?? '—') as string
164
- const msg = c.message as Record<string, unknown> | undefined
165
- const text = (msg?.text ?? '(no text)') as string
166
- console.log(chalk.bold(author))
167
- console.log(` ${text}`)
168
- console.log()
169
- }
170
- console.log(chalk.dim(`${comments.length} comment(s)`))
171
- })
172
-
173
- // blink linkedin comment <postUrn> "text"
174
- li.command('comment <postUrn> <text>')
175
- .description('Add a comment to a LinkedIn post')
176
- .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
177
- .addHelpText('after', `
178
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
179
-
180
- Examples:
181
- $ blink linkedin comment "urn:li:ugcPost:1234567890" "Great post!"
182
- $ blink linkedin comment "urn:li:ugcPost:1234567890" "Thanks for sharing" --json
183
- `)
184
- .action(async (postUrn: string, text: string, opts) => {
185
- requireToken()
186
- const agentId = requireAgentId(opts.agent)
187
- const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
188
- getPersonId(agentId)
135
+ await withSpinner('Deleting post...', () =>
136
+ liExec(`ugcPosts/${encoded}`, 'DELETE', {}, agentId)
189
137
  )
190
- const actor = `urn:li:person:${personId}`
191
- const encoded = encodeURIComponent(postUrn)
192
- const data = await withSpinner('Adding comment...', () =>
193
- liExec(
194
- `rest/socialActions/${encoded}/comments`,
195
- 'POST',
196
- { actor, object: postUrn, message: { text } },
197
- agentId
198
- )
199
- )
200
- if (isJsonMode()) return printJson(data)
201
- console.log(chalk.green('✓ Comment added'))
202
- if (data?.id) console.log(chalk.dim(` ID: ${data.id}`))
203
- })
204
-
205
- // blink linkedin like <postUrn>
206
- li.command('like <postUrn>')
207
- .description('Like a LinkedIn post')
208
- .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
209
- .addHelpText('after', `
210
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
211
-
212
- Examples:
213
- $ blink linkedin like "urn:li:ugcPost:1234567890"
214
- $ blink linkedin like "urn:li:ugcPost:1234567890" --json
215
- `)
216
- .action(async (postUrn: string, opts) => {
217
- requireToken()
218
- const agentId = requireAgentId(opts.agent)
219
- const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
220
- getPersonId(agentId)
221
- )
222
- const actor = `urn:li:person:${personId}`
223
- const encoded = encodeURIComponent(postUrn)
224
- const data = await withSpinner('Liking post...', () =>
225
- liExec(
226
- `rest/socialActions/${encoded}/likes`,
227
- 'POST',
228
- { actor, object: postUrn },
229
- agentId
230
- )
231
- )
232
- if (isJsonMode()) return printJson(data)
233
- console.log(chalk.green('✓ Post liked'))
234
- })
235
-
236
- // blink linkedin unlike <postUrn>
237
- li.command('unlike <postUrn>')
238
- .description('Unlike a LinkedIn post')
239
- .option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
240
- .addHelpText('after', `
241
- <postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
242
-
243
- Examples:
244
- $ blink linkedin unlike "urn:li:ugcPost:1234567890"
245
- $ blink linkedin unlike "urn:li:ugcPost:1234567890" --json
246
- `)
247
- .action(async (postUrn: string, opts) => {
248
- requireToken()
249
- const agentId = requireAgentId(opts.agent)
250
- const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
251
- getPersonId(agentId)
252
- )
253
- const actor = `urn:li:person:${personId}`
254
- const encodedPost = encodeURIComponent(postUrn)
255
- const encodedActor = encodeURIComponent(actor)
256
- // LinkedIn DELETE likes requires: DELETE /rest/socialActions/{postUrn}/likes/{actorUrn}?actor={actorUrn}
257
- const data = await withSpinner('Unliking post...', () =>
258
- liExec(
259
- `rest/socialActions/${encodedPost}/likes/${encodedActor}?actor=${encodedActor}`,
260
- 'DELETE',
261
- {},
262
- agentId
263
- )
264
- )
265
- if (isJsonMode()) return printJson(data)
266
- console.log(chalk.green('✓ Post unliked'))
138
+ if (isJsonMode()) return printJson({ deleted: true, urn: postUrn })
139
+ console.log(chalk.green('✓ Post deleted'))
267
140
  })
268
141
  }