opencode-avatar 0.3.11 → 0.3.12
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/electron.js +14 -8
- package/dist/index.js +41 -7
- package/package.json +1 -1
package/dist/electron.js
CHANGED
|
@@ -537,8 +537,10 @@ var httpServer = null;
|
|
|
537
537
|
var ongoingGenerations = new Map;
|
|
538
538
|
var lastHeartbeat = Date.now();
|
|
539
539
|
var HEARTBEAT_TIMEOUT = 1000;
|
|
540
|
-
function promptToFilename(prompt) {
|
|
541
|
-
|
|
540
|
+
function promptToFilename(prompt, agentBase) {
|
|
541
|
+
const base = agentBase || "avatar";
|
|
542
|
+
const action = prompt.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "_").substring(0, 50);
|
|
543
|
+
return `${base}-${action}.png`;
|
|
542
544
|
}
|
|
543
545
|
function getAvatarPort() {
|
|
544
546
|
const args = process.argv;
|
|
@@ -620,13 +622,14 @@ async function downloadImage(url, outputPath) {
|
|
|
620
622
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
621
623
|
fs.writeFileSync(outputPath, buffer);
|
|
622
624
|
}
|
|
623
|
-
async function generateAvatarForPrompt(prompt) {
|
|
625
|
+
async function generateAvatarForPrompt(prompt, agentBase) {
|
|
624
626
|
const config = getConfig();
|
|
627
|
+
const base = agentBase || "avatar";
|
|
625
628
|
if (!config.falKey) {
|
|
626
629
|
console.warn("falKey is not set. Cannot generate avatar. Using default avatar.");
|
|
627
|
-
return path.join(AVATAR_DIR,
|
|
630
|
+
return path.join(AVATAR_DIR, `${base}.png`);
|
|
628
631
|
}
|
|
629
|
-
const cachedFilename = promptToFilename(prompt);
|
|
632
|
+
const cachedFilename = promptToFilename(prompt, base);
|
|
630
633
|
const cachedPath = path.join(AVATAR_DIR, cachedFilename);
|
|
631
634
|
if (fs.existsSync(cachedPath)) {
|
|
632
635
|
return cachedPath;
|
|
@@ -640,7 +643,10 @@ async function generateAvatarForPrompt(prompt) {
|
|
|
640
643
|
if (fs.existsSync(cachedPath)) {
|
|
641
644
|
return;
|
|
642
645
|
}
|
|
643
|
-
|
|
646
|
+
let sourceAvatar = path.join(AVATAR_DIR, `${base}.png`);
|
|
647
|
+
if (!fs.existsSync(sourceAvatar)) {
|
|
648
|
+
sourceAvatar = path.join(AVATAR_DIR, "avatar.png");
|
|
649
|
+
}
|
|
644
650
|
const uploadedUrl = await uploadFile(sourceAvatar, config.falKey);
|
|
645
651
|
let fullPrompt = `make a character variant: ${prompt}. The action should be literal - the character should actually be performing the action (writing means writing, typing means typing, etc.), not shown as some abstract Terminal visualization or text output. Maintain the character's original essence and physicality. Keep the background as a solid green screen color. Do not let the green screen color appear in reflections or on the subject.`;
|
|
646
652
|
if (config.prompt) {
|
|
@@ -702,13 +708,13 @@ function startAvatarServer() {
|
|
|
702
708
|
});
|
|
703
709
|
req.on("end", async () => {
|
|
704
710
|
try {
|
|
705
|
-
const { prompt } = JSON.parse(body);
|
|
711
|
+
const { prompt, agentBase } = JSON.parse(body);
|
|
706
712
|
if (!prompt) {
|
|
707
713
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
708
714
|
res.end(JSON.stringify({ error: "Missing prompt" }));
|
|
709
715
|
return;
|
|
710
716
|
}
|
|
711
|
-
const avatarPath = await generateAvatarForPrompt(prompt);
|
|
717
|
+
const avatarPath = await generateAvatarForPrompt(prompt, agentBase);
|
|
712
718
|
if (mainWindow) {
|
|
713
719
|
const imageBuffer = fs.readFileSync(avatarPath);
|
|
714
720
|
const base64 = imageBuffer.toString("base64");
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,19 @@ var AVATAR_DIR = path.join(os.homedir(), ".config", "opencode");
|
|
|
11
11
|
var DEFAULT_AVATAR = "avatar.png";
|
|
12
12
|
var THINKING_PROMPT = "thinking hard";
|
|
13
13
|
var AVATAR_PORT = 47291;
|
|
14
|
+
function normalizeAgentName(name) {
|
|
15
|
+
return name.toLowerCase().replace(/\s+/g, "_");
|
|
16
|
+
}
|
|
17
|
+
function getAgentAvatarBase(agentName) {
|
|
18
|
+
if (!agentName)
|
|
19
|
+
return "avatar";
|
|
20
|
+
const normalized = normalizeAgentName(agentName);
|
|
21
|
+
const agentAvatarPath = path.join(AVATAR_DIR, `avatar-${normalized}.png`);
|
|
22
|
+
if (fs.existsSync(agentAvatarPath)) {
|
|
23
|
+
return `avatar-${normalized}`;
|
|
24
|
+
}
|
|
25
|
+
return "avatar";
|
|
26
|
+
}
|
|
14
27
|
function getToolPrompt(toolName, toolDescription) {
|
|
15
28
|
if (toolDescription) {
|
|
16
29
|
const shortDesc = toolDescription.split(".")[0].substring(0, 50);
|
|
@@ -26,6 +39,8 @@ var isShuttingDown = false;
|
|
|
26
39
|
var idleTriggered = false;
|
|
27
40
|
var currentRequestId = null;
|
|
28
41
|
var heartbeatInterval = null;
|
|
42
|
+
var currentAgentName = null;
|
|
43
|
+
var currentAgentBase = "avatar";
|
|
29
44
|
function sendHeartbeat() {
|
|
30
45
|
const req = http.request({
|
|
31
46
|
hostname: "localhost",
|
|
@@ -88,21 +103,30 @@ async function sendShutdownCommand() {
|
|
|
88
103
|
req.end();
|
|
89
104
|
});
|
|
90
105
|
}
|
|
91
|
-
function promptToFilename(prompt, toolName) {
|
|
106
|
+
function promptToFilename(prompt, toolName, agentBase) {
|
|
107
|
+
const base = agentBase || "avatar";
|
|
92
108
|
const baseName = toolName || prompt;
|
|
93
|
-
|
|
109
|
+
const action = baseName.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, "_").substring(0, 50);
|
|
110
|
+
return `${base}-${action}.png`;
|
|
94
111
|
}
|
|
95
|
-
function getAvatarPath(prompt, toolName) {
|
|
96
|
-
const
|
|
112
|
+
function getAvatarPath(prompt, toolName, agentBase) {
|
|
113
|
+
const base = agentBase || currentAgentBase;
|
|
114
|
+
const defaultAvatar = path.join(AVATAR_DIR, `${base}.png`);
|
|
97
115
|
if (!prompt) {
|
|
116
|
+
if (!fs.existsSync(defaultAvatar)) {
|
|
117
|
+
return path.join(AVATAR_DIR, DEFAULT_AVATAR);
|
|
118
|
+
}
|
|
98
119
|
return defaultAvatar;
|
|
99
120
|
}
|
|
100
|
-
const filename = promptToFilename(prompt, toolName);
|
|
121
|
+
const filename = promptToFilename(prompt, toolName, base);
|
|
101
122
|
const avatarPath = path.join(AVATAR_DIR, filename);
|
|
102
123
|
if (fs.existsSync(avatarPath)) {
|
|
103
124
|
return avatarPath;
|
|
104
125
|
}
|
|
105
|
-
|
|
126
|
+
if (fs.existsSync(defaultAvatar)) {
|
|
127
|
+
return defaultAvatar;
|
|
128
|
+
}
|
|
129
|
+
return path.join(AVATAR_DIR, DEFAULT_AVATAR);
|
|
106
130
|
}
|
|
107
131
|
async function startElectron(avatarPath) {
|
|
108
132
|
if (isShuttingDown) {
|
|
@@ -256,7 +280,7 @@ var AvatarPlugin = async ({ client }) => {
|
|
|
256
280
|
}
|
|
257
281
|
reject(err);
|
|
258
282
|
});
|
|
259
|
-
req.write(JSON.stringify({ prompt }));
|
|
283
|
+
req.write(JSON.stringify({ prompt, agentBase: currentAgentBase }));
|
|
260
284
|
req.end();
|
|
261
285
|
});
|
|
262
286
|
}
|
|
@@ -292,6 +316,16 @@ var AvatarPlugin = async ({ client }) => {
|
|
|
292
316
|
});
|
|
293
317
|
},
|
|
294
318
|
event: async ({ event }) => {
|
|
319
|
+
if (event.type === "message.updated") {
|
|
320
|
+
const message = event.properties?.info;
|
|
321
|
+
if (message?.role === "user" && message?.agent) {
|
|
322
|
+
const agentName = message.agent;
|
|
323
|
+
if (currentAgentName !== agentName) {
|
|
324
|
+
currentAgentName = agentName;
|
|
325
|
+
currentAgentBase = getAgentAvatarBase(currentAgentName);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
295
329
|
if (event.type === "session.idle" && (isThinking || isToolActive)) {
|
|
296
330
|
idleTriggered = true;
|
|
297
331
|
isThinking = false;
|