@jackwener/opencli 0.9.5 → 0.9.6
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/README.md +4 -4
- package/README.zh-CN.md +4 -4
- package/dist/cli-manifest.json +222 -4
- package/dist/clis/antigravity/model.js +2 -2
- package/dist/clis/antigravity/send.js +2 -2
- package/dist/clis/chatgpt/ask.d.ts +1 -0
- package/dist/clis/chatgpt/ask.js +68 -0
- package/dist/clis/chatgpt/send.js +11 -0
- package/dist/clis/codex/ask.d.ts +1 -0
- package/dist/clis/codex/ask.js +67 -0
- package/dist/clis/codex/export.d.ts +1 -0
- package/dist/clis/codex/export.js +37 -0
- package/dist/clis/codex/history.d.ts +1 -0
- package/dist/clis/codex/history.js +43 -0
- package/dist/clis/codex/read.js +3 -5
- package/dist/clis/codex/screenshot.d.ts +1 -0
- package/dist/clis/codex/screenshot.js +27 -0
- package/dist/clis/codex/send.js +3 -6
- package/dist/clis/codex/status.js +2 -1
- package/dist/clis/cursor/ask.d.ts +1 -0
- package/dist/clis/cursor/ask.js +69 -0
- package/dist/clis/cursor/composer.js +9 -28
- package/dist/clis/cursor/export.d.ts +1 -0
- package/dist/clis/cursor/export.js +51 -0
- package/dist/clis/cursor/history.d.ts +1 -0
- package/dist/clis/cursor/history.js +43 -0
- package/dist/clis/cursor/new.js +4 -13
- package/dist/clis/cursor/screenshot.d.ts +1 -0
- package/dist/clis/cursor/screenshot.js +31 -0
- package/package.json +1 -1
- package/src/clis/antigravity/README.md +2 -3
- package/src/clis/antigravity/README.zh-CN.md +2 -3
- package/src/clis/antigravity/SKILL.md +1 -1
- package/src/clis/antigravity/model.ts +2 -2
- package/src/clis/antigravity/send.ts +2 -2
- package/src/clis/chatgpt/README.md +25 -16
- package/src/clis/chatgpt/README.zh-CN.md +27 -18
- package/src/clis/chatgpt/ask.ts +77 -0
- package/src/clis/chatgpt/send.ts +12 -0
- package/src/clis/codex/ask.ts +77 -0
- package/src/clis/codex/export.ts +42 -0
- package/src/clis/codex/extract-diff.ts +1 -0
- package/src/clis/codex/history.ts +47 -0
- package/src/clis/codex/read.ts +5 -6
- package/src/clis/codex/screenshot.ts +33 -0
- package/src/clis/codex/send.ts +6 -7
- package/src/clis/codex/status.ts +4 -2
- package/src/clis/cursor/ask.ts +81 -0
- package/src/clis/cursor/composer.ts +9 -30
- package/src/clis/cursor/export.ts +57 -0
- package/src/clis/cursor/history.ts +47 -0
- package/src/clis/cursor/new.ts +4 -15
- package/src/clis/cursor/screenshot.ts +38 -0
package/README.md
CHANGED
|
@@ -143,20 +143,20 @@ npm install -g @jackwener/opencli@latest
|
|
|
143
143
|
|
|
144
144
|
## Built-in Commands
|
|
145
145
|
|
|
146
|
-
**26 sites ·
|
|
146
|
+
**26 sites · 128 commands** — run `opencli list` for the live registry.
|
|
147
147
|
|
|
148
148
|
| Site | Commands | Count | Mode |
|
|
149
149
|
|------|----------|:-----:|------|
|
|
150
150
|
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 18 | 🔐 Browser |
|
|
151
151
|
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 15 | 🔐 Browser |
|
|
152
152
|
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 11 | 🔐 Browser |
|
|
153
|
-
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` |
|
|
154
|
-
| **codex** | `status` `send` `read` `new` `extract-diff` `model` |
|
|
153
|
+
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 12 | 🖥️ Desktop |
|
|
154
|
+
| **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 10 | 🖥️ Desktop |
|
|
155
155
|
| **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 6 | 🌐 / 🔐 |
|
|
156
156
|
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 6 | 🔐 Browser |
|
|
157
157
|
| **antigravity** | `status` `send` `read` `new` `evaluate` | 5 | 🖥️ Desktop |
|
|
158
158
|
| **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 5 | 🔐 Browser |
|
|
159
|
-
| **chatgpt** | `status` `new` `send` `read` |
|
|
159
|
+
| **chatgpt** | `status` `new` `send` `read` `ask` | 5 | 🖥️ Desktop |
|
|
160
160
|
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 3 | 🌐 Public |
|
|
161
161
|
| **youtube** | `search` `video` `transcript` | 3 | 🔐 Browser |
|
|
162
162
|
| **zhihu** | `hot` `search` `question` | 3 | 🔐 Browser |
|
package/README.zh-CN.md
CHANGED
|
@@ -144,16 +144,16 @@ npm install -g @jackwener/opencli@latest
|
|
|
144
144
|
|
|
145
145
|
## 内置命令
|
|
146
146
|
|
|
147
|
-
**26 个站点 ·
|
|
147
|
+
**26 个站点 · 128 命令** — 运行 `opencli list` 查看完整注册表。
|
|
148
148
|
|
|
149
149
|
| 站点 | 命令 | 数量 | 模式 |
|
|
150
150
|
|------|------|:----:|------|
|
|
151
151
|
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` | 18 | 🔐 浏览器 |
|
|
152
152
|
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 15 | 🔐 浏览器 |
|
|
153
153
|
| **antigravity** | `status` `send` `read` `new` `evaluate` | 5 | 🖥️ 桌面端 |
|
|
154
|
-
| **chatgpt** | `status` `new` `send` `read` |
|
|
155
|
-
| **codex** | `status` `send` `read` `new` `extract-diff` `model` |
|
|
156
|
-
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` |
|
|
154
|
+
| **chatgpt** | `status` `new` `send` `read` `ask` | 5 | 🖥️ 桌面端 |
|
|
155
|
+
| **codex** | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` | 10 | 🖥️ 桌面端 |
|
|
156
|
+
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 12 | 🖥️ 桌面端 |
|
|
157
157
|
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` | 11 | 🔐 浏览器 |
|
|
158
158
|
| **v2ex** | `hot` `latest` `topic` `daily` `me` `notifications` | 6 | 🌐 / 🔐 |
|
|
159
159
|
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 6 | 🔐 浏览器 |
|
package/dist/cli-manifest.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"modulePath": "antigravity/model.js",
|
|
48
48
|
"domain": "localhost",
|
|
49
49
|
"columns": [
|
|
50
|
-
"
|
|
50
|
+
"Status"
|
|
51
51
|
]
|
|
52
52
|
},
|
|
53
53
|
{
|
|
@@ -105,8 +105,8 @@
|
|
|
105
105
|
"modulePath": "antigravity/send.js",
|
|
106
106
|
"domain": "localhost",
|
|
107
107
|
"columns": [
|
|
108
|
-
"
|
|
109
|
-
"
|
|
108
|
+
"Status",
|
|
109
|
+
"Message"
|
|
110
110
|
]
|
|
111
111
|
},
|
|
112
112
|
{
|
|
@@ -810,6 +810,36 @@
|
|
|
810
810
|
"url"
|
|
811
811
|
]
|
|
812
812
|
},
|
|
813
|
+
{
|
|
814
|
+
"site": "chatgpt",
|
|
815
|
+
"name": "ask",
|
|
816
|
+
"description": "Send a prompt and wait for the AI response (send + wait + read)",
|
|
817
|
+
"strategy": "public",
|
|
818
|
+
"browser": false,
|
|
819
|
+
"args": [
|
|
820
|
+
{
|
|
821
|
+
"name": "text",
|
|
822
|
+
"type": "str",
|
|
823
|
+
"required": true,
|
|
824
|
+
"positional": true,
|
|
825
|
+
"help": "Prompt to send"
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"name": "timeout",
|
|
829
|
+
"type": "str",
|
|
830
|
+
"default": "30)",
|
|
831
|
+
"required": false,
|
|
832
|
+
"help": "Max seconds to wait for response (default: 30)"
|
|
833
|
+
}
|
|
834
|
+
],
|
|
835
|
+
"type": "ts",
|
|
836
|
+
"modulePath": "chatgpt/ask.js",
|
|
837
|
+
"domain": "localhost",
|
|
838
|
+
"columns": [
|
|
839
|
+
"Role",
|
|
840
|
+
"Text"
|
|
841
|
+
]
|
|
842
|
+
},
|
|
813
843
|
{
|
|
814
844
|
"site": "chatgpt",
|
|
815
845
|
"name": "new",
|
|
@@ -875,6 +905,36 @@
|
|
|
875
905
|
"Status"
|
|
876
906
|
]
|
|
877
907
|
},
|
|
908
|
+
{
|
|
909
|
+
"site": "codex",
|
|
910
|
+
"name": "ask",
|
|
911
|
+
"description": "Send a prompt and wait for the AI response (send + wait + read)",
|
|
912
|
+
"strategy": "ui",
|
|
913
|
+
"browser": true,
|
|
914
|
+
"args": [
|
|
915
|
+
{
|
|
916
|
+
"name": "text",
|
|
917
|
+
"type": "str",
|
|
918
|
+
"required": true,
|
|
919
|
+
"positional": true,
|
|
920
|
+
"help": "Prompt to send"
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
"name": "timeout",
|
|
924
|
+
"type": "str",
|
|
925
|
+
"default": "60)",
|
|
926
|
+
"required": false,
|
|
927
|
+
"help": "Max seconds to wait for response (default: 60)"
|
|
928
|
+
}
|
|
929
|
+
],
|
|
930
|
+
"type": "ts",
|
|
931
|
+
"modulePath": "codex/ask.js",
|
|
932
|
+
"domain": "localhost",
|
|
933
|
+
"columns": [
|
|
934
|
+
"Role",
|
|
935
|
+
"Text"
|
|
936
|
+
]
|
|
937
|
+
},
|
|
878
938
|
{
|
|
879
939
|
"site": "codex",
|
|
880
940
|
"name": "dump",
|
|
@@ -890,6 +950,31 @@
|
|
|
890
950
|
"files"
|
|
891
951
|
]
|
|
892
952
|
},
|
|
953
|
+
{
|
|
954
|
+
"site": "codex",
|
|
955
|
+
"name": "export",
|
|
956
|
+
"description": "Export the current Codex conversation to a Markdown file",
|
|
957
|
+
"strategy": "ui",
|
|
958
|
+
"browser": true,
|
|
959
|
+
"args": [
|
|
960
|
+
{
|
|
961
|
+
"name": "output",
|
|
962
|
+
"type": "str",
|
|
963
|
+
"default": "/tmp/codex-export.md)",
|
|
964
|
+
"required": false,
|
|
965
|
+
"positional": true,
|
|
966
|
+
"help": "Output file (default: /tmp/codex-export.md)"
|
|
967
|
+
}
|
|
968
|
+
],
|
|
969
|
+
"type": "ts",
|
|
970
|
+
"modulePath": "codex/export.js",
|
|
971
|
+
"domain": "localhost",
|
|
972
|
+
"columns": [
|
|
973
|
+
"Status",
|
|
974
|
+
"File",
|
|
975
|
+
"Messages"
|
|
976
|
+
]
|
|
977
|
+
},
|
|
893
978
|
{
|
|
894
979
|
"site": "codex",
|
|
895
980
|
"name": "extract-diff",
|
|
@@ -905,6 +990,21 @@
|
|
|
905
990
|
"Diff"
|
|
906
991
|
]
|
|
907
992
|
},
|
|
993
|
+
{
|
|
994
|
+
"site": "codex",
|
|
995
|
+
"name": "history",
|
|
996
|
+
"description": "List recent conversation threads in Codex",
|
|
997
|
+
"strategy": "ui",
|
|
998
|
+
"browser": true,
|
|
999
|
+
"args": [],
|
|
1000
|
+
"type": "ts",
|
|
1001
|
+
"modulePath": "codex/history.js",
|
|
1002
|
+
"domain": "localhost",
|
|
1003
|
+
"columns": [
|
|
1004
|
+
"Index",
|
|
1005
|
+
"Title"
|
|
1006
|
+
]
|
|
1007
|
+
},
|
|
908
1008
|
{
|
|
909
1009
|
"site": "codex",
|
|
910
1010
|
"name": "model",
|
|
@@ -954,7 +1054,31 @@
|
|
|
954
1054
|
"modulePath": "codex/read.js",
|
|
955
1055
|
"domain": "localhost",
|
|
956
1056
|
"columns": [
|
|
957
|
-
"
|
|
1057
|
+
"Content"
|
|
1058
|
+
]
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
"site": "codex",
|
|
1062
|
+
"name": "screenshot",
|
|
1063
|
+
"description": "Capture a snapshot of the current Codex window (DOM + Accessibility tree)",
|
|
1064
|
+
"strategy": "ui",
|
|
1065
|
+
"browser": true,
|
|
1066
|
+
"args": [
|
|
1067
|
+
{
|
|
1068
|
+
"name": "output",
|
|
1069
|
+
"type": "str",
|
|
1070
|
+
"default": "/tmp/codex-snapshot.txt)",
|
|
1071
|
+
"required": false,
|
|
1072
|
+
"positional": true,
|
|
1073
|
+
"help": "Output file path (default: /tmp/codex-snapshot.txt)"
|
|
1074
|
+
}
|
|
1075
|
+
],
|
|
1076
|
+
"type": "ts",
|
|
1077
|
+
"modulePath": "codex/screenshot.js",
|
|
1078
|
+
"domain": "localhost",
|
|
1079
|
+
"columns": [
|
|
1080
|
+
"Status",
|
|
1081
|
+
"File"
|
|
958
1082
|
]
|
|
959
1083
|
},
|
|
960
1084
|
{
|
|
@@ -1109,6 +1233,36 @@
|
|
|
1109
1233
|
"url"
|
|
1110
1234
|
]
|
|
1111
1235
|
},
|
|
1236
|
+
{
|
|
1237
|
+
"site": "cursor",
|
|
1238
|
+
"name": "ask",
|
|
1239
|
+
"description": "Send a prompt and wait for the AI response (send + wait + read)",
|
|
1240
|
+
"strategy": "ui",
|
|
1241
|
+
"browser": true,
|
|
1242
|
+
"args": [
|
|
1243
|
+
{
|
|
1244
|
+
"name": "text",
|
|
1245
|
+
"type": "str",
|
|
1246
|
+
"required": true,
|
|
1247
|
+
"positional": true,
|
|
1248
|
+
"help": "Prompt to send"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
"name": "timeout",
|
|
1252
|
+
"type": "str",
|
|
1253
|
+
"default": "30)",
|
|
1254
|
+
"required": false,
|
|
1255
|
+
"help": "Max seconds to wait for response (default: 30)"
|
|
1256
|
+
}
|
|
1257
|
+
],
|
|
1258
|
+
"type": "ts",
|
|
1259
|
+
"modulePath": "cursor/ask.js",
|
|
1260
|
+
"domain": "localhost",
|
|
1261
|
+
"columns": [
|
|
1262
|
+
"Role",
|
|
1263
|
+
"Text"
|
|
1264
|
+
]
|
|
1265
|
+
},
|
|
1112
1266
|
{
|
|
1113
1267
|
"site": "cursor",
|
|
1114
1268
|
"name": "composer",
|
|
@@ -1147,6 +1301,31 @@
|
|
|
1147
1301
|
"files"
|
|
1148
1302
|
]
|
|
1149
1303
|
},
|
|
1304
|
+
{
|
|
1305
|
+
"site": "cursor",
|
|
1306
|
+
"name": "export",
|
|
1307
|
+
"description": "Export the current ${site} conversation to a Markdown file",
|
|
1308
|
+
"strategy": "ui",
|
|
1309
|
+
"browser": true,
|
|
1310
|
+
"args": [
|
|
1311
|
+
{
|
|
1312
|
+
"name": "output",
|
|
1313
|
+
"type": "str",
|
|
1314
|
+
"default": "/tmp/${site",
|
|
1315
|
+
"required": false,
|
|
1316
|
+
"positional": true,
|
|
1317
|
+
"help": ""
|
|
1318
|
+
}
|
|
1319
|
+
],
|
|
1320
|
+
"type": "ts",
|
|
1321
|
+
"modulePath": "cursor/export.js",
|
|
1322
|
+
"domain": "localhost",
|
|
1323
|
+
"columns": [
|
|
1324
|
+
"Status",
|
|
1325
|
+
"File",
|
|
1326
|
+
"Messages"
|
|
1327
|
+
]
|
|
1328
|
+
},
|
|
1150
1329
|
{
|
|
1151
1330
|
"site": "cursor",
|
|
1152
1331
|
"name": "extract-code",
|
|
@@ -1161,6 +1340,21 @@
|
|
|
1161
1340
|
"Code"
|
|
1162
1341
|
]
|
|
1163
1342
|
},
|
|
1343
|
+
{
|
|
1344
|
+
"site": "cursor",
|
|
1345
|
+
"name": "history",
|
|
1346
|
+
"description": "List recent chat sessions from the Cursor sidebar",
|
|
1347
|
+
"strategy": "ui",
|
|
1348
|
+
"browser": true,
|
|
1349
|
+
"args": [],
|
|
1350
|
+
"type": "ts",
|
|
1351
|
+
"modulePath": "cursor/history.js",
|
|
1352
|
+
"domain": "localhost",
|
|
1353
|
+
"columns": [
|
|
1354
|
+
"Index",
|
|
1355
|
+
"Title"
|
|
1356
|
+
]
|
|
1357
|
+
},
|
|
1164
1358
|
{
|
|
1165
1359
|
"site": "cursor",
|
|
1166
1360
|
"name": "model",
|
|
@@ -1213,6 +1407,30 @@
|
|
|
1213
1407
|
"Text"
|
|
1214
1408
|
]
|
|
1215
1409
|
},
|
|
1410
|
+
{
|
|
1411
|
+
"site": "cursor",
|
|
1412
|
+
"name": "screenshot",
|
|
1413
|
+
"description": "Capture a snapshot of the current ${site} window (DOM + Accessibility tree)",
|
|
1414
|
+
"strategy": "ui",
|
|
1415
|
+
"browser": true,
|
|
1416
|
+
"args": [
|
|
1417
|
+
{
|
|
1418
|
+
"name": "output",
|
|
1419
|
+
"type": "str",
|
|
1420
|
+
"default": "/tmp/${site",
|
|
1421
|
+
"required": false,
|
|
1422
|
+
"positional": true,
|
|
1423
|
+
"help": ""
|
|
1424
|
+
}
|
|
1425
|
+
],
|
|
1426
|
+
"type": "ts",
|
|
1427
|
+
"modulePath": "cursor/screenshot.js",
|
|
1428
|
+
"domain": "localhost",
|
|
1429
|
+
"columns": [
|
|
1430
|
+
"Status",
|
|
1431
|
+
"File"
|
|
1432
|
+
]
|
|
1433
|
+
},
|
|
1216
1434
|
{
|
|
1217
1435
|
"site": "cursor",
|
|
1218
1436
|
"name": "send",
|
|
@@ -9,7 +9,7 @@ export const modelCommand = cli({
|
|
|
9
9
|
args: [
|
|
10
10
|
{ name: 'name', help: 'Target model name (e.g. claude, gemini, o1)', required: true, positional: true }
|
|
11
11
|
],
|
|
12
|
-
columns: ['
|
|
12
|
+
columns: ['Status'],
|
|
13
13
|
func: async (page, kwargs) => {
|
|
14
14
|
const targetName = kwargs.name.toLowerCase();
|
|
15
15
|
await page.evaluate(`
|
|
@@ -39,6 +39,6 @@ export const modelCommand = cli({
|
|
|
39
39
|
}
|
|
40
40
|
`);
|
|
41
41
|
await page.wait(0.5);
|
|
42
|
-
return [{
|
|
42
|
+
return [{ Status: `Model switched to: ${kwargs.name}` }];
|
|
43
43
|
},
|
|
44
44
|
});
|
|
@@ -9,7 +9,7 @@ export const sendCommand = cli({
|
|
|
9
9
|
args: [
|
|
10
10
|
{ name: 'message', help: 'The message text to send', required: true, positional: true }
|
|
11
11
|
],
|
|
12
|
-
columns: ['
|
|
12
|
+
columns: ['Status', 'Message'],
|
|
13
13
|
func: async (page, kwargs) => {
|
|
14
14
|
const text = kwargs.message;
|
|
15
15
|
// We use evaluate to focus and insert text because Lexical editors maintain
|
|
@@ -30,6 +30,6 @@ export const sendCommand = cli({
|
|
|
30
30
|
await page.wait(0.5);
|
|
31
31
|
// Press Enter to submit the message
|
|
32
32
|
await page.pressKey('Enter');
|
|
33
|
-
return [{
|
|
33
|
+
return [{ Status: 'Sent successfully', Message: text }];
|
|
34
34
|
},
|
|
35
35
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const askCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const askCommand = cli({
|
|
4
|
+
site: 'chatgpt',
|
|
5
|
+
name: 'ask',
|
|
6
|
+
description: 'Send a prompt and wait for the AI response (send + wait + read)',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [
|
|
11
|
+
{ name: 'text', required: true, positional: true, help: 'Prompt to send' },
|
|
12
|
+
{ name: 'timeout', required: false, help: 'Max seconds to wait for response (default: 30)', default: '30' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['Role', 'Text'],
|
|
15
|
+
func: async (page, kwargs) => {
|
|
16
|
+
const text = kwargs.text;
|
|
17
|
+
const timeout = parseInt(kwargs.timeout, 10) || 30;
|
|
18
|
+
// Backup clipboard
|
|
19
|
+
let clipBackup = '';
|
|
20
|
+
try {
|
|
21
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
// Send the message
|
|
25
|
+
spawnSync('pbcopy', { input: text });
|
|
26
|
+
execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
|
|
27
|
+
execSync("osascript -e 'delay 0.5'");
|
|
28
|
+
const cmd = "osascript " +
|
|
29
|
+
"-e 'tell application \"System Events\"' " +
|
|
30
|
+
"-e 'keystroke \"v\" using command down' " +
|
|
31
|
+
"-e 'delay 0.2' " +
|
|
32
|
+
"-e 'keystroke return' " +
|
|
33
|
+
"-e 'end tell'";
|
|
34
|
+
execSync(cmd);
|
|
35
|
+
// Clear clipboard marker
|
|
36
|
+
spawnSync('pbcopy', { input: '__OPENCLI_WAITING__' });
|
|
37
|
+
// Wait for response, then read it
|
|
38
|
+
const pollInterval = 3;
|
|
39
|
+
const maxPolls = Math.ceil(timeout / pollInterval);
|
|
40
|
+
let response = '';
|
|
41
|
+
for (let i = 0; i < maxPolls; i++) {
|
|
42
|
+
// Wait
|
|
43
|
+
execSync(`sleep ${pollInterval}`);
|
|
44
|
+
// Try Cmd+Shift+C to copy the latest response
|
|
45
|
+
execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
|
|
46
|
+
execSync("osascript -e 'tell application \"System Events\" to keystroke \"c\" using {command down, shift down}'");
|
|
47
|
+
execSync("osascript -e 'delay 0.3'");
|
|
48
|
+
const copied = execSync('pbpaste', { encoding: 'utf-8' }).trim();
|
|
49
|
+
if (copied && copied !== '__OPENCLI_WAITING__' && copied !== text) {
|
|
50
|
+
response = copied;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Restore clipboard
|
|
55
|
+
if (clipBackup)
|
|
56
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
57
|
+
if (!response) {
|
|
58
|
+
return [
|
|
59
|
+
{ Role: 'User', Text: text },
|
|
60
|
+
{ Role: 'System', Text: `No response within ${timeout}s. ChatGPT may still be generating.` },
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
{ Role: 'User', Text: text },
|
|
65
|
+
{ Role: 'Assistant', Text: response },
|
|
66
|
+
];
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -12,6 +12,13 @@ export const sendCommand = cli({
|
|
|
12
12
|
func: async (page, kwargs) => {
|
|
13
13
|
const text = kwargs.text;
|
|
14
14
|
try {
|
|
15
|
+
// Backup current clipboard content
|
|
16
|
+
let clipBackup = '';
|
|
17
|
+
try {
|
|
18
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
19
|
+
}
|
|
20
|
+
catch { /* clipboard may be empty */ }
|
|
21
|
+
// Copy text to clipboard
|
|
15
22
|
spawnSync('pbcopy', { input: text });
|
|
16
23
|
execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
|
|
17
24
|
execSync("osascript -e 'delay 0.5'");
|
|
@@ -22,6 +29,10 @@ export const sendCommand = cli({
|
|
|
22
29
|
"-e 'keystroke return' " +
|
|
23
30
|
"-e 'end tell'";
|
|
24
31
|
execSync(cmd);
|
|
32
|
+
// Restore original clipboard content
|
|
33
|
+
if (clipBackup) {
|
|
34
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
35
|
+
}
|
|
25
36
|
return [{ Status: 'Success' }];
|
|
26
37
|
}
|
|
27
38
|
catch (err) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const askCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const askCommand = cli({
|
|
3
|
+
site: 'codex',
|
|
4
|
+
name: 'ask',
|
|
5
|
+
description: 'Send a prompt and wait for the AI response (send + wait + read)',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'text', required: true, positional: true, help: 'Prompt to send' },
|
|
11
|
+
{ name: 'timeout', required: false, help: 'Max seconds to wait for response (default: 60)', default: '60' },
|
|
12
|
+
],
|
|
13
|
+
columns: ['Role', 'Text'],
|
|
14
|
+
func: async (page, kwargs) => {
|
|
15
|
+
const text = kwargs.text;
|
|
16
|
+
const timeout = parseInt(kwargs.timeout, 10) || 60;
|
|
17
|
+
// Snapshot the current content length before sending
|
|
18
|
+
const beforeLen = await page.evaluate(`
|
|
19
|
+
(function() {
|
|
20
|
+
const turns = document.querySelectorAll('[data-content-search-turn-key]');
|
|
21
|
+
return turns.length;
|
|
22
|
+
})()
|
|
23
|
+
`);
|
|
24
|
+
// Inject and send
|
|
25
|
+
await page.evaluate(`
|
|
26
|
+
(function(text) {
|
|
27
|
+
const editables = Array.from(document.querySelectorAll('[contenteditable="true"]'));
|
|
28
|
+
const composer = editables.length > 0 ? editables[editables.length - 1] : document.querySelector('textarea');
|
|
29
|
+
if (!composer) throw new Error('Could not find Codex input');
|
|
30
|
+
composer.focus();
|
|
31
|
+
document.execCommand('insertText', false, text);
|
|
32
|
+
})(${JSON.stringify(text)})
|
|
33
|
+
`);
|
|
34
|
+
await page.wait(0.5);
|
|
35
|
+
await page.pressKey('Enter');
|
|
36
|
+
// Poll for new content
|
|
37
|
+
const pollInterval = 3;
|
|
38
|
+
const maxPolls = Math.ceil(timeout / pollInterval);
|
|
39
|
+
let response = '';
|
|
40
|
+
for (let i = 0; i < maxPolls; i++) {
|
|
41
|
+
await page.wait(pollInterval);
|
|
42
|
+
const result = await page.evaluate(`
|
|
43
|
+
(function(prevLen) {
|
|
44
|
+
const turns = document.querySelectorAll('[data-content-search-turn-key]');
|
|
45
|
+
if (turns.length <= prevLen) return null;
|
|
46
|
+
const lastTurn = turns[turns.length - 1];
|
|
47
|
+
const text = lastTurn.innerText || lastTurn.textContent;
|
|
48
|
+
return text ? text.trim() : null;
|
|
49
|
+
})(${beforeLen})
|
|
50
|
+
`);
|
|
51
|
+
if (result) {
|
|
52
|
+
response = result;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!response) {
|
|
57
|
+
return [
|
|
58
|
+
{ Role: 'User', Text: text },
|
|
59
|
+
{ Role: 'System', Text: `No response within ${timeout}s. The agent may still be working.` },
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
{ Role: 'User', Text: text },
|
|
64
|
+
{ Role: 'Assistant', Text: response },
|
|
65
|
+
];
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const exportCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const exportCommand = cli({
|
|
4
|
+
site: 'codex',
|
|
5
|
+
name: 'export',
|
|
6
|
+
description: 'Export the current Codex conversation to a Markdown file',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.UI,
|
|
9
|
+
browser: true,
|
|
10
|
+
args: [
|
|
11
|
+
{ name: 'output', required: false, positional: true, help: 'Output file (default: /tmp/codex-export.md)' },
|
|
12
|
+
],
|
|
13
|
+
columns: ['Status', 'File', 'Messages'],
|
|
14
|
+
func: async (page, kwargs) => {
|
|
15
|
+
const outputPath = kwargs.output || '/tmp/codex-export.md';
|
|
16
|
+
const md = await page.evaluate(`
|
|
17
|
+
(function() {
|
|
18
|
+
const turns = document.querySelectorAll('[data-content-search-turn-key]');
|
|
19
|
+
if (turns.length > 0) {
|
|
20
|
+
return Array.from(turns).map((t, i) => '## Turn ' + (i + 1) + '\\n\\n' + (t.innerText || t.textContent).trim()).join('\\n\\n---\\n\\n');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const main = document.querySelector('main, [role="main"], [role="log"]');
|
|
24
|
+
if (main) return main.innerText || main.textContent;
|
|
25
|
+
return document.body.innerText;
|
|
26
|
+
})()
|
|
27
|
+
`);
|
|
28
|
+
fs.writeFileSync(outputPath, '# Codex Conversation Export\\n\\n' + md);
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
Status: 'Success',
|
|
32
|
+
File: outputPath,
|
|
33
|
+
Messages: md.split('## Turn').length - 1,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
},
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const historyCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const historyCommand = cli({
|
|
3
|
+
site: 'codex',
|
|
4
|
+
name: 'history',
|
|
5
|
+
description: 'List recent conversation threads in Codex',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['Index', 'Title'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
const items = await page.evaluate(`
|
|
13
|
+
(function() {
|
|
14
|
+
const results = [];
|
|
15
|
+
// Codex thread list items
|
|
16
|
+
const entries = document.querySelectorAll('[data-testid*="thread"], [class*="thread-list"] a, [role="listbox"] [role="option"]');
|
|
17
|
+
|
|
18
|
+
entries.forEach((item, i) => {
|
|
19
|
+
const title = (item.textContent || item.innerText || '').trim().substring(0, 100);
|
|
20
|
+
if (title) results.push({ Index: i + 1, Title: title });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Fallback: sidebar/nav links
|
|
24
|
+
if (results.length === 0) {
|
|
25
|
+
const nav = document.querySelector('nav, [role="navigation"], aside');
|
|
26
|
+
if (nav) {
|
|
27
|
+
const links = nav.querySelectorAll('a, button');
|
|
28
|
+
links.forEach((link, i) => {
|
|
29
|
+
const text = (link.textContent || '').trim().substring(0, 100);
|
|
30
|
+
if (text && text.length > 3) results.push({ Index: i + 1, Title: text });
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return results;
|
|
36
|
+
})()
|
|
37
|
+
`);
|
|
38
|
+
if (items.length === 0) {
|
|
39
|
+
return [{ Index: 0, Title: 'No threads found. Try opening the thread list first.' }];
|
|
40
|
+
}
|
|
41
|
+
return items;
|
|
42
|
+
},
|
|
43
|
+
});
|
package/dist/clis/codex/read.js
CHANGED
|
@@ -6,30 +6,28 @@ export const readCommand = cli({
|
|
|
6
6
|
domain: 'localhost',
|
|
7
7
|
strategy: Strategy.UI,
|
|
8
8
|
browser: true,
|
|
9
|
-
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['Content'],
|
|
10
11
|
func: async (page) => {
|
|
11
12
|
const historyText = await page.evaluate(`
|
|
12
13
|
(function() {
|
|
13
|
-
// Precise Codex selector for chat messages
|
|
14
14
|
const turns = Array.from(document.querySelectorAll('[data-content-search-turn-key]'));
|
|
15
15
|
if (turns.length > 0) {
|
|
16
16
|
return turns.map(t => t.innerText || t.textContent).join('\\n\\n---\\n\\n');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// Fallback robust scraping heuristic for chat history panes
|
|
20
19
|
const threadContainer = document.querySelector('[role="log"], [data-testid="conversation"], .thread-container, .messages-list, main');
|
|
21
20
|
|
|
22
21
|
if (threadContainer) {
|
|
23
22
|
return threadContainer.innerText || threadContainer.textContent;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// If specific containers fail, just dump the whole body's readable text minus the navigation
|
|
27
25
|
return document.body.innerText;
|
|
28
26
|
})()
|
|
29
27
|
`);
|
|
30
28
|
return [
|
|
31
29
|
{
|
|
32
|
-
|
|
30
|
+
Content: historyText,
|
|
33
31
|
},
|
|
34
32
|
];
|
|
35
33
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const screenshotCommand: import("../../registry.js").CliCommand;
|