@jackwener/opencli 1.5.0 → 1.5.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/dist/browser/cdp.js +5 -0
- package/dist/browser/page.d.ts +3 -0
- package/dist/browser/page.js +24 -1
- package/dist/cli-manifest.json +465 -5
- package/dist/cli.js +34 -3
- package/dist/clis/bluesky/feeds.yaml +29 -0
- package/dist/clis/bluesky/followers.yaml +33 -0
- package/dist/clis/bluesky/following.yaml +33 -0
- package/dist/clis/bluesky/profile.yaml +27 -0
- package/dist/clis/bluesky/search.yaml +34 -0
- package/dist/clis/bluesky/starter-packs.yaml +34 -0
- package/dist/clis/bluesky/thread.yaml +32 -0
- package/dist/clis/bluesky/trending.yaml +27 -0
- package/dist/clis/bluesky/user.yaml +34 -0
- package/dist/clis/twitter/trending.js +29 -61
- package/dist/clis/v2ex/hot.yaml +17 -3
- package/dist/clis/xiaohongshu/publish.js +78 -42
- package/dist/clis/xiaohongshu/publish.test.js +20 -8
- package/dist/clis/xiaohongshu/search.d.ts +8 -1
- package/dist/clis/xiaohongshu/search.js +20 -1
- package/dist/clis/xiaohongshu/search.test.d.ts +1 -1
- package/dist/clis/xiaohongshu/search.test.js +32 -1
- package/dist/discovery.js +40 -28
- package/dist/doctor.d.ts +1 -2
- package/dist/doctor.js +2 -2
- package/dist/engine.test.js +42 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +2 -2
- package/dist/execution.js +45 -7
- package/dist/execution.test.d.ts +1 -0
- package/dist/execution.test.js +40 -0
- package/dist/external.js +6 -1
- package/dist/main.js +1 -0
- package/dist/plugin-scaffold.d.ts +28 -0
- package/dist/plugin-scaffold.js +142 -0
- package/dist/plugin-scaffold.test.d.ts +4 -0
- package/dist/plugin-scaffold.test.js +83 -0
- package/dist/plugin.d.ts +55 -17
- package/dist/plugin.js +706 -154
- package/dist/plugin.test.js +836 -38
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1 -1
- package/dist/types.d.ts +2 -0
- package/docs/adapters/browser/bluesky.md +53 -0
- package/docs/guide/plugins.md +10 -0
- package/package.json +1 -1
- package/src/browser/cdp.ts +6 -0
- package/src/browser/page.ts +24 -1
- package/src/cli.ts +34 -3
- package/src/clis/bluesky/feeds.yaml +29 -0
- package/src/clis/bluesky/followers.yaml +33 -0
- package/src/clis/bluesky/following.yaml +33 -0
- package/src/clis/bluesky/profile.yaml +27 -0
- package/src/clis/bluesky/search.yaml +34 -0
- package/src/clis/bluesky/starter-packs.yaml +34 -0
- package/src/clis/bluesky/thread.yaml +32 -0
- package/src/clis/bluesky/trending.yaml +27 -0
- package/src/clis/bluesky/user.yaml +34 -0
- package/src/clis/twitter/trending.ts +29 -77
- package/src/clis/v2ex/hot.yaml +17 -3
- package/src/clis/xiaohongshu/publish.test.ts +22 -8
- package/src/clis/xiaohongshu/publish.ts +93 -52
- package/src/clis/xiaohongshu/search.test.ts +39 -1
- package/src/clis/xiaohongshu/search.ts +19 -1
- package/src/discovery.ts +41 -33
- package/src/doctor.ts +2 -3
- package/src/engine.test.ts +38 -0
- package/src/errors.ts +6 -2
- package/src/execution.test.ts +47 -0
- package/src/execution.ts +39 -6
- package/src/external.ts +6 -1
- package/src/main.ts +1 -0
- package/src/plugin-scaffold.test.ts +98 -0
- package/src/plugin-scaffold.ts +170 -0
- package/src/plugin.test.ts +881 -38
- package/src/plugin.ts +871 -158
- package/src/runtime.ts +2 -2
- package/src/types.ts +2 -0
- package/tests/e2e/browser-public.test.ts +1 -1
package/dist/browser/cdp.js
CHANGED
|
@@ -135,6 +135,7 @@ export class CDPBridge {
|
|
|
135
135
|
class CDPPage {
|
|
136
136
|
bridge;
|
|
137
137
|
_pageEnabled = false;
|
|
138
|
+
_lastUrl = null;
|
|
138
139
|
constructor(bridge) {
|
|
139
140
|
this.bridge = bridge;
|
|
140
141
|
}
|
|
@@ -146,6 +147,7 @@ class CDPPage {
|
|
|
146
147
|
const loadPromise = this.bridge.waitForEvent('Page.loadEventFired', 30_000).catch(() => { });
|
|
147
148
|
await this.bridge.send('Page.navigate', { url });
|
|
148
149
|
await loadPromise;
|
|
150
|
+
this._lastUrl = url;
|
|
149
151
|
if (options?.waitUntil !== 'none') {
|
|
150
152
|
const maxMs = options?.settleMs ?? 1000;
|
|
151
153
|
await this.evaluate(waitForDomStableJs(maxMs, Math.min(500, maxMs)));
|
|
@@ -251,6 +253,9 @@ class CDPPage {
|
|
|
251
253
|
async consoleMessages(_level) {
|
|
252
254
|
return [];
|
|
253
255
|
}
|
|
256
|
+
async getCurrentUrl() {
|
|
257
|
+
return this._lastUrl;
|
|
258
|
+
}
|
|
254
259
|
async installInterceptor(pattern) {
|
|
255
260
|
const { generateInterceptorJs } = await import('../interceptor.js');
|
|
256
261
|
await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
|
package/dist/browser/page.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export declare class Page implements IPage {
|
|
|
18
18
|
constructor(workspace?: string);
|
|
19
19
|
/** Active tab ID, set after navigate and used in all subsequent commands */
|
|
20
20
|
private _tabId;
|
|
21
|
+
/** Last navigated URL, tracked in-memory to avoid extra round-trips */
|
|
22
|
+
private _lastUrl;
|
|
21
23
|
/** Helper: spread workspace into command params */
|
|
22
24
|
private _wsOpt;
|
|
23
25
|
/** Helper: spread workspace + tabId into command params */
|
|
@@ -26,6 +28,7 @@ export declare class Page implements IPage {
|
|
|
26
28
|
waitUntil?: 'load' | 'none';
|
|
27
29
|
settleMs?: number;
|
|
28
30
|
}): Promise<void>;
|
|
31
|
+
getCurrentUrl(): Promise<string | null>;
|
|
29
32
|
/** Close the automation window in the extension */
|
|
30
33
|
closeWindow(): Promise<void>;
|
|
31
34
|
evaluate(js: string): Promise<unknown>;
|
package/dist/browser/page.js
CHANGED
|
@@ -26,6 +26,8 @@ export class Page {
|
|
|
26
26
|
}
|
|
27
27
|
/** Active tab ID, set after navigate and used in all subsequent commands */
|
|
28
28
|
_tabId;
|
|
29
|
+
/** Last navigated URL, tracked in-memory to avoid extra round-trips */
|
|
30
|
+
_lastUrl = null;
|
|
29
31
|
/** Helper: spread workspace into command params */
|
|
30
32
|
_wsOpt() {
|
|
31
33
|
return { workspace: this.workspace };
|
|
@@ -42,10 +44,11 @@ export class Page {
|
|
|
42
44
|
url,
|
|
43
45
|
...this._cmdOpts(),
|
|
44
46
|
});
|
|
45
|
-
// Remember the tabId for subsequent
|
|
47
|
+
// Remember the tabId and URL for subsequent calls
|
|
46
48
|
if (result?.tabId) {
|
|
47
49
|
this._tabId = result.tabId;
|
|
48
50
|
}
|
|
51
|
+
this._lastUrl = url;
|
|
49
52
|
// Inject stealth anti-detection patches (guard flag prevents double-injection).
|
|
50
53
|
try {
|
|
51
54
|
await sendCommand('exec', {
|
|
@@ -66,6 +69,9 @@ export class Page {
|
|
|
66
69
|
});
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
async getCurrentUrl() {
|
|
73
|
+
return this._lastUrl;
|
|
74
|
+
}
|
|
69
75
|
/** Close the automation window in the extension */
|
|
70
76
|
async closeWindow() {
|
|
71
77
|
try {
|
|
@@ -163,6 +169,23 @@ export class Page {
|
|
|
163
169
|
}
|
|
164
170
|
async wait(options) {
|
|
165
171
|
if (typeof options === 'number') {
|
|
172
|
+
if (options >= 1) {
|
|
173
|
+
// For waits >= 1s, use DOM-stable check: return early when the page
|
|
174
|
+
// stops mutating, with the original wait time as the hard cap.
|
|
175
|
+
// This turns e.g. `page.wait(5)` from a fixed 5s sleep into
|
|
176
|
+
// "wait until DOM is stable, max 5s" — often completing in <1s.
|
|
177
|
+
try {
|
|
178
|
+
const maxMs = options * 1000;
|
|
179
|
+
await sendCommand('exec', {
|
|
180
|
+
code: waitForDomStableJs(maxMs, Math.min(500, maxMs)),
|
|
181
|
+
...this._cmdOpts(),
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Fallback: fixed sleep (e.g. if page has no DOM yet)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
166
189
|
await new Promise(resolve => setTimeout(resolve, options * 1000));
|
|
167
190
|
return;
|
|
168
191
|
}
|
package/dist/cli-manifest.json
CHANGED
|
@@ -1311,6 +1311,464 @@
|
|
|
1311
1311
|
"mediaLinks"
|
|
1312
1312
|
]
|
|
1313
1313
|
},
|
|
1314
|
+
{
|
|
1315
|
+
"site": "bluesky",
|
|
1316
|
+
"name": "feeds",
|
|
1317
|
+
"description": "Popular Bluesky feed generators",
|
|
1318
|
+
"domain": "public.api.bsky.app",
|
|
1319
|
+
"strategy": "public",
|
|
1320
|
+
"browser": false,
|
|
1321
|
+
"args": [
|
|
1322
|
+
{
|
|
1323
|
+
"name": "limit",
|
|
1324
|
+
"type": "int",
|
|
1325
|
+
"default": 20,
|
|
1326
|
+
"required": false,
|
|
1327
|
+
"positional": false,
|
|
1328
|
+
"help": "Number of feeds"
|
|
1329
|
+
}
|
|
1330
|
+
],
|
|
1331
|
+
"columns": [
|
|
1332
|
+
"rank",
|
|
1333
|
+
"name",
|
|
1334
|
+
"likes",
|
|
1335
|
+
"creator",
|
|
1336
|
+
"description"
|
|
1337
|
+
],
|
|
1338
|
+
"pipeline": [
|
|
1339
|
+
{
|
|
1340
|
+
"fetch": {
|
|
1341
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.unspecced.getPopularFeedGenerators?limit=${{ args.limit }}"
|
|
1342
|
+
}
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
"select": "feeds"
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
"map": {
|
|
1349
|
+
"rank": "${{ index + 1 }}",
|
|
1350
|
+
"name": "${{ item.displayName }}",
|
|
1351
|
+
"likes": "${{ item.likeCount }}",
|
|
1352
|
+
"creator": "${{ item.creator.handle }}",
|
|
1353
|
+
"description": "${{ item.description }}"
|
|
1354
|
+
}
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
"limit": "${{ args.limit }}"
|
|
1358
|
+
}
|
|
1359
|
+
],
|
|
1360
|
+
"type": "yaml"
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
"site": "bluesky",
|
|
1364
|
+
"name": "followers",
|
|
1365
|
+
"description": "List followers of a Bluesky user",
|
|
1366
|
+
"domain": "public.api.bsky.app",
|
|
1367
|
+
"strategy": "public",
|
|
1368
|
+
"browser": false,
|
|
1369
|
+
"args": [
|
|
1370
|
+
{
|
|
1371
|
+
"name": "handle",
|
|
1372
|
+
"type": "str",
|
|
1373
|
+
"required": true,
|
|
1374
|
+
"positional": true,
|
|
1375
|
+
"help": "Bluesky handle"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"name": "limit",
|
|
1379
|
+
"type": "int",
|
|
1380
|
+
"default": 20,
|
|
1381
|
+
"required": false,
|
|
1382
|
+
"positional": false,
|
|
1383
|
+
"help": "Number of followers"
|
|
1384
|
+
}
|
|
1385
|
+
],
|
|
1386
|
+
"columns": [
|
|
1387
|
+
"rank",
|
|
1388
|
+
"handle",
|
|
1389
|
+
"name",
|
|
1390
|
+
"description"
|
|
1391
|
+
],
|
|
1392
|
+
"pipeline": [
|
|
1393
|
+
{
|
|
1394
|
+
"fetch": {
|
|
1395
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.graph.getFollowers?actor=${{ args.handle }}&limit=${{ args.limit }}"
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
"select": "followers"
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
"map": {
|
|
1403
|
+
"rank": "${{ index + 1 }}",
|
|
1404
|
+
"handle": "${{ item.handle }}",
|
|
1405
|
+
"name": "${{ item.displayName }}",
|
|
1406
|
+
"description": "${{ item.description }}"
|
|
1407
|
+
}
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
"limit": "${{ args.limit }}"
|
|
1411
|
+
}
|
|
1412
|
+
],
|
|
1413
|
+
"type": "yaml"
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
"site": "bluesky",
|
|
1417
|
+
"name": "following",
|
|
1418
|
+
"description": "List accounts a Bluesky user is following",
|
|
1419
|
+
"domain": "public.api.bsky.app",
|
|
1420
|
+
"strategy": "public",
|
|
1421
|
+
"browser": false,
|
|
1422
|
+
"args": [
|
|
1423
|
+
{
|
|
1424
|
+
"name": "handle",
|
|
1425
|
+
"type": "str",
|
|
1426
|
+
"required": true,
|
|
1427
|
+
"positional": true,
|
|
1428
|
+
"help": "Bluesky handle"
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
"name": "limit",
|
|
1432
|
+
"type": "int",
|
|
1433
|
+
"default": 20,
|
|
1434
|
+
"required": false,
|
|
1435
|
+
"positional": false,
|
|
1436
|
+
"help": "Number of accounts"
|
|
1437
|
+
}
|
|
1438
|
+
],
|
|
1439
|
+
"columns": [
|
|
1440
|
+
"rank",
|
|
1441
|
+
"handle",
|
|
1442
|
+
"name",
|
|
1443
|
+
"description"
|
|
1444
|
+
],
|
|
1445
|
+
"pipeline": [
|
|
1446
|
+
{
|
|
1447
|
+
"fetch": {
|
|
1448
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.graph.getFollows?actor=${{ args.handle }}&limit=${{ args.limit }}"
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
"select": "follows"
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
"map": {
|
|
1456
|
+
"rank": "${{ index + 1 }}",
|
|
1457
|
+
"handle": "${{ item.handle }}",
|
|
1458
|
+
"name": "${{ item.displayName }}",
|
|
1459
|
+
"description": "${{ item.description }}"
|
|
1460
|
+
}
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
"limit": "${{ args.limit }}"
|
|
1464
|
+
}
|
|
1465
|
+
],
|
|
1466
|
+
"type": "yaml"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
"site": "bluesky",
|
|
1470
|
+
"name": "profile",
|
|
1471
|
+
"description": "Get Bluesky user profile info",
|
|
1472
|
+
"domain": "public.api.bsky.app",
|
|
1473
|
+
"strategy": "public",
|
|
1474
|
+
"browser": false,
|
|
1475
|
+
"args": [
|
|
1476
|
+
{
|
|
1477
|
+
"name": "handle",
|
|
1478
|
+
"type": "str",
|
|
1479
|
+
"required": true,
|
|
1480
|
+
"positional": true,
|
|
1481
|
+
"help": "Bluesky handle (e.g. bsky.app, jay.bsky.team)"
|
|
1482
|
+
}
|
|
1483
|
+
],
|
|
1484
|
+
"columns": [
|
|
1485
|
+
"handle",
|
|
1486
|
+
"name",
|
|
1487
|
+
"followers",
|
|
1488
|
+
"following",
|
|
1489
|
+
"posts",
|
|
1490
|
+
"description"
|
|
1491
|
+
],
|
|
1492
|
+
"pipeline": [
|
|
1493
|
+
{
|
|
1494
|
+
"fetch": {
|
|
1495
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${{ args.handle }}"
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
"map": {
|
|
1500
|
+
"handle": "${{ item.handle }}",
|
|
1501
|
+
"name": "${{ item.displayName }}",
|
|
1502
|
+
"followers": "${{ item.followersCount }}",
|
|
1503
|
+
"following": "${{ item.followsCount }}",
|
|
1504
|
+
"posts": "${{ item.postsCount }}",
|
|
1505
|
+
"description": "${{ item.description }}"
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
],
|
|
1509
|
+
"type": "yaml"
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
"site": "bluesky",
|
|
1513
|
+
"name": "search",
|
|
1514
|
+
"description": "Search Bluesky users",
|
|
1515
|
+
"domain": "public.api.bsky.app",
|
|
1516
|
+
"strategy": "public",
|
|
1517
|
+
"browser": false,
|
|
1518
|
+
"args": [
|
|
1519
|
+
{
|
|
1520
|
+
"name": "query",
|
|
1521
|
+
"type": "str",
|
|
1522
|
+
"required": true,
|
|
1523
|
+
"positional": true,
|
|
1524
|
+
"help": "Search query"
|
|
1525
|
+
},
|
|
1526
|
+
{
|
|
1527
|
+
"name": "limit",
|
|
1528
|
+
"type": "int",
|
|
1529
|
+
"default": 10,
|
|
1530
|
+
"required": false,
|
|
1531
|
+
"positional": false,
|
|
1532
|
+
"help": "Number of results"
|
|
1533
|
+
}
|
|
1534
|
+
],
|
|
1535
|
+
"columns": [
|
|
1536
|
+
"rank",
|
|
1537
|
+
"handle",
|
|
1538
|
+
"name",
|
|
1539
|
+
"followers",
|
|
1540
|
+
"description"
|
|
1541
|
+
],
|
|
1542
|
+
"pipeline": [
|
|
1543
|
+
{
|
|
1544
|
+
"fetch": {
|
|
1545
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.actor.searchActors?q=${{ args.query }}&limit=${{ args.limit }}"
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
"select": "actors"
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
"map": {
|
|
1553
|
+
"rank": "${{ index + 1 }}",
|
|
1554
|
+
"handle": "${{ item.handle }}",
|
|
1555
|
+
"name": "${{ item.displayName }}",
|
|
1556
|
+
"followers": "${{ item.followersCount }}",
|
|
1557
|
+
"description": "${{ item.description }}"
|
|
1558
|
+
}
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
"limit": "${{ args.limit }}"
|
|
1562
|
+
}
|
|
1563
|
+
],
|
|
1564
|
+
"type": "yaml"
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
"site": "bluesky",
|
|
1568
|
+
"name": "starter-packs",
|
|
1569
|
+
"description": "Get starter packs created by a Bluesky user",
|
|
1570
|
+
"domain": "public.api.bsky.app",
|
|
1571
|
+
"strategy": "public",
|
|
1572
|
+
"browser": false,
|
|
1573
|
+
"args": [
|
|
1574
|
+
{
|
|
1575
|
+
"name": "handle",
|
|
1576
|
+
"type": "str",
|
|
1577
|
+
"required": true,
|
|
1578
|
+
"positional": true,
|
|
1579
|
+
"help": "Bluesky handle"
|
|
1580
|
+
},
|
|
1581
|
+
{
|
|
1582
|
+
"name": "limit",
|
|
1583
|
+
"type": "int",
|
|
1584
|
+
"default": 10,
|
|
1585
|
+
"required": false,
|
|
1586
|
+
"positional": false,
|
|
1587
|
+
"help": "Number of starter packs"
|
|
1588
|
+
}
|
|
1589
|
+
],
|
|
1590
|
+
"columns": [
|
|
1591
|
+
"rank",
|
|
1592
|
+
"name",
|
|
1593
|
+
"description",
|
|
1594
|
+
"members",
|
|
1595
|
+
"joins"
|
|
1596
|
+
],
|
|
1597
|
+
"pipeline": [
|
|
1598
|
+
{
|
|
1599
|
+
"fetch": {
|
|
1600
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.graph.getActorStarterPacks?actor=${{ args.handle }}&limit=${{ args.limit }}"
|
|
1601
|
+
}
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
"select": "starterPacks"
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
"map": {
|
|
1608
|
+
"rank": "${{ index + 1 }}",
|
|
1609
|
+
"name": "${{ item.record.name }}",
|
|
1610
|
+
"description": "${{ item.record.description }}",
|
|
1611
|
+
"members": "${{ item.listItemCount }}",
|
|
1612
|
+
"joins": "${{ item.joinedAllTimeCount }}"
|
|
1613
|
+
}
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
"limit": "${{ args.limit }}"
|
|
1617
|
+
}
|
|
1618
|
+
],
|
|
1619
|
+
"type": "yaml"
|
|
1620
|
+
},
|
|
1621
|
+
{
|
|
1622
|
+
"site": "bluesky",
|
|
1623
|
+
"name": "thread",
|
|
1624
|
+
"description": "Get a Bluesky post thread with replies",
|
|
1625
|
+
"domain": "public.api.bsky.app",
|
|
1626
|
+
"strategy": "public",
|
|
1627
|
+
"browser": false,
|
|
1628
|
+
"args": [
|
|
1629
|
+
{
|
|
1630
|
+
"name": "uri",
|
|
1631
|
+
"type": "str",
|
|
1632
|
+
"required": true,
|
|
1633
|
+
"positional": true,
|
|
1634
|
+
"help": "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL"
|
|
1635
|
+
},
|
|
1636
|
+
{
|
|
1637
|
+
"name": "limit",
|
|
1638
|
+
"type": "int",
|
|
1639
|
+
"default": 20,
|
|
1640
|
+
"required": false,
|
|
1641
|
+
"positional": false,
|
|
1642
|
+
"help": "Number of replies"
|
|
1643
|
+
}
|
|
1644
|
+
],
|
|
1645
|
+
"columns": [
|
|
1646
|
+
"author",
|
|
1647
|
+
"text",
|
|
1648
|
+
"likes",
|
|
1649
|
+
"reposts",
|
|
1650
|
+
"replies_count"
|
|
1651
|
+
],
|
|
1652
|
+
"pipeline": [
|
|
1653
|
+
{
|
|
1654
|
+
"fetch": {
|
|
1655
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${{ args.uri }}&depth=2"
|
|
1656
|
+
}
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
"select": "thread"
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
"map": {
|
|
1663
|
+
"author": "${{ item.post.author.handle }}",
|
|
1664
|
+
"text": "${{ item.post.record.text }}",
|
|
1665
|
+
"likes": "${{ item.post.likeCount }}",
|
|
1666
|
+
"reposts": "${{ item.post.repostCount }}",
|
|
1667
|
+
"replies_count": "${{ item.post.replyCount }}"
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
],
|
|
1671
|
+
"type": "yaml"
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
"site": "bluesky",
|
|
1675
|
+
"name": "trending",
|
|
1676
|
+
"description": "Trending topics on Bluesky",
|
|
1677
|
+
"domain": "public.api.bsky.app",
|
|
1678
|
+
"strategy": "public",
|
|
1679
|
+
"browser": false,
|
|
1680
|
+
"args": [
|
|
1681
|
+
{
|
|
1682
|
+
"name": "limit",
|
|
1683
|
+
"type": "int",
|
|
1684
|
+
"default": 20,
|
|
1685
|
+
"required": false,
|
|
1686
|
+
"positional": false,
|
|
1687
|
+
"help": "Number of topics"
|
|
1688
|
+
}
|
|
1689
|
+
],
|
|
1690
|
+
"columns": [
|
|
1691
|
+
"rank",
|
|
1692
|
+
"topic",
|
|
1693
|
+
"link"
|
|
1694
|
+
],
|
|
1695
|
+
"pipeline": [
|
|
1696
|
+
{
|
|
1697
|
+
"fetch": {
|
|
1698
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.unspecced.getTrendingTopics"
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
"select": "topics"
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
"map": {
|
|
1706
|
+
"rank": "${{ index + 1 }}",
|
|
1707
|
+
"topic": "${{ item.topic }}",
|
|
1708
|
+
"link": "${{ item.link }}"
|
|
1709
|
+
}
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
"limit": "${{ args.limit }}"
|
|
1713
|
+
}
|
|
1714
|
+
],
|
|
1715
|
+
"type": "yaml"
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
"site": "bluesky",
|
|
1719
|
+
"name": "user",
|
|
1720
|
+
"description": "Get recent posts from a Bluesky user",
|
|
1721
|
+
"domain": "public.api.bsky.app",
|
|
1722
|
+
"strategy": "public",
|
|
1723
|
+
"browser": false,
|
|
1724
|
+
"args": [
|
|
1725
|
+
{
|
|
1726
|
+
"name": "handle",
|
|
1727
|
+
"type": "str",
|
|
1728
|
+
"required": true,
|
|
1729
|
+
"positional": true,
|
|
1730
|
+
"help": "Bluesky handle (e.g. bsky.app)"
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
"name": "limit",
|
|
1734
|
+
"type": "int",
|
|
1735
|
+
"default": 20,
|
|
1736
|
+
"required": false,
|
|
1737
|
+
"positional": false,
|
|
1738
|
+
"help": "Number of posts"
|
|
1739
|
+
}
|
|
1740
|
+
],
|
|
1741
|
+
"columns": [
|
|
1742
|
+
"rank",
|
|
1743
|
+
"text",
|
|
1744
|
+
"likes",
|
|
1745
|
+
"reposts",
|
|
1746
|
+
"replies"
|
|
1747
|
+
],
|
|
1748
|
+
"pipeline": [
|
|
1749
|
+
{
|
|
1750
|
+
"fetch": {
|
|
1751
|
+
"url": "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${{ args.handle }}&limit=${{ args.limit }}"
|
|
1752
|
+
}
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
"select": "feed"
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
"map": {
|
|
1759
|
+
"rank": "${{ index + 1 }}",
|
|
1760
|
+
"text": "${{ item.post.record.text }}",
|
|
1761
|
+
"likes": "${{ item.post.likeCount }}",
|
|
1762
|
+
"reposts": "${{ item.post.repostCount }}",
|
|
1763
|
+
"replies": "${{ item.post.replyCount }}"
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
"limit": "${{ args.limit }}"
|
|
1768
|
+
}
|
|
1769
|
+
],
|
|
1770
|
+
"type": "yaml"
|
|
1771
|
+
},
|
|
1314
1772
|
{
|
|
1315
1773
|
"site": "boss",
|
|
1316
1774
|
"name": "batchgreet",
|
|
@@ -10442,7 +10900,7 @@
|
|
|
10442
10900
|
"description": "V2EX 热门话题",
|
|
10443
10901
|
"domain": "www.v2ex.com",
|
|
10444
10902
|
"strategy": "public",
|
|
10445
|
-
"browser":
|
|
10903
|
+
"browser": true,
|
|
10446
10904
|
"args": [
|
|
10447
10905
|
{
|
|
10448
10906
|
"name": "limit",
|
|
@@ -10460,9 +10918,10 @@
|
|
|
10460
10918
|
],
|
|
10461
10919
|
"pipeline": [
|
|
10462
10920
|
{
|
|
10463
|
-
"
|
|
10464
|
-
|
|
10465
|
-
|
|
10921
|
+
"navigate": "https://www.v2ex.com/"
|
|
10922
|
+
},
|
|
10923
|
+
{
|
|
10924
|
+
"evaluate": "(async () => {\n const response = await fetch('/api/topics/hot.json', {\n credentials: 'include',\n headers: {\n accept: 'application/json, text/plain, */*',\n 'x-requested-with': 'XMLHttpRequest',\n },\n });\n if (!response.ok) {\n throw new Error(`V2EX hot API request failed: ${response.status}`);\n }\n return await response.json();\n})()\n"
|
|
10466
10925
|
},
|
|
10467
10926
|
{
|
|
10468
10927
|
"map": {
|
|
@@ -11801,7 +12260,7 @@
|
|
|
11801
12260
|
{
|
|
11802
12261
|
"name": "images",
|
|
11803
12262
|
"type": "str",
|
|
11804
|
-
"required":
|
|
12263
|
+
"required": true,
|
|
11805
12264
|
"help": "图片路径,逗号分隔,最多9张 (jpg/png/gif/webp)"
|
|
11806
12265
|
},
|
|
11807
12266
|
{
|
|
@@ -11856,6 +12315,7 @@
|
|
|
11856
12315
|
"title",
|
|
11857
12316
|
"author",
|
|
11858
12317
|
"likes",
|
|
12318
|
+
"published_at",
|
|
11859
12319
|
"url"
|
|
11860
12320
|
]
|
|
11861
12321
|
},
|
package/dist/cli.js
CHANGED
|
@@ -69,9 +69,10 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
|
|
|
69
69
|
for (const [site, cmds] of sites) {
|
|
70
70
|
console.log(chalk.bold.cyan(` ${site}`));
|
|
71
71
|
for (const cmd of cmds) {
|
|
72
|
-
const
|
|
72
|
+
const label = strategyLabel(cmd);
|
|
73
|
+
const tag = label === 'public'
|
|
73
74
|
? chalk.green('[public]')
|
|
74
|
-
: chalk.yellow(`[${
|
|
75
|
+
: chalk.yellow(`[${label}]`);
|
|
75
76
|
console.log(` ${cmd.name} ${tag}${cmd.description ? chalk.dim(` — ${cmd.description}`) : ''}`);
|
|
76
77
|
}
|
|
77
78
|
console.log();
|
|
@@ -228,7 +229,7 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
|
|
|
228
229
|
const pluginCmd = program.command('plugin').description('Manage opencli plugins');
|
|
229
230
|
pluginCmd
|
|
230
231
|
.command('install')
|
|
231
|
-
.description('Install a plugin from
|
|
232
|
+
.description('Install a plugin from a git repository')
|
|
232
233
|
.argument('<source>', 'Plugin source (e.g. github:user/repo)')
|
|
233
234
|
.action(async (source) => {
|
|
234
235
|
const { installPlugin } = await import('./plugin.js');
|
|
@@ -380,6 +381,36 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
|
|
|
380
381
|
console.log(chalk.dim(` ${plugins.length} plugin(s) installed`));
|
|
381
382
|
console.log();
|
|
382
383
|
});
|
|
384
|
+
pluginCmd
|
|
385
|
+
.command('create')
|
|
386
|
+
.description('Create a new plugin scaffold')
|
|
387
|
+
.argument('<name>', 'Plugin name (lowercase, hyphens allowed)')
|
|
388
|
+
.option('-d, --dir <path>', 'Output directory (default: ./<name>)')
|
|
389
|
+
.option('--description <text>', 'Plugin description')
|
|
390
|
+
.action(async (name, opts) => {
|
|
391
|
+
const { createPluginScaffold } = await import('./plugin-scaffold.js');
|
|
392
|
+
try {
|
|
393
|
+
const result = createPluginScaffold(name, {
|
|
394
|
+
dir: opts.dir,
|
|
395
|
+
description: opts.description,
|
|
396
|
+
});
|
|
397
|
+
console.log(chalk.green(`✅ Plugin scaffold created at ${result.dir}`));
|
|
398
|
+
console.log();
|
|
399
|
+
console.log(chalk.bold(' Files created:'));
|
|
400
|
+
for (const f of result.files) {
|
|
401
|
+
console.log(` ${chalk.cyan(f)}`);
|
|
402
|
+
}
|
|
403
|
+
console.log();
|
|
404
|
+
console.log(chalk.dim(' Next steps:'));
|
|
405
|
+
console.log(chalk.dim(` cd ${result.dir}`));
|
|
406
|
+
console.log(chalk.dim(` opencli plugin install file://${result.dir}`));
|
|
407
|
+
console.log(chalk.dim(` opencli ${name} hello`));
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
411
|
+
process.exitCode = 1;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
383
414
|
// ── External CLIs ─────────────────────────────────────────────────────────
|
|
384
415
|
const externalClis = loadExternalClis();
|
|
385
416
|
program
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: feeds
|
|
3
|
+
description: Popular Bluesky feed generators
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
limit:
|
|
10
|
+
type: int
|
|
11
|
+
default: 20
|
|
12
|
+
description: Number of feeds
|
|
13
|
+
|
|
14
|
+
pipeline:
|
|
15
|
+
- fetch:
|
|
16
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.unspecced.getPopularFeedGenerators?limit=${{ args.limit }}
|
|
17
|
+
|
|
18
|
+
- select: feeds
|
|
19
|
+
|
|
20
|
+
- map:
|
|
21
|
+
rank: ${{ index + 1 }}
|
|
22
|
+
name: ${{ item.displayName }}
|
|
23
|
+
likes: ${{ item.likeCount }}
|
|
24
|
+
creator: ${{ item.creator.handle }}
|
|
25
|
+
description: ${{ item.description }}
|
|
26
|
+
|
|
27
|
+
- limit: ${{ args.limit }}
|
|
28
|
+
|
|
29
|
+
columns: [rank, name, likes, creator, description]
|