vibecodingmachine-cli 2025.12.6-1702 ā 2025.12.24-2348
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/bin/vibecodingmachine.js +260 -52
- package/logs/audit/2025-12-24.jsonl +2 -0
- package/package.json +2 -2
- package/reproduce_issue.js +160 -0
- package/src/commands/auth.js +19 -19
- package/src/commands/auto-direct.js +133 -93
- package/src/commands/auto.js +165 -67
- package/src/commands/computers.js +62 -59
- package/src/commands/locale.js +73 -0
- package/src/commands/repo.js +0 -1
- package/src/commands/requirements-remote.js +14 -9
- package/src/commands/requirements.js +29 -3
- package/src/commands/setup.js +14 -13
- package/src/commands/status.js +17 -18
- package/src/commands/sync.js +72 -71
- package/src/utils/agent-selector.js +50 -0
- package/src/utils/antigravity-installer.js +212 -0
- package/src/utils/antigravity-js-handler.js +60 -0
- package/src/utils/asset-cleanup.js +0 -1
- package/src/utils/auth.js +149 -2
- package/src/utils/auto-mode-ansi-ui.js +0 -1
- package/src/utils/auto-mode-simple-ui.js +1 -1
- package/src/utils/compliance-check.js +166 -0
- package/src/utils/config.js +27 -1
- package/src/utils/copy-with-progress.js +167 -0
- package/src/utils/download-with-progress.js +84 -0
- package/src/utils/first-run.js +189 -71
- package/src/utils/interactive.js +419 -362
- package/src/utils/kiro-installer.js +56 -24
- package/src/utils/persistent-header.js +1 -3
- package/src/utils/provider-registry.js +43 -5
- package/src/utils/user-tracking.js +300 -0
- package/tests/requirements-navigator-buildtree-await.test.js +28 -0
package/src/commands/sync.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const Table = require('cli-table3');
|
|
3
3
|
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
4
|
+
const { t } = require('vibecodingmachine-core');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Trigger immediate sync
|
|
7
8
|
*/
|
|
8
|
-
async function syncNow(
|
|
9
|
+
async function syncNow() {
|
|
9
10
|
const syncEngine = new SyncEngine();
|
|
10
11
|
|
|
11
12
|
try {
|
|
12
|
-
console.log(chalk.cyan(
|
|
13
|
-
|
|
13
|
+
console.log(chalk.cyan(`\nš ${t('sync.starting')}\n`));
|
|
14
|
+
|
|
14
15
|
await syncEngine.initialize();
|
|
15
16
|
|
|
16
17
|
// Listen for sync completion
|
|
@@ -31,18 +32,18 @@ async function syncNow(options = {}) {
|
|
|
31
32
|
const result = await syncPromise;
|
|
32
33
|
|
|
33
34
|
const status = syncEngine.getStatus();
|
|
34
|
-
|
|
35
|
-
console.log(chalk.green('
|
|
36
|
-
console.log(chalk.white('
|
|
37
|
-
console.log(chalk.white('
|
|
38
|
-
console.log(chalk.white('
|
|
39
|
-
console.log(chalk.white('
|
|
40
|
-
console.log(chalk.white('
|
|
35
|
+
|
|
36
|
+
console.log(chalk.green(`ā ${t('sync.complete')}\n`));
|
|
37
|
+
console.log(chalk.white(t('sync.last.sync').padEnd(17)) + new Date(status.lastSyncTime).toLocaleString());
|
|
38
|
+
console.log(chalk.white(t('sync.remote.changes').padEnd(17)) + (result.remoteChanges || 0));
|
|
39
|
+
console.log(chalk.white(t('sync.local.changes').padEnd(17)) + (result.localChanges || 0));
|
|
40
|
+
console.log(chalk.white(t('sync.conflicts').padEnd(17)) + (result.conflicts || 0));
|
|
41
|
+
console.log(chalk.white(t('sync.queued.changes').padEnd(17)) + status.queuedChanges);
|
|
41
42
|
console.log('');
|
|
42
43
|
|
|
43
44
|
} catch (error) {
|
|
44
|
-
console.error(chalk.red(
|
|
45
|
-
console.log(chalk.gray('\
|
|
45
|
+
console.error(chalk.red(`\nā ${t('sync.failed')}`), error.message);
|
|
46
|
+
console.log(chalk.gray(`\n${t('sync.tip.aws')}\n`));
|
|
46
47
|
// Don't throw - just log the error
|
|
47
48
|
} finally {
|
|
48
49
|
syncEngine.stop();
|
|
@@ -52,49 +53,49 @@ async function syncNow(options = {}) {
|
|
|
52
53
|
/**
|
|
53
54
|
* Show sync status and statistics
|
|
54
55
|
*/
|
|
55
|
-
async function syncStatus(
|
|
56
|
+
async function syncStatus() {
|
|
56
57
|
const syncEngine = new SyncEngine();
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
60
|
await syncEngine.initialize();
|
|
60
61
|
|
|
61
62
|
const status = syncEngine.getStatus();
|
|
62
|
-
|
|
63
|
-
console.log('\n' + chalk.bold.cyan('
|
|
63
|
+
|
|
64
|
+
console.log('\n' + chalk.bold.cyan(t('sync.status.title')));
|
|
64
65
|
console.log(chalk.gray('ā'.repeat(60)));
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
// Connection status
|
|
67
68
|
const onlineIcon = status.isOnline ? chalk.green('ā') : chalk.red('ā');
|
|
68
|
-
const onlineText = status.isOnline ? '
|
|
69
|
-
console.log(chalk.white('
|
|
70
|
-
|
|
69
|
+
const onlineText = status.isOnline ? t('sync.status.online') : t('sync.status.offline');
|
|
70
|
+
console.log(chalk.white(t('sync.connection').padEnd(17)) + onlineIcon + ' ' + onlineText);
|
|
71
|
+
|
|
71
72
|
// Sync status
|
|
72
73
|
const syncingIcon = status.isSyncing ? chalk.yellow('ā³') : chalk.gray('ā');
|
|
73
|
-
const syncingText = status.isSyncing ? '
|
|
74
|
-
console.log(chalk.white('
|
|
75
|
-
|
|
74
|
+
const syncingText = status.isSyncing ? t('sync.status.syncing') : t('sync.status.idle');
|
|
75
|
+
console.log(chalk.white(t('sync.status').padEnd(17)) + syncingIcon + ' ' + syncingText);
|
|
76
|
+
|
|
76
77
|
// Last sync
|
|
77
|
-
const lastSync = status.lastSyncTime
|
|
78
|
+
const lastSync = status.lastSyncTime
|
|
78
79
|
? new Date(status.lastSyncTime).toLocaleString()
|
|
79
|
-
: '
|
|
80
|
-
console.log(chalk.white('
|
|
81
|
-
|
|
80
|
+
: t('sync.never');
|
|
81
|
+
console.log(chalk.white(t('sync.last.sync').padEnd(17)) + lastSync);
|
|
82
|
+
|
|
82
83
|
// Queued changes
|
|
83
84
|
const queueColor = status.queuedChanges > 0 ? chalk.yellow : chalk.gray;
|
|
84
|
-
console.log(chalk.white('
|
|
85
|
-
|
|
85
|
+
console.log(chalk.white(t('sync.queued.changes').padEnd(17)) + queueColor(status.queuedChanges));
|
|
86
|
+
|
|
86
87
|
// Conflict strategy
|
|
87
|
-
console.log(chalk.white('
|
|
88
|
-
|
|
88
|
+
console.log(chalk.white(t('sync.conflict.mode').padEnd(17)) + status.conflictStrategy);
|
|
89
|
+
|
|
89
90
|
// Computer ID
|
|
90
|
-
console.log(chalk.white('
|
|
91
|
-
|
|
91
|
+
console.log(chalk.white(t('sync.computer.id').padEnd(17)) + status.computerId);
|
|
92
|
+
|
|
92
93
|
console.log('');
|
|
93
94
|
|
|
94
95
|
// Show recent sync events
|
|
95
96
|
const history = syncEngine.getHistory(5);
|
|
96
97
|
if (history.length > 0) {
|
|
97
|
-
console.log(chalk.bold.cyan('
|
|
98
|
+
console.log(chalk.bold.cyan(t('sync.recent.events')));
|
|
98
99
|
console.log(chalk.gray('ā'.repeat(60)));
|
|
99
100
|
|
|
100
101
|
for (const event of history.reverse()) {
|
|
@@ -107,7 +108,7 @@ async function syncStatus(options = {}) {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
} catch (error) {
|
|
110
|
-
console.error(chalk.red(
|
|
111
|
+
console.error(chalk.red(`\nā ${t('sync.status.failed')}`), error.message);
|
|
111
112
|
throw error;
|
|
112
113
|
} finally {
|
|
113
114
|
syncEngine.stop();
|
|
@@ -117,28 +118,28 @@ async function syncStatus(options = {}) {
|
|
|
117
118
|
/**
|
|
118
119
|
* View pending changes in offline queue
|
|
119
120
|
*/
|
|
120
|
-
async function viewQueue(
|
|
121
|
+
async function viewQueue() {
|
|
121
122
|
const syncEngine = new SyncEngine();
|
|
122
123
|
|
|
123
124
|
try {
|
|
124
125
|
await syncEngine.initialize();
|
|
125
126
|
|
|
126
127
|
const status = syncEngine.getStatus();
|
|
127
|
-
|
|
128
|
+
|
|
128
129
|
if (status.queuedChanges === 0) {
|
|
129
|
-
console.log(chalk.gray('
|
|
130
|
+
console.log(chalk.gray(`\n${t('sync.queue.no.pending')}\n`));
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
|
-
|
|
133
|
-
console.log(chalk.cyan(`\nš
|
|
134
|
-
|
|
133
|
+
|
|
134
|
+
console.log(chalk.cyan(`\nš ${t('sync.queue.title', { count: status.queuedChanges })}\n`));
|
|
135
|
+
|
|
135
136
|
// Create table
|
|
136
137
|
const table = new Table({
|
|
137
138
|
head: [
|
|
138
|
-
chalk.cyan('
|
|
139
|
-
chalk.cyan('
|
|
140
|
-
chalk.cyan('
|
|
141
|
-
chalk.cyan('
|
|
139
|
+
chalk.cyan(t('sync.queue.header.number')),
|
|
140
|
+
chalk.cyan(t('sync.queue.header.type')),
|
|
141
|
+
chalk.cyan(t('sync.queue.header.requirement')),
|
|
142
|
+
chalk.cyan(t('sync.queue.header.timestamp'))
|
|
142
143
|
],
|
|
143
144
|
colWidths: [5, 15, 40, 20]
|
|
144
145
|
});
|
|
@@ -154,15 +155,15 @@ async function viewQueue(options = {}) {
|
|
|
154
155
|
});
|
|
155
156
|
|
|
156
157
|
console.log(table.toString() + '\n');
|
|
157
|
-
|
|
158
|
-
console.log(chalk.gray('
|
|
159
|
-
console.log(chalk.white('
|
|
160
|
-
console.log(chalk.gray(' vcm sync:force ') + '
|
|
161
|
-
console.log(chalk.gray(' vcm sync:now ') + '
|
|
158
|
+
|
|
159
|
+
console.log(chalk.gray(`${t('sync.queue.will.sync')}\n`));
|
|
160
|
+
console.log(chalk.white(t('sync.queue.commands')));
|
|
161
|
+
console.log(chalk.gray(' vcm sync:force ') + `- ${t('sync.queue.force.now')}`);
|
|
162
|
+
console.log(chalk.gray(' vcm sync:now ') + `- ${t('sync.queue.sync.online')}`);
|
|
162
163
|
console.log('');
|
|
163
164
|
|
|
164
165
|
} catch (error) {
|
|
165
|
-
console.error(chalk.red(
|
|
166
|
+
console.error(chalk.red(`\nā ${t('sync.queue.view.failed')}`), error.message);
|
|
166
167
|
throw error;
|
|
167
168
|
} finally {
|
|
168
169
|
syncEngine.stop();
|
|
@@ -172,29 +173,29 @@ async function viewQueue(options = {}) {
|
|
|
172
173
|
/**
|
|
173
174
|
* Force sync even if offline
|
|
174
175
|
*/
|
|
175
|
-
async function forceSync(
|
|
176
|
+
async function forceSync() {
|
|
176
177
|
const syncEngine = new SyncEngine();
|
|
177
178
|
|
|
178
179
|
try {
|
|
179
|
-
console.log(chalk.yellow(
|
|
180
|
-
|
|
180
|
+
console.log(chalk.yellow(`\nā ${t('sync.force.starting')}\n`));
|
|
181
|
+
|
|
181
182
|
await syncEngine.initialize();
|
|
182
|
-
|
|
183
|
+
|
|
183
184
|
// Temporarily set online
|
|
184
185
|
const wasOnline = syncEngine.isOnline;
|
|
185
186
|
syncEngine.setOnlineMode(true);
|
|
186
|
-
|
|
187
|
+
|
|
187
188
|
try {
|
|
188
189
|
await syncEngine.sync();
|
|
189
|
-
console.log(chalk.green('
|
|
190
|
+
console.log(chalk.green(`ā ${t('sync.force.complete')}\n`));
|
|
190
191
|
} finally {
|
|
191
192
|
// Restore original online status
|
|
192
193
|
syncEngine.setOnlineMode(wasOnline);
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
} catch (error) {
|
|
196
|
-
console.error(chalk.red(
|
|
197
|
-
console.log(chalk.gray('
|
|
197
|
+
console.error(chalk.red(`\nā ${t('sync.force.failed')}`), error.message);
|
|
198
|
+
console.log(chalk.gray(`\n${t('sync.force.unreachable')}\n`));
|
|
198
199
|
throw error;
|
|
199
200
|
} finally {
|
|
200
201
|
syncEngine.stop();
|
|
@@ -212,20 +213,20 @@ async function viewHistory(options = {}) {
|
|
|
212
213
|
|
|
213
214
|
const limit = parseInt(options.limit) || 50;
|
|
214
215
|
const history = syncEngine.getHistory(limit);
|
|
215
|
-
|
|
216
|
+
|
|
216
217
|
if (history.length === 0) {
|
|
217
|
-
console.log(chalk.gray('
|
|
218
|
+
console.log(chalk.gray(`\n${t('sync.history.no.events')}\n`));
|
|
218
219
|
return;
|
|
219
220
|
}
|
|
220
|
-
|
|
221
|
-
console.log(chalk.cyan(`\nš
|
|
222
|
-
|
|
221
|
+
|
|
222
|
+
console.log(chalk.cyan(`\nš ${t('sync.history.title', { count: history.length })}\n`));
|
|
223
|
+
|
|
223
224
|
// Create table
|
|
224
225
|
const table = new Table({
|
|
225
226
|
head: [
|
|
226
|
-
chalk.cyan('
|
|
227
|
-
chalk.cyan('
|
|
228
|
-
chalk.cyan('
|
|
227
|
+
chalk.cyan(t('sync.history.header.time')),
|
|
228
|
+
chalk.cyan(t('sync.history.header.type')),
|
|
229
|
+
chalk.cyan(t('sync.history.header.details'))
|
|
229
230
|
],
|
|
230
231
|
colWidths: [20, 20, 50]
|
|
231
232
|
});
|
|
@@ -242,7 +243,7 @@ async function viewHistory(options = {}) {
|
|
|
242
243
|
console.log(table.toString() + '\n');
|
|
243
244
|
|
|
244
245
|
} catch (error) {
|
|
245
|
-
console.error(chalk.red(
|
|
246
|
+
console.error(chalk.red(`\nā ${t('sync.history.view.failed')}`), error.message);
|
|
246
247
|
throw error;
|
|
247
248
|
} finally {
|
|
248
249
|
syncEngine.stop();
|
|
@@ -259,13 +260,13 @@ function truncate(str, maxLength) {
|
|
|
259
260
|
function getEventDetails(event) {
|
|
260
261
|
switch (event.type) {
|
|
261
262
|
case 'conflict-resolution':
|
|
262
|
-
return
|
|
263
|
+
return t('sync.event.resolved', { id: event.resolution?.requirementId || 'unknown' });
|
|
263
264
|
case 'sync-complete':
|
|
264
|
-
return
|
|
265
|
+
return t('sync.event.remote.local', { remote: event.remoteChanges || 0, local: event.localChanges || 0 });
|
|
265
266
|
case 'remote-change-applied':
|
|
266
|
-
return
|
|
267
|
+
return t('sync.event.applied', { id: event.change?.requirementId || 'unknown' });
|
|
267
268
|
case 'local-change-pushed':
|
|
268
|
-
return
|
|
269
|
+
return t('sync.event.pushed', { id: event.change?.requirementId || 'unknown' });
|
|
269
270
|
default:
|
|
270
271
|
return JSON.stringify(event).substring(0, 47);
|
|
271
272
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized agent selection logic
|
|
3
|
+
* Respects provider preferences order and ensures DRY principle
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { getProviderPreferences } = require('./provider-registry');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the effective agent based on provider preferences and options
|
|
10
|
+
* @param {Object} options - Command line options (may include ide)
|
|
11
|
+
* @param {Array} providerDefinitions - Array of provider definitions
|
|
12
|
+
* @param {Map} providerDefinitionMap - Map of provider definitions by ID
|
|
13
|
+
* @returns {Object} - { effectiveAgent: string, providerDef: Object }
|
|
14
|
+
*/
|
|
15
|
+
async function getEffectiveAgent(options = {}, providerDefinitions, providerDefinitionMap) {
|
|
16
|
+
const prefs = await getProviderPreferences();
|
|
17
|
+
|
|
18
|
+
// Get all available providers in the order specified in preferences
|
|
19
|
+
const availableProviders = [];
|
|
20
|
+
for (const id of prefs.order) {
|
|
21
|
+
if (prefs.enabled[id] !== false && providerDefinitionMap.has(id)) {
|
|
22
|
+
availableProviders.push(id);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// If no providers are available, use the first one from definitions as fallback
|
|
27
|
+
if (availableProviders.length === 0) {
|
|
28
|
+
availableProviders.push(providerDefinitions[0]?.id || 'claude-code');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Use the first available provider by default, unless overridden by options
|
|
32
|
+
let effectiveAgent = options.ide || availableProviders[0];
|
|
33
|
+
|
|
34
|
+
// If the requested agent isn't available, use the first available one
|
|
35
|
+
if (!availableProviders.includes(effectiveAgent)) {
|
|
36
|
+
effectiveAgent = availableProviders[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const providerDef = providerDefinitionMap.get(effectiveAgent);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
effectiveAgent,
|
|
43
|
+
providerDef,
|
|
44
|
+
availableProviders
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
getEffectiveAgent
|
|
50
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
async function installAntigravity() {
|
|
9
|
+
console.log(chalk.cyan('\nš Initiating Google Antigravity IDE Installation...'));
|
|
10
|
+
|
|
11
|
+
const spinner = ora('Checking system requirements...').start();
|
|
12
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
13
|
+
|
|
14
|
+
if (os.platform() !== 'darwin') {
|
|
15
|
+
spinner.fail('Automated Antigravity installation is only supported on macOS');
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const arch = os.arch();
|
|
20
|
+
const isArm = arch === 'arm64';
|
|
21
|
+
|
|
22
|
+
// URLs from electron-app IDE installer
|
|
23
|
+
const downloadUrl = isArm
|
|
24
|
+
? 'https://edgedl.me.gvt1.com/edgedl/release2/j0qc3/antigravity/stable/1.11.2-6251250307170304/darwin-arm/Antigravity.dmg'
|
|
25
|
+
: 'https://edgedl.me.gvt1.com/edgedl/release2/j0qc3/antigravity/stable/1.11.2-6251250307170304/darwin-x64/Antigravity.dmg';
|
|
26
|
+
|
|
27
|
+
spinner.succeed(`System OK (${isArm ? 'Apple Silicon' : 'Intel'})`);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const cacheDir = path.join(os.homedir(), '.vibecodingmachine', 'cache');
|
|
31
|
+
await fs.ensureDir(cacheDir);
|
|
32
|
+
const cachedPath = path.join(cacheDir, 'Antigravity.dmg');
|
|
33
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'antigravity-'));
|
|
34
|
+
const dmgPath = path.join(tempDir, 'Antigravity.dmg');
|
|
35
|
+
const { downloadWithProgress } = require('./download-with-progress');
|
|
36
|
+
|
|
37
|
+
// Prefer cached DMG if it exists and matches upstream size (or exists at all)
|
|
38
|
+
let usedCache = false;
|
|
39
|
+
try {
|
|
40
|
+
if (await fs.pathExists(cachedPath)) {
|
|
41
|
+
// Try HEAD to compare sizes
|
|
42
|
+
try {
|
|
43
|
+
const fetch = require('node-fetch');
|
|
44
|
+
const head = await fetch(downloadUrl, { method: 'HEAD' });
|
|
45
|
+
const total = Number(head.headers.get('content-length')) || 0;
|
|
46
|
+
const stat = await fs.stat(cachedPath);
|
|
47
|
+
if (total && stat.size === total) {
|
|
48
|
+
await fs.copy(cachedPath, dmgPath);
|
|
49
|
+
usedCache = true;
|
|
50
|
+
console.log(chalk.gray('Using cached Antigravity DMG'));
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// If HEAD fails, still allow reuse of cache to avoid re-download
|
|
54
|
+
await fs.copy(cachedPath, dmgPath);
|
|
55
|
+
usedCache = true;
|
|
56
|
+
console.log(chalk.gray('Using cached Antigravity DMG (no HEAD)'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// ignore cache errors
|
|
61
|
+
usedCache = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Use download helper which displays progress and ETA if we didn't reuse cache
|
|
65
|
+
if (!usedCache) {
|
|
66
|
+
await downloadWithProgress(downloadUrl, dmgPath, { label: 'Downloading Antigravity...' });
|
|
67
|
+
// Save to cache for future runs
|
|
68
|
+
try { await fs.copy(dmgPath, cachedPath); } catch (e) { /* ignore cache write errors */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const installSpinner = ora('Installing Antigravity...').start();
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
installSpinner.text = 'Mounting disk image...';
|
|
75
|
+
execSync(`hdiutil attach "${dmgPath}" -nobrowse -noverify -mountpoint "${tempDir}/mount"`);
|
|
76
|
+
|
|
77
|
+
installSpinner.text = 'Copying to /Applications...';
|
|
78
|
+
const files = await fs.readdir(path.join(tempDir, 'mount'));
|
|
79
|
+
const appName = files.find((f) => f.endsWith('.app'));
|
|
80
|
+
if (!appName) throw new Error('Could not find .app in DMG');
|
|
81
|
+
|
|
82
|
+
const src = path.join(tempDir, 'mount', appName);
|
|
83
|
+
const dest = path.join('/Applications', appName);
|
|
84
|
+
|
|
85
|
+
if (await fs.pathExists(dest)) {
|
|
86
|
+
installSpinner.text = 'Replacing existing application...';
|
|
87
|
+
await fs.remove(dest);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Prefer rsync/ditto; fallback to node-stream copy with progress
|
|
91
|
+
try {
|
|
92
|
+
const { copyAppWithProgress } = require('./copy-with-progress');
|
|
93
|
+
installSpinner.stop();
|
|
94
|
+
const ok = await copyAppWithProgress(src, dest, { spinner: installSpinner });
|
|
95
|
+
if (!ok) {
|
|
96
|
+
installSpinner.text = 'Copying (fs fallback) to /Applications...';
|
|
97
|
+
await fs.copy(src, dest);
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
// final fallback
|
|
101
|
+
installSpinner.text = 'Copying (fs fallback) to /Applications...';
|
|
102
|
+
try {
|
|
103
|
+
execSync(`cp -R "${src}" "/Applications/"`, { stdio: 'inherit' });
|
|
104
|
+
} catch (err) {
|
|
105
|
+
await fs.copy(src, dest);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
installSpinner.text = 'Cleaning up...';
|
|
110
|
+
execSync(`hdiutil detach "${tempDir}/mount" -force`);
|
|
111
|
+
await fs.remove(tempDir);
|
|
112
|
+
|
|
113
|
+
installSpinner.succeed('Antigravity installed');
|
|
114
|
+
console.log(chalk.green(`\nā
Installed to ${dest}`));
|
|
115
|
+
// Attempt to automatically configure Antigravity's first-run settings
|
|
116
|
+
try {
|
|
117
|
+
await configureAntigravityDefaults(dest);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
// Non-fatal: log and continue
|
|
120
|
+
console.log(chalk.yellow('Warning: could not auto-configure Antigravity:'), e.message || e);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
installSpinner.fail('Installation failed');
|
|
126
|
+
try {
|
|
127
|
+
if (await fs.pathExists(path.join(tempDir, 'mount'))) {
|
|
128
|
+
execSync(`hdiutil detach "${tempDir}/mount" -force`);
|
|
129
|
+
}
|
|
130
|
+
try { await fs.remove(tempDir); } catch (e) { /* ignore */ }
|
|
131
|
+
console.log(chalk.red('\nInstallation error:'), err.message);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(chalk.red('\nFailed to install Antigravity:'), error.message);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function configureAntigravityDefaults() {
|
|
141
|
+
// Attempt to open Antigravity and set the onboarding defaults via AppleScript
|
|
142
|
+
// Desired selections:
|
|
143
|
+
// - Agent-driven development (instead of recommended Agent-assisted)
|
|
144
|
+
// - Terminal execution policy: Turbo
|
|
145
|
+
// - Review policy: Always Proceed
|
|
146
|
+
// - Use the default allowlist for the browser (checked)
|
|
147
|
+
try {
|
|
148
|
+
const script = `
|
|
149
|
+
tell application "Antigravity"
|
|
150
|
+
activate
|
|
151
|
+
end tell
|
|
152
|
+
delay 0.8
|
|
153
|
+
tell application "System Events"
|
|
154
|
+
tell process "Antigravity"
|
|
155
|
+
set frontmost to true
|
|
156
|
+
delay 0.5
|
|
157
|
+
-- Try to select Agent-driven development radio button
|
|
158
|
+
try
|
|
159
|
+
click radio button "Agent-driven development" of radio group 1 of window 1
|
|
160
|
+
delay 0.2
|
|
161
|
+
end try
|
|
162
|
+
|
|
163
|
+
-- Set Terminal execution policy to Turbo (attempt by label, then by index)
|
|
164
|
+
try
|
|
165
|
+
try
|
|
166
|
+
click pop up button "Terminal execution policy" of window 1
|
|
167
|
+
delay 0.2
|
|
168
|
+
click menu item "Turbo" of menu 1 of pop up button "Terminal execution policy" of window 1
|
|
169
|
+
on error
|
|
170
|
+
-- fallback: click first pop up button and choose Turbo
|
|
171
|
+
click pop up button 1 of window 1
|
|
172
|
+
delay 0.2
|
|
173
|
+
click menu item "Turbo" of menu 1 of pop up button 1 of window 1
|
|
174
|
+
end try
|
|
175
|
+
delay 0.2
|
|
176
|
+
end try
|
|
177
|
+
|
|
178
|
+
-- Set Review policy to Always Proceed
|
|
179
|
+
try
|
|
180
|
+
click pop up button "Review policy" of window 1
|
|
181
|
+
delay 0.2
|
|
182
|
+
click menu item "Always Proceed" of menu 1 of pop up button "Review policy" of window 1
|
|
183
|
+
delay 0.2
|
|
184
|
+
end try
|
|
185
|
+
|
|
186
|
+
-- Ensure default allowlist checkbox is checked
|
|
187
|
+
try
|
|
188
|
+
set cb to checkbox "Use the default allowlist for the browser" of window 1
|
|
189
|
+
if (value of cb as boolean) is false then click cb
|
|
190
|
+
delay 0.2
|
|
191
|
+
end try
|
|
192
|
+
|
|
193
|
+
-- Advance the onboarding if a Next/Done button exists
|
|
194
|
+
try
|
|
195
|
+
if exists button "Next" of window 1 then click button "Next" of window 1
|
|
196
|
+
delay 0.3
|
|
197
|
+
end try
|
|
198
|
+
end tell
|
|
199
|
+
end tell
|
|
200
|
+
`;
|
|
201
|
+
|
|
202
|
+
// Use osascript to run the UI automation. JSON-stringify ensures safe quoting.
|
|
203
|
+
const { execSync } = require('child_process');
|
|
204
|
+
execSync(`osascript -e ${JSON.stringify(script)}`, { stdio: 'ignore', timeout: 15000 });
|
|
205
|
+
console.log(chalk.gray('Auto-configured Antigravity first-run preferences'));
|
|
206
|
+
return true;
|
|
207
|
+
} catch (err) {
|
|
208
|
+
throw new Error(err.message || err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = { installAntigravity };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const {
|
|
3
|
+
getProviderPreferences,
|
|
4
|
+
saveProviderPreferences
|
|
5
|
+
} = require('./provider-registry');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if Antigravity agent has hit a rate limit.
|
|
9
|
+
* @param {string} stderr - Standard error output from the agent.
|
|
10
|
+
* @returns {{isRateLimited: boolean, message: string|null}} - Rate limit status and message.
|
|
11
|
+
*/
|
|
12
|
+
function checkAntigravityRateLimit(stderr) {
|
|
13
|
+
const rateLimitPatterns = [
|
|
14
|
+
/quota limit/i,
|
|
15
|
+
/rate limit/i,
|
|
16
|
+
/too many requests/i,
|
|
17
|
+
/limit exceeded/i
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const pattern of rateLimitPatterns) {
|
|
21
|
+
if (pattern.test(stderr)) {
|
|
22
|
+
return {
|
|
23
|
+
isRateLimited: true,
|
|
24
|
+
message: 'Antigravity quota limit reached.'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { isRateLimited: false, message: null };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Handle rate limit for Antigravity by disabling it and selecting the next available provider.
|
|
34
|
+
* @returns {Promise<{success: boolean, nextProvider: string|null, error: string|null}>}
|
|
35
|
+
*/
|
|
36
|
+
async function handleAntigravityRateLimit() {
|
|
37
|
+
console.log(chalk.yellow('Antigravity rate limit detected. Disabling for this session.'));
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const prefs = await getProviderPreferences();
|
|
41
|
+
prefs.enabled.antigravity = false;
|
|
42
|
+
await saveProviderPreferences(prefs);
|
|
43
|
+
|
|
44
|
+
const nextProvider = prefs.order.find(p => p !== 'antigravity' && prefs.enabled[p]);
|
|
45
|
+
|
|
46
|
+
if (nextProvider) {
|
|
47
|
+
console.log(chalk.cyan(`Switching to next available provider: ${nextProvider}`));
|
|
48
|
+
return { success: true, nextProvider, error: null };
|
|
49
|
+
} else {
|
|
50
|
+
return { success: false, nextProvider: null, error: 'No fallback providers available.' };
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return { success: false, nextProvider: null, error: 'Failed to update provider preferences.' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
checkAntigravityRateLimit,
|
|
59
|
+
handleAntigravityRateLimit
|
|
60
|
+
};
|