@jackwener/opencli 0.7.11 → 0.9.0
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/CDP.md +103 -0
- package/CDP.zh-CN.md +103 -0
- package/README.md +5 -0
- package/README.zh-CN.md +5 -0
- package/dist/browser/discover.d.ts +15 -0
- package/dist/browser/discover.js +68 -2
- package/dist/browser/errors.d.ts +2 -1
- package/dist/browser/errors.js +13 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/mcp.js +8 -3
- package/dist/browser/page.js +11 -2
- package/dist/cli-manifest.json +246 -0
- package/dist/clis/antigravity/dump.d.ts +1 -0
- package/dist/clis/antigravity/dump.js +28 -0
- package/dist/clis/antigravity/extract-code.d.ts +1 -0
- package/dist/clis/antigravity/extract-code.js +32 -0
- package/dist/clis/antigravity/model.d.ts +1 -0
- package/dist/clis/antigravity/model.js +44 -0
- package/dist/clis/antigravity/new.d.ts +1 -0
- package/dist/clis/antigravity/new.js +25 -0
- package/dist/clis/antigravity/read.d.ts +1 -0
- package/dist/clis/antigravity/read.js +34 -0
- package/dist/clis/antigravity/send.d.ts +1 -0
- package/dist/clis/antigravity/send.js +35 -0
- package/dist/clis/antigravity/status.d.ts +1 -0
- package/dist/clis/antigravity/status.js +18 -0
- package/dist/clis/antigravity/watch.d.ts +1 -0
- package/dist/clis/antigravity/watch.js +41 -0
- package/dist/clis/barchart/flow.js +56 -58
- package/dist/clis/xiaoyuzhou/episode.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/episode.js +28 -0
- package/dist/clis/xiaoyuzhou/podcast-episodes.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/podcast-episodes.js +36 -0
- package/dist/clis/xiaoyuzhou/podcast.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/podcast.js +27 -0
- package/dist/clis/xiaoyuzhou/utils.d.ts +16 -0
- package/dist/clis/xiaoyuzhou/utils.js +55 -0
- package/dist/clis/xiaoyuzhou/utils.test.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/utils.test.js +99 -0
- package/dist/doctor.js +8 -0
- package/dist/engine.d.ts +1 -1
- package/dist/engine.js +59 -1
- package/dist/main.js +2 -15
- package/dist/pipeline/executor.js +2 -24
- package/dist/pipeline/registry.d.ts +19 -0
- package/dist/pipeline/registry.js +41 -0
- package/package.json +1 -1
- package/src/browser/discover.ts +79 -5
- package/src/browser/errors.ts +17 -1
- package/src/browser/index.ts +1 -0
- package/src/browser/mcp.ts +8 -3
- package/src/browser/page.ts +21 -2
- package/src/clis/antigravity/README.md +49 -0
- package/src/clis/antigravity/README.zh-CN.md +52 -0
- package/src/clis/antigravity/SKILL.md +42 -0
- package/src/clis/antigravity/dump.ts +30 -0
- package/src/clis/antigravity/extract-code.ts +34 -0
- package/src/clis/antigravity/model.ts +47 -0
- package/src/clis/antigravity/new.ts +28 -0
- package/src/clis/antigravity/read.ts +36 -0
- package/src/clis/antigravity/send.ts +40 -0
- package/src/clis/antigravity/status.ts +19 -0
- package/src/clis/antigravity/watch.ts +45 -0
- package/src/clis/barchart/flow.ts +57 -58
- package/src/clis/xiaoyuzhou/episode.ts +28 -0
- package/src/clis/xiaoyuzhou/podcast-episodes.ts +36 -0
- package/src/clis/xiaoyuzhou/podcast.ts +27 -0
- package/src/clis/xiaoyuzhou/utils.test.ts +122 -0
- package/src/clis/xiaoyuzhou/utils.ts +65 -0
- package/src/doctor.ts +9 -0
- package/src/engine.ts +58 -1
- package/src/main.ts +6 -11
- package/src/pipeline/executor.ts +2 -28
- package/src/pipeline/registry.ts +60 -0
- package/tests/e2e/public-commands.test.ts +62 -0
package/dist/cli-manifest.json
CHANGED
|
@@ -1,4 +1,142 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"site": "antigravity",
|
|
4
|
+
"name": "dump",
|
|
5
|
+
"description": "Dump the DOM to help AI understand the UI",
|
|
6
|
+
"strategy": "ui",
|
|
7
|
+
"browser": true,
|
|
8
|
+
"args": [],
|
|
9
|
+
"type": "ts",
|
|
10
|
+
"modulePath": "antigravity/dump.js",
|
|
11
|
+
"domain": "localhost",
|
|
12
|
+
"columns": [
|
|
13
|
+
"htmlFile",
|
|
14
|
+
"snapFile"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"site": "antigravity",
|
|
19
|
+
"name": "extract-code",
|
|
20
|
+
"description": "Extract multi-line code blocks from the current Antigravity conversation",
|
|
21
|
+
"strategy": "ui",
|
|
22
|
+
"browser": true,
|
|
23
|
+
"args": [],
|
|
24
|
+
"type": "ts",
|
|
25
|
+
"modulePath": "antigravity/extract-code.js",
|
|
26
|
+
"domain": "localhost",
|
|
27
|
+
"columns": [
|
|
28
|
+
"code"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"site": "antigravity",
|
|
33
|
+
"name": "model",
|
|
34
|
+
"description": "Switch the active LLM model in Antigravity",
|
|
35
|
+
"strategy": "ui",
|
|
36
|
+
"browser": true,
|
|
37
|
+
"args": [
|
|
38
|
+
{
|
|
39
|
+
"name": "name",
|
|
40
|
+
"type": "str",
|
|
41
|
+
"required": true,
|
|
42
|
+
"positional": true,
|
|
43
|
+
"help": "Target model name (e.g. claude, gemini, o1)"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"type": "ts",
|
|
47
|
+
"modulePath": "antigravity/model.js",
|
|
48
|
+
"domain": "localhost",
|
|
49
|
+
"columns": [
|
|
50
|
+
"status"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"site": "antigravity",
|
|
55
|
+
"name": "new",
|
|
56
|
+
"description": "Start a new conversation / clear context in Antigravity",
|
|
57
|
+
"strategy": "ui",
|
|
58
|
+
"browser": true,
|
|
59
|
+
"args": [],
|
|
60
|
+
"type": "ts",
|
|
61
|
+
"modulePath": "antigravity/new.js",
|
|
62
|
+
"domain": "localhost",
|
|
63
|
+
"columns": [
|
|
64
|
+
"status"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"site": "antigravity",
|
|
69
|
+
"name": "read",
|
|
70
|
+
"description": "Read the latest chat messages from Antigravity AI",
|
|
71
|
+
"strategy": "ui",
|
|
72
|
+
"browser": true,
|
|
73
|
+
"args": [
|
|
74
|
+
{
|
|
75
|
+
"name": "last",
|
|
76
|
+
"type": "str",
|
|
77
|
+
"required": false,
|
|
78
|
+
"help": "Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"type": "ts",
|
|
82
|
+
"modulePath": "antigravity/read.js",
|
|
83
|
+
"domain": "localhost",
|
|
84
|
+
"columns": [
|
|
85
|
+
"role",
|
|
86
|
+
"content"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"site": "antigravity",
|
|
91
|
+
"name": "send",
|
|
92
|
+
"description": "Send a message to Antigravity AI via the internal Lexical editor",
|
|
93
|
+
"strategy": "ui",
|
|
94
|
+
"browser": true,
|
|
95
|
+
"args": [
|
|
96
|
+
{
|
|
97
|
+
"name": "message",
|
|
98
|
+
"type": "str",
|
|
99
|
+
"required": true,
|
|
100
|
+
"positional": true,
|
|
101
|
+
"help": "The message text to send"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"type": "ts",
|
|
105
|
+
"modulePath": "antigravity/send.js",
|
|
106
|
+
"domain": "localhost",
|
|
107
|
+
"columns": [
|
|
108
|
+
"status",
|
|
109
|
+
"message"
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"site": "antigravity",
|
|
114
|
+
"name": "status",
|
|
115
|
+
"description": "Check Antigravity CDP connection and get current page state",
|
|
116
|
+
"strategy": "ui",
|
|
117
|
+
"browser": true,
|
|
118
|
+
"args": [],
|
|
119
|
+
"type": "ts",
|
|
120
|
+
"modulePath": "antigravity/status.js",
|
|
121
|
+
"domain": "localhost",
|
|
122
|
+
"columns": [
|
|
123
|
+
"status",
|
|
124
|
+
"url",
|
|
125
|
+
"title"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"site": "antigravity",
|
|
130
|
+
"name": "watch",
|
|
131
|
+
"description": "Stream new chat messages from Antigravity in real-time",
|
|
132
|
+
"strategy": "ui",
|
|
133
|
+
"browser": true,
|
|
134
|
+
"args": [],
|
|
135
|
+
"type": "ts",
|
|
136
|
+
"modulePath": "antigravity/watch.js",
|
|
137
|
+
"domain": "localhost",
|
|
138
|
+
"columns": []
|
|
139
|
+
},
|
|
2
140
|
{
|
|
3
141
|
"site": "barchart",
|
|
4
142
|
"name": "flow",
|
|
@@ -2522,6 +2660,114 @@
|
|
|
2522
2660
|
"url"
|
|
2523
2661
|
]
|
|
2524
2662
|
},
|
|
2663
|
+
{
|
|
2664
|
+
"site": "xiaoyuzhou",
|
|
2665
|
+
"name": "episode",
|
|
2666
|
+
"description": "View details of a Xiaoyuzhou podcast episode",
|
|
2667
|
+
"strategy": "public",
|
|
2668
|
+
"browser": false,
|
|
2669
|
+
"args": [
|
|
2670
|
+
{
|
|
2671
|
+
"name": "id",
|
|
2672
|
+
"type": "str",
|
|
2673
|
+
"required": true,
|
|
2674
|
+
"positional": true,
|
|
2675
|
+
"help": "Episode ID (eid from podcast-episodes output)"
|
|
2676
|
+
}
|
|
2677
|
+
],
|
|
2678
|
+
"type": "ts",
|
|
2679
|
+
"modulePath": "xiaoyuzhou/episode.js",
|
|
2680
|
+
"domain": "www.xiaoyuzhoufm.com",
|
|
2681
|
+
"columns": [
|
|
2682
|
+
"title",
|
|
2683
|
+
"podcast",
|
|
2684
|
+
"duration",
|
|
2685
|
+
"plays",
|
|
2686
|
+
"comments",
|
|
2687
|
+
"likes",
|
|
2688
|
+
"date"
|
|
2689
|
+
]
|
|
2690
|
+
},
|
|
2691
|
+
{
|
|
2692
|
+
"site": "xiaoyuzhou",
|
|
2693
|
+
"name": "podcast-episodes",
|
|
2694
|
+
"description": "List recent episodes of a Xiaoyuzhou podcast (up to 15, SSR limit)",
|
|
2695
|
+
"strategy": "public",
|
|
2696
|
+
"browser": false,
|
|
2697
|
+
"args": [
|
|
2698
|
+
{
|
|
2699
|
+
"name": "id",
|
|
2700
|
+
"type": "str",
|
|
2701
|
+
"required": true,
|
|
2702
|
+
"positional": true,
|
|
2703
|
+
"help": "Podcast ID (from xiaoyuzhoufm.com URL)"
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
"name": "limit",
|
|
2707
|
+
"type": "int",
|
|
2708
|
+
"default": 15,
|
|
2709
|
+
"required": false,
|
|
2710
|
+
"help": "Max episodes to show (up to 15, SSR limit)"
|
|
2711
|
+
}
|
|
2712
|
+
],
|
|
2713
|
+
"type": "ts",
|
|
2714
|
+
"modulePath": "xiaoyuzhou/podcast-episodes.js",
|
|
2715
|
+
"domain": "www.xiaoyuzhoufm.com",
|
|
2716
|
+
"columns": [
|
|
2717
|
+
"eid",
|
|
2718
|
+
"title",
|
|
2719
|
+
"duration",
|
|
2720
|
+
"plays",
|
|
2721
|
+
"date"
|
|
2722
|
+
]
|
|
2723
|
+
},
|
|
2724
|
+
{
|
|
2725
|
+
"site": "xiaoyuzhou",
|
|
2726
|
+
"name": "podcast",
|
|
2727
|
+
"description": "View a Xiaoyuzhou podcast profile",
|
|
2728
|
+
"strategy": "public",
|
|
2729
|
+
"browser": false,
|
|
2730
|
+
"args": [
|
|
2731
|
+
{
|
|
2732
|
+
"name": "id",
|
|
2733
|
+
"type": "str",
|
|
2734
|
+
"required": true,
|
|
2735
|
+
"positional": true,
|
|
2736
|
+
"help": "Podcast ID (from xiaoyuzhoufm.com URL)"
|
|
2737
|
+
}
|
|
2738
|
+
],
|
|
2739
|
+
"type": "ts",
|
|
2740
|
+
"modulePath": "xiaoyuzhou/podcast.js",
|
|
2741
|
+
"domain": "www.xiaoyuzhoufm.com",
|
|
2742
|
+
"columns": [
|
|
2743
|
+
"title",
|
|
2744
|
+
"author",
|
|
2745
|
+
"description",
|
|
2746
|
+
"subscribers",
|
|
2747
|
+
"episodes",
|
|
2748
|
+
"updated"
|
|
2749
|
+
]
|
|
2750
|
+
},
|
|
2751
|
+
{
|
|
2752
|
+
"site": "xiaoyuzhou",
|
|
2753
|
+
"name": "utils",
|
|
2754
|
+
"description": "",
|
|
2755
|
+
"strategy": "cookie",
|
|
2756
|
+
"browser": true,
|
|
2757
|
+
"args": [],
|
|
2758
|
+
"type": "ts",
|
|
2759
|
+
"modulePath": "xiaoyuzhou/utils.js"
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
"site": "xiaoyuzhou",
|
|
2763
|
+
"name": "utils.test",
|
|
2764
|
+
"description": "",
|
|
2765
|
+
"strategy": "cookie",
|
|
2766
|
+
"browser": true,
|
|
2767
|
+
"args": [],
|
|
2768
|
+
"type": "ts",
|
|
2769
|
+
"modulePath": "xiaoyuzhou/utils.test.js"
|
|
2770
|
+
},
|
|
2525
2771
|
{
|
|
2526
2772
|
"site": "xueqiu",
|
|
2527
2773
|
"name": "feed",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const dumpCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
export const dumpCommand = cli({
|
|
4
|
+
site: 'antigravity',
|
|
5
|
+
name: 'dump',
|
|
6
|
+
description: 'Dump the DOM to help AI understand the UI',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.UI,
|
|
9
|
+
browser: true,
|
|
10
|
+
args: [],
|
|
11
|
+
columns: ['htmlFile', 'snapFile'],
|
|
12
|
+
func: async (page) => {
|
|
13
|
+
// Extract HTML
|
|
14
|
+
const html = await page.evaluate('document.body.innerHTML');
|
|
15
|
+
fs.writeFileSync('/tmp/antigravity-dom.html', html);
|
|
16
|
+
// Extract Snapshot
|
|
17
|
+
let snapFile = '';
|
|
18
|
+
try {
|
|
19
|
+
const snap = await page.snapshot({ raw: true });
|
|
20
|
+
snapFile = '/tmp/antigravity-snapshot.json';
|
|
21
|
+
fs.writeFileSync(snapFile, JSON.stringify(snap, null, 2));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
snapFile = 'Failed';
|
|
25
|
+
}
|
|
26
|
+
return [{ htmlFile: '/tmp/antigravity-dom.html', snapFile }];
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const extractCodeCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const extractCodeCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'extract-code',
|
|
5
|
+
description: 'Extract multi-line code blocks from the current Antigravity conversation',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['code'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
const blocks = await page.evaluate(`
|
|
13
|
+
async () => {
|
|
14
|
+
// Find standard pre/code blocks
|
|
15
|
+
let elements = Array.from(document.querySelectorAll('pre code'));
|
|
16
|
+
|
|
17
|
+
// Fallback to Monaco editor content inside the UI
|
|
18
|
+
if (elements.length === 0) {
|
|
19
|
+
elements = Array.from(document.querySelectorAll('.monaco-editor'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Generic fallback to any code tag that spans multiple lines
|
|
23
|
+
if (elements.length === 0) {
|
|
24
|
+
elements = Array.from(document.querySelectorAll('code')).filter(c => c.innerText.includes('\\n'));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return elements.map(el => el.innerText).filter(text => text.trim().length > 0);
|
|
28
|
+
}
|
|
29
|
+
`);
|
|
30
|
+
return blocks.map((code) => ({ code }));
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const modelCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const modelCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'model',
|
|
5
|
+
description: 'Switch the active LLM model in Antigravity',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'name', help: 'Target model name (e.g. claude, gemini, o1)', required: true, positional: true }
|
|
11
|
+
],
|
|
12
|
+
columns: ['status'],
|
|
13
|
+
func: async (page, kwargs) => {
|
|
14
|
+
const targetName = kwargs.name.toLowerCase();
|
|
15
|
+
await page.evaluate(`
|
|
16
|
+
async () => {
|
|
17
|
+
const targetModelName = ${JSON.stringify(targetName)};
|
|
18
|
+
|
|
19
|
+
// 1. Locate the model selector dropdown trigger
|
|
20
|
+
const trigger = document.querySelector('div[aria-haspopup="dialog"] > div[tabindex="0"]');
|
|
21
|
+
if (!trigger) throw new Error('Could not find the model selector trigger in the UI');
|
|
22
|
+
trigger.click();
|
|
23
|
+
|
|
24
|
+
// 2. Wait a brief moment for React to mount the Portal/Dialog
|
|
25
|
+
await new Promise(r => setTimeout(r, 200));
|
|
26
|
+
|
|
27
|
+
// 3. Find the option spanning target text
|
|
28
|
+
const spans = Array.from(document.querySelectorAll('[role="dialog"] span'));
|
|
29
|
+
const target = spans.find(s => s.innerText.toLowerCase().includes(targetModelName));
|
|
30
|
+
if (!target) {
|
|
31
|
+
// If not found, click the trigger again to close it safely
|
|
32
|
+
trigger.click();
|
|
33
|
+
throw new Error('Model matching "' + targetModelName + '" was not found in the dropdown list.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 4. Click the closest parent that handles the row action
|
|
37
|
+
const optionNode = target.closest('.cursor-pointer') || target;
|
|
38
|
+
optionNode.click();
|
|
39
|
+
}
|
|
40
|
+
`);
|
|
41
|
+
await page.wait(0.5);
|
|
42
|
+
return [{ status: `Model switched to: ${kwargs.name}` }];
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const newCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const newCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'new',
|
|
5
|
+
description: 'Start a new conversation / clear context in Antigravity',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['status'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
await page.evaluate(`
|
|
13
|
+
async () => {
|
|
14
|
+
const btn = document.querySelector('[data-tooltip-id="new-conversation-tooltip"]');
|
|
15
|
+
if (!btn) throw new Error('Could not find New Conversation button');
|
|
16
|
+
|
|
17
|
+
// In case it's disabled, we must check, but we'll try to click it anyway
|
|
18
|
+
btn.click();
|
|
19
|
+
}
|
|
20
|
+
`);
|
|
21
|
+
// Give it a moment to reset the UI
|
|
22
|
+
await page.wait(0.5);
|
|
23
|
+
return [{ status: 'Successfully started a new conversation' }];
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const readCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'read',
|
|
5
|
+
description: 'Read the latest chat messages from Antigravity AI',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'last', help: 'Number of recent messages to read (not fully implemented due to generic structure, currently returns full history text or latest chunk)' }
|
|
11
|
+
],
|
|
12
|
+
columns: ['role', 'content'],
|
|
13
|
+
func: async (page, kwargs) => {
|
|
14
|
+
// We execute a script inside Antigravity's Chromium environment to extract the text
|
|
15
|
+
// of the entire conversation pane.
|
|
16
|
+
const rawText = await page.evaluate(`
|
|
17
|
+
async () => {
|
|
18
|
+
const container = document.getElementById('conversation');
|
|
19
|
+
if (!container) throw new Error('Could not find conversation container');
|
|
20
|
+
|
|
21
|
+
// Extract the full visible text of the conversation
|
|
22
|
+
// In Electron/Chromium, innerText preserves basic visual line breaks nicely
|
|
23
|
+
return container.innerText;
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
26
|
+
// We can do simple heuristic parsing based on typical visual markers if needed.
|
|
27
|
+
// For now, we return the entire text blob, or just the last 2000 characters if it's too long.
|
|
28
|
+
const cleanText = String(rawText).trim();
|
|
29
|
+
return [{
|
|
30
|
+
role: 'history',
|
|
31
|
+
content: cleanText
|
|
32
|
+
}];
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sendCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const sendCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'send',
|
|
5
|
+
description: 'Send a message to Antigravity AI via the internal Lexical editor',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'message', help: 'The message text to send', required: true, positional: true }
|
|
11
|
+
],
|
|
12
|
+
columns: ['status', 'message'],
|
|
13
|
+
func: async (page, kwargs) => {
|
|
14
|
+
const text = kwargs.message;
|
|
15
|
+
// We use evaluate to focus and insert text because Lexical editors maintain
|
|
16
|
+
// absolute control over their DOM and don't respond to raw node.textContent.
|
|
17
|
+
// document.execCommand simulates a native paste/typing action perfectly.
|
|
18
|
+
await page.evaluate(`
|
|
19
|
+
async () => {
|
|
20
|
+
const container = document.getElementById('antigravity.agentSidePanelInputBox');
|
|
21
|
+
if (!container) throw new Error('Could not find antigravity.agentSidePanelInputBox');
|
|
22
|
+
const editor = container.querySelector('[data-lexical-editor="true"]');
|
|
23
|
+
if (!editor) throw new Error('Could not find Antigravity input box');
|
|
24
|
+
|
|
25
|
+
editor.focus();
|
|
26
|
+
document.execCommand('insertText', false, ${JSON.stringify(text)});
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
29
|
+
// Wait for the React/Lexical state to flush the new input
|
|
30
|
+
await page.wait(0.5);
|
|
31
|
+
// Press Enter to submit the message
|
|
32
|
+
await page.pressKey('Enter');
|
|
33
|
+
return [{ status: 'Sent successfully', message: text }];
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const statusCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const statusCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'status',
|
|
5
|
+
description: 'Check Antigravity CDP connection and get current page state',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['status', 'url', 'title'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
return {
|
|
13
|
+
status: 'Connected',
|
|
14
|
+
url: await page.evaluate('window.location.href'),
|
|
15
|
+
title: await page.evaluate('document.title'),
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const watchCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const watchCommand = cli({
|
|
3
|
+
site: 'antigravity',
|
|
4
|
+
name: 'watch',
|
|
5
|
+
description: 'Stream new chat messages from Antigravity in real-time',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
timeoutSeconds: 86400, // Run for up to 24 hours
|
|
11
|
+
columns: [], // We use direct stdout streaming
|
|
12
|
+
func: async (page) => {
|
|
13
|
+
console.log('Watching Antigravity chat... (Press Ctrl+C to stop)');
|
|
14
|
+
let lastLength = 0;
|
|
15
|
+
// Loop until process gets killed
|
|
16
|
+
while (true) {
|
|
17
|
+
const text = await page.evaluate(`
|
|
18
|
+
async () => {
|
|
19
|
+
const container = document.getElementById('conversation');
|
|
20
|
+
return container ? container.innerText : '';
|
|
21
|
+
}
|
|
22
|
+
`);
|
|
23
|
+
const currentLength = text.length;
|
|
24
|
+
if (currentLength > lastLength) {
|
|
25
|
+
// Delta mode
|
|
26
|
+
const newSegment = text.substring(lastLength);
|
|
27
|
+
if (newSegment.trim().length > 0) {
|
|
28
|
+
process.stdout.write(newSegment);
|
|
29
|
+
}
|
|
30
|
+
lastLength = currentLength;
|
|
31
|
+
}
|
|
32
|
+
else if (currentLength < lastLength) {
|
|
33
|
+
// The conversation was cleared or updated significantly
|
|
34
|
+
lastLength = currentLength;
|
|
35
|
+
console.log('\\n--- Conversation Cleared/Changed ---\\n');
|
|
36
|
+
process.stdout.write(text);
|
|
37
|
+
}
|
|
38
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
});
|