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,178 +1,680 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as nodeFs from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
51
|
-
|
|
299
|
+
}
|
|
300
|
+
async function detectInstalledVersion(codeType) {
|
|
52
301
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
106
|
-
${red("\u2717")} Installation failed`);
|
|
438
|
+
spinner.fail(i18n.t("installation:installMethodFailed", { method }));
|
|
107
439
|
if (error instanceof Error) {
|
|
108
|
-
console.error(
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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 {
|
|
680
|
+
export { createHomebrewSymlink, detectInstalledVersion, displayVerificationResult, executeInstallMethod, getInstallationStatus, handleInstallFailure, installClaudeCode, installCodex, isClaudeCodeInstalled, isCodexInstalled, selectInstallMethod, setInstallMethod, uninstallCodeTool, verifyInstallation };
|