@quantiya/codevibe-antigravity-plugin 2.0.25 → 2.0.27
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/dist/installer-cli.js +17 -26
- package/dist/server.js +49 -2
- package/libexec/companion-launcher +8 -5
- package/package.json +2 -4
package/dist/installer-cli.js
CHANGED
|
@@ -59,8 +59,7 @@ async function ensureInstalled(options) {
|
|
|
59
59
|
const {
|
|
60
60
|
pluginJsonSourcePath,
|
|
61
61
|
installDir,
|
|
62
|
-
mcpConfigPath
|
|
63
|
-
serverScriptPath
|
|
62
|
+
mcpConfigPath
|
|
64
63
|
} = options;
|
|
65
64
|
const pid = options.pidOverride ?? process.pid;
|
|
66
65
|
let manifestContent;
|
|
@@ -94,7 +93,7 @@ async function ensureInstalled(options) {
|
|
|
94
93
|
await crashRecoverySweep(installDir, logger);
|
|
95
94
|
const currentHash = await tryReadCurrentHash(installDir);
|
|
96
95
|
if (currentHash === expectedManifestHash) {
|
|
97
|
-
await
|
|
96
|
+
await ensureMcpConfigCourtesy(mcpConfigPath, logger);
|
|
98
97
|
return { installed: false, upToDate: true, installDir, manifestHash: currentHash };
|
|
99
98
|
}
|
|
100
99
|
await stageAndInstall({
|
|
@@ -105,7 +104,7 @@ async function ensureInstalled(options) {
|
|
|
105
104
|
pid,
|
|
106
105
|
logger
|
|
107
106
|
});
|
|
108
|
-
await
|
|
107
|
+
await ensureMcpConfigCourtesy(mcpConfigPath, logger);
|
|
109
108
|
logger.info("codevibe-antigravity-plugin installed", {
|
|
110
109
|
installDir,
|
|
111
110
|
manifestHash: expectedManifestHash
|
|
@@ -200,7 +199,7 @@ async function crashRecoverySweep(installDir, logger) {
|
|
|
200
199
|
await rmRecursive(dir);
|
|
201
200
|
}
|
|
202
201
|
}
|
|
203
|
-
async function
|
|
202
|
+
async function ensureMcpConfigCourtesy(mcpConfigPath, logger) {
|
|
204
203
|
try {
|
|
205
204
|
await fs.promises.mkdir(path.dirname(mcpConfigPath), { recursive: true });
|
|
206
205
|
} catch (err) {
|
|
@@ -215,14 +214,15 @@ async function ensureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
|
215
214
|
const lockPath = `${mcpConfigPath}.lock`;
|
|
216
215
|
const releaseLock = await acquireLock(lockPath, LOCK_ACQUIRE_TIMEOUT_MS, logger, "mcp-config");
|
|
217
216
|
try {
|
|
218
|
-
await
|
|
217
|
+
await doEnsureMcpConfigCourtesy(mcpConfigPath, logger);
|
|
219
218
|
} finally {
|
|
220
219
|
await releaseLock();
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
|
-
async function
|
|
222
|
+
async function doEnsureMcpConfigCourtesy(mcpConfigPath, logger) {
|
|
224
223
|
let config = {};
|
|
225
224
|
let existingMode = null;
|
|
225
|
+
let onDiskWasValid = false;
|
|
226
226
|
try {
|
|
227
227
|
const stat = await fs.promises.stat(mcpConfigPath);
|
|
228
228
|
existingMode = stat.mode & 511;
|
|
@@ -232,6 +232,7 @@ async function doEnsureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
|
232
232
|
const parsed = JSON.parse(raw);
|
|
233
233
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
234
234
|
config = parsed;
|
|
235
|
+
onDiskWasValid = !!config.mcpServers && typeof config.mcpServers === "object" && !Array.isArray(config.mcpServers);
|
|
235
236
|
} else {
|
|
236
237
|
logger.warn("mcp_config.json root is not a plain object; overwriting", {
|
|
237
238
|
mcpConfigPath
|
|
@@ -250,23 +251,18 @@ async function doEnsureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
|
250
251
|
if (!config.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) {
|
|
251
252
|
config.mcpServers = {};
|
|
252
253
|
}
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
};
|
|
257
|
-
const current = config.mcpServers[MCP_SERVERS_KEY];
|
|
258
|
-
if (current?.env && typeof current.env === "object" && !Array.isArray(current.env)) {
|
|
259
|
-
expected.env = current.env;
|
|
254
|
+
const hadStaleEntry = Object.prototype.hasOwnProperty.call(config.mcpServers, MCP_SERVERS_KEY);
|
|
255
|
+
if (hadStaleEntry) {
|
|
256
|
+
delete config.mcpServers[MCP_SERVERS_KEY];
|
|
260
257
|
}
|
|
261
|
-
const
|
|
262
|
-
const needsWrite = !current || current.command !== expected.command || !arraysEqual(current.args ?? [], expected.args) || !envEqual;
|
|
258
|
+
const needsWrite = hadStaleEntry || !onDiskWasValid;
|
|
263
259
|
if (!needsWrite) return;
|
|
264
|
-
config.mcpServers[MCP_SERVERS_KEY] = expected;
|
|
265
260
|
const targetMode = existingMode ?? 420;
|
|
266
261
|
const randSuffix = `${process.pid}.${Date.now()}.${crypto.randomBytes(6).toString("hex")}`;
|
|
267
262
|
const tmpPath = `${mcpConfigPath}.tmp.${randSuffix}`;
|
|
268
263
|
const tmpFd = await fs.promises.open(tmpPath, "wx", targetMode);
|
|
269
264
|
try {
|
|
265
|
+
if (existingMode !== null) await tmpFd.chmod(existingMode);
|
|
270
266
|
await tmpFd.writeFile(JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
271
267
|
await tmpFd.close();
|
|
272
268
|
} catch (writeErr) {
|
|
@@ -289,10 +285,10 @@ async function doEnsureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
|
289
285
|
}
|
|
290
286
|
throw err;
|
|
291
287
|
}
|
|
292
|
-
logger.info(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
288
|
+
logger.info(
|
|
289
|
+
hadStaleEntry ? "Removed the stale codevibe-antigravity MCP server entry from mcp_config.json (the daemon is launched by the wrapper only)" : "Wrote the mcp_config.json courtesy object",
|
|
290
|
+
{ mcpConfigPath, removedStaleEntry: hadStaleEntry }
|
|
291
|
+
);
|
|
296
292
|
}
|
|
297
293
|
function sha256Hex(s) {
|
|
298
294
|
return crypto.createHash("sha256").update(s, "utf8").digest("hex");
|
|
@@ -425,11 +421,6 @@ async function rmRecursive(p) {
|
|
|
425
421
|
} catch {
|
|
426
422
|
}
|
|
427
423
|
}
|
|
428
|
-
function arraysEqual(a, b) {
|
|
429
|
-
if (a.length !== b.length) return false;
|
|
430
|
-
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
431
|
-
return true;
|
|
432
|
-
}
|
|
433
424
|
function defaultLogger() {
|
|
434
425
|
return {
|
|
435
426
|
info: () => {
|
package/dist/server.js
CHANGED
|
@@ -34,10 +34,12 @@ __export(server_exports, {
|
|
|
34
34
|
SessionNotFoundError: () => SessionNotFoundError,
|
|
35
35
|
TERMINAL_SHUTDOWN_SIGNALS: () => TERMINAL_SHUTDOWN_SIGNALS,
|
|
36
36
|
__testing: () => __testing,
|
|
37
|
+
classifyEntrypoint: () => classifyEntrypoint,
|
|
37
38
|
classifyTmuxHasSessionError: () => classifyTmuxHasSessionError,
|
|
38
39
|
generateLaunchSessionId: () => generateLaunchSessionId,
|
|
39
40
|
getActiveConversationFromCliLog: () => getActiveConversationFromCliLog,
|
|
40
|
-
parseMaybeJson: () => parseMaybeJson
|
|
41
|
+
parseMaybeJson: () => parseMaybeJson,
|
|
42
|
+
runForeignSpawnIdle: () => runForeignSpawnIdle
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(server_exports);
|
|
43
45
|
var crypto3 = __toESM(require("crypto"));
|
|
@@ -5350,8 +5352,51 @@ function getActiveConversationFromCliLog(cliLogPath) {
|
|
|
5350
5352
|
return null;
|
|
5351
5353
|
}
|
|
5352
5354
|
}
|
|
5355
|
+
function classifyEntrypoint(args) {
|
|
5356
|
+
return args.runtimeDir ? "wrapper" : "foreign";
|
|
5357
|
+
}
|
|
5358
|
+
function runForeignSpawnIdle(io) {
|
|
5359
|
+
let done = false;
|
|
5360
|
+
const finish = () => {
|
|
5361
|
+
if (done) return;
|
|
5362
|
+
done = true;
|
|
5363
|
+
io.exit(0);
|
|
5364
|
+
};
|
|
5365
|
+
io.writeStderr(
|
|
5366
|
+
"codevibe-antigravity-plugin: started without --runtime-dir, so not by the CodeVibe companion launcher (an agy mcp_config.json entry from an earlier release?). Refusing to start the daemon: no session, no backend calls. Run `codevibe --agent agy` once to remove the stale entry. Idling until the spawner closes stdin.\n"
|
|
5367
|
+
);
|
|
5368
|
+
void io.beacon().catch(() => void 0);
|
|
5369
|
+
io.onSignal(finish);
|
|
5370
|
+
io.stdin.on("end", finish);
|
|
5371
|
+
io.stdin.on("close", finish);
|
|
5372
|
+
io.stdin.on("error", finish);
|
|
5373
|
+
io.stdin.resume();
|
|
5374
|
+
return () => {
|
|
5375
|
+
done = true;
|
|
5376
|
+
};
|
|
5377
|
+
}
|
|
5353
5378
|
async function main() {
|
|
5354
5379
|
const args = parseArgs(process.argv.slice(2));
|
|
5380
|
+
if (classifyEntrypoint(args) === "foreign") {
|
|
5381
|
+
runForeignSpawnIdle({
|
|
5382
|
+
stdin: process.stdin,
|
|
5383
|
+
writeStderr: (line) => {
|
|
5384
|
+
process.stderr.write(line);
|
|
5385
|
+
},
|
|
5386
|
+
onSignal: (cb) => {
|
|
5387
|
+
process.once("SIGINT", cb);
|
|
5388
|
+
process.once("SIGTERM", cb);
|
|
5389
|
+
},
|
|
5390
|
+
exit: (code) => process.exit(code),
|
|
5391
|
+
beacon: () => fireDaemonBeacon("daemon_init_step", {
|
|
5392
|
+
step: "foreign_spawn",
|
|
5393
|
+
outcome: "refused",
|
|
5394
|
+
has_wrapper_pid: process.env.CODEVIBE_AGY_WRAPPER_PID ? "yes" : "no",
|
|
5395
|
+
has_tmux_target: process.env.CODEVIBE_AGY_TMUX_TARGET ? "yes" : "no"
|
|
5396
|
+
})
|
|
5397
|
+
});
|
|
5398
|
+
return;
|
|
5399
|
+
}
|
|
5355
5400
|
const bearerToken = process.env.CODEVIBE_AGY_MCP_TOKEN || args.token;
|
|
5356
5401
|
if (!bearerToken) {
|
|
5357
5402
|
console.error("codevibe-antigravity-plugin: bearer token required via $CODEVIBE_AGY_MCP_TOKEN or --token");
|
|
@@ -5493,8 +5538,10 @@ var __testing = {
|
|
|
5493
5538
|
SessionNotFoundError,
|
|
5494
5539
|
TERMINAL_SHUTDOWN_SIGNALS,
|
|
5495
5540
|
__testing,
|
|
5541
|
+
classifyEntrypoint,
|
|
5496
5542
|
classifyTmuxHasSessionError,
|
|
5497
5543
|
generateLaunchSessionId,
|
|
5498
5544
|
getActiveConversationFromCliLog,
|
|
5499
|
-
parseMaybeJson
|
|
5545
|
+
parseMaybeJson,
|
|
5546
|
+
runForeignSpawnIdle
|
|
5500
5547
|
});
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
# Architecture (DESIGN.md §3 + §5.2):
|
|
7
7
|
#
|
|
8
8
|
# 1. ensureInstalled (idempotent): writes plugin manifest to
|
|
9
|
-
# ~/.gemini/antigravity-cli/plugins/codevibe-antigravity/
|
|
10
|
-
# ~/.gemini/config/mcp_config.json
|
|
9
|
+
# ~/.gemini/antigravity-cli/plugins/codevibe-antigravity/ and REMOVES any
|
|
10
|
+
# stale codevibe-antigravity entry from ~/.gemini/config/mcp_config.json
|
|
11
|
+
# (P42, 2026-09-09: a registered entry made agy spawn a second, argument-
|
|
12
|
+
# less daemon every few minutes; the daemon is launched by this launcher only).
|
|
11
13
|
#
|
|
12
14
|
# 2. Generate ephemeral port + bearer token; create runtime dir at
|
|
13
15
|
# ~/.gemini/antigravity-cli/plugins/codevibe-antigravity/runtime/$$
|
|
@@ -279,9 +281,10 @@ fi
|
|
|
279
281
|
|
|
280
282
|
# ─── Atomic install of plugin footprint (DESIGN.md §5.1) ───────────────
|
|
281
283
|
# Runs ensureInstalled which writes antigravity-plugin.json to
|
|
282
|
-
# ~/.gemini/antigravity-cli/plugins/codevibe-antigravity/
|
|
283
|
-
#
|
|
284
|
-
# subsequent runs are no-ops
|
|
284
|
+
# ~/.gemini/antigravity-cli/plugins/codevibe-antigravity/ and removes any
|
|
285
|
+
# stale codevibe-antigravity entry from ~/.gemini/config/mcp_config.json
|
|
286
|
+
# (P42). Idempotent — subsequent runs are no-ops once the manifest hash
|
|
287
|
+
# matches and no stale entry remains.
|
|
285
288
|
log "Running ensureInstalled"
|
|
286
289
|
if ! node "$PLUGIN_DIR/dist/installer-cli.js"; then
|
|
287
290
|
echo "Error: codevibe-antigravity installer failed."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.27",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"codevibe": {
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
"build": "rm -rf dist && npm run typecheck && esbuild src/server.ts src/installer-cli.ts --bundle --platform=node --target=node18 --packages=external --outdir=dist",
|
|
19
19
|
"prepack": "npm run build",
|
|
20
20
|
"postpack": "echo '[codevibe-antigravity-plugin] postpack: noop'",
|
|
21
|
-
"dev": "node dist/server.js",
|
|
22
|
-
"start": "node dist/server.js",
|
|
23
21
|
"test": "jest --config jest.config.js --forceExit"
|
|
24
22
|
},
|
|
25
23
|
"keywords": [
|
|
@@ -48,7 +46,7 @@
|
|
|
48
46
|
"node": ">=22.0.0"
|
|
49
47
|
},
|
|
50
48
|
"dependencies": {
|
|
51
|
-
"@quantiya/codevibe-core": "^2.0.
|
|
49
|
+
"@quantiya/codevibe-core": "^2.0.24",
|
|
52
50
|
"chokidar": "^5.0.0",
|
|
53
51
|
"dotenv": "^16.6.1",
|
|
54
52
|
"express": "^5.1.0",
|