paseo-acp-agy 1.1.8 → 1.1.9
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/antigravity-process.d.ts +4 -1
- package/dist/antigravity-process.js +112 -45
- package/dist/index.js +163 -1
- package/dist/paseo-patcher.d.ts +13 -0
- package/dist/paseo-patcher.js +270 -48
- package/dist/protocol.d.ts +6 -1
- package/dist/protocol.js +17 -7
- package/dist/slash-commands.js +4 -3
- package/package.json +1 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { AgyInitEvent, AgyStepUpdateEvent, AgyResultEvent } from "./protocol.js";
|
|
3
3
|
import { PermissionSettings } from "./permissions.js";
|
|
4
|
-
export declare
|
|
4
|
+
export declare const BINARY_RESOLVE_TTL_MS: number;
|
|
5
|
+
export declare function clearBinaryResolutionCache(): void;
|
|
6
|
+
export declare function isWindowsBatchScript(filePath: string): boolean;
|
|
7
|
+
export declare function resolveDefaultAgyBinary(force?: boolean): string;
|
|
5
8
|
export interface AntigravityProcessOptions {
|
|
6
9
|
binaryPath?: string;
|
|
7
10
|
cwd?: string;
|
|
@@ -6,54 +6,120 @@ import path from "node:path";
|
|
|
6
6
|
import { logger } from "./logger.js";
|
|
7
7
|
import { getEffectiveEffortForModel, } from "./protocol.js";
|
|
8
8
|
import { buildAgyArgs, resolvePermissionSettings } from "./permissions.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
9
|
+
let cachedBinaryPath = null;
|
|
10
|
+
let lastBinaryResolveTime = 0;
|
|
11
|
+
export const BINARY_RESOLVE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours TTL
|
|
12
|
+
export function clearBinaryResolutionCache() {
|
|
13
|
+
cachedBinaryPath = null;
|
|
14
|
+
lastBinaryResolveTime = 0;
|
|
15
|
+
}
|
|
16
|
+
export function isWindowsBatchScript(filePath) {
|
|
17
|
+
return /\.(cmd|bat)$/i.test(filePath);
|
|
18
|
+
}
|
|
19
|
+
export function resolveDefaultAgyBinary(force = false) {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
if (!force &&
|
|
22
|
+
cachedBinaryPath &&
|
|
23
|
+
now - lastBinaryResolveTime < BINARY_RESOLVE_TTL_MS &&
|
|
24
|
+
(cachedBinaryPath === "agy" || fs.existsSync(cachedBinaryPath))) {
|
|
25
|
+
return cachedBinaryPath;
|
|
26
|
+
}
|
|
27
|
+
let resolved = "agy";
|
|
28
|
+
if (process.env.AGY_BIN_PATH) {
|
|
29
|
+
resolved = process.env.AGY_BIN_PATH;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const home = os.homedir();
|
|
33
|
+
if (home) {
|
|
34
|
+
if (process.platform === "win32") {
|
|
35
|
+
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
36
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
37
|
+
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
38
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
39
|
+
const exeCandidates = [
|
|
40
|
+
path.join(localAppData, "Programs", "Antigravity", "bin", "agy.exe"),
|
|
41
|
+
path.join(localAppData, "Programs", "antigravity", "agy.exe"),
|
|
42
|
+
path.join(localAppData, "Programs", "Antigravity", "agy.exe"),
|
|
43
|
+
path.join(programFiles, "Antigravity", "bin", "agy.exe"),
|
|
44
|
+
path.join(programFilesX86, "Antigravity", "bin", "agy.exe"),
|
|
45
|
+
path.join(localAppData, "Microsoft", "WindowsApps", "agy.exe"),
|
|
46
|
+
path.join(home, ".local", "bin", "agy.exe"),
|
|
47
|
+
path.join(appData, "npm", "agy.exe"),
|
|
48
|
+
path.join(localAppData, "npm", "agy.exe"),
|
|
49
|
+
];
|
|
50
|
+
for (const cand of exeCandidates) {
|
|
51
|
+
if (fs.existsSync(cand)) {
|
|
52
|
+
resolved = cand;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (resolved === "agy") {
|
|
57
|
+
for (const target of ["agy", "agy.exe"]) {
|
|
58
|
+
try {
|
|
59
|
+
const out = execFileSync("where.exe", [target], {
|
|
60
|
+
encoding: "utf-8",
|
|
61
|
+
timeout: 2000,
|
|
62
|
+
windowsHide: true,
|
|
63
|
+
}).trim();
|
|
64
|
+
const lines = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
if (/\.exe$/i.test(line) && fs.existsSync(line)) {
|
|
67
|
+
resolved = line;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (resolved !== "agy")
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (resolved === "agy") {
|
|
78
|
+
const batchCandidates = [
|
|
79
|
+
path.join(appData, "npm", "agy.cmd"),
|
|
80
|
+
path.join(localAppData, "npm", "agy.cmd"),
|
|
81
|
+
path.join(home, ".local", "bin", "agy.cmd"),
|
|
82
|
+
path.join(appData, "npm", "agy.bat"),
|
|
83
|
+
];
|
|
84
|
+
for (const cand of batchCandidates) {
|
|
85
|
+
if (fs.existsSync(cand)) {
|
|
86
|
+
resolved = cand;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (resolved === "agy") {
|
|
91
|
+
for (const target of ["agy", "agy.cmd", "agy.bat"]) {
|
|
92
|
+
try {
|
|
93
|
+
const out = execFileSync("where.exe", [target], {
|
|
94
|
+
encoding: "utf-8",
|
|
95
|
+
timeout: 2000,
|
|
96
|
+
windowsHide: true,
|
|
97
|
+
}).trim();
|
|
98
|
+
const lines = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
if (isWindowsBatchScript(line) && fs.existsSync(line)) {
|
|
101
|
+
resolved = line;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (resolved !== "agy")
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
37
112
|
}
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
windowsHide: true,
|
|
43
|
-
}).trim();
|
|
44
|
-
const first = out.split(/\r?\n/)[0]?.trim();
|
|
45
|
-
if (first && fs.existsSync(first))
|
|
46
|
-
return first;
|
|
113
|
+
else {
|
|
114
|
+
const localPath = path.join(home, ".local", "bin", "agy");
|
|
115
|
+
if (fs.existsSync(localPath))
|
|
116
|
+
resolved = localPath;
|
|
47
117
|
}
|
|
48
|
-
catch { }
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const localPath = path.join(home, ".local", "bin", "agy");
|
|
52
|
-
if (fs.existsSync(localPath))
|
|
53
|
-
return localPath;
|
|
54
118
|
}
|
|
55
119
|
}
|
|
56
|
-
|
|
120
|
+
cachedBinaryPath = resolved;
|
|
121
|
+
lastBinaryResolveTime = now;
|
|
122
|
+
return resolved;
|
|
57
123
|
}
|
|
58
124
|
export class AntigravityProcess extends EventEmitter {
|
|
59
125
|
child = null;
|
|
@@ -282,12 +348,13 @@ export class AntigravityProcess extends EventEmitter {
|
|
|
282
348
|
effectiveEffort,
|
|
283
349
|
});
|
|
284
350
|
const isWin = process.platform === "win32";
|
|
351
|
+
const isBatch = isWin && isWindowsBatchScript(this.binaryPath);
|
|
285
352
|
const child = spawn(this.binaryPath, args, {
|
|
286
353
|
cwd: this.cwd,
|
|
287
354
|
env: this.env,
|
|
288
355
|
stdio: ["pipe", "pipe", "pipe"],
|
|
289
356
|
detached: !isWin,
|
|
290
|
-
shell:
|
|
357
|
+
shell: isBatch,
|
|
291
358
|
windowsHide: true,
|
|
292
359
|
});
|
|
293
360
|
this.child = child;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
2
4
|
import { ACPServer } from "./acp-server.js";
|
|
3
5
|
import { logger } from "./logger.js";
|
|
4
6
|
import { formatDiagnosticVersion, resolveBuildMetadata } from "./version.js";
|
|
5
|
-
import { ensurePaseoIntegration } from "./paseo-patcher.js";
|
|
7
|
+
import { ensurePaseoIntegration, findPaseoServerInstallations, findPaseoAsarPaths, isPaseoServerPatched, isPaseoAsarPatched, isPaseoRunning, } from "./paseo-patcher.js";
|
|
8
|
+
import { resolveDefaultAgyBinary, isWindowsBatchScript } from "./antigravity-process.js";
|
|
9
|
+
import { parseAgyQuotaOutput } from "./protocol.js";
|
|
6
10
|
const args = process.argv.slice(2);
|
|
7
11
|
if (args.includes("--version") || args.includes("-v")) {
|
|
8
12
|
if (args.includes("--json")) {
|
|
@@ -21,13 +25,16 @@ Usage:
|
|
|
21
25
|
agy-acp [options]
|
|
22
26
|
npx -y paseo-acp-agy [options]
|
|
23
27
|
npx -y paseo-acp-agy setup
|
|
28
|
+
npx -y paseo-acp-agy doctor
|
|
24
29
|
|
|
25
30
|
Commands:
|
|
26
31
|
setup, patch Configure and integrate Antigravity telemetry with Paseo
|
|
32
|
+
doctor Diagnose Antigravity binary, quota provider, and Paseo status
|
|
27
33
|
|
|
28
34
|
Options:
|
|
29
35
|
--acp Start ACP server over stdio (default)
|
|
30
36
|
--setup Integrate Antigravity with local Paseo server installation
|
|
37
|
+
--doctor Run environment, binary, and telemetry diagnostics
|
|
31
38
|
-v, --version Show version
|
|
32
39
|
--json Show version in JSON format (with --version)
|
|
33
40
|
-h, --help Show help
|
|
@@ -39,13 +46,168 @@ Environment Variables:
|
|
|
39
46
|
AGY_ACP_DANGEROUSLY_SKIP_PERMISSIONS Set to 'true' to auto-approve tool permissions
|
|
40
47
|
AGY_BIN_PATH Path to agy binary (default: agy in PATH or ~/.local/bin/agy)
|
|
41
48
|
PASEO_SERVER_PATH Path to local @getpaseo/server directory
|
|
49
|
+
PASEO_ASAR_PATH Path to local Paseo app.asar package
|
|
42
50
|
`);
|
|
43
51
|
process.exit(0);
|
|
44
52
|
}
|
|
53
|
+
if (args.includes("doctor") || args.includes("--doctor")) {
|
|
54
|
+
process.stdout.write("=== Paseo & Antigravity Doctor ===\n");
|
|
55
|
+
process.stdout.write(`Operating System: ${process.platform} (${process.arch})\n`);
|
|
56
|
+
process.stdout.write(`Node.js Version: ${process.version}\n`);
|
|
57
|
+
process.stdout.write(`Paseo ACP Adapter: ${formatDiagnosticVersion()}\n\n`);
|
|
58
|
+
process.stdout.write("1. Antigravity Binary Resolution:\n");
|
|
59
|
+
const binPath = resolveDefaultAgyBinary(true);
|
|
60
|
+
const exists = fs.existsSync(binPath);
|
|
61
|
+
const isWin = process.platform === "win32";
|
|
62
|
+
let execType = "Executable";
|
|
63
|
+
if (isWin) {
|
|
64
|
+
if (/\.exe$/i.test(binPath))
|
|
65
|
+
execType = "Native Windows Executable (.exe)";
|
|
66
|
+
else if (/\.cmd$/i.test(binPath))
|
|
67
|
+
execType = "Windows Command Script (.cmd)";
|
|
68
|
+
else if (/\.bat$/i.test(binPath))
|
|
69
|
+
execType = "Windows Batch Script (.bat)";
|
|
70
|
+
else
|
|
71
|
+
execType = "CLI command in PATH";
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
execType = "POSIX binary";
|
|
75
|
+
}
|
|
76
|
+
if (exists) {
|
|
77
|
+
process.stdout.write(` [OK] Binary found: ${binPath} (${execType})\n`);
|
|
78
|
+
}
|
|
79
|
+
else if (binPath === "agy") {
|
|
80
|
+
process.stdout.write(` [?] Binary fallback: 'agy' via system PATH (${execType})\n`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
process.stdout.write(` [FAIL] Binary not found at: ${binPath}\n`);
|
|
84
|
+
}
|
|
85
|
+
process.stdout.write("\n2. CLI Telemetry & Runtime Probes:\n");
|
|
86
|
+
const isBatch = isWin && isWindowsBatchScript(binPath);
|
|
87
|
+
let modelsSuccess = false;
|
|
88
|
+
let modelsCount = 0;
|
|
89
|
+
const t0 = Date.now();
|
|
90
|
+
try {
|
|
91
|
+
const modelsOut = execFileSync(binPath, ["models"], {
|
|
92
|
+
encoding: "utf-8",
|
|
93
|
+
timeout: 15000,
|
|
94
|
+
windowsHide: true,
|
|
95
|
+
shell: isBatch,
|
|
96
|
+
});
|
|
97
|
+
const latency = Date.now() - t0;
|
|
98
|
+
const modelLines = modelsOut
|
|
99
|
+
.trim()
|
|
100
|
+
.split(/\r?\n/)
|
|
101
|
+
.map((l) => l.trim())
|
|
102
|
+
.filter((l) => Boolean(l) && !l.toLowerCase().startsWith("models:"));
|
|
103
|
+
modelsCount = modelLines.length;
|
|
104
|
+
modelsSuccess = true;
|
|
105
|
+
process.stdout.write(` [OK] 'agy models' probe succeeded in ${latency}ms (${modelsCount} models detected)\n`);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const latency = Date.now() - t0;
|
|
109
|
+
process.stdout.write(` [WARN] 'agy models' probe failed in ${latency}ms: ${err?.message || String(err)}\n`);
|
|
110
|
+
}
|
|
111
|
+
let quotaSuccess = false;
|
|
112
|
+
let quotaCount = 0;
|
|
113
|
+
const t1 = Date.now();
|
|
114
|
+
try {
|
|
115
|
+
const usageOut = execFileSync(binPath, ["--print-timeout", "24h", "--print", "/usage"], {
|
|
116
|
+
encoding: "utf-8",
|
|
117
|
+
timeout: 15000,
|
|
118
|
+
windowsHide: true,
|
|
119
|
+
shell: isBatch,
|
|
120
|
+
});
|
|
121
|
+
const latency = Date.now() - t1;
|
|
122
|
+
const normalized = usageOut.replace(/\r\n/g, "\n");
|
|
123
|
+
const windows = parseAgyQuotaOutput(normalized);
|
|
124
|
+
quotaCount = windows.length;
|
|
125
|
+
quotaSuccess = true;
|
|
126
|
+
process.stdout.write(` [OK] 'agy --print /usage' probe succeeded in ${latency}ms (${quotaCount} quota limit item(s) parsed)\n`);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
const latency = Date.now() - t1;
|
|
130
|
+
process.stdout.write(` [WARN] 'agy --print /usage' probe failed in ${latency}ms: ${err?.message || String(err)}\n`);
|
|
131
|
+
}
|
|
132
|
+
process.stdout.write("\n3. Paseo Installation Targets:\n");
|
|
133
|
+
const serverPaths = findPaseoServerInstallations();
|
|
134
|
+
const asarPaths = findPaseoAsarPaths();
|
|
135
|
+
const totalFound = serverPaths.length + asarPaths.length;
|
|
136
|
+
if (totalFound === 0) {
|
|
137
|
+
process.stdout.write(" [!] No Paseo server or desktop installations discovered in standard paths.\n");
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
for (const sPath of serverPaths) {
|
|
141
|
+
const patched = isPaseoServerPatched(sPath);
|
|
142
|
+
process.stdout.write(` - [Server] ${sPath} -> ${patched ? "[PATCHED]" : "[UNPATCHED]"}\n`);
|
|
143
|
+
}
|
|
144
|
+
for (const aPath of asarPaths) {
|
|
145
|
+
const patched = await isPaseoAsarPatched(aPath);
|
|
146
|
+
process.stdout.write(` - [ASAR] ${aPath} -> ${patched ? "[PATCHED]" : "[UNPATCHED]"}\n`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
process.stdout.write("\n4. Paseo Process Status:\n");
|
|
150
|
+
const paseoRunning = isPaseoRunning();
|
|
151
|
+
if (paseoRunning) {
|
|
152
|
+
process.stdout.write(" [!] Paseo process is currently running.\n");
|
|
153
|
+
process.stdout.write(" Note: If you run setup/patch, please close Paseo completely (check system tray and Task Manager) to avoid EBUSY file locking.\n");
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
process.stdout.write(" [OK] Paseo is not currently running (safe to patch/update).\n");
|
|
157
|
+
}
|
|
158
|
+
process.stdout.write("\n=== Doctor Recommendations ===\n");
|
|
159
|
+
let unpatchedCount = 0;
|
|
160
|
+
for (const sPath of serverPaths) {
|
|
161
|
+
if (!isPaseoServerPatched(sPath))
|
|
162
|
+
unpatchedCount++;
|
|
163
|
+
}
|
|
164
|
+
for (const aPath of asarPaths) {
|
|
165
|
+
if (!(await isPaseoAsarPatched(aPath)))
|
|
166
|
+
unpatchedCount++;
|
|
167
|
+
}
|
|
168
|
+
let hasIssue = false;
|
|
169
|
+
if (!exists && binPath !== "agy") {
|
|
170
|
+
hasIssue = true;
|
|
171
|
+
process.stdout.write(" - Antigravity CLI ('agy') executable was not found.\n");
|
|
172
|
+
process.stdout.write(" Action: Install Antigravity CLI or set AGY_BIN_PATH environment variable.\n");
|
|
173
|
+
}
|
|
174
|
+
if (totalFound === 0) {
|
|
175
|
+
hasIssue = true;
|
|
176
|
+
process.stdout.write(" - No Paseo installations found.\n");
|
|
177
|
+
process.stdout.write(" Action: Set PASEO_SERVER_PATH or PASEO_ASAR_PATH to your Paseo install directory if located elsewhere.\n");
|
|
178
|
+
}
|
|
179
|
+
else if (unpatchedCount > 0) {
|
|
180
|
+
hasIssue = true;
|
|
181
|
+
if (paseoRunning) {
|
|
182
|
+
process.stdout.write(` - Found ${unpatchedCount} unpatched Paseo target(s), but Paseo is currently running.\n`);
|
|
183
|
+
process.stdout.write(" Action: Close Paseo.exe completely, then run:\n");
|
|
184
|
+
process.stdout.write(" npx -y paseo-acp-agy setup\n");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
process.stdout.write(` - Found ${unpatchedCount} unpatched Paseo target(s).\n`);
|
|
188
|
+
process.stdout.write(" Action: Run setup to patch Paseo for Antigravity telemetry:\n");
|
|
189
|
+
process.stdout.write(" npx -y paseo-acp-agy setup\n");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!modelsSuccess || !quotaSuccess) {
|
|
193
|
+
if (exists || binPath === "agy") {
|
|
194
|
+
hasIssue = true;
|
|
195
|
+
process.stdout.write(" - Antigravity CLI telemetry probes experienced errors.\n");
|
|
196
|
+
process.stdout.write(" Action: Ensure you are logged in to Antigravity CLI (try running 'agy auth' or 'agy /status').\n");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (!hasIssue) {
|
|
200
|
+
process.stdout.write(" [ALL OK] Everything is properly configured! Antigravity quota and telemetry are ready to use in Paseo.\n");
|
|
201
|
+
}
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
45
204
|
if (args.includes("setup") ||
|
|
46
205
|
args.includes("patch") ||
|
|
47
206
|
args.includes("--setup") ||
|
|
48
207
|
args.includes("--patch")) {
|
|
208
|
+
if (isPaseoRunning()) {
|
|
209
|
+
process.stdout.write("Notice: Paseo appears to be running. If setup fails with EBUSY, please close Paseo completely (from system tray / task manager) and re-run setup.\n\n");
|
|
210
|
+
}
|
|
49
211
|
process.stdout.write("Checking Paseo installation and configuring Antigravity telemetry...\n");
|
|
50
212
|
try {
|
|
51
213
|
const res = await ensurePaseoIntegration({ verbose: true });
|
package/dist/paseo-patcher.d.ts
CHANGED
|
@@ -49,3 +49,16 @@ export declare function ensurePaseoIntegration(options?: {
|
|
|
49
49
|
targetPaths?: string[];
|
|
50
50
|
targetAsarPaths?: string[];
|
|
51
51
|
}): Promise<PatchResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Checks whether a @getpaseo/server installation directory is already patched
|
|
54
|
+
* for Antigravity quota and telemetry support.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isPaseoServerPatched(serverDir: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Checks whether a Paseo app.asar archive is already patched with Antigravity telemetry.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isPaseoAsarPatched(asarPath: string): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Detects if Paseo Desktop is currently running on the host.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isPaseoRunning(): boolean;
|
package/dist/paseo-patcher.js
CHANGED
|
@@ -92,6 +92,8 @@ export function findPaseoServerInstallations() {
|
|
|
92
92
|
path.join(path.dirname(paseoDir), "node_modules", "@getpaseo", "server"),
|
|
93
93
|
path.join(paseoDir, "resources", "app.asar.unpacked", "node_modules", "@getpaseo", "server"),
|
|
94
94
|
path.join(paseoDir, "resources", "app", "node_modules", "@getpaseo", "server"),
|
|
95
|
+
path.join(path.dirname(paseoDir), "resources", "app.asar.unpacked", "node_modules", "@getpaseo", "server"),
|
|
96
|
+
path.join(path.dirname(paseoDir), "resources", "app", "node_modules", "@getpaseo", "server"),
|
|
95
97
|
];
|
|
96
98
|
for (const c of checks) {
|
|
97
99
|
if (fs.existsSync(c))
|
|
@@ -179,8 +181,18 @@ import { toneFromUsedPct, windowFromUsedPct, unavailableUsage } from "../usage.j
|
|
|
179
181
|
|
|
180
182
|
const execFileAsync = promisify(execFile);
|
|
181
183
|
|
|
184
|
+
let cachedAgyBin = null;
|
|
185
|
+
let cachedAgyBinTime = 0;
|
|
186
|
+
const BIN_CACHE_TTL_MS = 86400000; // 24 hours
|
|
187
|
+
|
|
182
188
|
function resolveAgyBinary() {
|
|
183
189
|
if (process.env.AGY_BIN_PATH) return process.env.AGY_BIN_PATH;
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
if (cachedAgyBin && (now - cachedAgyBinTime < BIN_CACHE_TTL_MS)) {
|
|
192
|
+
return cachedAgyBin;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let resolved = "agy";
|
|
184
196
|
const home = os.homedir();
|
|
185
197
|
if (home) {
|
|
186
198
|
if (process.platform === "win32") {
|
|
@@ -189,35 +201,80 @@ function resolveAgyBinary() {
|
|
|
189
201
|
const programFiles = process.env.ProgramFiles || "C:\\\\Program Files";
|
|
190
202
|
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\\\Program Files (x86)";
|
|
191
203
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
path.join(home, ".local", "bin", "agy.cmd"),
|
|
195
|
-
path.join(home, ".local", "bin", "agy.bat"),
|
|
196
|
-
path.join(appData, "npm", "agy.cmd"),
|
|
197
|
-
path.join(appData, "npm", "agy.exe"),
|
|
198
|
-
path.join(appData, "npm", "agy.bat"),
|
|
199
|
-
path.join(localAppData, "npm", "agy.cmd"),
|
|
200
|
-
path.join(localAppData, "npm", "agy.exe"),
|
|
201
|
-
path.join(localAppData, "Programs", "antigravity", "agy.exe"),
|
|
204
|
+
// Prioritize .exe candidates over .cmd / .bat
|
|
205
|
+
const exeCandidates = [
|
|
202
206
|
path.join(localAppData, "Programs", "Antigravity", "bin", "agy.exe"),
|
|
203
|
-
path.join(localAppData, "
|
|
207
|
+
path.join(localAppData, "Programs", "antigravity", "agy.exe"),
|
|
208
|
+
path.join(localAppData, "Programs", "Antigravity", "agy.exe"),
|
|
204
209
|
path.join(programFiles, "Antigravity", "bin", "agy.exe"),
|
|
205
210
|
path.join(programFilesX86, "Antigravity", "bin", "agy.exe"),
|
|
211
|
+
path.join(localAppData, "Microsoft", "WindowsApps", "agy.exe"),
|
|
212
|
+
path.join(home, ".local", "bin", "agy.exe"),
|
|
213
|
+
path.join(appData, "npm", "agy.exe"),
|
|
214
|
+
path.join(localAppData, "npm", "agy.exe"),
|
|
206
215
|
];
|
|
207
|
-
for (const cand of
|
|
208
|
-
if (fs.existsSync(cand))
|
|
216
|
+
for (const cand of exeCandidates) {
|
|
217
|
+
if (fs.existsSync(cand)) {
|
|
218
|
+
resolved = cand;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check where.exe agy and select first .exe
|
|
224
|
+
if (resolved === "agy") {
|
|
225
|
+
for (const target of ["agy", "agy.exe"]) {
|
|
226
|
+
try {
|
|
227
|
+
const out = execFileSync("where.exe", [target], { encoding: "utf-8", timeout: 2000, windowsHide: true }).trim();
|
|
228
|
+
const lines = out.split(/\\r?\\n/).map(l => l.trim()).filter(Boolean);
|
|
229
|
+
const firstExe = lines.find(l => /\\.exe$/i.test(l) && fs.existsSync(l));
|
|
230
|
+
if (firstExe) {
|
|
231
|
+
resolved = firstExe;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
} catch {}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Fallback to batch scripts (.cmd / .bat) if no .exe found
|
|
239
|
+
if (resolved === "agy") {
|
|
240
|
+
const batchCandidates = [
|
|
241
|
+
path.join(appData, "npm", "agy.cmd"),
|
|
242
|
+
path.join(localAppData, "npm", "agy.cmd"),
|
|
243
|
+
path.join(home, ".local", "bin", "agy.cmd"),
|
|
244
|
+
path.join(appData, "npm", "agy.bat"),
|
|
245
|
+
path.join(localAppData, "npm", "agy.bat"),
|
|
246
|
+
path.join(home, ".local", "bin", "agy.bat"),
|
|
247
|
+
];
|
|
248
|
+
for (const cand of batchCandidates) {
|
|
249
|
+
if (fs.existsSync(cand)) {
|
|
250
|
+
resolved = cand;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (resolved === "agy") {
|
|
257
|
+
for (const target of ["agy", "agy.cmd", "agy.bat"]) {
|
|
258
|
+
try {
|
|
259
|
+
const out = execFileSync("where.exe", [target], { encoding: "utf-8", timeout: 2000, windowsHide: true }).trim();
|
|
260
|
+
const lines = out.split(/\\r?\\n/).map(l => l.trim()).filter(Boolean);
|
|
261
|
+
const firstAny = lines.find(l => /\\.(cmd|bat)$/i.test(l) && fs.existsSync(l));
|
|
262
|
+
if (firstAny) {
|
|
263
|
+
resolved = firstAny;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
} catch {}
|
|
267
|
+
}
|
|
209
268
|
}
|
|
210
|
-
try {
|
|
211
|
-
const out = execFileSync("where.exe", ["agy"], { encoding: "utf-8", timeout: 2000, windowsHide: true }).trim();
|
|
212
|
-
const first = out.split(/\\r?\\n/)[0]?.trim();
|
|
213
|
-
if (first && fs.existsSync(first)) return first;
|
|
214
|
-
} catch {}
|
|
215
269
|
} else {
|
|
216
270
|
const localPath = path.join(home, ".local", "bin", "agy");
|
|
217
|
-
if (fs.existsSync(localPath))
|
|
271
|
+
if (fs.existsSync(localPath)) resolved = localPath;
|
|
218
272
|
}
|
|
219
273
|
}
|
|
220
|
-
|
|
274
|
+
|
|
275
|
+
cachedAgyBin = resolved;
|
|
276
|
+
cachedAgyBinTime = now;
|
|
277
|
+
return resolved;
|
|
221
278
|
}
|
|
222
279
|
|
|
223
280
|
export class AntigravityQuotaProvider {
|
|
@@ -231,20 +288,22 @@ export class AntigravityQuotaProvider {
|
|
|
231
288
|
async fetchUsage() {
|
|
232
289
|
try {
|
|
233
290
|
const isWin = process.platform === "win32";
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
291
|
+
const bin = resolveAgyBinary();
|
|
292
|
+
this.binaryPath = bin;
|
|
293
|
+
const isBatch = isWin && /\\.(cmd|bat)$/i.test(bin);
|
|
238
294
|
const [usageRes, creditsRes] = await Promise.allSettled([
|
|
239
|
-
execFileAsync(bin, ["--print-timeout", "24h", "--print", "/usage"], { timeout: 15000, env: process.env, shell:
|
|
240
|
-
execFileAsync(bin, ["--print-timeout", "24h", "--print", "/credits"], { timeout: 15000, env: process.env, shell:
|
|
295
|
+
execFileAsync(bin, ["--print-timeout", "24h", "--print", "/usage"], { timeout: 15000, env: process.env, shell: isBatch, windowsHide: true }),
|
|
296
|
+
execFileAsync(bin, ["--print-timeout", "24h", "--print", "/credits"], { timeout: 15000, env: process.env, shell: isBatch, windowsHide: true }),
|
|
241
297
|
]);
|
|
242
298
|
|
|
243
|
-
const
|
|
244
|
-
const
|
|
299
|
+
const rawUsageOut = usageRes.status === "fulfilled" ? usageRes.value.stdout || usageRes.value.stderr : "";
|
|
300
|
+
const rawCreditsOut = creditsRes.status === "fulfilled" ? creditsRes.value.stdout || creditsRes.value.stderr : "";
|
|
301
|
+
|
|
302
|
+
const usageOut = (rawUsageOut || "").replace(/\\r\\n/g, "\\n");
|
|
303
|
+
const creditsOut = (rawCreditsOut || "").replace(/\\r\\n/g, "\\n");
|
|
245
304
|
|
|
246
305
|
const rawWindows = [];
|
|
247
|
-
for (const line of usageOut.split(
|
|
306
|
+
for (const line of usageOut.split("\\n")) {
|
|
248
307
|
const trimmed = line.trim();
|
|
249
308
|
if (!trimmed || trimmed.toLowerCase().startsWith("quota:")) continue;
|
|
250
309
|
|
|
@@ -362,7 +421,7 @@ export function patchPaseoServer(serverDir) {
|
|
|
362
421
|
if (found)
|
|
363
422
|
return found;
|
|
364
423
|
}
|
|
365
|
-
else if (e.isFile() && e.name === "manifest.js" && dir.includes("quota-fetcher")) {
|
|
424
|
+
else if (e.isFile() && e.name === "manifest.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
366
425
|
return full;
|
|
367
426
|
}
|
|
368
427
|
}
|
|
@@ -389,9 +448,9 @@ export function patchPaseoServer(serverDir) {
|
|
|
389
448
|
}
|
|
390
449
|
if (!manifestCode.includes('providerId: "antigravity"')) {
|
|
391
450
|
const entryToAdd = ` {\n providerId: "antigravity",\n create: (options) => new AntigravityQuotaProvider({\n logger: options.logger,\n fetch: options.fetch,\n }),\n },\n`;
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
394
|
-
manifestCode = manifestCode.replace(
|
|
451
|
+
const fetcherArrayRegex = /export\s+const\s+PROVIDER_USAGE_FETCHERS\s*=\s*\[/;
|
|
452
|
+
if (fetcherArrayRegex.test(manifestCode)) {
|
|
453
|
+
manifestCode = manifestCode.replace(fetcherArrayRegex, (match) => `${match}\n${entryToAdd}`);
|
|
395
454
|
manifestModified = true;
|
|
396
455
|
}
|
|
397
456
|
}
|
|
@@ -448,11 +507,44 @@ export function patchPaseoServer(serverDir) {
|
|
|
448
507
|
acpCode = acpCode.replace(oldMapRegex, newMap);
|
|
449
508
|
acpModified = true;
|
|
450
509
|
}
|
|
510
|
+
else {
|
|
511
|
+
const mapMatch = acpCode.match(/export\s+function\s+mapACPUsage\s*\([^)]*\)\s*\{/);
|
|
512
|
+
if (mapMatch && mapMatch.index !== undefined) {
|
|
513
|
+
const mapStart = mapMatch.index;
|
|
514
|
+
const openBrace = acpCode.indexOf("{", mapStart);
|
|
515
|
+
let depth = 1;
|
|
516
|
+
let i = openBrace + 1;
|
|
517
|
+
while (i < acpCode.length && depth > 0) {
|
|
518
|
+
if (acpCode[i] === "{")
|
|
519
|
+
depth++;
|
|
520
|
+
else if (acpCode[i] === "}")
|
|
521
|
+
depth--;
|
|
522
|
+
i++;
|
|
523
|
+
}
|
|
524
|
+
if (depth === 0) {
|
|
525
|
+
acpCode = acpCode.slice(0, mapStart) + newMap + acpCode.slice(i);
|
|
526
|
+
acpModified = true;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
451
530
|
}
|
|
452
531
|
// Patch handleUsageUpdate
|
|
453
|
-
if (acpCode.includes("handleUsageUpdate
|
|
454
|
-
const
|
|
455
|
-
|
|
532
|
+
if (acpCode.includes("handleUsageUpdate") && (!acpCode.includes("this.deliverTranslatedEvents") || acpCode.includes("this.notifySubscribers"))) {
|
|
533
|
+
const startIdx = acpCode.search(/\bhandleUsageUpdate\s*\(/);
|
|
534
|
+
if (startIdx !== -1) {
|
|
535
|
+
const openBrace = acpCode.indexOf("{", startIdx);
|
|
536
|
+
if (openBrace !== -1) {
|
|
537
|
+
let depth = 1;
|
|
538
|
+
let i = openBrace + 1;
|
|
539
|
+
while (i < acpCode.length && depth > 0) {
|
|
540
|
+
if (acpCode[i] === "{")
|
|
541
|
+
depth++;
|
|
542
|
+
else if (acpCode[i] === "}")
|
|
543
|
+
depth--;
|
|
544
|
+
i++;
|
|
545
|
+
}
|
|
546
|
+
if (depth === 0) {
|
|
547
|
+
const newHandler = `handleUsageUpdate(update) {
|
|
456
548
|
if (!update) return;
|
|
457
549
|
const usage = mapACPUsage(update);
|
|
458
550
|
if (usage) {
|
|
@@ -470,9 +562,10 @@ export function patchPaseoServer(serverDir) {
|
|
|
470
562
|
}
|
|
471
563
|
}
|
|
472
564
|
}`;
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
565
|
+
acpCode = acpCode.slice(0, startIdx) + newHandler + acpCode.slice(i);
|
|
566
|
+
acpModified = true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
476
569
|
}
|
|
477
570
|
}
|
|
478
571
|
if (acpModified) {
|
|
@@ -505,9 +598,12 @@ export function findPaseoAsarPaths() {
|
|
|
505
598
|
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
506
599
|
const winAsarLocations = [
|
|
507
600
|
path.join(localAppData, "Programs", "Paseo", "resources", "app.asar"),
|
|
601
|
+
path.join(localAppData, "Programs", "paseo", "resources", "app.asar"),
|
|
508
602
|
path.join(localAppData, "Paseo", "resources", "app.asar"),
|
|
509
603
|
path.join(programFiles, "Paseo", "resources", "app.asar"),
|
|
604
|
+
path.join(programFiles, "paseo", "resources", "app.asar"),
|
|
510
605
|
path.join(programFilesX86, "Paseo", "resources", "app.asar"),
|
|
606
|
+
path.join(programFilesX86, "paseo", "resources", "app.asar"),
|
|
511
607
|
path.join(appData, "Paseo", "resources", "app.asar"),
|
|
512
608
|
];
|
|
513
609
|
for (const loc of winAsarLocations) {
|
|
@@ -521,9 +617,13 @@ export function findPaseoAsarPaths() {
|
|
|
521
617
|
windowsHide: true,
|
|
522
618
|
}).trim();
|
|
523
619
|
for (const line of whereOut.split(/\r?\n/).map((l) => l.trim()).filter(Boolean)) {
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
620
|
+
const asarCandidate1 = path.join(path.dirname(line), "resources", "app.asar");
|
|
621
|
+
const asarCandidate2 = path.resolve(path.dirname(line), "..", "resources", "app.asar");
|
|
622
|
+
const asarCandidate3 = path.resolve(line, "..", "..", "resources", "app.asar");
|
|
623
|
+
for (const cand of [asarCandidate1, asarCandidate2, asarCandidate3]) {
|
|
624
|
+
if (fs.existsSync(cand))
|
|
625
|
+
candidates.add(path.resolve(cand));
|
|
626
|
+
}
|
|
527
627
|
}
|
|
528
628
|
}
|
|
529
629
|
catch { }
|
|
@@ -584,7 +684,8 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
584
684
|
for (const ent of entries) {
|
|
585
685
|
if (ent.isDirectory()) {
|
|
586
686
|
const full = path.join(current, ent.name);
|
|
587
|
-
|
|
687
|
+
const normalizedFull = full.replace(/\\/g, "/");
|
|
688
|
+
if (ent.name === "server" && normalizedFull.includes("@getpaseo/server")) {
|
|
588
689
|
serverDir = full;
|
|
589
690
|
break;
|
|
590
691
|
}
|
|
@@ -612,14 +713,37 @@ export async function patchPaseoAsar(asarPath) {
|
|
|
612
713
|
// Create backup if not already present
|
|
613
714
|
const backupPath = `${asarPath}.bak`;
|
|
614
715
|
if (!fs.existsSync(backupPath)) {
|
|
615
|
-
|
|
616
|
-
|
|
716
|
+
try {
|
|
717
|
+
fs.copyFileSync(asarPath, backupPath);
|
|
718
|
+
changes.push(`Backed up original asar to ${backupPath}`);
|
|
719
|
+
}
|
|
720
|
+
catch (backupErr) {
|
|
721
|
+
logger.warn(`Could not create asar backup at ${backupPath}`, { error: String(backupErr) });
|
|
722
|
+
}
|
|
617
723
|
}
|
|
618
724
|
tempAsar = path.join(os.tmpdir(), `app-${Date.now()}.asar`);
|
|
619
725
|
await asar.createPackage(tempDir, tempAsar);
|
|
620
|
-
// Replace original archive
|
|
621
|
-
|
|
622
|
-
|
|
726
|
+
// Replace original archive with locked file handling for Windows
|
|
727
|
+
try {
|
|
728
|
+
fs.copyFileSync(tempAsar, asarPath);
|
|
729
|
+
changes.push(`Repacked updated asar archive at ${asarPath}`);
|
|
730
|
+
}
|
|
731
|
+
catch (copyErr) {
|
|
732
|
+
if (copyErr && (copyErr.code === "EBUSY" || copyErr.code === "EPERM" || copyErr.code === "EACCES")) {
|
|
733
|
+
const oldPath = `${asarPath}.old-${Date.now()}`;
|
|
734
|
+
try {
|
|
735
|
+
fs.renameSync(asarPath, oldPath);
|
|
736
|
+
fs.copyFileSync(tempAsar, asarPath);
|
|
737
|
+
changes.push(`Repacked updated asar archive at ${asarPath} (safe replaced locked file, moved previous to ${oldPath})`);
|
|
738
|
+
}
|
|
739
|
+
catch (renameErr) {
|
|
740
|
+
throw new Error(`Cannot update ${asarPath}: file is locked by a running Paseo process (${copyErr.code}). Please close Paseo completely (Paseo.exe in system tray / Task Manager) and retry.`);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
throw copyErr;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
623
747
|
return { success: true, changes };
|
|
624
748
|
}
|
|
625
749
|
catch (err) {
|
|
@@ -687,3 +811,101 @@ export async function ensurePaseoIntegration(options) {
|
|
|
687
811
|
errors,
|
|
688
812
|
};
|
|
689
813
|
}
|
|
814
|
+
/**
|
|
815
|
+
* Checks whether a @getpaseo/server installation directory is already patched
|
|
816
|
+
* for Antigravity quota and telemetry support.
|
|
817
|
+
*/
|
|
818
|
+
export function isPaseoServerPatched(serverDir) {
|
|
819
|
+
try {
|
|
820
|
+
if (!fs.existsSync(serverDir))
|
|
821
|
+
return false;
|
|
822
|
+
const antigravityJsCandidates = [
|
|
823
|
+
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "providers", "antigravity.js"),
|
|
824
|
+
path.join(serverDir, "dist", "services", "quota-fetcher", "providers", "antigravity.js"),
|
|
825
|
+
];
|
|
826
|
+
let hasProvider = antigravityJsCandidates.some((p) => fs.existsSync(p));
|
|
827
|
+
if (!hasProvider && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
828
|
+
const checkRecursive = (dir, depth = 0) => {
|
|
829
|
+
if (depth > 5)
|
|
830
|
+
return false;
|
|
831
|
+
try {
|
|
832
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
833
|
+
for (const e of entries) {
|
|
834
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
835
|
+
if (checkRecursive(path.join(dir, e.name), depth + 1))
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
else if (e.isFile() && e.name === "antigravity.js" && dir.replace(/\\/g, "/").includes("quota-fetcher")) {
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
catch { }
|
|
844
|
+
return false;
|
|
845
|
+
};
|
|
846
|
+
hasProvider = checkRecursive(path.join(serverDir, "dist"));
|
|
847
|
+
}
|
|
848
|
+
if (hasProvider)
|
|
849
|
+
return true;
|
|
850
|
+
const manifestCandidates = [
|
|
851
|
+
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "manifest.js"),
|
|
852
|
+
path.join(serverDir, "dist", "services", "quota-fetcher", "manifest.js"),
|
|
853
|
+
];
|
|
854
|
+
for (const cand of manifestCandidates) {
|
|
855
|
+
if (fs.existsSync(cand)) {
|
|
856
|
+
const content = fs.readFileSync(cand, "utf-8");
|
|
857
|
+
if (content.includes('providerId: "antigravity"'))
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
catch {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Checks whether a Paseo app.asar archive is already patched with Antigravity telemetry.
|
|
869
|
+
*/
|
|
870
|
+
export async function isPaseoAsarPatched(asarPath) {
|
|
871
|
+
try {
|
|
872
|
+
if (!fs.existsSync(asarPath))
|
|
873
|
+
return false;
|
|
874
|
+
const asarModule = await import("@electron/asar");
|
|
875
|
+
const asar = asarModule.default || asarModule;
|
|
876
|
+
const files = asar.listPackage(asarPath);
|
|
877
|
+
return files.some((f) => f.includes("antigravity.js"));
|
|
878
|
+
}
|
|
879
|
+
catch {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Detects if Paseo Desktop is currently running on the host.
|
|
885
|
+
*/
|
|
886
|
+
export function isPaseoRunning() {
|
|
887
|
+
try {
|
|
888
|
+
if (process.platform === "win32") {
|
|
889
|
+
const out = execFileSync("tasklist.exe", ["/FI", "IMAGENAME eq Paseo*", "/NH"], {
|
|
890
|
+
encoding: "utf-8",
|
|
891
|
+
timeout: 3000,
|
|
892
|
+
windowsHide: true,
|
|
893
|
+
});
|
|
894
|
+
return /paseo/i.test(out) && !out.includes("INFO:") && !out.includes("No tasks");
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
const out = execFileSync("pgrep", ["-i", "-x", "paseo"], {
|
|
898
|
+
encoding: "utf-8",
|
|
899
|
+
timeout: 2000,
|
|
900
|
+
});
|
|
901
|
+
const pids = out
|
|
902
|
+
.split(/\r?\n/)
|
|
903
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
904
|
+
.filter((p) => !isNaN(p) && p !== process.pid);
|
|
905
|
+
return pids.length > 0;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -173,7 +173,12 @@ export interface ProviderUsage {
|
|
|
173
173
|
}>;
|
|
174
174
|
error: string | null;
|
|
175
175
|
}
|
|
176
|
-
export declare function formatExecBinaryPath(binaryPath: string): string;
|
|
176
|
+
export declare function formatExecBinaryPath(binaryPath: string, shell?: boolean): string;
|
|
177
|
+
export interface ResolvedCommandExecution {
|
|
178
|
+
cmd: string;
|
|
179
|
+
shell: boolean;
|
|
180
|
+
}
|
|
181
|
+
export declare function resolveCommandExecution(binaryPath: string): ResolvedCommandExecution;
|
|
177
182
|
export declare function fetchAntigravityUsage(binaryPath?: string, force?: boolean): Promise<ProviderUsage>;
|
|
178
183
|
export declare const ALL_THINKING_LEVELS: Record<string, {
|
|
179
184
|
name: string;
|
package/dist/protocol.js
CHANGED
|
@@ -3,6 +3,7 @@ import { promisify } from "node:util";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { logger } from "./logger.js";
|
|
5
5
|
import { saveBase64Image } from "./attachments.js";
|
|
6
|
+
import { isWindowsBatchScript } from "./antigravity-process.js";
|
|
6
7
|
const execFileAsync = promisify(execFile);
|
|
7
8
|
// ACP wire methods use snake_case. The aliases retain the public method names
|
|
8
9
|
// accepted by the adapter before the hardening work so existing clients do not
|
|
@@ -278,12 +279,19 @@ function cacheProviderUsage(binaryPath, result, now) {
|
|
|
278
279
|
lastProviderUsageFetch = now;
|
|
279
280
|
return result;
|
|
280
281
|
}
|
|
281
|
-
export function formatExecBinaryPath(binaryPath) {
|
|
282
|
-
|
|
282
|
+
export function formatExecBinaryPath(binaryPath, shell) {
|
|
283
|
+
const isWin = process.platform === "win32";
|
|
284
|
+
const needsShell = shell ?? (isWin && isWindowsBatchScript(binaryPath));
|
|
285
|
+
if (needsShell && isWin && binaryPath.includes(" ") && !binaryPath.startsWith('"')) {
|
|
283
286
|
return `"${binaryPath}"`;
|
|
284
287
|
}
|
|
285
288
|
return binaryPath;
|
|
286
289
|
}
|
|
290
|
+
export function resolveCommandExecution(binaryPath) {
|
|
291
|
+
const isBatch = process.platform === "win32" && isWindowsBatchScript(binaryPath);
|
|
292
|
+
const cmd = isBatch && binaryPath.includes(" ") && !binaryPath.startsWith('"') ? `"${binaryPath}"` : binaryPath;
|
|
293
|
+
return { cmd, shell: isBatch };
|
|
294
|
+
}
|
|
287
295
|
export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
288
296
|
const now = Date.now();
|
|
289
297
|
if (!force &&
|
|
@@ -292,21 +300,22 @@ export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
|
292
300
|
now - lastProviderUsageFetch < PROVIDER_USAGE_CACHE_TTL_MS) {
|
|
293
301
|
return cachedProviderUsage;
|
|
294
302
|
}
|
|
295
|
-
const
|
|
303
|
+
const isBatch = process.platform === "win32" && isWindowsBatchScript(binaryPath);
|
|
304
|
+
const cmd = isBatch && binaryPath.includes(" ") && !binaryPath.startsWith('"') ? `"${binaryPath}"` : binaryPath;
|
|
296
305
|
try {
|
|
297
306
|
const [usageResult, creditsResult] = await Promise.allSettled([
|
|
298
307
|
execFileAsync(cmd, ["--print", "/usage"], {
|
|
299
308
|
timeout: 8_000,
|
|
300
309
|
maxBuffer: 1024 * 1024,
|
|
301
310
|
env: process.env,
|
|
302
|
-
shell:
|
|
311
|
+
shell: isBatch,
|
|
303
312
|
windowsHide: true,
|
|
304
313
|
}),
|
|
305
314
|
execFileAsync(cmd, ["--print", "/credits"], {
|
|
306
315
|
timeout: 8_000,
|
|
307
316
|
maxBuffer: 1024 * 1024,
|
|
308
317
|
env: process.env,
|
|
309
|
-
shell:
|
|
318
|
+
shell: isBatch,
|
|
310
319
|
windowsHide: true,
|
|
311
320
|
}),
|
|
312
321
|
]);
|
|
@@ -429,13 +438,14 @@ export async function fetchAvailableModels(binaryPath = "agy", force = false) {
|
|
|
429
438
|
return inFlightModelFetch;
|
|
430
439
|
}
|
|
431
440
|
inFlightModelFetch = (async () => {
|
|
432
|
-
const
|
|
441
|
+
const isBatch = process.platform === "win32" && isWindowsBatchScript(binaryPath);
|
|
442
|
+
const cmd = isBatch && binaryPath.includes(" ") && !binaryPath.startsWith('"') ? `"${binaryPath}"` : binaryPath;
|
|
433
443
|
try {
|
|
434
444
|
const { stdout } = await execFileAsync(cmd, ["models"], {
|
|
435
445
|
timeout: 10_000,
|
|
436
446
|
env: process.env,
|
|
437
447
|
maxBuffer: 4 * 1024 * 1024,
|
|
438
|
-
shell:
|
|
448
|
+
shell: isBatch,
|
|
439
449
|
windowsHide: true,
|
|
440
450
|
});
|
|
441
451
|
const parsed = parseAgyModelsOutput(stdout);
|
package/dist/slash-commands.js
CHANGED
|
@@ -5,7 +5,7 @@ import { execFile } from "node:child_process";
|
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { logger } from "./logger.js";
|
|
7
7
|
import { sessionStore } from "./session-store.js";
|
|
8
|
-
import {
|
|
8
|
+
import { isWindowsBatchScript } from "./antigravity-process.js";
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
const AGY_COMMAND_TIMEOUT_MS = 30_000;
|
|
11
11
|
const AGY_COMMAND_MAX_BUFFER = 4 * 1024 * 1024;
|
|
@@ -337,13 +337,14 @@ export function formatUsageOutput(rawText) {
|
|
|
337
337
|
return out;
|
|
338
338
|
}
|
|
339
339
|
async function runAgySlash(binaryPath, cwd, slashCommand) {
|
|
340
|
-
const
|
|
340
|
+
const isBatch = process.platform === "win32" && isWindowsBatchScript(binaryPath);
|
|
341
|
+
const cmd = isBatch && binaryPath.includes(" ") && !binaryPath.startsWith('"') ? `"${binaryPath}"` : binaryPath;
|
|
341
342
|
const { stdout, stderr } = await execFileAsync(cmd, ["--print-timeout", "24h", "--print", slashCommand], {
|
|
342
343
|
cwd,
|
|
343
344
|
env: process.env,
|
|
344
345
|
timeout: AGY_COMMAND_TIMEOUT_MS,
|
|
345
346
|
maxBuffer: AGY_COMMAND_MAX_BUFFER,
|
|
346
|
-
shell:
|
|
347
|
+
shell: isBatch,
|
|
347
348
|
windowsHide: true,
|
|
348
349
|
});
|
|
349
350
|
return stdout.trim() || stderr.trim();
|