opencrush 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +131 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -245027,118 +245027,46 @@ async function runSetupWizard() {
245027
245027
  console.log(source_default.gray("This will create a .env file with your settings.\n"));
245028
245028
  const region = detectRegion();
245029
245029
  const isCN = region === "cn";
245030
- step(1, isCN ? "\u9009\u62E9 AI \u5927\u8111" : "Choose your AI brain");
245031
- if (isCN) {
245032
- console.log(source_default.cyan(" \u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u65F6\u533A \u2014 \u4F18\u5148\u663E\u793A\u56FD\u5185\u53EF\u76F4\u8FDE\u7684\u6A21\u578B\n"));
245033
- } else {
245034
- console.log(source_default.gray(" Your companion needs an AI to think. All providers below work.\n"));
245035
- }
245036
- const cnFirst = PROVIDER_INFO.filter((p2) => !p2.requiresVPN && !p2.isLocal);
245037
- const intl = PROVIDER_INFO.filter((p2) => p2.requiresVPN);
245038
- const local = PROVIDER_INFO.filter((p2) => p2.isLocal);
245039
- const orderedProviders = isCN ? [...cnFirst, ...intl, ...local] : [PROVIDER_INFO.find((p2) => p2.id === "anthropic"), PROVIDER_INFO.find((p2) => p2.id === "openai"), ...cnFirst, ...local];
245040
- const { llmProvider } = await esm_default12.prompt([{
245041
- type: "list",
245042
- name: "llmProvider",
245043
- message: isCN ? "\u9009\u62E9 AI \u6A21\u578B\u63D0\u4F9B\u5546:" : "Which AI provider?",
245044
- choices: orderedProviders.map((p2) => ({
245045
- name: `${p2.emoji} ${p2.name}
245046
- ${source_default.gray(isCN ? p2.taglineCN : p2.tagline)}`,
245047
- value: p2.id,
245048
- short: p2.name
245049
- }))
245050
- }]);
245051
- const envValues = { LLM_PROVIDER: llmProvider };
245052
- const providerInfo = getProviderInfo(llmProvider);
245053
- if (llmProvider !== "ollama") {
245054
- const keyUrl = isCN ? providerInfo.keyUrlCN : providerInfo.keyUrl;
245055
- console.log(source_default.yellow(`
245056
- \u{1F449} ${isCN ? "\u83B7\u53D6 API Key: " : "Get API key: "}${keyUrl}`));
245057
- await openBrowserPrompt(keyUrl, isCN);
245058
- if (llmProvider === "anthropic") {
245059
- console.log(source_default.gray(
245060
- isCN ? ' 1. \u6CE8\u518C\u8D26\u53F7 \u2192 API Keys \u2192 Create Key\n 2. \u590D\u5236\u4EE5 "sk-ant-" \u5F00\u5934\u7684\u5BC6\u94A5\n' : ' 1. Create account \u2192 API Keys \u2192 Create Key\n 2. Copy the key (starts with "sk-ant-")\n'
245061
- ));
245062
- } else if (llmProvider === "openai") {
245063
- console.log(source_default.gray(
245064
- isCN ? " 1. \u6CE8\u518C \u2192 API Keys \u2192 Create new secret key\n" : " 1. Sign up \u2192 API Keys \u2192 Create new secret key\n"
245065
- ));
245066
- } else if (llmProvider === "deepseek") {
245067
- console.log(source_default.gray(
245068
- isCN ? " 1. \u6CE8\u518C \u2192 API Keys \u2192 \u521B\u5EFA API Key\n 2. \u65B0\u7528\u6237\u6709\u514D\u8D39\u989D\u5EA6\n" : " 1. Sign up \u2192 API Keys \u2192 Create key\n 2. New users get free credits\n"
245069
- ));
245070
- } else if (llmProvider === "qwen") {
245071
- console.log(source_default.gray(
245072
- isCN ? " 1. \u767B\u5F55\u963F\u91CC\u4E91\u63A7\u5236\u53F0 \u2192 DashScope \u2192 API-KEY\u7BA1\u7406 \u2192 \u521B\u5EFA\n" : " 1. Aliyun console \u2192 DashScope \u2192 API-KEY management \u2192 Create\n"
245073
- ));
245074
- } else if (llmProvider === "kimi") {
245075
- console.log(source_default.gray(
245076
- isCN ? " 1. \u6CE8\u518C\u6708\u4E4B\u6697\u9762 \u2192 \u63A7\u5236\u53F0 \u2192 API Keys \u2192 \u65B0\u5EFA\n" : " 1. Sign up at Moonshot \u2192 Console \u2192 API Keys \u2192 New\n"
245077
- ));
245078
- } else if (llmProvider === "zhipu") {
245079
- console.log(source_default.gray(
245080
- isCN ? " 1. \u6CE8\u518C\u667A\u8C31\u5F00\u653E\u5E73\u53F0 \u2192 \u4E2A\u4EBA\u4E2D\u5FC3 \u2192 API Keys \u2192 \u6DFB\u52A0\n 2. \u65B0\u7528\u6237\u8D60\u9001\u514D\u8D39 tokens\n" : " 1. Sign up at open.bigmodel.cn \u2192 API Keys \u2192 Add\n 2. Free tokens on signup\n"
245081
- ));
245082
- } else if (llmProvider === "minimax") {
245083
- console.log(source_default.gray(
245084
- isCN ? " 1. \u6CE8\u518C MiniMax \u5F00\u653E\u5E73\u53F0 \u2192 \u8D26\u53F7\u8BBE\u7F6E \u2192 \u63A5\u53E3\u5BC6\u94A5\n" : " 1. Sign up at platform.minimaxi.com \u2192 Account \u2192 API Keys\n"
245085
- ));
245086
- }
245087
- const { apiKey } = await esm_default12.prompt([{
245088
- type: "password",
245089
- name: "apiKey",
245090
- message: isCN ? `\u7C98\u8D34\u4F60\u7684 ${providerInfo.name} API Key:` : `Paste your ${providerInfo.name} API key:`,
245091
- mask: "*",
245092
- validate: (v2) => {
245093
- if (!v2.trim()) return isCN ? "\u8BF7\u8F93\u5165 API Key" : "API key required";
245094
- if (providerInfo.keyPrefix && !v2.startsWith(providerInfo.keyPrefix)) {
245095
- return isCN ? `Key \u683C\u5F0F\u4E0D\u5BF9\uFF0C\u5E94\u4EE5 "${providerInfo.keyPrefix}" \u5F00\u5934` : `Should start with "${providerInfo.keyPrefix}"`;
245096
- }
245097
- return true;
245098
- }
245099
- }]);
245100
- envValues[providerInfo.envKey] = apiKey;
245101
- }
245102
- if (llmProvider === "ollama") {
245103
- console.log(source_default.yellow(
245104
- isCN ? "\n \u786E\u4FDD Ollama \u5DF2\u8FD0\u884C: https://ollama.ai" : "\n Make sure Ollama is running: https://ollama.ai"
245105
- ));
245106
- console.log(source_default.gray(
245107
- isCN ? " \u8FD0\u884C: ollama pull qwen2.5:7b\n" : " Run: ollama pull qwen2.5:7b\n"
245108
- ));
245109
- envValues.OLLAMA_BASE_URL = "http://localhost:11434";
245110
- envValues.OLLAMA_MODEL = "qwen2.5:7b";
245111
- }
245112
- step(2, isCN ? "\u4F60\u7684\u4F34\u4FA3" : "Your companion");
245030
+ step(1, isCN ? "\u9047\u89C1\u4F60\u7684\u4F34\u4FA3" : "Meet your companion");
245031
+ console.log(source_default.gray(
245032
+ isCN ? " \u9009\u62E9\u4E00\u4E2A\u5DF2\u6709\u7684\u4F34\u4FA3\uFF0C\u6216\u8005\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684\u3002\n" : " Pick an existing companion or create a new one.\n"
245033
+ ));
245113
245034
  const characters = getExistingCharacters();
245114
245035
  let characterName;
245036
+ let characterCreatedNew = false;
245115
245037
  if (characters.length > 0) {
245116
245038
  const { characterChoice } = await esm_default12.prompt([{
245117
245039
  type: "list",
245118
245040
  name: "characterChoice",
245119
- message: "Use an existing companion or create a new one?",
245041
+ message: isCN ? "\u9009\u62E9\u4F60\u7684\u4F34\u4FA3:" : "Choose your companion:",
245120
245042
  choices: [
245121
- ...characters.map((c2) => ({ name: `\u2728 ${c2} (existing)`, value: c2 })),
245122
- { name: "\u2795 Create a new companion", value: "__new__" }
245043
+ ...characters.map((c2) => {
245044
+ const preview = getCharacterPreview(c2);
245045
+ return {
245046
+ name: `\u2728 ${c2}${preview ? source_default.gray(" \xB7 " + preview) : ""}`,
245047
+ value: c2,
245048
+ short: c2
245049
+ };
245050
+ }),
245051
+ { name: "\u2795 Create a new companion", value: "__new__", short: "New" }
245123
245052
  ]
245124
245053
  }]);
245125
245054
  if (characterChoice === "__new__") {
245126
- const apiKey = envValues[providerInfo.envKey];
245127
- const created = await createCharacterFlow(apiKey, llmProvider);
245055
+ const created = await createCharacterFlow();
245128
245056
  characterName = created.folderName;
245129
- await runTestChat(characterName, apiKey, llmProvider);
245057
+ characterCreatedNew = true;
245130
245058
  } else {
245131
245059
  characterName = characterChoice;
245060
+ maybeOpenCard(characterName);
245132
245061
  }
245133
245062
  } else {
245134
- console.log(source_default.gray(" No companions yet \u2014 let's create one!\n"));
245135
- const apiKey = envValues[providerInfo.envKey];
245136
- const created = await createCharacterFlow(apiKey, llmProvider);
245063
+ console.log(source_default.gray(isCN ? " \u8FD8\u6CA1\u6709\u4F34\u4FA3 \u2014 \u6765\u521B\u5EFA\u4E00\u4E2A\uFF01\n" : " No companions yet \u2014 let's create one!\n"));
245064
+ const created = await createCharacterFlow();
245137
245065
  characterName = created.folderName;
245138
- await runTestChat(characterName, apiKey, llmProvider);
245066
+ characterCreatedNew = true;
245139
245067
  }
245140
- envValues.CHARACTER_NAME = characterName;
245141
- step(3, isCN ? "\u9009\u62E9\u804A\u5929\u5E73\u53F0" : "Where do you want to chat?");
245068
+ const envValues = { CHARACTER_NAME: characterName };
245069
+ step(2, isCN ? "\u9009\u62E9\u804A\u5929\u5E73\u53F0" : "Where do you want to chat?");
245142
245070
  const { platforms } = await esm_default12.prompt([{
245143
245071
  type: "checkbox",
245144
245072
  name: "platforms",
@@ -245225,6 +245153,91 @@ async function runSetupWizard() {
245225
245153
  console.log(source_default.cyan("\n \u2139\uFE0F WhatsApp: No token needed! A QR code will appear when you start."));
245226
245154
  console.log(source_default.gray(" You'll scan it with WhatsApp on your phone (Linked Devices)."));
245227
245155
  }
245156
+ step(3, isCN ? "\u9009\u62E9 AI \u5927\u8111" : "Choose your AI brain");
245157
+ if (isCN) {
245158
+ console.log(source_default.cyan(" \u68C0\u6D4B\u5230\u4E2D\u56FD\u5927\u9646\u65F6\u533A \u2014 \u4F18\u5148\u663E\u793A\u56FD\u5185\u53EF\u76F4\u8FDE\u7684\u6A21\u578B\n"));
245159
+ } else {
245160
+ console.log(source_default.gray(" Your companion needs an AI to think. All providers below work.\n"));
245161
+ }
245162
+ const cnFirst = PROVIDER_INFO.filter((p2) => !p2.requiresVPN && !p2.isLocal);
245163
+ const intl = PROVIDER_INFO.filter((p2) => p2.requiresVPN);
245164
+ const local = PROVIDER_INFO.filter((p2) => p2.isLocal);
245165
+ const orderedProviders = isCN ? [...cnFirst, ...intl, ...local] : [PROVIDER_INFO.find((p2) => p2.id === "anthropic"), PROVIDER_INFO.find((p2) => p2.id === "openai"), ...cnFirst, ...local];
245166
+ const { llmProvider } = await esm_default12.prompt([{
245167
+ type: "list",
245168
+ name: "llmProvider",
245169
+ message: isCN ? "\u9009\u62E9 AI \u6A21\u578B\u63D0\u4F9B\u5546:" : "Which AI provider?",
245170
+ choices: orderedProviders.map((p2) => ({
245171
+ name: `${p2.emoji} ${p2.name}
245172
+ ${source_default.gray(isCN ? p2.taglineCN : p2.tagline)}`,
245173
+ value: p2.id,
245174
+ short: p2.name
245175
+ }))
245176
+ }]);
245177
+ envValues.LLM_PROVIDER = llmProvider;
245178
+ const providerInfo = getProviderInfo(llmProvider);
245179
+ if (llmProvider !== "ollama") {
245180
+ const keyUrl = isCN ? providerInfo.keyUrlCN : providerInfo.keyUrl;
245181
+ console.log(source_default.yellow(`
245182
+ \u{1F449} ${isCN ? "\u83B7\u53D6 API Key: " : "Get API key: "}${keyUrl}`));
245183
+ await openBrowserPrompt(keyUrl, isCN);
245184
+ if (llmProvider === "anthropic") {
245185
+ console.log(source_default.gray(
245186
+ isCN ? ' 1. \u6CE8\u518C\u8D26\u53F7 \u2192 API Keys \u2192 Create Key\n 2. \u590D\u5236\u4EE5 "sk-ant-" \u5F00\u5934\u7684\u5BC6\u94A5\n' : ' 1. Create account \u2192 API Keys \u2192 Create Key\n 2. Copy the key (starts with "sk-ant-")\n'
245187
+ ));
245188
+ } else if (llmProvider === "openai") {
245189
+ console.log(source_default.gray(
245190
+ isCN ? " 1. \u6CE8\u518C \u2192 API Keys \u2192 Create new secret key\n" : " 1. Sign up \u2192 API Keys \u2192 Create new secret key\n"
245191
+ ));
245192
+ } else if (llmProvider === "deepseek") {
245193
+ console.log(source_default.gray(
245194
+ isCN ? " 1. \u6CE8\u518C \u2192 API Keys \u2192 \u521B\u5EFA API Key\n 2. \u65B0\u7528\u6237\u6709\u514D\u8D39\u989D\u5EA6\n" : " 1. Sign up \u2192 API Keys \u2192 Create key\n 2. New users get free credits\n"
245195
+ ));
245196
+ } else if (llmProvider === "qwen") {
245197
+ console.log(source_default.gray(
245198
+ isCN ? " 1. \u767B\u5F55\u963F\u91CC\u4E91\u63A7\u5236\u53F0 \u2192 DashScope \u2192 API-KEY\u7BA1\u7406 \u2192 \u521B\u5EFA\n" : " 1. Aliyun console \u2192 DashScope \u2192 API-KEY management \u2192 Create\n"
245199
+ ));
245200
+ } else if (llmProvider === "kimi") {
245201
+ console.log(source_default.gray(
245202
+ isCN ? " 1. \u6CE8\u518C\u6708\u4E4B\u6697\u9762 \u2192 \u63A7\u5236\u53F0 \u2192 API Keys \u2192 \u65B0\u5EFA\n" : " 1. Sign up at Moonshot \u2192 Console \u2192 API Keys \u2192 New\n"
245203
+ ));
245204
+ } else if (llmProvider === "zhipu") {
245205
+ console.log(source_default.gray(
245206
+ isCN ? " 1. \u6CE8\u518C\u667A\u8C31\u5F00\u653E\u5E73\u53F0 \u2192 \u4E2A\u4EBA\u4E2D\u5FC3 \u2192 API Keys \u2192 \u6DFB\u52A0\n 2. \u65B0\u7528\u6237\u8D60\u9001\u514D\u8D39 tokens\n" : " 1. Sign up at open.bigmodel.cn \u2192 API Keys \u2192 Add\n 2. Free tokens on signup\n"
245207
+ ));
245208
+ } else if (llmProvider === "minimax") {
245209
+ console.log(source_default.gray(
245210
+ isCN ? " 1. \u6CE8\u518C MiniMax \u5F00\u653E\u5E73\u53F0 \u2192 \u8D26\u53F7\u8BBE\u7F6E \u2192 \u63A5\u53E3\u5BC6\u94A5\n" : " 1. Sign up at platform.minimaxi.com \u2192 Account \u2192 API Keys\n"
245211
+ ));
245212
+ }
245213
+ const { apiKey } = await esm_default12.prompt([{
245214
+ type: "password",
245215
+ name: "apiKey",
245216
+ message: isCN ? `\u7C98\u8D34\u4F60\u7684 ${providerInfo.name} API Key:` : `Paste your ${providerInfo.name} API key:`,
245217
+ mask: "*",
245218
+ validate: (v2) => {
245219
+ if (!v2.trim()) return isCN ? "\u8BF7\u8F93\u5165 API Key" : "API key required";
245220
+ if (providerInfo.keyPrefix && !v2.startsWith(providerInfo.keyPrefix)) {
245221
+ return isCN ? `Key \u683C\u5F0F\u4E0D\u5BF9\uFF0C\u5E94\u4EE5 "${providerInfo.keyPrefix}" \u5F00\u5934` : `Should start with "${providerInfo.keyPrefix}"`;
245222
+ }
245223
+ return true;
245224
+ }
245225
+ }]);
245226
+ envValues[providerInfo.envKey] = apiKey;
245227
+ if (characterCreatedNew) {
245228
+ await runTestChat(characterName, apiKey, llmProvider);
245229
+ }
245230
+ }
245231
+ if (llmProvider === "ollama") {
245232
+ console.log(source_default.yellow(
245233
+ isCN ? "\n \u786E\u4FDD Ollama \u5DF2\u8FD0\u884C: https://ollama.ai" : "\n Make sure Ollama is running: https://ollama.ai"
245234
+ ));
245235
+ console.log(source_default.gray(
245236
+ isCN ? " \u8FD0\u884C: ollama pull qwen2.5:7b\n" : " Run: ollama pull qwen2.5:7b\n"
245237
+ ));
245238
+ envValues.OLLAMA_BASE_URL = "http://localhost:11434";
245239
+ envValues.OLLAMA_MODEL = "qwen2.5:7b";
245240
+ }
245228
245241
  step(4, isCN ? "\u611F\u5B98\u529F\u80FD\uFF08\u53EF\u9009\uFF09" : "Sensory features (optional)");
245229
245242
  console.log(source_default.gray(isCN ? "\u8FD9\u4E9B\u529F\u80FD\u8BA9\u4F60\u7684\u4F34\u4FA3\u66F4\u52A0\u771F\u5B9E\u2014\u2014\u770B\u5F97\u89C1\u3001\u542C\u5F97\u5230\u3001\u6709\u81EA\u5DF1\u7684\u793E\u4EA4\u5708\u3002\n" : "These features make your companion come alive \u2014 visible, audible, and social.\n"));
245230
245243
  const { optionalFeatures } = await esm_default12.prompt([{
@@ -245235,7 +245248,7 @@ async function runSetupWizard() {
245235
245248
  {
245236
245249
  name: isCN ? "\u{1F4F8} \u81EA\u62CD & \u89C6\u9891 \u2014 \u53EF\u4EE5\u53D1\u81EA\u62CD\u548C\u77ED\u89C6\u9891 (\u9700\u8981 fal.ai key)" : "\u{1F4F8} Selfies & Video \u2014 send photos and short videos (needs fal.ai key)",
245237
245250
  value: "images",
245238
- checked: true
245251
+ checked: false
245239
245252
  },
245240
245253
  {
245241
245254
  name: isCN ? "\u{1F3A4} \u8BED\u97F3\u6D88\u606F \u2014 \u53EF\u4EE5\u53D1\u8BED\u97F3 (ElevenLabs / Fish Audio / FAL Kokoro)" : "\u{1F3A4} Voice messages \u2014 send voice notes (ElevenLabs / Fish Audio / FAL Kokoro)",
@@ -245535,6 +245548,28 @@ async function runSetupWizard() {
245535
245548
  await startOpencrush2();
245536
245549
  }
245537
245550
  }
245551
+ function getCharacterPreview(name) {
245552
+ var _a3, _b2;
245553
+ const identityPath = (0, import_path14.join)(getCharactersDir(), name, "IDENTITY.md");
245554
+ if (!(0, import_fs20.existsSync)(identityPath)) return "";
245555
+ try {
245556
+ const { readFileSync: readFileSync8 } = require("fs");
245557
+ const content = readFileSync8(identityPath, "utf-8");
245558
+ const ageMatch = content.match(/\*\*Age:\*\*\s*(\d+)/i);
245559
+ const fromMatch = content.match(/\*\*From:\*\*\s*(.+)/i);
245560
+ const age = ageMatch == null ? void 0 : ageMatch[1];
245561
+ const from2 = (_b2 = (_a3 = fromMatch == null ? void 0 : fromMatch[1]) == null ? void 0 : _a3.trim().split(",")[0]) == null ? void 0 : _b2.trim();
245562
+ return [age ? `${age}yo` : "", from2].filter(Boolean).join(" \xB7 ");
245563
+ } catch {
245564
+ return "";
245565
+ }
245566
+ }
245567
+ function maybeOpenCard(name) {
245568
+ const cardPath = (0, import_path14.join)(getCharactersDir(), name, "card.png");
245569
+ if (!(0, import_fs20.existsSync)(cardPath)) return;
245570
+ const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
245571
+ (0, import_child_process5.exec)(`${opener} "${cardPath}"`);
245572
+ }
245538
245573
  function getExistingCharacters() {
245539
245574
  const charactersDir = getCharactersDir();
245540
245575
  if (!(0, import_fs20.existsSync)(charactersDir)) return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrush",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Your AI companion lives on your computer. She watches dramas, listens to music, and thinks of you.",
5
5
  "bin": {
6
6
  "opencrush": "dist/index.js"