antigravity-autopilot 1.0.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/AutoAccept.ps1 +305 -0
- package/README.md +38 -0
- package/extension.js +453 -0
- package/icon-sidebar.svg +3 -0
- package/icon.png +0 -0
- package/install.ps1 +48 -0
- package/package.json +71 -0
- package/patcher.js +265 -0
package/AutoAccept.ps1
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Antigravity Auto-Accept - Floating Panel
|
|
2
|
+
# A small always-on-top window for toggling auto-accept
|
|
3
|
+
# Run with: powershell -WindowStyle Hidden -File AutoAccept.ps1
|
|
4
|
+
|
|
5
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
6
|
+
Add-Type -AssemblyName System.Drawing
|
|
7
|
+
|
|
8
|
+
Add-Type @"
|
|
9
|
+
using System;
|
|
10
|
+
using System.Runtime.InteropServices;
|
|
11
|
+
public class WinAPI {
|
|
12
|
+
[DllImport("user32.dll")]
|
|
13
|
+
public static extern IntPtr GetForegroundWindow();
|
|
14
|
+
[DllImport("user32.dll", SetLastError=true)]
|
|
15
|
+
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
|
|
16
|
+
[DllImport("user32.dll")]
|
|
17
|
+
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
|
18
|
+
}
|
|
19
|
+
"@
|
|
20
|
+
|
|
21
|
+
# ---- State ----
|
|
22
|
+
$script:IsEnabled = $false
|
|
23
|
+
$script:Count = 0
|
|
24
|
+
$script:Timer = $null
|
|
25
|
+
$HWND_TOPMOST = [IntPtr](-1)
|
|
26
|
+
$SWP_NOSIZE = 0x0001
|
|
27
|
+
$SWP_NOMOVE = 0x0002
|
|
28
|
+
|
|
29
|
+
# ---- Fonts & Colors ----
|
|
30
|
+
$fontMain = New-Object System.Drawing.Font("Segoe UI", 9)
|
|
31
|
+
$fontBig = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
|
|
32
|
+
$fontMono = New-Object System.Drawing.Font("Consolas", 8)
|
|
33
|
+
$colorBg = [System.Drawing.Color]::FromArgb(30, 30, 30)
|
|
34
|
+
$colorPanel = [System.Drawing.Color]::FromArgb(40, 40, 40)
|
|
35
|
+
$colorOn = [System.Drawing.Color]::FromArgb(0, 200, 100)
|
|
36
|
+
$colorOff = [System.Drawing.Color]::FromArgb(100, 100, 100)
|
|
37
|
+
$colorText = [System.Drawing.Color]::White
|
|
38
|
+
$colorDim = [System.Drawing.Color]::FromArgb(160, 160, 160)
|
|
39
|
+
$colorAccent = [System.Drawing.Color]::FromArgb(90, 180, 255)
|
|
40
|
+
|
|
41
|
+
# ---- Main Form ----
|
|
42
|
+
$form = New-Object System.Windows.Forms.Form
|
|
43
|
+
$form.Text = "AG Auto-Accept"
|
|
44
|
+
$form.Size = New-Object System.Drawing.Size(280, 320)
|
|
45
|
+
$form.StartPosition = "Manual"
|
|
46
|
+
$form.Location = New-Object System.Drawing.Point(
|
|
47
|
+
([System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Right - 290),
|
|
48
|
+
([System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Bottom - 330)
|
|
49
|
+
)
|
|
50
|
+
$form.FormBorderStyle = "FixedSingle"
|
|
51
|
+
$form.MaximizeBox = $false
|
|
52
|
+
$form.MinimizeBox = $true
|
|
53
|
+
$form.BackColor = $colorBg
|
|
54
|
+
$form.ForeColor = $colorText
|
|
55
|
+
$form.Font = $fontMain
|
|
56
|
+
$form.TopMost = $true
|
|
57
|
+
$form.ShowInTaskbar = $true
|
|
58
|
+
|
|
59
|
+
# ---- Title bar area ----
|
|
60
|
+
$lblTitle = New-Object System.Windows.Forms.Label
|
|
61
|
+
$lblTitle.Text = "⚡ Antigravity Auto-Accept"
|
|
62
|
+
$lblTitle.Font = $fontBig
|
|
63
|
+
$lblTitle.ForeColor = $colorAccent
|
|
64
|
+
$lblTitle.Location = New-Object System.Drawing.Point(12, 12)
|
|
65
|
+
$lblTitle.Size = New-Object System.Drawing.Size(256, 24)
|
|
66
|
+
$form.Controls.Add($lblTitle)
|
|
67
|
+
|
|
68
|
+
$lblSub = New-Object System.Windows.Forms.Label
|
|
69
|
+
$lblSub.Text = "Tự động accept Antigravity commands"
|
|
70
|
+
$lblSub.Font = $fontMono
|
|
71
|
+
$lblSub.ForeColor = $colorDim
|
|
72
|
+
$lblSub.Location = New-Object System.Drawing.Point(12, 36)
|
|
73
|
+
$lblSub.Size = New-Object System.Drawing.Size(256, 16)
|
|
74
|
+
$form.Controls.Add($lblSub)
|
|
75
|
+
|
|
76
|
+
# ---- Separator ----
|
|
77
|
+
$sep = New-Object System.Windows.Forms.Label
|
|
78
|
+
$sep.Location = New-Object System.Drawing.Point(12, 57)
|
|
79
|
+
$sep.Size = New-Object System.Drawing.Size(254, 1)
|
|
80
|
+
$sep.BackColor = [System.Drawing.Color]::FromArgb(60, 60, 60)
|
|
81
|
+
$form.Controls.Add($sep)
|
|
82
|
+
|
|
83
|
+
# ---- Status indicator (big circle + text) ----
|
|
84
|
+
$pnlStatus = New-Object System.Windows.Forms.Panel
|
|
85
|
+
$pnlStatus.Location = New-Object System.Drawing.Point(12, 68)
|
|
86
|
+
$pnlStatus.Size = New-Object System.Drawing.Size(254, 80)
|
|
87
|
+
$pnlStatus.BackColor = $colorPanel
|
|
88
|
+
$form.Controls.Add($pnlStatus)
|
|
89
|
+
|
|
90
|
+
$picDot = New-Object System.Windows.Forms.PictureBox
|
|
91
|
+
$picDot.Location = New-Object System.Drawing.Point(16, 20)
|
|
92
|
+
$picDot.Size = New-Object System.Drawing.Size(40, 40)
|
|
93
|
+
$picDot.BackColor = [System.Drawing.Color]::Transparent
|
|
94
|
+
$pnlStatus.Controls.Add($picDot)
|
|
95
|
+
|
|
96
|
+
function Draw-Dot([bool]$on) {
|
|
97
|
+
$bmp = New-Object System.Drawing.Bitmap(40, 40)
|
|
98
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
99
|
+
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
|
100
|
+
$g.Clear([System.Drawing.Color]::Transparent)
|
|
101
|
+
if ($on) {
|
|
102
|
+
$g.FillEllipse([System.Drawing.Brushes]::LimeGreen, 4, 4, 32, 32)
|
|
103
|
+
# lightning bolt
|
|
104
|
+
$pen = New-Object System.Drawing.Pen([System.Drawing.Color]::White, 2.5)
|
|
105
|
+
$pts = @(
|
|
106
|
+
[System.Drawing.Point]::new(24, 8),
|
|
107
|
+
[System.Drawing.Point]::new(16, 22),
|
|
108
|
+
[System.Drawing.Point]::new(22, 22),
|
|
109
|
+
[System.Drawing.Point]::new(14, 32)
|
|
110
|
+
)
|
|
111
|
+
$g.DrawLines($pen, $pts)
|
|
112
|
+
$pen.Dispose()
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
$g.FillEllipse([System.Drawing.Brushes]::DimGray, 4, 4, 32, 32)
|
|
116
|
+
$pen = New-Object System.Drawing.Pen([System.Drawing.Color]::Gray, 2)
|
|
117
|
+
$g.DrawLine($pen, 12, 12, 28, 28)
|
|
118
|
+
$g.DrawLine($pen, 28, 12, 12, 28)
|
|
119
|
+
$pen.Dispose()
|
|
120
|
+
}
|
|
121
|
+
$g.Dispose()
|
|
122
|
+
$picDot.Image = $bmp
|
|
123
|
+
}
|
|
124
|
+
Draw-Dot $false
|
|
125
|
+
|
|
126
|
+
$lblStatus = New-Object System.Windows.Forms.Label
|
|
127
|
+
$lblStatus.Text = "OFF"
|
|
128
|
+
$lblStatus.Font = New-Object System.Drawing.Font("Segoe UI", 20, [System.Drawing.FontStyle]::Bold)
|
|
129
|
+
$lblStatus.ForeColor = $colorOff
|
|
130
|
+
$lblStatus.Location = New-Object System.Drawing.Point(66, 14)
|
|
131
|
+
$lblStatus.Size = New-Object System.Drawing.Size(100, 36)
|
|
132
|
+
$pnlStatus.Controls.Add($lblStatus)
|
|
133
|
+
|
|
134
|
+
$lblStatusSub = New-Object System.Windows.Forms.Label
|
|
135
|
+
$lblStatusSub.Text = "Click button to enable"
|
|
136
|
+
$lblStatusSub.Font = $fontMono
|
|
137
|
+
$lblStatusSub.ForeColor = $colorDim
|
|
138
|
+
$lblStatusSub.Location = New-Object System.Drawing.Point(66, 50)
|
|
139
|
+
$lblStatusSub.Size = New-Object System.Drawing.Size(180, 20)
|
|
140
|
+
$pnlStatus.Controls.Add($lblStatusSub)
|
|
141
|
+
|
|
142
|
+
# ---- Stats ----
|
|
143
|
+
$pnlStats = New-Object System.Windows.Forms.Panel
|
|
144
|
+
$pnlStats.Location = New-Object System.Drawing.Point(12, 158)
|
|
145
|
+
$pnlStats.Size = New-Object System.Drawing.Size(254, 50)
|
|
146
|
+
$pnlStats.BackColor = $colorPanel
|
|
147
|
+
$form.Controls.Add($pnlStats)
|
|
148
|
+
|
|
149
|
+
$lblCountLabel = New-Object System.Windows.Forms.Label
|
|
150
|
+
$lblCountLabel.Text = "Accepted"
|
|
151
|
+
$lblCountLabel.Font = $fontMono
|
|
152
|
+
$lblCountLabel.ForeColor = $colorDim
|
|
153
|
+
$lblCountLabel.Location = New-Object System.Drawing.Point(12, 8)
|
|
154
|
+
$lblCountLabel.Size = New-Object System.Drawing.Size(80, 16)
|
|
155
|
+
$pnlStats.Controls.Add($lblCountLabel)
|
|
156
|
+
|
|
157
|
+
$lblCount = New-Object System.Windows.Forms.Label
|
|
158
|
+
$lblCount.Text = "0"
|
|
159
|
+
$lblCount.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)
|
|
160
|
+
$lblCount.ForeColor = $colorAccent
|
|
161
|
+
$lblCount.Location = New-Object System.Drawing.Point(100, 4)
|
|
162
|
+
$lblCount.Size = New-Object System.Drawing.Size(80, 30)
|
|
163
|
+
$pnlStats.Controls.Add($lblCount)
|
|
164
|
+
|
|
165
|
+
$lblUnit = New-Object System.Windows.Forms.Label
|
|
166
|
+
$lblUnit.Text = "commands"
|
|
167
|
+
$lblUnit.Font = $fontMono
|
|
168
|
+
$lblUnit.ForeColor = $colorDim
|
|
169
|
+
$lblUnit.Location = New-Object System.Drawing.Point(180, 10)
|
|
170
|
+
$lblUnit.Size = New-Object System.Drawing.Size(70, 16)
|
|
171
|
+
$pnlStats.Controls.Add($lblUnit)
|
|
172
|
+
|
|
173
|
+
# ---- Toggle Button ----
|
|
174
|
+
$btnToggle = New-Object System.Windows.Forms.Button
|
|
175
|
+
$btnToggle.Text = "▶ ENABLE AUTO-ACCEPT"
|
|
176
|
+
$btnToggle.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
|
|
177
|
+
$btnToggle.Size = New-Object System.Drawing.Size(254, 44)
|
|
178
|
+
$btnToggle.Location = New-Object System.Drawing.Point(12, 220)
|
|
179
|
+
$btnToggle.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
|
|
180
|
+
$btnToggle.FlatAppearance.BorderSize = 0
|
|
181
|
+
$btnToggle.BackColor = [System.Drawing.Color]::FromArgb(0, 150, 80)
|
|
182
|
+
$btnToggle.ForeColor = $colorText
|
|
183
|
+
$btnToggle.Cursor = [System.Windows.Forms.Cursors]::Hand
|
|
184
|
+
$form.Controls.Add($btnToggle)
|
|
185
|
+
|
|
186
|
+
# ---- Interval label ----
|
|
187
|
+
$lblInterval = New-Object System.Windows.Forms.Label
|
|
188
|
+
$lblInterval.Text = "Interval: 800ms | Only when VS Code focused"
|
|
189
|
+
$lblInterval.Font = $fontMono
|
|
190
|
+
$lblInterval.ForeColor = $colorDim
|
|
191
|
+
$lblInterval.Location = New-Object System.Drawing.Point(12, 274)
|
|
192
|
+
$lblInterval.Size = New-Object System.Drawing.Size(254, 16)
|
|
193
|
+
$lblInterval.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
|
194
|
+
$form.Controls.Add($lblInterval)
|
|
195
|
+
|
|
196
|
+
# ---- System tray icon ----
|
|
197
|
+
$tray = New-Object System.Windows.Forms.NotifyIcon
|
|
198
|
+
$tray.Text = "Antigravity Auto-Accept: OFF"
|
|
199
|
+
$tray.Visible = $true
|
|
200
|
+
|
|
201
|
+
$trayBmp = New-Object System.Drawing.Bitmap(16, 16)
|
|
202
|
+
$tg = [System.Drawing.Graphics]::FromImage($trayBmp)
|
|
203
|
+
$tg.Clear([System.Drawing.Color]::Transparent)
|
|
204
|
+
$tg.FillEllipse([System.Drawing.Brushes]::CornflowerBlue, 1, 1, 14, 14)
|
|
205
|
+
$tg.Dispose()
|
|
206
|
+
$tray.Icon = [System.Drawing.Icon]::FromHandle($trayBmp.GetHicon())
|
|
207
|
+
|
|
208
|
+
$trayMenu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
209
|
+
$trayShow = New-Object System.Windows.Forms.ToolStripMenuItem "Show Panel"
|
|
210
|
+
$trayToggle = New-Object System.Windows.Forms.ToolStripMenuItem "Enable Auto-Accept"
|
|
211
|
+
$traySep = New-Object System.Windows.Forms.ToolStripSeparator
|
|
212
|
+
$trayExit = New-Object System.Windows.Forms.ToolStripMenuItem "Exit"
|
|
213
|
+
$trayMenu.Items.AddRange(@($trayShow, $trayToggle, $traySep, $trayExit))
|
|
214
|
+
$tray.ContextMenuStrip = $trayMenu
|
|
215
|
+
|
|
216
|
+
# ---- Core: Send Alt+A ----
|
|
217
|
+
function Send-AltA {
|
|
218
|
+
$fgWnd = [WinAPI]::GetForegroundWindow()
|
|
219
|
+
$fgPid = 0
|
|
220
|
+
[WinAPI]::GetWindowThreadProcessId($fgWnd, [ref]$fgPid) | Out-Null
|
|
221
|
+
$fgProc = Get-Process -Id $fgPid -ErrorAction SilentlyContinue
|
|
222
|
+
if ($fgProc -and $fgProc.ProcessName -match 'Code') {
|
|
223
|
+
[System.Windows.Forms.SendKeys]::SendWait("%a")
|
|
224
|
+
$script:Count++
|
|
225
|
+
$lblCount.Text = "$($script:Count)"
|
|
226
|
+
$tray.Text = "AG Auto-Accept: ON ($($script:Count) accepted)"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
# ---- Toggle logic ----
|
|
231
|
+
function Toggle {
|
|
232
|
+
$script:IsEnabled = -not $script:IsEnabled
|
|
233
|
+
|
|
234
|
+
if ($script:IsEnabled) {
|
|
235
|
+
$script:Count = 0
|
|
236
|
+
$lblCount.Text = "0"
|
|
237
|
+
|
|
238
|
+
# Start timer
|
|
239
|
+
$script:Timer = New-Object System.Windows.Forms.Timer
|
|
240
|
+
$script:Timer.Interval = 800
|
|
241
|
+
$script:Timer.Add_Tick({ Send-AltA })
|
|
242
|
+
$script:Timer.Start()
|
|
243
|
+
|
|
244
|
+
# Update UI
|
|
245
|
+
$lblStatus.Text = "ON"
|
|
246
|
+
$lblStatus.ForeColor = $colorOn
|
|
247
|
+
$lblStatusSub.Text = "Sending Alt+A every 800ms..."
|
|
248
|
+
$btnToggle.Text = "⏸ DISABLE AUTO-ACCEPT"
|
|
249
|
+
$btnToggle.BackColor = [System.Drawing.Color]::FromArgb(180, 50, 50)
|
|
250
|
+
$pnlStatus.BackColor = [System.Drawing.Color]::FromArgb(20, 60, 30)
|
|
251
|
+
$trayToggle.Text = "Disable Auto-Accept"
|
|
252
|
+
$tray.Text = "AG Auto-Accept: ON"
|
|
253
|
+
Draw-Dot $true
|
|
254
|
+
|
|
255
|
+
$tray.ShowBalloonTip(2000, "Auto-Accept ON", "Tự động accept Antigravity commands", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
if ($script:Timer) {
|
|
259
|
+
$script:Timer.Stop()
|
|
260
|
+
$script:Timer.Dispose()
|
|
261
|
+
$script:Timer = $null
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# Update UI
|
|
265
|
+
$lblStatus.Text = "OFF"
|
|
266
|
+
$lblStatus.ForeColor = $colorOff
|
|
267
|
+
$lblStatusSub.Text = "Click button to enable"
|
|
268
|
+
$btnToggle.Text = "▶ ENABLE AUTO-ACCEPT"
|
|
269
|
+
$btnToggle.BackColor = [System.Drawing.Color]::FromArgb(0, 150, 80)
|
|
270
|
+
$pnlStatus.BackColor = $colorPanel
|
|
271
|
+
$trayToggle.Text = "Enable Auto-Accept"
|
|
272
|
+
$tray.Text = "AG Auto-Accept: OFF"
|
|
273
|
+
Draw-Dot $false
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
# ---- Event handlers ----
|
|
278
|
+
$btnToggle.Add_Click({ Toggle })
|
|
279
|
+
$trayToggle.Add_Click({ Toggle })
|
|
280
|
+
$trayShow.Add_Click({ $form.Show(); $form.BringToFront() })
|
|
281
|
+
$tray.Add_DoubleClick({ $form.Show(); $form.BringToFront() })
|
|
282
|
+
$trayExit.Add_Click({
|
|
283
|
+
if ($script:Timer) { $script:Timer.Stop(); $script:Timer.Dispose() }
|
|
284
|
+
$tray.Visible = $false
|
|
285
|
+
$tray.Dispose()
|
|
286
|
+
$form.Close()
|
|
287
|
+
})
|
|
288
|
+
$form.Add_FormClosing({
|
|
289
|
+
param($s, $e)
|
|
290
|
+
# Minimize to tray instead of closing
|
|
291
|
+
if ($e.CloseReason -eq [System.Windows.Forms.CloseReason]::UserClosing) {
|
|
292
|
+
$e.Cancel = $true
|
|
293
|
+
$form.Hide()
|
|
294
|
+
$tray.ShowBalloonTip(1500, "Still running!", "Double-click tray icon to reopen.", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
# Keep always on top
|
|
299
|
+
$form.Add_Shown({
|
|
300
|
+
[WinAPI]::SetWindowPos($form.Handle, $HWND_TOPMOST, 0, 0, 0, 0, ($SWP_NOSIZE -bor $SWP_NOMOVE)) | Out-Null
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
# ---- Launch ----
|
|
304
|
+
$tray.ShowBalloonTip(2500, "Antigravity Auto-Accept", "✅ Running! Click toggle button to enable.", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
305
|
+
[System.Windows.Forms.Application]::Run($form)
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Antigravity Auto-Accept
|
|
2
|
+
|
|
3
|
+
Auto-accept Antigravity tool call prompts with a **one-click toggle** in the VS Code status bar.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ⚡ **Status bar toggle** — Click to enable/disable auto-accept
|
|
8
|
+
- 🎯 **Smart targeting** — Only sends keystrokes when VS Code is focused
|
|
9
|
+
- ⌨️ **Keyboard shortcut** — `Ctrl+Shift+F12` to toggle
|
|
10
|
+
- 📊 **Accept counter** — Shows how many commands were auto-accepted
|
|
11
|
+
- ⚙️ **Configurable** — Adjust interval, auto-start on boot
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
# Run from this directory
|
|
17
|
+
.\install.ps1
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then reload VS Code (`Ctrl+Shift+P` → "Developer: Reload Window").
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
1. Look for **`$(circle-slash) Auto-Accept: OFF`** in the status bar (bottom right)
|
|
25
|
+
2. Click it or press `Ctrl+Shift+F12` to toggle ON
|
|
26
|
+
3. When ON (⚡ yellow background), all Antigravity commands will be auto-accepted
|
|
27
|
+
4. Click again to turn OFF
|
|
28
|
+
|
|
29
|
+
## Settings
|
|
30
|
+
|
|
31
|
+
| Setting | Default | Description |
|
|
32
|
+
|---------|---------|-------------|
|
|
33
|
+
| `antigravityAutoAccept.intervalMs` | `800` | Interval between accept attempts (ms) |
|
|
34
|
+
| `antigravityAutoAccept.enabledOnStartup` | `false` | Auto-enable on VS Code start |
|
|
35
|
+
|
|
36
|
+
## How It Works
|
|
37
|
+
|
|
38
|
+
Sends `Alt+A` (the Antigravity accept shortcut) via PowerShell `SendKeys` at a configurable interval, but **only** when VS Code is the foreground window.
|
package/extension.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const vscode = require('vscode');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { fork } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const PATCHER = path.join(__dirname, 'patcher.js');
|
|
9
|
+
|
|
10
|
+
// ─── State ───────────────────────────────────────────────────────────────────
|
|
11
|
+
/** @type {vscode.StatusBarItem} */
|
|
12
|
+
let statusBarItem;
|
|
13
|
+
/** @type {boolean} */
|
|
14
|
+
let isPatchApplied = false;
|
|
15
|
+
/** @type {AntigravityPanelProvider | null} */
|
|
16
|
+
let panelProvider = null;
|
|
17
|
+
/** @type {{ basePath: string|null, files: any[], patched: boolean } | null} */
|
|
18
|
+
let _cachedStatus = null;
|
|
19
|
+
|
|
20
|
+
// ─── Child Process Bridge ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Runs patcher.js in a child process.
|
|
24
|
+
* All heavy file I/O lives in patcher.js — never blocks the extension host.
|
|
25
|
+
* @param {'status'|'apply'|'revert'} command
|
|
26
|
+
* @returns {Promise<any>}
|
|
27
|
+
*/
|
|
28
|
+
function runPatcher(command) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const child = fork(PATCHER, [], { silent: true });
|
|
31
|
+
const channel = vscode.window.createOutputChannel('AutoAccept');
|
|
32
|
+
|
|
33
|
+
child.on('message', (msg) => {
|
|
34
|
+
if (!msg || typeof msg !== 'object') return;
|
|
35
|
+
const m = /** @type {{type:string,msg?:string}} */(msg);
|
|
36
|
+
if (m.type === 'log') {
|
|
37
|
+
console.log(m.msg);
|
|
38
|
+
channel.appendLine(m.msg || '');
|
|
39
|
+
} else {
|
|
40
|
+
resolve(msg); // status or result message
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
child.on('error', (err) => {
|
|
45
|
+
channel.appendLine(`[AutoAccept] fork error: ${err.message}`);
|
|
46
|
+
reject(err);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
child.on('exit', (code) => {
|
|
50
|
+
if (code !== 0) {
|
|
51
|
+
resolve({
|
|
52
|
+
type: 'result',
|
|
53
|
+
success: false,
|
|
54
|
+
message: `Process exited with code ${code}. Check Output > AutoAccept for details.`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
child.send({ command });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Patch Manager (async, non-blocking) ─────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets patch status via child process.
|
|
67
|
+
* @returns {Promise<{basePath:string|null, patched:boolean, files:Array<{label:string,patched:boolean,exists:boolean}>}>}
|
|
68
|
+
*/
|
|
69
|
+
async function getPatchStatus() {
|
|
70
|
+
try {
|
|
71
|
+
const res = /** @type {any} */(await runPatcher('status'));
|
|
72
|
+
const files = res.files || [];
|
|
73
|
+
_cachedStatus = { basePath: res.basePath || null, files, patched: files.some((/** @type {any} */f) => f.patched) };
|
|
74
|
+
return _cachedStatus;
|
|
75
|
+
} catch {
|
|
76
|
+
return { basePath: null, patched: false, files: [] };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Applies the patch via child process.
|
|
82
|
+
* @returns {Promise<{success:boolean, message:string}>}
|
|
83
|
+
*/
|
|
84
|
+
async function applyPatch() {
|
|
85
|
+
try {
|
|
86
|
+
const res = /** @type {any} */(await runPatcher('apply'));
|
|
87
|
+
const success = res.success === true;
|
|
88
|
+
isPatchApplied = success;
|
|
89
|
+
await refreshStatus();
|
|
90
|
+
return { success, message: res.message || '' };
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return { success: false, message: `❌ Error: ${e}` };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reverts the patch via child process.
|
|
98
|
+
* @returns {Promise<{success:boolean, message:string}>}
|
|
99
|
+
*/
|
|
100
|
+
async function revertPatch() {
|
|
101
|
+
try {
|
|
102
|
+
const res = /** @type {any} */(await runPatcher('revert'));
|
|
103
|
+
isPatchApplied = false;
|
|
104
|
+
await refreshStatus();
|
|
105
|
+
return { success: res.success === true, message: res.message || '' };
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return { success: false, message: `❌ Error: ${e}` };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Refreshes status and updates all UI elements. */
|
|
112
|
+
async function refreshStatus() {
|
|
113
|
+
const status = await getPatchStatus();
|
|
114
|
+
isPatchApplied = status.patched;
|
|
115
|
+
updateStatusBarFromCache();
|
|
116
|
+
if (panelProvider) panelProvider.sendStatus(status);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── Sidebar WebView ──────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
class AntigravityPanelProvider {
|
|
122
|
+
/** @param {vscode.ExtensionContext} context */
|
|
123
|
+
constructor(context) {
|
|
124
|
+
this._context = context;
|
|
125
|
+
/** @type {vscode.WebviewView | null} */
|
|
126
|
+
this._view = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** @param {vscode.WebviewView} webviewView */
|
|
130
|
+
resolveWebviewView(webviewView) {
|
|
131
|
+
this._view = webviewView;
|
|
132
|
+
webviewView.webview.options = { enableScripts: true };
|
|
133
|
+
webviewView.webview.html = this._getHtml();
|
|
134
|
+
|
|
135
|
+
webviewView.webview.onDidReceiveMessage(async (msg) => {
|
|
136
|
+
if (msg.command === 'apply') {
|
|
137
|
+
this._postLoading('⏳ Patching...');
|
|
138
|
+
const result = await applyPatch();
|
|
139
|
+
vscode.window.showInformationMessage(result.message);
|
|
140
|
+
} else if (msg.command === 'revert') {
|
|
141
|
+
this._postLoading('⏳ Reverting...');
|
|
142
|
+
const result = await revertPatch();
|
|
143
|
+
vscode.window.showInformationMessage(result.message);
|
|
144
|
+
} else if (msg.command === 'refresh') {
|
|
145
|
+
this._postLoading('⏳ Checking...');
|
|
146
|
+
await refreshStatus();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Initial load
|
|
151
|
+
refreshStatus();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** @param {string} text */
|
|
155
|
+
_postLoading(text) {
|
|
156
|
+
if (this._view) this._view.webview.postMessage({ command: 'loading', text });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** @param {{basePath:string|null,patched:boolean,files:any[]}} status */
|
|
160
|
+
sendStatus(status) {
|
|
161
|
+
if (!this._view) return;
|
|
162
|
+
this._view.webview.postMessage({
|
|
163
|
+
command: 'update',
|
|
164
|
+
patched: status.patched,
|
|
165
|
+
basePath: status.basePath,
|
|
166
|
+
files: status.files,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @deprecated use sendStatus */
|
|
171
|
+
updateState() { refreshStatus(); }
|
|
172
|
+
|
|
173
|
+
_getHtml() {
|
|
174
|
+
return /* html */`<!DOCTYPE html>
|
|
175
|
+
<html lang="en">
|
|
176
|
+
<head>
|
|
177
|
+
<meta charset="UTF-8">
|
|
178
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
179
|
+
<style>
|
|
180
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
181
|
+
body{
|
|
182
|
+
font-family:'Segoe UI',sans-serif;
|
|
183
|
+
background:var(--vscode-sideBar-background);
|
|
184
|
+
color:var(--vscode-foreground);
|
|
185
|
+
padding:16px;user-select:none;
|
|
186
|
+
}
|
|
187
|
+
.header{
|
|
188
|
+
display:flex;align-items:center;gap:8px;
|
|
189
|
+
margin-bottom:16px;padding-bottom:12px;
|
|
190
|
+
border-bottom:1px solid var(--vscode-panel-border);
|
|
191
|
+
}
|
|
192
|
+
.header-icon{font-size:20px}
|
|
193
|
+
.header-title{font-size:13px;font-weight:600;letter-spacing:.3px}
|
|
194
|
+
.header-sub{font-size:11px;color:var(--vscode-descriptionForeground);margin-top:2px}
|
|
195
|
+
|
|
196
|
+
.status-card{
|
|
197
|
+
border-radius:8px;padding:16px;margin-bottom:12px;
|
|
198
|
+
background:var(--vscode-editor-background);
|
|
199
|
+
border:1px solid var(--vscode-panel-border);
|
|
200
|
+
transition:border-color .3s,background .3s;
|
|
201
|
+
}
|
|
202
|
+
.status-card.patched{border-color:#4ec94e;background:rgba(78,201,78,.07)}
|
|
203
|
+
.status-card.not-found{border-color:#e06c75;background:rgba(224,108,117,.07)}
|
|
204
|
+
.status-row{display:flex;align-items:center;gap:12px}
|
|
205
|
+
.dot{
|
|
206
|
+
width:36px;height:36px;border-radius:50%;
|
|
207
|
+
display:flex;align-items:center;justify-content:center;
|
|
208
|
+
font-size:18px;flex-shrink:0;background:#3c3c3c;
|
|
209
|
+
transition:background .3s;
|
|
210
|
+
}
|
|
211
|
+
.dot.patched{background:#4ec94e}
|
|
212
|
+
.dot.not-found{background:#e06c75}
|
|
213
|
+
.status-label{font-size:18px;font-weight:700;line-height:1}
|
|
214
|
+
.status-label.patched{color:#4ec94e}
|
|
215
|
+
.status-label.pending{color:#e5c07b}
|
|
216
|
+
.status-label.not-found{color:#e06c75}
|
|
217
|
+
.status-label.loading{color:var(--vscode-descriptionForeground)}
|
|
218
|
+
.status-desc{font-size:11px;color:var(--vscode-descriptionForeground);margin-top:4px}
|
|
219
|
+
|
|
220
|
+
.path-box{
|
|
221
|
+
font-size:10px;color:var(--vscode-descriptionForeground);
|
|
222
|
+
background:var(--vscode-editor-background);
|
|
223
|
+
border:1px solid var(--vscode-panel-border);
|
|
224
|
+
border-radius:4px;padding:6px 8px;margin-bottom:10px;
|
|
225
|
+
word-break:break-all;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.files-list{margin-bottom:12px}
|
|
229
|
+
.file-item{
|
|
230
|
+
display:flex;align-items:center;gap:6px;
|
|
231
|
+
font-size:11px;padding:4px 0;
|
|
232
|
+
}
|
|
233
|
+
.file-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
234
|
+
.file-dot.patched{background:#4ec94e}
|
|
235
|
+
.file-dot.pending{background:#e5c07b}
|
|
236
|
+
|
|
237
|
+
.btn{
|
|
238
|
+
width:100%;padding:10px;border:none;border-radius:6px;
|
|
239
|
+
font-size:12px;font-weight:700;letter-spacing:.5px;
|
|
240
|
+
cursor:pointer;transition:background .2s,transform .1s;
|
|
241
|
+
font-family:inherit;margin-bottom:6px;
|
|
242
|
+
}
|
|
243
|
+
.btn:active{transform:scale(.98)}
|
|
244
|
+
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
245
|
+
.btn-apply{background:#0e7a4c;color:#fff}
|
|
246
|
+
.btn-apply:hover:not(:disabled){background:#0f9058}
|
|
247
|
+
.btn-revert{background:#5a1a1a;color:#fff}
|
|
248
|
+
.btn-revert:hover:not(:disabled){background:#7a2020}
|
|
249
|
+
.btn-refresh{background:var(--vscode-button-secondaryBackground);color:var(--vscode-button-secondaryForeground)}
|
|
250
|
+
|
|
251
|
+
.note{
|
|
252
|
+
margin-top:10px;font-size:10px;
|
|
253
|
+
color:var(--vscode-descriptionForeground);
|
|
254
|
+
text-align:center;line-height:1.5;
|
|
255
|
+
}
|
|
256
|
+
</style>
|
|
257
|
+
</head>
|
|
258
|
+
<body>
|
|
259
|
+
<div class="header">
|
|
260
|
+
<span class="header-icon">⚡</span>
|
|
261
|
+
<div>
|
|
262
|
+
<div class="header-title">Antigravity Auto-Accept</div>
|
|
263
|
+
<div class="header-sub">Patches "Always Proceed" to auto-run</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div class="status-card" id="card">
|
|
268
|
+
<div class="status-row">
|
|
269
|
+
<div class="dot" id="dot">⊘</div>
|
|
270
|
+
<div>
|
|
271
|
+
<div class="status-label loading" id="lbl">Loading...</div>
|
|
272
|
+
<div class="status-desc" id="desc">Detecting Antigravity...</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<div class="path-box" id="pathBox" style="display:none"></div>
|
|
278
|
+
|
|
279
|
+
<div class="files-list" id="filesList"></div>
|
|
280
|
+
|
|
281
|
+
<button class="btn btn-apply" id="btnApply" onclick="send('apply')" style="display:none">⚡ APPLY PATCH</button>
|
|
282
|
+
<button class="btn btn-revert" id="btnRevert" onclick="send('revert')" style="display:none">↩ REVERT PATCH</button>
|
|
283
|
+
<button class="btn btn-refresh" onclick="send('refresh')">🔄 Refresh Status</button>
|
|
284
|
+
|
|
285
|
+
<div class="note" id="noteBox"></div>
|
|
286
|
+
|
|
287
|
+
<script>
|
|
288
|
+
const vscode = acquireVsCodeApi();
|
|
289
|
+
function send(cmd) {
|
|
290
|
+
document.getElementById('btnApply').disabled = true;
|
|
291
|
+
document.getElementById('btnRevert').disabled = true;
|
|
292
|
+
document.getElementById('btnRefresh') && (document.getElementById('btnRefresh').disabled = true);
|
|
293
|
+
vscode.postMessage({ command: cmd });
|
|
294
|
+
}
|
|
295
|
+
send('refresh');
|
|
296
|
+
|
|
297
|
+
window.addEventListener('message', e => {
|
|
298
|
+
const { command, patched, basePath, files, text } = e.data;
|
|
299
|
+
|
|
300
|
+
if (command === 'loading') {
|
|
301
|
+
document.getElementById('lbl').className = 'status-label loading';
|
|
302
|
+
document.getElementById('lbl').textContent = text || '⏳ Working...';
|
|
303
|
+
document.getElementById('desc').textContent = 'Please wait...';
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (command !== 'update') return;
|
|
308
|
+
|
|
309
|
+
// Re-enable buttons
|
|
310
|
+
document.getElementById('btnApply').disabled = false;
|
|
311
|
+
document.getElementById('btnRevert').disabled = false;
|
|
312
|
+
|
|
313
|
+
const notFound = !basePath;
|
|
314
|
+
|
|
315
|
+
document.getElementById('card').className = 'status-card' + (notFound ? ' not-found' : patched ? ' patched' : '');
|
|
316
|
+
document.getElementById('dot').className = 'dot' + (notFound ? ' not-found' : patched ? ' patched' : '');
|
|
317
|
+
document.getElementById('dot').textContent = notFound ? '✕' : patched ? '✓' : '○';
|
|
318
|
+
|
|
319
|
+
const lbl = document.getElementById('lbl');
|
|
320
|
+
lbl.className = 'status-label ' + (notFound ? 'not-found' : patched ? 'patched' : 'pending');
|
|
321
|
+
lbl.textContent = notFound ? 'NOT FOUND' : patched ? 'PATCHED' : 'NOT PATCHED';
|
|
322
|
+
|
|
323
|
+
document.getElementById('desc').textContent = notFound
|
|
324
|
+
? 'Antigravity not installed'
|
|
325
|
+
: patched
|
|
326
|
+
? 'useEffect added — restart Antigravity!'
|
|
327
|
+
: 'Patch not applied yet';
|
|
328
|
+
|
|
329
|
+
const pathBox = document.getElementById('pathBox');
|
|
330
|
+
if (basePath) {
|
|
331
|
+
pathBox.textContent = '📍 ' + basePath;
|
|
332
|
+
pathBox.style.display = '';
|
|
333
|
+
} else {
|
|
334
|
+
pathBox.style.display = 'none';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const filesList = document.getElementById('filesList');
|
|
338
|
+
filesList.innerHTML = '';
|
|
339
|
+
if (files && files.length) {
|
|
340
|
+
for (const f of files) {
|
|
341
|
+
const d = document.createElement('div');
|
|
342
|
+
d.className = 'file-item';
|
|
343
|
+
d.innerHTML = '<div class="file-dot ' + (f.patched ? 'patched' : 'pending') + '"></div>'
|
|
344
|
+
+ '<span>' + f.label + ': ' + (f.patched ? '✅ patched' : '⬜ not patched') + '</span>';
|
|
345
|
+
filesList.appendChild(d);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
document.getElementById('btnApply').style.display = (notFound || patched) ? 'none' : '';
|
|
350
|
+
document.getElementById('btnRevert').style.display = patched ? '' : 'none';
|
|
351
|
+
|
|
352
|
+
document.getElementById('noteBox').textContent = notFound
|
|
353
|
+
? '⚠️ Install Antigravity first'
|
|
354
|
+
: patched
|
|
355
|
+
? '💡 Re-run after Antigravity updates'
|
|
356
|
+
: '💡 Apply patch once, then restart Antigravity';
|
|
357
|
+
});
|
|
358
|
+
</script>
|
|
359
|
+
</body>
|
|
360
|
+
</html>`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ─── Status Bar ──────────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
function updateStatusBarFromCache() {
|
|
367
|
+
const status = _cachedStatus;
|
|
368
|
+
if (!status || !status.basePath) {
|
|
369
|
+
statusBarItem.text = `$(warning) AG Patch: Not Found`;
|
|
370
|
+
statusBarItem.tooltip = 'Antigravity not detected';
|
|
371
|
+
statusBarItem.backgroundColor = undefined;
|
|
372
|
+
} else if (status.patched) {
|
|
373
|
+
statusBarItem.text = `$(check) AG Patch: Active`;
|
|
374
|
+
statusBarItem.tooltip = 'Auto-Accept patch is applied — click to manage';
|
|
375
|
+
statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
|
|
376
|
+
} else {
|
|
377
|
+
statusBarItem.text = `$(zap) AG Patch: OFF`;
|
|
378
|
+
statusBarItem.tooltip = 'Auto-Accept patch not applied — click to open panel';
|
|
379
|
+
statusBarItem.backgroundColor = undefined;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** @deprecated kept for backward compat */
|
|
384
|
+
function updateStatusBar() { updateStatusBarFromCache(); }
|
|
385
|
+
|
|
386
|
+
// ─── Activate / Deactivate ──────────────────────────────────────────────
|
|
387
|
+
|
|
388
|
+
/** @param {vscode.ExtensionContext} context */
|
|
389
|
+
function activate(context) {
|
|
390
|
+
// Status bar — shows spinner until first async check completes
|
|
391
|
+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
392
|
+
statusBarItem.command = 'antigravityAutoAccept.openPanel';
|
|
393
|
+
statusBarItem.text = `$(sync~spin) AG Patch`;
|
|
394
|
+
statusBarItem.tooltip = 'Checking patch status...';
|
|
395
|
+
statusBarItem.show();
|
|
396
|
+
|
|
397
|
+
// Sidebar
|
|
398
|
+
panelProvider = new AntigravityPanelProvider(context);
|
|
399
|
+
context.subscriptions.push(
|
|
400
|
+
vscode.window.registerWebviewViewProvider(
|
|
401
|
+
'antigravityAutoAccept.panel',
|
|
402
|
+
panelProvider,
|
|
403
|
+
{ webviewOptions: { retainContextWhenHidden: true } },
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// Commands
|
|
408
|
+
context.subscriptions.push(
|
|
409
|
+
vscode.commands.registerCommand('antigravityAutoAccept.applyPatch', async () => {
|
|
410
|
+
const result = await applyPatch();
|
|
411
|
+
vscode.window.showInformationMessage(result.message);
|
|
412
|
+
}),
|
|
413
|
+
vscode.commands.registerCommand('antigravityAutoAccept.revertPatch', async () => {
|
|
414
|
+
const result = await revertPatch();
|
|
415
|
+
vscode.window.showInformationMessage(result.message);
|
|
416
|
+
}),
|
|
417
|
+
vscode.commands.registerCommand('antigravityAutoAccept.openPanel', () => {
|
|
418
|
+
vscode.commands.executeCommand('antigravityAutoAccept.panel.focus');
|
|
419
|
+
}),
|
|
420
|
+
vscode.commands.registerCommand('antigravityAutoAccept.checkStatus', async () => {
|
|
421
|
+
const status = await getPatchStatus();
|
|
422
|
+
if (!status.basePath) {
|
|
423
|
+
vscode.window.showWarningMessage('Antigravity not found!');
|
|
424
|
+
} else {
|
|
425
|
+
vscode.window.showInformationMessage(
|
|
426
|
+
`Patch status: ${status.patched ? '✅ Applied' : '⬜ Not applied'} | ${status.basePath}`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
context.subscriptions.push(statusBarItem);
|
|
433
|
+
|
|
434
|
+
// Async startup — never blocks extension host!
|
|
435
|
+
(async () => {
|
|
436
|
+
const status = await getPatchStatus();
|
|
437
|
+
isPatchApplied = status.patched;
|
|
438
|
+
updateStatusBarFromCache();
|
|
439
|
+
if (panelProvider) panelProvider.sendStatus(status);
|
|
440
|
+
|
|
441
|
+
const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
442
|
+
if (cfg.get('applyOnStartup') && !status.patched && status.basePath) {
|
|
443
|
+
const result = await applyPatch();
|
|
444
|
+
if (result.success) {
|
|
445
|
+
console.log('[AutoAccept] Auto-patch applied on startup');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
})();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function deactivate() { /* nothing to clean up */ }
|
|
452
|
+
|
|
453
|
+
module.exports = { activate, deactivate };
|
package/icon-sidebar.svg
ADDED
package/icon.png
ADDED
|
Binary file
|
package/install.ps1
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Antigravity Auto-Accept - Installation Script
|
|
2
|
+
# Creates a symlink in VS Code extensions directory for development
|
|
3
|
+
|
|
4
|
+
$ExtensionName = "antigravity-auto-accept"
|
|
5
|
+
$SourceDir = $PSScriptRoot
|
|
6
|
+
$VSCodeExtDir = Join-Path $env:USERPROFILE ".vscode\extensions\$ExtensionName"
|
|
7
|
+
|
|
8
|
+
Write-Host ""
|
|
9
|
+
Write-Host "========================================" -ForegroundColor Cyan
|
|
10
|
+
Write-Host " Antigravity Auto-Accept Installer" -ForegroundColor Cyan
|
|
11
|
+
Write-Host "========================================" -ForegroundColor Cyan
|
|
12
|
+
Write-Host ""
|
|
13
|
+
|
|
14
|
+
# Check if already installed
|
|
15
|
+
if (Test-Path $VSCodeExtDir) {
|
|
16
|
+
Write-Host "[!] Extension already installed at: $VSCodeExtDir" -ForegroundColor Yellow
|
|
17
|
+
$confirm = Read-Host "Remove and reinstall? (y/n)"
|
|
18
|
+
if ($confirm -ne 'y') {
|
|
19
|
+
Write-Host "Cancelled." -ForegroundColor Red
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
# Remove existing (handle both symlink and directory)
|
|
23
|
+
if ((Get-Item $VSCodeExtDir).Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
|
24
|
+
(Get-Item $VSCodeExtDir).Delete()
|
|
25
|
+
} else {
|
|
26
|
+
Remove-Item $VSCodeExtDir -Recurse -Force
|
|
27
|
+
}
|
|
28
|
+
Write-Host "[OK] Removed existing installation" -ForegroundColor Green
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Create symlink (requires admin or developer mode)
|
|
32
|
+
try {
|
|
33
|
+
New-Item -ItemType SymbolicLink -Path $VSCodeExtDir -Target $SourceDir -ErrorAction Stop | Out-Null
|
|
34
|
+
Write-Host "[OK] Created symlink:" -ForegroundColor Green
|
|
35
|
+
Write-Host " $VSCodeExtDir -> $SourceDir" -ForegroundColor DarkGray
|
|
36
|
+
} catch {
|
|
37
|
+
Write-Host "[!] Symlink failed (may need admin or Developer Mode)." -ForegroundColor Yellow
|
|
38
|
+
Write-Host " Falling back to file copy..." -ForegroundColor Yellow
|
|
39
|
+
|
|
40
|
+
New-Item -ItemType Directory -Path $VSCodeExtDir -Force | Out-Null
|
|
41
|
+
Copy-Item -Path "$SourceDir\*" -Destination $VSCodeExtDir -Recurse -Force
|
|
42
|
+
Write-Host "[OK] Copied files to: $VSCodeExtDir" -ForegroundColor Green
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Write-Host ""
|
|
46
|
+
Write-Host "[NEXT] Reload VS Code:" -ForegroundColor Cyan
|
|
47
|
+
Write-Host " Ctrl+Shift+P -> 'Developer: Reload Window'" -ForegroundColor White
|
|
48
|
+
Write-Host ""
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antigravity-autopilot",
|
|
3
|
+
"displayName": "Antigravity AutoPilot",
|
|
4
|
+
"description": "Enables autopilot mode for Antigravity: automatically executes all tool calls and terminal commands without manual confirmation. Patches the runtime JS bundle to inject auto-accept logic whenever the 'Always Proceed' policy is active — regex-based and version-agnostic.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"publisher": "nguyen-hoang",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.85.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": ["Other"],
|
|
11
|
+
"activationEvents": ["onStartupFinished"],
|
|
12
|
+
"main": "./extension.js",
|
|
13
|
+
"icon": "icon.png",
|
|
14
|
+
"contributes": {
|
|
15
|
+
"viewsContainers": {
|
|
16
|
+
"activitybar": [
|
|
17
|
+
{
|
|
18
|
+
"id": "antigravity-auto-accept",
|
|
19
|
+
"title": "Antigravity Auto-Accept",
|
|
20
|
+
"icon": "icon-sidebar.svg"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"views": {
|
|
25
|
+
"antigravity-auto-accept": [
|
|
26
|
+
{
|
|
27
|
+
"type": "webview",
|
|
28
|
+
"id": "antigravityAutoAccept.panel",
|
|
29
|
+
"name": "Auto-Accept Patcher"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"commands": [
|
|
34
|
+
{
|
|
35
|
+
"command": "antigravityAutoAccept.applyPatch",
|
|
36
|
+
"title": "Antigravity: Apply Auto-Accept Patch",
|
|
37
|
+
"icon": "$(zap)"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"command": "antigravityAutoAccept.revertPatch",
|
|
41
|
+
"title": "Antigravity: Revert Auto-Accept Patch",
|
|
42
|
+
"icon": "$(discard)"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"command": "antigravityAutoAccept.checkStatus",
|
|
46
|
+
"title": "Antigravity: Check Patch Status",
|
|
47
|
+
"icon": "$(info)"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"command": "antigravityAutoAccept.openPanel",
|
|
51
|
+
"title": "Antigravity: Open Patcher Panel"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"keybindings": [
|
|
55
|
+
{
|
|
56
|
+
"command": "antigravityAutoAccept.applyPatch",
|
|
57
|
+
"key": "ctrl+shift+f12"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"configuration": {
|
|
61
|
+
"title": "Antigravity Auto-Accept",
|
|
62
|
+
"properties": {
|
|
63
|
+
"antigravityAutoAccept.applyOnStartup": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"default": false,
|
|
66
|
+
"description": "Automatically apply the patch when this extension activates (on startup). Safe to enable — patch is idempotent."
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
package/patcher.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// patcher.js — runs in child_process.fork(), handles all heavy file I/O
|
|
2
|
+
// Ported directly from https://github.com/Kanezal/better-antigravity/blob/main/fixes/auto-run-fix/patch.js
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
// ─── Installation Detection ──────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
function findAntigravityPath() {
|
|
12
|
+
const candidates = [];
|
|
13
|
+
if (process.platform === 'win32') {
|
|
14
|
+
candidates.push(
|
|
15
|
+
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Antigravity'),
|
|
16
|
+
path.join(process.env.PROGRAMFILES || '', 'Antigravity'),
|
|
17
|
+
path.join(process.env['PROGRAMFILES(X86)'] || '', 'Antigravity'),
|
|
18
|
+
);
|
|
19
|
+
} else if (process.platform === 'darwin') {
|
|
20
|
+
candidates.push(
|
|
21
|
+
'/Applications/Antigravity.app/Contents/Resources',
|
|
22
|
+
path.join(os.homedir(), 'Applications', 'Antigravity.app', 'Contents', 'Resources'),
|
|
23
|
+
);
|
|
24
|
+
} else {
|
|
25
|
+
candidates.push(
|
|
26
|
+
'/usr/share/antigravity',
|
|
27
|
+
'/opt/antigravity',
|
|
28
|
+
path.join(os.homedir(), '.local', 'share', 'antigravity'),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
for (const c of candidates) {
|
|
32
|
+
const f = path.join(c, 'resources', 'app', 'out', 'vs', 'workbench', 'workbench.desktop.main.js');
|
|
33
|
+
if (fs.existsSync(f)) return c;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getTargetFiles(basePath) {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
filePath: path.join(basePath, 'resources', 'app', 'out', 'vs', 'workbench', 'workbench.desktop.main.js'),
|
|
42
|
+
label: 'workbench',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
filePath: path.join(basePath, 'resources', 'app', 'out', 'jetskiAgent', 'main.js'),
|
|
46
|
+
label: 'jetskiAgent',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Smart Pattern Matching (ported from better-antigravity) ─────────────────
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Finds the onChange handler and extracts variable names, regardless of minification.
|
|
55
|
+
* Port of: https://github.com/Kanezal/better-antigravity/blob/main/fixes/auto-run-fix/patch.js
|
|
56
|
+
*/
|
|
57
|
+
function analyzeFile(content, label) {
|
|
58
|
+
const log = (msg) => process.send({ type: 'log', msg: `[AutoAccept] [${label}] ${msg}` });
|
|
59
|
+
|
|
60
|
+
// 1. Find the onChange handler: contains setTerminalAutoExecutionPolicy AND .EAGER
|
|
61
|
+
// Pattern: VARNAME=CALLBACK(ARG=>{...setTerminalAutoExecutionPolicy...,ARG===ENUM.EAGER&&CONFIRM(!0)},[...])
|
|
62
|
+
// Exact regex from https://github.com/Kanezal/better-antigravity/blob/main/fixes/auto-run-fix/patch.js
|
|
63
|
+
const onChangeRe = /(\w+)=(\w+)\((\w+)=>\{\w+\?\.setTerminalAutoExecutionPolicy\?\.\(\3\),\3===(\w+)\.EAGER&&(\w+)\(!0\)\},\[[\w,]*\]\)/;
|
|
64
|
+
const onChangeMatch = content.match(onChangeRe);
|
|
65
|
+
|
|
66
|
+
if (!onChangeMatch) {
|
|
67
|
+
log('❌ Could not find onChange handler pattern');
|
|
68
|
+
const idx = content.indexOf('setTerminalAutoExecutionPolicy');
|
|
69
|
+
if (idx >= 0) {
|
|
70
|
+
log(` Context: ...${content.slice(Math.max(0, idx - 80), idx + 120)}...`);
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const [fullMatch, assignVar, callbackAlias, argName, enumAlias, confirmFn] = onChangeMatch;
|
|
76
|
+
const matchIndex = content.indexOf(fullMatch);
|
|
77
|
+
|
|
78
|
+
log(`✓ Found onChange at offset ${matchIndex}`);
|
|
79
|
+
log(` callback=${callbackAlias}, enum=${enumAlias}, confirm=${confirmFn}`);
|
|
80
|
+
|
|
81
|
+
// 2. Find policy variable: VARNAME=HANDLER?.terminalAutoExecutionPolicy??ENUM.OFF
|
|
82
|
+
// NOTE: must use ?\. (optional chaining) — this was the bug in previous version
|
|
83
|
+
const policyRe = new RegExp(`(\\w+)=\\w+\\?\\.terminalAutoExecutionPolicy\\?\\?${enumAlias}\\.OFF`);
|
|
84
|
+
const policyMatch = content.substring(Math.max(0, matchIndex - 2000), matchIndex).match(policyRe);
|
|
85
|
+
|
|
86
|
+
if (!policyMatch) {
|
|
87
|
+
log('❌ Could not find policy variable');
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const policyVar = policyMatch[1];
|
|
91
|
+
log(` policyVar=${policyVar}`);
|
|
92
|
+
|
|
93
|
+
// 3. Find secureMode variable: VARNAME=HANDLER?.secureModeEnabled??!1
|
|
94
|
+
const secureRe = /(\w+)=\w+\?\.secureModeEnabled\?\?!1/;
|
|
95
|
+
const secureMatch = content.substring(Math.max(0, matchIndex - 2000), matchIndex).match(secureRe);
|
|
96
|
+
|
|
97
|
+
if (!secureMatch) {
|
|
98
|
+
log('❌ Could not find secureMode variable');
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const secureVar = secureMatch[1];
|
|
102
|
+
log(` secureVar=${secureVar}`);
|
|
103
|
+
|
|
104
|
+
// 4. Find useEffect alias: look for ALIAS(()=>{...},[...]) calls nearby (not useCallback/useMemo)
|
|
105
|
+
const nearbyCode = content.substring(Math.max(0, matchIndex - 5000), matchIndex + 5000);
|
|
106
|
+
const effectCandidates = {};
|
|
107
|
+
const effectRe = /\b(\w{2,3})\(\(\)=>\{[^}]{3,80}\},\[/g;
|
|
108
|
+
let m;
|
|
109
|
+
while ((m = effectRe.exec(nearbyCode)) !== null) {
|
|
110
|
+
const alias = m[1];
|
|
111
|
+
if (alias !== callbackAlias && alias !== 'var' && alias !== 'new') {
|
|
112
|
+
effectCandidates[alias] = (effectCandidates[alias] || 0) + 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Also check broader file for common useEffect patterns (with cleanup return)
|
|
117
|
+
const cleanupRe = /\b(\w{2,3})\(\(\)=>\{[^}]*return\s*\(\)=>/g;
|
|
118
|
+
while ((m = cleanupRe.exec(content)) !== null) {
|
|
119
|
+
const alias = m[1];
|
|
120
|
+
if (alias !== callbackAlias) {
|
|
121
|
+
effectCandidates[alias] = (effectCandidates[alias] || 0) + 5; // higher weight
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Pick the most common candidate
|
|
126
|
+
let useEffectAlias = null;
|
|
127
|
+
let maxCount = 0;
|
|
128
|
+
for (const [alias, count] of Object.entries(effectCandidates)) {
|
|
129
|
+
if (count > maxCount) {
|
|
130
|
+
maxCount = count;
|
|
131
|
+
useEffectAlias = alias;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!useEffectAlias) {
|
|
136
|
+
log('❌ Could not determine useEffect alias');
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
log(` useEffect=${useEffectAlias} (confidence: ${maxCount} hits)`);
|
|
140
|
+
|
|
141
|
+
// 5. Build patch — exact same logic as original
|
|
142
|
+
const patchCode = `_aep=${useEffectAlias}(()=>{${policyVar}===${enumAlias}.EAGER&&!${secureVar}&&${confirmFn}(!0)},[]),`;
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
target: fullMatch,
|
|
146
|
+
replacement: patchCode + fullMatch,
|
|
147
|
+
patchMarker: `_aep=${useEffectAlias}(()=>{${policyVar}===${enumAlias}.EAGER`,
|
|
148
|
+
label
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── File Operations ─────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
function isFilePatched(filePath) {
|
|
155
|
+
if (!fs.existsSync(filePath)) return false;
|
|
156
|
+
try {
|
|
157
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
158
|
+
return content.includes('_aep=') && /_aep=\w+\(\(\)=>\{[^}]+EAGER/.test(content);
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function patchFile(filePath, label) {
|
|
165
|
+
if (!fs.existsSync(filePath)) {
|
|
166
|
+
process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] File not found, skipping` });
|
|
167
|
+
return true; // optional file missing is not a failure
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let content;
|
|
171
|
+
try {
|
|
172
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
173
|
+
} catch (e) {
|
|
174
|
+
process.send({ type: 'log', msg: `[AutoAccept] ❌ [${label}] Read error: ${e.message}` });
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (isFilePatched(filePath)) {
|
|
179
|
+
process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] Already patched` });
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const analysis = analyzeFile(content, label);
|
|
184
|
+
if (!analysis) return false;
|
|
185
|
+
|
|
186
|
+
// Verify target uniqueness
|
|
187
|
+
const count = content.split(analysis.target).length - 1;
|
|
188
|
+
if (count !== 1) {
|
|
189
|
+
process.send({ type: 'log', msg: `[AutoAccept] ❌ [${label}] Target found ${count}x (expected 1)` });
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Backup original
|
|
194
|
+
const bakPath = filePath + '.bak';
|
|
195
|
+
if (!fs.existsSync(bakPath)) {
|
|
196
|
+
fs.copyFileSync(filePath, bakPath);
|
|
197
|
+
process.send({ type: 'log', msg: `[AutoAccept] 📦 [${label}] Backup created` });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const patched = content.replace(analysis.target, analysis.replacement);
|
|
201
|
+
fs.writeFileSync(filePath, patched, 'utf8');
|
|
202
|
+
|
|
203
|
+
const sizeDiff = fs.statSync(filePath).size - fs.statSync(bakPath).size;
|
|
204
|
+
process.send({ type: 'log', msg: `[AutoAccept] ✅ [${label}] Patched (+${sizeDiff} bytes)` });
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function revertFile(filePath, label) {
|
|
209
|
+
const bak = filePath + '.bak';
|
|
210
|
+
if (!fs.existsSync(bak)) {
|
|
211
|
+
process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] No backup, skipping` });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
fs.copyFileSync(bak, filePath);
|
|
215
|
+
process.send({ type: 'log', msg: `[AutoAccept] ✅ [${label}] Reverted` });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── Message Handler ──────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
process.on('message', (msg) => {
|
|
221
|
+
const basePath = findAntigravityPath();
|
|
222
|
+
|
|
223
|
+
if (msg.command === 'status') {
|
|
224
|
+
if (!basePath) {
|
|
225
|
+
process.send({ type: 'status', basePath: null, files: [] });
|
|
226
|
+
process.exit(0);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const files = getTargetFiles(basePath).map(f => ({
|
|
230
|
+
label: f.label,
|
|
231
|
+
patched: isFilePatched(f.filePath),
|
|
232
|
+
exists: fs.existsSync(f.filePath),
|
|
233
|
+
}));
|
|
234
|
+
process.send({ type: 'status', basePath, files });
|
|
235
|
+
process.exit(0);
|
|
236
|
+
|
|
237
|
+
} else if (msg.command === 'apply') {
|
|
238
|
+
if (!basePath) {
|
|
239
|
+
process.send({ type: 'result', success: false, message: '❌ Antigravity không tìm thấy! Hãy đảm bảo đã cài đặt.' });
|
|
240
|
+
process.exit(1);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const targets = getTargetFiles(basePath);
|
|
244
|
+
const results = targets.map(f => patchFile(f.filePath, f.label));
|
|
245
|
+
const success = results.every(Boolean);
|
|
246
|
+
process.send({
|
|
247
|
+
type: 'result',
|
|
248
|
+
success,
|
|
249
|
+
message: success
|
|
250
|
+
? '✅ Patch thành công! Restart Antigravity để áp dụng.'
|
|
251
|
+
: '⚠️ Một số file không patch được. Xem Output > AutoAccept để biết chi tiết.',
|
|
252
|
+
});
|
|
253
|
+
process.exit(success ? 0 : 1);
|
|
254
|
+
|
|
255
|
+
} else if (msg.command === 'revert') {
|
|
256
|
+
if (!basePath) {
|
|
257
|
+
process.send({ type: 'result', success: false, message: '❌ Antigravity không tìm thấy!' });
|
|
258
|
+
process.exit(1);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
getTargetFiles(basePath).forEach(f => revertFile(f.filePath, f.label));
|
|
262
|
+
process.send({ type: 'result', success: true, message: '✅ Đã hoàn tác! Restart Antigravity để áp dụng.' });
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
});
|