antigravity-autopilot 1.4.3 → 1.4.5

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.
@@ -1,6 +1,6 @@
1
1
  # Antigravity Auto-Accept - Floating Panel
2
2
  # A small always-on-top window for toggling auto-accept
3
- # Run with: powershell -WindowStyle Hidden -File AutoAccept.ps1
3
+ # Run with: powershell -WindowStyle Hidden -File AutoPilot.ps1
4
4
 
5
5
  Add-Type -AssemblyName System.Windows.Forms
6
6
  Add-Type -AssemblyName System.Drawing
package/README.md CHANGED
@@ -95,7 +95,7 @@ Built-in protection against destructive commands. **54+ preset patterns** coveri
95
95
  Add your own patterns via Settings:
96
96
 
97
97
  ```json
98
- "antigravityAutoAccept.dangerousCommandBlocking.customPatterns": [
98
+ "antigravityAutoPilot.dangerousCommandBlocking.customPatterns": [
99
99
  "^my-dangerous-script",
100
100
  "DROP TABLE"
101
101
  ]
package/extension.js CHANGED
@@ -112,7 +112,7 @@ function getActiveBuiltinPatterns() {
112
112
  * @returns {{ matched: boolean, label: string, pattern: string }}
113
113
  */
114
114
  function checkDangerousCommand(cmd) {
115
- const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
115
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
116
116
  const enabled = cfg.get('dangerousCommandBlocking.enabled', true);
117
117
  if (!enabled) return { matched: false, label: '', pattern: '' };
118
118
 
@@ -149,7 +149,7 @@ function checkDangerousCommand(cmd) {
149
149
  * @param {string} label - Human-readable reason
150
150
  */
151
151
  function handleDangerousCommand(cmd, label) {
152
- const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
152
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
153
153
  const action = cfg.get('dangerousCommandBlocking.action', 'block');
154
154
  const msg = `🛡️ Dangerous command detected: "${label}" — \`${cmd.trim().substring(0, 80)}\``;
155
155
 
