antigravity-autopilot 1.0.0 → 1.2.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/README.md +78 -25
- package/cli.js +232 -0
- package/extension.js +381 -98
- package/package.json +44 -10
package/README.md
CHANGED
|
@@ -1,38 +1,91 @@
|
|
|
1
|
-
# Antigravity
|
|
1
|
+
# Antigravity AutoPilot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Automatically execute all tool calls and terminal commands in Antigravity — no manual confirmation needed.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/antigravity-autopilot)
|
|
6
|
+
[](https://github.com/nguyenhx2/Antigravity-AutoPilot)
|
|
6
7
|
|
|
7
|
-
|
|
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
|
|
8
|
+
---
|
|
12
9
|
|
|
13
|
-
##
|
|
10
|
+
## What it does
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
Antigravity has an **"Always Proceed"** terminal execution policy, but due to a missing `useEffect` in its bundled JS, the policy never actually fires — commands still wait for manual approval.
|
|
13
|
+
|
|
14
|
+
**Antigravity AutoPilot** patches the runtime JS bundle to inject the missing auto-accept logic, so every tool call and terminal command runs instantly when the policy is active.
|
|
15
|
+
|
|
16
|
+
- ✅ Regex-based matching — works across Antigravity versions
|
|
17
|
+
- ✅ Non-destructive — creates `.bak` backup before patching
|
|
18
|
+
- ✅ Reversible — restore originals anytime with `--revert`
|
|
19
|
+
- ✅ Available as VS Code Extension **and** CLI (`npx`)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## CLI Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Apply the autopilot patch
|
|
27
|
+
npx antigravity-autopilot
|
|
28
|
+
|
|
29
|
+
# Check if already patched
|
|
30
|
+
npx antigravity-autopilot --check
|
|
31
|
+
|
|
32
|
+
# Revert to original files
|
|
33
|
+
npx antigravity-autopilot --revert
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Workflow
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
1. npx antigravity-autopilot → patch applied
|
|
40
|
+
2. Restart Antigravity → AutoPilot active 🚀
|
|
41
|
+
3. npx antigravity-autopilot --revert → undo anytime
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## VS Code Extension
|
|
47
|
+
|
|
48
|
+
Install the extension directly into Antigravity for a UI-based experience (sidebar panel, status bar, apply/revert commands):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Download .vsix from GitHub Releases, then:
|
|
52
|
+
antigravity --install-extension antigravity-autopilot-1.0.0.vsix
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Extension features:**
|
|
56
|
+
- ⚡ Sidebar panel with one-click Apply / Revert
|
|
57
|
+
- 📊 Status bar showing current patch state
|
|
58
|
+
- ⌨️ Keyboard shortcut: `Ctrl+Shift+F12`
|
|
59
|
+
- ⚙️ `applyOnStartup` setting for fully automatic operation
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## How it works
|
|
64
|
+
|
|
65
|
+
Antigravity bundles its UI as minified JavaScript. The patch locates the `setTerminalAutoExecutionPolicy` onChange handler and injects a `useEffect` that fires the auto-confirm function whenever the policy is set to `EAGER`:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// Injected patch (conceptual):
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (policyVar === ENUM.EAGER && !secureMode) confirmFn(true);
|
|
71
|
+
}, []);
|
|
18
72
|
```
|
|
19
73
|
|
|
20
|
-
|
|
74
|
+
Variable names are resolved via regex at runtime, making the patch resilient to minification changes between versions.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
21
79
|
|
|
22
|
-
|
|
80
|
+
- [Antigravity](https://antigravity.dev) installed on your system
|
|
81
|
+
- Node.js 16+
|
|
23
82
|
|
|
24
|
-
|
|
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
|
|
83
|
+
---
|
|
28
84
|
|
|
29
|
-
##
|
|
85
|
+
## Repository
|
|
30
86
|
|
|
31
|
-
|
|
32
|
-
|---------|---------|-------------|
|
|
33
|
-
| `antigravityAutoAccept.intervalMs` | `800` | Interval between accept attempts (ms) |
|
|
34
|
-
| `antigravityAutoAccept.enabledOnStartup` | `false` | Auto-enable on VS Code start |
|
|
87
|
+
[github.com/nguyenhx2/Antigravity-AutoPilot](https://github.com/nguyenhx2/Antigravity-AutoPilot)
|
|
35
88
|
|
|
36
|
-
##
|
|
89
|
+
## License
|
|
37
90
|
|
|
38
|
-
|
|
91
|
+
MIT
|
package/cli.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Antigravity AutoPilot — CLI
|
|
5
|
+
* ============================
|
|
6
|
+
* Patches Antigravity's runtime JS bundle so that the
|
|
7
|
+
* "Always Proceed" terminal execution policy actually
|
|
8
|
+
* auto-executes commands without manual confirmation.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx antigravity-autopilot Apply patch
|
|
12
|
+
* npx antigravity-autopilot --check Check patch status
|
|
13
|
+
* npx antigravity-autopilot --revert Restore original files
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
|
|
22
|
+
// ─── Installation Detection ──────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
function findAntigravityPath() {
|
|
25
|
+
const candidates = [];
|
|
26
|
+
if (process.platform === 'win32') {
|
|
27
|
+
candidates.push(
|
|
28
|
+
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Antigravity'),
|
|
29
|
+
path.join(process.env.PROGRAMFILES || '', 'Antigravity'),
|
|
30
|
+
);
|
|
31
|
+
} else if (process.platform === 'darwin') {
|
|
32
|
+
candidates.push(
|
|
33
|
+
'/Applications/Antigravity.app/Contents/Resources',
|
|
34
|
+
path.join(os.homedir(), 'Applications', 'Antigravity.app', 'Contents', 'Resources'),
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
candidates.push(
|
|
38
|
+
'/usr/share/antigravity',
|
|
39
|
+
'/opt/antigravity',
|
|
40
|
+
path.join(os.homedir(), '.local', 'share', 'antigravity'),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
for (const c of candidates) {
|
|
44
|
+
const f = path.join(c, 'resources', 'app', 'out', 'vs', 'workbench', 'workbench.desktop.main.js');
|
|
45
|
+
if (fs.existsSync(f)) return c;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getTargetFiles(basePath) {
|
|
51
|
+
return [
|
|
52
|
+
{ filePath: path.join(basePath, 'resources', 'app', 'out', 'vs', 'workbench', 'workbench.desktop.main.js'), label: 'workbench' },
|
|
53
|
+
{ filePath: path.join(basePath, 'resources', 'app', 'out', 'jetskiAgent', 'main.js'), label: 'jetskiAgent' },
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getVersion(basePath) {
|
|
58
|
+
try {
|
|
59
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(basePath, 'resources', 'app', 'package.json'), 'utf8'));
|
|
60
|
+
const product = JSON.parse(fs.readFileSync(path.join(basePath, 'resources', 'app', 'product.json'), 'utf8'));
|
|
61
|
+
return `${pkg.version} (IDE ${product.ideVersion})`;
|
|
62
|
+
} catch { return 'unknown'; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Core Patch Logic (regex-based, version-agnostic) ───────────────────────
|
|
66
|
+
|
|
67
|
+
function analyzeFile(content, label) {
|
|
68
|
+
// 1. Find the onChange handler for setTerminalAutoExecutionPolicy
|
|
69
|
+
const onChangeRe = /(\w+)=(\w+)\((\w+)=>\{\w+\?\.setTerminalAutoExecutionPolicy\?\.\(\3\),\3===(\w+)\.EAGER&&(\w+)\(!0\)\},\[[\w,]*\]\)/;
|
|
70
|
+
const onChangeMatch = content.match(onChangeRe);
|
|
71
|
+
if (!onChangeMatch) {
|
|
72
|
+
console.log(` ❌ [${label}] Could not find onChange handler pattern`);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const [fullMatch, , callbackAlias, , enumAlias, confirmFn] = onChangeMatch;
|
|
77
|
+
const matchIndex = content.indexOf(fullMatch);
|
|
78
|
+
console.log(` 📋 [${label}] Found onChange at offset ${matchIndex}`);
|
|
79
|
+
console.log(` callback=${callbackAlias}, enum=${enumAlias}, confirm=${confirmFn}`);
|
|
80
|
+
|
|
81
|
+
// 2. Find policy variable: VAR=HANDLER?.terminalAutoExecutionPolicy??ENUM.OFF
|
|
82
|
+
const policyRe = new RegExp(`(\\w+)=\\w+\\?\\.terminalAutoExecutionPolicy\\?\\?${enumAlias}\\.OFF`);
|
|
83
|
+
const policyMatch = content.substring(Math.max(0, matchIndex - 2000), matchIndex).match(policyRe);
|
|
84
|
+
if (!policyMatch) { console.log(` ❌ [${label}] Could not find policy variable`); return null; }
|
|
85
|
+
const policyVar = policyMatch[1];
|
|
86
|
+
console.log(` policyVar=${policyVar}`);
|
|
87
|
+
|
|
88
|
+
// 3. Find secureMode variable: VAR=HANDLER?.secureModeEnabled??!1
|
|
89
|
+
const secureRe = /(\w+)=\w+\?\.secureModeEnabled\?\?!1/;
|
|
90
|
+
const secureMatch = content.substring(Math.max(0, matchIndex - 2000), matchIndex).match(secureRe);
|
|
91
|
+
if (!secureMatch) { console.log(` ❌ [${label}] Could not find secureMode variable`); return null; }
|
|
92
|
+
const secureVar = secureMatch[1];
|
|
93
|
+
console.log(` secureVar=${secureVar}`);
|
|
94
|
+
|
|
95
|
+
// 4. Find useEffect alias via frequency counting
|
|
96
|
+
const nearbyCode = content.substring(Math.max(0, matchIndex - 5000), matchIndex + 5000);
|
|
97
|
+
const effectCandidates = {};
|
|
98
|
+
const effectRe = /\b(\w{2,3})\(\(\)=>\{[^}]{3,80}\},\[/g;
|
|
99
|
+
let m;
|
|
100
|
+
while ((m = effectRe.exec(nearbyCode)) !== null) {
|
|
101
|
+
const alias = m[1];
|
|
102
|
+
if (alias !== callbackAlias && alias !== 'var' && alias !== 'new')
|
|
103
|
+
effectCandidates[alias] = (effectCandidates[alias] || 0) + 1;
|
|
104
|
+
}
|
|
105
|
+
const cleanupRe = /\b(\w{2,3})\(\(\)=>\{[^}]*return\s*\(\)=>/g;
|
|
106
|
+
while ((m = cleanupRe.exec(content)) !== null) {
|
|
107
|
+
const alias = m[1];
|
|
108
|
+
if (alias !== callbackAlias)
|
|
109
|
+
effectCandidates[alias] = (effectCandidates[alias] || 0) + 5;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let useEffectAlias = null, maxCount = 0;
|
|
113
|
+
for (const [alias, count] of Object.entries(effectCandidates)) {
|
|
114
|
+
if (count > maxCount) { maxCount = count; useEffectAlias = alias; }
|
|
115
|
+
}
|
|
116
|
+
if (!useEffectAlias) { console.log(` ❌ [${label}] Could not determine useEffect alias`); return null; }
|
|
117
|
+
console.log(` useEffect=${useEffectAlias} (confidence: ${maxCount} hits)`);
|
|
118
|
+
|
|
119
|
+
// 5. Build patch
|
|
120
|
+
const patchCode = `_aep=${useEffectAlias}(()=>{${policyVar}===${enumAlias}.EAGER&&!${secureVar}&&${confirmFn}(!0)},[]),`;
|
|
121
|
+
return {
|
|
122
|
+
target: fullMatch,
|
|
123
|
+
replacement: patchCode + fullMatch,
|
|
124
|
+
patchMarker: `_aep=${useEffectAlias}(()=>{${policyVar}===${enumAlias}.EAGER`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── File Operations ─────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
function isPatched(filePath) {
|
|
131
|
+
if (!fs.existsSync(filePath)) return false;
|
|
132
|
+
const c = fs.readFileSync(filePath, 'utf8');
|
|
133
|
+
return c.includes('_aep=') && /_aep=\w+\(\(\)=>\{[^}]+EAGER/.test(c);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function patchFile(filePath, label) {
|
|
137
|
+
if (!fs.existsSync(filePath)) {
|
|
138
|
+
console.log(` ⏭️ [${label}] Not found, skipping`);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
142
|
+
if (isPatched(filePath)) {
|
|
143
|
+
console.log(` ⏭️ [${label}] Already patched`);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
const analysis = analyzeFile(content, label);
|
|
147
|
+
if (!analysis) return false;
|
|
148
|
+
|
|
149
|
+
const count = content.split(analysis.target).length - 1;
|
|
150
|
+
if (count !== 1) { console.log(` ❌ [${label}] Target found ${count}x (expected 1)`); return false; }
|
|
151
|
+
|
|
152
|
+
const bak = filePath + '.bak';
|
|
153
|
+
if (!fs.existsSync(bak)) { fs.copyFileSync(filePath, bak); console.log(` 📦 [${label}] Backup created`); }
|
|
154
|
+
|
|
155
|
+
fs.writeFileSync(filePath, content.replace(analysis.target, analysis.replacement), 'utf8');
|
|
156
|
+
const diff = fs.statSync(filePath).size - fs.statSync(bak).size;
|
|
157
|
+
console.log(` ✅ [${label}] Patched (+${diff} bytes)`);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function revertFile(filePath, label) {
|
|
162
|
+
const bak = filePath + '.bak';
|
|
163
|
+
if (!fs.existsSync(bak)) { console.log(` ⏭️ [${label}] No backup, skipping`); return; }
|
|
164
|
+
fs.copyFileSync(bak, filePath);
|
|
165
|
+
console.log(` ✅ [${label}] Restored`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function checkFile(filePath, label) {
|
|
169
|
+
if (!fs.existsSync(filePath)) { console.log(` ❌ [${label}] Not found`); return false; }
|
|
170
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
171
|
+
const patched = isPatched(filePath);
|
|
172
|
+
const hasBak = fs.existsSync(filePath + '.bak');
|
|
173
|
+
if (patched) {
|
|
174
|
+
console.log(` ✅ [${label}] PATCHED` + (hasBak ? ' (backup exists)' : ''));
|
|
175
|
+
} else {
|
|
176
|
+
const analysis = analyzeFile(content, label);
|
|
177
|
+
console.log(analysis ? ` ⬜ [${label}] NOT PATCHED (patchable)` : ` ⚠️ [${label}] NOT PATCHED (may be incompatible)`);
|
|
178
|
+
}
|
|
179
|
+
return patched;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
function main() {
|
|
185
|
+
const args = process.argv.slice(2);
|
|
186
|
+
const action = args.includes('--revert') ? 'revert' : args.includes('--check') ? 'check' : 'apply';
|
|
187
|
+
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log('╔══════════════════════════════════════════╗');
|
|
190
|
+
console.log('║ ⚡ Antigravity AutoPilot ║');
|
|
191
|
+
console.log('╚══════════════════════════════════════════╝');
|
|
192
|
+
|
|
193
|
+
const basePath = findAntigravityPath();
|
|
194
|
+
if (!basePath) {
|
|
195
|
+
console.log('\n❌ Antigravity not found. Make sure it is installed.\n');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(`\n📍 ${basePath}`);
|
|
200
|
+
console.log(`📦 Antigravity version: ${getVersion(basePath)}\n`);
|
|
201
|
+
|
|
202
|
+
const files = getTargetFiles(basePath);
|
|
203
|
+
|
|
204
|
+
switch (action) {
|
|
205
|
+
case 'check':
|
|
206
|
+
console.log('🔍 Checking patch status...\n');
|
|
207
|
+
files.forEach(f => checkFile(f.filePath, f.label));
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case 'revert':
|
|
211
|
+
console.log('↩️ Reverting patch...\n');
|
|
212
|
+
files.forEach(f => revertFile(f.filePath, f.label));
|
|
213
|
+
console.log('\n✨ Restored! Restart Antigravity to apply changes.');
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case 'apply':
|
|
217
|
+
default:
|
|
218
|
+
console.log('🚀 Applying autopilot patch...\n');
|
|
219
|
+
const ok = files.every(f => patchFile(f.filePath, f.label));
|
|
220
|
+
if (ok) {
|
|
221
|
+
console.log('\n✨ Done! Restart Antigravity to activate AutoPilot.');
|
|
222
|
+
console.log('💡 Run with --revert to undo.');
|
|
223
|
+
console.log('⚠️ Re-run after Antigravity updates.\n');
|
|
224
|
+
} else {
|
|
225
|
+
console.log('\n⚠️ Some files could not be patched. Check output above.\n');
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
main();
|
package/extension.js
CHANGED
|
@@ -16,6 +16,142 @@ let isPatchApplied = false;
|
|
|
16
16
|
let panelProvider = null;
|
|
17
17
|
/** @type {{ basePath: string|null, files: any[], patched: boolean } | null} */
|
|
18
18
|
let _cachedStatus = null;
|
|
19
|
+
/** @type {boolean} */
|
|
20
|
+
let autoPilotEnabled = true;
|
|
21
|
+
/** @type {vscode.OutputChannel} */
|
|
22
|
+
let outputChannel;
|
|
23
|
+
|
|
24
|
+
// ─── Dangerous Command Blocking ───────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Built-in dangerous command patterns.
|
|
28
|
+
* Covers Linux/macOS/Windows destructive commands.
|
|
29
|
+
* Each entry: { pattern: RegExp, label: string, os: string[] }
|
|
30
|
+
*/
|
|
31
|
+
const BUILTIN_DANGEROUS_PATTERNS = [
|
|
32
|
+
// ── Linux / macOS ──
|
|
33
|
+
{ pattern: /rm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|--force\s+).*\/(\s|$)/, label: 'rm -rf /', os: ['linux', 'darwin'] },
|
|
34
|
+
{ pattern: /rm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\/(\s|$)/, label: 'rm -r / (root wipe)', os: ['linux', 'darwin'] },
|
|
35
|
+
{ pattern: /rm\s+-[a-zA-Z]*r[a-zA-Z]*\s+~(\s|$|\/)/, label: 'rm -r ~ (home wipe)', os: ['linux', 'darwin'] },
|
|
36
|
+
{ pattern: /rm\s+.*--no-preserve-root/, label: 'rm --no-preserve-root', os: ['linux', 'darwin'] },
|
|
37
|
+
{ pattern: /:\(\)\s*\{.*:\|:&\s*\};\s*:/, label: 'Fork bomb :(){:|:&};:', os: ['linux', 'darwin'] },
|
|
38
|
+
{ pattern: /mkfs\.(ext[234]|xfs|btrfs|vfat|ntfs)\s+\/dev\/(sd|hd|nvme|vd)/, label: 'mkfs on block device', os: ['linux', 'darwin'] },
|
|
39
|
+
{ pattern: /dd\s+.*of=\/dev\/(sd[a-z]|hd[a-z]|nvme\d+|zero|null)/, label: 'dd overwrite device', os: ['linux', 'darwin'] },
|
|
40
|
+
{ pattern: />\s*\/dev\/(sd[a-z]|hd[a-z]|nvme\d+)/, label: 'Redirect to block device', os: ['linux', 'darwin'] },
|
|
41
|
+
{ pattern: /shred\s+(-[a-zA-Z]*n\s*\d+\s+)?\/dev\//, label: 'shred device', os: ['linux', 'darwin'] },
|
|
42
|
+
{ pattern: /mv\s+.*\s+\/dev\/null/, label: 'mv to /dev/null', os: ['linux', 'darwin'] },
|
|
43
|
+
{ pattern: /chmod\s+-[rR]\s+000\s+\//, label: 'chmod 000 recursive on /', os: ['linux', 'darwin'] },
|
|
44
|
+
{ pattern: /chmod\s+777\s+-R\s+\/(\s|$)/, label: 'chmod 777 -R /', os: ['linux', 'darwin'] },
|
|
45
|
+
{ pattern: /chown\s+.*-R\s+.*\s+\/(\s|$)/, label: 'chown -R on /', os: ['linux', 'darwin'] },
|
|
46
|
+
{ pattern: /passwd\s+root\s*$/, label: 'passwd root (no new password)', os: ['linux', 'darwin'] },
|
|
47
|
+
{ pattern: /sudo\s+rm\s+-[a-zA-Z]*rf?\s+\/(\s|$)/, label: 'sudo rm -rf /', os: ['linux', 'darwin'] },
|
|
48
|
+
{ pattern: /wget\s+.*\|\s*(ba)?sh/, label: 'wget pipe to shell', os: ['linux', 'darwin'] },
|
|
49
|
+
{ pattern: /curl\s+.*\|\s*(ba)?sh/, label: 'curl pipe to shell', os: ['linux', 'darwin'] },
|
|
50
|
+
{ pattern: /base64\s+-d.*\|\s*(ba)?sh/, label: 'base64 decode pipe to shell', os: ['linux', 'darwin'] },
|
|
51
|
+
{ pattern: /eval\s+\$\(.*\)/, label: 'eval $(...) subshell', os: ['linux', 'darwin'] },
|
|
52
|
+
{ pattern: /fdisk\s+\/dev\/(sd[a-z]|nvme\d+)/, label: 'fdisk on disk', os: ['linux', 'darwin'] },
|
|
53
|
+
{ pattern: /parted\s+\/dev\/(sd[a-z]|nvme\d+)/, label: 'parted on disk', os: ['linux', 'darwin'] },
|
|
54
|
+
{ pattern: /wipefs\s+.*\/dev\//, label: 'wipefs on device', os: ['linux', 'darwin'] },
|
|
55
|
+
{ pattern: /truncate\s+-s\s+0\s+\/dev\//, label: 'truncate device to 0', os: ['linux', 'darwin'] },
|
|
56
|
+
{ pattern: /echo\s+.*>\s*\/boot\//, label: 'overwrite /boot/', os: ['linux', 'darwin'] },
|
|
57
|
+
{ pattern: /cat\s+\/dev\/zero\s+>\s+\//, label: 'cat /dev/zero to /', os: ['linux', 'darwin'] },
|
|
58
|
+
{ pattern: /umount\s+-a/, label: 'umount -a (unmount all)', os: ['linux', 'darwin'] },
|
|
59
|
+
{ pattern: /init\s+0/, label: 'init 0 (halt system)', os: ['linux', 'darwin'] },
|
|
60
|
+
{ pattern: /poweroff|halt\s*$/, label: 'System shutdown command', os: ['linux', 'darwin'] },
|
|
61
|
+
{ pattern: /iptables\s+-F/, label: 'iptables -F (flush all rules)', os: ['linux', 'darwin'] },
|
|
62
|
+
{ pattern: /ufw\s+--force\s+reset/, label: 'ufw --force reset', os: ['linux', 'darwin'] },
|
|
63
|
+
// ── macOS specific ──
|
|
64
|
+
{ pattern: /diskutil\s+(eraseDisk|eraseVolume|partitionDisk)\s+/, label: 'diskutil erase/repartition', os: ['darwin'] },
|
|
65
|
+
{ pattern: /diskutil\s+zeroDisk\s+/, label: 'diskutil zeroDisk', os: ['darwin'] },
|
|
66
|
+
{ pattern: /csrutil\s+disable/, label: 'csrutil disable (SIP)', os: ['darwin'] },
|
|
67
|
+
// ── Windows (PowerShell / cmd) ──
|
|
68
|
+
{ pattern: /Format-Volume\s+.*-Confirm:\s*\$false/i, label: 'Format-Volume without confirm', os: ['win32'] },
|
|
69
|
+
{ pattern: /format\s+[cC]:\s*\/[qQy]/i, label: 'format C: /q or /y', os: ['win32'] },
|
|
70
|
+
{ pattern: /format\s+[a-zA-Z]:\s*\/[qQy]/i, label: 'format <drive> /q or /y', os: ['win32'] },
|
|
71
|
+
{ pattern: /del\s+\/[fsqSFQ]+\s+[cC]:\\/i, label: 'del /f/s/q C:\\ (wipe drive)', os: ['win32'] },
|
|
72
|
+
{ pattern: /rd\s+\/[sq]+\s+[cC]:\\/i, label: 'rd /s/q C:\\ (remove all)', os: ['win32'] },
|
|
73
|
+
{ pattern: /Remove-Item\s+.*-Recurse\s+.*-Force.*[cC]:\\/i, label: 'Remove-Item -Recurse -Force C:\\', os: ['win32'] },
|
|
74
|
+
{ pattern: /Remove-Item\s+.*-Recurse\s+.*-Force\s+\/\s/i, label: 'Remove-Item -Recurse -Force /', os: ['win32'] },
|
|
75
|
+
{ pattern: /Set-ExecutionPolicy\s+Unrestricted\s+-Force/i, label: 'Set-ExecutionPolicy Unrestricted -Force', os: ['win32'] },
|
|
76
|
+
{ pattern: /reg\s+(delete|add)\s+HKLM\\SYSTEM\\CurrentControlSet/i, label: 'reg delete HKLM\\SYSTEM critical', os: ['win32'] },
|
|
77
|
+
{ pattern: /bcdedit\s+\/deletevalue/i, label: 'bcdedit /deletevalue (boot config)', os: ['win32'] },
|
|
78
|
+
{ pattern: /bcdedit\s+\/set.*safeboot/i, label: 'bcdedit /set safeboot (forces safe mode)', os: ['win32'] },
|
|
79
|
+
{ pattern: /cipher\s+\/w:[cC]:\\/i, label: 'cipher /w:C:\\ (wipe free space)', os: ['win32'] },
|
|
80
|
+
{ pattern: /sfc\s+\/scannow.*\/offwindir/i, label: 'sfc offline (system repair risk)', os: ['win32'] },
|
|
81
|
+
{ pattern: /wmic\s+.*delete/i, label: 'wmic delete', os: ['win32'] },
|
|
82
|
+
{ pattern: /Invoke-Expression\s+\(.*Download.*\)/i, label: 'IEX download-and-execute', os: ['win32'] },
|
|
83
|
+
{ pattern: /iex\s+\(.*WebClient.*DownloadString/i, label: 'iex WebClient DownloadString (remote exec)', os: ['win32'] },
|
|
84
|
+
{ pattern: /powershell\s+.*-EncodedCommand/i, label: 'powershell -EncodedCommand (obfuscated)', os: ['win32'] },
|
|
85
|
+
{ pattern: /net\s+user\s+administrator\s+\*?\s*\/active:yes/i, label: 'net user administrator enable', os: ['win32'] },
|
|
86
|
+
{ pattern: /takeown\s+\/f\s+[cC]:\\/i, label: 'takeown /f C:\\ (ownership grab)', os: ['win32'] },
|
|
87
|
+
{ pattern: /icacls\s+[cC]:\\\s+\/grant/i, label: 'icacls C:\\ /grant (permission escalation)', os: ['win32'] },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks a command string against built-in + custom dangerous patterns.
|
|
92
|
+
* @param {string} cmd
|
|
93
|
+
* @returns {{ matched: boolean, label: string, pattern: string }}
|
|
94
|
+
*/
|
|
95
|
+
function checkDangerousCommand(cmd) {
|
|
96
|
+
const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
97
|
+
const enabled = cfg.get('dangerousCommandBlocking.enabled', true);
|
|
98
|
+
if (!enabled) return { matched: false, label: '', pattern: '' };
|
|
99
|
+
|
|
100
|
+
const platform = process.platform; // 'win32' | 'linux' | 'darwin'
|
|
101
|
+
const trimmed = cmd.trim();
|
|
102
|
+
|
|
103
|
+
// Check built-in patterns (platform-filtered)
|
|
104
|
+
for (const entry of BUILTIN_DANGEROUS_PATTERNS) {
|
|
105
|
+
if (!entry.os.includes(platform)) continue;
|
|
106
|
+
if (entry.pattern.test(trimmed)) {
|
|
107
|
+
return { matched: true, label: entry.label, pattern: entry.pattern.toString() };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check custom user patterns
|
|
112
|
+
const customPatterns = /** @type {string[]} */ (cfg.get('dangerousCommandBlocking.customPatterns', []));
|
|
113
|
+
for (const raw of customPatterns) {
|
|
114
|
+
try {
|
|
115
|
+
const re = new RegExp(raw, 'i');
|
|
116
|
+
if (re.test(trimmed)) {
|
|
117
|
+
return { matched: true, label: `Custom: ${raw}`, pattern: raw };
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// Invalid regex — skip silently
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { matched: false, label: '', pattern: '' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Handles a detected dangerous command according to the configured action.
|
|
129
|
+
* @param {string} cmd - The full command text
|
|
130
|
+
* @param {string} label - Human-readable reason
|
|
131
|
+
*/
|
|
132
|
+
function handleDangerousCommand(cmd, label) {
|
|
133
|
+
const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
134
|
+
const action = cfg.get('dangerousCommandBlocking.action', 'block');
|
|
135
|
+
const msg = `🛡️ Dangerous command detected: "${label}" — \`${cmd.trim().substring(0, 80)}\``;
|
|
136
|
+
|
|
137
|
+
outputChannel.appendLine(`[DangerBlock][${new Date().toISOString()}] ${action.toUpperCase()} | ${label} | CMD: ${cmd.trim()}`);
|
|
138
|
+
|
|
139
|
+
if (action === 'block') {
|
|
140
|
+
vscode.window.showErrorMessage(
|
|
141
|
+
`⛔ Blocked: ${label}`,
|
|
142
|
+
{ modal: false },
|
|
143
|
+
'View Details',
|
|
144
|
+
).then((choice) => {
|
|
145
|
+
if (choice === 'View Details') {
|
|
146
|
+
outputChannel.show(true);
|
|
147
|
+
outputChannel.appendLine(`[DangerBlock] Blocked command: ${cmd.trim()}`);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
} else if (action === 'warn') {
|
|
151
|
+
vscode.window.showWarningMessage(`⚠️ Warning: ${msg}`);
|
|
152
|
+
}
|
|
153
|
+
// 'log' — already logged above, no UI notification
|
|
154
|
+
}
|
|
19
155
|
|
|
20
156
|
// ─── Child Process Bridge ─────────────────────────────────────────────────────
|
|
21
157
|
|
|
@@ -28,21 +164,20 @@ let _cachedStatus = null;
|
|
|
28
164
|
function runPatcher(command) {
|
|
29
165
|
return new Promise((resolve, reject) => {
|
|
30
166
|
const child = fork(PATCHER, [], { silent: true });
|
|
31
|
-
const channel = vscode.window.createOutputChannel('AutoAccept');
|
|
32
167
|
|
|
33
168
|
child.on('message', (msg) => {
|
|
34
169
|
if (!msg || typeof msg !== 'object') return;
|
|
35
170
|
const m = /** @type {{type:string,msg?:string}} */(msg);
|
|
36
171
|
if (m.type === 'log') {
|
|
37
172
|
console.log(m.msg);
|
|
38
|
-
|
|
173
|
+
outputChannel.appendLine(m.msg || '');
|
|
39
174
|
} else {
|
|
40
|
-
resolve(msg);
|
|
175
|
+
resolve(msg);
|
|
41
176
|
}
|
|
42
177
|
});
|
|
43
178
|
|
|
44
179
|
child.on('error', (err) => {
|
|
45
|
-
|
|
180
|
+
outputChannel.appendLine(`[AutoPilot] fork error: ${err.message}`);
|
|
46
181
|
reject(err);
|
|
47
182
|
});
|
|
48
183
|
|
|
@@ -51,7 +186,7 @@ function runPatcher(command) {
|
|
|
51
186
|
resolve({
|
|
52
187
|
type: 'result',
|
|
53
188
|
success: false,
|
|
54
|
-
message: `Process exited with code ${code}. Check Output >
|
|
189
|
+
message: `Process exited with code ${code}. Check Output > AutoPilot for details.`,
|
|
55
190
|
});
|
|
56
191
|
}
|
|
57
192
|
});
|
|
@@ -116,7 +251,32 @@ async function refreshStatus() {
|
|
|
116
251
|
if (panelProvider) panelProvider.sendStatus(status);
|
|
117
252
|
}
|
|
118
253
|
|
|
119
|
-
// ───
|
|
254
|
+
// ─── Status Bar ───────────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
function updateStatusBarFromCache() {
|
|
257
|
+
if (!statusBarItem) return;
|
|
258
|
+
if (!autoPilotEnabled) {
|
|
259
|
+
statusBarItem.text = '$(debug-pause) AG Paused';
|
|
260
|
+
statusBarItem.tooltip = 'Antigravity AutoPilot is suspended';
|
|
261
|
+
statusBarItem.color = new vscode.ThemeColor('statusBarItem.warningForeground');
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (!_cachedStatus || !_cachedStatus.basePath) {
|
|
265
|
+
statusBarItem.text = '$(warning) AG: Not Found';
|
|
266
|
+
statusBarItem.tooltip = 'Antigravity not found on this system';
|
|
267
|
+
statusBarItem.color = new vscode.ThemeColor('statusBarItem.errorForeground');
|
|
268
|
+
} else if (_cachedStatus.patched) {
|
|
269
|
+
statusBarItem.text = '$(zap) AG: Active';
|
|
270
|
+
statusBarItem.tooltip = 'Antigravity AutoPilot — Patch Applied ✅';
|
|
271
|
+
statusBarItem.color = new vscode.ThemeColor('statusBarItem.prominentForeground');
|
|
272
|
+
} else {
|
|
273
|
+
statusBarItem.text = '$(circle-slash) AG: Inactive';
|
|
274
|
+
statusBarItem.tooltip = 'Antigravity AutoPilot — Patch Not Applied';
|
|
275
|
+
statusBarItem.color = undefined;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ─── Sidebar WebView ──────────────────────────────────────────────────────────
|
|
120
280
|
|
|
121
281
|
class AntigravityPanelProvider {
|
|
122
282
|
/** @param {vscode.ExtensionContext} context */
|
|
@@ -144,11 +304,24 @@ class AntigravityPanelProvider {
|
|
|
144
304
|
} else if (msg.command === 'refresh') {
|
|
145
305
|
this._postLoading('⏳ Checking...');
|
|
146
306
|
await refreshStatus();
|
|
307
|
+
} else if (msg.command === 'toggleEnabled') {
|
|
308
|
+
autoPilotEnabled = !autoPilotEnabled;
|
|
309
|
+
updateStatusBarFromCache();
|
|
310
|
+
// Persist into workspace config
|
|
311
|
+
const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
312
|
+
await cfg.update('enabledOnStartup', autoPilotEnabled, vscode.ConfigurationTarget.Global);
|
|
313
|
+
if (panelProvider) panelProvider.sendEnabled(autoPilotEnabled);
|
|
314
|
+
vscode.window.showInformationMessage(
|
|
315
|
+
autoPilotEnabled ? '⚡ AutoPilot resumed' : '⏸ AutoPilot suspended',
|
|
316
|
+
);
|
|
317
|
+
} else if (msg.command === 'openSettings') {
|
|
318
|
+
vscode.commands.executeCommand('workbench.action.openSettings', 'antigravityAutoAccept');
|
|
147
319
|
}
|
|
148
320
|
});
|
|
149
321
|
|
|
150
322
|
// Initial load
|
|
151
323
|
refreshStatus();
|
|
324
|
+
this.sendEnabled(autoPilotEnabled);
|
|
152
325
|
}
|
|
153
326
|
|
|
154
327
|
/** @param {string} text */
|
|
@@ -167,8 +340,11 @@ class AntigravityPanelProvider {
|
|
|
167
340
|
});
|
|
168
341
|
}
|
|
169
342
|
|
|
170
|
-
/** @
|
|
171
|
-
|
|
343
|
+
/** @param {boolean} enabled */
|
|
344
|
+
sendEnabled(enabled) {
|
|
345
|
+
if (!this._view) return;
|
|
346
|
+
this._view.webview.postMessage({ command: 'setEnabled', enabled });
|
|
347
|
+
}
|
|
172
348
|
|
|
173
349
|
_getHtml() {
|
|
174
350
|
return /* html */`<!DOCTYPE html>
|
|
@@ -182,88 +358,154 @@ class AntigravityPanelProvider {
|
|
|
182
358
|
font-family:'Segoe UI',sans-serif;
|
|
183
359
|
background:var(--vscode-sideBar-background);
|
|
184
360
|
color:var(--vscode-foreground);
|
|
185
|
-
padding:
|
|
361
|
+
padding:12px;user-select:none;
|
|
186
362
|
}
|
|
363
|
+
|
|
364
|
+
/* ── Header ── */
|
|
187
365
|
.header{
|
|
188
366
|
display:flex;align-items:center;gap:8px;
|
|
189
|
-
margin-bottom:
|
|
367
|
+
margin-bottom:12px;padding-bottom:10px;
|
|
190
368
|
border-bottom:1px solid var(--vscode-panel-border);
|
|
191
369
|
}
|
|
192
|
-
.header-icon{font-size:
|
|
370
|
+
.header-icon{font-size:18px}
|
|
193
371
|
.header-title{font-size:13px;font-weight:600;letter-spacing:.3px}
|
|
194
|
-
.header-sub{font-size:
|
|
372
|
+
.header-sub{font-size:10px;color:var(--vscode-descriptionForeground);margin-top:2px}
|
|
195
373
|
|
|
374
|
+
/* ── Toggle Row ── */
|
|
375
|
+
.toggle-row{
|
|
376
|
+
display:flex;align-items:center;justify-content:space-between;
|
|
377
|
+
background:var(--vscode-editor-background);
|
|
378
|
+
border:1px solid var(--vscode-panel-border);
|
|
379
|
+
border-radius:6px;padding:8px 10px;margin-bottom:10px;
|
|
380
|
+
}
|
|
381
|
+
.toggle-label{font-size:11px;font-weight:600}
|
|
382
|
+
.toggle-sub{font-size:10px;color:var(--vscode-descriptionForeground);margin-top:1px}
|
|
383
|
+
.switch{position:relative;display:inline-block;width:34px;height:18px;flex-shrink:0}
|
|
384
|
+
.switch input{opacity:0;width:0;height:0}
|
|
385
|
+
.slider{
|
|
386
|
+
position:absolute;cursor:pointer;inset:0;
|
|
387
|
+
background:#555;border-radius:18px;
|
|
388
|
+
transition:background .2s;
|
|
389
|
+
}
|
|
390
|
+
.slider:before{
|
|
391
|
+
position:absolute;content:'';height:14px;width:14px;
|
|
392
|
+
left:2px;bottom:2px;background:#fff;border-radius:50%;
|
|
393
|
+
transition:transform .2s;
|
|
394
|
+
}
|
|
395
|
+
input:checked + .slider{background:#4ec94e}
|
|
396
|
+
input:checked + .slider:before{transform:translateX(16px)}
|
|
397
|
+
|
|
398
|
+
/* ── Status card ── */
|
|
196
399
|
.status-card{
|
|
197
|
-
border-radius:
|
|
400
|
+
border-radius:6px;padding:12px;margin-bottom:10px;
|
|
198
401
|
background:var(--vscode-editor-background);
|
|
199
402
|
border:1px solid var(--vscode-panel-border);
|
|
200
403
|
transition:border-color .3s,background .3s;
|
|
201
404
|
}
|
|
202
405
|
.status-card.patched{border-color:#4ec94e;background:rgba(78,201,78,.07)}
|
|
203
406
|
.status-card.not-found{border-color:#e06c75;background:rgba(224,108,117,.07)}
|
|
204
|
-
.status-row{display:flex;align-items:center;gap:
|
|
407
|
+
.status-row{display:flex;align-items:center;gap:10px}
|
|
205
408
|
.dot{
|
|
206
|
-
width:
|
|
409
|
+
width:32px;height:32px;border-radius:50%;
|
|
207
410
|
display:flex;align-items:center;justify-content:center;
|
|
208
|
-
font-size:
|
|
411
|
+
font-size:16px;flex-shrink:0;background:#3c3c3c;
|
|
209
412
|
transition:background .3s;
|
|
210
413
|
}
|
|
211
414
|
.dot.patched{background:#4ec94e}
|
|
212
415
|
.dot.not-found{background:#e06c75}
|
|
213
|
-
.status-label{font-size:
|
|
416
|
+
.status-label{font-size:16px;font-weight:700;line-height:1}
|
|
214
417
|
.status-label.patched{color:#4ec94e}
|
|
215
418
|
.status-label.pending{color:#e5c07b}
|
|
216
419
|
.status-label.not-found{color:#e06c75}
|
|
217
420
|
.status-label.loading{color:var(--vscode-descriptionForeground)}
|
|
218
|
-
.status-desc{font-size:
|
|
421
|
+
.status-desc{font-size:10px;color:var(--vscode-descriptionForeground);margin-top:3px}
|
|
422
|
+
|
|
423
|
+
/* ── Security section ── */
|
|
424
|
+
.section{
|
|
425
|
+
border:1px solid var(--vscode-panel-border);
|
|
426
|
+
border-radius:6px;margin-bottom:10px;overflow:hidden;
|
|
427
|
+
}
|
|
428
|
+
.section-header{
|
|
429
|
+
display:flex;align-items:center;justify-content:space-between;
|
|
430
|
+
padding:7px 10px;
|
|
431
|
+
background:var(--vscode-editor-background);
|
|
432
|
+
font-size:11px;font-weight:600;
|
|
433
|
+
border-bottom:1px solid var(--vscode-panel-border);
|
|
434
|
+
}
|
|
435
|
+
.section-header .badge{
|
|
436
|
+
font-size:9px;padding:1px 6px;border-radius:10px;
|
|
437
|
+
background:#e06c75;color:#fff;font-weight:700;
|
|
438
|
+
}
|
|
439
|
+
.section-header .badge.on{background:#4ec94e}
|
|
440
|
+
.blocklist{padding:6px 10px}
|
|
441
|
+
.block-item{
|
|
442
|
+
display:flex;align-items:center;gap:5px;
|
|
443
|
+
font-size:10px;padding:2px 0;color:var(--vscode-descriptionForeground);
|
|
444
|
+
}
|
|
445
|
+
.block-dot{
|
|
446
|
+
width:5px;height:5px;border-radius:50%;background:#e5c07b;flex-shrink:0;
|
|
447
|
+
}
|
|
219
448
|
|
|
449
|
+
/* ── Path box ── */
|
|
220
450
|
.path-box{
|
|
221
|
-
font-size:
|
|
451
|
+
font-size:9px;color:var(--vscode-descriptionForeground);
|
|
222
452
|
background:var(--vscode-editor-background);
|
|
223
453
|
border:1px solid var(--vscode-panel-border);
|
|
224
|
-
border-radius:4px;padding:6px
|
|
454
|
+
border-radius:4px;padding:4px 6px;margin-bottom:8px;
|
|
225
455
|
word-break:break-all;
|
|
226
456
|
}
|
|
227
457
|
|
|
228
|
-
|
|
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
|
-
|
|
458
|
+
/* ── Buttons ── */
|
|
237
459
|
.btn{
|
|
238
|
-
width:100%;padding:
|
|
239
|
-
font-size:
|
|
460
|
+
width:100%;padding:8px;border:none;border-radius:5px;
|
|
461
|
+
font-size:11px;font-weight:700;letter-spacing:.4px;
|
|
240
462
|
cursor:pointer;transition:background .2s,transform .1s;
|
|
241
|
-
font-family:inherit;margin-bottom:
|
|
463
|
+
font-family:inherit;margin-bottom:5px;
|
|
242
464
|
}
|
|
243
465
|
.btn:active{transform:scale(.98)}
|
|
244
|
-
.btn:disabled{opacity:.
|
|
466
|
+
.btn:disabled{opacity:.4;cursor:not-allowed}
|
|
245
467
|
.btn-apply{background:#0e7a4c;color:#fff}
|
|
246
468
|
.btn-apply:hover:not(:disabled){background:#0f9058}
|
|
247
469
|
.btn-revert{background:#5a1a1a;color:#fff}
|
|
248
470
|
.btn-revert:hover:not(:disabled){background:#7a2020}
|
|
249
471
|
.btn-refresh{background:var(--vscode-button-secondaryBackground);color:var(--vscode-button-secondaryForeground)}
|
|
472
|
+
.btn-settings{
|
|
473
|
+
background:transparent;color:var(--vscode-descriptionForeground);
|
|
474
|
+
border:1px solid var(--vscode-panel-border);font-size:10px;
|
|
475
|
+
margin-top:2px;
|
|
476
|
+
}
|
|
477
|
+
.btn-settings:hover{background:var(--vscode-editor-background)}
|
|
250
478
|
|
|
251
479
|
.note{
|
|
252
|
-
margin-top:
|
|
480
|
+
margin-top:8px;font-size:9px;
|
|
253
481
|
color:var(--vscode-descriptionForeground);
|
|
254
482
|
text-align:center;line-height:1.5;
|
|
255
483
|
}
|
|
256
484
|
</style>
|
|
257
485
|
</head>
|
|
258
486
|
<body>
|
|
487
|
+
|
|
259
488
|
<div class="header">
|
|
260
489
|
<span class="header-icon">⚡</span>
|
|
261
490
|
<div>
|
|
262
|
-
<div class="header-title">Antigravity
|
|
263
|
-
<div class="header-sub">
|
|
491
|
+
<div class="header-title">Antigravity AutoPilot</div>
|
|
492
|
+
<div class="header-sub">Auto-execute all tool calls & commands</div>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<!-- Enabled/Disabled toggle -->
|
|
497
|
+
<div class="toggle-row" id="toggleRow">
|
|
498
|
+
<div>
|
|
499
|
+
<div class="toggle-label">AutoPilot</div>
|
|
500
|
+
<div class="toggle-sub" id="toggleSub">Active — executing all commands</div>
|
|
264
501
|
</div>
|
|
502
|
+
<label class="switch" title="Toggle AutoPilot on/off">
|
|
503
|
+
<input type="checkbox" id="toggleCheck" checked onchange="send('toggleEnabled')">
|
|
504
|
+
<span class="slider"></span>
|
|
505
|
+
</label>
|
|
265
506
|
</div>
|
|
266
507
|
|
|
508
|
+
<!-- Patch Status -->
|
|
267
509
|
<div class="status-card" id="card">
|
|
268
510
|
<div class="status-row">
|
|
269
511
|
<div class="dot" id="dot">⊘</div>
|
|
@@ -276,26 +518,62 @@ class AntigravityPanelProvider {
|
|
|
276
518
|
|
|
277
519
|
<div class="path-box" id="pathBox" style="display:none"></div>
|
|
278
520
|
|
|
279
|
-
|
|
521
|
+
<!-- Dangerous Command Blocking section -->
|
|
522
|
+
<div class="section">
|
|
523
|
+
<div class="section-header">
|
|
524
|
+
🛡️ Command Blocking
|
|
525
|
+
<span class="badge on" id="blockBadge">ON</span>
|
|
526
|
+
</div>
|
|
527
|
+
<div class="blocklist">
|
|
528
|
+
<div class="block-item"><span class="block-dot"></span>rm -rf / and variants (Linux/macOS)</div>
|
|
529
|
+
<div class="block-item"><span class="block-dot"></span>dd / mkfs / wipefs on devices</div>
|
|
530
|
+
<div class="block-item"><span class="block-dot"></span>format C: / Remove-Item -Force (Windows)</div>
|
|
531
|
+
<div class="block-item"><span class="block-dot"></span>curl/wget pipe to shell</div>
|
|
532
|
+
<div class="block-item"><span class="block-dot"></span>Fork bombs, IEX download-exec</div>
|
|
533
|
+
<div class="block-item"><span class="block-dot"></span>diskutil erase, bcdedit delete</div>
|
|
534
|
+
<div class="block-item" style="color:var(--vscode-foreground);font-style:italic">+ 40 more built-in patterns</div>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
280
537
|
|
|
281
|
-
<button class="btn btn-apply" id="btnApply"
|
|
282
|
-
<button class="btn btn-revert" id="btnRevert"
|
|
283
|
-
<button class="btn btn-refresh"
|
|
538
|
+
<button class="btn btn-apply" id="btnApply" style="display:none">⚡ APPLY PATCH</button>
|
|
539
|
+
<button class="btn btn-revert" id="btnRevert" style="display:none">↩ REVERT PATCH</button>
|
|
540
|
+
<button class="btn btn-refresh">🔄 Refresh Status</button>
|
|
541
|
+
<button class="btn btn-settings" onclick="send('openSettings')">⚙️ Open Settings</button>
|
|
284
542
|
|
|
285
543
|
<div class="note" id="noteBox"></div>
|
|
286
544
|
|
|
287
545
|
<script>
|
|
288
546
|
const vscode = acquireVsCodeApi();
|
|
547
|
+
|
|
289
548
|
function send(cmd) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
549
|
+
if (cmd !== 'openSettings' && cmd !== 'toggleEnabled' && cmd !== 'refresh') {
|
|
550
|
+
document.getElementById('btnApply').disabled = true;
|
|
551
|
+
document.getElementById('btnRevert').disabled = true;
|
|
552
|
+
}
|
|
293
553
|
vscode.postMessage({ command: cmd });
|
|
294
554
|
}
|
|
555
|
+
|
|
556
|
+
// Wire up buttons
|
|
557
|
+
document.getElementById('btnApply').addEventListener('click', () => send('apply'));
|
|
558
|
+
document.getElementById('btnRevert').addEventListener('click', () => send('revert'));
|
|
559
|
+
document.querySelector('.btn-refresh').addEventListener('click', () => {
|
|
560
|
+
send('refresh');
|
|
561
|
+
document.querySelector('.btn-refresh').disabled = true;
|
|
562
|
+
setTimeout(() => { document.querySelector('.btn-refresh').disabled = false; }, 2000);
|
|
563
|
+
});
|
|
564
|
+
|
|
295
565
|
send('refresh');
|
|
296
566
|
|
|
297
567
|
window.addEventListener('message', e => {
|
|
298
|
-
const { command, patched, basePath, files, text } = e.data;
|
|
568
|
+
const { command, patched, basePath, files, text, enabled } = e.data;
|
|
569
|
+
|
|
570
|
+
if (command === 'setEnabled') {
|
|
571
|
+
const chk = document.getElementById('toggleCheck');
|
|
572
|
+
chk.checked = enabled;
|
|
573
|
+
document.getElementById('toggleSub').textContent = enabled
|
|
574
|
+
? 'Active — executing all commands'
|
|
575
|
+
: 'Suspended — commands require confirmation';
|
|
576
|
+
}
|
|
299
577
|
|
|
300
578
|
if (command === 'loading') {
|
|
301
579
|
document.getElementById('lbl').className = 'status-label loading';
|
|
@@ -318,42 +596,24 @@ class AntigravityPanelProvider {
|
|
|
318
596
|
|
|
319
597
|
const lbl = document.getElementById('lbl');
|
|
320
598
|
lbl.className = 'status-label ' + (notFound ? 'not-found' : patched ? 'patched' : 'pending');
|
|
321
|
-
lbl.textContent = notFound ? '
|
|
599
|
+
lbl.textContent = notFound ? 'Not Found' : patched ? 'Patched' : 'Not Patched';
|
|
322
600
|
|
|
323
601
|
document.getElementById('desc').textContent = notFound
|
|
324
|
-
? 'Antigravity not
|
|
325
|
-
: patched
|
|
326
|
-
? 'useEffect added — restart Antigravity!'
|
|
327
|
-
: 'Patch not applied yet';
|
|
602
|
+
? 'Antigravity installation not detected'
|
|
603
|
+
: patched ? 'AutoPilot is active on this machine' : 'Click APPLY PATCH to activate';
|
|
328
604
|
|
|
329
|
-
const pathBox = document.getElementById('pathBox');
|
|
330
605
|
if (basePath) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
}
|
|
606
|
+
const pb = document.getElementById('pathBox');
|
|
607
|
+
pb.textContent = basePath;
|
|
608
|
+
pb.style.display = 'block';
|
|
347
609
|
}
|
|
348
610
|
|
|
349
|
-
document.getElementById('btnApply').style.display =
|
|
350
|
-
document.getElementById('btnRevert').style.display = patched ? '' : 'none';
|
|
611
|
+
document.getElementById('btnApply').style.display = notFound || patched ? 'none' : 'block';
|
|
612
|
+
document.getElementById('btnRevert').style.display = patched ? 'block' : 'none';
|
|
351
613
|
|
|
352
614
|
document.getElementById('noteBox').textContent = notFound
|
|
353
|
-
? '
|
|
354
|
-
: patched
|
|
355
|
-
? '💡 Re-run after Antigravity updates'
|
|
356
|
-
: '💡 Apply patch once, then restart Antigravity';
|
|
615
|
+
? 'Install Antigravity first, then click Refresh.'
|
|
616
|
+
: patched ? 'Restart Antigravity to apply changes.' : '';
|
|
357
617
|
});
|
|
358
618
|
</script>
|
|
359
619
|
</body>
|
|
@@ -361,38 +621,30 @@ class AntigravityPanelProvider {
|
|
|
361
621
|
}
|
|
362
622
|
}
|
|
363
623
|
|
|
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
|
-
}
|
|
624
|
+
// ─── Status Bar (legacy helpers) ──────────────────────────────────────────────
|
|
382
625
|
|
|
383
626
|
/** @deprecated kept for backward compat */
|
|
384
627
|
function updateStatusBar() { updateStatusBarFromCache(); }
|
|
385
628
|
|
|
386
|
-
// ─── Activate / Deactivate
|
|
629
|
+
// ─── Activate / Deactivate ────────────────────────────────────────────────────
|
|
387
630
|
|
|
388
631
|
/** @param {vscode.ExtensionContext} context */
|
|
389
632
|
function activate(context) {
|
|
390
|
-
//
|
|
633
|
+
// Shared output channel
|
|
634
|
+
outputChannel = vscode.window.createOutputChannel('AutoPilot');
|
|
635
|
+
context.subscriptions.push(outputChannel);
|
|
636
|
+
|
|
637
|
+
// Read enabledOnStartup setting
|
|
638
|
+
const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
639
|
+
autoPilotEnabled = cfg.get('enabledOnStartup', true);
|
|
640
|
+
|
|
641
|
+
// Status bar
|
|
391
642
|
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
392
643
|
statusBarItem.command = 'antigravityAutoAccept.openPanel';
|
|
393
644
|
statusBarItem.text = `$(sync~spin) AG Patch`;
|
|
394
645
|
statusBarItem.tooltip = 'Checking patch status...';
|
|
395
646
|
statusBarItem.show();
|
|
647
|
+
context.subscriptions.push(statusBarItem);
|
|
396
648
|
|
|
397
649
|
// Sidebar
|
|
398
650
|
panelProvider = new AntigravityPanelProvider(context);
|
|
@@ -404,6 +656,36 @@ function activate(context) {
|
|
|
404
656
|
),
|
|
405
657
|
);
|
|
406
658
|
|
|
659
|
+
// ── Terminal command watcher (Dangerous Command Blocking) ──────────────────
|
|
660
|
+
// VS Code API: onDidWriteTerminalData captures output; we intercept typed
|
|
661
|
+
// commands via onDidStartTerminalShellExecution (VS Code 1.87+).
|
|
662
|
+
// Fallback: detect via terminal write events.
|
|
663
|
+
if (typeof vscode.window.onDidStartTerminalShellExecution === 'function') {
|
|
664
|
+
context.subscriptions.push(
|
|
665
|
+
vscode.window.onDidStartTerminalShellExecution((event) => {
|
|
666
|
+
const cmd = event.execution.commandLine?.value || '';
|
|
667
|
+
if (!cmd) return;
|
|
668
|
+
const check = checkDangerousCommand(cmd);
|
|
669
|
+
if (check.matched) {
|
|
670
|
+
handleDangerousCommand(cmd, check.label);
|
|
671
|
+
// Note: VS Code does not expose a cancellation API for shell exec;
|
|
672
|
+
// we log/warn/notify. For full blocking, pair with shell hook.
|
|
673
|
+
}
|
|
674
|
+
}),
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Config change listener — react to user toggling blocking or action
|
|
679
|
+
context.subscriptions.push(
|
|
680
|
+
vscode.workspace.onDidChangeConfiguration((e) => {
|
|
681
|
+
if (e.affectsConfiguration('antigravityAutoAccept.enabledOnStartup')) {
|
|
682
|
+
autoPilotEnabled = vscode.workspace.getConfiguration('antigravityAutoAccept').get('enabledOnStartup', true);
|
|
683
|
+
updateStatusBarFromCache();
|
|
684
|
+
if (panelProvider) panelProvider.sendEnabled(autoPilotEnabled);
|
|
685
|
+
}
|
|
686
|
+
}),
|
|
687
|
+
);
|
|
688
|
+
|
|
407
689
|
// Commands
|
|
408
690
|
context.subscriptions.push(
|
|
409
691
|
vscode.commands.registerCommand('antigravityAutoAccept.applyPatch', async () => {
|
|
@@ -429,20 +711,21 @@ function activate(context) {
|
|
|
429
711
|
}),
|
|
430
712
|
);
|
|
431
713
|
|
|
432
|
-
context.subscriptions.push(statusBarItem);
|
|
433
|
-
|
|
434
714
|
// Async startup — never blocks extension host!
|
|
435
715
|
(async () => {
|
|
436
716
|
const status = await getPatchStatus();
|
|
437
717
|
isPatchApplied = status.patched;
|
|
438
718
|
updateStatusBarFromCache();
|
|
439
|
-
if (panelProvider)
|
|
719
|
+
if (panelProvider) {
|
|
720
|
+
panelProvider.sendStatus(status);
|
|
721
|
+
panelProvider.sendEnabled(autoPilotEnabled);
|
|
722
|
+
}
|
|
440
723
|
|
|
441
|
-
const
|
|
442
|
-
if (
|
|
724
|
+
const startCfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
|
|
725
|
+
if (startCfg.get('applyOnStartup') && !status.patched && status.basePath) {
|
|
443
726
|
const result = await applyPatch();
|
|
444
727
|
if (result.success) {
|
|
445
|
-
|
|
728
|
+
outputChannel.appendLine('[AutoPilot] Auto-patch applied on startup');
|
|
446
729
|
}
|
|
447
730
|
}
|
|
448
731
|
})();
|
package/package.json
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
"name": "antigravity-autopilot",
|
|
3
3
|
"displayName": "Antigravity AutoPilot",
|
|
4
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.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"publisher": "nguyen-hoang",
|
|
7
|
+
"bin": {
|
|
8
|
+
"antigravity-autopilot": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/nguyenhx2/Antigravity-AutoPilot"
|
|
13
|
+
},
|
|
7
14
|
"engines": {
|
|
8
15
|
"vscode": "^1.85.0"
|
|
9
16
|
},
|
|
@@ -15,30 +22,30 @@
|
|
|
15
22
|
"viewsContainers": {
|
|
16
23
|
"activitybar": [
|
|
17
24
|
{
|
|
18
|
-
"id": "antigravity-
|
|
19
|
-
"title": "Antigravity
|
|
25
|
+
"id": "antigravity-autopilot",
|
|
26
|
+
"title": "Antigravity AutoPilot",
|
|
20
27
|
"icon": "icon-sidebar.svg"
|
|
21
28
|
}
|
|
22
29
|
]
|
|
23
30
|
},
|
|
24
31
|
"views": {
|
|
25
|
-
"antigravity-
|
|
32
|
+
"antigravity-autopilot": [
|
|
26
33
|
{
|
|
27
34
|
"type": "webview",
|
|
28
35
|
"id": "antigravityAutoAccept.panel",
|
|
29
|
-
"name": "
|
|
36
|
+
"name": "AutoPilot"
|
|
30
37
|
}
|
|
31
38
|
]
|
|
32
39
|
},
|
|
33
40
|
"commands": [
|
|
34
41
|
{
|
|
35
42
|
"command": "antigravityAutoAccept.applyPatch",
|
|
36
|
-
"title": "Antigravity: Apply
|
|
43
|
+
"title": "Antigravity: Apply AutoPilot Patch",
|
|
37
44
|
"icon": "$(zap)"
|
|
38
45
|
},
|
|
39
46
|
{
|
|
40
47
|
"command": "antigravityAutoAccept.revertPatch",
|
|
41
|
-
"title": "Antigravity: Revert
|
|
48
|
+
"title": "Antigravity: Revert AutoPilot Patch",
|
|
42
49
|
"icon": "$(discard)"
|
|
43
50
|
},
|
|
44
51
|
{
|
|
@@ -48,7 +55,7 @@
|
|
|
48
55
|
},
|
|
49
56
|
{
|
|
50
57
|
"command": "antigravityAutoAccept.openPanel",
|
|
51
|
-
"title": "Antigravity: Open
|
|
58
|
+
"title": "Antigravity: Open AutoPilot Panel"
|
|
52
59
|
}
|
|
53
60
|
],
|
|
54
61
|
"keybindings": [
|
|
@@ -58,12 +65,39 @@
|
|
|
58
65
|
}
|
|
59
66
|
],
|
|
60
67
|
"configuration": {
|
|
61
|
-
"title": "Antigravity
|
|
68
|
+
"title": "Antigravity AutoPilot",
|
|
62
69
|
"properties": {
|
|
63
70
|
"antigravityAutoAccept.applyOnStartup": {
|
|
64
71
|
"type": "boolean",
|
|
65
72
|
"default": false,
|
|
66
|
-
"description": "Automatically apply the patch when
|
|
73
|
+
"description": "Automatically apply the AutoPilot patch when VS Code starts. Safe to enable — patch is idempotent."
|
|
74
|
+
},
|
|
75
|
+
"antigravityAutoAccept.enabledOnStartup": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"default": true,
|
|
78
|
+
"description": "Keep AutoPilot enabled (active) when VS Code starts. When disabled, AutoPilot will be suspended until manually turned on from the sidebar."
|
|
79
|
+
},
|
|
80
|
+
"antigravityAutoAccept.dangerousCommandBlocking.enabled": {
|
|
81
|
+
"type": "boolean",
|
|
82
|
+
"default": true,
|
|
83
|
+
"description": "Block dangerous terminal commands (e.g. rm -rf /, format C:) before they execute. Fully customizable via the blocklist setting."
|
|
84
|
+
},
|
|
85
|
+
"antigravityAutoAccept.dangerousCommandBlocking.action": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["block", "warn", "log"],
|
|
88
|
+
"enumDescriptions": [
|
|
89
|
+
"Block the command entirely and show an error notification.",
|
|
90
|
+
"Show a warning notification but still allow the command to proceed.",
|
|
91
|
+
"Silently log the command to the output channel and allow it to proceed."
|
|
92
|
+
],
|
|
93
|
+
"default": "block",
|
|
94
|
+
"description": "Action to take when a dangerous command is detected."
|
|
95
|
+
},
|
|
96
|
+
"antigravityAutoAccept.dangerousCommandBlocking.customPatterns": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"items": { "type": "string" },
|
|
99
|
+
"default": [],
|
|
100
|
+
"description": "Additional regex patterns (JavaScript syntax) to treat as dangerous commands. Example: [\"^dd if=.*of=/dev/sd\", \"^shred\"]"
|
|
67
101
|
}
|
|
68
102
|
}
|
|
69
103
|
}
|