@blinkdotnew/cli 0.2.3 → 0.2.4
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 +276 -41
- package/package.json +1 -1
- package/src/cli.ts +12 -0
- package/src/commands/connector.ts +1 -1
- package/src/commands/linkedin.ts +265 -0
package/dist/cli.js
CHANGED
|
@@ -1067,7 +1067,7 @@ Once linked, use \`blink connector exec\` to call their APIs without managing to
|
|
|
1067
1067
|
|
|
1068
1068
|
Connect accounts at: https://blink.new/settings?tab=connectors
|
|
1069
1069
|
|
|
1070
|
-
Run \`blink connector providers\` to see all
|
|
1070
|
+
Run \`blink connector providers\` to see all 38 supported providers.
|
|
1071
1071
|
|
|
1072
1072
|
Quick examples:
|
|
1073
1073
|
$ blink connector exec github /user/repos GET
|
|
@@ -1198,6 +1198,230 @@ Provider base URLs used:
|
|
|
1198
1198
|
});
|
|
1199
1199
|
}
|
|
1200
1200
|
|
|
1201
|
+
// src/commands/linkedin.ts
|
|
1202
|
+
init_agent();
|
|
1203
|
+
import chalk7 from "chalk";
|
|
1204
|
+
var NOT_LINKED = "LinkedIn not linked. Link it in the Agent Integrations tab at blink.new/claw";
|
|
1205
|
+
async function liExec(method, httpMethod, params, agentId) {
|
|
1206
|
+
const result = await resourcesRequest("/v1/connectors/linkedin/execute", {
|
|
1207
|
+
body: { method, http_method: httpMethod, params },
|
|
1208
|
+
headers: { "x-blink-agent-id": agentId }
|
|
1209
|
+
});
|
|
1210
|
+
if (!result?.success) throw new Error(result?.error ?? NOT_LINKED);
|
|
1211
|
+
return result.data;
|
|
1212
|
+
}
|
|
1213
|
+
async function getPersonId(agentId) {
|
|
1214
|
+
const data = await liExec("/me", "GET", {}, agentId);
|
|
1215
|
+
const id = data?.id;
|
|
1216
|
+
if (!id) throw new Error("Could not resolve LinkedIn person ID from /me response");
|
|
1217
|
+
return id;
|
|
1218
|
+
}
|
|
1219
|
+
function registerLinkedInCommands(program2) {
|
|
1220
|
+
const li = program2.command("linkedin").description("LinkedIn connector \u2014 post content, manage comments, and view your profile").addHelpText("after", `
|
|
1221
|
+
LinkedIn must be linked to your agent via the Integrations tab at blink.new/claw.
|
|
1222
|
+
Agent ID defaults to BLINK_AGENT_ID (automatically set on Claw Fly machines).
|
|
1223
|
+
|
|
1224
|
+
Examples:
|
|
1225
|
+
$ blink linkedin me Show your LinkedIn profile
|
|
1226
|
+
$ blink linkedin posts List your 10 most recent posts
|
|
1227
|
+
$ blink linkedin post "Excited to announce..." Publish a text post
|
|
1228
|
+
$ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
|
|
1229
|
+
$ blink linkedin comment "urn:li:ugcPost:123" "Great post!" Add a comment
|
|
1230
|
+
$ blink linkedin like "urn:li:ugcPost:123" Like a post
|
|
1231
|
+
$ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
|
|
1232
|
+
`);
|
|
1233
|
+
li.command("me").description("Show your LinkedIn profile (name, ID, vanity URL)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
|
|
1234
|
+
Examples:
|
|
1235
|
+
$ blink linkedin me
|
|
1236
|
+
$ blink linkedin me --json
|
|
1237
|
+
$ blink linkedin me --agent clw_xxx
|
|
1238
|
+
`).action(async (opts) => {
|
|
1239
|
+
requireToken();
|
|
1240
|
+
const agentId = requireAgentId(opts.agent);
|
|
1241
|
+
const data = await withSpinner(
|
|
1242
|
+
"Fetching LinkedIn profile...",
|
|
1243
|
+
() => liExec("/me", "GET", {}, agentId)
|
|
1244
|
+
);
|
|
1245
|
+
if (isJsonMode()) return printJson(data);
|
|
1246
|
+
const name = [data?.localizedFirstName, data?.localizedLastName].filter(Boolean).join(" ");
|
|
1247
|
+
console.log(chalk7.bold("LinkedIn Profile"));
|
|
1248
|
+
console.log(` ${chalk7.dim("ID:")} ${data?.id ?? "\u2014"}`);
|
|
1249
|
+
if (name) console.log(` ${chalk7.dim("Name:")} ${name}`);
|
|
1250
|
+
if (data?.vanityName) console.log(` ${chalk7.dim("URL:")} https://linkedin.com/in/${data.vanityName}`);
|
|
1251
|
+
});
|
|
1252
|
+
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", `
|
|
1253
|
+
Examples:
|
|
1254
|
+
$ blink linkedin posts
|
|
1255
|
+
$ blink linkedin posts --limit 5
|
|
1256
|
+
$ blink linkedin posts --json | jq '.[].id'
|
|
1257
|
+
`).action(async (opts) => {
|
|
1258
|
+
requireToken();
|
|
1259
|
+
const agentId = requireAgentId(opts.agent);
|
|
1260
|
+
const personId = await withSpinner(
|
|
1261
|
+
"Resolving your LinkedIn identity...",
|
|
1262
|
+
() => getPersonId(agentId)
|
|
1263
|
+
);
|
|
1264
|
+
const urn = encodeURIComponent(`urn:li:person:${personId}`);
|
|
1265
|
+
const data = await withSpinner(
|
|
1266
|
+
"Fetching posts...",
|
|
1267
|
+
() => liExec(
|
|
1268
|
+
`/ugcPosts?q=authors&authors=List(${urn})&sortBy=LAST_MODIFIED&count=${opts.limit}`,
|
|
1269
|
+
"GET",
|
|
1270
|
+
{},
|
|
1271
|
+
agentId
|
|
1272
|
+
)
|
|
1273
|
+
);
|
|
1274
|
+
const posts = data?.elements ?? (Array.isArray(data) ? data : []);
|
|
1275
|
+
if (isJsonMode()) return printJson(posts);
|
|
1276
|
+
if (!posts.length) {
|
|
1277
|
+
console.log(chalk7.dim("No posts found."));
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
for (const post of posts) {
|
|
1281
|
+
const id = post.id ?? "\u2014";
|
|
1282
|
+
const content = post.specificContent;
|
|
1283
|
+
const share = content?.["com.linkedin.ugc.ShareContent"];
|
|
1284
|
+
const commentary = share?.shareCommentary;
|
|
1285
|
+
const text = commentary?.text ?? post.text?.text ?? "(no text)";
|
|
1286
|
+
const preview = text.length > 120 ? text.slice(0, 120) + "\u2026" : text;
|
|
1287
|
+
console.log(chalk7.bold(id));
|
|
1288
|
+
console.log(` ${chalk7.dim(preview)}`);
|
|
1289
|
+
console.log();
|
|
1290
|
+
}
|
|
1291
|
+
console.log(chalk7.dim(`${posts.length} post(s)`));
|
|
1292
|
+
});
|
|
1293
|
+
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", `
|
|
1294
|
+
Examples:
|
|
1295
|
+
$ blink linkedin post "Excited to share our latest update!"
|
|
1296
|
+
$ blink linkedin post "Internal update" --visibility CONNECTIONS
|
|
1297
|
+
$ blink linkedin post "Hello LinkedIn" --json
|
|
1298
|
+
`).action(async (text, opts) => {
|
|
1299
|
+
requireToken();
|
|
1300
|
+
const agentId = requireAgentId(opts.agent);
|
|
1301
|
+
const data = await withSpinner(
|
|
1302
|
+
"Publishing post...",
|
|
1303
|
+
() => liExec("/ugcPosts", "POST", { text, visibility: opts.visibility }, agentId)
|
|
1304
|
+
);
|
|
1305
|
+
if (isJsonMode()) return printJson(data);
|
|
1306
|
+
console.log(chalk7.green("\u2713 Post published"));
|
|
1307
|
+
if (data?.id) console.log(chalk7.dim(` URN: ${data.id}`));
|
|
1308
|
+
});
|
|
1309
|
+
li.command("comments <postUrn>").description("Read comments on a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
|
|
1310
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
1311
|
+
Use "blink linkedin posts" to find your post URNs.
|
|
1312
|
+
|
|
1313
|
+
Examples:
|
|
1314
|
+
$ blink linkedin comments "urn:li:ugcPost:1234567890"
|
|
1315
|
+
$ blink linkedin comments "urn:li:ugcPost:1234567890" --json
|
|
1316
|
+
`).action(async (postUrn, opts) => {
|
|
1317
|
+
requireToken();
|
|
1318
|
+
const agentId = requireAgentId(opts.agent);
|
|
1319
|
+
const encoded = encodeURIComponent(postUrn);
|
|
1320
|
+
const data = await withSpinner(
|
|
1321
|
+
"Fetching comments...",
|
|
1322
|
+
() => liExec(`rest/socialActions/${encoded}/comments`, "GET", {}, agentId)
|
|
1323
|
+
);
|
|
1324
|
+
const comments = data?.elements ?? (Array.isArray(data) ? data : []);
|
|
1325
|
+
if (isJsonMode()) return printJson(comments);
|
|
1326
|
+
if (!comments.length) {
|
|
1327
|
+
console.log(chalk7.dim("No comments."));
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
for (const c of comments) {
|
|
1331
|
+
const author = c.actor ?? "\u2014";
|
|
1332
|
+
const msg = c.message;
|
|
1333
|
+
const text = msg?.text ?? "(no text)";
|
|
1334
|
+
console.log(chalk7.bold(author));
|
|
1335
|
+
console.log(` ${text}`);
|
|
1336
|
+
console.log();
|
|
1337
|
+
}
|
|
1338
|
+
console.log(chalk7.dim(`${comments.length} comment(s)`));
|
|
1339
|
+
});
|
|
1340
|
+
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", `
|
|
1341
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
1342
|
+
|
|
1343
|
+
Examples:
|
|
1344
|
+
$ blink linkedin comment "urn:li:ugcPost:1234567890" "Great post!"
|
|
1345
|
+
$ blink linkedin comment "urn:li:ugcPost:1234567890" "Thanks for sharing" --json
|
|
1346
|
+
`).action(async (postUrn, text, 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 actor = `urn:li:person:${personId}`;
|
|
1354
|
+
const encoded = encodeURIComponent(postUrn);
|
|
1355
|
+
const data = await withSpinner(
|
|
1356
|
+
"Adding comment...",
|
|
1357
|
+
() => liExec(
|
|
1358
|
+
`rest/socialActions/${encoded}/comments`,
|
|
1359
|
+
"POST",
|
|
1360
|
+
{ actor, object: postUrn, message: { text } },
|
|
1361
|
+
agentId
|
|
1362
|
+
)
|
|
1363
|
+
);
|
|
1364
|
+
if (isJsonMode()) return printJson(data);
|
|
1365
|
+
console.log(chalk7.green("\u2713 Comment added"));
|
|
1366
|
+
if (data?.id) console.log(chalk7.dim(` ID: ${data.id}`));
|
|
1367
|
+
});
|
|
1368
|
+
li.command("like <postUrn>").description("Like a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
|
|
1369
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
1370
|
+
|
|
1371
|
+
Examples:
|
|
1372
|
+
$ blink linkedin like "urn:li:ugcPost:1234567890"
|
|
1373
|
+
$ blink linkedin like "urn:li:ugcPost:1234567890" --json
|
|
1374
|
+
`).action(async (postUrn, opts) => {
|
|
1375
|
+
requireToken();
|
|
1376
|
+
const agentId = requireAgentId(opts.agent);
|
|
1377
|
+
const personId = await withSpinner(
|
|
1378
|
+
"Resolving your LinkedIn identity...",
|
|
1379
|
+
() => getPersonId(agentId)
|
|
1380
|
+
);
|
|
1381
|
+
const actor = `urn:li:person:${personId}`;
|
|
1382
|
+
const encoded = encodeURIComponent(postUrn);
|
|
1383
|
+
const data = await withSpinner(
|
|
1384
|
+
"Liking post...",
|
|
1385
|
+
() => liExec(
|
|
1386
|
+
`rest/socialActions/${encoded}/likes`,
|
|
1387
|
+
"POST",
|
|
1388
|
+
{ actor, object: postUrn },
|
|
1389
|
+
agentId
|
|
1390
|
+
)
|
|
1391
|
+
);
|
|
1392
|
+
if (isJsonMode()) return printJson(data);
|
|
1393
|
+
console.log(chalk7.green("\u2713 Post liked"));
|
|
1394
|
+
});
|
|
1395
|
+
li.command("unlike <postUrn>").description("Unlike a LinkedIn post").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID)").addHelpText("after", `
|
|
1396
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
1397
|
+
|
|
1398
|
+
Examples:
|
|
1399
|
+
$ blink linkedin unlike "urn:li:ugcPost:1234567890"
|
|
1400
|
+
$ blink linkedin unlike "urn:li:ugcPost:1234567890" --json
|
|
1401
|
+
`).action(async (postUrn, opts) => {
|
|
1402
|
+
requireToken();
|
|
1403
|
+
const agentId = requireAgentId(opts.agent);
|
|
1404
|
+
const personId = await withSpinner(
|
|
1405
|
+
"Resolving your LinkedIn identity...",
|
|
1406
|
+
() => getPersonId(agentId)
|
|
1407
|
+
);
|
|
1408
|
+
const actor = `urn:li:person:${personId}`;
|
|
1409
|
+
const encodedPost = encodeURIComponent(postUrn);
|
|
1410
|
+
const encodedActor = encodeURIComponent(actor);
|
|
1411
|
+
const data = await withSpinner(
|
|
1412
|
+
"Unliking post...",
|
|
1413
|
+
() => liExec(
|
|
1414
|
+
`rest/socialActions/${encodedPost}/likes/${encodedActor}?actor=${encodedActor}`,
|
|
1415
|
+
"DELETE",
|
|
1416
|
+
{},
|
|
1417
|
+
agentId
|
|
1418
|
+
)
|
|
1419
|
+
);
|
|
1420
|
+
if (isJsonMode()) return printJson(data);
|
|
1421
|
+
console.log(chalk7.green("\u2713 Post unliked"));
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1201
1425
|
// src/lib/api-app.ts
|
|
1202
1426
|
var BASE_URL2 = process.env.BLINK_APP_URL ?? "https://blink.new";
|
|
1203
1427
|
async function appRequest(path, opts = {}) {
|
|
@@ -1236,7 +1460,7 @@ async function appRequest(path, opts = {}) {
|
|
|
1236
1460
|
init_project();
|
|
1237
1461
|
import { readdirSync, readFileSync as readFileSync8 } from "fs";
|
|
1238
1462
|
import { join as join3, relative } from "path";
|
|
1239
|
-
import
|
|
1463
|
+
import chalk8 from "chalk";
|
|
1240
1464
|
function collectFiles(dir) {
|
|
1241
1465
|
const files = [];
|
|
1242
1466
|
function walk(current) {
|
|
@@ -1309,7 +1533,7 @@ Project resolution:
|
|
|
1309
1533
|
const production = opts.prod === true;
|
|
1310
1534
|
if (!isJsonMode()) {
|
|
1311
1535
|
console.log();
|
|
1312
|
-
console.log(
|
|
1536
|
+
console.log(chalk8.bold(" Blink Deploy"));
|
|
1313
1537
|
console.log();
|
|
1314
1538
|
}
|
|
1315
1539
|
const files = await withSpinner(`Packaging ${buildDir}...`, async () => collectFiles(buildDir));
|
|
@@ -1363,7 +1587,7 @@ Project resolution:
|
|
|
1363
1587
|
|
|
1364
1588
|
// src/commands/project.ts
|
|
1365
1589
|
init_project();
|
|
1366
|
-
import
|
|
1590
|
+
import chalk9 from "chalk";
|
|
1367
1591
|
function registerProjectCommands(program2) {
|
|
1368
1592
|
const project = program2.command("project").description("Create, list, and delete Blink projects").addHelpText("after", `
|
|
1369
1593
|
Examples:
|
|
@@ -1392,8 +1616,8 @@ After creating a project, link it to your current directory:
|
|
|
1392
1616
|
);
|
|
1393
1617
|
if (isJsonMode()) return printJson(result);
|
|
1394
1618
|
const proj = result?.project ?? result;
|
|
1395
|
-
console.log(
|
|
1396
|
-
console.log(
|
|
1619
|
+
console.log(chalk9.green("\u2713") + ` Created: ${proj.id}`);
|
|
1620
|
+
console.log(chalk9.dim(" Run `blink link " + proj.id + "` to use it"));
|
|
1397
1621
|
});
|
|
1398
1622
|
project.command("delete <project_id>").description("Delete a project").option("--yes", "Skip confirmation").action(async (projectId, opts) => {
|
|
1399
1623
|
requireToken();
|
|
@@ -1436,7 +1660,7 @@ After linking, most commands work without specifying a project_id:
|
|
|
1436
1660
|
});
|
|
1437
1661
|
}
|
|
1438
1662
|
writeProjectConfig({ projectId: id });
|
|
1439
|
-
console.log(
|
|
1663
|
+
console.log(chalk9.green("\u2713") + " Linked to " + id);
|
|
1440
1664
|
});
|
|
1441
1665
|
program2.command("unlink").description("Remove project link from current directory").action(() => {
|
|
1442
1666
|
clearProjectConfig();
|
|
@@ -1448,29 +1672,29 @@ After linking, most commands work without specifying a project_id:
|
|
|
1448
1672
|
const agentId = resolveAgentId3();
|
|
1449
1673
|
const agentSource = process.env.BLINK_AGENT_ID ? "BLINK_AGENT_ID env" : process.env.BLINK_ACTIVE_AGENT ? "BLINK_ACTIVE_AGENT env" : null;
|
|
1450
1674
|
if (agentId) {
|
|
1451
|
-
console.log(
|
|
1675
|
+
console.log(chalk9.bold("Agent ") + agentId + chalk9.dim(" (" + agentSource + ")"));
|
|
1452
1676
|
} else {
|
|
1453
|
-
console.log(
|
|
1677
|
+
console.log(chalk9.dim("Agent not set (use: eval $(blink agent use clw_xxx --export))"));
|
|
1454
1678
|
}
|
|
1455
1679
|
if (config) {
|
|
1456
1680
|
const projectSource = process.env.BLINK_ACTIVE_PROJECT ? "BLINK_ACTIVE_PROJECT env" : ".blink/project.json";
|
|
1457
|
-
console.log(
|
|
1681
|
+
console.log(chalk9.bold("Project ") + config.projectId + chalk9.dim(" (" + projectSource + ")"));
|
|
1458
1682
|
} else if (process.env.BLINK_ACTIVE_PROJECT) {
|
|
1459
|
-
console.log(
|
|
1683
|
+
console.log(chalk9.bold("Project ") + process.env.BLINK_ACTIVE_PROJECT + chalk9.dim(" (BLINK_ACTIVE_PROJECT env)"));
|
|
1460
1684
|
} else {
|
|
1461
|
-
console.log(
|
|
1685
|
+
console.log(chalk9.dim("Project not linked (use: blink link or eval $(blink use proj_xxx --export))"));
|
|
1462
1686
|
}
|
|
1463
1687
|
const authSource = process.env.BLINK_API_KEY ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
|
|
1464
1688
|
const hasProjectKey = !!process.env.BLINK_PROJECT_KEY;
|
|
1465
|
-
console.log(
|
|
1689
|
+
console.log(chalk9.bold("Auth ") + authSource);
|
|
1466
1690
|
if (hasProjectKey) {
|
|
1467
|
-
console.log(
|
|
1691
|
+
console.log(chalk9.bold("ProjKey ") + "BLINK_PROJECT_KEY env" + chalk9.dim(" (used for db/storage/rag)"));
|
|
1468
1692
|
}
|
|
1469
1693
|
});
|
|
1470
1694
|
}
|
|
1471
1695
|
|
|
1472
1696
|
// src/commands/auth.ts
|
|
1473
|
-
import
|
|
1697
|
+
import chalk10 from "chalk";
|
|
1474
1698
|
function registerAuthCommands(program2) {
|
|
1475
1699
|
program2.command("login").description("Authenticate with your Blink API key").option("--interactive", "Prompt for API key (for headless/SSH/CI environments)").addHelpText("after", `
|
|
1476
1700
|
Get your API key at: blink.new \u2192 Settings \u2192 API Keys (starts with blnk_ak_)
|
|
@@ -1483,7 +1707,7 @@ In Blink Claw agents: BLINK_API_KEY is already set \u2014 login is not needed.
|
|
|
1483
1707
|
For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
|
|
1484
1708
|
`).action(async (opts) => {
|
|
1485
1709
|
if (process.env.BLINK_API_KEY && !opts.interactive) {
|
|
1486
|
-
console.log(
|
|
1710
|
+
console.log(chalk10.green("\u2713") + " Already authenticated via BLINK_API_KEY env var.");
|
|
1487
1711
|
return;
|
|
1488
1712
|
}
|
|
1489
1713
|
const { password } = await import("@clack/prompts");
|
|
@@ -1493,7 +1717,7 @@ For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
|
|
|
1493
1717
|
process.exit(1);
|
|
1494
1718
|
}
|
|
1495
1719
|
writeConfig({ api_key: apiKey });
|
|
1496
|
-
console.log(
|
|
1720
|
+
console.log(chalk10.green("\u2713") + " Saved to ~/.config/blink/config.toml");
|
|
1497
1721
|
});
|
|
1498
1722
|
program2.command("logout").description("Remove stored credentials").action(() => {
|
|
1499
1723
|
clearConfig();
|
|
@@ -1513,15 +1737,15 @@ For CI/GitHub Actions: set BLINK_API_KEY as a secret, skip login entirely.
|
|
|
1513
1737
|
});
|
|
1514
1738
|
}
|
|
1515
1739
|
const source = process.env.BLINK_API_KEY === token ? "BLINK_API_KEY env" : "~/.config/blink/config.toml";
|
|
1516
|
-
console.log(
|
|
1517
|
-
console.log(
|
|
1518
|
-
console.log(
|
|
1740
|
+
console.log(chalk10.green("\u2713") + " Authenticated");
|
|
1741
|
+
console.log(chalk10.bold("Key ") + token.slice(0, 20) + chalk10.dim("..."));
|
|
1742
|
+
console.log(chalk10.bold("Source ") + chalk10.dim(source));
|
|
1519
1743
|
});
|
|
1520
1744
|
}
|
|
1521
1745
|
|
|
1522
1746
|
// src/commands/agent.ts
|
|
1523
1747
|
init_agent();
|
|
1524
|
-
import
|
|
1748
|
+
import chalk11 from "chalk";
|
|
1525
1749
|
function registerAgentCommands(program2) {
|
|
1526
1750
|
const agent = program2.command("agent").description("Manage Blink Claw agents in your workspace").addHelpText("after", `
|
|
1527
1751
|
Examples:
|
|
@@ -1541,7 +1765,7 @@ Agent ID resolution for all agent/secrets commands (priority: high \u2192 low):
|
|
|
1541
1765
|
if (isJsonMode()) return printJson(result);
|
|
1542
1766
|
const agents = Array.isArray(result) ? result : result?.agents ?? [];
|
|
1543
1767
|
if (!agents.length) {
|
|
1544
|
-
console.log(
|
|
1768
|
+
console.log(chalk11.dim("No agents found. Deploy one at blink.new/claw"));
|
|
1545
1769
|
return;
|
|
1546
1770
|
}
|
|
1547
1771
|
const table = createTable(["ID", "Name", "Status", "Size", "Model"]);
|
|
@@ -1564,9 +1788,9 @@ After setting, secrets commands use this agent automatically:
|
|
|
1564
1788
|
process.stdout.write(`export BLINK_ACTIVE_AGENT=${agentId}
|
|
1565
1789
|
`);
|
|
1566
1790
|
} else {
|
|
1567
|
-
console.log(
|
|
1568
|
-
console.log(
|
|
1569
|
-
console.log(
|
|
1791
|
+
console.log(chalk11.bold("Active agent: ") + agentId);
|
|
1792
|
+
console.log(chalk11.dim(`Run: export BLINK_ACTIVE_AGENT=${agentId}`));
|
|
1793
|
+
console.log(chalk11.dim(`Or: eval $(blink agent use ${agentId} --export)`));
|
|
1570
1794
|
}
|
|
1571
1795
|
});
|
|
1572
1796
|
agent.command("status [agent_id]").description("Show details for an agent").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
|
|
@@ -1582,18 +1806,18 @@ Examples:
|
|
|
1582
1806
|
);
|
|
1583
1807
|
if (isJsonMode()) return printJson(result);
|
|
1584
1808
|
const a = result?.agent ?? result;
|
|
1585
|
-
console.log(
|
|
1586
|
-
console.log(
|
|
1587
|
-
console.log(
|
|
1588
|
-
console.log(
|
|
1589
|
-
console.log(
|
|
1590
|
-
if (a.fly_app_name) console.log(
|
|
1809
|
+
console.log(chalk11.bold("ID ") + a.id);
|
|
1810
|
+
console.log(chalk11.bold("Name ") + a.name);
|
|
1811
|
+
console.log(chalk11.bold("Status ") + a.status);
|
|
1812
|
+
console.log(chalk11.bold("Model ") + (a.model ?? "-"));
|
|
1813
|
+
console.log(chalk11.bold("Machine ") + (a.machine_size ?? "-"));
|
|
1814
|
+
if (a.fly_app_name) console.log(chalk11.bold("Fly App ") + a.fly_app_name);
|
|
1591
1815
|
});
|
|
1592
1816
|
}
|
|
1593
1817
|
|
|
1594
1818
|
// src/commands/secrets.ts
|
|
1595
1819
|
init_agent();
|
|
1596
|
-
import
|
|
1820
|
+
import chalk12 from "chalk";
|
|
1597
1821
|
function registerSecretsCommands(program2) {
|
|
1598
1822
|
const secrets = program2.command("secrets").description("Manage encrypted secrets vault for a Claw agent").addHelpText("after", `
|
|
1599
1823
|
Secrets are encrypted key-value pairs stored in the agent's vault.
|
|
@@ -1629,11 +1853,11 @@ Examples:
|
|
|
1629
1853
|
if (isJsonMode()) return printJson(result);
|
|
1630
1854
|
const keys = result?.secrets?.map((s) => s.key) ?? result?.keys ?? [];
|
|
1631
1855
|
if (!keys.length) {
|
|
1632
|
-
console.log(
|
|
1856
|
+
console.log(chalk12.dim("(no secrets set \u2014 use `blink secrets set KEY value`)"));
|
|
1633
1857
|
return;
|
|
1634
1858
|
}
|
|
1635
|
-
for (const k of keys) console.log(
|
|
1636
|
-
console.log(
|
|
1859
|
+
for (const k of keys) console.log(chalk12.bold(k));
|
|
1860
|
+
console.log(chalk12.dim(`
|
|
1637
1861
|
${keys.length} secret${keys.length === 1 ? "" : "s"} (values hidden)`));
|
|
1638
1862
|
});
|
|
1639
1863
|
secrets.command("set <key> <value>").description("Add or update a secret (stored encrypted, value never shown again)").option("--agent <id>", "Agent ID (defaults to BLINK_AGENT_ID or BLINK_ACTIVE_AGENT)").addHelpText("after", `
|
|
@@ -1656,8 +1880,8 @@ After setting, the secret is available as $KEY_NAME in agent shell commands.
|
|
|
1656
1880
|
);
|
|
1657
1881
|
const normalised = key.toUpperCase();
|
|
1658
1882
|
if (!isJsonMode()) {
|
|
1659
|
-
console.log(
|
|
1660
|
-
console.log(
|
|
1883
|
+
console.log(chalk12.green("\u2713") + ` ${normalised} saved`);
|
|
1884
|
+
console.log(chalk12.dim(" Value hidden. Use $" + normalised + " in shell commands."));
|
|
1661
1885
|
} else {
|
|
1662
1886
|
printJson({ status: "ok", key: normalised, agent_id: agentId });
|
|
1663
1887
|
}
|
|
@@ -1753,6 +1977,16 @@ Connectors (38 OAuth providers \u2014 GitHub, Notion, Slack, Stripe, Shopify, Ji
|
|
|
1753
1977
|
$ blink connector exec linear '{ viewer { id name } }' POST GraphQL (Linear)
|
|
1754
1978
|
Connect accounts at: blink.new/settings?tab=connectors
|
|
1755
1979
|
|
|
1980
|
+
LinkedIn (dedicated commands for the LinkedIn connector):
|
|
1981
|
+
$ blink linkedin me Show your LinkedIn profile
|
|
1982
|
+
$ blink linkedin posts List your 10 most recent posts
|
|
1983
|
+
$ blink linkedin post "Hello LinkedIn!" Publish a text post
|
|
1984
|
+
$ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
|
|
1985
|
+
$ blink linkedin comment "urn:li:ugcPost:123" "Nice!" Add a comment
|
|
1986
|
+
$ blink linkedin like "urn:li:ugcPost:123" Like a post
|
|
1987
|
+
$ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
|
|
1988
|
+
Link LinkedIn at: blink.new/claw (Agent Integrations tab)
|
|
1989
|
+
|
|
1756
1990
|
Agents (Claw \u2014 zero config on Fly machines, BLINK_AGENT_ID is already set):
|
|
1757
1991
|
$ blink agent list List all agents in workspace
|
|
1758
1992
|
$ blink agent status Show current agent details
|
|
@@ -1791,6 +2025,7 @@ registerRealtimeCommands(program);
|
|
|
1791
2025
|
registerRagCommands(program);
|
|
1792
2026
|
registerNotifyCommands(program);
|
|
1793
2027
|
registerConnectorCommands(program);
|
|
2028
|
+
registerLinkedInCommands(program);
|
|
1794
2029
|
program.command("use <project_id>").description("Set active project for this shell session (alternative to blink link)").option("--export", "Output a shell export statement \u2014 use with eval to actually set it").addHelpText("after", `
|
|
1795
2030
|
Examples:
|
|
1796
2031
|
$ blink use proj_xxx Shows the export command to run
|
|
@@ -1806,10 +2041,10 @@ After setting:
|
|
|
1806
2041
|
process.stdout.write(`export BLINK_ACTIVE_PROJECT=${projectId}
|
|
1807
2042
|
`);
|
|
1808
2043
|
} else {
|
|
1809
|
-
const { default:
|
|
1810
|
-
console.log(
|
|
1811
|
-
console.log(
|
|
1812
|
-
console.log(
|
|
2044
|
+
const { default: chalk13 } = await import("chalk");
|
|
2045
|
+
console.log(chalk13.bold("Active project: ") + projectId);
|
|
2046
|
+
console.log(chalk13.dim(`Run: export BLINK_ACTIVE_PROJECT=${projectId}`));
|
|
2047
|
+
console.log(chalk13.dim(`Or: eval $(blink use ${projectId} --export)`));
|
|
1813
2048
|
}
|
|
1814
2049
|
});
|
|
1815
2050
|
program.action(async () => {
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { registerRealtimeCommands } from './commands/realtime.js'
|
|
|
9
9
|
import { registerRagCommands } from './commands/rag.js'
|
|
10
10
|
import { registerNotifyCommands } from './commands/notify.js'
|
|
11
11
|
import { registerConnectorCommands } from './commands/connector.js'
|
|
12
|
+
import { registerLinkedInCommands } from './commands/linkedin.js'
|
|
12
13
|
import { registerDeployCommands } from './commands/deploy.js'
|
|
13
14
|
import { registerProjectCommands } from './commands/project.js'
|
|
14
15
|
import { registerAuthCommands } from './commands/auth.js'
|
|
@@ -90,6 +91,16 @@ Connectors (38 OAuth providers — GitHub, Notion, Slack, Stripe, Shopify, Jira,
|
|
|
90
91
|
$ blink connector exec linear '{ viewer { id name } }' POST GraphQL (Linear)
|
|
91
92
|
Connect accounts at: blink.new/settings?tab=connectors
|
|
92
93
|
|
|
94
|
+
LinkedIn (dedicated commands for the LinkedIn connector):
|
|
95
|
+
$ blink linkedin me Show your LinkedIn profile
|
|
96
|
+
$ blink linkedin posts List your 10 most recent posts
|
|
97
|
+
$ blink linkedin post "Hello LinkedIn!" Publish a text post
|
|
98
|
+
$ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
|
|
99
|
+
$ blink linkedin comment "urn:li:ugcPost:123" "Nice!" Add a comment
|
|
100
|
+
$ blink linkedin like "urn:li:ugcPost:123" Like a post
|
|
101
|
+
$ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
|
|
102
|
+
Link LinkedIn at: blink.new/claw (Agent Integrations tab)
|
|
103
|
+
|
|
93
104
|
Agents (Claw — zero config on Fly machines, BLINK_AGENT_ID is already set):
|
|
94
105
|
$ blink agent list List all agents in workspace
|
|
95
106
|
$ blink agent status Show current agent details
|
|
@@ -130,6 +141,7 @@ registerRealtimeCommands(program)
|
|
|
130
141
|
registerRagCommands(program)
|
|
131
142
|
registerNotifyCommands(program)
|
|
132
143
|
registerConnectorCommands(program)
|
|
144
|
+
registerLinkedInCommands(program)
|
|
133
145
|
|
|
134
146
|
program.command('use <project_id>')
|
|
135
147
|
.description('Set active project for this shell session (alternative to blink link)')
|
|
@@ -65,7 +65,7 @@ Once linked, use \`blink connector exec\` to call their APIs without managing to
|
|
|
65
65
|
|
|
66
66
|
Connect accounts at: https://blink.new/settings?tab=connectors
|
|
67
67
|
|
|
68
|
-
Run \`blink connector providers\` to see all
|
|
68
|
+
Run \`blink connector providers\` to see all 38 supported providers.
|
|
69
69
|
|
|
70
70
|
Quick examples:
|
|
71
71
|
$ blink connector exec github /user/repos GET
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resourcesRequest } from '../lib/api-resources.js'
|
|
3
|
+
import { requireToken } from '../lib/auth.js'
|
|
4
|
+
import { requireAgentId } from '../lib/agent.js'
|
|
5
|
+
import { printJson, isJsonMode, withSpinner } from '../lib/output.js'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
|
|
8
|
+
const NOT_LINKED = 'LinkedIn not linked. Link it in the Agent Integrations tab at blink.new/claw'
|
|
9
|
+
|
|
10
|
+
async function liExec(
|
|
11
|
+
method: string,
|
|
12
|
+
httpMethod: string,
|
|
13
|
+
params: Record<string, unknown>,
|
|
14
|
+
agentId: string
|
|
15
|
+
) {
|
|
16
|
+
const result = await resourcesRequest('/v1/connectors/linkedin/execute', {
|
|
17
|
+
body: { method, http_method: httpMethod, params },
|
|
18
|
+
headers: { 'x-blink-agent-id': agentId },
|
|
19
|
+
})
|
|
20
|
+
if (!result?.success) throw new Error(result?.error ?? NOT_LINKED)
|
|
21
|
+
return result.data
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function getPersonId(agentId: string): Promise<string> {
|
|
25
|
+
const data = await liExec('/me', 'GET', {}, agentId)
|
|
26
|
+
const id = data?.id
|
|
27
|
+
if (!id) throw new Error('Could not resolve LinkedIn person ID from /me response')
|
|
28
|
+
return id
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function registerLinkedInCommands(program: Command) {
|
|
32
|
+
const li = program.command('linkedin')
|
|
33
|
+
.description('LinkedIn connector — post content, manage comments, and view your profile')
|
|
34
|
+
.addHelpText('after', `
|
|
35
|
+
LinkedIn must be linked to your agent via the Integrations tab at blink.new/claw.
|
|
36
|
+
Agent ID defaults to BLINK_AGENT_ID (automatically set on Claw Fly machines).
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
$ blink linkedin me Show your LinkedIn profile
|
|
40
|
+
$ blink linkedin posts List your 10 most recent posts
|
|
41
|
+
$ blink linkedin post "Excited to announce..." Publish a text post
|
|
42
|
+
$ blink linkedin comments "urn:li:ugcPost:123" Read comments on a post
|
|
43
|
+
$ blink linkedin comment "urn:li:ugcPost:123" "Great post!" Add a comment
|
|
44
|
+
$ blink linkedin like "urn:li:ugcPost:123" Like a post
|
|
45
|
+
$ blink linkedin unlike "urn:li:ugcPost:123" Unlike a post
|
|
46
|
+
`)
|
|
47
|
+
|
|
48
|
+
// blink linkedin me
|
|
49
|
+
li.command('me')
|
|
50
|
+
.description('Show your LinkedIn profile (name, ID, vanity URL)')
|
|
51
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
52
|
+
.addHelpText('after', `
|
|
53
|
+
Examples:
|
|
54
|
+
$ blink linkedin me
|
|
55
|
+
$ blink linkedin me --json
|
|
56
|
+
$ blink linkedin me --agent clw_xxx
|
|
57
|
+
`)
|
|
58
|
+
.action(async (opts) => {
|
|
59
|
+
requireToken()
|
|
60
|
+
const agentId = requireAgentId(opts.agent)
|
|
61
|
+
const data = await withSpinner('Fetching LinkedIn profile...', () =>
|
|
62
|
+
liExec('/me', 'GET', {}, agentId)
|
|
63
|
+
)
|
|
64
|
+
if (isJsonMode()) return printJson(data)
|
|
65
|
+
const name = [data?.localizedFirstName, data?.localizedLastName].filter(Boolean).join(' ')
|
|
66
|
+
console.log(chalk.bold('LinkedIn Profile'))
|
|
67
|
+
console.log(` ${chalk.dim('ID:')} ${data?.id ?? '—'}`)
|
|
68
|
+
if (name) console.log(` ${chalk.dim('Name:')} ${name}`)
|
|
69
|
+
if (data?.vanityName) console.log(` ${chalk.dim('URL:')} https://linkedin.com/in/${data.vanityName}`)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// blink linkedin posts
|
|
73
|
+
li.command('posts')
|
|
74
|
+
.description('List your most recent LinkedIn posts (last 10)')
|
|
75
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
76
|
+
.option('--limit <n>', 'Number of posts to fetch (default: 10)', '10')
|
|
77
|
+
.addHelpText('after', `
|
|
78
|
+
Examples:
|
|
79
|
+
$ blink linkedin posts
|
|
80
|
+
$ blink linkedin posts --limit 5
|
|
81
|
+
$ blink linkedin posts --json | jq '.[].id'
|
|
82
|
+
`)
|
|
83
|
+
.action(async (opts) => {
|
|
84
|
+
requireToken()
|
|
85
|
+
const agentId = requireAgentId(opts.agent)
|
|
86
|
+
const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
|
|
87
|
+
getPersonId(agentId)
|
|
88
|
+
)
|
|
89
|
+
const urn = encodeURIComponent(`urn:li:person:${personId}`)
|
|
90
|
+
const data = await withSpinner('Fetching posts...', () =>
|
|
91
|
+
liExec(
|
|
92
|
+
`/ugcPosts?q=authors&authors=List(${urn})&sortBy=LAST_MODIFIED&count=${opts.limit}`,
|
|
93
|
+
'GET',
|
|
94
|
+
{},
|
|
95
|
+
agentId
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
const posts: Array<Record<string, unknown>> = data?.elements ?? (Array.isArray(data) ? data : [])
|
|
99
|
+
if (isJsonMode()) return printJson(posts)
|
|
100
|
+
if (!posts.length) { console.log(chalk.dim('No posts found.')); return }
|
|
101
|
+
for (const post of posts) {
|
|
102
|
+
const id = (post.id ?? '—') as string
|
|
103
|
+
const content = post.specificContent as Record<string, unknown> | undefined
|
|
104
|
+
const share = content?.['com.linkedin.ugc.ShareContent'] as Record<string, unknown> | undefined
|
|
105
|
+
const commentary = share?.shareCommentary as Record<string, unknown> | undefined
|
|
106
|
+
const text = (commentary?.text ?? (post.text as Record<string, unknown>)?.text ?? '(no text)') as string
|
|
107
|
+
const preview = text.length > 120 ? text.slice(0, 120) + '…' : text
|
|
108
|
+
console.log(chalk.bold(id))
|
|
109
|
+
console.log(` ${chalk.dim(preview)}`)
|
|
110
|
+
console.log()
|
|
111
|
+
}
|
|
112
|
+
console.log(chalk.dim(`${posts.length} post(s)`))
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// blink linkedin post "text"
|
|
116
|
+
li.command('post <text>')
|
|
117
|
+
.description('Publish a text post to LinkedIn')
|
|
118
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
119
|
+
.option('--visibility <vis>', 'Post visibility: PUBLIC | CONNECTIONS (default: PUBLIC)', 'PUBLIC')
|
|
120
|
+
.addHelpText('after', `
|
|
121
|
+
Examples:
|
|
122
|
+
$ blink linkedin post "Excited to share our latest update!"
|
|
123
|
+
$ blink linkedin post "Internal update" --visibility CONNECTIONS
|
|
124
|
+
$ blink linkedin post "Hello LinkedIn" --json
|
|
125
|
+
`)
|
|
126
|
+
.action(async (text: string, opts) => {
|
|
127
|
+
requireToken()
|
|
128
|
+
const agentId = requireAgentId(opts.agent)
|
|
129
|
+
const data = await withSpinner('Publishing post...', () =>
|
|
130
|
+
liExec('/ugcPosts', 'POST', { text, visibility: opts.visibility }, agentId)
|
|
131
|
+
)
|
|
132
|
+
if (isJsonMode()) return printJson(data)
|
|
133
|
+
console.log(chalk.green('✓ Post published'))
|
|
134
|
+
if (data?.id) console.log(chalk.dim(` URN: ${data.id}`))
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// blink linkedin comments <postUrn>
|
|
138
|
+
li.command('comments <postUrn>')
|
|
139
|
+
.description('Read comments on a LinkedIn post')
|
|
140
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
141
|
+
.addHelpText('after', `
|
|
142
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
143
|
+
Use "blink linkedin posts" to find your post URNs.
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
$ blink linkedin comments "urn:li:ugcPost:1234567890"
|
|
147
|
+
$ blink linkedin comments "urn:li:ugcPost:1234567890" --json
|
|
148
|
+
`)
|
|
149
|
+
.action(async (postUrn: string, opts) => {
|
|
150
|
+
requireToken()
|
|
151
|
+
const agentId = requireAgentId(opts.agent)
|
|
152
|
+
const encoded = encodeURIComponent(postUrn)
|
|
153
|
+
const data = await withSpinner('Fetching comments...', () =>
|
|
154
|
+
liExec(`rest/socialActions/${encoded}/comments`, 'GET', {}, agentId)
|
|
155
|
+
)
|
|
156
|
+
const comments: Array<Record<string, unknown>> = data?.elements ?? (Array.isArray(data) ? data : [])
|
|
157
|
+
if (isJsonMode()) return printJson(comments)
|
|
158
|
+
if (!comments.length) { console.log(chalk.dim('No comments.')); return }
|
|
159
|
+
for (const c of comments) {
|
|
160
|
+
const author = (c.actor ?? '—') as string
|
|
161
|
+
const msg = c.message as Record<string, unknown> | undefined
|
|
162
|
+
const text = (msg?.text ?? '(no text)') as string
|
|
163
|
+
console.log(chalk.bold(author))
|
|
164
|
+
console.log(` ${text}`)
|
|
165
|
+
console.log()
|
|
166
|
+
}
|
|
167
|
+
console.log(chalk.dim(`${comments.length} comment(s)`))
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// blink linkedin comment <postUrn> "text"
|
|
171
|
+
li.command('comment <postUrn> <text>')
|
|
172
|
+
.description('Add a comment to a LinkedIn post')
|
|
173
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
174
|
+
.addHelpText('after', `
|
|
175
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
176
|
+
|
|
177
|
+
Examples:
|
|
178
|
+
$ blink linkedin comment "urn:li:ugcPost:1234567890" "Great post!"
|
|
179
|
+
$ blink linkedin comment "urn:li:ugcPost:1234567890" "Thanks for sharing" --json
|
|
180
|
+
`)
|
|
181
|
+
.action(async (postUrn: string, text: string, opts) => {
|
|
182
|
+
requireToken()
|
|
183
|
+
const agentId = requireAgentId(opts.agent)
|
|
184
|
+
const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
|
|
185
|
+
getPersonId(agentId)
|
|
186
|
+
)
|
|
187
|
+
const actor = `urn:li:person:${personId}`
|
|
188
|
+
const encoded = encodeURIComponent(postUrn)
|
|
189
|
+
const data = await withSpinner('Adding comment...', () =>
|
|
190
|
+
liExec(
|
|
191
|
+
`rest/socialActions/${encoded}/comments`,
|
|
192
|
+
'POST',
|
|
193
|
+
{ actor, object: postUrn, message: { text } },
|
|
194
|
+
agentId
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
if (isJsonMode()) return printJson(data)
|
|
198
|
+
console.log(chalk.green('✓ Comment added'))
|
|
199
|
+
if (data?.id) console.log(chalk.dim(` ID: ${data.id}`))
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// blink linkedin like <postUrn>
|
|
203
|
+
li.command('like <postUrn>')
|
|
204
|
+
.description('Like a LinkedIn post')
|
|
205
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
206
|
+
.addHelpText('after', `
|
|
207
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
208
|
+
|
|
209
|
+
Examples:
|
|
210
|
+
$ blink linkedin like "urn:li:ugcPost:1234567890"
|
|
211
|
+
$ blink linkedin like "urn:li:ugcPost:1234567890" --json
|
|
212
|
+
`)
|
|
213
|
+
.action(async (postUrn: string, opts) => {
|
|
214
|
+
requireToken()
|
|
215
|
+
const agentId = requireAgentId(opts.agent)
|
|
216
|
+
const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
|
|
217
|
+
getPersonId(agentId)
|
|
218
|
+
)
|
|
219
|
+
const actor = `urn:li:person:${personId}`
|
|
220
|
+
const encoded = encodeURIComponent(postUrn)
|
|
221
|
+
const data = await withSpinner('Liking post...', () =>
|
|
222
|
+
liExec(
|
|
223
|
+
`rest/socialActions/${encoded}/likes`,
|
|
224
|
+
'POST',
|
|
225
|
+
{ actor, object: postUrn },
|
|
226
|
+
agentId
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
if (isJsonMode()) return printJson(data)
|
|
230
|
+
console.log(chalk.green('✓ Post liked'))
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// blink linkedin unlike <postUrn>
|
|
234
|
+
li.command('unlike <postUrn>')
|
|
235
|
+
.description('Unlike a LinkedIn post')
|
|
236
|
+
.option('--agent <id>', 'Agent ID (defaults to BLINK_AGENT_ID)')
|
|
237
|
+
.addHelpText('after', `
|
|
238
|
+
<postUrn> is the LinkedIn post URN, e.g. urn:li:ugcPost:1234567890
|
|
239
|
+
|
|
240
|
+
Examples:
|
|
241
|
+
$ blink linkedin unlike "urn:li:ugcPost:1234567890"
|
|
242
|
+
$ blink linkedin unlike "urn:li:ugcPost:1234567890" --json
|
|
243
|
+
`)
|
|
244
|
+
.action(async (postUrn: string, opts) => {
|
|
245
|
+
requireToken()
|
|
246
|
+
const agentId = requireAgentId(opts.agent)
|
|
247
|
+
const personId = await withSpinner('Resolving your LinkedIn identity...', () =>
|
|
248
|
+
getPersonId(agentId)
|
|
249
|
+
)
|
|
250
|
+
const actor = `urn:li:person:${personId}`
|
|
251
|
+
const encodedPost = encodeURIComponent(postUrn)
|
|
252
|
+
const encodedActor = encodeURIComponent(actor)
|
|
253
|
+
// LinkedIn DELETE likes requires: DELETE /rest/socialActions/{postUrn}/likes/{actorUrn}?actor={actorUrn}
|
|
254
|
+
const data = await withSpinner('Unliking post...', () =>
|
|
255
|
+
liExec(
|
|
256
|
+
`rest/socialActions/${encodedPost}/likes/${encodedActor}?actor=${encodedActor}`,
|
|
257
|
+
'DELETE',
|
|
258
|
+
{},
|
|
259
|
+
agentId
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
if (isJsonMode()) return printJson(data)
|
|
263
|
+
console.log(chalk.green('✓ Post unliked'))
|
|
264
|
+
})
|
|
265
|
+
}
|