@@ -326,16 +326,16 @@ class AntigravityPanelProvider {
326
326
  } else if (msg.command === 'toggleEnabled') {
327
327
  autoPilotEnabled = !autoPilotEnabled;
328
328
  updateStatusBarFromCache();
329
- const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
329
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
330
330
  await cfg.update('enabledOnStartup', autoPilotEnabled, vscode.ConfigurationTarget.Global);
331
331
  if (panelProvider) panelProvider.sendEnabled(autoPilotEnabled);
332
332
  vscode.window.showInformationMessage(
333
333
  autoPilotEnabled ? '⚡ AutoPilot resumed' : '⏸ AutoPilot suspended',
334
334
  );
335
335
  } else if (msg.command === 'openSettings') {
336
- vscode.commands.executeCommand('workbench.action.openSettings', 'antigravityAutoAccept');
336
+ vscode.commands.executeCommand('workbench.action.openSettings', 'antigravityAutoPilot');
337
337
  } else if (msg.command === 'toggleCommandBlocking') {
338
- const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
338
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
339
339
  const current = cfg.get('dangerousCommandBlocking.enabled', true);
340
340
  const next = !current;
341
341
  await cfg.update('dangerousCommandBlocking.enabled', next, vscode.ConfigurationTarget.Global);
@@ -343,6 +343,24 @@ class AntigravityPanelProvider {
343
343
  vscode.window.showInformationMessage(
344
344
  next ? '🛡️ Command Blocking enabled' : '⚠️ Command Blocking disabled',
345
345
  );
346
+ } else if (msg.command === 'toggleBrowserPermission') {
347
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
348
+ const current = cfg.get('browserPermission', true);
349
+ const next = !current;
350
+ await cfg.update('browserPermission', next, vscode.ConfigurationTarget.Global);
351
+ this.sendBrowserPermissionEnabled(next);
352
+ vscode.window.showInformationMessage(
353
+ next ? '🌐 Auto-accept browser permission enabled' : '🌐 Auto-accept browser permission disabled',
354
+ );
355
+ } else if (msg.command === 'toggleFilePermission') {
356
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
357
+ const current = cfg.get('filePermission', true);
358
+ const next = !current;
359
+ await cfg.update('filePermission', next, vscode.ConfigurationTarget.Global);
360
+ this.sendFilePermissionEnabled(next);
361
+ vscode.window.showInformationMessage(
362
+ next ? '📁 Auto-accept file permission enabled' : '📁 Auto-accept file permission disabled',
363
+ );
346
364
  } else if (msg.command === 'removePattern') {
347
365
  // Remove a built-in pattern by ID
348
366
  const removed = getRemovedPatternIds();
@@ -362,7 +380,13 @@ class AntigravityPanelProvider {
362
380
  refreshStatus();
363
381
  this.sendEnabled(autoPilotEnabled);
364
382
  this.sendBlockingEnabled(
365
- vscode.workspace.getConfiguration('antigravityAutoAccept').get('dangerousCommandBlocking.enabled', true),
383
+ vscode.workspace.getConfiguration('antigravityAutoPilot').get('dangerousCommandBlocking.enabled', true),
384
+ );
385
+ this.sendBrowserPermissionEnabled(
386
+ vscode.workspace.getConfiguration('antigravityAutoPilot').get('browserPermission', true),
387
+ );
388
+ this.sendFilePermissionEnabled(
389
+ vscode.workspace.getConfiguration('antigravityAutoPilot').get('filePermission', true),
366
390
  );
367
391
  this.sendPatterns();
368
392
  }
@@ -395,6 +419,18 @@ class AntigravityPanelProvider {
395
419
  this._view.webview.postMessage({ command: 'setBlockingEnabled', enabled });
396
420
  }
397
421
 
422
+ /** @param {boolean} enabled */
423
+ sendBrowserPermissionEnabled(enabled) {
424
+ if (!this._view) return;
425
+ this._view.webview.postMessage({ command: 'setBrowserPermissionEnabled', enabled });
426
+ }
427
+
428
+ /** @param {boolean} enabled */
429
+ sendFilePermissionEnabled(enabled) {
430
+ if (!this._view) return;
431
+ this._view.webview.postMessage({ command: 'setFilePermissionEnabled', enabled });
432
+ }
433
+
398
434
  /** Send current pattern list to the webview */
399
435
  sendPatterns() {
400
436
  if (!this._view) return;
@@ -633,6 +669,30 @@ class AntigravityPanelProvider {
633
669
  </label>
634
670
  </div>
635
671
 
672
+ <!-- Browser Permission toggle -->
673
+ <div class="toggle-row" id="browserPermToggleRow">
674
+ <div>
675
+ <div class="toggle-label">🌐 Browser Permission</div>
676
+ <div class="toggle-sub" id="browserPermToggleSub">Active — auto-accepting browser actions</div>
677
+ </div>
678
+ <label class="switch" title="Toggle auto-accept browser permission">
679
+ <input type="checkbox" id="browserPermToggleCheck" checked onchange="send('toggleBrowserPermission')">
680
+ <span class="slider"></span>
681
+ </label>
682
+ </div>
683
+
684
+ <!-- File Permission toggle -->
685
+ <div class="toggle-row" id="filePermToggleRow">
686
+ <div>
687
+ <div class="toggle-label">📁 File Permission</div>
688
+ <div class="toggle-sub" id="filePermToggleSub">Active — auto-allowing file access</div>
689
+ </div>
690
+ <label class="switch" title="Toggle auto-accept file permission">
691
+ <input type="checkbox" id="filePermToggleCheck" checked onchange="send('toggleFilePermission')">
692
+ <span class="slider"></span>
693
+ </label>
694
+ </div>
695
+
636
696
  <!-- Dangerous Command Blocking section -->
637
697
  <div class="section" id="blockSection">
638
698
  <div class="section-header" id="blockHeader">
@@ -660,6 +720,7 @@ class AntigravityPanelProvider {
660
720
 
661
721
  function send(cmd, extra) {
662
722
  if (cmd !== 'openSettings' && cmd !== 'toggleEnabled' && cmd !== 'toggleCommandBlocking'
723
+ && cmd !== 'toggleBrowserPermission' && cmd !== 'toggleFilePermission'
663
724
  && cmd !== 'refresh' && cmd !== 'removePattern' && cmd !== 'resetPatterns') {
664
725
  document.getElementById('btnApply').disabled = true;
665
726
  document.getElementById('btnRevert').disabled = true;
@@ -751,6 +812,22 @@ class AntigravityPanelProvider {
751
812
  if (section) section.style.opacity = data.enabled ? '1' : '0.4';
752
813
  }
753
814
 
815
+ if (data.command === 'setBrowserPermissionEnabled') {
816
+ const chk = document.getElementById('browserPermToggleCheck');
817
+ chk.checked = data.enabled;
818
+ document.getElementById('browserPermToggleSub').textContent = data.enabled
819
+ ? 'Active — auto-accepting browser actions'
820
+ : 'Disabled — browser actions require confirmation';
821
+ }
822
+
823
+ if (data.command === 'setFilePermissionEnabled') {
824
+ const chk = document.getElementById('filePermToggleCheck');
825
+ chk.checked = data.enabled;
826
+ document.getElementById('filePermToggleSub').textContent = data.enabled
827
+ ? 'Active — auto-allowing file access'
828
+ : 'Disabled — file access requires confirmation';
829
+ }
830
+
754
831
  if (data.command === 'patterns') {
755
832
  renderPatterns(data.patterns, data.totalBuiltin);
756
833
  }
@@ -817,12 +894,12 @@ function activate(context) {
817
894
  context.subscriptions.push(outputChannel);
818
895
 
819
896
  // Read enabledOnStartup setting
820
- const cfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
897
+ const cfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
821
898
  autoPilotEnabled = cfg.get('enabledOnStartup', true);
822
899
 
823
900
  // Status bar
824
901
  statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
825
- statusBarItem.command = 'antigravityAutoAccept.openPanel';
902
+ statusBarItem.command = 'antigravityAutoPilot.openPanel';
826
903
  statusBarItem.text = `$(sync~spin) AG Patch`;
827
904
  statusBarItem.tooltip = 'Checking patch status...';
828
905
  statusBarItem.show();
@@ -832,7 +909,7 @@ function activate(context) {
832
909
  panelProvider = new AntigravityPanelProvider(context);
833
910
  context.subscriptions.push(
834
911
  vscode.window.registerWebviewViewProvider(
835
- 'antigravityAutoAccept.panel',
912
+ 'antigravityAutoPilot.panel',
836
913
  panelProvider,
837
914
  { webviewOptions: { retainContextWhenHidden: true } },
838
915
  ),
@@ -855,8 +932,8 @@ function activate(context) {
855
932
  // Config change listener
856
933
  context.subscriptions.push(
857
934
  vscode.workspace.onDidChangeConfiguration((e) => {
858
- if (e.affectsConfiguration('antigravityAutoAccept.enabledOnStartup')) {
859
- autoPilotEnabled = vscode.workspace.getConfiguration('antigravityAutoAccept').get('enabledOnStartup', true);
935
+ if (e.affectsConfiguration('antigravityAutoPilot.enabledOnStartup')) {
936
+ autoPilotEnabled = vscode.workspace.getConfiguration('antigravityAutoPilot').get('enabledOnStartup', true);
860
937
  updateStatusBarFromCache();
861
938
  if (panelProvider) panelProvider.sendEnabled(autoPilotEnabled);
862
939
  }
@@ -865,18 +942,18 @@ function activate(context) {
865
942
 
866
943
  // Commands
867
944
  context.subscriptions.push(
868
- vscode.commands.registerCommand('antigravityAutoAccept.applyPatch', async () => {
945
+ vscode.commands.registerCommand('antigravityAutoPilot.applyPatch', async () => {
869
946
  const result = await applyPatch();
870
947
  vscode.window.showInformationMessage(result.message);
871
948
  }),
872
- vscode.commands.registerCommand('antigravityAutoAccept.revertPatch', async () => {
949
+ vscode.commands.registerCommand('antigravityAutoPilot.revertPatch', async () => {
873
950
  const result = await revertPatch();
874
951
  vscode.window.showInformationMessage(result.message);
875
952
  }),
876
- vscode.commands.registerCommand('antigravityAutoAccept.openPanel', () => {
877
- vscode.commands.executeCommand('antigravityAutoAccept.panel.focus');
953
+ vscode.commands.registerCommand('antigravityAutoPilot.openPanel', () => {
954
+ vscode.commands.executeCommand('antigravityAutoPilot.panel.focus');
878
955
  }),
879
- vscode.commands.registerCommand('antigravityAutoAccept.checkStatus', async () => {
956
+ vscode.commands.registerCommand('antigravityAutoPilot.checkStatus', async () => {
880
957
  const status = await getPatchStatus();
881
958
  if (!status.basePath) {
882
959
  vscode.window.showWarningMessage('Antigravity not found!');
@@ -898,7 +975,7 @@ function activate(context) {
898
975
  panelProvider.sendEnabled(autoPilotEnabled);
899
976
  }
900
977
 
901
- const startCfg = vscode.workspace.getConfiguration('antigravityAutoAccept');
978
+ const startCfg = vscode.workspace.getConfiguration('antigravityAutoPilot');
902
979
  if (startCfg.get('applyOnStartup') && !status.patched && status.basePath) {
903
980
  const result = await applyPatch();
904
981
  if (result.success) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
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.4.3",
5
+ "version": "1.4.5",
6
6
  "license": "MIT",
7
7
  "publisher": "nguyen-hoang",
8
8
  "bin": {
@@ -37,57 +37,57 @@
37
37
  "antigravity-autopilot": [
38
38
  {
39
39
  "type": "webview",
40
- "id": "antigravityAutoAccept.panel",
40
+ "id": "antigravityAutoPilot.panel",
41
41
  "name": "AutoPilot"
42
42
  }
43
43
  ]
44
44
  },
45
45
  "commands": [
46
46
  {
47
- "command": "antigravityAutoAccept.applyPatch",
47
+ "command": "antigravityAutoPilot.applyPatch",
48
48
  "title": "Antigravity: Apply AutoPilot Patch",
49
49
  "icon": "$(zap)"
50
50
  },
51
51
  {
52
- "command": "antigravityAutoAccept.revertPatch",
52
+ "command": "antigravityAutoPilot.revertPatch",
53
53
  "title": "Antigravity: Revert AutoPilot Patch",
54
54
  "icon": "$(discard)"
55
55
  },
56
56
  {
57
- "command": "antigravityAutoAccept.checkStatus",
57
+ "command": "antigravityAutoPilot.checkStatus",
58
58
  "title": "Antigravity: Check Patch Status",
59
59
  "icon": "$(info)"
60
60
  },
61
61
  {
62
- "command": "antigravityAutoAccept.openPanel",
62
+ "command": "antigravityAutoPilot.openPanel",
63
63
  "title": "Antigravity: Open AutoPilot Panel"
64
64
  }
65
65
  ],
66
66
  "keybindings": [
67
67
  {
68
- "command": "antigravityAutoAccept.applyPatch",
68
+ "command": "antigravityAutoPilot.applyPatch",
69
69
  "key": "ctrl+shift+f12"
70
70
  }
71
71
  ],
72
72
  "configuration": {
73
73
  "title": "Antigravity AutoPilot",
74
74
  "properties": {
75
- "antigravityAutoAccept.applyOnStartup": {
75
+ "antigravityAutoPilot.applyOnStartup": {
76
76
  "type": "boolean",
77
77
  "default": false,
78
78
  "description": "Automatically apply the AutoPilot patch when VS Code starts. Safe to enable — patch is idempotent."
79
79
  },
80
- "antigravityAutoAccept.enabledOnStartup": {
80
+ "antigravityAutoPilot.enabledOnStartup": {
81
81
  "type": "boolean",
82
82
  "default": true,
83
83
  "description": "Keep AutoPilot enabled (active) when VS Code starts. When disabled, AutoPilot will be suspended until manually turned on from the sidebar."
84
84
  },
85
- "antigravityAutoAccept.dangerousCommandBlocking.enabled": {
85
+ "antigravityAutoPilot.dangerousCommandBlocking.enabled": {
86
86
  "type": "boolean",
87
87
  "default": true,
88
88
  "description": "Block dangerous terminal commands (e.g. rm -rf /, format C:) before they execute. Fully customizable via the blocklist setting."
89
89
  },
90
- "antigravityAutoAccept.dangerousCommandBlocking.action": {
90
+ "antigravityAutoPilot.dangerousCommandBlocking.action": {
91
91
  "type": "string",
92
92
  "enum": [
93
93
  "block",
@@ -102,13 +102,23 @@
102
102
  "default": "block",
103
103
  "description": "Action to take when a dangerous command is detected."
104
104
  },
105
- "antigravityAutoAccept.dangerousCommandBlocking.customPatterns": {
105
+ "antigravityAutoPilot.dangerousCommandBlocking.customPatterns": {
106
106
  "type": "array",
107
107
  "items": {
108
108
  "type": "string"
109
109
  },
110
110
  "default": [],
111
111
  "description": "Additional regex patterns (JavaScript syntax) to treat as dangerous commands. Example: [\"^dd if=.*of=/dev/sd\", \"^shred\"]"
112
+ },
113
+ "antigravityAutoPilot.browserPermission": {
114
+ "type": "boolean",
115
+ "default": true,
116
+ "description": "Automatically accept browser action permissions (e.g. 'Agent needs permission to act on chromewebdata'). Requires patch to be applied."
117
+ },
118
+ "antigravityAutoPilot.filePermission": {
119
+ "type": "boolean",
120
+ "default": true,
121
+ "description": "Automatically allow file access permissions with conversation scope. Requires patch to be applied."
112
122
  }
113
123
  }
114
124
  }
package/patcher.js CHANGED
@@ -95,7 +95,7 @@ function getTargetFiles(basePath) {
95
95
  * Port of: https://github.com/Kanezal/better-antigravity/blob/main/fixes/auto-run-fix/patch.js
96
96
  */
97
97
  function analyzeFile(content, label) {
98
- const log = (msg) => process.send({ type: 'log', msg: `[AutoAccept] [${label}] ${msg}` });
98
+ const log = (msg) => process.send({ type: 'log', msg: `[AutoPilot] [${label}] ${msg}` });
99
99
 
100
100
  // 1. Find the onChange handler: contains setTerminalAutoExecutionPolicy AND .EAGER
101
101
  // Pattern: VARNAME=CALLBACK(ARG=>{...setTerminalAutoExecutionPolicy...,ARG===ENUM.EAGER&&CONFIRM(!0)},[...])
@@ -189,13 +189,170 @@ function analyzeFile(content, label) {
189
189
  };
190
190
  }
191
191
 
192
+ // ─── Browser Action Permission (auto-confirm) ───────────────────────────────
193
+
194
+ /**
195
+ * Finds the JPc browser-action confirmation component and builds auto-confirm patch.
196
+ * Pattern: COMP=({sourceTrajectoryStepInfo:VAR,...,url:VAR})=>{...CONFIRM_FN=Mt(()=>{SEND(Ui(MSG,{...,interaction:{case:"browserAction",value:Ui(TYPE,{confirm:!0})}}))},...)...}
197
+ */
198
+ function analyzeBrowserAction(content, label) {
199
+ const log = (msg) => process.send({ type: 'log', msg: `[AutoPilot] [${label}] [browser] ${msg}` });
200
+
201
+ // 1. Find the browserAction confirm:!0 callback pattern
202
+ // VAR=Mt(()=>{SEND(Ui(MSG,{trajectoryId:VAR,stepIndex:VAR,interaction:{case:"browserAction",value:Ui(TYPE,{confirm:!0})}}))},DEPS)
203
+ const confirmRe = /(\w+)=Mt\(\(\)=>\{(\w+)\(Ui\((\w+),\{trajectoryId:(\w+),stepIndex:(\w+),interaction:\{case:"browserAction",value:Ui\((\w+),\{confirm:!0\}\)\}\}\)\)\},\[([\w,]*)\]\)/;
204
+ const confirmMatch = content.match(confirmRe);
205
+
206
+ if (!confirmMatch) {
207
+ log('❌ Could not find browserAction confirm pattern');
208
+ const idx = content.indexOf('browserAction');
209
+ if (idx >= 0) {
210
+ log(` Context: ...${content.slice(Math.max(0, idx - 80), idx + 120)}...`);
211
+ }
212
+ return null;
213
+ }
214
+
215
+ const [fullMatch, confirmVar] = confirmMatch;
216
+ const matchIndex = content.indexOf(fullMatch);
217
+ log(`✓ Found browserAction confirm at offset ${matchIndex}`);
218
+ log(` confirmVar=${confirmVar}`);
219
+
220
+ // 2. Find useEffect alias (reuse from nearby code)
221
+ const nearbyCode = content.substring(Math.max(0, matchIndex - 5000), matchIndex + 5000);
222
+ const effectCandidates = {};
223
+ const effectRe = /\b(\w{2,3})\(\(\)=>\{[^}]{3,80}\},\[/g;
224
+ let m;
225
+ while ((m = effectRe.exec(nearbyCode)) !== null) {
226
+ const alias = m[1];
227
+ if (alias !== 'Mt' && alias !== 'Vi' && alias !== 'var' && alias !== 'new') {
228
+ effectCandidates[alias] = (effectCandidates[alias] || 0) + 1;
229
+ }
230
+ }
231
+ const cleanupRe = /\b(\w{2,3})\(\(\)=>\{[^}]*return\s*\(\)=>/g;
232
+ while ((m = cleanupRe.exec(content)) !== null) {
233
+ const alias = m[1];
234
+ if (alias !== 'Mt' && alias !== 'Vi') {
235
+ effectCandidates[alias] = (effectCandidates[alias] || 0) + 5;
236
+ }
237
+ }
238
+ let useEffectAlias = null;
239
+ let maxCount = 0;
240
+ for (const [alias, count] of Object.entries(effectCandidates)) {
241
+ if (count > maxCount) {
242
+ maxCount = count;
243
+ useEffectAlias = alias;
244
+ }
245
+ }
246
+ if (!useEffectAlias) {
247
+ log('❌ Could not determine useEffect alias');
248
+ return null;
249
+ }
250
+ log(` useEffect=${useEffectAlias} (confidence: ${maxCount} hits)`);
251
+
252
+ // 3. Build patch — auto-call confirmVar() on mount
253
+ const patchCode = `_abp=${useEffectAlias}(()=>{${confirmVar}()},[${confirmVar}]),`;
254
+
255
+ return {
256
+ target: fullMatch,
257
+ replacement: patchCode + fullMatch,
258
+ patchMarker: `_abp=${useEffectAlias}(()=>{${confirmVar}()}`,
259
+ label
260
+ };
261
+ }
262
+
263
+ // ─── File Access Permission (auto-allow with conversation scope) ─────────────
264
+
265
+ /**
266
+ * Finds the rBe file-permission component and builds auto-allow patch.
267
+ * Pattern: COMP=({sourceTrajectoryStepInfo:VAR,req:VAR,status:VAR})=>{...SEND_FN...filePermission...scope...}
268
+ */
269
+ function analyzeFilePermission(content, label) {
270
+ const log = (msg) => process.send({ type: 'log', msg: `[AutoPilot] [${label}] [file] ${msg}` });
271
+
272
+ // 1. Find the filePermission sender pattern
273
+ // VAR=(ALLOW_VAR,SCOPE_VAR)=>{SEND(Ui(MSG,{trajectoryId:VAR,stepIndex:VAR,interaction:{case:"filePermission",value:Ui(TYPE,{allow:ALLOW_VAR,scope:SCOPE_VAR,absolutePathUri:REQ.absolutePathUri})}}))};
274
+ const senderRe = /(\w+)=\((\w+),(\w+)\)=>\{(\w+)\(Ui\((\w+),\{trajectoryId:(\w+),stepIndex:(\w+),interaction:\{case:"filePermission",value:Ui\((\w+),\{allow:\2,scope:\3,absolutePathUri:(\w+)\.absolutePathUri\}\)\}\}\)\)\}/;
275
+ const senderMatch = content.match(senderRe);
276
+
277
+ if (!senderMatch) {
278
+ log('❌ Could not find filePermission sender pattern');
279
+ const idx = content.indexOf('filePermission');
280
+ if (idx >= 0) {
281
+ log(` Context: ...${content.slice(Math.max(0, idx - 80), idx + 120)}...`);
282
+ }
283
+ return null;
284
+ }
285
+
286
+ const [fullMatch, senderVar, , , , , , , , reqVar] = senderMatch;
287
+ const matchIndex = content.indexOf(fullMatch);
288
+ log(`✓ Found filePermission sender at offset ${matchIndex}`);
289
+ log(` senderVar=${senderVar}, reqVar=${reqVar}`);
290
+
291
+ // 2. Find the scope enum (kot) — look for kot.CONVERSATION or similar near filePermission
292
+ // Pattern: o(!0,ENUM.CONVERSATION) in the Allow This Conversation button
293
+ const scopeRe = new RegExp(`${senderVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\(!0,(\w+)\.CONVERSATION\)`);
294
+ const scopeMatch = content.substring(matchIndex, matchIndex + 2000).match(scopeRe);
295
+
296
+ if (!scopeMatch) {
297
+ log('❌ Could not find scope enum (CONVERSATION)');
298
+ return null;
299
+ }
300
+ const scopeEnum = scopeMatch[1];
301
+ log(` scopeEnum=${scopeEnum}`);
302
+
303
+ // 3. Find useEffect alias
304
+ const nearbyCode = content.substring(Math.max(0, matchIndex - 5000), matchIndex + 5000);
305
+ const effectCandidates = {};
306
+ const effectRe = /\b(\w{2,3})\(\(\)=>\{[^}]{3,80}\},\[/g;
307
+ let m2;
308
+ while ((m2 = effectRe.exec(nearbyCode)) !== null) {
309
+ const alias = m2[1];
310
+ if (alias !== 'Mt' && alias !== 'Vi' && alias !== 'var' && alias !== 'new') {
311
+ effectCandidates[alias] = (effectCandidates[alias] || 0) + 1;
312
+ }
313
+ }
314
+ const cleanupRe = /\b(\w{2,3})\(\(\)=>\{[^}]*return\s*\(\)=>/g;
315
+ while ((m2 = cleanupRe.exec(content)) !== null) {
316
+ const alias = m2[1];
317
+ if (alias !== 'Mt' && alias !== 'Vi') {
318
+ effectCandidates[alias] = (effectCandidates[alias] || 0) + 5;
319
+ }
320
+ }
321
+ let useEffectAlias = null;
322
+ let maxCount = 0;
323
+ for (const [alias, count] of Object.entries(effectCandidates)) {
324
+ if (count > maxCount) {
325
+ maxCount = count;
326
+ useEffectAlias = alias;
327
+ }
328
+ }
329
+ if (!useEffectAlias) {
330
+ log('❌ Could not determine useEffect alias');
331
+ return null;
332
+ }
333
+ log(` useEffect=${useEffectAlias} (confidence: ${maxCount} hits)`);
334
+
335
+ // 4. Build patch — auto-call senderVar(!0, scopeEnum.CONVERSATION) on mount
336
+ const patchCode = `_afp=${useEffectAlias}(()=>{${senderVar}(!0,${scopeEnum}.CONVERSATION)},[${senderVar}]),`;
337
+
338
+ return {
339
+ target: fullMatch,
340
+ replacement: patchCode + fullMatch,
341
+ patchMarker: `_afp=${useEffectAlias}(()=>{${senderVar}(!0,${scopeEnum}.CONVERSATION)`,
342
+ label
343
+ };
344
+ }
345
+
192
346
  // ─── File Operations ─────────────────────────────────────────────────────────
193
347
 
194
348
  function isFilePatched(filePath) {
195
349
  if (!fs.existsSync(filePath)) return false;
196
350
  try {
197
351
  const content = fs.readFileSync(filePath, 'utf8');
198
- return content.includes('_aep=') && /_aep=\w+\(\(\)=>\{[^}]+EAGER/.test(content);
352
+ const hasTerminal = content.includes('_aep=') && /_aep=\w+\(\(\)=>\{[^}]+EAGER/.test(content);
353
+ const hasBrowser = content.includes('_abp=') && /_abp=\w+\(\(\)=>\{\w+\(\)\}/.test(content);
354
+ const hasFile = content.includes('_afp=') && /_afp=\w+\(\(\)=>\{\w+\(!0,/.test(content);
355
+ return hasTerminal || hasBrowser || hasFile;
199
356
  } catch {
200
357
  return false;
201
358
  }
@@ -203,7 +360,7 @@ function isFilePatched(filePath) {
203
360
 
204
361
  function patchFile(filePath, label) {
205
362
  if (!fs.existsSync(filePath)) {
206
- process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] File not found, skipping` });
363
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] File not found, skipping` });
207
364
  return true; // optional file missing is not a failure
208
365
  }
209
366
 
@@ -211,48 +368,92 @@ function patchFile(filePath, label) {
211
368
  try {
212
369
  content = fs.readFileSync(filePath, 'utf8');
213
370
  } catch (e) {
214
- process.send({ type: 'log', msg: `[AutoAccept] ❌ [${label}] Read error: ${e.message}` });
371
+ process.send({ type: 'log', msg: `[AutoPilot] ❌ [${label}] Read error: ${e.message}` });
215
372
  return false;
216
373
  }
217
374
 
218
- if (isFilePatched(filePath)) {
219
- process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] Already patched` });
220
- return true;
375
+ // Backup original (before any patching)
376
+ const bakPath = filePath + '.bak';
377
+ if (!fs.existsSync(bakPath)) {
378
+ fs.copyFileSync(filePath, bakPath);
379
+ process.send({ type: 'log', msg: `[AutoPilot] 📦 [${label}] Backup created` });
221
380
  }
222
381
 
223
- const analysis = analyzeFile(content, label);
224
- if (!analysis) return false;
225
-
226
- // Verify target uniqueness
227
- const count = content.split(analysis.target).length - 1;
228
- if (count !== 1) {
229
- process.send({ type: 'log', msg: `[AutoAccept] ❌ [${label}] Target found ${count}x (expected 1)` });
230
- return false;
382
+ let patched = content;
383
+ let anyPatched = false;
384
+
385
+ // ── Terminal auto-execute patch ──
386
+ if (!content.includes('_aep=')) {
387
+ const analysis = analyzeFile(content, label);
388
+ if (analysis) {
389
+ const count = patched.split(analysis.target).length - 1;
390
+ if (count === 1) {
391
+ patched = patched.replace(analysis.target, analysis.replacement);
392
+ anyPatched = true;
393
+ process.send({ type: 'log', msg: `[AutoPilot] ✅ [${label}] Terminal auto-execute patched` });
394
+ } else {
395
+ process.send({ type: 'log', msg: `[AutoPilot] ⚠️ [${label}] Terminal target found ${count}x (expected 1)` });
396
+ }
397
+ }
398
+ } else {
399
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] Terminal already patched` });
231
400
  }
232
401
 
233
- // Backup original
234
- const bakPath = filePath + '.bak';
235
- if (!fs.existsSync(bakPath)) {
236
- fs.copyFileSync(filePath, bakPath);
237
- process.send({ type: 'log', msg: `[AutoAccept] 📦 [${label}] Backup created` });
402
+ // ── Browser action auto-confirm patch ──
403
+ if (!patched.includes('_abp=')) {
404
+ const browserAnalysis = analyzeBrowserAction(patched, label);
405
+ if (browserAnalysis) {
406
+ const count = patched.split(browserAnalysis.target).length - 1;
407
+ if (count === 1) {
408
+ patched = patched.replace(browserAnalysis.target, browserAnalysis.replacement);
409
+ anyPatched = true;
410
+ process.send({ type: 'log', msg: `[AutoPilot] ✅ [${label}] Browser action auto-confirm patched` });
411
+ } else {
412
+ process.send({ type: 'log', msg: `[AutoPilot] ⚠️ [${label}] Browser target found ${count}x (expected 1)` });
413
+ }
414
+ }
415
+ } else {
416
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] Browser action already patched` });
238
417
  }
239
418
 
240
- const patched = content.replace(analysis.target, analysis.replacement);
241
- fs.writeFileSync(filePath, patched, 'utf8');
419
+ // ── File permission auto-allow patch ──
420
+ if (!patched.includes('_afp=')) {
421
+ const fileAnalysis = analyzeFilePermission(patched, label);
422
+ if (fileAnalysis) {
423
+ const count = patched.split(fileAnalysis.target).length - 1;
424
+ if (count === 1) {
425
+ patched = patched.replace(fileAnalysis.target, fileAnalysis.replacement);
426
+ anyPatched = true;
427
+ process.send({ type: 'log', msg: `[AutoPilot] ✅ [${label}] File permission auto-allow patched` });
428
+ } else {
429
+ process.send({ type: 'log', msg: `[AutoPilot] ⚠️ [${label}] File target found ${count}x (expected 1)` });
430
+ }
431
+ }
432
+ } else {
433
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] File permission already patched` });
434
+ }
242
435
 
243
- const sizeDiff = fs.statSync(filePath).size - fs.statSync(bakPath).size;
244
- process.send({ type: 'log', msg: `[AutoAccept] ✅ [${label}] Patched (+${sizeDiff} bytes)` });
436
+ if (anyPatched) {
437
+ fs.writeFileSync(filePath, patched, 'utf8');
438
+ const sizeDiff = fs.statSync(filePath).size - fs.statSync(bakPath).size;
439
+ process.send({ type: 'log', msg: `[AutoPilot] ✅ [${label}] All patches applied (+${sizeDiff} bytes)` });
440
+ } else if (!content.includes('_aep=') && !content.includes('_abp=') && !content.includes('_afp=')) {
441
+ process.send({ type: 'log', msg: `[AutoPilot] ❌ [${label}] No patches could be applied` });
442
+ return false;
443
+ } else {
444
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] All patches already applied` });
445
+ }
245
446
  return true;
246
447
  }
247
448
 
248
449
  function revertFile(filePath, label) {
249
450
  const bak = filePath + '.bak';
250
451
  if (!fs.existsSync(bak)) {
251
- process.send({ type: 'log', msg: `[AutoAccept] ⏭️ [${label}] No backup, skipping` });
452
+ process.send({ type: 'log', msg: `[AutoPilot] ⏭️ [${label}] No backup, skipping` });
252
453
  return;
253
454
  }
254
455
  fs.copyFileSync(bak, filePath);
255
- process.send({ type: 'log', msg: `[AutoAccept] ✅ [${label}] Reverted` });
456
+ process.send({ type: 'log', msg: `[AutoPilot] ✅ [${label}] Reverted` });
256
457
  }
257
458
 
258
459
  // ─── Message Handler ──────────────────────────────────────────────────────────
@@ -266,11 +467,23 @@ process.on('message', (msg) => {
266
467
  process.exit(0);
267
468
  return;
268
469
  }
269
- const files = getTargetFiles(basePath).map(f => ({
270
- label: f.label,
271
- patched: isFilePatched(f.filePath),
272
- exists: fs.existsSync(f.filePath),
273
- }));
470
+ const files = getTargetFiles(basePath).map(f => {
471
+ let patchDetails = { terminal: false, browser: false, file: false };
472
+ if (fs.existsSync(f.filePath)) {
473
+ try {
474
+ const fc = fs.readFileSync(f.filePath, 'utf8');
475
+ patchDetails.terminal = fc.includes('_aep=') && /_aep=\w+\(\(\)=>\{[^}]+EAGER/.test(fc);
476
+ patchDetails.browser = fc.includes('_abp=') && /_abp=\w+\(\(\)=>\{\w+\(\)\}/.test(fc);
477
+ patchDetails.file = fc.includes('_afp=') && /_afp=\w+\(\(\)=>\{\w+\(!0,/.test(fc);
478
+ } catch { }
479
+ }
480
+ return {
481
+ label: f.label,
482
+ patched: patchDetails.terminal || patchDetails.browser || patchDetails.file,
483
+ patchDetails,
484
+ exists: fs.existsSync(f.filePath),
485
+ };
486
+ });
274
487
  process.send({ type: 'status', basePath, files });
275
488
  process.exit(0);
276
489
 
@@ -288,7 +501,7 @@ process.on('message', (msg) => {
288
501
  success,
289
502
  message: success
290
503
  ? '✅ Patch thành công! Restart Antigravity để áp dụng.'
291
- : '⚠️ Một số file không patch được. Xem Output > AutoAccept để biết chi tiết.',
504
+ : '⚠️ Một số file không patch được. Xem Output > AutoPilot để biết chi tiết.',
292
505
  });
293
506
  process.exit(success ? 0 : 1);
294
507