paseo-acp-agy 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/acp-server.d.ts +1 -0
- package/dist/acp-server.js +23 -0
- package/dist/index.js +40 -0
- package/dist/paseo-patcher.d.ts +34 -0
- package/dist/paseo-patcher.js +457 -0
- package/dist/protocol.d.ts +1 -0
- package/dist/protocol.js +49 -34
- package/dist/slash-commands.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,28 @@ To include `paseo-acp-agy` in Paseo's built-in provider store (`ACP_PROVIDER_CAT
|
|
|
90
90
|
|
|
91
91
|
---
|
|
92
92
|
|
|
93
|
+
## Plan Usage & Context Window Meter Setup
|
|
94
|
+
|
|
95
|
+
To display the **Google Antigravity** quota breakdown in Paseo's `Settings -> Usage` (Plan usage) and activate the real-time context-window meter ("bolinha") in the chat composer:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx -y paseo-acp-agy setup
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or if installed globally:
|
|
102
|
+
```bash
|
|
103
|
+
paseo-acp-agy setup
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Then restart your Paseo app or daemon:
|
|
107
|
+
```bash
|
|
108
|
+
paseo daemon restart
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> **Note**: `paseo-acp-agy` also checks and auto-configures your local Paseo server automatically whenever it launches via `--acp`. Running `setup` manually ensures your existing daemon instance picks up the telemetry hooks immediately upon restart.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
93
115
|
## Requirements
|
|
94
116
|
|
|
95
117
|
- **Node.js**: >= 20.0.0 (Node.js 22 recommended)
|
package/dist/acp-server.d.ts
CHANGED
package/dist/acp-server.js
CHANGED
|
@@ -80,6 +80,26 @@ export class ACPServer {
|
|
|
80
80
|
},
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
+
publishUsageUpdate(session) {
|
|
84
|
+
const costUsd = roundUsageCostUsd(session.usage.totalCostUsd);
|
|
85
|
+
this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
|
|
86
|
+
sessionId: session.id,
|
|
87
|
+
update: {
|
|
88
|
+
sessionUpdate: "usage_update",
|
|
89
|
+
size: session.usage.contextWindowMaxTokens,
|
|
90
|
+
used: session.usage.contextWindowUsedTokens,
|
|
91
|
+
cost: { amount: costUsd, currency: "USD" },
|
|
92
|
+
inputTokens: session.usage.inputTokens,
|
|
93
|
+
outputTokens: session.usage.outputTokens,
|
|
94
|
+
cachedReadTokens: session.usage.cachedInputTokens,
|
|
95
|
+
cachedInputTokens: session.usage.cachedInputTokens,
|
|
96
|
+
totalTokens: session.usage.totalTokens,
|
|
97
|
+
totalCostUsd: costUsd,
|
|
98
|
+
contextWindowMaxTokens: session.usage.contextWindowMaxTokens,
|
|
99
|
+
contextWindowUsedTokens: session.usage.contextWindowUsedTokens,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
83
103
|
async sessionState(session, forceModels = false) {
|
|
84
104
|
const availableModels = await fetchAvailableModels(this.binaryPath, forceModels);
|
|
85
105
|
return {
|
|
@@ -178,6 +198,7 @@ export class ACPServer {
|
|
|
178
198
|
if (!isNotification) {
|
|
179
199
|
this.sendSuccess(id, await this.sessionState(session, true));
|
|
180
200
|
this.publishCommands(session.id);
|
|
201
|
+
this.publishUsageUpdate(session);
|
|
181
202
|
}
|
|
182
203
|
break;
|
|
183
204
|
}
|
|
@@ -212,6 +233,7 @@ export class ACPServer {
|
|
|
212
233
|
if (!isNotification) {
|
|
213
234
|
this.sendSuccess(id, await this.sessionState(session));
|
|
214
235
|
this.publishCommands(session.id);
|
|
236
|
+
this.publishUsageUpdate(session);
|
|
215
237
|
}
|
|
216
238
|
}
|
|
217
239
|
catch (err) {
|
|
@@ -375,6 +397,7 @@ export class ACPServer {
|
|
|
375
397
|
// after model/tool work. Record it before branching on status.
|
|
376
398
|
session.recordTurnUsage(turnUsage, executingModel);
|
|
377
399
|
const usagePayload = this.turnUsagePayload(session, turnUsage, executingModel);
|
|
400
|
+
this.publishUsageUpdate(session);
|
|
378
401
|
if (session.isCancelled) {
|
|
379
402
|
if (!isNotification) {
|
|
380
403
|
this.sendSuccess(id, { stopReason: "cancelled", usage: usagePayload });
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { ACPServer } from "./acp-server.js";
|
|
3
3
|
import { logger } from "./logger.js";
|
|
4
4
|
import { formatDiagnosticVersion, resolveBuildMetadata } from "./version.js";
|
|
5
|
+
import { ensurePaseoIntegration } from "./paseo-patcher.js";
|
|
5
6
|
const args = process.argv.slice(2);
|
|
6
7
|
if (args.includes("--version") || args.includes("-v")) {
|
|
7
8
|
if (args.includes("--json")) {
|
|
@@ -19,9 +20,14 @@ Usage:
|
|
|
19
20
|
paseo-acp-agy [options]
|
|
20
21
|
agy-acp [options]
|
|
21
22
|
npx -y paseo-acp-agy [options]
|
|
23
|
+
npx -y paseo-acp-agy setup
|
|
24
|
+
|
|
25
|
+
Commands:
|
|
26
|
+
setup, patch Configure and integrate Antigravity telemetry with Paseo
|
|
22
27
|
|
|
23
28
|
Options:
|
|
24
29
|
--acp Start ACP server over stdio (default)
|
|
30
|
+
--setup Integrate Antigravity with local Paseo server installation
|
|
25
31
|
-v, --version Show version
|
|
26
32
|
--json Show version in JSON format (with --version)
|
|
27
33
|
-h, --help Show help
|
|
@@ -32,9 +38,43 @@ Environment Variables:
|
|
|
32
38
|
AGY_ACP_SANDBOX Set to 'true' to run agy in sandbox mode
|
|
33
39
|
AGY_ACP_DANGEROUSLY_SKIP_PERMISSIONS Set to 'true' to auto-approve tool permissions
|
|
34
40
|
AGY_BIN_PATH Path to agy binary (default: agy in PATH or ~/.local/bin/agy)
|
|
41
|
+
PASEO_SERVER_PATH Path to local @getpaseo/server directory
|
|
35
42
|
`);
|
|
36
43
|
process.exit(0);
|
|
37
44
|
}
|
|
45
|
+
if (args.includes("setup") ||
|
|
46
|
+
args.includes("patch") ||
|
|
47
|
+
args.includes("--setup") ||
|
|
48
|
+
args.includes("--patch")) {
|
|
49
|
+
process.stdout.write("Checking Paseo installation and configuring Antigravity telemetry...\n");
|
|
50
|
+
try {
|
|
51
|
+
const res = ensurePaseoIntegration({ verbose: true });
|
|
52
|
+
if (!res.found) {
|
|
53
|
+
process.stdout.write("Notice: No active @getpaseo/server installation found in standard paths.\n" +
|
|
54
|
+
"If Paseo is installed in a custom directory, set PASEO_SERVER_PATH and run setup again.\n");
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
process.stdout.write(`Found ${res.serverPaths.length} Paseo server installation(s).\n`);
|
|
58
|
+
if (res.patchedPaths.length > 0) {
|
|
59
|
+
process.stdout.write(`Successfully integrated with: \n${res.patchedPaths.map((p) => ` - ${p}`).join("\n")}\n\n` +
|
|
60
|
+
`Antigravity quota provider and context-window telemetry are now enabled!\n` +
|
|
61
|
+
`Please restart Paseo (or run 'paseo daemon restart') to apply changes.\n`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
process.stdout.write("Paseo is already up-to-date and configured for Antigravity telemetry.\n");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
process.stderr.write(`Setup encountered an issue: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
70
|
+
}
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
// Auto-run integration in background when starting ACP server
|
|
74
|
+
try {
|
|
75
|
+
ensurePaseoIntegration();
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
38
78
|
const server = new ACPServer();
|
|
39
79
|
const cleanup = async () => {
|
|
40
80
|
try {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface PatchResult {
|
|
2
|
+
found: boolean;
|
|
3
|
+
serverPaths: string[];
|
|
4
|
+
patchedPaths: string[];
|
|
5
|
+
errors: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Searches the host machine for @getpaseo/server installations across
|
|
9
|
+
* Windows, macOS, and Linux.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findPaseoServerInstallations(): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Returns the JavaScript source for the Antigravity quota provider to be
|
|
14
|
+
* injected into Paseo server's quota-fetcher providers.
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateAntigravityQuotaProviderJs(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Patches a Paseo server installation directory to enable Antigravity:
|
|
19
|
+
* 1. Patches quota-fetcher/manifest.js to register Antigravity
|
|
20
|
+
* 2. Writes quota-fetcher/providers/antigravity.js
|
|
21
|
+
* 3. Patches acp-agent.js to map context window tokens and emit usage updates
|
|
22
|
+
*/
|
|
23
|
+
export declare function patchPaseoServer(serverDir: string): {
|
|
24
|
+
success: boolean;
|
|
25
|
+
changes: string[];
|
|
26
|
+
error?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Discovers and patches all accessible Paseo installations.
|
|
30
|
+
*/
|
|
31
|
+
export declare function ensurePaseoIntegration(options?: {
|
|
32
|
+
verbose?: boolean;
|
|
33
|
+
targetPaths?: string[];
|
|
34
|
+
}): PatchResult;
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { logger } from "./logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* Searches the host machine for @getpaseo/server installations across
|
|
8
|
+
* Windows, macOS, and Linux.
|
|
9
|
+
*/
|
|
10
|
+
export function findPaseoServerInstallations() {
|
|
11
|
+
const candidates = new Set();
|
|
12
|
+
// 1. Explicit environment variable overrides
|
|
13
|
+
if (process.env.PASEO_SERVER_PATH && fs.existsSync(process.env.PASEO_SERVER_PATH)) {
|
|
14
|
+
candidates.add(path.resolve(process.env.PASEO_SERVER_PATH));
|
|
15
|
+
}
|
|
16
|
+
if (process.env.PASEO_INSTALL_DIR && fs.existsSync(process.env.PASEO_INSTALL_DIR)) {
|
|
17
|
+
const direct = path.join(process.env.PASEO_INSTALL_DIR, "node_modules", "@getpaseo", "server");
|
|
18
|
+
if (fs.existsSync(direct))
|
|
19
|
+
candidates.add(path.resolve(direct));
|
|
20
|
+
}
|
|
21
|
+
// 2. Platform-specific default paths
|
|
22
|
+
const home = os.homedir();
|
|
23
|
+
if (process.platform === "win32") {
|
|
24
|
+
const appData = process.env.APPDATA || (home ? path.join(home, "AppData", "Roaming") : "");
|
|
25
|
+
const localAppData = process.env.LOCALAPPDATA || (home ? path.join(home, "AppData", "Local") : "");
|
|
26
|
+
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
27
|
+
const winLocations = [
|
|
28
|
+
path.join(appData, "npm", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"),
|
|
29
|
+
path.join(appData, "npm", "node_modules", "@getpaseo", "server"),
|
|
30
|
+
path.join(localAppData, "npm", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"),
|
|
31
|
+
path.join(localAppData, "npm", "node_modules", "@getpaseo", "server"),
|
|
32
|
+
path.join(programFiles, "nodejs", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"),
|
|
33
|
+
path.join(programFiles, "nodejs", "node_modules", "@getpaseo", "server"),
|
|
34
|
+
path.join(localAppData, "Programs", "Paseo", "resources", "app.asar.unpacked", "node_modules", "@getpaseo", "server"),
|
|
35
|
+
path.join(localAppData, "Programs", "Paseo", "resources", "app", "node_modules", "@getpaseo", "server"),
|
|
36
|
+
path.join(programFiles, "Paseo", "resources", "app.asar.unpacked", "node_modules", "@getpaseo", "server"),
|
|
37
|
+
];
|
|
38
|
+
for (const loc of winLocations) {
|
|
39
|
+
if (loc && fs.existsSync(loc))
|
|
40
|
+
candidates.add(path.resolve(loc));
|
|
41
|
+
}
|
|
42
|
+
// Try detecting global npm root via npm.cmd
|
|
43
|
+
try {
|
|
44
|
+
const npmRoot = execFileSync("cmd.exe", ["/c", "npm.cmd", "root", "-g"], {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
timeout: 2000,
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
}).trim();
|
|
49
|
+
if (npmRoot && fs.existsSync(npmRoot)) {
|
|
50
|
+
const p1 = path.join(npmRoot, "@getpaseo", "cli", "node_modules", "@getpaseo", "server");
|
|
51
|
+
const p2 = path.join(npmRoot, "@getpaseo", "server");
|
|
52
|
+
if (fs.existsSync(p1))
|
|
53
|
+
candidates.add(path.resolve(p1));
|
|
54
|
+
if (fs.existsSync(p2))
|
|
55
|
+
candidates.add(path.resolve(p2));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
// Check where.exe paseo
|
|
60
|
+
try {
|
|
61
|
+
const whereOut = execFileSync("where.exe", ["paseo"], {
|
|
62
|
+
encoding: "utf-8",
|
|
63
|
+
timeout: 2000,
|
|
64
|
+
windowsHide: true,
|
|
65
|
+
}).trim();
|
|
66
|
+
const first = whereOut.split(/\r?\n/)[0]?.trim();
|
|
67
|
+
if (first) {
|
|
68
|
+
const paseoDir = path.dirname(first);
|
|
69
|
+
const p1 = path.join(paseoDir, "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server");
|
|
70
|
+
const p2 = path.join(paseoDir, "node_modules", "@getpaseo", "server");
|
|
71
|
+
if (fs.existsSync(p1))
|
|
72
|
+
candidates.add(path.resolve(p1));
|
|
73
|
+
if (fs.existsSync(p2))
|
|
74
|
+
candidates.add(path.resolve(p2));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// POSIX locations (Linux / macOS)
|
|
81
|
+
const posixLocations = [
|
|
82
|
+
"/usr/lib/node_modules/@getpaseo/cli/node_modules/@getpaseo/server",
|
|
83
|
+
"/usr/lib/node_modules/@getpaseo/server",
|
|
84
|
+
"/usr/local/lib/node_modules/@getpaseo/cli/node_modules/@getpaseo/server",
|
|
85
|
+
"/usr/local/lib/node_modules/@getpaseo/server",
|
|
86
|
+
"/Applications/Paseo.app/Contents/Resources/app.asar.unpacked/node_modules/@getpaseo/server",
|
|
87
|
+
"/Applications/Paseo.app/Contents/Resources/app/node_modules/@getpaseo/server",
|
|
88
|
+
];
|
|
89
|
+
if (home) {
|
|
90
|
+
posixLocations.push(path.join(home, ".local", "share", "pnpm", "global", "5", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"), path.join(home, ".bun", "install", "global", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"));
|
|
91
|
+
// Check nvm paths if available
|
|
92
|
+
const nvmDir = path.join(home, ".nvm", "versions", "node");
|
|
93
|
+
if (fs.existsSync(nvmDir)) {
|
|
94
|
+
try {
|
|
95
|
+
const versions = fs.readdirSync(nvmDir);
|
|
96
|
+
for (const ver of versions) {
|
|
97
|
+
posixLocations.push(path.join(nvmDir, ver, "lib", "node_modules", "@getpaseo", "cli", "node_modules", "@getpaseo", "server"), path.join(nvmDir, ver, "lib", "node_modules", "@getpaseo", "server"));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const loc of posixLocations) {
|
|
104
|
+
if (fs.existsSync(loc))
|
|
105
|
+
candidates.add(path.resolve(loc));
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const npmRoot = execFileSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 2000 }).trim();
|
|
109
|
+
if (npmRoot && fs.existsSync(npmRoot)) {
|
|
110
|
+
const p1 = path.join(npmRoot, "@getpaseo", "cli", "node_modules", "@getpaseo", "server");
|
|
111
|
+
const p2 = path.join(npmRoot, "@getpaseo", "server");
|
|
112
|
+
if (fs.existsSync(p1))
|
|
113
|
+
candidates.add(path.resolve(p1));
|
|
114
|
+
if (fs.existsSync(p2))
|
|
115
|
+
candidates.add(path.resolve(p2));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
}
|
|
120
|
+
// Filter out any paths that do not actually have a dist directory or package.json
|
|
121
|
+
const verified = [];
|
|
122
|
+
for (const dir of candidates) {
|
|
123
|
+
if (fs.existsSync(path.join(dir, "dist")) || fs.existsSync(path.join(dir, "package.json"))) {
|
|
124
|
+
verified.push(dir);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return verified;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Returns the JavaScript source for the Antigravity quota provider to be
|
|
131
|
+
* injected into Paseo server's quota-fetcher providers.
|
|
132
|
+
*/
|
|
133
|
+
export function generateAntigravityQuotaProviderJs() {
|
|
134
|
+
return `import { execFile } from "node:child_process";
|
|
135
|
+
import { promisify } from "node:util";
|
|
136
|
+
import fs from "node:fs";
|
|
137
|
+
import os from "node:os";
|
|
138
|
+
import path from "node:path";
|
|
139
|
+
import { toneFromUsedPct, windowFromUsedPct, unavailableUsage } from "../usage.js";
|
|
140
|
+
|
|
141
|
+
const execFileAsync = promisify(execFile);
|
|
142
|
+
|
|
143
|
+
function resolveAgyBinary() {
|
|
144
|
+
if (process.env.AGY_BIN_PATH) return process.env.AGY_BIN_PATH;
|
|
145
|
+
const home = os.homedir();
|
|
146
|
+
if (home) {
|
|
147
|
+
if (process.platform === "win32") {
|
|
148
|
+
const candidates = [
|
|
149
|
+
path.join(home, ".local", "bin", "agy.exe"),
|
|
150
|
+
path.join(home, "AppData", "Local", "Programs", "antigravity", "agy.exe"),
|
|
151
|
+
path.join(home, ".local", "bin", "agy.cmd"),
|
|
152
|
+
path.join(home, ".local", "bin", "agy.bat"),
|
|
153
|
+
];
|
|
154
|
+
for (const cand of candidates) {
|
|
155
|
+
if (fs.existsSync(cand)) return cand;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const { execFileSync } = require("node:child_process");
|
|
159
|
+
const out = execFileSync("where.exe", ["agy"], { encoding: "utf-8", timeout: 1000 }).trim();
|
|
160
|
+
const first = out.split(/\\r?\\n/)[0]?.trim();
|
|
161
|
+
if (first && fs.existsSync(first)) return first;
|
|
162
|
+
} catch {}
|
|
163
|
+
} else {
|
|
164
|
+
const localPath = path.join(home, ".local", "bin", "agy");
|
|
165
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return "agy";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export class AntigravityQuotaProvider {
|
|
172
|
+
constructor(options) {
|
|
173
|
+
this.providerId = "antigravity";
|
|
174
|
+
this.displayName = "Antigravity";
|
|
175
|
+
this.logger = typeof options?.logger?.child === "function" ? options.logger.child({ module: "antigravity-quota-provider" }) : options?.logger;
|
|
176
|
+
this.binaryPath = resolveAgyBinary();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async fetchUsage() {
|
|
180
|
+
try {
|
|
181
|
+
const isWin = process.platform === "win32";
|
|
182
|
+
const [usageRes, creditsRes] = await Promise.allSettled([
|
|
183
|
+
execFileAsync(this.binaryPath, ["--print", "/usage"], { timeout: 8000, env: process.env, shell: isWin }),
|
|
184
|
+
execFileAsync(this.binaryPath, ["--print", "/credits"], { timeout: 8000, env: process.env, shell: isWin }),
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
const usageOut = usageRes.status === "fulfilled" ? usageRes.value.stdout || usageRes.value.stderr : "";
|
|
188
|
+
const creditsOut = creditsRes.status === "fulfilled" ? creditsRes.value.stdout || creditsRes.value.stderr : "";
|
|
189
|
+
|
|
190
|
+
const rawWindows = [];
|
|
191
|
+
for (const line of usageOut.split(/[\\r\\n]+/)) {
|
|
192
|
+
const trimmed = line.trim();
|
|
193
|
+
if (!trimmed || trimmed.toLowerCase().startsWith("quota:")) continue;
|
|
194
|
+
|
|
195
|
+
let scope = "";
|
|
196
|
+
let limitType = "";
|
|
197
|
+
let remainingPct = null;
|
|
198
|
+
let resetsAt = null;
|
|
199
|
+
|
|
200
|
+
const m = trimmed.match(/^(.*?)\\s{2,}(.*?Remaining)\\s+(\\d+)%(?:\\s+(.*))?$/i);
|
|
201
|
+
if (m) {
|
|
202
|
+
scope = m[1].trim();
|
|
203
|
+
limitType = m[2].trim();
|
|
204
|
+
remainingPct = parseInt(m[3], 10);
|
|
205
|
+
resetsAt = m[4] ? new Date(m[4].trim()).toISOString() : null;
|
|
206
|
+
} else {
|
|
207
|
+
const parts = trimmed.split(/\\t+|\\s{2,}/).map(p => p.trim());
|
|
208
|
+
if (parts.length >= 3) {
|
|
209
|
+
scope = parts[0];
|
|
210
|
+
limitType = parts[1];
|
|
211
|
+
const remMatch = parts[2].match(/(\\d+)%/);
|
|
212
|
+
if (remMatch) remainingPct = parseInt(remMatch[1], 10);
|
|
213
|
+
resetsAt = parts[3] ? new Date(parts[3]).toISOString() : null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (remainingPct !== null && !isNaN(remainingPct)) {
|
|
218
|
+
const usedPct = Math.max(0, Math.min(100, 100 - remainingPct));
|
|
219
|
+
const isFiveHour = /five\\s*hour/i.test(limitType);
|
|
220
|
+
const isWeekly = /weekly/i.test(limitType);
|
|
221
|
+
const isGemini = /gemini/i.test(scope);
|
|
222
|
+
|
|
223
|
+
let id = isFiveHour ? "session" : isWeekly ? "weekly" : "quota";
|
|
224
|
+
let label = isFiveHour ? "Session" : isWeekly ? "Weekly" : limitType.replace(/\\s+Remaining$/i, "");
|
|
225
|
+
if (!isGemini) {
|
|
226
|
+
id = \`claude_\${id}\`;
|
|
227
|
+
label = \`Claude \${label}\`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
rawWindows.push({
|
|
231
|
+
id,
|
|
232
|
+
label,
|
|
233
|
+
utilizationPct: usedPct,
|
|
234
|
+
resetsAt,
|
|
235
|
+
tone: toneFromUsedPct(usedPct),
|
|
236
|
+
isFiveHour,
|
|
237
|
+
isGemini,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
rawWindows.sort((a, b) => {
|
|
243
|
+
if (a.isGemini && !b.isGemini) return -1;
|
|
244
|
+
if (!a.isGemini && b.isGemini) return 1;
|
|
245
|
+
if (a.isFiveHour && !b.isFiveHour) return -1;
|
|
246
|
+
if (!a.isFiveHour && b.isFiveHour) return 1;
|
|
247
|
+
return 0;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const windows = rawWindows.map(w => windowFromUsedPct(w));
|
|
251
|
+
|
|
252
|
+
const balances = [];
|
|
253
|
+
const credMatch = creditsOut.match(/Remaining\\s+credits\\s+([\\d.]+)/i);
|
|
254
|
+
const remainingCredits = credMatch ? parseFloat(credMatch[1]) : 0;
|
|
255
|
+
balances.push({
|
|
256
|
+
id: "credits",
|
|
257
|
+
label: "Credits",
|
|
258
|
+
remaining: remainingCredits,
|
|
259
|
+
unit: "usd",
|
|
260
|
+
tone: remainingCredits > 0 ? "ok" : "default",
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
providerId: this.providerId,
|
|
265
|
+
displayName: "Antigravity",
|
|
266
|
+
status: "available",
|
|
267
|
+
planLabel: "Google Gemini",
|
|
268
|
+
windows,
|
|
269
|
+
balances,
|
|
270
|
+
details: [],
|
|
271
|
+
error: null,
|
|
272
|
+
};
|
|
273
|
+
} catch (err) {
|
|
274
|
+
return unavailableUsage({
|
|
275
|
+
providerId: this.providerId,
|
|
276
|
+
displayName: "Antigravity",
|
|
277
|
+
error: err.message,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
`;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Patches a Paseo server installation directory to enable Antigravity:
|
|
286
|
+
* 1. Patches quota-fetcher/manifest.js to register Antigravity
|
|
287
|
+
* 2. Writes quota-fetcher/providers/antigravity.js
|
|
288
|
+
* 3. Patches acp-agent.js to map context window tokens and emit usage updates
|
|
289
|
+
*/
|
|
290
|
+
export function patchPaseoServer(serverDir) {
|
|
291
|
+
const changes = [];
|
|
292
|
+
try {
|
|
293
|
+
// 1. Locate manifest.js in quota-fetcher
|
|
294
|
+
const manifestCandidates = [
|
|
295
|
+
path.join(serverDir, "dist", "server", "services", "quota-fetcher", "manifest.js"),
|
|
296
|
+
path.join(serverDir, "dist", "services", "quota-fetcher", "manifest.js"),
|
|
297
|
+
];
|
|
298
|
+
let manifestFile = manifestCandidates.find((f) => fs.existsSync(f));
|
|
299
|
+
if (!manifestFile && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
300
|
+
const findManifest = (dir) => {
|
|
301
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
302
|
+
for (const e of entries) {
|
|
303
|
+
const full = path.join(dir, e.name);
|
|
304
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
305
|
+
const found = findManifest(full);
|
|
306
|
+
if (found)
|
|
307
|
+
return found;
|
|
308
|
+
}
|
|
309
|
+
else if (e.isFile() && e.name === "manifest.js" && dir.includes("quota-fetcher")) {
|
|
310
|
+
return full;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
};
|
|
315
|
+
manifestFile = findManifest(path.join(serverDir, "dist")) || undefined;
|
|
316
|
+
}
|
|
317
|
+
if (manifestFile) {
|
|
318
|
+
const quotaDir = path.dirname(manifestFile);
|
|
319
|
+
const providersDir = path.join(quotaDir, "providers");
|
|
320
|
+
if (!fs.existsSync(providersDir)) {
|
|
321
|
+
fs.mkdirSync(providersDir, { recursive: true });
|
|
322
|
+
}
|
|
323
|
+
// Write / update providers/antigravity.js
|
|
324
|
+
const antigravityJsPath = path.join(providersDir, "antigravity.js");
|
|
325
|
+
fs.writeFileSync(antigravityJsPath, generateAntigravityQuotaProviderJs(), "utf-8");
|
|
326
|
+
changes.push(`Created/Updated ${antigravityJsPath}`);
|
|
327
|
+
// Patch manifest.js
|
|
328
|
+
let manifestCode = fs.readFileSync(manifestFile, "utf-8");
|
|
329
|
+
let manifestModified = false;
|
|
330
|
+
if (!manifestCode.includes('from "./providers/antigravity.js"') && !manifestCode.includes("AntigravityQuotaProvider")) {
|
|
331
|
+
manifestCode = `import { AntigravityQuotaProvider } from "./providers/antigravity.js";\n` + manifestCode;
|
|
332
|
+
manifestModified = true;
|
|
333
|
+
}
|
|
334
|
+
if (!manifestCode.includes('providerId: "antigravity"')) {
|
|
335
|
+
const entryToAdd = ` {\n providerId: "antigravity",\n create: (options) => new AntigravityQuotaProvider({\n logger: options.logger,\n fetch: options.fetch,\n }),\n },\n`;
|
|
336
|
+
const marker = "export const PROVIDER_USAGE_FETCHERS = [";
|
|
337
|
+
if (manifestCode.includes(marker)) {
|
|
338
|
+
manifestCode = manifestCode.replace(marker, `${marker}\n${entryToAdd}`);
|
|
339
|
+
manifestModified = true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (manifestModified) {
|
|
343
|
+
fs.writeFileSync(manifestFile, manifestCode, "utf-8");
|
|
344
|
+
changes.push(`Patched ${manifestFile} with AntigravityQuotaProvider`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// 2. Locate acp-agent.js
|
|
348
|
+
const acpCandidates = [
|
|
349
|
+
path.join(serverDir, "dist", "server", "server", "agent", "providers", "acp-agent.js"),
|
|
350
|
+
path.join(serverDir, "dist", "server", "agent", "providers", "acp-agent.js"),
|
|
351
|
+
path.join(serverDir, "dist", "agent", "providers", "acp-agent.js"),
|
|
352
|
+
];
|
|
353
|
+
let acpFile = acpCandidates.find((f) => fs.existsSync(f));
|
|
354
|
+
if (!acpFile && fs.existsSync(path.join(serverDir, "dist"))) {
|
|
355
|
+
const findAcp = (dir) => {
|
|
356
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
357
|
+
for (const e of entries) {
|
|
358
|
+
const full = path.join(dir, e.name);
|
|
359
|
+
if (e.isDirectory() && e.name !== "node_modules") {
|
|
360
|
+
const found = findAcp(full);
|
|
361
|
+
if (found)
|
|
362
|
+
return found;
|
|
363
|
+
}
|
|
364
|
+
else if (e.isFile() && e.name === "acp-agent.js") {
|
|
365
|
+
return full;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
};
|
|
370
|
+
acpFile = findAcp(path.join(serverDir, "dist")) || undefined;
|
|
371
|
+
}
|
|
372
|
+
if (acpFile) {
|
|
373
|
+
let acpCode = fs.readFileSync(acpFile, "utf-8");
|
|
374
|
+
let acpModified = false;
|
|
375
|
+
// Patch mapACPUsage
|
|
376
|
+
if (!acpCode.includes("contextWindowMaxTokens: usage.contextWindowMaxTokens")) {
|
|
377
|
+
const oldMapRegex = /export\s+function\s+mapACPUsage\s*\([^)]*\)\s*\{[\s\S]*?return\s*\{[\s\S]*?\};\s*\}/m;
|
|
378
|
+
const newMap = `export function mapACPUsage(usage) {
|
|
379
|
+
if (!usage) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
inputTokens: usage.inputTokens ?? undefined,
|
|
384
|
+
outputTokens: usage.outputTokens ?? undefined,
|
|
385
|
+
cachedInputTokens: usage.cachedReadTokens ?? usage.cachedInputTokens ?? undefined,
|
|
386
|
+
totalCostUsd: usage.totalCostUsd ?? (usage.cost?.amount !== undefined ? Number(usage.cost.amount) : undefined),
|
|
387
|
+
contextWindowMaxTokens: usage.contextWindowMaxTokens ?? usage.size ?? undefined,
|
|
388
|
+
contextWindowUsedTokens: usage.contextWindowUsedTokens ?? usage.used ?? undefined,
|
|
389
|
+
};
|
|
390
|
+
}`;
|
|
391
|
+
if (oldMapRegex.test(acpCode)) {
|
|
392
|
+
acpCode = acpCode.replace(oldMapRegex, newMap);
|
|
393
|
+
acpModified = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// Patch handleUsageUpdate
|
|
397
|
+
if (acpCode.includes("handleUsageUpdate(update) {") && !acpCode.includes("this.notifySubscribers({")) {
|
|
398
|
+
const oldHandlerRegex = /handleUsageUpdate\s*\(\s*update\s*\)\s*\{[\s\S]*?void\s+update;?[\s\S]*?\}/m;
|
|
399
|
+
const newHandler = `handleUsageUpdate(update) {
|
|
400
|
+
if (!update) return;
|
|
401
|
+
const usage = mapACPUsage(update);
|
|
402
|
+
if (usage) {
|
|
403
|
+
this.currentTurnUsage = { ...this.currentTurnUsage, ...usage };
|
|
404
|
+
this.notifySubscribers({
|
|
405
|
+
type: "usage_updated",
|
|
406
|
+
provider: this.provider,
|
|
407
|
+
usage: this.currentTurnUsage,
|
|
408
|
+
...(this.activeForegroundTurnId ? { turnId: this.activeForegroundTurnId } : {}),
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}`;
|
|
412
|
+
if (oldHandlerRegex.test(acpCode)) {
|
|
413
|
+
acpCode = acpCode.replace(oldHandlerRegex, newHandler);
|
|
414
|
+
acpModified = true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (acpModified) {
|
|
418
|
+
fs.writeFileSync(acpFile, acpCode, "utf-8");
|
|
419
|
+
changes.push(`Patched ${acpFile} for context-window token telemetry and usage updates`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { success: true, changes };
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
426
|
+
logger.warn("Failed to patch Paseo server", { serverDir, error: errorMsg });
|
|
427
|
+
return { success: false, changes, error: errorMsg };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Discovers and patches all accessible Paseo installations.
|
|
432
|
+
*/
|
|
433
|
+
export function ensurePaseoIntegration(options) {
|
|
434
|
+
const serverPaths = options?.targetPaths || findPaseoServerInstallations();
|
|
435
|
+
const patchedPaths = [];
|
|
436
|
+
const errors = [];
|
|
437
|
+
for (const sPath of serverPaths) {
|
|
438
|
+
const res = patchPaseoServer(sPath);
|
|
439
|
+
if (res.success) {
|
|
440
|
+
if (res.changes.length > 0) {
|
|
441
|
+
patchedPaths.push(sPath);
|
|
442
|
+
if (options?.verbose) {
|
|
443
|
+
logger.info(`Integrated with Paseo server at ${sPath}`, { changes: res.changes });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (res.error) {
|
|
448
|
+
errors.push(`${sPath}: ${res.error}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
found: serverPaths.length > 0,
|
|
453
|
+
serverPaths,
|
|
454
|
+
patchedPaths,
|
|
455
|
+
errors,
|
|
456
|
+
};
|
|
457
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -173,6 +173,7 @@ export interface ProviderUsage {
|
|
|
173
173
|
}>;
|
|
174
174
|
error: string | null;
|
|
175
175
|
}
|
|
176
|
+
export declare function formatExecBinaryPath(binaryPath: string): string;
|
|
176
177
|
export declare function fetchAntigravityUsage(binaryPath?: string, force?: boolean): Promise<ProviderUsage>;
|
|
177
178
|
export declare const ALL_THINKING_LEVELS: Record<string, {
|
|
178
179
|
name: string;
|
package/dist/protocol.js
CHANGED
|
@@ -278,6 +278,12 @@ function cacheProviderUsage(binaryPath, result, now) {
|
|
|
278
278
|
lastProviderUsageFetch = now;
|
|
279
279
|
return result;
|
|
280
280
|
}
|
|
281
|
+
export function formatExecBinaryPath(binaryPath) {
|
|
282
|
+
if (process.platform === "win32" && binaryPath.includes(" ") && !binaryPath.startsWith('"')) {
|
|
283
|
+
return `"${binaryPath}"`;
|
|
284
|
+
}
|
|
285
|
+
return binaryPath;
|
|
286
|
+
}
|
|
281
287
|
export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
282
288
|
const now = Date.now();
|
|
283
289
|
if (!force &&
|
|
@@ -286,15 +292,16 @@ export async function fetchAntigravityUsage(binaryPath = "agy", force = false) {
|
|
|
286
292
|
now - lastProviderUsageFetch < PROVIDER_USAGE_CACHE_TTL_MS) {
|
|
287
293
|
return cachedProviderUsage;
|
|
288
294
|
}
|
|
295
|
+
const cmd = formatExecBinaryPath(binaryPath);
|
|
289
296
|
try {
|
|
290
297
|
const [usageResult, creditsResult] = await Promise.allSettled([
|
|
291
|
-
execFileAsync(
|
|
298
|
+
execFileAsync(cmd, ["--print", "/usage"], {
|
|
292
299
|
timeout: 8_000,
|
|
293
300
|
maxBuffer: 1024 * 1024,
|
|
294
301
|
env: process.env,
|
|
295
302
|
shell: process.platform === "win32",
|
|
296
303
|
}),
|
|
297
|
-
execFileAsync(
|
|
304
|
+
execFileAsync(cmd, ["--print", "/credits"], {
|
|
298
305
|
timeout: 8_000,
|
|
299
306
|
maxBuffer: 1024 * 1024,
|
|
300
307
|
env: process.env,
|
|
@@ -371,6 +378,7 @@ export const FALLBACK_MODELS = [
|
|
|
371
378
|
let cachedModels = null;
|
|
372
379
|
let cachedModelsBinaryPath = null;
|
|
373
380
|
let lastModelFetch = 0;
|
|
381
|
+
let inFlightModelFetch = null;
|
|
374
382
|
const MODEL_CACHE_TTL_MS = 60_000;
|
|
375
383
|
export function parseAgyModelsOutput(rawOutput) {
|
|
376
384
|
const modelsMap = new Map();
|
|
@@ -384,24 +392,21 @@ export function parseAgyModelsOutput(rawOutput) {
|
|
|
384
392
|
const modelId = parts[0];
|
|
385
393
|
const label = parts[1];
|
|
386
394
|
const effortMatch = modelId.match(/-(high|medium|low)$/);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
baseId = modelId.slice(0, -(effort.length + 1));
|
|
393
|
-
baseLabel = label.replace(/\s*\((High|Medium|Low)\)$/, "");
|
|
394
|
-
}
|
|
395
|
+
const baseId = effortMatch ? modelId.replace(/-(high|medium|low)$/, "") : modelId;
|
|
396
|
+
const effort = effortMatch ? effortMatch[1] : undefined;
|
|
397
|
+
const cleanLabel = effortMatch
|
|
398
|
+
? label.replace(/\s*\((High|Medium|Low)\)$/i, "").trim()
|
|
399
|
+
: label;
|
|
395
400
|
if (!modelsMap.has(baseId)) {
|
|
396
401
|
modelsMap.set(baseId, {
|
|
397
402
|
modelId: baseId,
|
|
398
|
-
name:
|
|
403
|
+
name: cleanLabel,
|
|
399
404
|
description: label,
|
|
400
|
-
supportedEfforts: [],
|
|
405
|
+
supportedEfforts: effort ? [effort] : [],
|
|
401
406
|
contextWindowMaxTokens: getModelContextWindow(baseId),
|
|
402
407
|
});
|
|
403
408
|
}
|
|
404
|
-
if (effort) {
|
|
409
|
+
else if (effort) {
|
|
405
410
|
const entry = modelsMap.get(baseId);
|
|
406
411
|
if (!entry.supportedEfforts.includes(effort))
|
|
407
412
|
entry.supportedEfforts.push(effort);
|
|
@@ -418,28 +423,38 @@ export async function fetchAvailableModels(binaryPath = "agy", force = false) {
|
|
|
418
423
|
now - lastModelFetch < MODEL_CACHE_TTL_MS) {
|
|
419
424
|
return cachedModels;
|
|
420
425
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
timeout: 5_000,
|
|
424
|
-
env: process.env,
|
|
425
|
-
maxBuffer: 4 * 1024 * 1024,
|
|
426
|
-
shell: process.platform === "win32",
|
|
427
|
-
});
|
|
428
|
-
const parsed = parseAgyModelsOutput(stdout);
|
|
429
|
-
cachedModels = parsed;
|
|
430
|
-
cachedModelsBinaryPath = binaryPath;
|
|
431
|
-
lastModelFetch = now;
|
|
432
|
-
return parsed;
|
|
433
|
-
}
|
|
434
|
-
catch (err) {
|
|
435
|
-
logger.warn("Failed to fetch models from agy CLI, using fallback models", {
|
|
436
|
-
error: err.message,
|
|
437
|
-
});
|
|
438
|
-
cachedModels = FALLBACK_MODELS;
|
|
439
|
-
cachedModelsBinaryPath = binaryPath;
|
|
440
|
-
lastModelFetch = now;
|
|
441
|
-
return FALLBACK_MODELS;
|
|
426
|
+
if (inFlightModelFetch) {
|
|
427
|
+
return inFlightModelFetch;
|
|
442
428
|
}
|
|
429
|
+
inFlightModelFetch = (async () => {
|
|
430
|
+
const cmd = formatExecBinaryPath(binaryPath);
|
|
431
|
+
try {
|
|
432
|
+
const { stdout } = await execFileAsync(cmd, ["models"], {
|
|
433
|
+
timeout: 10_000,
|
|
434
|
+
env: process.env,
|
|
435
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
436
|
+
shell: process.platform === "win32",
|
|
437
|
+
});
|
|
438
|
+
const parsed = parseAgyModelsOutput(stdout);
|
|
439
|
+
cachedModels = parsed;
|
|
440
|
+
cachedModelsBinaryPath = binaryPath;
|
|
441
|
+
lastModelFetch = Date.now();
|
|
442
|
+
return parsed;
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
logger.warn("Failed to fetch models from agy CLI, using fallback models", {
|
|
446
|
+
error: err.message,
|
|
447
|
+
});
|
|
448
|
+
cachedModels = FALLBACK_MODELS;
|
|
449
|
+
cachedModelsBinaryPath = binaryPath;
|
|
450
|
+
lastModelFetch = Date.now();
|
|
451
|
+
return FALLBACK_MODELS;
|
|
452
|
+
}
|
|
453
|
+
finally {
|
|
454
|
+
inFlightModelFetch = null;
|
|
455
|
+
}
|
|
456
|
+
})();
|
|
457
|
+
return inFlightModelFetch;
|
|
443
458
|
}
|
|
444
459
|
export function getEffectiveEffortForModel(modelId, requestedEffort, models) {
|
|
445
460
|
if (!modelId)
|
package/dist/slash-commands.js
CHANGED
|
@@ -5,6 +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 { formatExecBinaryPath } from "./protocol.js";
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
const AGY_COMMAND_TIMEOUT_MS = 30_000;
|
|
10
11
|
const AGY_COMMAND_MAX_BUFFER = 4 * 1024 * 1024;
|
|
@@ -336,7 +337,8 @@ export function formatUsageOutput(rawText) {
|
|
|
336
337
|
return out;
|
|
337
338
|
}
|
|
338
339
|
async function runAgySlash(binaryPath, cwd, slashCommand) {
|
|
339
|
-
const
|
|
340
|
+
const cmd = formatExecBinaryPath(binaryPath);
|
|
341
|
+
const { stdout, stderr } = await execFileAsync(cmd, ["--print", slashCommand], {
|
|
340
342
|
cwd,
|
|
341
343
|
env: process.env,
|
|
342
344
|
timeout: AGY_COMMAND_TIMEOUT_MS,
|