opencrush 0.3.10 → 0.3.13

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 +145 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -42073,10 +42073,19 @@ async function createFromPreset(apiKey, provider) {
42073
42073
  personality,
42074
42074
  backstory
42075
42075
  };
42076
- const spinner = ora("Saving character files...").start();
42077
- const blueprint = buildBlueprintFromPreset(config);
42076
+ const spinner = ora("Building character files...").start();
42077
+ let blueprint = buildBlueprintFromPreset(config);
42078
+ if (apiKey && provider) {
42079
+ try {
42080
+ blueprint = await enrichBlueprintWithLLM(blueprint, config, apiKey, provider);
42081
+ spinner.succeed(source_default.green(`${displayName} created!`));
42082
+ } catch {
42083
+ spinner.succeed(source_default.green(`${displayName} created! (basic \u2014 add API key for richer files)`));
42084
+ }
42085
+ } else {
42086
+ spinner.succeed(source_default.green(`${displayName} created!`));
42087
+ }
42078
42088
  writeCharacterFiles(folderName, blueprint);
42079
- spinner.succeed(source_default.green(`${displayName} created!`));
42080
42089
  const { hasPortrait, falKey } = await craftPortrait(config, folderName, apiKey, provider);
42081
42090
  printCreationSuccess(folderName, displayName);
42082
42091
  return { folderName, displayName, gender: preset.gender, hasPortrait, falKey };
@@ -42301,20 +42310,24 @@ function buildPortraitPrompt(config) {
42301
42310
  `wearing ${appearance.fashionStyle} style`,
42302
42311
  archetype.portraitBase,
42303
42312
  vibeHints,
42304
- "cinematic photography, ultra-detailed, 8k resolution, professional studio lighting, beautiful, editorial fashion, sharp focus"
42313
+ "cinematic photography, ultra-detailed, 8k resolution, beautiful, editorial fashion, sharp focus, atmospheric background matching character aesthetic, environmental storytelling, moody scene lighting"
42305
42314
  ];
42306
42315
  return parts.filter(Boolean).join(", ");
42307
42316
  }
