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.
Files changed (109) hide show
  1. package/dist/chunks/agent-teams.mjs +1 -1
  2. package/dist/chunks/agent.mjs +1 -1
  3. package/dist/chunks/api-providers.mjs +1 -1
  4. package/dist/chunks/api.mjs +3 -3
  5. package/dist/chunks/auto-bootstrap.mjs +1 -1
  6. package/dist/chunks/auto-updater.mjs +1 -1
  7. package/dist/{shared/ccjk.Br91zBIG.mjs → chunks/banner.mjs} +52 -3
  8. package/dist/chunks/boost.mjs +2 -2
  9. package/dist/chunks/ccjk-agents.mjs +1 -1
  10. package/dist/chunks/ccjk-all.mjs +1 -1
  11. package/dist/chunks/ccjk-config.mjs +1 -1
  12. package/dist/chunks/ccjk-hooks.mjs +1 -1
  13. package/dist/chunks/ccjk-mcp.mjs +2 -2
  14. package/dist/chunks/ccjk-setup.mjs +1 -1
  15. package/dist/chunks/ccjk-skills.mjs +1 -1
  16. package/dist/chunks/ccr.mjs +10 -10
  17. package/dist/chunks/ccu.mjs +1 -1
  18. package/dist/chunks/check-updates.mjs +3 -3
  19. package/dist/chunks/claude-code-config-manager.mjs +7 -7
  20. package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
  21. package/dist/chunks/claude-config.mjs +3 -3
  22. package/dist/chunks/claude-wrapper.mjs +2 -2
  23. package/dist/chunks/codex-config-switch.mjs +2 -2
  24. package/dist/chunks/codex-provider-manager.mjs +2 -2
  25. package/dist/chunks/codex-uninstaller.mjs +2 -2
  26. package/dist/chunks/codex.mjs +5 -5
  27. package/dist/chunks/commands.mjs +88 -391
  28. package/dist/chunks/commands2.mjs +391 -88
  29. package/dist/chunks/completion.mjs +1 -1
  30. package/dist/chunks/config-consolidator.mjs +2 -2
  31. package/dist/chunks/config-switch.mjs +3 -3
  32. package/dist/chunks/config.mjs +5 -5
  33. package/dist/chunks/config2.mjs +410 -400
  34. package/dist/chunks/config3.mjs +400 -410
  35. package/dist/chunks/constants.mjs +1 -1
  36. package/dist/chunks/context.mjs +283 -1
  37. package/dist/chunks/dashboard.mjs +365 -0
  38. package/dist/chunks/doctor.mjs +4 -4
  39. package/dist/chunks/features.mjs +11 -11
  40. package/dist/chunks/fs-operations.mjs +1 -1
  41. package/dist/chunks/health-alerts.mjs +304 -0
  42. package/dist/chunks/health-check.mjs +532 -0
  43. package/dist/chunks/index.mjs +10 -177
  44. package/dist/chunks/index2.mjs +168 -1162
  45. package/dist/chunks/index3.mjs +1076 -910
  46. package/dist/chunks/index4.mjs +947 -137
  47. package/dist/chunks/index5.mjs +167 -635
  48. package/dist/chunks/index6.mjs +663 -0
  49. package/dist/chunks/init.mjs +19 -19
  50. package/dist/chunks/installer.mjs +649 -147
  51. package/dist/chunks/installer2.mjs +147 -649
  52. package/dist/chunks/interview.mjs +2 -2
  53. package/dist/chunks/marketplace.mjs +1 -1
  54. package/dist/chunks/mcp.mjs +4 -4
  55. package/dist/chunks/menu.mjs +16 -9
  56. package/dist/chunks/metrics-display.mjs +152 -0
  57. package/dist/chunks/migrator.mjs +1 -1
  58. package/dist/chunks/monitor.mjs +2 -2
  59. package/dist/chunks/notification.mjs +1 -1
  60. package/dist/chunks/onboarding.mjs +2 -2
  61. package/dist/chunks/package.mjs +1 -1
  62. package/dist/chunks/permission-manager.mjs +2 -2
  63. package/dist/chunks/permissions.mjs +1 -1
  64. package/dist/chunks/persistence-manager.mjs +781 -0
  65. package/dist/chunks/persistence.mjs +667 -0
  66. package/dist/chunks/platform.mjs +1 -1
  67. package/dist/chunks/plugin.mjs +1 -1
  68. package/dist/chunks/prompts.mjs +1 -1
  69. package/dist/chunks/providers.mjs +1 -1
  70. package/dist/chunks/quick-actions.mjs +321 -0
  71. package/dist/chunks/quick-setup.mjs +8 -8
  72. package/dist/chunks/silent-updater.mjs +1 -1
  73. package/dist/chunks/simple-config.mjs +2 -2
  74. package/dist/chunks/skill.mjs +1 -1
  75. package/dist/chunks/skills-sync.mjs +1 -1
  76. package/dist/chunks/skills.mjs +1 -1
  77. package/dist/chunks/slash-commands.mjs +208 -0
  78. package/dist/chunks/smart-defaults.mjs +1 -1
  79. package/dist/chunks/startup.mjs +1 -1
  80. package/dist/chunks/stats.mjs +1 -1
  81. package/dist/chunks/status.mjs +31 -2
  82. package/dist/chunks/team.mjs +1 -1
  83. package/dist/chunks/thinking.mjs +2 -2
  84. package/dist/chunks/uninstall.mjs +5 -5
  85. package/dist/chunks/update.mjs +7 -7
  86. package/dist/chunks/upgrade-manager.mjs +2 -2
  87. package/dist/chunks/version-checker.mjs +3 -3
  88. package/dist/chunks/vim.mjs +1 -1
  89. package/dist/cli.mjs +191 -21
  90. package/dist/i18n/locales/en/cli.json +14 -1
  91. package/dist/i18n/locales/en/common.json +27 -0
  92. package/dist/i18n/locales/en/context.json +54 -1
  93. package/dist/i18n/locales/en/dashboard.json +78 -0
  94. package/dist/i18n/locales/en/persistence.json +127 -0
  95. package/dist/i18n/locales/en/quick-actions.json +78 -0
  96. package/dist/i18n/locales/zh-CN/cli.json +14 -1
  97. package/dist/i18n/locales/zh-CN/common.json +27 -0
  98. package/dist/i18n/locales/zh-CN/context.json +54 -1
  99. package/dist/i18n/locales/zh-CN/dashboard.json +78 -0
  100. package/dist/i18n/locales/zh-CN/persistence.json +127 -0
  101. package/dist/i18n/locales/zh-CN/quick-actions.json +78 -0
  102. package/dist/index.mjs +2 -2
  103. package/dist/shared/{ccjk.DE91nClQ.mjs → ccjk.BKoi8-Hy.mjs} +1 -1
  104. package/dist/shared/{ccjk.Dpw86UX0.mjs → ccjk.CxtuJxaS.mjs} +1 -1
  105. package/dist/shared/{ccjk.ClzTOz9n.mjs → ccjk.DB2UYcq0.mjs} +5 -5
  106. package/dist/shared/{ccjk.Bndhan7G.mjs → ccjk.DfwJOEok.mjs} +1 -1
  107. package/dist/shared/{ccjk.DvIrK0wz.mjs → ccjk.DrMygfCF.mjs} +1 -1
  108. package/dist/shared/{ccjk.CmsW23FN.mjs → ccjk.IbImMVWM.mjs} +3 -3
  109. package/package.json +19 -19
