ccjk 10.1.0 → 10.2.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/dist/chunks/agent-teams.mjs +1 -1
- package/dist/chunks/agent.mjs +1 -1
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +3 -3
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-updater.mjs +1 -1
- package/dist/{shared/ccjk.Br91zBIG.mjs → chunks/banner.mjs} +52 -3
- package/dist/chunks/boost.mjs +2 -2
- package/dist/chunks/ccjk-agents.mjs +1 -1
- package/dist/chunks/ccjk-all.mjs +1 -1
- package/dist/chunks/ccjk-config.mjs +1 -1
- package/dist/chunks/ccjk-hooks.mjs +1 -1
- package/dist/chunks/ccjk-mcp.mjs +2 -2
- package/dist/chunks/ccjk-setup.mjs +1 -1
- package/dist/chunks/ccjk-skills.mjs +1 -1
- package/dist/chunks/ccr.mjs +10 -10
- package/dist/chunks/ccu.mjs +1 -1
- package/dist/chunks/check-updates.mjs +3 -3
- package/dist/chunks/claude-code-config-manager.mjs +7 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
- package/dist/chunks/claude-config.mjs +3 -3
- package/dist/chunks/claude-wrapper.mjs +2 -2
- package/dist/chunks/codex-config-switch.mjs +2 -2
- package/dist/chunks/codex-provider-manager.mjs +2 -2
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +5 -5
- package/dist/chunks/commands.mjs +88 -391
- package/dist/chunks/commands2.mjs +391 -88
- package/dist/chunks/completion.mjs +1 -1
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +3 -3
- package/dist/chunks/config.mjs +5 -5
- package/dist/chunks/config2.mjs +410 -400
- package/dist/chunks/config3.mjs +400 -410
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/context.mjs +283 -1
- package/dist/chunks/dashboard.mjs +365 -0
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/features.mjs +11 -11
- package/dist/chunks/fs-operations.mjs +1 -1
- package/dist/chunks/health-alerts.mjs +304 -0
- package/dist/chunks/health-check.mjs +532 -0
- package/dist/chunks/index.mjs +10 -177
- package/dist/chunks/index2.mjs +168 -1162
- package/dist/chunks/index3.mjs +1076 -910
- package/dist/chunks/index4.mjs +947 -137
- package/dist/chunks/index5.mjs +167 -635
- package/dist/chunks/index6.mjs +663 -0
- package/dist/chunks/init.mjs +19 -19
- package/dist/chunks/installer.mjs +649 -147
- package/dist/chunks/installer2.mjs +147 -649
- package/dist/chunks/interview.mjs +2 -2
- package/dist/chunks/marketplace.mjs +1 -1
- package/dist/chunks/mcp.mjs +4 -4
- package/dist/chunks/menu.mjs +16 -9
- package/dist/chunks/metrics-display.mjs +152 -0
- package/dist/chunks/migrator.mjs +1 -1
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +1 -1
- package/dist/chunks/onboarding.mjs +2 -2
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +1 -1
- package/dist/chunks/persistence-manager.mjs +781 -0
- package/dist/chunks/persistence.mjs +667 -0
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/plugin.mjs +1 -1
- package/dist/chunks/prompts.mjs +1 -1
- package/dist/chunks/providers.mjs +1 -1
- package/dist/chunks/quick-actions.mjs +321 -0
- package/dist/chunks/quick-setup.mjs +8 -8
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +2 -2
- package/dist/chunks/skill.mjs +1 -1
- package/dist/chunks/skills-sync.mjs +1 -1
- package/dist/chunks/skills.mjs +1 -1
- package/dist/chunks/slash-commands.mjs +208 -0
- package/dist/chunks/smart-defaults.mjs +1 -1
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +1 -1
- package/dist/chunks/status.mjs +31 -2
- package/dist/chunks/team.mjs +1 -1
- package/dist/chunks/thinking.mjs +2 -2
- package/dist/chunks/uninstall.mjs +5 -5
- package/dist/chunks/update.mjs +7 -7
- package/dist/chunks/upgrade-manager.mjs +2 -2
- package/dist/chunks/version-checker.mjs +3 -3
- package/dist/chunks/vim.mjs +1 -1
- package/dist/cli.mjs +191 -21
- package/dist/i18n/locales/en/cli.json +14 -1
- package/dist/i18n/locales/en/common.json +27 -0
- package/dist/i18n/locales/en/context.json +54 -1
- package/dist/i18n/locales/en/dashboard.json +78 -0
- package/dist/i18n/locales/en/persistence.json +127 -0
- package/dist/i18n/locales/en/quick-actions.json +78 -0
- package/dist/i18n/locales/zh-CN/cli.json +14 -1
- package/dist/i18n/locales/zh-CN/common.json +27 -0
- package/dist/i18n/locales/zh-CN/context.json +54 -1
- package/dist/i18n/locales/zh-CN/dashboard.json +78 -0
- package/dist/i18n/locales/zh-CN/persistence.json +127 -0
- package/dist/i18n/locales/zh-CN/quick-actions.json +78 -0
- package/dist/index.mjs +2 -2
- package/dist/shared/{ccjk.DE91nClQ.mjs → ccjk.BKoi8-Hy.mjs} +1 -1
- package/dist/shared/{ccjk.Dpw86UX0.mjs → ccjk.CxtuJxaS.mjs} +1 -1
- package/dist/shared/{ccjk.ClzTOz9n.mjs → ccjk.DB2UYcq0.mjs} +5 -5
- package/dist/shared/{ccjk.Bndhan7G.mjs → ccjk.DfwJOEok.mjs} +1 -1
- package/dist/shared/{ccjk.DvIrK0wz.mjs → ccjk.DrMygfCF.mjs} +1 -1
- package/dist/shared/{ccjk.CmsW23FN.mjs → ccjk.IbImMVWM.mjs} +3 -3
- package/package.json +19 -19
|
@@ -1,680 +1,178 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import
|
|
3
|
+
import { join } from 'node:path';
|
|
4
4
|
import ansis from 'ansis';
|
|
5
|
-
import inquirer from 'inquirer';
|
|
6
|
-
import ora from 'ora';
|
|
7
|
-
import { join } from 'pathe';
|
|
8
|
-
import { exec } from 'tinyexec';
|
|
9
|
-
import { ensureI18nInitialized, i18n } from './index.mjs';
|
|
10
|
-
import { updateClaudeCode } from './auto-updater.mjs';
|
|
11
|
-
import { exists } from './fs-operations.mjs';
|
|
12
|
-
import { f as findCommandPath, d as getPlatform, e as getHomebrewCommandPaths, a as isTermux, h as getTermuxPrefix, j as isWSL, k as getWSLInfo, w as wrapCommandWithSudo, c as commandExists, l as getRecommendedInstallMethods } from './platform.mjs';
|
|
13
|
-
import 'node:url';
|
|
14
|
-
import 'i18next';
|
|
15
|
-
import 'i18next-fs-backend';
|
|
16
|
-
import '../shared/ccjk.DHbrGcgg.mjs';
|
|
17
|
-
import 'inquirer-toggle';
|
|
18
|
-
import './version-checker.mjs';
|
|
19
|
-
import 'node:child_process';
|
|
20
|
-
import 'node:path';
|
|
21
|
-
import 'node:util';
|
|
22
|
-
import 'semver';
|
|
23
|
-
import 'node:crypto';
|
|
24
|
-
import 'node:fs/promises';
|
|
25
5
|
|
|
26
|
-
|
|
27
|
-
|
|
6
|
+
const { cyan, yellow, gray, green, red, blue: _blue, bold, dim: _dim } = ansis;
|
|
7
|
+
const INSTALL_DIR = join(homedir(), ".agent-browser");
|
|
8
|
+
const BIN_PATH = join(INSTALL_DIR, "bin", "agent-browser");
|
|
9
|
+
function getInstallPath() {
|
|
10
|
+
return INSTALL_DIR;
|
|
28
11
|
}
|
|
29
|
-
async function
|
|
30
|
-
ensureI18nInitialized();
|
|
31
|
-
const codeType = "claude-code";
|
|
32
|
-
const installed = await isClaudeCodeInstalled();
|
|
33
|
-
if (installed) {
|
|
34
|
-
console.log(ansis.green(`\u2714 ${i18n.t("installation:alreadyInstalled")}`));
|
|
35
|
-
const version = await detectInstalledVersion(codeType);
|
|
36
|
-
if (version) {
|
|
37
|
-
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version })}`));
|
|
38
|
-
}
|
|
39
|
-
const verification = await verifyInstallation(codeType);
|
|
40
|
-
if (verification.symlinkCreated) {
|
|
41
|
-
displayVerificationResult(verification, codeType);
|
|
42
|
-
}
|
|
43
|
-
await updateClaudeCode();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (isTermux()) {
|
|
47
|
-
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:termuxDetected")}`));
|
|
48
|
-
const termuxPrefix = getTermuxPrefix();
|
|
49
|
-
console.log(ansis.gray(i18n.t("installation:termuxPathInfo", { path: termuxPrefix })));
|
|
50
|
-
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
51
|
-
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
52
|
-
}
|
|
53
|
-
if (isWSL()) {
|
|
54
|
-
const wslInfo = getWSLInfo();
|
|
55
|
-
if (wslInfo?.distro) {
|
|
56
|
-
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:wslDetected", { distro: wslInfo.distro })}`));
|
|
57
|
-
} else {
|
|
58
|
-
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:wslDetectedGeneric")}`));
|
|
59
|
-
}
|
|
60
|
-
console.log(ansis.gray(i18n.t("installation:wslPathInfo", { path: `${homedir()}/.claude/` })));
|
|
61
|
-
}
|
|
62
|
-
if (skipMethodSelection) {
|
|
63
|
-
console.log(i18n.t("installation:installing"));
|
|
64
|
-
try {
|
|
65
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@anthropic-ai/claude-code", "--force"]);
|
|
66
|
-
if (usedSudo) {
|
|
67
|
-
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
68
|
-
}
|
|
69
|
-
await exec(command, args);
|
|
70
|
-
console.log(`\u2714 ${i18n.t("installation:installSuccess")}`);
|
|
71
|
-
await setInstallMethod("npm");
|
|
72
|
-
const verification = await verifyInstallation(codeType);
|
|
73
|
-
displayVerificationResult(verification, codeType);
|
|
74
|
-
if (isTermux()) {
|
|
75
|
-
console.log(ansis.gray(`
|
|
76
|
-
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
77
|
-
}
|
|
78
|
-
if (isWSL()) {
|
|
79
|
-
console.log(ansis.gray(`
|
|
80
|
-
${i18n.t("installation:wslInstallSuccess")}`));
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error(`\u2716 ${i18n.t("installation:installFailed")}`);
|
|
84
|
-
if (isTermux()) {
|
|
85
|
-
console.error(ansis.yellow(`
|
|
86
|
-
${i18n.t("installation:termuxInstallHint")}
|
|
87
|
-
`));
|
|
88
|
-
}
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const method = await selectInstallMethod(codeType);
|
|
94
|
-
if (!method) {
|
|
95
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const success = await executeInstallMethod(method, codeType);
|
|
99
|
-
if (!success) {
|
|
100
|
-
const retrySuccess = await handleInstallFailure(codeType, [method]);
|
|
101
|
-
if (!retrySuccess) {
|
|
102
|
-
console.error(ansis.red(`\u2716 ${i18n.t("installation:installFailed")}`));
|
|
103
|
-
throw new Error(i18n.t("installation:installFailed"));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (isTermux()) {
|
|
107
|
-
console.log(ansis.gray(`
|
|
108
|
-
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
109
|
-
}
|
|
110
|
-
if (isWSL()) {
|
|
111
|
-
console.log(ansis.gray(`
|
|
112
|
-
${i18n.t("installation:wslInstallSuccess")}`));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async function isCodexInstalled() {
|
|
116
|
-
return await commandExists("codex");
|
|
117
|
-
}
|
|
118
|
-
async function installCodex(skipMethodSelection = false) {
|
|
119
|
-
ensureI18nInitialized();
|
|
120
|
-
const codeType = "codex";
|
|
121
|
-
const codeTypeName = i18n.t("common:codex");
|
|
122
|
-
const installed = await isCodexInstalled();
|
|
123
|
-
if (installed) {
|
|
124
|
-
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:alreadyInstalled")}`));
|
|
125
|
-
const version = await detectInstalledVersion(codeType);
|
|
126
|
-
if (version) {
|
|
127
|
-
console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version })}`));
|
|
128
|
-
}
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (skipMethodSelection) {
|
|
132
|
-
console.log(i18n.t("installation:installingWith", { method: "npm", codeType: codeTypeName }));
|
|
133
|
-
try {
|
|
134
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex", "--force"]);
|
|
135
|
-
if (usedSudo) {
|
|
136
|
-
console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
|
|
137
|
-
}
|
|
138
|
-
await exec(command, args);
|
|
139
|
-
console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:installSuccess")}`));
|
|
140
|
-
const verification = await verifyInstallation(codeType);
|
|
141
|
-
displayVerificationResult(verification, codeType);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error(ansis.red(`\u2716 ${codeTypeName} ${i18n.t("installation:installFailed")}`));
|
|
144
|
-
throw error;
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const method = await selectInstallMethod(codeType);
|
|
149
|
-
if (!method) {
|
|
150
|
-
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const success = await executeInstallMethod(method, codeType);
|
|
154
|
-
if (!success) {
|
|
155
|
-
const retrySuccess = await handleInstallFailure(codeType, [method]);
|
|
156
|
-
if (!retrySuccess) {
|
|
157
|
-
console.error(ansis.red(`\u2716 ${codeTypeName} ${i18n.t("installation:installFailed")}`));
|
|
158
|
-
throw new Error(i18n.t("installation:installFailed"));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async function getInstallationStatus() {
|
|
163
|
-
const hasGlobal = await isClaudeCodeInstalled();
|
|
164
|
-
return {
|
|
165
|
-
hasGlobal,
|
|
166
|
-
// Local installation was never implemented - these are kept for backward compatibility
|
|
167
|
-
hasLocal: false,
|
|
168
|
-
localPath: ""
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
async function getInstallMethodFromConfig(codeType) {
|
|
12
|
+
async function checkAgentBrowserInstalled() {
|
|
172
13
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
14
|
+
const { execSync } = await import('node:child_process');
|
|
15
|
+
execSync("which agent-browser 2>/dev/null || where agent-browser 2>nul", {
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: "pipe"
|
|
18
|
+
});
|
|
19
|
+
return true;
|
|
178
20
|
} catch {
|
|
179
21
|
}
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
async function uninstallCodeTool(codeType) {
|
|
183
|
-
ensureI18nInitialized();
|
|
184
|
-
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
185
|
-
let method = await getInstallMethodFromConfig(codeType);
|
|
186
|
-
if (!method) {
|
|
187
|
-
if (codeType === "claude-code") {
|
|
188
|
-
try {
|
|
189
|
-
const result = await exec("brew", ["list", "--cask", "claude-code"]);
|
|
190
|
-
if (result.exitCode === 0) {
|
|
191
|
-
method = "homebrew";
|
|
192
|
-
}
|
|
193
|
-
} catch {
|
|
194
|
-
}
|
|
195
|
-
} else if (codeType === "codex") {
|
|
196
|
-
try {
|
|
197
|
-
const result = await exec("brew", ["list", "--cask", "codex"]);
|
|
198
|
-
if (result.exitCode === 0) {
|
|
199
|
-
method = "homebrew";
|
|
200
|
-
}
|
|
201
|
-
} catch {
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (!method) {
|
|
205
|
-
method = "npm";
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (method === "native") {
|
|
209
|
-
const platform = getPlatform();
|
|
210
|
-
if (platform === "macos" || platform === "linux") {
|
|
211
|
-
try {
|
|
212
|
-
const testResult = codeType === "claude-code" ? await exec("brew", ["list", "--cask", "claude-code"]) : await exec("brew", ["list", "--cask", "codex"]);
|
|
213
|
-
if (testResult.exitCode === 0) {
|
|
214
|
-
method = "homebrew";
|
|
215
|
-
}
|
|
216
|
-
} catch {
|
|
217
|
-
method = "manual";
|
|
218
|
-
}
|
|
219
|
-
} else {
|
|
220
|
-
method = "manual";
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
const spinner = ora(i18n.t("installation:uninstallingWith", { method, codeType: codeTypeName })).start();
|
|
224
|
-
try {
|
|
225
|
-
switch (method) {
|
|
226
|
-
case "npm":
|
|
227
|
-
case "npm-global": {
|
|
228
|
-
const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
|
|
229
|
-
const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["uninstall", "-g", packageName]);
|
|
230
|
-
if (usedSudo) {
|
|
231
|
-
spinner.info(i18n.t("installation:usingSudo"));
|
|
232
|
-
spinner.start();
|
|
233
|
-
}
|
|
234
|
-
await exec(command, args);
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
case "homebrew": {
|
|
238
|
-
if (codeType === "claude-code") {
|
|
239
|
-
await exec("brew", ["uninstall", "--cask", "claude-code"]);
|
|
240
|
-
} else {
|
|
241
|
-
await exec("brew", ["uninstall", "--cask", "codex"]);
|
|
242
|
-
}
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
case "manual":
|
|
246
|
-
default: {
|
|
247
|
-
spinner.warn(i18n.t("installation:manualUninstallRequired", { codeType: codeTypeName }));
|
|
248
|
-
const command = codeType === "claude-code" ? "claude" : "codex";
|
|
249
|
-
try {
|
|
250
|
-
const whichCmd = getPlatform() === "windows" ? "where" : "which";
|
|
251
|
-
const result = await exec(whichCmd, [command]);
|
|
252
|
-
if (result.stdout) {
|
|
253
|
-
const binaryPath = result.stdout.trim().split("\n")[0];
|
|
254
|
-
spinner.info(i18n.t("installation:binaryLocation", { path: binaryPath }));
|
|
255
|
-
const platform = getPlatform();
|
|
256
|
-
if (platform === "windows") {
|
|
257
|
-
const quotedBinaryPath = `"${binaryPath}"`;
|
|
258
|
-
await exec("cmd", ["/c", "del", "/f", "/q", quotedBinaryPath]);
|
|
259
|
-
} else {
|
|
260
|
-
const { command: rmCmd, args: rmArgs } = wrapCommandWithSudo("rm", ["-f", binaryPath]);
|
|
261
|
-
if (rmCmd === "sudo") {
|
|
262
|
-
spinner.info(i18n.t("installation:usingSudo"));
|
|
263
|
-
spinner.start();
|
|
264
|
-
}
|
|
265
|
-
await exec(rmCmd, rmArgs);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
} catch {
|
|
269
|
-
spinner.fail(i18n.t("installation:failedToLocateBinary", { command }));
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
spinner.succeed(i18n.t("installation:uninstallSuccess", { method, codeType: codeTypeName }));
|
|
22
|
+
if (existsSync(BIN_PATH)) {
|
|
276
23
|
return true;
|
|
277
|
-
} catch (error) {
|
|
278
|
-
spinner.fail(i18n.t("installation:uninstallFailed", { method, codeType: codeTypeName }));
|
|
279
|
-
if (error instanceof Error) {
|
|
280
|
-
console.error(ansis.gray(error.message));
|
|
281
|
-
}
|
|
282
|
-
return false;
|
|
283
24
|
}
|
|
284
|
-
}
|
|
285
|
-
async function setInstallMethod(method, codeType = "claude-code") {
|
|
286
25
|
try {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
writeMcpConfig(config);
|
|
295
|
-
}
|
|
296
|
-
} catch (error) {
|
|
297
|
-
console.error("Failed to set installMethod:", error);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
async function detectInstalledVersion(codeType) {
|
|
301
|
-
try {
|
|
302
|
-
const command = codeType === "claude-code" ? "claude" : "codex";
|
|
303
|
-
const result = await exec(command, ["--version"]);
|
|
304
|
-
if (result.exitCode === 0 && result.stdout) {
|
|
305
|
-
const versionMatch = result.stdout.match(/(\d+\.\d+\.\d+)/);
|
|
306
|
-
return versionMatch ? versionMatch[1] : result.stdout.trim();
|
|
307
|
-
}
|
|
26
|
+
const { execSync } = await import('node:child_process');
|
|
27
|
+
execSync("npx --yes @anthropic-ai/agent-browser --version 2>/dev/null", {
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
stdio: "pipe",
|
|
30
|
+
timeout: 1e4
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
308
33
|
} catch {
|
|
309
34
|
}
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
function getInstallMethodLabel(method) {
|
|
313
|
-
switch (method) {
|
|
314
|
-
case "npm":
|
|
315
|
-
return i18n.t("installation:installMethodNpm");
|
|
316
|
-
case "homebrew":
|
|
317
|
-
return i18n.t("installation:installMethodHomebrew");
|
|
318
|
-
case "curl":
|
|
319
|
-
return i18n.t("installation:installMethodCurl");
|
|
320
|
-
case "powershell":
|
|
321
|
-
return i18n.t("installation:installMethodPowershell");
|
|
322
|
-
case "cmd":
|
|
323
|
-
return i18n.t("installation:installMethodCmd");
|
|
324
|
-
default:
|
|
325
|
-
return method;
|
|
326
|
-
}
|
|
35
|
+
return false;
|
|
327
36
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
37
|
+
async function installAgentBrowser(options = {}) {
|
|
38
|
+
console.log(`
|
|
39
|
+
${bold(cyan("Installing Agent Browser..."))}
|
|
40
|
+
`);
|
|
41
|
+
if (!options.force) {
|
|
42
|
+
const installed = await checkAgentBrowserInstalled();
|
|
43
|
+
if (installed) {
|
|
44
|
+
console.log(`${green("\u2713")} Agent Browser is already installed`);
|
|
45
|
+
console.log(` ${gray("Use --force to reinstall")}
|
|
46
|
+
`);
|
|
47
|
+
return true;
|
|
334
48
|
}
|
|
335
|
-
if (method === "homebrew")
|
|
336
|
-
return platform === "macos" || platform === "linux";
|
|
337
|
-
if (method === "curl")
|
|
338
|
-
return platform !== "windows" || isWSL();
|
|
339
|
-
if (method === "powershell" || method === "cmd")
|
|
340
|
-
return platform === "windows";
|
|
341
|
-
return true;
|
|
342
|
-
});
|
|
343
|
-
const topRecommended = recommendedMethods.length > 0 ? recommendedMethods[0] : null;
|
|
344
|
-
return availableMethods.map((method) => {
|
|
345
|
-
const isTopRecommended = method === topRecommended;
|
|
346
|
-
const methodLabel = getInstallMethodLabel(method);
|
|
347
|
-
const title = isTopRecommended ? `${methodLabel} ${ansis.green(`[${i18n.t("installation:recommendedMethod")}]`)}` : methodLabel;
|
|
348
|
-
return {
|
|
349
|
-
title,
|
|
350
|
-
value: method
|
|
351
|
-
};
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
async function selectInstallMethod(codeType, excludeMethods = []) {
|
|
355
|
-
ensureI18nInitialized();
|
|
356
|
-
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
357
|
-
const recommendedMethods = getRecommendedInstallMethods(codeType);
|
|
358
|
-
const methodOptions = getInstallMethodOptions(codeType, recommendedMethods).filter((option) => !excludeMethods.includes(option.value));
|
|
359
|
-
if (methodOptions.length === 0) {
|
|
360
|
-
console.log(ansis.yellow(i18n.t("installation:noMoreMethods")));
|
|
361
|
-
return null;
|
|
362
49
|
}
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
name: "method",
|
|
366
|
-
message: i18n.t("installation:selectInstallMethod", { codeType: codeTypeName }),
|
|
367
|
-
choices: methodOptions.map((opt) => ({
|
|
368
|
-
name: opt.title,
|
|
369
|
-
value: opt.value
|
|
370
|
-
}))
|
|
371
|
-
});
|
|
372
|
-
return response.method || null;
|
|
373
|
-
}
|
|
374
|
-
async function executeInstallMethod(method, codeType) {
|
|
375
|
-
ensureI18nInitialized();
|
|
376
|
-
const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
|
|
377
|
-
const spinner = ora(i18n.t("installation:installingWith", { method, codeType: codeTypeName })).start();
|
|
50
|
+
const { execSync } = await import('node:child_process');
|
|
51
|
+
const { mkdirSync } = await import('node:fs');
|
|
378
52
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
default:
|
|
431
|
-
throw new Error(`Unsupported install method: ${method}`);
|
|
53
|
+
mkdirSync(INSTALL_DIR, { recursive: true });
|
|
54
|
+
console.log(`${cyan("Step 1/3:")} Installing via npm...`);
|
|
55
|
+
try {
|
|
56
|
+
execSync("npm install -g @anthropic-ai/agent-browser 2>&1", {
|
|
57
|
+
encoding: "utf-8",
|
|
58
|
+
stdio: options.verbose ? "inherit" : "pipe"
|
|
59
|
+
});
|
|
60
|
+
console.log(` ${green("\u2713")} npm package installed globally`);
|
|
61
|
+
} catch {
|
|
62
|
+
console.log(` ${yellow("!")} Global install failed, trying local install...`);
|
|
63
|
+
execSync(`npm install @anthropic-ai/agent-browser --prefix "${INSTALL_DIR}" 2>&1`, {
|
|
64
|
+
encoding: "utf-8",
|
|
65
|
+
stdio: options.verbose ? "inherit" : "pipe"
|
|
66
|
+
});
|
|
67
|
+
console.log(` ${green("\u2713")} npm package installed locally`);
|
|
68
|
+
}
|
|
69
|
+
console.log(`
|
|
70
|
+
${cyan("Step 2/3:")} Installing Playwright browsers...`);
|
|
71
|
+
try {
|
|
72
|
+
execSync("npx playwright install chromium 2>&1", {
|
|
73
|
+
encoding: "utf-8",
|
|
74
|
+
stdio: options.verbose ? "inherit" : "pipe",
|
|
75
|
+
timeout: 3e5
|
|
76
|
+
// 5 minutes
|
|
77
|
+
});
|
|
78
|
+
console.log(` ${green("\u2713")} Chromium browser installed`);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.log(` ${yellow("!")} Playwright browser installation may have issues`);
|
|
81
|
+
if (options.verbose && error instanceof Error) {
|
|
82
|
+
console.log(` ${gray(error.message)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
console.log(`
|
|
86
|
+
${cyan("Step 3/3:")} Verifying installation...`);
|
|
87
|
+
const verified = await checkAgentBrowserInstalled();
|
|
88
|
+
if (verified) {
|
|
89
|
+
console.log(` ${green("\u2713")} Installation verified`);
|
|
90
|
+
console.log(`
|
|
91
|
+
${green("\u2713")} ${bold("Agent Browser installed successfully!")}
|
|
92
|
+
`);
|
|
93
|
+
showQuickStart();
|
|
94
|
+
return true;
|
|
95
|
+
} else {
|
|
96
|
+
console.log(` ${red("\u2717")} Verification failed`);
|
|
97
|
+
console.log(`
|
|
98
|
+
${yellow("Try manual installation:")}`);
|
|
99
|
+
console.log(` npm install -g @anthropic-ai/agent-browser`);
|
|
100
|
+
console.log(` npx playwright install chromium
|
|
101
|
+
`);
|
|
102
|
+
return false;
|
|
432
103
|
}
|
|
433
|
-
spinner.succeed(i18n.t("installation:installMethodSuccess", { method }));
|
|
434
|
-
const verification = await verifyInstallation(codeType);
|
|
435
|
-
displayVerificationResult(verification, codeType);
|
|
436
|
-
return true;
|
|
437
104
|
} catch (error) {
|
|
438
|
-
|
|
105
|
+
console.error(`
|
|
106
|
+
${red("\u2717")} Installation failed`);
|
|
439
107
|
if (error instanceof Error) {
|
|
440
|
-
console.error(
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
type: "confirm",
|
|
449
|
-
name: "retry",
|
|
450
|
-
message: i18n.t("installation:tryAnotherMethod"),
|
|
451
|
-
default: true
|
|
452
|
-
});
|
|
453
|
-
if (!response.retry) {
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
const newMethod = await selectInstallMethod(codeType, failedMethods);
|
|
457
|
-
if (!newMethod) {
|
|
108
|
+
console.error(` ${gray("Error:")} ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
console.log(`
|
|
111
|
+
${yellow("Manual installation:")}`);
|
|
112
|
+
console.log(` ${cyan("1.")} npm install -g @anthropic-ai/agent-browser`);
|
|
113
|
+
console.log(` ${cyan("2.")} npx playwright install chromium`);
|
|
114
|
+
console.log(` ${cyan("3.")} agent-browser --version
|
|
115
|
+
`);
|
|
458
116
|
return false;
|
|
459
117
|
}
|
|
460
|
-
const success = await executeInstallMethod(newMethod, codeType);
|
|
461
|
-
if (success) {
|
|
462
|
-
return true;
|
|
463
|
-
}
|
|
464
|
-
return await handleInstallFailure(codeType, [...failedMethods, newMethod]);
|
|
465
118
|
}
|
|
466
|
-
async function
|
|
119
|
+
async function uninstallAgentBrowser(options = {}) {
|
|
120
|
+
console.log(`
|
|
121
|
+
${bold(cyan("Uninstalling Agent Browser..."))}
|
|
122
|
+
`);
|
|
123
|
+
const { execSync } = await import('node:child_process');
|
|
124
|
+
const { rmSync } = await import('node:fs');
|
|
467
125
|
try {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
version,
|
|
484
|
-
needsSymlink: false,
|
|
485
|
-
symlinkCreated: false
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
if (getPlatform() === "macos") {
|
|
489
|
-
const homebrewPaths = await getHomebrewCommandPaths(command);
|
|
490
|
-
let foundPath = null;
|
|
491
|
-
for (const path of homebrewPaths) {
|
|
492
|
-
if (exists(path)) {
|
|
493
|
-
foundPath = path;
|
|
494
|
-
break;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
if (foundPath) {
|
|
498
|
-
const symlinkResult = await createHomebrewSymlink(command, foundPath);
|
|
499
|
-
if (symlinkResult.success) {
|
|
500
|
-
const version = await detectInstalledVersion(codeType);
|
|
501
|
-
return {
|
|
502
|
-
success: true,
|
|
503
|
-
commandPath: symlinkResult.symlinkPath,
|
|
504
|
-
version,
|
|
505
|
-
needsSymlink: true,
|
|
506
|
-
symlinkCreated: true
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
success: false,
|
|
511
|
-
commandPath: foundPath,
|
|
512
|
-
version: null,
|
|
513
|
-
needsSymlink: true,
|
|
514
|
-
symlinkCreated: false,
|
|
515
|
-
error: symlinkResult.error
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
if (isTermux()) {
|
|
520
|
-
const termuxPrefix = getTermuxPrefix();
|
|
521
|
-
const termuxPaths = [
|
|
522
|
-
`${termuxPrefix}/bin/${command}`,
|
|
523
|
-
`${termuxPrefix}/usr/bin/${command}`
|
|
524
|
-
];
|
|
525
|
-
for (const path of termuxPaths) {
|
|
526
|
-
if (exists(path)) {
|
|
527
|
-
const version = await detectInstalledVersion(codeType);
|
|
528
|
-
return {
|
|
529
|
-
success: true,
|
|
530
|
-
commandPath: path,
|
|
531
|
-
version,
|
|
532
|
-
needsSymlink: false,
|
|
533
|
-
symlinkCreated: false
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
if (getPlatform() === "linux") {
|
|
539
|
-
const home = homedir();
|
|
540
|
-
const linuxPaths = [
|
|
541
|
-
`${home}/.local/bin/${command}`,
|
|
542
|
-
// Standard XDG user bin (curl install default)
|
|
543
|
-
`${home}/.claude/bin/${command}`,
|
|
544
|
-
// Claude-specific bin directory
|
|
545
|
-
`/usr/local/bin/${command}`,
|
|
546
|
-
// System-wide installation
|
|
547
|
-
`/usr/bin/${command}`
|
|
548
|
-
// System package manager
|
|
549
|
-
];
|
|
550
|
-
for (const path of linuxPaths) {
|
|
551
|
-
if (exists(path)) {
|
|
552
|
-
const version = await detectInstalledVersion(codeType);
|
|
553
|
-
const needsPathUpdate = path.includes(".local/bin") && !process__default.env.PATH?.includes(".local/bin");
|
|
554
|
-
return {
|
|
555
|
-
success: true,
|
|
556
|
-
commandPath: path,
|
|
557
|
-
version,
|
|
558
|
-
needsSymlink: false,
|
|
559
|
-
symlinkCreated: false,
|
|
560
|
-
error: needsPathUpdate ? "PATH_UPDATE_NEEDED" : void 0
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return {
|
|
566
|
-
success: false,
|
|
567
|
-
commandPath: null,
|
|
568
|
-
version: null,
|
|
569
|
-
needsSymlink: false,
|
|
570
|
-
symlinkCreated: false,
|
|
571
|
-
error: "Command not found in any known location"
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
async function createHomebrewSymlink(command, sourcePath) {
|
|
575
|
-
const homebrewBinPaths = [
|
|
576
|
-
"/opt/homebrew/bin",
|
|
577
|
-
// Apple Silicon (M1/M2)
|
|
578
|
-
"/usr/local/bin"
|
|
579
|
-
// Intel Mac
|
|
580
|
-
];
|
|
581
|
-
let targetDir = null;
|
|
582
|
-
for (const binPath of homebrewBinPaths) {
|
|
583
|
-
if (nodeFs.existsSync(binPath)) {
|
|
584
|
-
targetDir = binPath;
|
|
585
|
-
break;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
if (!targetDir) {
|
|
589
|
-
return {
|
|
590
|
-
success: false,
|
|
591
|
-
symlinkPath: null,
|
|
592
|
-
error: "No suitable Homebrew bin directory found"
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
const symlinkPath = join(targetDir, command);
|
|
596
|
-
try {
|
|
597
|
-
const stats = nodeFs.lstatSync(symlinkPath);
|
|
598
|
-
if (stats.isSymbolicLink()) {
|
|
599
|
-
const existingTarget = nodeFs.readlinkSync(symlinkPath);
|
|
600
|
-
if (existingTarget === sourcePath) {
|
|
601
|
-
return {
|
|
602
|
-
success: true,
|
|
603
|
-
symlinkPath
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
nodeFs.unlinkSync(symlinkPath);
|
|
126
|
+
console.log(`${cyan("Step 1/2:")} Removing npm package...`);
|
|
127
|
+
try {
|
|
128
|
+
execSync("npm uninstall -g @anthropic-ai/agent-browser 2>&1", {
|
|
129
|
+
encoding: "utf-8",
|
|
130
|
+
stdio: options.verbose ? "inherit" : "pipe"
|
|
131
|
+
});
|
|
132
|
+
console.log(` ${green("\u2713")} Global package removed`);
|
|
133
|
+
} catch {
|
|
134
|
+
console.log(` ${gray("-")} No global package found`);
|
|
135
|
+
}
|
|
136
|
+
console.log(`
|
|
137
|
+
${cyan("Step 2/2:")} Cleaning up local files...`);
|
|
138
|
+
if (existsSync(INSTALL_DIR)) {
|
|
139
|
+
rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
140
|
+
console.log(` ${green("\u2713")} Local files removed`);
|
|
607
141
|
} else {
|
|
608
|
-
|
|
609
|
-
success: false,
|
|
610
|
-
symlinkPath: null,
|
|
611
|
-
error: `File already exists at ${symlinkPath} and is not a symlink`
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
} catch (error) {
|
|
615
|
-
if (error.code !== "ENOENT") {
|
|
616
|
-
return {
|
|
617
|
-
success: false,
|
|
618
|
-
symlinkPath: null,
|
|
619
|
-
error: `Failed to check existing file: ${error}`
|
|
620
|
-
};
|
|
142
|
+
console.log(` ${gray("-")} No local files found`);
|
|
621
143
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return
|
|
626
|
-
success: true,
|
|
627
|
-
symlinkPath
|
|
628
|
-
};
|
|
144
|
+
console.log(`
|
|
145
|
+
${green("\u2713")} ${bold("Agent Browser uninstalled successfully!")}
|
|
146
|
+
`);
|
|
147
|
+
return true;
|
|
629
148
|
} catch (error) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
error: `Permission denied. Try running: sudo ln -sf ${sourcePath} ${symlinkPath}`
|
|
635
|
-
};
|
|
149
|
+
console.error(`
|
|
150
|
+
${red("\u2717")} Uninstallation failed`);
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
console.error(` ${gray("Error:")} ${error.message}`);
|
|
636
153
|
}
|
|
637
|
-
return
|
|
638
|
-
success: false,
|
|
639
|
-
symlinkPath: null,
|
|
640
|
-
error: `Failed to create symlink: ${error.message}`
|
|
641
|
-
};
|
|
154
|
+
return false;
|
|
642
155
|
}
|
|
643
156
|
}
|
|
644
|
-
function
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
console.log(ansis.gray(` ${i18n.t("installation:pathUpdatePermanent")}`));
|
|
664
|
-
console.log(ansis.green(` echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc`));
|
|
665
|
-
}
|
|
666
|
-
} else {
|
|
667
|
-
console.log(ansis.yellow(`\u26A0 ${codeTypeName} ${i18n.t("installation:verificationFailed")}`));
|
|
668
|
-
if (result.commandPath) {
|
|
669
|
-
console.log(ansis.gray(` ${i18n.t("installation:foundAtPath", { path: result.commandPath })}`));
|
|
670
|
-
}
|
|
671
|
-
if (result.error && result.error !== "PATH_UPDATE_NEEDED") {
|
|
672
|
-
console.log(ansis.gray(` ${result.error}`));
|
|
673
|
-
}
|
|
674
|
-
if (result.needsSymlink && !result.symlinkCreated) {
|
|
675
|
-
console.log(ansis.yellow(` ${i18n.t("installation:manualSymlinkHint")}`));
|
|
676
|
-
}
|
|
677
|
-
}
|
|
157
|
+
function showQuickStart() {
|
|
158
|
+
console.log(`${yellow("Quick Start:")}`);
|
|
159
|
+
console.log(` ${gray("# Open a webpage")}`);
|
|
160
|
+
console.log(` ${cyan("agent-browser open https://example.com")}`);
|
|
161
|
+
console.log();
|
|
162
|
+
console.log(` ${gray("# Get interactive elements")}`);
|
|
163
|
+
console.log(` ${cyan("agent-browser snapshot -i")}`);
|
|
164
|
+
console.log();
|
|
165
|
+
console.log(` ${gray("# Click an element by ref")}`);
|
|
166
|
+
console.log(` ${cyan("agent-browser click @e1")}`);
|
|
167
|
+
console.log();
|
|
168
|
+
console.log(` ${gray("# Take a screenshot")}`);
|
|
169
|
+
console.log(` ${cyan("agent-browser screenshot page.png")}`);
|
|
170
|
+
console.log();
|
|
171
|
+
console.log(` ${gray("# Close the browser")}`);
|
|
172
|
+
console.log(` ${cyan("agent-browser close")}`);
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(`${gray("For full documentation, run")} ${cyan("/browser")} ${gray("in Claude Code")}`);
|
|
175
|
+
console.log();
|
|
678
176
|
}
|
|
679
177
|
|
|
680
|
-
export {
|
|
178
|
+
export { checkAgentBrowserInstalled, getInstallPath, installAgentBrowser, uninstallAgentBrowser };
|