aico-cli 0.0.1 → 0.0.3
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/cli.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cac from 'cac';
|
|
3
3
|
import ansis from 'ansis';
|
|
4
|
-
import { J as readZcfConfig, K as inquirer, N as addNumbersToChoices, O as updateZcfConfig, P as getTranslation, h as SUPPORTED_LANGS, j as LANG_LABELS, k as AI_OUTPUT_LANGUAGES, Q as version, q as backupExistingConfig, r as copyConfigFiles, x as applyAiLanguageDirective, R as configureAiPersonality, C as CLAUDE_DIR,
|
|
4
|
+
import { J as readZcfConfig, K as inquirer, N as addNumbersToChoices, O as updateZcfConfig, P as getTranslation, h as SUPPORTED_LANGS, j as LANG_LABELS, k as AI_OUTPUT_LANGUAGES, Q as version, q as backupExistingConfig, r as copyConfigFiles, x as applyAiLanguageDirective, R as configureAiPersonality, C as CLAUDE_DIR, T as readJsonConfig, H as addCompletedOnboarding, S as SETTINGS_FILE, U as writeJsonConfig, o as openSettingsJson, b as importRecommendedPermissions, a as importRecommendedEnv, V as isWindows, z as readMcpConfig, G as fixWindowsMcpConfig, B as writeMcpConfig, W as selectMcpServices, D as backupMcpConfig, M as MCP_SERVICES, F as buildMcpServerConfig, E as mergeMcpServers, X as displayBanner, I as I18N, Y as selectAndInstallWorkflows, Z as handleExitPromptError, _ as handleGeneralError, $ as displayBannerWithInfo, a0 as readZcfConfigAsync, i as init, a1 as executeWithEscapeSupport, a2 as EscapeKeyPressed } from './shared/aico-cli.BlnVF1S3.mjs';
|
|
5
5
|
import inquirer$1 from 'inquirer';
|
|
6
6
|
import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
7
|
-
import { exec } from 'node:child_process';
|
|
7
|
+
import { exec, spawn } from 'node:child_process';
|
|
8
8
|
import { promisify } from 'node:util';
|
|
9
9
|
import dayjs from 'dayjs';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
@@ -103,120 +103,6 @@ async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfi
|
|
|
103
103
|
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function validateApiKey(apiKey, lang = "zh-CN") {
|
|
107
|
-
const i18n = getTranslation(lang);
|
|
108
|
-
if (!apiKey || apiKey.trim() === "") {
|
|
109
|
-
return {
|
|
110
|
-
isValid: false,
|
|
111
|
-
error: i18n.api.apiKeyValidation.empty
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
return { isValid: true };
|
|
115
|
-
}
|
|
116
|
-
function formatApiKeyDisplay(apiKey) {
|
|
117
|
-
if (!apiKey || apiKey.length < 12) {
|
|
118
|
-
return apiKey;
|
|
119
|
-
}
|
|
120
|
-
return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
|
|
124
|
-
let currentConfig = { ...existingConfig };
|
|
125
|
-
const latestConfig = getExistingApiConfig();
|
|
126
|
-
if (latestConfig) {
|
|
127
|
-
currentConfig = latestConfig;
|
|
128
|
-
}
|
|
129
|
-
const { item } = await inquirer.prompt({
|
|
130
|
-
type: "list",
|
|
131
|
-
name: "item",
|
|
132
|
-
message: i18n.api.selectModifyItems,
|
|
133
|
-
choices: addNumbersToChoices([
|
|
134
|
-
{ name: i18n.api.modifyApiUrl, value: "url" },
|
|
135
|
-
{ name: i18n.api.modifyApiKey, value: "key" },
|
|
136
|
-
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
137
|
-
])
|
|
138
|
-
});
|
|
139
|
-
if (!item) {
|
|
140
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (item === "url") {
|
|
144
|
-
const { url } = await inquirer.prompt({
|
|
145
|
-
type: "input",
|
|
146
|
-
name: "url",
|
|
147
|
-
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
148
|
-
default: currentConfig.url,
|
|
149
|
-
validate: (value) => {
|
|
150
|
-
if (!value) return i18n.api.urlRequired;
|
|
151
|
-
try {
|
|
152
|
-
new URL(value);
|
|
153
|
-
return true;
|
|
154
|
-
} catch {
|
|
155
|
-
return i18n.api.invalidUrl;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
if (url === void 0) {
|
|
160
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
currentConfig.url = url;
|
|
164
|
-
const savedConfig = configureApi(currentConfig);
|
|
165
|
-
if (savedConfig) {
|
|
166
|
-
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
167
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
|
|
168
|
-
}
|
|
169
|
-
} else if (item === "key") {
|
|
170
|
-
const authType = currentConfig.authType || "auth_token";
|
|
171
|
-
const keyMessage = authType === "auth_token" ? i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none) : i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none);
|
|
172
|
-
const { key } = await inquirer.prompt({
|
|
173
|
-
type: "input",
|
|
174
|
-
name: "key",
|
|
175
|
-
message: keyMessage,
|
|
176
|
-
validate: (value) => {
|
|
177
|
-
if (!value) {
|
|
178
|
-
return i18n.api.keyRequired;
|
|
179
|
-
}
|
|
180
|
-
const validation = validateApiKey(value, scriptLang);
|
|
181
|
-
if (!validation.isValid) {
|
|
182
|
-
return validation.error || i18n.api.invalidKeyFormat;
|
|
183
|
-
}
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
if (key === void 0) {
|
|
188
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
currentConfig.key = key;
|
|
192
|
-
const savedConfig = configureApi(currentConfig);
|
|
193
|
-
if (savedConfig) {
|
|
194
|
-
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
195
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
196
|
-
}
|
|
197
|
-
} else if (item === "authType") {
|
|
198
|
-
const { authType } = await inquirer.prompt({
|
|
199
|
-
type: "list",
|
|
200
|
-
name: "authType",
|
|
201
|
-
message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
|
|
202
|
-
choices: addNumbersToChoices([
|
|
203
|
-
{ name: "Auth Token (OAuth)", value: "auth_token" },
|
|
204
|
-
{ name: "API Key", value: "api_key" }
|
|
205
|
-
]),
|
|
206
|
-
default: currentConfig.authType === "api_key" ? 1 : 0
|
|
207
|
-
});
|
|
208
|
-
if (authType === void 0) {
|
|
209
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
currentConfig.authType = authType;
|
|
213
|
-
const savedConfig = configureApi(currentConfig);
|
|
214
|
-
if (savedConfig) {
|
|
215
|
-
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
216
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
106
|
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
221
107
|
const i18n = getTranslation(scriptLang);
|
|
222
108
|
const backupDir = backupExistingConfig();
|
|
@@ -688,142 +574,6 @@ function handleCancellation(scriptLang) {
|
|
|
688
574
|
const i18n = getTranslation(scriptLang);
|
|
689
575
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
690
576
|
}
|
|
691
|
-
async function configureApiFeature(scriptLang) {
|
|
692
|
-
const i18n = getTranslation(scriptLang);
|
|
693
|
-
const existingApiConfig = getExistingApiConfig();
|
|
694
|
-
if (existingApiConfig) {
|
|
695
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
696
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
697
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`));
|
|
698
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
699
|
-
`));
|
|
700
|
-
const { action } = await inquirer.prompt({
|
|
701
|
-
type: "list",
|
|
702
|
-
name: "action",
|
|
703
|
-
message: i18n.api.selectApiAction,
|
|
704
|
-
choices: addNumbersToChoices([
|
|
705
|
-
{ name: i18n.api.keepExistingConfig, value: "keep" },
|
|
706
|
-
{ name: i18n.api.modifyAllConfig, value: "modify-all" },
|
|
707
|
-
{ name: i18n.api.modifyPartialConfig, value: "modify-partial" },
|
|
708
|
-
{ name: i18n.api.useCcrProxy, value: "use-ccr" }
|
|
709
|
-
])
|
|
710
|
-
});
|
|
711
|
-
if (!action) {
|
|
712
|
-
handleCancellation(scriptLang);
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
if (action === "keep") {
|
|
716
|
-
console.log(ansis.green(`\u2714 ${i18n.api.keepExistingConfig}`));
|
|
717
|
-
try {
|
|
718
|
-
addCompletedOnboarding();
|
|
719
|
-
} catch (error) {
|
|
720
|
-
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
721
|
-
}
|
|
722
|
-
return;
|
|
723
|
-
} else if (action === "modify-partial") {
|
|
724
|
-
await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
|
|
725
|
-
return;
|
|
726
|
-
} else if (action === "use-ccr") {
|
|
727
|
-
const ccrInstalled = await isCcrInstalled();
|
|
728
|
-
if (!ccrInstalled) {
|
|
729
|
-
console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
|
|
730
|
-
await installCcr(scriptLang);
|
|
731
|
-
} else {
|
|
732
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
733
|
-
}
|
|
734
|
-
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
735
|
-
if (ccrConfigured) {
|
|
736
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
737
|
-
}
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
const { apiChoice } = await inquirer.prompt({
|
|
742
|
-
type: "list",
|
|
743
|
-
name: "apiChoice",
|
|
744
|
-
message: i18n.api.configureApi,
|
|
745
|
-
choices: addNumbersToChoices([
|
|
746
|
-
{
|
|
747
|
-
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
748
|
-
value: "auth_token",
|
|
749
|
-
short: i18n.api.useAuthToken
|
|
750
|
-
},
|
|
751
|
-
{
|
|
752
|
-
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
753
|
-
value: "api_key",
|
|
754
|
-
short: i18n.api.useApiKey
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
|
|
758
|
-
value: "ccr_proxy",
|
|
759
|
-
short: i18n.api.useCcrProxy
|
|
760
|
-
},
|
|
761
|
-
{ name: i18n.api.skipApi, value: "skip" }
|
|
762
|
-
])
|
|
763
|
-
});
|
|
764
|
-
if (!apiChoice || apiChoice === "skip") {
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
if (apiChoice === "ccr_proxy") {
|
|
768
|
-
const ccrInstalled = await isCcrInstalled();
|
|
769
|
-
if (!ccrInstalled) {
|
|
770
|
-
console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
|
|
771
|
-
await installCcr(scriptLang);
|
|
772
|
-
} else {
|
|
773
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
774
|
-
}
|
|
775
|
-
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
776
|
-
if (ccrConfigured) {
|
|
777
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
778
|
-
}
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
const { url } = await inquirer.prompt({
|
|
782
|
-
type: "input",
|
|
783
|
-
name: "url",
|
|
784
|
-
message: i18n.api.enterApiUrl,
|
|
785
|
-
validate: (value) => {
|
|
786
|
-
if (!value) return i18n.api.urlRequired;
|
|
787
|
-
try {
|
|
788
|
-
new URL(value);
|
|
789
|
-
return true;
|
|
790
|
-
} catch {
|
|
791
|
-
return i18n.api.invalidUrl;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
if (!url) {
|
|
796
|
-
handleCancellation(scriptLang);
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
const keyMessage = apiChoice === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
|
|
800
|
-
const { key } = await inquirer.prompt({
|
|
801
|
-
type: "input",
|
|
802
|
-
name: "key",
|
|
803
|
-
message: keyMessage,
|
|
804
|
-
validate: (value) => {
|
|
805
|
-
if (!value) {
|
|
806
|
-
return i18n.api.keyRequired;
|
|
807
|
-
}
|
|
808
|
-
const validation = validateApiKey(value, scriptLang);
|
|
809
|
-
if (!validation.isValid) {
|
|
810
|
-
return validation.error || i18n.api.invalidKeyFormat;
|
|
811
|
-
}
|
|
812
|
-
return true;
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
if (!key) {
|
|
816
|
-
handleCancellation(scriptLang);
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
const apiConfig = { url, key, authType: apiChoice };
|
|
820
|
-
const configuredApi = configureApi(apiConfig);
|
|
821
|
-
if (configuredApi) {
|
|
822
|
-
console.log(ansis.green(`\u2714 ${i18n.api.apiConfigSuccess}`));
|
|
823
|
-
console.log(ansis.gray(` URL: ${configuredApi.url}`));
|
|
824
|
-
console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
577
|
async function configureMcpFeature(scriptLang) {
|
|
828
578
|
const i18n = getTranslation(scriptLang);
|
|
829
579
|
if (isWindows()) {
|
|
@@ -951,6 +701,40 @@ async function configureEnvPermissionFeature(scriptLang) {
|
|
|
951
701
|
}
|
|
952
702
|
}
|
|
953
703
|
|
|
704
|
+
async function startClaudeCodeEditor(_lang) {
|
|
705
|
+
try {
|
|
706
|
+
console.log(ansis.cyan("\u{1F680} \u6B63\u5728\u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668..."));
|
|
707
|
+
const child = spawn("claude", ["--dangerously-skip-permissions"], {
|
|
708
|
+
stdio: "inherit",
|
|
709
|
+
// 继承父进程的 stdio,这样可以在当前终端中交互
|
|
710
|
+
cwd: process.cwd()
|
|
711
|
+
// 使用当前工作目录
|
|
712
|
+
});
|
|
713
|
+
await new Promise((resolve, reject) => {
|
|
714
|
+
child.on("close", (code) => {
|
|
715
|
+
if (code === 0) {
|
|
716
|
+
console.log(ansis.green("\u2705 \u4EE3\u7801\u7F16\u8F91\u5668\u5DF2\u9000\u51FA"));
|
|
717
|
+
resolve();
|
|
718
|
+
} else {
|
|
719
|
+
reject(new Error(`Claude \u8FDB\u7A0B\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${code}`));
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
child.on("error", (error) => {
|
|
723
|
+
reject(error);
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
} catch (error) {
|
|
727
|
+
console.error(ansis.red("\u274C \u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668\u5931\u8D25:"));
|
|
728
|
+
if (error.code === "ENOENT" || error.message?.includes("command not found")) {
|
|
729
|
+
console.error(ansis.yellow("\u{1F4A1} \u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 Claude Code:"));
|
|
730
|
+
console.error(ansis.gray(" npm install -g @anthropic-ai/claude-code"));
|
|
731
|
+
} else {
|
|
732
|
+
console.error(ansis.gray(` \u9519\u8BEF\u4FE1\u606F: ${error.message || error}`));
|
|
733
|
+
}
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
954
738
|
async function update(options = {}) {
|
|
955
739
|
try {
|
|
956
740
|
if (!options.skipBanner) {
|
|
@@ -962,7 +746,7 @@ async function update(options = {}) {
|
|
|
962
746
|
const configLang = options.configLang || "zh-CN";
|
|
963
747
|
const aiOutputLang = await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
|
|
964
748
|
console.log(ansis.cyan(`
|
|
965
|
-
${i18n.
|
|
749
|
+
${i18n.configuration.updatingPrompts}
|
|
966
750
|
`));
|
|
967
751
|
await updatePromptOnly(configLang, scriptLang, aiOutputLang);
|
|
968
752
|
await selectAndInstallWorkflows(configLang, scriptLang);
|
|
@@ -1010,14 +794,14 @@ async function showMainMenu() {
|
|
|
1010
794
|
},
|
|
1011
795
|
"5": {
|
|
1012
796
|
key: "5",
|
|
1013
|
-
labelKey: "
|
|
1014
|
-
processor: () =>
|
|
797
|
+
labelKey: "configureEnvPermission",
|
|
798
|
+
processor: () => configureEnvPermissionFeature(scriptLang),
|
|
1015
799
|
section: "\u57FA\u7840\u914D\u7F6E"
|
|
1016
800
|
},
|
|
1017
801
|
"6": {
|
|
1018
802
|
key: "6",
|
|
1019
|
-
labelKey: "
|
|
1020
|
-
processor: () =>
|
|
803
|
+
labelKey: "startCodeEditor",
|
|
804
|
+
processor: () => startClaudeCodeEditor(scriptLang),
|
|
1021
805
|
section: "\u57FA\u7840\u914D\u7F6E"
|
|
1022
806
|
}
|
|
1023
807
|
};
|
|
@@ -1067,7 +851,16 @@ async function showMainMenu() {
|
|
|
1067
851
|
} catch (error) {
|
|
1068
852
|
}
|
|
1069
853
|
}
|
|
1070
|
-
|
|
854
|
+
try {
|
|
855
|
+
await handleMenuContinuation(choice, i18n);
|
|
856
|
+
} catch (continuationError) {
|
|
857
|
+
if (continuationError.message === "EXIT_MENU") {
|
|
858
|
+
console.log(ansis.cyan(i18n.common.goodbye));
|
|
859
|
+
exitMenu = true;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
throw continuationError;
|
|
863
|
+
}
|
|
1071
864
|
}
|
|
1072
865
|
}
|
|
1073
866
|
}
|
|
@@ -1090,8 +883,7 @@ async function handleMenuContinuation(choice, i18n) {
|
|
|
1090
883
|
default: true
|
|
1091
884
|
});
|
|
1092
885
|
if (!shouldContinue) {
|
|
1093
|
-
|
|
1094
|
-
process.exit(0);
|
|
886
|
+
throw new Error("EXIT_MENU");
|
|
1095
887
|
}
|
|
1096
888
|
} catch (error) {
|
|
1097
889
|
if (error instanceof EscapeKeyPressed) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AICO_CONFIG_FILE, k as AI_OUTPUT_LANGUAGES, C as CLAUDE_DIR, e as CLAUDE_MD_FILE, f as ClAUDE_CONFIG_FILE, I as I18N, j as LANG_LABELS, L as LEGACY_AICO_CONFIG_FILE, M as MCP_SERVICES, S as SETTINGS_FILE, h as SUPPORTED_LANGS, H as addCompletedOnboarding, x as applyAiLanguageDirective, q as backupExistingConfig, D as backupMcpConfig, F as buildMcpServerConfig, d as cleanupPermissions, c as commandExists, s as configureApi, r as copyConfigFiles, p as ensureClaudeDir, G as fixWindowsMcpConfig, w as getExistingApiConfig, y as getMcpConfigPath, g as getPlatform, a as importRecommendedEnv, b as importRecommendedPermissions, i as init, n as installClaudeCode, l as isClaudeCodeInstalled, m as mergeAndCleanPermissions, t as mergeConfigs, E as mergeMcpServers, v as mergeSettingsFile, o as openSettingsJson, z as readMcpConfig, u as updateDefaultModel, B as writeMcpConfig } from './shared/aico-cli.
|
|
1
|
+
export { A as AICO_CONFIG_FILE, k as AI_OUTPUT_LANGUAGES, C as CLAUDE_DIR, e as CLAUDE_MD_FILE, f as ClAUDE_CONFIG_FILE, I as I18N, j as LANG_LABELS, L as LEGACY_AICO_CONFIG_FILE, M as MCP_SERVICES, S as SETTINGS_FILE, h as SUPPORTED_LANGS, H as addCompletedOnboarding, x as applyAiLanguageDirective, q as backupExistingConfig, D as backupMcpConfig, F as buildMcpServerConfig, d as cleanupPermissions, c as commandExists, s as configureApi, r as copyConfigFiles, p as ensureClaudeDir, G as fixWindowsMcpConfig, w as getExistingApiConfig, y as getMcpConfigPath, g as getPlatform, a as importRecommendedEnv, b as importRecommendedPermissions, i as init, n as installClaudeCode, l as isClaudeCodeInstalled, m as mergeAndCleanPermissions, t as mergeConfigs, E as mergeMcpServers, v as mergeSettingsFile, o as openSettingsJson, z as readMcpConfig, u as updateDefaultModel, B as writeMcpConfig } from './shared/aico-cli.BlnVF1S3.mjs';
|
|
2
2
|
import 'inquirer';
|
|
3
3
|
import 'ansis';
|
|
4
4
|
import 'node:fs';
|
|
@@ -65,6 +65,11 @@ ${i18n.common.returnToPrevious || "\u8FD4\u56DE\u4E0A\u4E00\u6B65"}`)
|
|
|
65
65
|
);
|
|
66
66
|
return false;
|
|
67
67
|
}
|
|
68
|
+
if (error.name === "ExitPromptError" || error.isTtyError || error.message?.includes("User force closed")) {
|
|
69
|
+
console.log(ansis.yellow(`
|
|
70
|
+
${i18n.common.cancelled || "\u64CD\u4F5C\u5DF2\u53D6\u6D88"}`));
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
68
73
|
throw error;
|
|
69
74
|
}
|
|
70
75
|
}
|
|
@@ -75,7 +80,7 @@ const inquirer = {
|
|
|
75
80
|
createPromptModule: inquirer$1.createPromptModule
|
|
76
81
|
};
|
|
77
82
|
|
|
78
|
-
const version = "0.0.
|
|
83
|
+
const version = "0.0.3";
|
|
79
84
|
|
|
80
85
|
const common$1 = {
|
|
81
86
|
// Basic
|
|
@@ -259,6 +264,7 @@ const menu$1 = {
|
|
|
259
264
|
configureModel: "\u914D\u7F6E\u9ED8\u8BA4\u6A21\u578B",
|
|
260
265
|
configureAiMemory: "\u914D\u7F6E Claude \u5168\u5C40\u8BB0\u5FC6",
|
|
261
266
|
configureEnvPermission: "\u5BFC\u5165\u63A8\u8350\u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650\u914D\u7F6E",
|
|
267
|
+
startCodeEditor: "\u542F\u52A8\u4EE3\u7801\u7F16\u8F91",
|
|
262
268
|
configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
|
|
263
269
|
ccrManagement: "CCR",
|
|
264
270
|
ccusage: "ccusage",
|
|
@@ -276,6 +282,7 @@ const menu$1 = {
|
|
|
276
282
|
configureModel: "\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\uFF08opus/sonnet\uFF09",
|
|
277
283
|
configureAiMemory: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00\u548C\u89D2\u8272\u98CE\u683C",
|
|
278
284
|
configureEnvPermission: "\u5BFC\u5165\u9690\u79C1\u4FDD\u62A4\u73AF\u5883\u53D8\u91CF\u548C\u7CFB\u7EDF\u6743\u9650\u914D\u7F6E",
|
|
285
|
+
startCodeEditor: "\u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668\u8FDB\u884C\u4EE3\u7801\u5F00\u53D1",
|
|
279
286
|
configureCcr: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
280
287
|
ccrManagement: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
|
|
281
288
|
ccusage: "Claude Code \u7528\u91CF\u5206\u6790",
|
|
@@ -290,7 +297,11 @@ const workflow$1 = {
|
|
|
290
297
|
workflowOption: {
|
|
291
298
|
featPlanUx: "\u529F\u80FD\u89C4\u5212\u548C UX \u8BBE\u8BA1 (feat + planner + ui-ux-designer)",
|
|
292
299
|
sixStepsWorkflow: "\u516D\u6B65\u5DE5\u4F5C\u6D41 (workflow)",
|
|
293
|
-
bmadWorkflow: "BMAD-Method \u6269\u5C55\u5B89\u88C5\u5668 (\u652F\u6301\u654F\u6377\u5F00\u53D1\u5DE5\u4F5C\u6D41)"
|
|
300
|
+
bmadWorkflow: "BMAD-Method \u6269\u5C55\u5B89\u88C5\u5668 (\u652F\u6301\u654F\u6377\u5F00\u53D1\u5DE5\u4F5C\u6D41)",
|
|
301
|
+
gitWorkflow: "\u667A\u80FD Git \u64CD\u4F5C (Git \u63D0\u4EA4\u3001\u56DE\u6EDA\u3001\u6E05\u7406\u5206\u652F)"
|
|
302
|
+
},
|
|
303
|
+
workflowDescription: {
|
|
304
|
+
gitWorkflow: "\u589E\u5F3A\u7248 Git \u547D\u4EE4\uFF0C\u652F\u6301\u667A\u80FD\u63D0\u4EA4\u3001\u5B89\u5168\u56DE\u6EDA\u548C\u81EA\u52A8\u5316\u5206\u652F\u6E05\u7406"
|
|
294
305
|
},
|
|
295
306
|
// BMAD workflow
|
|
296
307
|
bmadInitPrompt: "\u2728 \u8BF7\u5728\u9879\u76EE\u4E2D\u8FD0\u884C /bmad-init \u547D\u4EE4\u6765\u521D\u59CB\u5316\u6216\u66F4\u65B0 BMAD-Method \u6269\u5C55",
|
|
@@ -304,7 +315,8 @@ const workflow$1 = {
|
|
|
304
315
|
workflowInstallSuccess: "\u5DE5\u4F5C\u6D41\u5B89\u88C5\u6210\u529F",
|
|
305
316
|
workflowInstallError: "\u5DE5\u4F5C\u6D41\u5B89\u88C5\u51FA\u9519",
|
|
306
317
|
cleaningOldFiles: "\u6E05\u7406\u65E7\u7248\u672C\u6587\u4EF6",
|
|
307
|
-
removedOldFile: "\u5DF2\u5220\u9664\u65E7\u6587\u4EF6"
|
|
318
|
+
removedOldFile: "\u5DF2\u5220\u9664\u65E7\u6587\u4EF6",
|
|
319
|
+
failedToRemoveFile: "\u5220\u9664\u6587\u4EF6\u5931\u8D25"
|
|
308
320
|
};
|
|
309
321
|
|
|
310
322
|
const cli$1 = {
|
|
@@ -691,6 +703,7 @@ const menu = {
|
|
|
691
703
|
configureModel: "Configure default model",
|
|
692
704
|
configureAiMemory: "Configure Claude global memory",
|
|
693
705
|
configureEnvPermission: "Import recommended environment variables and permissions",
|
|
706
|
+
startCodeEditor: "Start Code Editor",
|
|
694
707
|
configureCcr: "Configure Model Proxy (CCR)",
|
|
695
708
|
ccrManagement: "CCR",
|
|
696
709
|
ccusage: "ccusage",
|
|
@@ -708,6 +721,7 @@ const menu = {
|
|
|
708
721
|
configureModel: "Set default model (opus/sonnet)",
|
|
709
722
|
configureAiMemory: "Configure AI output language and personality",
|
|
710
723
|
configureEnvPermission: "Import privacy protection environment variables and system permissions",
|
|
724
|
+
startCodeEditor: "Start Claude code editor (skip permission checks)",
|
|
711
725
|
configureCcr: "Configure Claude Code Router to use multiple AI models",
|
|
712
726
|
ccrManagement: "Configure Claude Code Router to use multiple AI models",
|
|
713
727
|
ccusage: "Claude Code usage analysis",
|
|
@@ -722,7 +736,11 @@ const workflow = {
|
|
|
722
736
|
workflowOption: {
|
|
723
737
|
featPlanUx: "Feature Planning and UX Design (feat + planner + ui-ux-designer)",
|
|
724
738
|
sixStepsWorkflow: "Six Steps Workflow (workflow)",
|
|
725
|
-
bmadWorkflow: "BMAD-Method Extension Installer (Agile Development Workflow)"
|
|
739
|
+
bmadWorkflow: "BMAD-Method Extension Installer (Agile Development Workflow)",
|
|
740
|
+
gitWorkflow: "Smart Git Operations (Git Commit, Rollback, Clean Branches)"
|
|
741
|
+
},
|
|
742
|
+
workflowDescription: {
|
|
743
|
+
gitWorkflow: "Enhanced Git commands for smart commits, safe rollbacks, and automated branch cleanup"
|
|
726
744
|
},
|
|
727
745
|
// BMAD workflow
|
|
728
746
|
bmadInitPrompt: "\u2728 Please run /bmad-init command in your project to initialize or update BMAD-Method extension",
|
|
@@ -736,7 +754,8 @@ const workflow = {
|
|
|
736
754
|
workflowInstallSuccess: "workflow installed successfully",
|
|
737
755
|
workflowInstallError: "workflow installation had errors",
|
|
738
756
|
cleaningOldFiles: "Cleaning up old version files",
|
|
739
|
-
removedOldFile: "Removed old file"
|
|
757
|
+
removedOldFile: "Removed old file",
|
|
758
|
+
failedToRemoveFile: "Failed to remove"
|
|
740
759
|
};
|
|
741
760
|
|
|
742
761
|
const cli = {
|
|
@@ -1255,7 +1274,7 @@ function updateZcfConfig(updates) {
|
|
|
1255
1274
|
const AI_PERSONALITIES = [
|
|
1256
1275
|
{
|
|
1257
1276
|
id: "professional",
|
|
1258
|
-
name: { "zh-CN": "\u4E13\u4E1A\
|
|
1277
|
+
name: { "zh-CN": "\u4E13\u4E1A\u7A0B\u5E8F\u5458\u6A21\u5F0F", en: "Professional Assistant(Default)" },
|
|
1259
1278
|
directive: {
|
|
1260
1279
|
"zh-CN": "\u4F60\u662F\u4E00\u540D\u7ECF\u9A8C\u4E30\u5BCC\u7684[\u4E13\u4E1A\u9886\u57DF\uFF0C\u4F8B\u5982\uFF1A\u8F6F\u4EF6\u5F00\u53D1\u5DE5\u7A0B\u5E08 / \u7CFB\u7EDF\u8BBE\u8BA1\u5E08 / \u4EE3\u7801\u67B6\u6784\u5E08]\uFF0C\u4E13\u6CE8\u4E8E\u6784\u5EFA[\u6838\u5FC3\u7279\u957F\uFF0C\u4F8B\u5982\uFF1A\u9AD8\u6027\u80FD / \u53EF\u7EF4\u62A4 / \u5065\u58EE / \u9886\u57DF\u9A71\u52A8]\u7684\u89E3\u51B3\u65B9\u6848\u3002",
|
|
1261
1280
|
en: "You are an experienced [professional domain, e.g., Software Development Engineer / System Designer / Code Architect], specializing in building [core strengths, e.g., high-performance / maintainable / robust / domain-driven] solutions."
|
|
@@ -1263,7 +1282,7 @@ const AI_PERSONALITIES = [
|
|
|
1263
1282
|
},
|
|
1264
1283
|
{
|
|
1265
1284
|
id: "friendly",
|
|
1266
|
-
name: { "zh-CN": "\
|
|
1285
|
+
name: { "zh-CN": "\u7ED3\u4F34\u7F16\u7A0B\u6A21\u5F0F", en: "Friendly Assistant" },
|
|
1267
1286
|
directive: {
|
|
1268
1287
|
"zh-CN": "\u4F60\u662F\u4E00\u4F4D\u53CB\u597D\u3001\u8010\u5FC3\u3001\u5584\u4E8E\u89E3\u91CA\u7684\u7F16\u7A0B\u52A9\u624B\u3002\u4F1A\u7528\u901A\u4FD7\u6613\u61C2\u7684\u65B9\u5F0F\u89E3\u91CA\u590D\u6742\u6982\u5FF5\uFF0C\u5E76\u7ECF\u5E38\u7ED9\u4E88\u9F13\u52B1\u3002",
|
|
1269
1288
|
en: "You are a friendly, patient, and explanatory programming assistant. You explain complex concepts in easy-to-understand ways and often provide encouragement."
|
|
@@ -1271,16 +1290,11 @@ const AI_PERSONALITIES = [
|
|
|
1271
1290
|
},
|
|
1272
1291
|
{
|
|
1273
1292
|
id: "mentor",
|
|
1274
|
-
name: { "zh-CN": "\u5BFC\
|
|
1293
|
+
name: { "zh-CN": "\u7A0B\u5E8F\u5458\u5927\u725B\u6307\u5BFC\u6A21\u5F0F", en: "Mentor Mode" },
|
|
1275
1294
|
directive: {
|
|
1276
1295
|
"zh-CN": "\u4F60\u662F\u4E00\u4F4D\u7ECF\u9A8C\u4E30\u5BCC\u7684\u7F16\u7A0B\u5BFC\u5E08\u3002\u4E0D\u4EC5\u63D0\u4F9B\u89E3\u51B3\u65B9\u6848\uFF0C\u8FD8\u4F1A\u89E3\u91CA\u80CC\u540E\u7684\u539F\u7406\uFF0C\u5F15\u5BFC\u7528\u6237\u601D\u8003\uFF0C\u57F9\u517B\u72EC\u7ACB\u89E3\u51B3\u95EE\u9898\u7684\u80FD\u529B\u3002",
|
|
1277
1296
|
en: "You are an experienced programming mentor. You not only provide solutions but also explain the principles behind them, guide users to think, and cultivate their ability to solve problems independently."
|
|
1278
1297
|
}
|
|
1279
|
-
},
|
|
1280
|
-
{
|
|
1281
|
-
id: "custom",
|
|
1282
|
-
name: { "zh-CN": "\u81EA\u5B9A\u4E49", en: "Custom" },
|
|
1283
|
-
directive: { "zh-CN": "", en: "" }
|
|
1284
1298
|
}
|
|
1285
1299
|
];
|
|
1286
1300
|
function getExistingPersonality() {
|
|
@@ -1296,9 +1310,15 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1296
1310
|
if (showExisting && existingPersonality) {
|
|
1297
1311
|
const personalityInfo = getPersonalityInfo(existingPersonality);
|
|
1298
1312
|
if (personalityInfo) {
|
|
1299
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`));
|
|
1300
1313
|
console.log(
|
|
1301
|
-
|
|
1314
|
+
"\n" + ansis.blue(
|
|
1315
|
+
`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`
|
|
1316
|
+
)
|
|
1317
|
+
);
|
|
1318
|
+
console.log(
|
|
1319
|
+
ansis.gray(
|
|
1320
|
+
` ${i18n.configuration.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`
|
|
1321
|
+
)
|
|
1302
1322
|
);
|
|
1303
1323
|
const { modify } = await inquirer.prompt({
|
|
1304
1324
|
type: "confirm",
|
|
@@ -1307,7 +1327,11 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1307
1327
|
default: false
|
|
1308
1328
|
});
|
|
1309
1329
|
if (!modify) {
|
|
1310
|
-
console.log(
|
|
1330
|
+
console.log(
|
|
1331
|
+
ansis.green(
|
|
1332
|
+
`\u2714 ${i18n.configuration.keepPersonality || "Keeping existing personality"}`
|
|
1333
|
+
)
|
|
1334
|
+
);
|
|
1311
1335
|
return;
|
|
1312
1336
|
}
|
|
1313
1337
|
}
|
|
@@ -1316,11 +1340,17 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1316
1340
|
type: "list",
|
|
1317
1341
|
name: "personality",
|
|
1318
1342
|
message: i18n.configuration.selectAiPersonality || "Select AI personality",
|
|
1319
|
-
choices: addNumbersToChoices(
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1343
|
+
choices: addNumbersToChoices(
|
|
1344
|
+
AI_PERSONALITIES.map((p) => ({
|
|
1345
|
+
name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(
|
|
1346
|
+
p.directive[scriptLang].substring(0, 50) + "..."
|
|
1347
|
+
)}` : `${p.name[scriptLang]} - ${ansis.gray(
|
|
1348
|
+
i18n.configuration.customPersonalityHint || "Define your own personality"
|
|
1349
|
+
)}`,
|
|
1350
|
+
value: p.id,
|
|
1351
|
+
short: p.name[scriptLang]
|
|
1352
|
+
}))
|
|
1353
|
+
),
|
|
1324
1354
|
default: existingPersonality ? AI_PERSONALITIES.findIndex((p) => p.id === existingPersonality) : 0
|
|
1325
1355
|
});
|
|
1326
1356
|
if (!personality) {
|
|
@@ -1348,7 +1378,11 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
|
|
|
1348
1378
|
}
|
|
1349
1379
|
await applyPersonalityDirective(directive);
|
|
1350
1380
|
updateZcfConfig({ aiPersonality: personality });
|
|
1351
|
-
console.log(
|
|
1381
|
+
console.log(
|
|
1382
|
+
ansis.green(
|
|
1383
|
+
`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`
|
|
1384
|
+
)
|
|
1385
|
+
);
|
|
1352
1386
|
}
|
|
1353
1387
|
async function applyPersonalityDirective(directive) {
|
|
1354
1388
|
try {
|
|
@@ -1357,7 +1391,12 @@ async function applyPersonalityDirective(directive) {
|
|
|
1357
1391
|
} catch (error) {
|
|
1358
1392
|
const lang = readZcfConfig()?.preferredLang || "en";
|
|
1359
1393
|
const errorI18n = getTranslation(lang);
|
|
1360
|
-
console.error(
|
|
1394
|
+
console.error(
|
|
1395
|
+
ansis.red(
|
|
1396
|
+
errorI18n.configuration.failedToApplyPersonality || "Failed to apply personality"
|
|
1397
|
+
),
|
|
1398
|
+
error
|
|
1399
|
+
);
|
|
1361
1400
|
}
|
|
1362
1401
|
}
|
|
1363
1402
|
|
|
@@ -1367,19 +1406,19 @@ function displayBanner(subtitle) {
|
|
|
1367
1406
|
const paddedSubtitle = subtitleText.padEnd(60, " ");
|
|
1368
1407
|
console.log(
|
|
1369
1408
|
ansis.cyan.bold(`
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1409
|
+
|
|
1410
|
+
|
|
1411
|
+
\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
1412
|
+
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557
|
|
1413
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
1414
|
+
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
1415
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
1416
|
+
\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1417
|
+
|
|
1418
|
+
${ansis.white.bold("\u667A\u80FD\u8F6F\u4EF6\u661F\u5DE5\u5382")}
|
|
1419
|
+
${ansis.gray(paddedSubtitle)}
|
|
1420
|
+
|
|
1421
|
+
|
|
1383
1422
|
`)
|
|
1384
1423
|
);
|
|
1385
1424
|
}
|
|
@@ -1393,10 +1432,10 @@ function handleExitPromptError(error) {
|
|
|
1393
1432
|
const zcfConfig = readZcfConfig();
|
|
1394
1433
|
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
1395
1434
|
const i18n = getTranslation(defaultLang);
|
|
1396
|
-
console.log(ansis.
|
|
1397
|
-
${i18n.common.
|
|
1435
|
+
console.log(ansis.yellow(`
|
|
1436
|
+
${i18n.common.cancelled || "\u64CD\u4F5C\u5DF2\u53D6\u6D88"}
|
|
1398
1437
|
`));
|
|
1399
|
-
|
|
1438
|
+
return true;
|
|
1400
1439
|
}
|
|
1401
1440
|
if (error instanceof EscapeKeyPressed) {
|
|
1402
1441
|
return true;
|
|
@@ -1929,6 +1968,18 @@ const WORKFLOW_CONFIGS = [
|
|
|
1929
1968
|
autoInstallAgents: false,
|
|
1930
1969
|
category: "bmad",
|
|
1931
1970
|
outputDir: "bmad"
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
id: "gitWorkflow",
|
|
1974
|
+
nameKey: "workflowOption.gitWorkflow",
|
|
1975
|
+
descriptionKey: "workflowDescription.gitWorkflow",
|
|
1976
|
+
defaultSelected: true,
|
|
1977
|
+
order: 4,
|
|
1978
|
+
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md"],
|
|
1979
|
+
agents: [],
|
|
1980
|
+
autoInstallAgents: false,
|
|
1981
|
+
category: "git",
|
|
1982
|
+
outputDir: "git"
|
|
1932
1983
|
}
|
|
1933
1984
|
];
|
|
1934
1985
|
function getWorkflowConfig(workflowId) {
|