42308
42317
  async function optimizePortraitPrompt(basePrompt, config, apiKey, provider) {
42309
- const system = `You are an expert at writing image generation prompts for portrait photography.
42318
+ const system = `You are an expert at writing image generation prompts for character portraits.
42310
42319
  Given a character description and base prompt, write an optimized FLUX/Stable Diffusion prompt.
42311
- Rules: under 200 words, focus on visual details only, include quality tags, match the character's aesthetic.
42320
+ Rules:
42321
+ - Under 200 words, focus on visual details, include quality tags
42322
+ - ALWAYS include a scene-appropriate background that matches the character's vibe and lifestyle (e.g. neon cityscape for cyberpunk, cozy caf\xE9 for warm characters, art studio for artists, rain-soaked street for moody characters)
42323
+ - NEVER use plain white, studio, or blank backgrounds
42324
+ - The background should tell a story about who this character is
42312
42325
  Return ONLY the prompt text \u2014 no explanation, no quotes.`;
42313
42326
  const userMsg = `Character: ${config.name}, ${config.gender}
42314
42327
  Archetype: ${config.archetype.label}
42315
42328
  Base prompt: ${basePrompt}
42316
42329
 
42317
- Optimize this into a vivid, effective portrait generation prompt. Preserve all physical details.`;
42330
+ Optimize this into a vivid portrait prompt with an atmospheric background that fits the character's world. Preserve all physical details.`;
42318
42331
  try {
42319
42332
  const result = await callLLMDirect(provider, apiKey, system, [{ role: "user", content: userMsg }], 300);
42320
42333
  return result.trim() || basePrompt;
@@ -42358,6 +42371,38 @@ function openImage(filePath) {
42358
42371
  (0, import_child_process2.exec)(cmd, () => {
42359
42372
  });
42360
42373
  }
42374
+ async function enrichBlueprintWithLLM(blueprint, config, apiKey, provider) {
42375
+ const system = `You are a character writer creating rich companion profiles. Write in second person removed \u2014 describe the character as they are, not to them. Be specific, vivid, contradictory. Real people have texture.
42376
+
42377
+ RULES:
42378
+ - Expand appearance into 2-3 paragraphs of vivid prose (not bullet points)
42379
+ - Add a ## Background section (2-3 paragraphs about their history, how they got here)
42380
+ - Flesh out the SOUL with specific examples, not generic traits
42381
+ - Keep the markdown structure and frontmatter exactly as given
42382
+ - Write in English only
42383
+ - Do NOT add any sections that aren't in the original
42384
+ - Return the FULL expanded file content, not just the additions`;
42385
+ const identityPrompt = `Expand this IDENTITY.md into a rich, detailed character file. The appearance section should be vivid prose paragraphs (like a novel character introduction), not a list. Add a ## Background section at the end with 2-3 paragraphs of backstory.
42386
+
42387
+ Current file:
42388
+ ${blueprint.identity}
42389
+
42390
+ Character context: ${config.archetype.label} archetype, personality traits: ${config.personality.join(", ")}${config.backstory ? ", backstory moment: " + config.backstory : ""}`;
42391
+ const soulPrompt = `Expand this SOUL.md into a richer version. Add specific examples to each section. The "Voice & Vibe" section should be 2+ paragraphs. "Loves" and "Dislikes" should have 5-7 items each with colorful detail. Add a "## Things She Does" section with 6-8 specific behavioral habits. Keep the same structure.
42392
+
42393
+ Current file:
42394
+ ${blueprint.soul}`;
42395
+ const [enrichedIdentity, enrichedSoul] = await Promise.all([
42396
+ callLLMDirect(provider, apiKey, system, [{ role: "user", content: identityPrompt }], 2e3).catch(() => blueprint.identity),
42397
+ callLLMDirect(provider, apiKey, system, [{ role: "user", content: soulPrompt }], 2e3).catch(() => blueprint.soul)
42398
+ ]);
42399
+ return {
42400
+ identity: enrichedIdentity || blueprint.identity,
42401
+ soul: enrichedSoul || blueprint.soul,
42402
+ user: blueprint.user,
42403
+ memory: blueprint.memory
42404
+ };
42405
+ }
42361
42406
  function buildBlueprintFromPreset(config) {
42362
42407
  const { archetype, name, appearance, personality, backstory } = config;
42363
42408
  const renameIn = (text) => text.replace(new RegExp(`# ${escapeRegex(archetype.id.charAt(0).toUpperCase() + archetype.id.slice(1))}`, "g"), `# ${name}`).replace(new RegExp(`\\b${escapeRegex(archetype.id)}\\b`, "gi"), name).replace(new RegExp(`\\b(Yuna|Valentina|Hana|Nyx|Hu Lan|Riot|Kai|Eli)\\b`, "g"), name);
@@ -245110,7 +245155,68 @@ async function runSetupWizard() {
245110
245155
  const TOTAL = 4;
245111
245156
  const step = (n2, label) => console.log(source_default.cyan(`
245112
245157
  [${n2}/${TOTAL}] ${label}`));
245113
- step(1, "Pick your companion");
245158
+ step(1, "Choose your AI brain");
245159
+ console.log(source_default.gray(" Pick whichever you already have a key for.\n"));
245160
+ const envValues = {};
245161
+ const providerChoices = [
245162
+ PROVIDER_INFO.find((p2) => p2.id === "anthropic"),
245163
+ PROVIDER_INFO.find((p2) => p2.id === "openai"),
245164
+ PROVIDER_INFO.find((p2) => p2.id === "deepseek"),
245165
+ PROVIDER_INFO.find((p2) => p2.id === "qwen"),
245166
+ PROVIDER_INFO.find((p2) => p2.id === "kimi"),
245167
+ PROVIDER_INFO.find((p2) => p2.id === "zhipu"),
245168
+ PROVIDER_INFO.find((p2) => p2.id === "minimax"),
245169
+ PROVIDER_INFO.find((p2) => p2.isLocal)
245170
+ ].filter(Boolean);
245171
+ const { llmProvider } = await esm_default12.prompt([{
245172
+ type: "list",
245173
+ name: "llmProvider",
245174
+ message: "Which AI provider?",
245175
+ choices: providerChoices.map((p2) => ({
245176
+ name: `${p2.emoji} ${p2.name}
245177
+ ${source_default.gray(p2.tagline)}`,
245178
+ value: p2.id,
245179
+ short: p2.name
245180
+ }))
245181
+ }]);
245182
+ envValues.LLM_PROVIDER = llmProvider;
245183
+ const providerInfo = getProviderInfo(llmProvider);
245184
+ let collectedApiKey;
245185
+ let collectedProvider;
245186
+ if (llmProvider !== "ollama") {
245187
+ console.log(source_default.yellow(`
245188
+ Get your API key: ${providerInfo.keyUrl}`));
245189
+ await openBrowser(providerInfo.keyUrl);
245190
+ if (llmProvider === "anthropic") {
245191
+ console.log(source_default.gray(' Sign up \u2192 API Keys \u2192 Create Key \u2192 copy the key (starts with "sk-ant-")\n'));
245192
+ } else if (llmProvider === "openai") {
245193
+ console.log(source_default.gray(" Sign up \u2192 API Keys \u2192 Create new secret key\n"));
245194
+ } else if (llmProvider === "deepseek") {
245195
+ console.log(source_default.gray(" Sign up \u2192 API Keys \u2192 Create key (new users get free credits)\n"));
245196
+ }
245197
+ const { apiKey } = await esm_default12.prompt([{
245198
+ type: "password",
245199
+ name: "apiKey",
245200
+ message: `Paste your ${providerInfo.name} API key:`,
245201
+ mask: "*",
245202
+ validate: (v2) => {
245203
+ if (!v2.trim()) return "API key required";
245204
+ if (providerInfo.keyPrefix && !v2.startsWith(providerInfo.keyPrefix)) {
245205
+ return `Should start with "${providerInfo.keyPrefix}"`;
245206
+ }
245207
+ return true;
245208
+ }
245209
+ }]);
245210
+ envValues[providerInfo.envKey] = apiKey;
245211
+ collectedApiKey = apiKey;
245212
+ collectedProvider = llmProvider;
245213
+ } else {
245214
+ console.log(source_default.yellow("\n Make sure Ollama is running: https://ollama.ai"));
245215
+ console.log(source_default.gray(" Run: ollama pull qwen2.5:7b\n"));
245216
+ envValues.OLLAMA_BASE_URL = "http://localhost:11434";
245217
+ envValues.OLLAMA_MODEL = "qwen2.5:7b";
245218
+ }
245219
+ step(2, "Pick your companion");
245114
245220
  const characters = getExistingCharacters();
245115
245221
  let characterName;
245116
245222
  let characterCreatedNew = false;
@@ -245137,7 +245243,7 @@ async function runSetupWizard() {
245137
245243
  choices
245138
245244
  }]);
245139
245245
  if (pick === "__new__") {
245140
- const created = await createCharacterFlow();
245246
+ const created = await createCharacterFlow(collectedApiKey, collectedProvider);
245141
245247
  characterName = created.folderName;
245142
245248
  characterCreatedNew = true;
245143
245249
  gender = created.gender;
@@ -245148,14 +245254,17 @@ async function runSetupWizard() {
245148
245254
  }
245149
245255
  } else {
245150
245256
  console.log(source_default.gray(" No companions found yet \u2014 let's create one!\n"));
245151
- const created = await createCharacterFlow();
245257
+ const created = await createCharacterFlow(collectedApiKey, collectedProvider);
245152
245258
  characterName = created.folderName;
245153
245259
  characterCreatedNew = true;
245154
245260
  gender = created.gender;
245155
245261
  }
245262
+ envValues.CHARACTER_NAME = characterName;
245156
245263
  const pro = pronouns(gender);
245157
- const envValues = { CHARACTER_NAME: characterName };
245158
- step(2, `Where do you want to chat with ${characterName}?`);
245264
+ if (characterCreatedNew && collectedApiKey) {
245265
+ await runTestChat(characterName, collectedApiKey, collectedProvider ?? "anthropic");
245266
+ }
245267
+ step(3, `Where do you want to chat with ${characterName}?`);
245159
245268
  const { platforms } = await esm_default12.prompt([{
245160
245269
  type: "checkbox",
245161
245270
  name: "platforms",
@@ -245221,61 +245330,6 @@ async function runSetupWizard() {
245221
245330
  WhatsApp needs no setup! A QR code will appear when ${characterName} starts.`));
245222
245331
  console.log(source_default.gray(" Scan it with WhatsApp \u2192 Linked Devices on your phone."));
245223
245332
  }
245224
- step(3, `Give ${characterName} a brain`);
245225
- console.log(source_default.gray(" Pick whichever you already have a key for.\n"));
245226
- const providerChoices = [
245227
- PROVIDER_INFO.find((p2) => p2.id === "anthropic"),
245228
- PROVIDER_INFO.find((p2) => p2.id === "openai"),
245229
- PROVIDER_INFO.find((p2) => p2.id === "deepseek"),
245230
- PROVIDER_INFO.find((p2) => p2.isLocal)
245231
- ].filter(Boolean);
245232
- const { llmProvider } = await esm_default12.prompt([{
245233
- type: "list",
245234
- name: "llmProvider",
245235
- message: "Which AI provider?",
245236
- choices: providerChoices.map((p2) => ({
245237
- name: `${p2.emoji} ${p2.name}
245238
- ${source_default.gray(p2.tagline)}`,
245239
- value: p2.id,
245240
- short: p2.name
245241
- }))
245242
- }]);
245243
- envValues.LLM_PROVIDER = llmProvider;
245244
- const providerInfo = getProviderInfo(llmProvider);
245245
- if (llmProvider !== "ollama") {
245246
- console.log(source_default.yellow(`
245247
- Get your API key: ${providerInfo.keyUrl}`));
245248
- await openBrowser(providerInfo.keyUrl);
245249
- if (llmProvider === "anthropic") {
245250
- console.log(source_default.gray(' Sign up \u2192 API Keys \u2192 Create Key \u2192 copy the key (starts with "sk-ant-")\n'));
245251
- } else if (llmProvider === "openai") {
245252
- console.log(source_default.gray(" Sign up \u2192 API Keys \u2192 Create new secret key\n"));
245253
- } else if (llmProvider === "deepseek") {
245254
- console.log(source_default.gray(" Sign up \u2192 API Keys \u2192 Create key (new users get free credits)\n"));
245255
- }
245256
- const { apiKey } = await esm_default12.prompt([{
245257
- type: "password",
245258
- name: "apiKey",
245259
- message: `Paste your ${providerInfo.name} API key:`,
245260
- mask: "*",
245261
- validate: (v2) => {
245262
- if (!v2.trim()) return "API key required";
245263
- if (providerInfo.keyPrefix && !v2.startsWith(providerInfo.keyPrefix)) {
245264
- return `Should start with "${providerInfo.keyPrefix}"`;
245265
- }
245266
- return true;
245267
- }
245268
- }]);
245269
- envValues[providerInfo.envKey] = apiKey;
245270
- if (characterCreatedNew) {
245271
- await runTestChat(characterName, apiKey, llmProvider);
245272
- }
245273
- } else {
245274
- console.log(source_default.yellow("\n Make sure Ollama is running: https://ollama.ai"));
245275
- console.log(source_default.gray(" Run: ollama pull qwen2.5:7b\n"));
245276
- envValues.OLLAMA_BASE_URL = "http://localhost:11434";
245277
- envValues.OLLAMA_MODEL = "qwen2.5:7b";
245278
- }
245279
245333
  step(4, "Extras");
245280
245334
  console.log(source_default.gray(" All optional \u2014 skip everything by pressing Enter.\n"));
245281
245335
  const quickChoices = [
@@ -245520,7 +245574,7 @@ function parseIdentity(identityPath) {
245520
245574
  const age = (ageMatch == null ? void 0 : ageMatch[1]) ?? "??";
245521
245575
  const fromMatch = content.match(/\*\*From:\*\*\s*(.+)/i);
245522
245576
  const locationRaw = ((_b2 = fromMatch == null ? void 0 : fromMatch[1]) == null ? void 0 : _b2.trim()) ?? "Unknown";
245523
- const location = locationRaw.replace(/\s*\(.*\)\s*$/, "").trim();
245577
+ const location = locationRaw.replace(/\s*\(.*\)/, "").split(" \u2014 ")[0].trim();
245524
245578
  const hobbiesMatch = content.match(/\*\*Hobbies:\*\*\s*(.+)/i);
245525
245579
  const hobbiesRaw = (hobbiesMatch == null ? void 0 : hobbiesMatch[1]) ?? "";
245526
245580
  const tags = hobbiesRaw.split(",").map((t2) => t2.trim()).filter(Boolean).slice(0, 5);
@@ -245549,18 +245603,20 @@ function pickGradient(name) {
245549
245603
  return gradients[hash % gradients.length];
245550
245604
  }
245551
245605
  function createSvgOverlay(data, gradient) {
245552
- const textX = PORTRAIT_X + PORTRAIT_SIZE + 60;
245553
- const tagY = 280;
245554
- const maxTagX = WIDTH - 40;
245606
+ const textX = PORTRAIT_X + PORTRAIT_SIZE + 50;
245607
+ const maxTagX = WIDTH - 30;
245608
+ const nameY = 210;
245609
+ const metaY = nameY + 45;
245610
+ const tagStartY = metaY + 50;
245555
245611
  let inlineTags = "";
245556
245612
  let offsetX = textX;
245557
- let rowY = tagY;
245558
- const rowHeight = 34;
245613
+ let rowY = tagStartY;
245614
+ const rowHeight = 30;
245559
245615
  const maxRows = 2;
245560
245616
  let currentRow = 1;
245561
245617
  for (const tag of data.tags) {
245562
- const label = tag.length > 22 ? tag.slice(0, 21) + "\u2026" : tag;
245563
- const w2 = label.length * 8.5 + 24;
245618
+ const label = tag.length > 18 ? tag.slice(0, 17) + "\u2026" : tag;
245619
+ const w2 = label.length * 7.5 + 16;
245564
245620
  if (offsetX + w2 > maxTagX) {
245565
245621
  if (currentRow >= maxRows) break;
245566
245622
  currentRow++;
@@ -245568,12 +245624,12 @@ function createSvgOverlay(data, gradient) {
245568
245624
  rowY += rowHeight;
245569
245625
  }
245570
245626
  inlineTags += `
245571
- <rect x="${offsetX - 2}" y="${rowY - 16}" width="${w2}" height="26" rx="13" fill="rgba(255,255,255,0.10)" />
245572
- <text x="${offsetX + 10}" y="${rowY}" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#c0c0c0">${escapeXml(label)}</text>
245627
+ <rect x="${offsetX}" y="${rowY - 14}" width="${w2}" height="22" rx="11" fill="rgba(255,255,255,0.10)" />
245628
+ <text x="${offsetX + 8}" y="${rowY + 1}" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="#c0c0c0">${escapeXml(label)}</text>
245573
245629
  `;
245574
- offsetX += w2 + 8;
245630
+ offsetX += w2 + 6;
245575
245631
  }
245576
- const descriptionY = tagY + currentRow * rowHeight + 16;
245632
+ const descriptionY = tagStartY + currentRow * rowHeight + 14;
245577
245633
  return `<svg width="${WIDTH}" height="${HEIGHT}" xmlns="http://www.w3.org/2000/svg">
245578
245634
  <defs>
245579
245635
  <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
@@ -245585,28 +245641,21 @@ function createSvgOverlay(data, gradient) {
245585
245641
  </clipPath>
245586
245642
  </defs>
245587
245643
 
245588
- <!-- Background -->
245589
245644
  <rect width="${WIDTH}" height="${HEIGHT}" fill="url(#bg)" />
245590
245645
 
245591
- <!-- Subtle border around portrait area -->
245592
- <circle cx="${PORTRAIT_X + PORTRAIT_SIZE / 2}" cy="${PORTRAIT_Y + PORTRAIT_SIZE / 2}" r="${PORTRAIT_SIZE / 2 + 3}" fill="none" stroke="rgba(255,255,255,0.15)" stroke-width="2" />
245646
+ <circle cx="${PORTRAIT_X + PORTRAIT_SIZE / 2}" cy="${PORTRAIT_Y + PORTRAIT_SIZE / 2}" r="${PORTRAIT_SIZE / 2 + 2}" fill="none" stroke="rgba(255,255,255,0.12)" stroke-width="2" />
245593
245647
 
245594
- <!-- Character Name -->
245595
- <text x="${textX}" y="170" font-family="system-ui, -apple-system, sans-serif" font-size="48" font-weight="bold" fill="white">${escapeXml(data.name)}</text>
245648
+ <text x="${textX}" y="${nameY}" font-family="system-ui, -apple-system, sans-serif" font-size="44" font-weight="bold" fill="white">${escapeXml(data.name)}</text>
245596
245649
 
245597
- <!-- Age + Location -->
245598
- <text x="${textX}" y="215" font-family="system-ui, -apple-system, sans-serif" font-size="18" fill="#a0a0a0">${escapeXml(data.age)} \xB7 ${escapeXml(truncate(data.location, 60))}</text>
245650
+ <text x="${textX}" y="${metaY}" font-family="system-ui, -apple-system, sans-serif" font-size="16" fill="#a0a0a0">${escapeXml(data.age)} \xB7 ${escapeXml(truncate(data.location, 35))}</text>
245599
245651
 
245600
- <!-- Personality Tags (inline) -->
245601
245652
  ${inlineTags}
245602
245653
 
245603
- <!-- One-line description -->
245604
- <text x="${textX}" y="${descriptionY}" font-family="system-ui, -apple-system, sans-serif" font-size="15" fill="#b0b0b0" font-style="italic">${escapeXml(truncate(data.description, 70))}</text>
245654
+ <text x="${textX}" y="${descriptionY}" font-family="system-ui, -apple-system, sans-serif" font-size="14" fill="#b0b0b0" font-style="italic">${escapeXml(truncate(data.description, 60))}</text>
245605
245655
 
245606
- <!-- Bottom bar -->
245607
- <rect x="0" y="${HEIGHT - 50}" width="${WIDTH}" height="50" fill="rgba(0,0,0,0.3)" />
245608
- <text x="40" y="${HEIGHT - 20}" font-family="system-ui, -apple-system, sans-serif" font-size="16" font-weight="bold" fill="#ff69b4">Opencrush</text>
245609
- <text x="${WIDTH - 40}" y="${HEIGHT - 20}" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#888" text-anchor="end">github.com/Hollandchirs/Opencrush</text>
245656
+ <rect x="0" y="${HEIGHT - 44}" width="${WIDTH}" height="44" fill="rgba(0,0,0,0.3)" />
245657
+ <text x="36" y="${HEIGHT - 17}" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="bold" fill="#ff69b4">Opencrush</text>
245658
+ <text x="${WIDTH - 36}" y="${HEIGHT - 17}" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="#888" text-anchor="end">github.com/Hollandchirs/Opencrush</text>
245610
245659
  </svg>`;
245611
245660
  }
245612
245661
  function escapeXml(str2) {
@@ -245639,7 +245688,7 @@ async function generateCard(characterName) {
245639
245688
  }
245640
245689
  const svgOverlay = createSvgOverlay(data, gradient);
245641
245690
  const svgBuffer = Buffer.from(svgOverlay);
245642
- let card = sharp(svgBuffer, { density: 150 }).resize(WIDTH, HEIGHT);
245691
+ let card = sharp(svgBuffer, { density: 300 }).resize(WIDTH, HEIGHT);
245643
245692
  if (refImagePath) {
245644
245693
  const circleMask = Buffer.from(
245645
245694
  `<svg width="${PORTRAIT_SIZE}" height="${PORTRAIT_SIZE}">
@@ -245672,9 +245721,9 @@ var init_card = __esm({
245672
245721
  init_source();
245673
245722
  WIDTH = 1200;
245674
245723
  HEIGHT = 630;
245675
- PORTRAIT_SIZE = 280;
245724
+ PORTRAIT_SIZE = 260;
245676
245725
  PORTRAIT_X = 80;
245677
- PORTRAIT_Y = 100;
245726
+ PORTRAIT_Y = (HEIGHT - PORTRAIT_SIZE) / 2 - 10;
245678
245727
  }
245679
245728
  });
245680
245729
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrush",
3
- "version": "0.3.10",
3
+ "version": "0.3.13",
4
4
  "description": "Your AI companion lives on your device. She watches dramas, listens to music, and thinks of you.",
5
5
  "bin": {
6
6
  "opencrush": "dist/index.js"