@@ -1,178 +1,680 @@
1
- import { existsSync } from 'node:fs';
1
+ import * as nodeFs from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
- import { join } from 'node:path';
3
+ import process__default from 'node:process';
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 './index2.mjs';
10
+ import { updateClaudeCode } from './auto-updater.mjs';
11
+ import { exists } from './fs-operations.mjs';
12
+ import { f as findCommandPath, g as getPlatform, a as getHomebrewCommandPaths, b as isTermux, c as getTermuxPrefix, d as isWSL, e as getWSLInfo, w as wrapCommandWithSudo, h as commandExists, j 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';
5
25
 
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;
26
+ async function isClaudeCodeInstalled() {
27
+ return await commandExists("claude");
11
28
  }
12
- async function checkAgentBrowserInstalled() {
29
+ async function installClaudeCode(skipMethodSelection = false) {
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) {
13
172
  try {
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;
173
+ if (codeType === "claude-code") {
174
+ const { readMcpConfig } = await import('./claude-config.mjs').then(function (n) { return n.h; });
175
+ const config = readMcpConfig();
176
+ return config?.installMethod || null;
177
+ }
20
178
  } catch {
21
179
  }
22
- if (existsSync(BIN_PATH)) {
23
- return true;
180
+ return null;
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
+ }
24
222
  }
223
+ const spinner = ora(i18n.t("installation:uninstallingWith", { method, codeType: codeTypeName })).start();
25
224
  try {
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
- });
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 }));
32
276
  return true;
