@usesocial/cli 0.9.0 → 0.10.1
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/CHANGELOG.md +12 -0
- package/README.md +27 -8
- package/dist/index.mjs +491 -138
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -334,11 +334,12 @@ const buildRuntimeArgs = (adapter, op) => {
|
|
|
334
334
|
};
|
|
335
335
|
for (const [name, param] of Object.entries(op.config.bodyParams ?? {})) {
|
|
336
336
|
const argName = bodyParamArgFor(adapter, name, param);
|
|
337
|
+
const valueHint = param.valueHint ?? flagName(argName);
|
|
337
338
|
args[flagName(argName)] = {
|
|
338
339
|
type: param.positional ? "positional" : "string",
|
|
339
340
|
required: param.required === true,
|
|
340
341
|
description: param.description ?? `${name} body value`,
|
|
341
|
-
valueHint: param.valueHint
|
|
342
|
+
valueHint: param.kind === "array" ? `${valueHint}...` : valueHint
|
|
342
343
|
};
|
|
343
344
|
}
|
|
344
345
|
if (op.config.mediaArg) args[flagName(op.config.mediaArg)] = {
|
|
@@ -363,6 +364,7 @@ const buildRuntimeArgs = (adapter, op) => {
|
|
|
363
364
|
type: "string",
|
|
364
365
|
description: "Account selector: @username or profile_id:<id>; overrides config default"
|
|
365
366
|
};
|
|
367
|
+
for (const [name, def] of Object.entries(adapter.extraArgsFor?.(op) ?? {})) args[name] = def;
|
|
366
368
|
if (adapter.isCacheable(op.endpoint)) args.header = {
|
|
367
369
|
type: "string",
|
|
368
370
|
alias: "H",
|
|
@@ -432,7 +434,8 @@ const buildRuntimeInput = (adapter, op, args) => {
|
|
|
432
434
|
input.body = args.body;
|
|
433
435
|
if (op.config.bodyTextArg) input[op.config.bodyTextArg] = args[bodyTextDisplayKeyFor(op) ?? op.config.bodyTextArg];
|
|
434
436
|
Object.entries(op.config.bodyParams ?? {}).forEach(([name, param], index) => {
|
|
435
|
-
|
|
437
|
+
const argName = bodyParamArgFor(adapter, name, param);
|
|
438
|
+
input[name] = param.positional && param.kind === "array" && Array.isArray(args._) && args._.length > 0 ? args._ : args[flagName(argName)] ?? (Array.isArray(args._) ? args._[index] : void 0);
|
|
436
439
|
});
|
|
437
440
|
if (op.config.replyToArg) input[op.config.replyToArg] = args[op.config.replyToArg];
|
|
438
441
|
}
|
|
@@ -445,7 +448,10 @@ const checkBodyRequired = (adapter, op, args, bodyTextOptional) => {
|
|
|
445
448
|
const expressions = [];
|
|
446
449
|
const bodyTextArg = op.config.bodyTextArg;
|
|
447
450
|
if (bodyTextArg && op.config.bodyTextRequired !== false) expressions.push(args[bodyTextDisplayKeyFor(op) ?? bodyTextArg] === void 0);
|
|
448
|
-
for (const [name, param] of Object.entries(op.config.bodyParams ?? {})) if (param.required)
|
|
451
|
+
for (const [name, param] of Object.entries(op.config.bodyParams ?? {})) if (param.required) {
|
|
452
|
+
const positionalValuePresent = param.positional && Array.isArray(args._) && args._.length > 0;
|
|
453
|
+
expressions.push(args[flagName(bodyParamArgFor(adapter, name, param))] === void 0 && !positionalValuePresent);
|
|
454
|
+
}
|
|
449
455
|
if (expressions.length > 0 && typeof args.body !== "string" && expressions.some(Boolean)) throw bodyRequiredPreparseError(adapter, op);
|
|
450
456
|
};
|
|
451
457
|
const buildBody = (adapter, deps, op, input, bodyTextOptional) => {
|
|
@@ -458,7 +464,8 @@ const buildBody = (adapter, deps, op, input, bodyTextOptional) => {
|
|
|
458
464
|
const bodyParamValues = /* @__PURE__ */ new Map();
|
|
459
465
|
for (const [name, param] of bodyParamEntries) {
|
|
460
466
|
const value = input[name];
|
|
461
|
-
|
|
467
|
+
const displayName = param.positional ? bodyParamArgFor(adapter, name, param) : `--${bodyParamArgFor(adapter, name, param)}`;
|
|
468
|
+
bodyParamValues.set(name, value === void 0 ? param.default : param.kind === "array" ? deps.parseResourceIdList(displayName, Array.isArray(value) ? value : [value]) : deps.parseQueryString(displayName, value));
|
|
462
469
|
}
|
|
463
470
|
const body = { ...bodyFromJSON ?? {} };
|
|
464
471
|
for (const [name] of bodyParamEntries) {
|
|
@@ -626,6 +633,16 @@ const runRuntimeOperation = async (adapter, op, contract, deps, args, rawArgs =
|
|
|
626
633
|
}
|
|
627
634
|
}
|
|
628
635
|
for (const param of op.parameters.filter((candidate) => candidate.in === "path")) {
|
|
636
|
+
if (input[param.name] === void 0) {
|
|
637
|
+
const seeded = await adapter.seedPathValue?.({
|
|
638
|
+
op,
|
|
639
|
+
param,
|
|
640
|
+
args,
|
|
641
|
+
deps,
|
|
642
|
+
account
|
|
643
|
+
});
|
|
644
|
+
if (seeded !== void 0) input[param.name] = seeded;
|
|
645
|
+
}
|
|
629
646
|
const mode = ownMode$1(op, param.name);
|
|
630
647
|
const display = positionalDisplayKeyFor$1(op, param);
|
|
631
648
|
let resolution;
|
|
@@ -752,7 +769,7 @@ const defineRuntimeOperationCommand = (adapter, op, deps) => {
|
|
|
752
769
|
});
|
|
753
770
|
};
|
|
754
771
|
//#endregion
|
|
755
|
-
//#region ../../packages/
|
|
772
|
+
//#region ../../packages/sqlsync/src/collections/linkedin.ts
|
|
756
773
|
const linkedinCursorPagination = {
|
|
757
774
|
style: "cursor",
|
|
758
775
|
limitParam: "limit",
|
|
@@ -1072,7 +1089,7 @@ const LINKEDIN_CACHE_METADATA = {
|
|
|
1072
1089
|
}
|
|
1073
1090
|
};
|
|
1074
1091
|
//#endregion
|
|
1075
|
-
//#region ../../packages/
|
|
1092
|
+
//#region ../../packages/sqlsync/src/collections/x.ts
|
|
1076
1093
|
const xPagination = {
|
|
1077
1094
|
style: "cursor",
|
|
1078
1095
|
limitParam: "max_results",
|
|
@@ -1216,6 +1233,21 @@ const X_COLLECTIONS = [
|
|
|
1216
1233
|
expectedFields: expectedTweetFields,
|
|
1217
1234
|
lift: liftTweet
|
|
1218
1235
|
},
|
|
1236
|
+
{
|
|
1237
|
+
key: "x_timeline",
|
|
1238
|
+
table: "x_timeline_raw",
|
|
1239
|
+
platform: "x",
|
|
1240
|
+
requests: [{
|
|
1241
|
+
path: "/users/:id/timelines/reverse_chronological",
|
|
1242
|
+
bakedQuery: defaultTweetBakedQuery
|
|
1243
|
+
}],
|
|
1244
|
+
ownIdToken: ":id",
|
|
1245
|
+
pagination: xPagination,
|
|
1246
|
+
pageSize: 100,
|
|
1247
|
+
sinceField: "created_at",
|
|
1248
|
+
expectedFields: expectedTweetFields,
|
|
1249
|
+
lift: liftTweet
|
|
1250
|
+
},
|
|
1219
1251
|
{
|
|
1220
1252
|
key: "x_followers",
|
|
1221
1253
|
table: "x_followers_raw",
|
|
@@ -1313,7 +1345,8 @@ const X_CACHE_METADATA = {
|
|
|
1313
1345
|
"x_messages.sender_id = x_profiles.provider_id",
|
|
1314
1346
|
"x_messages.counterpart_id = x_profiles.provider_id",
|
|
1315
1347
|
"x_conversation_participants.participant_id = x_profiles.provider_id",
|
|
1316
|
-
"x_followers.id = x_profiles.provider_id"
|
|
1348
|
+
"x_followers.id = x_profiles.provider_id",
|
|
1349
|
+
"x_timeline.author_id = x_profiles.provider_id"
|
|
1317
1350
|
],
|
|
1318
1351
|
enums: {
|
|
1319
1352
|
"x_followers.verified_type": [
|
|
@@ -1364,6 +1397,11 @@ const X_CACHE_METADATA = {
|
|
|
1364
1397
|
"sender_id"
|
|
1365
1398
|
],
|
|
1366
1399
|
x_profiles: ["username"],
|
|
1400
|
+
x_timeline: [
|
|
1401
|
+
"created_at",
|
|
1402
|
+
"author_id",
|
|
1403
|
+
"conversation_id"
|
|
1404
|
+
],
|
|
1367
1405
|
x_tweets: [
|
|
1368
1406
|
"created_at",
|
|
1369
1407
|
"author_id",
|
|
@@ -1372,7 +1410,7 @@ const X_CACHE_METADATA = {
|
|
|
1372
1410
|
}
|
|
1373
1411
|
};
|
|
1374
1412
|
//#endregion
|
|
1375
|
-
//#region ../../packages/
|
|
1413
|
+
//#region ../../packages/sqlsync/src/collections/index.ts
|
|
1376
1414
|
const COLLECTIONS = [...X_COLLECTIONS, ...LINKEDIN_COLLECTIONS];
|
|
1377
1415
|
const CACHE_METADATA_BY_PLATFORM = {
|
|
1378
1416
|
linkedin: LINKEDIN_CACHE_METADATA,
|
|
@@ -1392,7 +1430,7 @@ const resetTablesFor = (collection) => {
|
|
|
1392
1430
|
};
|
|
1393
1431
|
const resetPhysicalTablesFor = (collection) => resetTablesFor(collection).map(physicalTableFor);
|
|
1394
1432
|
//#endregion
|
|
1395
|
-
//#region ../../packages/
|
|
1433
|
+
//#region ../../packages/sqlsync/src/migrations.generated.ts
|
|
1396
1434
|
const MIGRATIONS = [
|
|
1397
1435
|
{
|
|
1398
1436
|
"tag": "0000_modern_stephen_strange",
|
|
@@ -1433,10 +1471,14 @@ const MIGRATIONS = [
|
|
|
1433
1471
|
{
|
|
1434
1472
|
"tag": "0009_x_liked_mentions",
|
|
1435
1473
|
"sql": "CREATE TABLE \"x_liked_raw\" (\n \"id\" text PRIMARY KEY NOT NULL,\n \"created_at\" integer,\n \"author_id\" text,\n \"conversation_id\" text,\n \"in_reply_to_user_id\" text,\n \"lang\" text,\n \"reply_settings\" text,\n \"text\" text,\n \"url\" text,\n \"paid_partnership\" integer,\n \"possibly_sensitive\" integer,\n \"like_count\" integer,\n \"retweet_count\" integer,\n \"reply_count\" integer,\n \"quote_count\" integer,\n \"bookmark_count\" integer,\n \"impression_count\" integer,\n \"raw\" text NOT NULL,\n \"synced_at\" integer NOT NULL\n);\n--> statement-breakpoint\nCREATE INDEX \"x_liked_created_at_idx\" ON \"x_liked_raw\" (\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_liked_author_id_created_at_idx\" ON \"x_liked_raw\" (\"author_id\",\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_liked_conversation_id_created_at_idx\" ON \"x_liked_raw\" (\"conversation_id\",\"created_at\");--> statement-breakpoint\nCREATE VIEW \"x_liked\" AS\nSELECT \"id\", \"created_at\", \"author_id\", \"conversation_id\", \"in_reply_to_user_id\", \"lang\",\n \"reply_settings\", \"text\", \"url\", \"paid_partnership\", \"possibly_sensitive\",\n \"like_count\", \"retweet_count\", \"reply_count\", \"quote_count\", \"bookmark_count\",\n \"impression_count\"\nFROM \"x_liked_raw\";\n--> statement-breakpoint\nCREATE TABLE \"x_mentions_raw\" (\n \"id\" text PRIMARY KEY NOT NULL,\n \"created_at\" integer,\n \"author_id\" text,\n \"conversation_id\" text,\n \"in_reply_to_user_id\" text,\n \"lang\" text,\n \"reply_settings\" text,\n \"text\" text,\n \"url\" text,\n \"paid_partnership\" integer,\n \"possibly_sensitive\" integer,\n \"like_count\" integer,\n \"retweet_count\" integer,\n \"reply_count\" integer,\n \"quote_count\" integer,\n \"bookmark_count\" integer,\n \"impression_count\" integer,\n \"raw\" text NOT NULL,\n \"synced_at\" integer NOT NULL\n);\n--> statement-breakpoint\nCREATE INDEX \"x_mentions_created_at_idx\" ON \"x_mentions_raw\" (\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_mentions_author_id_created_at_idx\" ON \"x_mentions_raw\" (\"author_id\",\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_mentions_conversation_id_created_at_idx\" ON \"x_mentions_raw\" (\"conversation_id\",\"created_at\");--> statement-breakpoint\nCREATE VIEW \"x_mentions\" AS\nSELECT \"id\", \"created_at\", \"author_id\", \"conversation_id\", \"in_reply_to_user_id\", \"lang\",\n \"reply_settings\", \"text\", \"url\", \"paid_partnership\", \"possibly_sensitive\",\n \"like_count\", \"retweet_count\", \"reply_count\", \"quote_count\", \"bookmark_count\",\n \"impression_count\"\nFROM \"x_mentions_raw\";\n"
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
"tag": "0010_x_timeline",
|
|
1477
|
+
"sql": "CREATE TABLE \"x_timeline_raw\" (\n \"id\" text PRIMARY KEY NOT NULL,\n \"created_at\" integer,\n \"author_id\" text,\n \"conversation_id\" text,\n \"in_reply_to_user_id\" text,\n \"lang\" text,\n \"reply_settings\" text,\n \"text\" text,\n \"url\" text,\n \"paid_partnership\" integer,\n \"possibly_sensitive\" integer,\n \"like_count\" integer,\n \"retweet_count\" integer,\n \"reply_count\" integer,\n \"quote_count\" integer,\n \"bookmark_count\" integer,\n \"impression_count\" integer,\n \"raw\" text NOT NULL,\n \"synced_at\" integer NOT NULL\n);\n--> statement-breakpoint\nCREATE INDEX \"x_timeline_created_at_idx\" ON \"x_timeline_raw\" (\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_timeline_author_id_created_at_idx\" ON \"x_timeline_raw\" (\"author_id\",\"created_at\");--> statement-breakpoint\nCREATE INDEX \"x_timeline_conversation_id_created_at_idx\" ON \"x_timeline_raw\" (\"conversation_id\",\"created_at\");--> statement-breakpoint\nCREATE VIEW \"x_timeline\" AS\nSELECT \"id\", \"created_at\", \"author_id\", \"conversation_id\", \"in_reply_to_user_id\", \"lang\",\n \"reply_settings\", \"text\", \"url\", \"paid_partnership\", \"possibly_sensitive\",\n \"like_count\", \"retweet_count\", \"reply_count\", \"quote_count\", \"bookmark_count\",\n \"impression_count\"\nFROM \"x_timeline_raw\";\n"
|
|
1436
1478
|
}
|
|
1437
1479
|
];
|
|
1438
1480
|
//#endregion
|
|
1439
|
-
//#region ../../packages/
|
|
1481
|
+
//#region ../../packages/sqlsync/src/migrate.ts
|
|
1440
1482
|
const readUserVersion = (db) => {
|
|
1441
1483
|
const version = db.prepare("PRAGMA user_version").get()?.user_version;
|
|
1442
1484
|
return typeof version === "number" ? version : Number(version ?? 0);
|
|
@@ -1470,7 +1512,7 @@ const assertAtLatest = (db) => {
|
|
|
1470
1512
|
if (currentVersion < MIGRATIONS.length) throw new Error(`Local SQLite cache schema is at version ${currentVersion}; expected ${MIGRATIONS.length}`);
|
|
1471
1513
|
};
|
|
1472
1514
|
//#endregion
|
|
1473
|
-
//#region ../../packages/
|
|
1515
|
+
//#region ../../packages/sqlsync/src/sqlite.ts
|
|
1474
1516
|
const loadModule = createRequire(import.meta.url);
|
|
1475
1517
|
const isBunRuntime = () => typeof Bun !== "undefined";
|
|
1476
1518
|
const normalizeValue = (value) => {
|
|
@@ -1526,7 +1568,7 @@ const openDatabase = (path, opts) => {
|
|
|
1526
1568
|
};
|
|
1527
1569
|
};
|
|
1528
1570
|
//#endregion
|
|
1529
|
-
//#region ../../packages/
|
|
1571
|
+
//#region ../../packages/sqlsync/src/write-through.ts
|
|
1530
1572
|
const quoteIdentifier$3 = (identifier) => `\`${identifier.replaceAll("`", "``")}\``;
|
|
1531
1573
|
const collectionForTable = (table) => table.endsWith("_raw") ? table.slice(0, -4) : table;
|
|
1532
1574
|
const profileTables$1 = new Set(["x_profiles_raw", "li_profiles_raw"]);
|
|
@@ -1574,17 +1616,10 @@ const applyWriteThrough = (db, args) => {
|
|
|
1574
1616
|
};
|
|
1575
1617
|
};
|
|
1576
1618
|
//#endregion
|
|
1577
|
-
//#region ../../packages/
|
|
1578
|
-
var NeverSyncedError = class extends Error {
|
|
1579
|
-
name = "NeverSyncedError";
|
|
1580
|
-
tables;
|
|
1581
|
-
constructor(tables, message) {
|
|
1582
|
-
super(message);
|
|
1583
|
-
this.tables = tables;
|
|
1584
|
-
}
|
|
1585
|
-
};
|
|
1619
|
+
//#region ../../packages/sqlsync/src/cache.ts
|
|
1586
1620
|
const publicCacheTables = [
|
|
1587
1621
|
"x_tweets",
|
|
1622
|
+
"x_timeline",
|
|
1588
1623
|
"x_followers",
|
|
1589
1624
|
"x_following",
|
|
1590
1625
|
"x_bookmarks",
|
|
@@ -1614,6 +1649,7 @@ const sanitizeAccountId = (accountId) => accountId.replaceAll(/[^A-Za-z0-9._-]/g
|
|
|
1614
1649
|
const quoteIdentifier$2 = (identifier) => `\`${identifier.replaceAll("`", "``")}\``;
|
|
1615
1650
|
const resolveCachePath = (path) => path === ":memory:" ? path : resolve(path);
|
|
1616
1651
|
const rawTableFor = (table) => table === "sync_state" ? table : `${table}_raw`;
|
|
1652
|
+
const partialSyncCursorPrefix = "partial-sync-v1:";
|
|
1617
1653
|
const collectionForReadTable = (table) => table.endsWith("_raw") ? table.slice(0, -4) : table;
|
|
1618
1654
|
const physicalTableForCacheTable = (table) => table === "sync_state" || table.endsWith("_raw") ? table : rawTableFor(table);
|
|
1619
1655
|
const stringOrNull = (value) => typeof value === "string" ? value : null;
|
|
@@ -1644,7 +1680,6 @@ const referencedTablesForRead = (sql, platform) => {
|
|
|
1644
1680
|
const allowedTables = platformTables[platform];
|
|
1645
1681
|
return knownTableTokens(sql).filter((table) => allowedTables.has(table));
|
|
1646
1682
|
};
|
|
1647
|
-
const collectionShortName = (collection) => collection.replace(/^(x|li)_/, "");
|
|
1648
1683
|
const tableFreshnessPolicies = {
|
|
1649
1684
|
x_conversation_participants: {
|
|
1650
1685
|
kind: "collection",
|
|
@@ -1656,13 +1691,11 @@ const tableFreshnessPolicies = {
|
|
|
1656
1691
|
},
|
|
1657
1692
|
li_conversations: {
|
|
1658
1693
|
kind: "collection",
|
|
1659
|
-
collection: "li_conversations"
|
|
1660
|
-
commandCollection: "li_messages"
|
|
1694
|
+
collection: "li_conversations"
|
|
1661
1695
|
},
|
|
1662
1696
|
li_conversations_raw: {
|
|
1663
1697
|
kind: "collection",
|
|
1664
|
-
collection: "li_conversations"
|
|
1665
|
-
commandCollection: "li_messages"
|
|
1698
|
+
collection: "li_conversations"
|
|
1666
1699
|
},
|
|
1667
1700
|
li_profiles: {
|
|
1668
1701
|
kind: "platform",
|
|
@@ -1686,44 +1719,25 @@ const freshnessPolicyForTable = (table) => tableFreshnessPolicies[table] ?? {
|
|
|
1686
1719
|
collection: collectionForReadTable(table)
|
|
1687
1720
|
};
|
|
1688
1721
|
const platformCollectionPrefix = (platform) => platform === "linkedin" ? "li_" : "x_";
|
|
1689
|
-
const
|
|
1690
|
-
if (
|
|
1691
|
-
|
|
1692
|
-
};
|
|
1693
|
-
const missingNameForPolicy = (table, policy) => policy.kind === "platform" ? table : policy.collection;
|
|
1694
|
-
const formatList = (items) => {
|
|
1695
|
-
if (items.length <= 1) return items[0] ?? "";
|
|
1696
|
-
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
1697
|
-
return `${items.slice(0, -1).join(", ")}, and ${items.at(-1)}`;
|
|
1722
|
+
const partialCollectionLastSynced = (db, collection, cursor) => {
|
|
1723
|
+
if (typeof cursor !== "string" || !cursor.startsWith(partialSyncCursorPrefix)) return null;
|
|
1724
|
+
if (!cacheTables.has(collection)) return null;
|
|
1725
|
+
return numberOrNull(db.prepare(`SELECT MAX(synced_at) AS last_synced FROM ${quoteIdentifier$2(rawTableFor(collection))}`).get()?.last_synced);
|
|
1698
1726
|
};
|
|
1699
1727
|
const collectionLastSynced = (db, collection) => {
|
|
1700
|
-
|
|
1728
|
+
const syncState = db.prepare("SELECT cursor, last_synced FROM sync_state WHERE collection = :collection").get({ collection });
|
|
1729
|
+
return numberOrNull(syncState?.last_synced) ?? partialCollectionLastSynced(db, collection, syncState?.cursor);
|
|
1701
1730
|
};
|
|
1702
1731
|
const platformLastSynced = (db, platform) => {
|
|
1703
|
-
|
|
1732
|
+
const syncState = db.prepare("SELECT MAX(last_synced) AS last_synced FROM sync_state WHERE collection GLOB :glob").get({ glob: `${platformCollectionPrefix(platform)}*` });
|
|
1733
|
+
const partialRows = db.prepare("SELECT collection, cursor FROM sync_state WHERE collection GLOB :glob AND last_synced IS NULL AND cursor LIKE :partial").all({
|
|
1734
|
+
glob: `${platformCollectionPrefix(platform)}*`,
|
|
1735
|
+
partial: `${partialSyncCursorPrefix}%`
|
|
1736
|
+
});
|
|
1737
|
+
const timestamps = [numberOrNull(syncState?.last_synced), ...partialRows.map((row) => partialCollectionLastSynced(db, String(row.collection), row.cursor))].filter((value) => value !== null);
|
|
1738
|
+
return timestamps.length === 0 ? null : Math.max(...timestamps);
|
|
1704
1739
|
};
|
|
1705
1740
|
const lastSyncedForPolicy = (db, policy) => policy.kind === "platform" ? platformLastSynced(db, policy.platform) : collectionLastSynced(db, policy.collection);
|
|
1706
|
-
const missingFreshnessForTables = (tables) => {
|
|
1707
|
-
const missing = [];
|
|
1708
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1709
|
-
for (const table of tables) {
|
|
1710
|
-
if (table.lastSynced != null) continue;
|
|
1711
|
-
const key = `${table.missingName}\0${table.command}`;
|
|
1712
|
-
if (!seen.has(key)) {
|
|
1713
|
-
seen.add(key);
|
|
1714
|
-
missing.push({
|
|
1715
|
-
name: table.missingName,
|
|
1716
|
-
command: table.command
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
return missing;
|
|
1721
|
-
};
|
|
1722
|
-
const neverSyncedMessage = (missing) => {
|
|
1723
|
-
const names = missing.map((item) => item.name);
|
|
1724
|
-
const commands = missing.map((item) => `\`${item.command}\``);
|
|
1725
|
-
return `No synced ${formatList(names)} yet — run ${formatList(commands)} first.`;
|
|
1726
|
-
};
|
|
1727
1741
|
const assertKnownProfileTable = (table) => {
|
|
1728
1742
|
assertKnownTable(table);
|
|
1729
1743
|
if (!profileTables.has(physicalTableForCacheTable(table))) throw new Error(`Unknown profile table: ${table}`);
|
|
@@ -1817,7 +1831,7 @@ const createCache = (path, db) => ({
|
|
|
1817
1831
|
if (!row) return;
|
|
1818
1832
|
return {
|
|
1819
1833
|
cursor: stringOrNull(row.cursor),
|
|
1820
|
-
lastSynced: numberOrNull(row.last_synced),
|
|
1834
|
+
lastSynced: numberOrNull(row.last_synced) ?? partialCollectionLastSynced(db, collection, row.cursor),
|
|
1821
1835
|
objectCount: numberOrNull(row.object_count)
|
|
1822
1836
|
};
|
|
1823
1837
|
},
|
|
@@ -1853,22 +1867,14 @@ const createCache = (path, db) => ({
|
|
|
1853
1867
|
read: (sql, opts) => {
|
|
1854
1868
|
assertPlatformQuery(sql, opts.platform);
|
|
1855
1869
|
const tables = referencedTablesForRead(sql, opts.platform).map((name) => {
|
|
1856
|
-
const policy = freshnessPolicyForTable(name);
|
|
1857
1870
|
return {
|
|
1858
1871
|
name,
|
|
1859
|
-
|
|
1860
|
-
command: syncCommandForPolicy(opts.platform, policy),
|
|
1861
|
-
lastSynced: lastSyncedForPolicy(db, policy)
|
|
1872
|
+
lastSynced: lastSyncedForPolicy(db, freshnessPolicyForTable(name))
|
|
1862
1873
|
};
|
|
1863
1874
|
});
|
|
1864
|
-
const missingFreshness = missingFreshnessForTables(tables);
|
|
1865
|
-
if (missingFreshness.length > 0) throw new NeverSyncedError(missingFreshness.map((item) => item.name), neverSyncedMessage(missingFreshness));
|
|
1866
1875
|
return {
|
|
1867
1876
|
rows: db.prepare(sql).all(),
|
|
1868
|
-
tables
|
|
1869
|
-
name,
|
|
1870
|
-
lastSynced
|
|
1871
|
-
}))
|
|
1877
|
+
tables
|
|
1872
1878
|
};
|
|
1873
1879
|
},
|
|
1874
1880
|
describe: (opts) => {
|
|
@@ -1893,7 +1899,6 @@ const createCache = (path, db) => ({
|
|
|
1893
1899
|
name,
|
|
1894
1900
|
rows: Number(rowCount?.rows ?? 0),
|
|
1895
1901
|
synced_at: syncedAt,
|
|
1896
|
-
query_ready: syncedAt != null,
|
|
1897
1902
|
columns: columnsForTable(name),
|
|
1898
1903
|
indexed: metadata.indexed[name] ?? []
|
|
1899
1904
|
};
|
|
@@ -1905,7 +1910,7 @@ const createCache = (path, db) => ({
|
|
|
1905
1910
|
}
|
|
1906
1911
|
});
|
|
1907
1912
|
//#endregion
|
|
1908
|
-
//#region ../../packages/
|
|
1913
|
+
//#region ../../packages/sqlsync/src/since.ts
|
|
1909
1914
|
const isoDatePattern = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
|
1910
1915
|
const isoDateTimePattern = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d{1,3})?)?(?:Z|[+-]([01]\d|2[0-3]):[0-5]\d)?$/;
|
|
1911
1916
|
const timezonePattern = /(Z|[+-]\d{2}:\d{2})$/;
|
|
@@ -6499,17 +6504,17 @@ function superRefine(fn, params) {
|
|
|
6499
6504
|
return /* @__PURE__ */ _superRefine(fn, params);
|
|
6500
6505
|
}
|
|
6501
6506
|
//#endregion
|
|
6502
|
-
//#region ../../packages/
|
|
6507
|
+
//#region ../../packages/sqlsync/src/cost.ts
|
|
6503
6508
|
const creditsFromHeader = (headers) => {
|
|
6504
6509
|
const rawCredits = headers?.get("social-x-credits-used");
|
|
6505
6510
|
const credits = rawCredits == null ? NaN : Number.parseInt(rawCredits, 10);
|
|
6506
6511
|
return Number.isNaN(credits) || credits < 0 ? 0 : credits;
|
|
6507
6512
|
};
|
|
6508
6513
|
//#endregion
|
|
6509
|
-
//#region ../../packages/
|
|
6514
|
+
//#region ../../packages/sqlsync/src/profiles.ts
|
|
6510
6515
|
const profileTableFor = (platform) => platform === "x" ? "x_profiles_raw" : "li_profiles_raw";
|
|
6511
6516
|
//#endregion
|
|
6512
|
-
//#region ../../packages/
|
|
6517
|
+
//#region ../../packages/sqlsync/src/sync.ts
|
|
6513
6518
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6514
6519
|
const pageDelayFor = (pageDelayMs, pagesCompleted) => typeof pageDelayMs === "function" ? pageDelayMs(pagesCompleted) : pageDelayMs;
|
|
6515
6520
|
var SyncPageError = class extends Error {
|
|
@@ -6561,6 +6566,12 @@ const decodePartialSyncCursor = (value) => {
|
|
|
6561
6566
|
}
|
|
6562
6567
|
};
|
|
6563
6568
|
const encodePartialSyncCursor = (cursor) => `${PARTIAL_SYNC_CURSOR_PREFIX}${JSON.stringify(cursor)}`;
|
|
6569
|
+
const partialCursorMatchesSince = (cursor, since) => {
|
|
6570
|
+
const requestedSince = since ?? null;
|
|
6571
|
+
if (cursor.since === requestedSince) return true;
|
|
6572
|
+
return requestedSince !== null && (cursor.since === null || cursor.since <= requestedSince);
|
|
6573
|
+
};
|
|
6574
|
+
const preservesBroaderPartialOnSince = (cursor, since, stoppedReason) => cursor !== void 0 && cursor.since !== (since ?? null) && stoppedReason === "since";
|
|
6564
6575
|
const isRateLimitError = (error) => isRecord$9(error) && error.status === 429;
|
|
6565
6576
|
const retryAfterSecondsFromRateLimitError = (error) => {
|
|
6566
6577
|
if (!(isRateLimitError(error) && isRecord$9(error))) return;
|
|
@@ -6757,11 +6768,11 @@ const walkRequest = async (args) => {
|
|
|
6757
6768
|
cursor: state.cursor ?? "",
|
|
6758
6769
|
checkpoint,
|
|
6759
6770
|
newestId: state.newestId,
|
|
6760
|
-
since: args.since ?? null,
|
|
6771
|
+
since: args.partialCursorSince === void 0 ? args.since ?? null : args.partialCursorSince,
|
|
6761
6772
|
...args.parentId === void 0 ? {} : { parentId: args.parentId }
|
|
6762
6773
|
}),
|
|
6763
6774
|
objectCount: upserted,
|
|
6764
|
-
lastSynced:
|
|
6775
|
+
lastSynced: Date.now()
|
|
6765
6776
|
});
|
|
6766
6777
|
partialCursorPersisted = true;
|
|
6767
6778
|
return true;
|
|
@@ -6837,7 +6848,7 @@ const walkRequest = async (args) => {
|
|
|
6837
6848
|
upserted += step.rows.length;
|
|
6838
6849
|
ids.push(...step.ids);
|
|
6839
6850
|
state = step.nextState;
|
|
6840
|
-
persistPartialCursor();
|
|
6851
|
+
if (!(args.preservePartialCursorOnSince && step.done && step.stoppedReason === "since")) persistPartialCursor();
|
|
6841
6852
|
await args.onPage?.({
|
|
6842
6853
|
collectionKey: args.collection.key,
|
|
6843
6854
|
pagesCompleted: pages,
|
|
@@ -6865,15 +6876,19 @@ const singleRequestCheckpoint = (args) => ({
|
|
|
6865
6876
|
objectCount: args.result.upserted,
|
|
6866
6877
|
lastSynced: Date.now()
|
|
6867
6878
|
});
|
|
6868
|
-
const parentIdsFromCache = (deps, collection) => {
|
|
6879
|
+
const parentIdsFromCache = (deps, collection, since) => {
|
|
6869
6880
|
const table = collection.table ?? collection.key;
|
|
6870
6881
|
const orderBy = collection.sinceField ? `${collection.sinceField} DESC, id` : "id";
|
|
6871
|
-
|
|
6882
|
+
const sinceFilter = since != null && collection.sinceField ? ` WHERE ${collection.sinceField} >= ${since}` : "";
|
|
6883
|
+
return deps.cache.query(`SELECT id FROM ${table}${sinceFilter} ORDER BY ${orderBy}`).map((row) => String(row.id));
|
|
6872
6884
|
};
|
|
6873
6885
|
const runSync = async (deps, opts) => {
|
|
6874
6886
|
const { collection } = opts;
|
|
6875
6887
|
if (opts.since != null && collection.sinceField === void 0) throw new Error(`Collection ${collection.key} does not support --since`);
|
|
6876
|
-
if (opts.since != null && collection.requiresFullInitialSync
|
|
6888
|
+
if (opts.since != null && collection.requiresFullInitialSync) {
|
|
6889
|
+
const storedState = deps.cache.getCursor(collection.key);
|
|
6890
|
+
if (storedState?.lastSynced == null || decodePartialSyncCursor(storedState.cursor)) throw new Error(`Collection ${collection.key} requires a full initial sync before --since`);
|
|
6891
|
+
}
|
|
6877
6892
|
const credits = { spent: 0 };
|
|
6878
6893
|
let pages = 0;
|
|
6879
6894
|
let upserted = 0;
|
|
@@ -6899,13 +6914,14 @@ const runSync = async (deps, opts) => {
|
|
|
6899
6914
|
const parentCollection = collectionByKey(collection.parentCollection);
|
|
6900
6915
|
if (!parentCollection) throw new Error(`Missing parent collection ${collection.parentCollection}`);
|
|
6901
6916
|
const storedPartialCursor = decodePartialSyncCursor(deps.cache.getCursor(collection.key)?.cursor);
|
|
6902
|
-
let activePartialCursor = storedPartialCursor
|
|
6917
|
+
let activePartialCursor = storedPartialCursor && partialCursorMatchesSince(storedPartialCursor, opts.since) ? storedPartialCursor : void 0;
|
|
6903
6918
|
if (activePartialCursor?.phase !== "parent" && activePartialCursor?.phase !== "child") activePartialCursor = void 0;
|
|
6919
|
+
const resumedPartialCursor = activePartialCursor;
|
|
6904
6920
|
const parentIds = [];
|
|
6905
6921
|
const parentPersistsCheckpoint = parentCollection.requests.length === 1;
|
|
6906
6922
|
let parentCheckpoint;
|
|
6907
6923
|
if (activePartialCursor?.phase === "child") {
|
|
6908
|
-
const cachedParentIds = parentIdsFromCache(deps, parentCollection);
|
|
6924
|
+
const cachedParentIds = parentIdsFromCache(deps, parentCollection, opts.since);
|
|
6909
6925
|
if (activePartialCursor.parentId && cachedParentIds.includes(activePartialCursor.parentId)) parentIds.push(...cachedParentIds);
|
|
6910
6926
|
else activePartialCursor = void 0;
|
|
6911
6927
|
}
|
|
@@ -6922,6 +6938,8 @@ const runSync = async (deps, opts) => {
|
|
|
6922
6938
|
collectIds: true,
|
|
6923
6939
|
resumeCursor: activePartialCursor?.phase === "parent" && activePartialCursor.requestIndex === requestIndex ? activePartialCursor : void 0,
|
|
6924
6940
|
allowPartialResume: true,
|
|
6941
|
+
partialCursorSince: activePartialCursor?.since,
|
|
6942
|
+
preservePartialCursorOnSince: activePartialCursor?.since !== (opts.since ?? null),
|
|
6925
6943
|
partialCursorCollection: collection.key,
|
|
6926
6944
|
partialCursorPhase: "parent",
|
|
6927
6945
|
credits,
|
|
@@ -6946,7 +6964,7 @@ const runSync = async (deps, opts) => {
|
|
|
6946
6964
|
lastSynced: Date.now()
|
|
6947
6965
|
};
|
|
6948
6966
|
if (parentCheckpoint) writePendingCursor(parentCheckpoint);
|
|
6949
|
-
if (activePartialCursor?.phase === "parent") parentIds.splice(0, parentIds.length, ...parentIdsFromCache(deps, parentCollection));
|
|
6967
|
+
if (activePartialCursor?.phase === "parent") parentIds.splice(0, parentIds.length, ...parentIdsFromCache(deps, parentCollection, opts.since));
|
|
6950
6968
|
}
|
|
6951
6969
|
let reachedResumeParent = activePartialCursor?.phase !== "child";
|
|
6952
6970
|
for (const parentId of parentIds) {
|
|
@@ -6965,6 +6983,8 @@ const runSync = async (deps, opts) => {
|
|
|
6965
6983
|
useCheckpoint: false,
|
|
6966
6984
|
resumeCursor: activePartialCursor?.phase === "child" && activePartialCursor.parentId === parentId && activePartialCursor.requestIndex === requestIndex ? activePartialCursor : void 0,
|
|
6967
6985
|
allowPartialResume: true,
|
|
6986
|
+
partialCursorSince: activePartialCursor?.since,
|
|
6987
|
+
preservePartialCursorOnSince: activePartialCursor?.since !== (opts.since ?? null),
|
|
6968
6988
|
partialCursorCollection: collection.key,
|
|
6969
6989
|
partialCursorPhase: "child",
|
|
6970
6990
|
parentId,
|
|
@@ -6980,7 +7000,7 @@ const runSync = async (deps, opts) => {
|
|
|
6980
7000
|
if (activePartialCursor?.phase === "child" && activePartialCursor.parentId === parentId) activePartialCursor = void 0;
|
|
6981
7001
|
}
|
|
6982
7002
|
}
|
|
6983
|
-
pendingCursors.push({
|
|
7003
|
+
if (!preservesBroaderPartialOnSince(resumedPartialCursor, opts.since, stoppedReason)) pendingCursors.push({
|
|
6984
7004
|
collection: collection.key,
|
|
6985
7005
|
cursor: null,
|
|
6986
7006
|
objectCount: upserted,
|
|
@@ -6989,7 +7009,7 @@ const runSync = async (deps, opts) => {
|
|
|
6989
7009
|
} else {
|
|
6990
7010
|
const useCheckpoint = collection.requests.length === 1;
|
|
6991
7011
|
const partialCursor = decodePartialSyncCursor(deps.cache.getCursor(collection.key)?.cursor ?? null);
|
|
6992
|
-
const activePartialCursor = partialCursor && partialCursor
|
|
7012
|
+
const activePartialCursor = partialCursor && partialCursorMatchesSince(partialCursor, opts.since) && partialCursor.requestIndex < collection.requests.length ? partialCursor : void 0;
|
|
6993
7013
|
for (const [requestIndex, request] of collection.requests.entries()) {
|
|
6994
7014
|
if (activePartialCursor && requestIndex < activePartialCursor.requestIndex) continue;
|
|
6995
7015
|
const result = await walkRequest({
|
|
@@ -7001,6 +7021,8 @@ const runSync = async (deps, opts) => {
|
|
|
7001
7021
|
useCheckpoint,
|
|
7002
7022
|
resumeCursor: activePartialCursor?.requestIndex === requestIndex ? activePartialCursor : void 0,
|
|
7003
7023
|
allowPartialResume: true,
|
|
7024
|
+
partialCursorSince: activePartialCursor?.since,
|
|
7025
|
+
preservePartialCursorOnSince: activePartialCursor?.since !== (opts.since ?? null),
|
|
7004
7026
|
credits,
|
|
7005
7027
|
pageDelayState,
|
|
7006
7028
|
rateLimit: opts.rateLimit,
|
|
@@ -7010,12 +7032,12 @@ const runSync = async (deps, opts) => {
|
|
|
7010
7032
|
onProgress: opts.onProgress
|
|
7011
7033
|
});
|
|
7012
7034
|
addResult(result);
|
|
7013
|
-
if (useCheckpoint) pendingCursors.push(singleRequestCheckpoint({
|
|
7035
|
+
if (!preservesBroaderPartialOnSince(activePartialCursor, opts.since, result.stoppedReason) && useCheckpoint) pendingCursors.push(singleRequestCheckpoint({
|
|
7014
7036
|
collection,
|
|
7015
7037
|
result
|
|
7016
7038
|
}));
|
|
7017
7039
|
}
|
|
7018
|
-
if (!useCheckpoint) pendingCursors.push({
|
|
7040
|
+
if (!(useCheckpoint || preservesBroaderPartialOnSince(activePartialCursor, opts.since, stoppedReason))) pendingCursors.push({
|
|
7019
7041
|
collection: collection.key,
|
|
7020
7042
|
cursor: null,
|
|
7021
7043
|
objectCount: upserted,
|
|
@@ -7528,6 +7550,30 @@ const { costBPSForCategory: costBPSForCategory$1 } = createPricing([
|
|
|
7528
7550
|
"unitPriceUSD": .015,
|
|
7529
7551
|
"featureId": "user_interaction_create_request"
|
|
7530
7552
|
},
|
|
7553
|
+
{
|
|
7554
|
+
"key": "page_analytics_read",
|
|
7555
|
+
"label": "Page Analytics: Read",
|
|
7556
|
+
"description": "Read LinkedIn company Page analytics through the raw LinkedIn passthrough.",
|
|
7557
|
+
"unit": "per_resource",
|
|
7558
|
+
"unitPriceUSD": .02,
|
|
7559
|
+
"featureId": "user_read_resource"
|
|
7560
|
+
},
|
|
7561
|
+
{
|
|
7562
|
+
"key": "page_invite_create",
|
|
7563
|
+
"label": "Page Invite: Create",
|
|
7564
|
+
"description": "Invite LinkedIn users to follow a company Page.",
|
|
7565
|
+
"unit": "per_request",
|
|
7566
|
+
"unitPriceUSD": .015,
|
|
7567
|
+
"featureId": "user_interaction_create_request"
|
|
7568
|
+
},
|
|
7569
|
+
{
|
|
7570
|
+
"key": "proxy_passthrough",
|
|
7571
|
+
"label": "Proxy Passthrough",
|
|
7572
|
+
"description": "Forward a raw LinkedIn passthrough envelope with conservative write-tier pricing.",
|
|
7573
|
+
"unit": "per_request",
|
|
7574
|
+
"unitPriceUSD": .2,
|
|
7575
|
+
"featureId": "content_create_with_url_request"
|
|
7576
|
+
},
|
|
7531
7577
|
{
|
|
7532
7578
|
"key": "following_read",
|
|
7533
7579
|
"label": "Following/Followers: Read",
|
|
@@ -10450,6 +10496,54 @@ const LINKEDIN_OPERATIONS = [
|
|
|
10450
10496
|
}
|
|
10451
10497
|
]
|
|
10452
10498
|
},
|
|
10499
|
+
{
|
|
10500
|
+
schemaBaseName: "PageVisitors",
|
|
10501
|
+
hasBody: false,
|
|
10502
|
+
config: {
|
|
10503
|
+
command: "visitors",
|
|
10504
|
+
group: "page",
|
|
10505
|
+
root: false,
|
|
10506
|
+
summary: "Read company Page visitor analytics",
|
|
10507
|
+
paramLabels: { companyId: "company" },
|
|
10508
|
+
targetKind: "company"
|
|
10509
|
+
},
|
|
10510
|
+
endpoint: {
|
|
10511
|
+
method: "GET",
|
|
10512
|
+
path: "/page/:companyId/visitors",
|
|
10513
|
+
upstreamMethod: "POST",
|
|
10514
|
+
upstreamPath: "/v2/{account_id}/linkedin/",
|
|
10515
|
+
capability: "read",
|
|
10516
|
+
mutates: false,
|
|
10517
|
+
pagination: { style: "none" },
|
|
10518
|
+
costBPS: 200,
|
|
10519
|
+
billingCategory: "page_analytics_read",
|
|
10520
|
+
kind: "page"
|
|
10521
|
+
},
|
|
10522
|
+
parameters: [
|
|
10523
|
+
{
|
|
10524
|
+
name: "since",
|
|
10525
|
+
in: "query",
|
|
10526
|
+
required: false,
|
|
10527
|
+
kind: "string",
|
|
10528
|
+
description: "Start of the analytics window as an ISO timestamp"
|
|
10529
|
+
},
|
|
10530
|
+
{
|
|
10531
|
+
name: "until",
|
|
10532
|
+
in: "query",
|
|
10533
|
+
required: false,
|
|
10534
|
+
kind: "string",
|
|
10535
|
+
description: "End of the analytics window as an ISO timestamp"
|
|
10536
|
+
},
|
|
10537
|
+
{
|
|
10538
|
+
name: "companyId",
|
|
10539
|
+
in: "path",
|
|
10540
|
+
required: true,
|
|
10541
|
+
kind: "string",
|
|
10542
|
+
valueHint: "company",
|
|
10543
|
+
description: "Company Page selector: company_id:ID, a company URL, or a vanity; supplied by --page or config"
|
|
10544
|
+
}
|
|
10545
|
+
]
|
|
10546
|
+
},
|
|
10453
10547
|
{
|
|
10454
10548
|
schemaBaseName: "PostsComments",
|
|
10455
10549
|
hasBody: false,
|
|
@@ -10892,7 +10986,7 @@ const LINKEDIN_OPERATIONS = [
|
|
|
10892
10986
|
summary: "List a user's LinkedIn connections",
|
|
10893
10987
|
itemUrlTemplate: "https://www.linkedin.com/in/{id}",
|
|
10894
10988
|
paramLabels: { user_id: "target" },
|
|
10895
|
-
|
|
10989
|
+
optionalPositionalQueryParams: ["user_id"],
|
|
10896
10990
|
outputGuardMessage: "Unexpected LinkedIn connections response. Expected a relations list.",
|
|
10897
10991
|
resolveResourceParams: ["user_id"],
|
|
10898
10992
|
targetKind: "profile"
|
|
@@ -10932,10 +11026,10 @@ const LINKEDIN_OPERATIONS = [
|
|
|
10932
11026
|
{
|
|
10933
11027
|
name: "user_id",
|
|
10934
11028
|
in: "query",
|
|
10935
|
-
required:
|
|
11029
|
+
required: false,
|
|
10936
11030
|
kind: "string",
|
|
10937
11031
|
valueHint: "target",
|
|
10938
|
-
description: "
|
|
11032
|
+
description: "Optional profile target: @public-identifier, profile_id:<id>, a LinkedIn profile URL, or a profile URN. Omit for account-level connections"
|
|
10939
11033
|
},
|
|
10940
11034
|
{
|
|
10941
11035
|
name: "filter",
|
|
@@ -11061,6 +11155,44 @@ const LINKEDIN_OPERATIONS = [
|
|
|
11061
11155
|
description: "Conversation target: chat_id:<id> or a LinkedIn messaging thread URL"
|
|
11062
11156
|
}]
|
|
11063
11157
|
},
|
|
11158
|
+
{
|
|
11159
|
+
schemaBaseName: "PageInvite",
|
|
11160
|
+
hasBody: true,
|
|
11161
|
+
config: {
|
|
11162
|
+
command: "invite",
|
|
11163
|
+
group: "page",
|
|
11164
|
+
root: false,
|
|
11165
|
+
summary: "Invite users to follow a company Page",
|
|
11166
|
+
bodyParams: { invitees: {
|
|
11167
|
+
kind: "array",
|
|
11168
|
+
positional: true,
|
|
11169
|
+
required: true,
|
|
11170
|
+
valueHint: "user",
|
|
11171
|
+
description: "Users to invite: @username, profile_id:<id>, profile URL, or profile URN"
|
|
11172
|
+
} },
|
|
11173
|
+
paramLabels: { companyId: "company" },
|
|
11174
|
+
targetKind: "company"
|
|
11175
|
+
},
|
|
11176
|
+
endpoint: {
|
|
11177
|
+
method: "POST",
|
|
11178
|
+
path: "/page/:companyId/invite",
|
|
11179
|
+
upstreamPath: "/v2/{account_id}/linkedin/",
|
|
11180
|
+
capability: "write",
|
|
11181
|
+
mutates: true,
|
|
11182
|
+
pagination: { style: "none" },
|
|
11183
|
+
costBPS: 150,
|
|
11184
|
+
billingCategory: "page_invite_create",
|
|
11185
|
+
kind: "page"
|
|
11186
|
+
},
|
|
11187
|
+
parameters: [{
|
|
11188
|
+
name: "companyId",
|
|
11189
|
+
in: "path",
|
|
11190
|
+
required: true,
|
|
11191
|
+
kind: "string",
|
|
11192
|
+
valueHint: "company",
|
|
11193
|
+
description: "Company Page selector: company_id:ID, a company URL, or a vanity; supplied by --page or config"
|
|
11194
|
+
}]
|
|
11195
|
+
},
|
|
11064
11196
|
{
|
|
11065
11197
|
schemaBaseName: "PostsPost",
|
|
11066
11198
|
hasBody: true,
|
|
@@ -11173,6 +11305,29 @@ const LINKEDIN_OPERATIONS = [
|
|
|
11173
11305
|
},
|
|
11174
11306
|
parameters: []
|
|
11175
11307
|
},
|
|
11308
|
+
{
|
|
11309
|
+
schemaBaseName: "LinkedinProxy",
|
|
11310
|
+
hasBody: true,
|
|
11311
|
+
config: {
|
|
11312
|
+
command: "proxy",
|
|
11313
|
+
group: "linkedin",
|
|
11314
|
+
root: true,
|
|
11315
|
+
summary: "Forward a raw request to LinkedIn (advanced escape hatch)",
|
|
11316
|
+
bodyTextArg: "body"
|
|
11317
|
+
},
|
|
11318
|
+
endpoint: {
|
|
11319
|
+
method: "POST",
|
|
11320
|
+
path: "/proxy",
|
|
11321
|
+
upstreamPath: "/v2/{account_id}/linkedin/",
|
|
11322
|
+
capability: "write",
|
|
11323
|
+
mutates: true,
|
|
11324
|
+
pagination: { style: "none" },
|
|
11325
|
+
costBPS: 2e3,
|
|
11326
|
+
billingCategory: "proxy_passthrough",
|
|
11327
|
+
kind: "proxy"
|
|
11328
|
+
},
|
|
11329
|
+
parameters: []
|
|
11330
|
+
},
|
|
11176
11331
|
{
|
|
11177
11332
|
schemaBaseName: "RequestsSend",
|
|
11178
11333
|
hasBody: true,
|
|
@@ -11429,6 +11584,12 @@ const CompaniesJobsOutput = object({
|
|
|
11429
11584
|
total_count: number().optional(),
|
|
11430
11585
|
next_cursor: string().optional()
|
|
11431
11586
|
}).passthrough();
|
|
11587
|
+
const PageVisitorsInput = object({
|
|
11588
|
+
since: string().optional(),
|
|
11589
|
+
until: string().optional(),
|
|
11590
|
+
companyId: string().optional()
|
|
11591
|
+
});
|
|
11592
|
+
const PageVisitorsOutput = object({}).passthrough();
|
|
11432
11593
|
const PostsCommentsInput = object({
|
|
11433
11594
|
offset: union([string(), number()]).optional(),
|
|
11434
11595
|
limit: union([string(), number()]).optional(),
|
|
@@ -11509,7 +11670,7 @@ const UserPostsOutput = object({
|
|
|
11509
11670
|
const UserConnectionsInput = object({
|
|
11510
11671
|
limit: union([string(), number()]).optional(),
|
|
11511
11672
|
cursor: string().optional(),
|
|
11512
|
-
user_id: string(),
|
|
11673
|
+
user_id: string().optional(),
|
|
11513
11674
|
filter: string().optional()
|
|
11514
11675
|
});
|
|
11515
11676
|
const UserConnectionsOutput = object({
|
|
@@ -11558,6 +11719,16 @@ const MessagesMessageOutput = object({
|
|
|
11558
11719
|
unknown()
|
|
11559
11720
|
])
|
|
11560
11721
|
}).passthrough();
|
|
11722
|
+
const PageInviteInput = object({
|
|
11723
|
+
companyId: string().optional(),
|
|
11724
|
+
body: string().optional(),
|
|
11725
|
+
invitees: union([array(string()), string()]).optional()
|
|
11726
|
+
});
|
|
11727
|
+
const PageInviteContractInput = PageInviteInput.extend({
|
|
11728
|
+
body: object({ invitees: array(string()) }).passthrough().optional(),
|
|
11729
|
+
invitees: union([array(string()), string()]).optional()
|
|
11730
|
+
});
|
|
11731
|
+
const PageInviteOutput = object({}).passthrough();
|
|
11561
11732
|
const PostsPostInput = object({
|
|
11562
11733
|
body: string().optional(),
|
|
11563
11734
|
text: string().optional(),
|
|
@@ -11640,6 +11811,9 @@ const PostsReactContractInput = PostsReactInput.extend({
|
|
|
11640
11811
|
reaction_type: string().optional()
|
|
11641
11812
|
});
|
|
11642
11813
|
const PostsReactOutput = object({ object: _enum(["PostReactionAdded"]) }).passthrough();
|
|
11814
|
+
const LinkedinProxyInput = object({ body: string().optional() });
|
|
11815
|
+
const LinkedinProxyContractInput = LinkedinProxyInput.extend({ body: object({}).passthrough().optional() });
|
|
11816
|
+
const LinkedinProxyOutput = object({}).passthrough();
|
|
11643
11817
|
const RequestsSendInput = object({
|
|
11644
11818
|
id: string(),
|
|
11645
11819
|
body: string().optional(),
|
|
@@ -11654,6 +11828,8 @@ const schemas$1 = {
|
|
|
11654
11828
|
CompaniesCompanyOutput,
|
|
11655
11829
|
CompaniesJobsInput,
|
|
11656
11830
|
CompaniesJobsOutput,
|
|
11831
|
+
PageVisitorsInput,
|
|
11832
|
+
PageVisitorsOutput,
|
|
11657
11833
|
PostsCommentsInput,
|
|
11658
11834
|
PostsCommentsOutput,
|
|
11659
11835
|
PostsReactionsInput,
|
|
@@ -11679,6 +11855,9 @@ const schemas$1 = {
|
|
|
11679
11855
|
MessagesMessageInput,
|
|
11680
11856
|
MessagesMessageContractInput,
|
|
11681
11857
|
MessagesMessageOutput,
|
|
11858
|
+
PageInviteInput,
|
|
11859
|
+
PageInviteContractInput,
|
|
11860
|
+
PageInviteOutput,
|
|
11682
11861
|
PostsPostInput,
|
|
11683
11862
|
PostsPostContractInput,
|
|
11684
11863
|
PostsPostOutput,
|
|
@@ -11688,6 +11867,9 @@ const schemas$1 = {
|
|
|
11688
11867
|
PostsReactInput,
|
|
11689
11868
|
PostsReactContractInput,
|
|
11690
11869
|
PostsReactOutput,
|
|
11870
|
+
LinkedinProxyInput,
|
|
11871
|
+
LinkedinProxyContractInput,
|
|
11872
|
+
LinkedinProxyOutput,
|
|
11691
11873
|
RequestsSendInput,
|
|
11692
11874
|
RequestsSendContractInput: RequestsSendInput.extend({
|
|
11693
11875
|
body: object({
|
|
@@ -11801,6 +11983,12 @@ const targetKindsFor = (op) => {
|
|
|
11801
11983
|
if (kinds === void 0) throw new Error(`Missing targetKind for ${commandPathFor(op)}.`);
|
|
11802
11984
|
return Array.isArray(kinds) ? kinds : [kinds];
|
|
11803
11985
|
};
|
|
11986
|
+
const isSeededPageCompanyParam = (op, param) => op.config.group === "page" && param.in === "path" && param.name === "companyId";
|
|
11987
|
+
const pageParametersForRuntimeInput = (op) => op.config.group === "page" ? op.parameters.filter((param) => !isSeededPageCompanyParam(op, param)) : op.parameters;
|
|
11988
|
+
const pageSelectorForTargetResolution = (page) => {
|
|
11989
|
+
if (NumericCompanyId.safeParse(page).success || !(page.startsWith("company_id:") || page.startsWith("http") || page.startsWith("urn:"))) return `company_id:${page}`;
|
|
11990
|
+
return page;
|
|
11991
|
+
};
|
|
11804
11992
|
const positionalDisplayKeyFor = (op, param) => op.config.paramLabels?.[param.name] ?? flagName(param.name);
|
|
11805
11993
|
const idempotencyFor$1 = (op) => {
|
|
11806
11994
|
if (op.endpoint.capability === "read") return {
|
|
@@ -11850,7 +12038,7 @@ const exampleCommandFor$1 = (op, root) => {
|
|
|
11850
12038
|
op.config.command
|
|
11851
12039
|
];
|
|
11852
12040
|
for (const param of op.parameters) {
|
|
11853
|
-
if (!(param.in === "path" || positionalQuery.has(param.name)) || op.config.ownAccountPathParams?.[param.name] === "optional") continue;
|
|
12041
|
+
if (!(param.in === "path" || positionalQuery.has(param.name)) || op.config.ownAccountPathParams?.[param.name] === "optional" || isSeededPageCompanyParam(op, param)) continue;
|
|
11854
12042
|
const hint = `<${positionalDisplayKeyFor(op, param)}>`;
|
|
11855
12043
|
parts.push(param.in === "path" || param.kind !== "array" ? hint : `${hint}...`);
|
|
11856
12044
|
}
|
|
@@ -11944,10 +12132,21 @@ const linkedinRuntimeAdapter = {
|
|
|
11944
12132
|
includeIdField: true,
|
|
11945
12133
|
maximumItemsHintFor: maximumItemsHintFor$1,
|
|
11946
12134
|
queryArrayMode: (op, param) => param.kind === "array" || op.schemaBaseName === "UserProfile" && param.name === "with_sections" ? "repeat" : "set",
|
|
12135
|
+
parametersForArgs: pageParametersForRuntimeInput,
|
|
12136
|
+
parametersForInput: pageParametersForRuntimeInput,
|
|
11947
12137
|
setUnexposedFieldPresets: true,
|
|
11948
12138
|
signaturePartFor: signaturePartFor$1,
|
|
11949
12139
|
stdinBodyTextRequired: (op) => op.config.bodyTextArg !== void 0 && op.config.bodyTextRequired !== false,
|
|
11950
12140
|
targetKinds: targetKindsFor,
|
|
12141
|
+
seedPathValue: async ({ op, param, args, deps }) => {
|
|
12142
|
+
if (op.config.group !== "page" || param.name !== "companyId") return;
|
|
12143
|
+
const page = typeof args.page === "string" ? args.page : void 0;
|
|
12144
|
+
return pageSelectorForTargetResolution(await deps.resolveLinkedinPage({ page }));
|
|
12145
|
+
},
|
|
12146
|
+
extraArgsFor: (op) => op.config.group === "page" ? { page: {
|
|
12147
|
+
type: "string",
|
|
12148
|
+
description: "Company Page selector: company_id:<id>, a company URL, or a vanity; overrides the configured default Page"
|
|
12149
|
+
} } : void 0,
|
|
11951
12150
|
useGETMaxUsageUnitsEstimate: true,
|
|
11952
12151
|
transformPathResolution: async ({ args, deps, op, param, rawArgs, resolution, resolveSelectedAccount }) => {
|
|
11953
12152
|
if ((op.schemaBaseName === "PostsComments" || op.schemaBaseName === "PostsReactions") && param.name === "post_id") {
|
|
@@ -11992,6 +12191,26 @@ const linkedinRuntimeAdapter = {
|
|
|
11992
12191
|
}]
|
|
11993
12192
|
};
|
|
11994
12193
|
}
|
|
12194
|
+
if (op.config.group === "page" && param.name === "companyId" && !NumericCompanyId.safeParse(resolution.id).success) {
|
|
12195
|
+
const companyAccount = await resolveSelectedAccount();
|
|
12196
|
+
const company = await deps.api.get(`linkedin/companies/${encodeURIComponent(resolution.id)}`, { headers: {
|
|
12197
|
+
...await deps.commandHeaders({
|
|
12198
|
+
headers: args.header,
|
|
12199
|
+
rawArgs
|
|
12200
|
+
}),
|
|
12201
|
+
...companyAccount.selector === void 0 ? {} : { "x-social-account-id": companyAccount.selector }
|
|
12202
|
+
} }).json();
|
|
12203
|
+
const companyId = NumericCompanyId.safeParse(companyProfileIdFrom(company));
|
|
12204
|
+
if (!companyId.success) throw new Error(`Could not resolve LinkedIn company ID for ${resolution.id}.`);
|
|
12205
|
+
return {
|
|
12206
|
+
pathValue: companyId.data,
|
|
12207
|
+
records: [{
|
|
12208
|
+
...resolution.record,
|
|
12209
|
+
id: companyId.data,
|
|
12210
|
+
source: "lookup"
|
|
12211
|
+
}]
|
|
12212
|
+
};
|
|
12213
|
+
}
|
|
11995
12214
|
},
|
|
11996
12215
|
afterQueryParams: ({ op, params, pathResolutions }) => {
|
|
11997
12216
|
if (op.schemaBaseName === "UserPosts" && pathResolutions.identifier?.kind === "company") params.set("is_company", "true");
|
|
@@ -12045,6 +12264,20 @@ const linkedinRuntimeAdapter = {
|
|
|
12045
12264
|
});
|
|
12046
12265
|
input.post_as = companyId.data;
|
|
12047
12266
|
}
|
|
12267
|
+
if (op.schemaBaseName === "PageInvite" && input.invitees !== void 0) {
|
|
12268
|
+
const invitees = Array.isArray(input.invitees) ? input.invitees : [input.invitees];
|
|
12269
|
+
const profileIds = [];
|
|
12270
|
+
for (const invitee of invitees) {
|
|
12271
|
+
const resolution = await deps.resolveTarget("invitee", invitee, {
|
|
12272
|
+
platform: "linkedin",
|
|
12273
|
+
account,
|
|
12274
|
+
expect: ["profile"]
|
|
12275
|
+
});
|
|
12276
|
+
resolved.push(resolution.record);
|
|
12277
|
+
profileIds.push(resolution.id);
|
|
12278
|
+
}
|
|
12279
|
+
input.invitees = profileIds;
|
|
12280
|
+
}
|
|
12048
12281
|
},
|
|
12049
12282
|
transformOutput: ({ body, data, op }) => {
|
|
12050
12283
|
const outputData = dataWithAuditableWriteTarget(op, data, body);
|
|
@@ -12514,7 +12747,7 @@ const buildSync$1 = (deps, platform) => defineCommand({
|
|
|
12514
12747
|
const buildSql$1 = (deps, platform) => defineCommand({
|
|
12515
12748
|
meta: {
|
|
12516
12749
|
name: "sql",
|
|
12517
|
-
description: "Query your
|
|
12750
|
+
description: "Query your local mirror with read-only SQL (free; run with no query to print tables)",
|
|
12518
12751
|
capability: "read",
|
|
12519
12752
|
mutates: false,
|
|
12520
12753
|
schemaContract: {
|
|
@@ -12603,7 +12836,6 @@ const buildSql$1 = (deps, platform) => defineCommand({
|
|
|
12603
12836
|
}
|
|
12604
12837
|
});
|
|
12605
12838
|
} catch (error) {
|
|
12606
|
-
if (error instanceof NeverSyncedError) throw new UsageError(incompleteSyncMessageFor(readonlyCache, error));
|
|
12607
12839
|
throw new UsageError(`Query failed (cache is read-only): ${error.message}`);
|
|
12608
12840
|
}
|
|
12609
12841
|
} finally {
|
|
@@ -12915,12 +13147,6 @@ const sentMessageRow = (args) => {
|
|
|
12915
13147
|
raw: JSON.stringify(raw)
|
|
12916
13148
|
};
|
|
12917
13149
|
};
|
|
12918
|
-
const incompleteSyncMessageFor = (cache, error) => {
|
|
12919
|
-
if (!error.tables.includes("li_conversations")) return error.message;
|
|
12920
|
-
const rows = cache.query("SELECT count(*) AS count FROM li_conversations_raw")[0]?.count;
|
|
12921
|
-
if (typeof rows !== "number" || rows <= 0) return error.message;
|
|
12922
|
-
return `li_conversations has ${rows.toLocaleString("en-US")} rows from an incomplete messages sync (rate-limited before finishing) — run \`social linkedin sync messages\` to complete it before querying.`;
|
|
12923
|
-
};
|
|
12924
13150
|
const buildMessageDelete = (deps) => {
|
|
12925
13151
|
return defineCommand({
|
|
12926
13152
|
meta: commandMeta$2({
|
|
@@ -13583,6 +13809,7 @@ createLinkedinCommands({
|
|
|
13583
13809
|
printResult: unconfigured$1,
|
|
13584
13810
|
resolveAccount: unconfigured$1,
|
|
13585
13811
|
resolveAccountId: unconfigured$1,
|
|
13812
|
+
resolveLinkedinPage: unconfigured$1,
|
|
13586
13813
|
resolveOwnLinkedinAccount: unconfigured$1
|
|
13587
13814
|
});
|
|
13588
13815
|
//#endregion
|
|
@@ -13882,6 +14109,7 @@ const xProfileExtractors = {
|
|
|
13882
14109
|
"GET /users/:id": extractProfile,
|
|
13883
14110
|
"GET /users/:id/followers": extractList,
|
|
13884
14111
|
"GET /users/:id/following": extractList,
|
|
14112
|
+
"GET /users/:id/timelines/reverse_chronological": extractIncludesUsers,
|
|
13885
14113
|
"GET /users/by/username/:username": extractProfile
|
|
13886
14114
|
};
|
|
13887
14115
|
const X_OPERATIONS = [
|
|
@@ -16636,11 +16864,10 @@ const defineOperationCommand = (op, deps) => defineRuntimeOperationCommand(xRunt
|
|
|
16636
16864
|
//#endregion
|
|
16637
16865
|
//#region ../../packages/x/src/cli/commands.ts
|
|
16638
16866
|
const hiddenRootCommands = new Set(["search"]);
|
|
16639
|
-
const suppressedRootCommands = new Set(["bookmarks"]);
|
|
16867
|
+
const suppressedRootCommands = new Set(["bookmarks", "timeline"]);
|
|
16640
16868
|
const rootCommandOrder = [
|
|
16641
16869
|
"profile",
|
|
16642
16870
|
"tweets",
|
|
16643
|
-
"timeline",
|
|
16644
16871
|
"mentions",
|
|
16645
16872
|
"liked",
|
|
16646
16873
|
"followers",
|
|
@@ -16942,7 +17169,7 @@ const buildSql = (deps, platform) => {
|
|
|
16942
17169
|
return defineCommand({
|
|
16943
17170
|
meta: commandMeta$1({
|
|
16944
17171
|
name: "sql",
|
|
16945
|
-
description: "Query your
|
|
17172
|
+
description: "Query your local mirror with read-only SQL (free; run with no query to print tables)",
|
|
16946
17173
|
method: "GET",
|
|
16947
17174
|
capability: "read",
|
|
16948
17175
|
mutates: false,
|
|
@@ -17000,7 +17227,6 @@ const buildSql = (deps, platform) => {
|
|
|
17000
17227
|
}
|
|
17001
17228
|
});
|
|
17002
17229
|
} catch (error) {
|
|
17003
|
-
if (error instanceof NeverSyncedError) throw new UsageError(error.message);
|
|
17004
17230
|
throw new UsageError(`Query failed (cache is read-only): ${error.message}`);
|
|
17005
17231
|
}
|
|
17006
17232
|
} finally {
|
|
@@ -21173,7 +21399,7 @@ function createEnv(opts) {
|
|
|
21173
21399
|
}
|
|
21174
21400
|
//#endregion
|
|
21175
21401
|
//#region package.json
|
|
21176
|
-
var version$1 = "0.
|
|
21402
|
+
var version$1 = "0.10.1";
|
|
21177
21403
|
//#endregion
|
|
21178
21404
|
//#region src/lib/env.ts
|
|
21179
21405
|
const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
|
|
@@ -21587,8 +21813,17 @@ const authWhoamiOutput = async (client, apiURL = env.SOCIAL_API_URL) => {
|
|
|
21587
21813
|
}
|
|
21588
21814
|
};
|
|
21589
21815
|
const MAX_CACHE_TTL_SECONDS = 10080 * 60;
|
|
21590
|
-
const CLIConfig = object({
|
|
21591
|
-
|
|
21816
|
+
const CLIConfig = object({
|
|
21817
|
+
cache: object({ cacheTTLSeconds: number().int().min(0).max(MAX_CACHE_TTL_SECONDS).default(900) }).default({ cacheTTLSeconds: 900 }),
|
|
21818
|
+
defaults: object({
|
|
21819
|
+
account: string().optional(),
|
|
21820
|
+
linkedinPage: string().optional()
|
|
21821
|
+
}).default({})
|
|
21822
|
+
});
|
|
21823
|
+
const defaultCLIConfig = () => ({
|
|
21824
|
+
cache: { cacheTTLSeconds: 900 },
|
|
21825
|
+
defaults: {}
|
|
21826
|
+
});
|
|
21592
21827
|
const configPath = () => join(process.env.HOME ?? homedir(), ".social", "config.json");
|
|
21593
21828
|
const parseCLIConfig = (serialized) => CLIConfig.parse(JSON.parse(serialized));
|
|
21594
21829
|
const readCLIConfig = async () => {
|
|
@@ -21614,6 +21849,30 @@ const setCacheTTLSeconds = async (cacheTTLSeconds) => {
|
|
|
21614
21849
|
await writeCLIConfig(next);
|
|
21615
21850
|
return next;
|
|
21616
21851
|
};
|
|
21852
|
+
const setDefaultAccount = async (selector) => {
|
|
21853
|
+
const config = await readCLIConfig();
|
|
21854
|
+
const next = {
|
|
21855
|
+
...config,
|
|
21856
|
+
defaults: {
|
|
21857
|
+
...config.defaults,
|
|
21858
|
+
account: selector
|
|
21859
|
+
}
|
|
21860
|
+
};
|
|
21861
|
+
await writeCLIConfig(next);
|
|
21862
|
+
return next;
|
|
21863
|
+
};
|
|
21864
|
+
const setDefaultLinkedinPage = async (companyId) => {
|
|
21865
|
+
const config = await readCLIConfig();
|
|
21866
|
+
const next = {
|
|
21867
|
+
...config,
|
|
21868
|
+
defaults: {
|
|
21869
|
+
...config.defaults,
|
|
21870
|
+
linkedinPage: companyId
|
|
21871
|
+
}
|
|
21872
|
+
};
|
|
21873
|
+
await writeCLIConfig(next);
|
|
21874
|
+
return next;
|
|
21875
|
+
};
|
|
21617
21876
|
const cacheControlHeaderFromConfig = async () => `max-age=${(await readCLIConfig()).cache.cacheTTLSeconds}`;
|
|
21618
21877
|
//#endregion
|
|
21619
21878
|
//#region src/lib/output.ts
|
|
@@ -22123,24 +22382,70 @@ const commandMeta = (meta) => {
|
|
|
22123
22382
|
};
|
|
22124
22383
|
};
|
|
22125
22384
|
const CacheStateOutput = object({ cacheTTLSeconds: number().int().nonnegative() });
|
|
22385
|
+
const AccountDefaultOutput = object({ account: string().nullable() });
|
|
22386
|
+
const PageDefaultOutput = object({ linkedinPage: string().nullable() });
|
|
22126
22387
|
const cacheStateFrom = (config) => ({ cacheTTLSeconds: config.cache.cacheTTLSeconds });
|
|
22127
22388
|
const printCacheState = (config) => {
|
|
22128
22389
|
printData(cacheStateFrom(config));
|
|
22129
22390
|
};
|
|
22391
|
+
const printDefaultAccount = (config) => {
|
|
22392
|
+
printData({ account: config.defaults.account ?? null });
|
|
22393
|
+
};
|
|
22394
|
+
const printDefaultLinkedinPage = (config) => {
|
|
22395
|
+
printData({ linkedinPage: config.defaults.linkedinPage ?? null });
|
|
22396
|
+
};
|
|
22397
|
+
const cacheCommand = defineCommand({
|
|
22398
|
+
meta: {
|
|
22399
|
+
name: "cache",
|
|
22400
|
+
description: "Read or configure proxy cache defaults."
|
|
22401
|
+
},
|
|
22402
|
+
subCommands: { ttl: defineCommand({
|
|
22403
|
+
meta: commandMeta({
|
|
22404
|
+
name: "ttl",
|
|
22405
|
+
description: "Read or set the default cache TTL in seconds.",
|
|
22406
|
+
contract: {
|
|
22407
|
+
capability: "none",
|
|
22408
|
+
auth: {
|
|
22409
|
+
local: true,
|
|
22410
|
+
required: false
|
|
22411
|
+
},
|
|
22412
|
+
mutates: true,
|
|
22413
|
+
outputSchema: CacheStateOutput,
|
|
22414
|
+
idempotency: "idempotent",
|
|
22415
|
+
responseShaping: { formats: ["json"] },
|
|
22416
|
+
hazards: [{
|
|
22417
|
+
code: "local_config_write",
|
|
22418
|
+
message: "Writes local CLI configuration when ttl_seconds is provided."
|
|
22419
|
+
}]
|
|
22420
|
+
}
|
|
22421
|
+
}),
|
|
22422
|
+
args: { ttl_seconds: {
|
|
22423
|
+
type: "positional",
|
|
22424
|
+
required: false,
|
|
22425
|
+
description: "Cache TTL in seconds."
|
|
22426
|
+
} },
|
|
22427
|
+
run: async ({ args }) => {
|
|
22428
|
+
if (args.ttl_seconds === void 0) {
|
|
22429
|
+
printCacheState(await readCLIConfig());
|
|
22430
|
+
return;
|
|
22431
|
+
}
|
|
22432
|
+
printCacheState(await setCacheTTLSeconds(parseIntegerString("--ttl-seconds", args.ttl_seconds, {
|
|
22433
|
+
min: 0,
|
|
22434
|
+
max: MAX_CACHE_TTL_SECONDS
|
|
22435
|
+
})));
|
|
22436
|
+
}
|
|
22437
|
+
}) }
|
|
22438
|
+
});
|
|
22130
22439
|
const configCommand = defineCommand({
|
|
22131
22440
|
meta: {
|
|
22132
22441
|
name: "config",
|
|
22133
22442
|
description: "Read or configure local social CLI settings."
|
|
22134
22443
|
},
|
|
22135
|
-
subCommands: {
|
|
22136
|
-
|
|
22137
|
-
name: "cache",
|
|
22138
|
-
description: "Read or configure proxy cache defaults."
|
|
22139
|
-
},
|
|
22140
|
-
subCommands: { ttl: defineCommand({
|
|
22444
|
+
subCommands: {
|
|
22445
|
+
account: defineCommand({
|
|
22141
22446
|
meta: commandMeta({
|
|
22142
|
-
name: "
|
|
22143
|
-
description: "Read or set the default
|
|
22447
|
+
name: "account",
|
|
22448
|
+
description: "Read or set the default connected account selector.",
|
|
22144
22449
|
contract: {
|
|
22145
22450
|
capability: "none",
|
|
22146
22451
|
auth: {
|
|
@@ -22148,32 +22453,63 @@ const configCommand = defineCommand({
|
|
|
22148
22453
|
required: false
|
|
22149
22454
|
},
|
|
22150
22455
|
mutates: true,
|
|
22151
|
-
outputSchema:
|
|
22456
|
+
outputSchema: AccountDefaultOutput,
|
|
22152
22457
|
idempotency: "idempotent",
|
|
22153
22458
|
responseShaping: { formats: ["json"] },
|
|
22154
22459
|
hazards: [{
|
|
22155
22460
|
code: "local_config_write",
|
|
22156
|
-
message: "Writes local CLI configuration when
|
|
22461
|
+
message: "Writes local CLI configuration when selector is provided."
|
|
22157
22462
|
}]
|
|
22158
22463
|
}
|
|
22159
22464
|
}),
|
|
22160
|
-
args: {
|
|
22465
|
+
args: { selector: {
|
|
22161
22466
|
type: "positional",
|
|
22162
22467
|
required: false,
|
|
22163
|
-
description: "
|
|
22468
|
+
description: "Default account selector."
|
|
22164
22469
|
} },
|
|
22165
22470
|
run: async ({ args }) => {
|
|
22166
|
-
if (args.
|
|
22167
|
-
|
|
22471
|
+
if (args.selector === void 0) {
|
|
22472
|
+
printDefaultAccount(await readCLIConfig());
|
|
22168
22473
|
return;
|
|
22169
22474
|
}
|
|
22170
|
-
|
|
22171
|
-
min: 0,
|
|
22172
|
-
max: MAX_CACHE_TTL_SECONDS
|
|
22173
|
-
})));
|
|
22475
|
+
printDefaultAccount(await setDefaultAccount(String(args.selector)));
|
|
22174
22476
|
}
|
|
22175
|
-
})
|
|
22176
|
-
|
|
22477
|
+
}),
|
|
22478
|
+
cache: cacheCommand,
|
|
22479
|
+
page: defineCommand({
|
|
22480
|
+
meta: commandMeta({
|
|
22481
|
+
name: "page",
|
|
22482
|
+
description: "Read or set the default LinkedIn company Page selector.",
|
|
22483
|
+
contract: {
|
|
22484
|
+
capability: "none",
|
|
22485
|
+
auth: {
|
|
22486
|
+
local: true,
|
|
22487
|
+
required: false
|
|
22488
|
+
},
|
|
22489
|
+
mutates: true,
|
|
22490
|
+
outputSchema: PageDefaultOutput,
|
|
22491
|
+
idempotency: "idempotent",
|
|
22492
|
+
responseShaping: { formats: ["json"] },
|
|
22493
|
+
hazards: [{
|
|
22494
|
+
code: "local_config_write",
|
|
22495
|
+
message: "Writes local CLI configuration when company_id is provided."
|
|
22496
|
+
}]
|
|
22497
|
+
}
|
|
22498
|
+
}),
|
|
22499
|
+
args: { company_id: {
|
|
22500
|
+
type: "positional",
|
|
22501
|
+
required: false,
|
|
22502
|
+
description: "Default LinkedIn Page selector."
|
|
22503
|
+
} },
|
|
22504
|
+
run: async ({ args }) => {
|
|
22505
|
+
if (args.company_id === void 0) {
|
|
22506
|
+
printDefaultLinkedinPage(await readCLIConfig());
|
|
22507
|
+
return;
|
|
22508
|
+
}
|
|
22509
|
+
printDefaultLinkedinPage(await setDefaultLinkedinPage(String(args.company_id)));
|
|
22510
|
+
}
|
|
22511
|
+
})
|
|
22512
|
+
}
|
|
22177
22513
|
});
|
|
22178
22514
|
//#endregion
|
|
22179
22515
|
//#region src/billing/index.ts
|
|
@@ -34209,17 +34545,20 @@ const createCLIDeps = () => {
|
|
|
34209
34545
|
}
|
|
34210
34546
|
};
|
|
34211
34547
|
const resolveAccount = async ({ account, platform }) => {
|
|
34212
|
-
|
|
34548
|
+
const configDefault = (await readCLIConfig()).defaults.account;
|
|
34549
|
+
const selectorToTry = account ?? configDefault;
|
|
34550
|
+
if (selectorToTry) resolveAccountId({ account: selectorToTry });
|
|
34213
34551
|
const accounts = await client.accounts.list({
|
|
34214
34552
|
platform,
|
|
34215
34553
|
includeDisconnected: false
|
|
34216
34554
|
});
|
|
34217
|
-
const
|
|
34218
|
-
if (account && !
|
|
34555
|
+
const matched = selectorToTry ? accounts.find((candidate) => accountMatchesIdentifier(candidate, selectorToTry)) : void 0;
|
|
34556
|
+
if (account && !matched) throw new NotFoundError("account_not_found");
|
|
34557
|
+
const selected = matched ?? accounts[0];
|
|
34219
34558
|
return {
|
|
34220
34559
|
platform,
|
|
34221
|
-
username: selected?.username ?? (
|
|
34222
|
-
selector: selected ? accountSelectorFor(selected) :
|
|
34560
|
+
username: selected?.username ?? (selectorToTry?.startsWith("@") ? selectorToTry.slice(1) : selectorToTry ?? "default"),
|
|
34561
|
+
selector: selected ? accountSelectorFor(selected) : selectorToTry
|
|
34223
34562
|
};
|
|
34224
34563
|
};
|
|
34225
34564
|
const lookupXHandle = async (input, account) => {
|
|
@@ -34320,13 +34659,23 @@ const createCLIDeps = () => {
|
|
|
34320
34659
|
printResult: printResultWithUsage,
|
|
34321
34660
|
resolveAccount,
|
|
34322
34661
|
resolveAccountId: async (args) => resolveAccountId(args),
|
|
34662
|
+
resolveLinkedinPage: async ({ page }) => {
|
|
34663
|
+
const selectedPage = page ?? (await readCLIConfig()).defaults.linkedinPage;
|
|
34664
|
+
if (!selectedPage) throw new UsageError("No Page selected — pass --page or run `social config page <id>`");
|
|
34665
|
+
return selectedPage;
|
|
34666
|
+
},
|
|
34323
34667
|
resolveOwnXAccount: async ({ account }) => {
|
|
34668
|
+
const configDefault = (await readCLIConfig()).defaults.account;
|
|
34669
|
+
const selectorToTry = account ?? configDefault;
|
|
34670
|
+
if (selectorToTry) resolveAccountId({ account: selectorToTry });
|
|
34324
34671
|
const accounts = await client.accounts.list({
|
|
34325
34672
|
platform: "x",
|
|
34326
34673
|
includeDisconnected: false
|
|
34327
34674
|
});
|
|
34328
|
-
const
|
|
34329
|
-
if (!
|
|
34675
|
+
const matched = selectorToTry ? accounts.find((candidate) => accountMatchesIdentifier(candidate, selectorToTry)) : void 0;
|
|
34676
|
+
if (account && !matched) throw new NotFoundError("x_account_not_found");
|
|
34677
|
+
const selected = matched ?? accounts[0];
|
|
34678
|
+
if (!selected) throw new Error("x_account_not_connected");
|
|
34330
34679
|
return {
|
|
34331
34680
|
account: {
|
|
34332
34681
|
platform: "x",
|
|
@@ -34338,14 +34687,18 @@ const createCLIDeps = () => {
|
|
|
34338
34687
|
};
|
|
34339
34688
|
},
|
|
34340
34689
|
resolveOwnLinkedinAccount: async ({ account }) => {
|
|
34341
|
-
|
|
34690
|
+
const configDefault = (await readCLIConfig()).defaults.account;
|
|
34691
|
+
const selectorToTry = account ?? configDefault;
|
|
34692
|
+
if (selectorToTry) resolveAccountId({ account: selectorToTry });
|
|
34342
34693
|
const accounts = await client.accounts.list({
|
|
34343
34694
|
platform: "linkedin",
|
|
34344
34695
|
includeDisconnected: false
|
|
34345
34696
|
});
|
|
34346
|
-
const
|
|
34347
|
-
if (!
|
|
34348
|
-
const
|
|
34697
|
+
const matched = selectorToTry ? accounts.find((candidate) => accountMatchesIdentifier(candidate, selectorToTry)) : void 0;
|
|
34698
|
+
if (account && !matched) throw new NotFoundError("linkedin_account_not_found");
|
|
34699
|
+
const selected = matched ?? accounts[0];
|
|
34700
|
+
if (!selected) throw new Error("linkedin_account_not_connected");
|
|
34701
|
+
const profile = await lookupLinkedinProfile("me", selectorToTry, {
|
|
34349
34702
|
platform: "linkedin",
|
|
34350
34703
|
username: selected.username,
|
|
34351
34704
|
selector: accountSelectorFor(selected)
|