@quantiya/codevibe-antigravity-plugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +28 -0
- package/README.md +112 -0
- package/antigravity-plugin.json +11 -0
- package/bin/codevibe-agy +518 -0
- package/dist/installer-cli.js +478 -0
- package/dist/server.js +3558 -0
- package/package.json +71 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/installer-cli.ts
|
|
31
|
+
var installer_cli_exports = {};
|
|
32
|
+
__export(installer_cli_exports, {
|
|
33
|
+
__testing: () => __testing
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(installer_cli_exports);
|
|
36
|
+
var path2 = __toESM(require("path"));
|
|
37
|
+
|
|
38
|
+
// src/installer.ts
|
|
39
|
+
var fs = __toESM(require("fs"));
|
|
40
|
+
var path = __toESM(require("path"));
|
|
41
|
+
var os = __toESM(require("os"));
|
|
42
|
+
var crypto = __toESM(require("crypto"));
|
|
43
|
+
var MANIFEST_FILENAME = "antigravity-plugin.json";
|
|
44
|
+
var SENTINEL_FILENAME = ".codevibe-manifest-hash";
|
|
45
|
+
var MCP_SERVERS_KEY = "codevibe-antigravity";
|
|
46
|
+
var LOCK_ACQUIRE_TIMEOUT_MS = 5e3;
|
|
47
|
+
var LOCK_RETRY_INTERVAL_MS = 50;
|
|
48
|
+
function resolveInstallerPaths(opts) {
|
|
49
|
+
const home = opts.homedir ?? os.homedir();
|
|
50
|
+
return {
|
|
51
|
+
pluginJsonSourcePath: path.join(opts.pluginPkgDir, MANIFEST_FILENAME),
|
|
52
|
+
installDir: path.join(home, ".gemini", "antigravity-cli", "plugins", "codevibe-antigravity"),
|
|
53
|
+
mcpConfigPath: path.join(home, ".gemini", "config", "mcp_config.json"),
|
|
54
|
+
serverScriptPath: path.join(opts.pluginPkgDir, "dist", "server.js")
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function ensureInstalled(options) {
|
|
58
|
+
const logger = options.logger ?? defaultLogger();
|
|
59
|
+
const {
|
|
60
|
+
pluginJsonSourcePath,
|
|
61
|
+
installDir,
|
|
62
|
+
mcpConfigPath,
|
|
63
|
+
serverScriptPath
|
|
64
|
+
} = options;
|
|
65
|
+
const pid = options.pidOverride ?? process.pid;
|
|
66
|
+
let manifestContent;
|
|
67
|
+
let manifestFd = null;
|
|
68
|
+
try {
|
|
69
|
+
manifestFd = await fs.promises.open(
|
|
70
|
+
pluginJsonSourcePath,
|
|
71
|
+
fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW
|
|
72
|
+
);
|
|
73
|
+
manifestContent = await manifestFd.readFile("utf8");
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const code = err.code;
|
|
76
|
+
if (code === "ELOOP" || code === "EMLINK") {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Refusing to install: ${MANIFEST_FILENAME} source is a symlink (${pluginJsonSourcePath}). The packaged manifest must be a regular file.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
} finally {
|
|
83
|
+
if (manifestFd) {
|
|
84
|
+
try {
|
|
85
|
+
await manifestFd.close();
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const expectedManifestHash = sha256Hex(manifestContent);
|
|
91
|
+
const lockPath = path.join(path.dirname(installDir), ".codevibe-install.lock");
|
|
92
|
+
const releaseLock = await acquireLock(lockPath, LOCK_ACQUIRE_TIMEOUT_MS, logger, "install");
|
|
93
|
+
try {
|
|
94
|
+
await crashRecoverySweep(installDir, logger);
|
|
95
|
+
const currentHash = await tryReadCurrentHash(installDir);
|
|
96
|
+
if (currentHash === expectedManifestHash) {
|
|
97
|
+
await ensureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger);
|
|
98
|
+
return { installed: false, upToDate: true, installDir, manifestHash: currentHash };
|
|
99
|
+
}
|
|
100
|
+
await stageAndInstall({
|
|
101
|
+
installDir,
|
|
102
|
+
pluginJsonSourcePath,
|
|
103
|
+
manifestContent,
|
|
104
|
+
expectedManifestHash,
|
|
105
|
+
pid,
|
|
106
|
+
logger
|
|
107
|
+
});
|
|
108
|
+
await ensureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger);
|
|
109
|
+
logger.info("codevibe-antigravity-plugin installed", {
|
|
110
|
+
installDir,
|
|
111
|
+
manifestHash: expectedManifestHash
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
installed: true,
|
|
115
|
+
upToDate: false,
|
|
116
|
+
installDir,
|
|
117
|
+
manifestHash: expectedManifestHash
|
|
118
|
+
};
|
|
119
|
+
} finally {
|
|
120
|
+
await releaseLock();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function stageAndInstall(args) {
|
|
124
|
+
const { installDir, manifestContent, expectedManifestHash, pid } = args;
|
|
125
|
+
const stagingDir = `${installDir}.new.${pid}`;
|
|
126
|
+
const oldDir = `${installDir}.old.${pid}`;
|
|
127
|
+
await fs.promises.mkdir(path.dirname(installDir), { recursive: true });
|
|
128
|
+
await rmRecursive(stagingDir);
|
|
129
|
+
await fs.promises.mkdir(stagingDir, { recursive: true, mode: 493 });
|
|
130
|
+
const manifestDest = path.join(stagingDir, MANIFEST_FILENAME);
|
|
131
|
+
const manifestTmp = `${manifestDest}.partial`;
|
|
132
|
+
await fs.promises.writeFile(manifestTmp, manifestContent, { mode: 420 });
|
|
133
|
+
await fs.promises.rename(manifestTmp, manifestDest);
|
|
134
|
+
const sentinelDest = path.join(stagingDir, SENTINEL_FILENAME);
|
|
135
|
+
await fs.promises.writeFile(sentinelDest, expectedManifestHash + "\n", { mode: 420 });
|
|
136
|
+
const installExists = await lstatExists(installDir);
|
|
137
|
+
if (installExists) {
|
|
138
|
+
await rmRecursive(oldDir);
|
|
139
|
+
await fs.promises.rename(installDir, oldDir);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
await fs.promises.rename(stagingDir, installDir);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (installExists) {
|
|
145
|
+
try {
|
|
146
|
+
await fs.promises.rename(oldDir, installDir);
|
|
147
|
+
} catch {
|
|
148
|
+
args.logger.error("Installer: failed to restore previous install dir", {
|
|
149
|
+
installDir,
|
|
150
|
+
oldDir,
|
|
151
|
+
error: String(err)
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
if (installExists) {
|
|
158
|
+
await rmRecursive(oldDir);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function crashRecoverySweep(installDir, logger) {
|
|
162
|
+
const parent = path.dirname(installDir);
|
|
163
|
+
const baseName = path.basename(installDir);
|
|
164
|
+
let entries;
|
|
165
|
+
try {
|
|
166
|
+
entries = await fs.promises.readdir(parent, { withFileTypes: true });
|
|
167
|
+
} catch {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
if (!entry.isDirectory()) continue;
|
|
172
|
+
if (!entry.name.startsWith(`${baseName}.new.`)) continue;
|
|
173
|
+
const dir = path.join(parent, entry.name);
|
|
174
|
+
const sentinel = path.join(dir, SENTINEL_FILENAME);
|
|
175
|
+
if (await pathExists(sentinel)) {
|
|
176
|
+
if (!await lstatExists(installDir)) {
|
|
177
|
+
try {
|
|
178
|
+
await fs.promises.rename(dir, installDir);
|
|
179
|
+
logger.info("Crash-recovery: adopted orphan staging dir as install", { adopted: dir });
|
|
180
|
+
} catch (err) {
|
|
181
|
+
logger.warn("Crash-recovery: failed to adopt orphan staging dir", {
|
|
182
|
+
dir,
|
|
183
|
+
error: String(err)
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
logger.info("Crash-recovery: discarding orphan staging dir (current install exists)", { dir });
|
|
189
|
+
await rmRecursive(dir);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
logger.info("Crash-recovery: deleting incomplete staging dir", { dir });
|
|
193
|
+
await rmRecursive(dir);
|
|
194
|
+
}
|
|
195
|
+
for (const entry of entries) {
|
|
196
|
+
if (!entry.isDirectory()) continue;
|
|
197
|
+
if (!entry.name.startsWith(`${baseName}.old.`)) continue;
|
|
198
|
+
const dir = path.join(parent, entry.name);
|
|
199
|
+
logger.info("Crash-recovery: deleting orphan .old dir", { dir });
|
|
200
|
+
await rmRecursive(dir);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function ensureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
204
|
+
try {
|
|
205
|
+
await fs.promises.mkdir(path.dirname(mcpConfigPath), { recursive: true });
|
|
206
|
+
} catch (err) {
|
|
207
|
+
const code = err.code;
|
|
208
|
+
if (code === "ENOTDIR" || code === "EEXIST") {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`mcp_config.json parent path exists as a file, expected a directory: ${path.dirname(mcpConfigPath)}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
const lockPath = `${mcpConfigPath}.lock`;
|
|
216
|
+
const releaseLock = await acquireLock(lockPath, LOCK_ACQUIRE_TIMEOUT_MS, logger, "mcp-config");
|
|
217
|
+
try {
|
|
218
|
+
await doEnsureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger);
|
|
219
|
+
} finally {
|
|
220
|
+
await releaseLock();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function doEnsureMcpConfigEntry(mcpConfigPath, serverScriptPath, logger) {
|
|
224
|
+
let config = {};
|
|
225
|
+
let existingMode = null;
|
|
226
|
+
try {
|
|
227
|
+
const stat = await fs.promises.stat(mcpConfigPath);
|
|
228
|
+
existingMode = stat.mode & 511;
|
|
229
|
+
if (stat.size > 0) {
|
|
230
|
+
const raw = await fs.promises.readFile(mcpConfigPath, "utf8");
|
|
231
|
+
try {
|
|
232
|
+
const parsed = JSON.parse(raw);
|
|
233
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
234
|
+
config = parsed;
|
|
235
|
+
} else {
|
|
236
|
+
logger.warn("mcp_config.json root is not a plain object; overwriting", {
|
|
237
|
+
mcpConfigPath
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
} catch {
|
|
241
|
+
logger.warn("mcp_config.json was malformed JSON; overwriting with valid empty config", {
|
|
242
|
+
mcpConfigPath
|
|
243
|
+
});
|
|
244
|
+
config = {};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (err.code !== "ENOENT") throw err;
|
|
249
|
+
}
|
|
250
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) {
|
|
251
|
+
config.mcpServers = {};
|
|
252
|
+
}
|
|
253
|
+
const expected = {
|
|
254
|
+
command: "node",
|
|
255
|
+
args: [serverScriptPath]
|
|
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;
|
|
260
|
+
}
|
|
261
|
+
const envEqual = !current?.env && !expected.env ? true : JSON.stringify(current?.env ?? null) === JSON.stringify(expected.env ?? null);
|
|
262
|
+
const needsWrite = !current || current.command !== expected.command || !arraysEqual(current.args ?? [], expected.args) || !envEqual;
|
|
263
|
+
if (!needsWrite) return;
|
|
264
|
+
config.mcpServers[MCP_SERVERS_KEY] = expected;
|
|
265
|
+
const targetMode = existingMode ?? 420;
|
|
266
|
+
const randSuffix = `${process.pid}.${Date.now()}.${crypto.randomBytes(6).toString("hex")}`;
|
|
267
|
+
const tmpPath = `${mcpConfigPath}.tmp.${randSuffix}`;
|
|
268
|
+
const tmpFd = await fs.promises.open(tmpPath, "wx", targetMode);
|
|
269
|
+
try {
|
|
270
|
+
await tmpFd.writeFile(JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
271
|
+
await tmpFd.close();
|
|
272
|
+
} catch (writeErr) {
|
|
273
|
+
try {
|
|
274
|
+
await tmpFd.close();
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
await fs.promises.unlink(tmpPath);
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
throw writeErr;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
await fs.promises.rename(tmpPath, mcpConfigPath);
|
|
285
|
+
} catch (err) {
|
|
286
|
+
try {
|
|
287
|
+
await fs.promises.unlink(tmpPath);
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
throw err;
|
|
291
|
+
}
|
|
292
|
+
logger.info("Updated mcp_config.json with codevibe-antigravity entry", {
|
|
293
|
+
mcpConfigPath,
|
|
294
|
+
serverScriptPath
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
function sha256Hex(s) {
|
|
298
|
+
return crypto.createHash("sha256").update(s, "utf8").digest("hex");
|
|
299
|
+
}
|
|
300
|
+
async function tryReadCurrentHash(installDir) {
|
|
301
|
+
try {
|
|
302
|
+
const sentinel = await fs.promises.readFile(path.join(installDir, SENTINEL_FILENAME), "utf8");
|
|
303
|
+
return sentinel.trim();
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function pathExists(p) {
|
|
309
|
+
try {
|
|
310
|
+
await fs.promises.stat(p);
|
|
311
|
+
return true;
|
|
312
|
+
} catch {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async function lstatExists(p) {
|
|
317
|
+
try {
|
|
318
|
+
await fs.promises.lstat(p);
|
|
319
|
+
return true;
|
|
320
|
+
} catch {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function acquireLock(lockPath, timeoutMs, logger, label = "install") {
|
|
325
|
+
await fs.promises.mkdir(path.dirname(lockPath), { recursive: true });
|
|
326
|
+
const myPidLine = `${process.pid}
|
|
327
|
+
`;
|
|
328
|
+
const deadline = Date.now() + timeoutMs;
|
|
329
|
+
while (true) {
|
|
330
|
+
const tmpSuffix = `${process.pid}.${Date.now()}.${crypto.randomBytes(6).toString("hex")}`;
|
|
331
|
+
const tmpPath = `${lockPath}.create.${tmpSuffix}`;
|
|
332
|
+
let tmpWritten = false;
|
|
333
|
+
try {
|
|
334
|
+
await fs.promises.writeFile(tmpPath, myPidLine, { flag: "wx", mode: 384 });
|
|
335
|
+
tmpWritten = true;
|
|
336
|
+
try {
|
|
337
|
+
await fs.promises.link(tmpPath, lockPath);
|
|
338
|
+
} catch (linkErr) {
|
|
339
|
+
const code = linkErr.code;
|
|
340
|
+
if (code !== "EEXIST") {
|
|
341
|
+
throw linkErr;
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
await fs.promises.unlink(tmpPath);
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
tmpWritten = false;
|
|
348
|
+
const holder = await readLockHolder(lockPath);
|
|
349
|
+
const stale = holder === null || !isPidAlive(holder);
|
|
350
|
+
if (stale) {
|
|
351
|
+
const takeoverPath = `${lockPath}.takeover.${process.pid}.${Date.now()}`;
|
|
352
|
+
try {
|
|
353
|
+
await fs.promises.rename(lockPath, takeoverPath);
|
|
354
|
+
try {
|
|
355
|
+
await fs.promises.unlink(takeoverPath);
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
logger.warn(`Stale ${label} lock detected \u2014 taking over`, {
|
|
359
|
+
lockPath,
|
|
360
|
+
deadPid: holder
|
|
361
|
+
});
|
|
362
|
+
continue;
|
|
363
|
+
} catch (renameErr) {
|
|
364
|
+
const rcode = renameErr.code;
|
|
365
|
+
if (rcode !== "ENOENT") {
|
|
366
|
+
logger.warn(`Failed to take over stale ${label} lock; retrying`, {
|
|
367
|
+
lockPath,
|
|
368
|
+
error: String(renameErr)
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (Date.now() >= deadline) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Failed to acquire ${label} lock ${lockPath} within ${timeoutMs}ms (held by pid ${holder ?? "<unknown>"})`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
await new Promise((r) => setTimeout(r, LOCK_RETRY_INTERVAL_MS));
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
await fs.promises.unlink(tmpPath);
|
|
383
|
+
} catch {
|
|
384
|
+
}
|
|
385
|
+
tmpWritten = false;
|
|
386
|
+
return async () => {
|
|
387
|
+
try {
|
|
388
|
+
const currentHolder = await readLockHolder(lockPath);
|
|
389
|
+
if (currentHolder !== process.pid) return;
|
|
390
|
+
await fs.promises.unlink(lockPath);
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
} catch (err) {
|
|
395
|
+
if (tmpWritten) {
|
|
396
|
+
try {
|
|
397
|
+
await fs.promises.unlink(tmpPath);
|
|
398
|
+
} catch {
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
throw err;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function readLockHolder(lockPath) {
|
|
406
|
+
try {
|
|
407
|
+
const raw = await fs.promises.readFile(lockPath, "utf8");
|
|
408
|
+
const pid = parseInt(raw.trim(), 10);
|
|
409
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
410
|
+
} catch {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function isPidAlive(pid) {
|
|
415
|
+
try {
|
|
416
|
+
process.kill(pid, 0);
|
|
417
|
+
return true;
|
|
418
|
+
} catch (err) {
|
|
419
|
+
return err.code === "EPERM";
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function rmRecursive(p) {
|
|
423
|
+
try {
|
|
424
|
+
await fs.promises.rm(p, { recursive: true, force: true });
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
}
|
|
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
|
+
function defaultLogger() {
|
|
434
|
+
return {
|
|
435
|
+
info: () => {
|
|
436
|
+
},
|
|
437
|
+
warn: (msg, meta) => console.warn("[installer]", msg, meta ?? ""),
|
|
438
|
+
error: (msg, meta) => console.error("[installer]", msg, meta ?? "")
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/installer-cli.ts
|
|
443
|
+
async function main() {
|
|
444
|
+
const pkgDir = path2.resolve(__dirname, "..");
|
|
445
|
+
const paths = resolveInstallerPaths({ pluginPkgDir: pkgDir });
|
|
446
|
+
try {
|
|
447
|
+
const result = await ensureInstalled({
|
|
448
|
+
...paths,
|
|
449
|
+
logger: {
|
|
450
|
+
info: (msg, meta) => {
|
|
451
|
+
if (process.env.CODEVIBE_AGY_INSTALLER_VERBOSE === "1") {
|
|
452
|
+
console.log(`[installer] ${msg}`, meta ?? "");
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
warn: (msg, meta) => console.warn(`[installer] WARN: ${msg}`, meta ?? ""),
|
|
456
|
+
error: (msg, meta) => console.error(`[installer] ERROR: ${msg}`, meta ?? "")
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
if (result.installed && process.env.CODEVIBE_AGY_INSTALLER_VERBOSE === "1") {
|
|
460
|
+
console.log(`[installer] Installed fresh manifest hash=${result.manifestHash}`);
|
|
461
|
+
}
|
|
462
|
+
process.exit(0);
|
|
463
|
+
} catch (err) {
|
|
464
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
465
|
+
console.error(`[installer] FATAL: ${msg}`);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (require.main === module) {
|
|
470
|
+
void main();
|
|
471
|
+
}
|
|
472
|
+
var __testing = {
|
|
473
|
+
main
|
|
474
|
+
};
|
|
475
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
476
|
+
0 && (module.exports = {
|
|
477
|
+
__testing
|
|
478
|
+
});
|