33
- } catch {
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;
34
283
  }
35
- return false;
36
284
  }
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;
285
+ async function setInstallMethod(method, codeType = "claude-code") {
286
+ try {
287
+ if (codeType === "claude-code") {
288
+ const { readMcpConfig, writeMcpConfig } = await import('./claude-config.mjs').then(function (n) { return n.h; });
289
+ let config = readMcpConfig();
290
+ if (!config) {
291
+ config = { mcpServers: {} };
292
+ }
293
+ config.installMethod = method === "npm" ? "npm-global" : method;
294
+ writeMcpConfig(config);
48
295
  }
296
+ } catch (error) {
297
+ console.error("Failed to set installMethod:", error);
49
298
  }
50
- const { execSync } = await import('node:child_process');
51
- const { mkdirSync } = await import('node:fs');
299
+ }
300
+ async function detectInstalledVersion(codeType) {
52
301
  try {
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
- `);
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
+ }
308
+ } catch {
309
+ }
310
+ return null;
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
+ }
327
+ }
328
+ function getInstallMethodOptions(codeType, recommendedMethods) {
329
+ const allMethods = ["npm", "homebrew", "curl", "powershell", "cmd"];
330
+ const platform = getPlatform();
331
+ const availableMethods = allMethods.filter((method) => {
332
+ if (codeType === "codex" && !["npm", "homebrew"].includes(method)) {
102
333
  return false;
103
334
  }
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
+ }
363
+ const response = await inquirer.prompt({
364
+ type: "list",
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();
378
+ try {
379
+ switch (method) {
380
+ case "npm": {
381
+ const packageName = codeType === "claude-code" ? "@anthropic-ai/claude-code" : "@openai/codex";
382
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", packageName, "--force"]);
383
+ if (usedSudo) {
384
+ spinner.info(i18n.t("installation:usingSudo"));
385
+ spinner.start();
386
+ }
387
+ await exec(command, args);
388
+ await setInstallMethod("npm", codeType);
389
+ break;
390
+ }
391
+ case "homebrew": {
392
+ if (codeType === "claude-code") {
393
+ await exec("brew", ["install", "--cask", "claude-code"]);
394
+ } else {
395
+ await exec("brew", ["install", "--cask", "codex"]);
396
+ }
397
+ await setInstallMethod("homebrew", codeType);
398
+ break;
399
+ }
400
+ case "curl": {
401
+ if (codeType === "claude-code") {
402
+ await exec("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"]);
403
+ } else {
404
+ spinner.stop();
405
+ return await executeInstallMethod("npm", codeType);
406
+ }
407
+ await setInstallMethod("curl", codeType);
408
+ break;
409
+ }
410
+ case "powershell": {
411
+ if (codeType === "claude-code") {
412
+ await exec("powershell", ["-Command", "irm https://claude.ai/install.ps1 | iex"]);
413
+ } else {
414
+ spinner.stop();
415
+ return await executeInstallMethod("npm", codeType);
416
+ }
417
+ await setInstallMethod("powershell", codeType);
418
+ break;
419
+ }
420
+ case "cmd": {
421
+ if (codeType === "claude-code") {
422
+ await exec("cmd", ["/c", "curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd"]);
423
+ } else {
424
+ spinner.stop();
425
+ return await executeInstallMethod("npm", codeType);
426
+ }
427
+ await setInstallMethod("cmd", codeType);
428
+ break;
429
+ }
430
+ default:
431
+ throw new Error(`Unsupported install method: ${method}`);
432
+ }
433
+ spinner.succeed(i18n.t("installation:installMethodSuccess", { method }));
434
+ const verification = await verifyInstallation(codeType);
435
+ displayVerificationResult(verification, codeType);
436
+ return true;
104
437
  } catch (error) {
105
- console.error(`
106
- ${red("\u2717")} Installation failed`);
438
+ spinner.fail(i18n.t("installation:installMethodFailed", { method }));
107
439
  if (error instanceof Error) {
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
- `);
440
+ console.error(ansis.gray(error.message));
441
+ }
442
+ return false;
443
+ }
444
+ }
445
+ async function handleInstallFailure(codeType, failedMethods) {
446
+ ensureI18nInitialized();
447
+ const response = await inquirer.prompt({
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) {
116
458
  return false;
117
459
  }
460
+ const success = await executeInstallMethod(newMethod, codeType);
461
+ if (success) {
462
+ return true;
463
+ }
464
+ return await handleInstallFailure(codeType, [...failedMethods, newMethod]);
118
465
  }
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');
466
+ async function isCommandInPath(command) {
125
467
  try {
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`);
468
+ const cmd = getPlatform() === "windows" ? "where" : "which";
469
+ const res = await exec(cmd, [command]);
470
+ return res.exitCode === 0;
471
+ } catch {
472
+ return false;
473
+ }
474
+ }
475
+ async function verifyInstallation(codeType) {
476
+ const command = codeType === "claude-code" ? "claude" : "codex";
477
+ const commandInPath = await isCommandInPath(command);
478
+ if (commandInPath) {
479
+ const version = await detectInstalledVersion(codeType);
480
+ return {
481
+ success: true,
482
+ commandPath: await findCommandPath(command),
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);
141
607
  } else {
142
- console.log(` ${gray("-")} No local files found`);
608
+ return {
609
+ success: false,
610
+ symlinkPath: null,
611
+ error: `File already exists at ${symlinkPath} and is not a symlink`
612
+ };
143
613
  }
144
- console.log(`
145
- ${green("\u2713")} ${bold("Agent Browser uninstalled successfully!")}
146
- `);
147
- return true;
148
614
  } catch (error) {
149
- console.error(`
150
- ${red("\u2717")} Uninstallation failed`);
151
- if (error instanceof Error) {
152
- console.error(` ${gray("Error:")} ${error.message}`);
615
+ if (error.code !== "ENOENT") {
616
+ return {
617
+ success: false,
618
+ symlinkPath: null,
619
+ error: `Failed to check existing file: ${error}`
620
+ };
153
621
  }
154
- return false;
622
+ }
623
+ try {
624
+ nodeFs.symlinkSync(sourcePath, symlinkPath);
625
+ return {
626
+ success: true,
627
+ symlinkPath
628
+ };
629
+ } catch (error) {
630
+ if (error.code === "EACCES") {
631
+ return {
632
+ success: false,
633
+ symlinkPath: null,
634
+ error: `Permission denied. Try running: sudo ln -sf ${sourcePath} ${symlinkPath}`
635
+ };
636
+ }
637
+ return {
638
+ success: false,
639
+ symlinkPath: null,
640
+ error: `Failed to create symlink: ${error.message}`
641
+ };
155
642
  }
156
643
  }
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();
644
+ function displayVerificationResult(result, codeType) {
645
+ ensureI18nInitialized();
646
+ const codeTypeName = codeType === "claude-code" ? i18n.t("common:claudeCode") : i18n.t("common:codex");
647
+ if (result.success) {
648
+ if (result.symlinkCreated) {
649
+ console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:verificationSuccess")}`));
650
+ console.log(ansis.gray(` ${i18n.t("installation:symlinkCreated", { path: result.commandPath })}`));
651
+ } else if (result.commandPath) {
652
+ console.log(ansis.green(`\u2714 ${codeTypeName} ${i18n.t("installation:verificationSuccess")}`));
653
+ console.log(ansis.gray(` ${i18n.t("installation:foundAtPath", { path: result.commandPath })}`));
654
+ }
655
+ if (result.version) {
656
+ console.log(ansis.gray(` ${i18n.t("installation:detectedVersion", { version: result.version })}`));
657
+ }
658
+ if (result.error === "PATH_UPDATE_NEEDED") {
659
+ console.log(ansis.yellow(`
660
+ \u26A0 ${i18n.t("installation:pathUpdateNeeded")}`));
661
+ console.log(ansis.gray(` ${i18n.t("installation:pathUpdateHint")}`));
662
+ console.log(ansis.green(` export PATH="$HOME/.local/bin:$PATH"`));
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
+ }
176
678
  }
177
679
 
178
- export { checkAgentBrowserInstalled, getInstallPath, installAgentBrowser, uninstallAgentBrowser };
680
+ export { createHomebrewSymlink, detectInstalledVersion, displayVerificationResult, executeInstallMethod, getInstallationStatus, handleInstallFailure, installClaudeCode, installCodex, isClaudeCodeInstalled, isCodexInstalled, selectInstallMethod, setInstallMethod, uninstallCodeTool, verifyInstallation };