opencode-avatar 0.1.0 → 0.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/README.md CHANGED
@@ -42,12 +42,14 @@ Then add to your OpenCode config:
42
42
 
43
43
  ## Configuration
44
44
 
45
- ### Environment Variables
45
+ ### API Key Configuration
46
46
 
47
- Create a `.env` file in the plugin directory:
47
+ Create a config file at `~/.config/opencode/opencode-avatar.json`:
48
48
 
49
- ```env
50
- FAL_KEY=your_fal_ai_api_key_here
49
+ ```json
50
+ {
51
+ "falKey": "your_fal_ai_api_key_here"
52
+ }
51
53
  ```
52
54
 
53
55
  Get your FAL.ai API key from [fal.ai](https://fal.ai).
@@ -168,7 +170,7 @@ The output shows:
168
170
  ### Avatar Not Showing
169
171
 
170
172
  1. Check console output for errors
171
- 2. Verify FAL.ai API key in `.env`
173
+ 2. Verify FAL.ai API key in `~/.config/opencode/opencode-avatar.json`
172
174
  3. Ensure port 47291 is available
173
175
  4. Try restarting OpenCode
174
176
 
package/dist/electron.js CHANGED
@@ -402,8 +402,23 @@ import { app, BrowserWindow, screen, Tray, Menu, nativeImage } from "electron";
402
402
  import * as path from "path";
403
403
  import * as http from "http";
404
404
  import * as fs from "fs";
405
+ import * as os from "os";
405
406
  import { fileURLToPath } from "url";
406
407
  require_main().config();
408
+ function getFalKey() {
409
+ try {
410
+ const configPath = path.join(os.homedir(), ".config", "opencode", "opencode-avatar.json");
411
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
412
+ if (!config.falKey) {
413
+ console.warn("Warning: falKey not found in config file. Avatar generation will not work. Please set falKey in ~/.config/opencode/opencode-avatar.json");
414
+ return null;
415
+ }
416
+ return config.falKey;
417
+ } catch (error) {
418
+ console.warn(`Warning: Failed to read config file: ${error.message}. Avatar generation will not work. Please ensure ~/.config/opencode/opencode-avatar.json exists and contains falKey.`);
419
+ return null;
420
+ }
421
+ }
407
422
  var FAL_CDN_URL = "https://v3.fal.media";
408
423
  var FAL_REST_URL = "https://rest.alpha.fal.ai";
409
424
  var FAL_NANO_BANANA_URL = "https://fal.run/fal-ai/nano-banana-pro/edit";
@@ -551,13 +566,13 @@ function getAvatarPath() {
551
566
  }
552
567
  return path.join(AVATAR_DIR, "avatar.png");
553
568
  }
554
- async function uploadFile(filePath) {
569
+ async function uploadFile(filePath, falKey) {
555
570
  const fileBuffer = fs.readFileSync(filePath);
556
571
  const fileName = path.basename(filePath);
557
572
  const tokenResponse = await fetch(`${FAL_REST_URL}/storage/auth/token?storage_type=fal-cdn-v3`, {
558
573
  method: "POST",
559
574
  headers: {
560
- Authorization: `Key ${process.env.FAL_KEY}`,
575
+ Authorization: `Key ${falKey}`,
561
576
  Accept: "application/json",
562
577
  "Content-Type": "application/json"
563
578
  },
@@ -585,11 +600,11 @@ async function uploadFile(filePath) {
585
600
  const result = await response.json();
586
601
  return result.access_url || result.url || "";
587
602
  }
588
- async function generateAvatarImage(imageUrl, prompt) {
603
+ async function generateAvatarImage(imageUrl, prompt, falKey) {
589
604
  const response = await fetch(FAL_NANO_BANANA_URL, {
590
605
  method: "POST",
591
606
  headers: {
592
- Authorization: `Key ${process.env.FAL_KEY}`,
607
+ Authorization: `Key ${falKey}`,
593
608
  "Content-Type": "application/json"
594
609
  },
595
610
  body: JSON.stringify({
@@ -612,6 +627,11 @@ async function downloadImage(url, outputPath) {
612
627
  fs.writeFileSync(outputPath, buffer);
613
628
  }
614
629
  async function generateAvatarForPrompt(prompt) {
630
+ const falKey = getFalKey();
631
+ if (!falKey) {
632
+ console.warn("falKey is not set. Cannot generate avatar. Using default avatar.");
633
+ return path.join(AVATAR_DIR, "avatar.png");
634
+ }
615
635
  const cachedFilename = promptToFilename(prompt);
616
636
  const cachedPath = path.join(AVATAR_DIR, cachedFilename);
617
637
  if (fs.existsSync(cachedPath)) {
@@ -627,9 +647,9 @@ async function generateAvatarForPrompt(prompt) {
627
647
  return;
628
648
  }
629
649
  const sourceAvatar = path.join(AVATAR_DIR, "avatar.png");
630
- const uploadedUrl = await uploadFile(sourceAvatar);
650
+ const uploadedUrl = await uploadFile(sourceAvatar, falKey);
631
651
  const fullPrompt = `make a character variant: ${prompt}. Keep the background as a solid green screen color. Do not let the green screen color appear in reflections or on the subject.`;
632
- const result = await generateAvatarImage(uploadedUrl, fullPrompt);
652
+ const result = await generateAvatarImage(uploadedUrl, fullPrompt, falKey);
633
653
  const outputUrl = result.images?.[0]?.url || result.image?.url || result.url;
634
654
  if (!outputUrl) {
635
655
  throw new Error("No output image URL in response: " + JSON.stringify(result, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-avatar",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Dynamic desktop avatar plugin for OpenCode that reacts to your coding activities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",