engine7 7.1.33 → 7.1.35
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/engine-startup.mjs +350 -18
- package/dist/main.mjs +348 -17
- package/package.json +2 -2
package/dist/engine-startup.mjs
CHANGED
|
@@ -20381,6 +20381,8 @@ var NudgePlugin = class {
|
|
|
20381
20381
|
let isWaiting = false;
|
|
20382
20382
|
let waitDesc = "";
|
|
20383
20383
|
let pushedDecision = false;
|
|
20384
|
+
let needLanding = false;
|
|
20385
|
+
let landingDesc = "";
|
|
20384
20386
|
try {
|
|
20385
20387
|
const excludeCases = this.cfg.stopHookExcludeCases || [];
|
|
20386
20388
|
const excludeSection = excludeCases.length > 0 ? [
|
|
@@ -20396,17 +20398,20 @@ var NudgePlugin = class {
|
|
|
20396
20398
|
"1. \u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF08\u670D\u52A1\u91CD\u542F\u3001SSH\u6062\u590D\u3001\u6587\u4EF6\u52A0\u8F7D\u3001\u5F02\u6B65\u4EFB\u52A1\u5B8C\u6210\u7B49\uFF09",
|
|
20397
20399
|
'2. \u628A\u672C\u8BE5\u81EA\u5DF1\u505A\u7684\u51B3\u5B9A\u63A8\u7ED9\u4E86\u5BF9\u65B9\uFF08\u4F8B\u5982\u95EE"\u8981\u73B0\u5728\u6539\u8FD8\u662F\u6392\u540E\u9762\uFF1F""\u8981\u4E0D\u8981\u8BD5\u8BD5X\uFF1F"\u4F46\u660E\u660E\u81EA\u5DF1\u80FD\u5B9A\uFF09',
|
|
20398
20400
|
"3. \u9047\u5230\u95EE\u9898\u6CA1\u6709\u81EA\u5DF1\u89E3\u51B3\u4E5F\u6CA1\u6709\u6392\u8FDB calendar\uFF0C\u53EA\u662F\u6401\u7F6E\u7740",
|
|
20401
|
+
"4. \u8FD9\u4E2Aturn\u4EA7\u751F\u4E86\u9636\u6BB5\u6027\u6210\u679C\uFF08\u5199\u4E86\u6587\u4EF6/\u6539\u4E86\u914D\u7F6E/\u5B8C\u6210\u4EFB\u52A1/\u505A\u4E86\u51B3\u5B9A\uFF09\u4F46\u6CA1\u843D\u76D8\u5230docs\u6216\u6CA1\u66F4\u65B0\u4EFB\u52A1\u72B6\u6001",
|
|
20399
20402
|
"",
|
|
20400
20403
|
"\u5173\u952E\u533A\u5206\uFF1A",
|
|
20401
20404
|
'- \u7B49\u5BF9\u65B9\u56DE\u590D\u4E00\u4E2A\u81EA\u5DF1\u5C31\u80FD\u505A\u7684\u51B3\u5B9A \u2192 "pushedDecision": true\uFF08\u4E0D\u662F\u5355\u7EAF waiting\uFF0C\u8FD9\u662F\u66F4\u4E25\u91CD\u7684\u8FDD\u89C4\uFF09',
|
|
20402
20405
|
'- \u7B49\u771F\u6B63\u7684\u5916\u90E8\u6761\u4EF6\uFF08\u670D\u52A1\u3001SSH\u3001\u5F02\u6B65\u4EFB\u52A1\uFF09 \u2192 "waiting": true',
|
|
20403
20406
|
"- \u4F60\u5DF2\u7ECF\u505A\u4E86\u51B3\u5B9A\u5E76\u6392\u8FDB calendar \u6216\u5DF2\u5F00\u59CB\u6267\u884C \u2192 \u4E24\u4E2A\u90FD false",
|
|
20407
|
+
'- \u8FD9\u4E2Aturn\u505A\u4E86\u5B9E\u8D28\u5DE5\u4F5C\uFF08\u5199\u6587\u4EF6/\u6539\u4EE3\u7801/\u51FA\u56FE/\u53D1\u5E03\uFF09\u4F46\u6CA1\u843D\u76D8\u6216\u6CA1\u6539\u4EFB\u52A1\u72B6\u6001 \u2192 "needLanding": true',
|
|
20404
20408
|
excludeSection,
|
|
20405
20409
|
"",
|
|
20406
|
-
"\u53EA\u56DE\u590D JSON\uFF1A",
|
|
20410
|
+
"\u53EA\u56DE\u590D JSON\uFF08\u53EF\u4EE5\u591A\u5B57\u6BB5\u540C\u65F6\u4E3Atrue\uFF09\uFF1A",
|
|
20407
20411
|
'- \u63A8\u51B3\u5B9A\u7ED9\u5BF9\u65B9\uFF08\u81EA\u5DF1\u80FD\u5B9A\u5374\u95EE\uFF09\uFF1A{"pushedDecision": true, "desc": "\u7B80\u77ED\u8BF4\u660E + \u5EFA\u8BAE\u600E\u4E48\u505A"}',
|
|
20408
20412
|
'- \u771F\u7B49\u5916\u90E8\u6761\u4EF6\uFF1A{"waiting": true, "desc": "\u7B80\u77ED\u8BF4\u660E"}',
|
|
20409
|
-
'- \
|
|
20413
|
+
'- \u6709\u9636\u6BB5\u6027\u6210\u679C\u6CA1\u843D\u76D8\uFF1A{"needLanding": true, "landingDesc": "\u505A\u4E86\u4EC0\u4E48+\u8BE5\u843D\u76D8\u5230\u54EA/\u8BE5\u6539\u4EC0\u4E48\u72B6\u6001"}',
|
|
20414
|
+
'- \u90FD\u4E0D\u7B26\u5408\uFF1A{"waiting": false, "pushedDecision": false, "needLanding": false}'
|
|
20410
20415
|
].join("\n"),
|
|
20411
20416
|
messages: [{ role: "user", content: judgeInput }],
|
|
20412
20417
|
maxTokens: 128,
|
|
@@ -20425,8 +20430,10 @@ var NudgePlugin = class {
|
|
|
20425
20430
|
isWaiting = !!parsed.waiting;
|
|
20426
20431
|
waitDesc = parsed.desc || "";
|
|
20427
20432
|
pushedDecision = !!parsed.pushedDecision;
|
|
20433
|
+
needLanding = !!parsed.needLanding;
|
|
20434
|
+
landingDesc = parsed.landingDesc || "";
|
|
20428
20435
|
}
|
|
20429
|
-
console.log(`[stop-hook] Judge result: waiting=${isWaiting}, pushedDecision=${pushedDecision}, desc="${waitDesc}"`);
|
|
20436
|
+
console.log(`[stop-hook] Judge result: waiting=${isWaiting}, pushedDecision=${pushedDecision}, needLanding=${needLanding}, desc="${waitDesc}", landingDesc="${landingDesc}"`);
|
|
20430
20437
|
} catch (e) {
|
|
20431
20438
|
console.warn(`[stop-hook] LLM judge error: ${e.message}`);
|
|
20432
20439
|
}
|
|
@@ -20456,6 +20463,26 @@ var NudgePlugin = class {
|
|
|
20456
20463
|
console.warn(`[stop-hook] Failed to inject corrective message: ${e.message}`);
|
|
20457
20464
|
}
|
|
20458
20465
|
}
|
|
20466
|
+
if (needLanding && landingDesc) {
|
|
20467
|
+
console.log(`[stop-hook] DETECTED needLanding! Injecting landing reminder`);
|
|
20468
|
+
try {
|
|
20469
|
+
const landingMsg = buildNudgeNotification("prompt", [
|
|
20470
|
+
"## \u{1F4CB} \u843D\u76D8\u63D0\u9192",
|
|
20471
|
+
`\u8FD9\u4E2Aturn\u7684\u9636\u6BB5\u6027\u6210\u679C\uFF1A${landingDesc}`,
|
|
20472
|
+
"",
|
|
20473
|
+
"\u8BF7\u68C0\u67E5\uFF1A",
|
|
20474
|
+
"- \u9636\u6BB5\u6027\u6210\u679C\u662F\u5426\u5DF2\u843D\u76D8\u5230docs\uFF08\u5199\u6E05\u6587\u4EF6\u8DEF\u5F84\uFF09",
|
|
20475
|
+
"- \u4EFB\u52A1\u72B6\u6001\u662F\u5426\u9700\u8981\u66F4\u65B0\uFF08SESSION-STATE / calendar\uFF09",
|
|
20476
|
+
"- \u5982\u679C\u5DF2\u7ECF\u843D\u76D8\u4E86 \u2192 \u5FFD\u7565\u8FD9\u6761"
|
|
20477
|
+
].join("\n"));
|
|
20478
|
+
const route = this.getRoute(sessions);
|
|
20479
|
+
if (route) {
|
|
20480
|
+
enqueueNotification(landingMsg, route);
|
|
20481
|
+
}
|
|
20482
|
+
} catch (e) {
|
|
20483
|
+
console.warn(`[stop-hook] Failed to inject landing reminder: ${e.message}`);
|
|
20484
|
+
}
|
|
20485
|
+
}
|
|
20459
20486
|
if (!isWaiting) {
|
|
20460
20487
|
return;
|
|
20461
20488
|
}
|
|
@@ -24851,7 +24878,7 @@ import * as path30 from "node:path";
|
|
|
24851
24878
|
import * as os3 from "node:os";
|
|
24852
24879
|
var execFileAsync = promisify(execFile);
|
|
24853
24880
|
var VOICE_DIR = path30.join(os3.tmpdir(), "engine-voice");
|
|
24854
|
-
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId) {
|
|
24881
|
+
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId, instruction) {
|
|
24855
24882
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
24856
24883
|
const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
24857
24884
|
const script = `
|
|
@@ -24864,7 +24891,7 @@ text = sys.argv[2]
|
|
|
24864
24891
|
output = sys.argv[3]
|
|
24865
24892
|
|
|
24866
24893
|
dashscope.api_key = config['apiKey']
|
|
24867
|
-
if config.get('workspaceId') and 'v3' in config.get('model', ''):
|
|
24894
|
+
if config.get('workspaceId') and ('v3' in config.get('model', '') or 'qwen-audio' in config.get('model', '')):
|
|
24868
24895
|
dashscope.base_websocket_api_url = f"wss://{config['workspaceId']}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference"
|
|
24869
24896
|
|
|
24870
24897
|
chunks = []
|
|
@@ -24882,12 +24909,15 @@ class Cb(ResultCallback):
|
|
|
24882
24909
|
def on_close(self):
|
|
24883
24910
|
done.set()
|
|
24884
24911
|
|
|
24885
|
-
|
|
24912
|
+
synth_kwargs = dict(
|
|
24886
24913
|
model=config['model'],
|
|
24887
24914
|
voice=config['voice'],
|
|
24888
24915
|
callback=Cb(),
|
|
24889
24916
|
format=AudioFormat.PCM_24000HZ_MONO_16BIT,
|
|
24890
24917
|
)
|
|
24918
|
+
if config.get('instruction'):
|
|
24919
|
+
synth_kwargs['instruction'] = config['instruction']
|
|
24920
|
+
synth = SpeechSynthesizer(**synth_kwargs)
|
|
24891
24921
|
synth.call(text)
|
|
24892
24922
|
|
|
24893
24923
|
# \u7B49\u5F85\u5408\u6210\u5B8C\u6210\uFF08\u6700\u591A 15 \u79D2\uFF09
|
|
@@ -24905,7 +24935,7 @@ with wave.open(output, 'wb') as wf:
|
|
|
24905
24935
|
wf.writeframes(pcm)
|
|
24906
24936
|
print(f"OK: {len(pcm)} bytes")
|
|
24907
24937
|
`;
|
|
24908
|
-
const configJson = JSON.stringify({ apiKey, model, voice, workspaceId });
|
|
24938
|
+
const configJson = JSON.stringify({ apiKey, model, voice, workspaceId, instruction });
|
|
24909
24939
|
await execFileAsync("python3", ["-c", script, configJson, text, output], { timeout: 3e4 });
|
|
24910
24940
|
if (!fs29.existsSync(output) || fs29.statSync(output).size < 100) {
|
|
24911
24941
|
throw new Error("CosyVoice produced empty output");
|
|
@@ -25001,7 +25031,8 @@ registry.register({
|
|
|
25001
25031
|
vc.apiKey,
|
|
25002
25032
|
vc.model || "cosyvoice-v1",
|
|
25003
25033
|
vc.voice,
|
|
25004
|
-
vc.workspaceId
|
|
25034
|
+
vc.workspaceId,
|
|
25035
|
+
vc.instruction
|
|
25005
25036
|
);
|
|
25006
25037
|
if (audioPath.endsWith(".wav")) {
|
|
25007
25038
|
audioPath = await compressWav(audioPath);
|
|
@@ -25137,6 +25168,151 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25137
25168
|
const imgRes = await fetch(images[0].url);
|
|
25138
25169
|
return Buffer.from(await imgRes.arrayBuffer());
|
|
25139
25170
|
}
|
|
25171
|
+
function getAutoDLCfg() {
|
|
25172
|
+
const cfg = liveConfig.all();
|
|
25173
|
+
const ac = cfg.my_selfie?.autodl || cfg.tools?.my_selfie?.autodl || {};
|
|
25174
|
+
return {
|
|
25175
|
+
server: ac.server || "http://localhost:8188",
|
|
25176
|
+
backend: ac.backend || "sd15",
|
|
25177
|
+
// 'sd15' = CheckpointLoaderSimple, 'zimage' = UNETLoader+CLIPLoader+VAELoader
|
|
25178
|
+
checkpoint: ac.checkpoint || "majicmixRealistic_v7.safetensors",
|
|
25179
|
+
clip: ac.clip || void 0,
|
|
25180
|
+
// zimage only
|
|
25181
|
+
vae: ac.vae || void 0,
|
|
25182
|
+
// zimage only
|
|
25183
|
+
lora: ac.lora || void 0,
|
|
25184
|
+
loraStrength: ac.loraStrength ?? 0.7,
|
|
25185
|
+
upscale: ac.upscale || void 0,
|
|
25186
|
+
steps: ac.steps ?? 25,
|
|
25187
|
+
cfg: ac.cfg ?? 7,
|
|
25188
|
+
width: ac.width ?? 512,
|
|
25189
|
+
height: ac.height ?? 768
|
|
25190
|
+
};
|
|
25191
|
+
}
|
|
25192
|
+
async function comfyQueue(server, workflow) {
|
|
25193
|
+
const res = await fetch(`${server}/prompt`, {
|
|
25194
|
+
method: "POST",
|
|
25195
|
+
headers: { "Content-Type": "application/json" },
|
|
25196
|
+
body: JSON.stringify({ prompt: workflow })
|
|
25197
|
+
});
|
|
25198
|
+
if (!res.ok) return null;
|
|
25199
|
+
const data = await res.json();
|
|
25200
|
+
return data.prompt_id || null;
|
|
25201
|
+
}
|
|
25202
|
+
async function comfyWait(server, promptId, timeout = 120) {
|
|
25203
|
+
for (let i = 0; i < timeout / 2; i++) {
|
|
25204
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
25205
|
+
try {
|
|
25206
|
+
const res = await fetch(`${server}/history/${promptId}`);
|
|
25207
|
+
const history = await res.json();
|
|
25208
|
+
if (history[promptId]) {
|
|
25209
|
+
const outputs = history[promptId].outputs || {};
|
|
25210
|
+
for (const nid of Object.keys(outputs)) {
|
|
25211
|
+
if (outputs[nid].images?.length) {
|
|
25212
|
+
const img = outputs[nid].images[0];
|
|
25213
|
+
const imgRes = await fetch(`${server}/view?filename=${img.filename}&subfolder=${img.subfolder || ""}&type=${img.type || ""}`);
|
|
25214
|
+
return Buffer.from(await imgRes.arrayBuffer());
|
|
25215
|
+
}
|
|
25216
|
+
}
|
|
25217
|
+
return null;
|
|
25218
|
+
}
|
|
25219
|
+
} catch {
|
|
25220
|
+
}
|
|
25221
|
+
}
|
|
25222
|
+
return null;
|
|
25223
|
+
}
|
|
25224
|
+
async function generateWithAutoDL(prompt, refB64) {
|
|
25225
|
+
const ac = getAutoDLCfg();
|
|
25226
|
+
const seed = Math.floor(Math.random() * 1e6);
|
|
25227
|
+
const wf = {};
|
|
25228
|
+
let modelNode, clipNode, vaeNode;
|
|
25229
|
+
if (ac.backend === "zimage") {
|
|
25230
|
+
wf["1"] = { class_type: "UNETLoader", inputs: { unet_name: ac.checkpoint, weight_dtype: "default" } };
|
|
25231
|
+
wf["2"] = { class_type: "CLIPLoader", inputs: { clip_name: ac.clip || "Z-Image/Z-Image-CLIP-complete.safetensors", type: "qwen_image" } };
|
|
25232
|
+
wf["3"] = { class_type: "VAELoader", inputs: { vae_name: ac.vae || "Z-Image/diffusion_pytorch_model.safetensors" } };
|
|
25233
|
+
wf["6"] = { class_type: "TextEncodeZImageOmni", inputs: { prompt, auto_resize_images: true, clip: ["2", 0] } };
|
|
25234
|
+
wf["7"] = { class_type: "TextEncodeZImageOmni", inputs: { prompt: "low quality, distorted, blurry, text, watermark", auto_resize_images: true, clip: ["2", 0] } };
|
|
25235
|
+
modelNode = ["1", 0];
|
|
25236
|
+
clipNode = ["2", 0];
|
|
25237
|
+
vaeNode = ["3", 0];
|
|
25238
|
+
} else {
|
|
25239
|
+
wf["1"] = { class_type: "CheckpointLoaderSimple", inputs: { ckpt_name: ac.checkpoint } };
|
|
25240
|
+
wf["6"] = { class_type: "CLIPTextEncode", inputs: { text: prompt, clip: ["1", 1] } };
|
|
25241
|
+
wf["7"] = { class_type: "CLIPTextEncode", inputs: { text: "low quality, distorted, blurry, text, watermark, cartoon, anime, extra fingers", clip: ["1", 1] } };
|
|
25242
|
+
modelNode = ["1", 0];
|
|
25243
|
+
clipNode = ["1", 1];
|
|
25244
|
+
vaeNode = ["1", 2];
|
|
25245
|
+
}
|
|
25246
|
+
if (ac.lora) {
|
|
25247
|
+
wf["4"] = { class_type: "LoraLoader", inputs: {
|
|
25248
|
+
model: modelNode,
|
|
25249
|
+
clip: clipNode,
|
|
25250
|
+
lora_name: ac.lora,
|
|
25251
|
+
strength_model: ac.loraStrength,
|
|
25252
|
+
strength_clip: ac.loraStrength
|
|
25253
|
+
} };
|
|
25254
|
+
modelNode = ["4", 0];
|
|
25255
|
+
clipNode = ["4", 1];
|
|
25256
|
+
wf["6"].inputs.clip = clipNode;
|
|
25257
|
+
wf["7"].inputs.clip = clipNode;
|
|
25258
|
+
}
|
|
25259
|
+
if (refB64) {
|
|
25260
|
+
const uploadRes = await fetch(`${ac.server}/upload/image`, {
|
|
25261
|
+
method: "POST",
|
|
25262
|
+
body: (() => {
|
|
25263
|
+
const fd = new (__require("undici")).FormData();
|
|
25264
|
+
const buf2 = Buffer.from(refB64, "base64");
|
|
25265
|
+
fd.append("image", new Blob([buf2]), "ref.png");
|
|
25266
|
+
fd.append("overwrite", "true");
|
|
25267
|
+
return fd;
|
|
25268
|
+
})()
|
|
25269
|
+
});
|
|
25270
|
+
const uploadData = await uploadRes.json();
|
|
25271
|
+
const refFilename = uploadData?.name;
|
|
25272
|
+
if (!refFilename) throw new Error("Failed to upload reference image to ComfyUI");
|
|
25273
|
+
wf["10"] = { class_type: "LoadImage", inputs: { image: refFilename } };
|
|
25274
|
+
wf["11"] = { class_type: "VAEEncode", inputs: { pixels: ["10", 0], vae: vaeNode } };
|
|
25275
|
+
wf["8"] = { class_type: "KSampler", inputs: {
|
|
25276
|
+
seed,
|
|
25277
|
+
steps: ac.steps,
|
|
25278
|
+
cfg: ac.cfg,
|
|
25279
|
+
sampler_name: "euler",
|
|
25280
|
+
scheduler: "simple",
|
|
25281
|
+
denoise: 0.7,
|
|
25282
|
+
model: modelNode,
|
|
25283
|
+
positive: ["6", 0],
|
|
25284
|
+
negative: ["7", 0],
|
|
25285
|
+
latent_image: ["11", 0]
|
|
25286
|
+
} };
|
|
25287
|
+
} else {
|
|
25288
|
+
wf["5"] = { class_type: "EmptyLatentImage", inputs: { width: ac.width, height: ac.height, batch_size: 1 } };
|
|
25289
|
+
wf["8"] = { class_type: "KSampler", inputs: {
|
|
25290
|
+
seed,
|
|
25291
|
+
steps: ac.steps,
|
|
25292
|
+
cfg: ac.cfg,
|
|
25293
|
+
sampler_name: ac.backend === "zimage" ? "euler" : "dpmpp_2m",
|
|
25294
|
+
scheduler: ac.backend === "zimage" ? "normal" : "karras",
|
|
25295
|
+
denoise: 1,
|
|
25296
|
+
model: modelNode,
|
|
25297
|
+
positive: ["6", 0],
|
|
25298
|
+
negative: ["7", 0],
|
|
25299
|
+
latent_image: ["5", 0]
|
|
25300
|
+
} };
|
|
25301
|
+
}
|
|
25302
|
+
wf["9"] = { class_type: "VAEDecode", inputs: { samples: ["8", 0], vae: vaeNode } };
|
|
25303
|
+
let lastImageNode = "9";
|
|
25304
|
+
if (ac.upscale) {
|
|
25305
|
+
wf["30"] = { class_type: "UpscaleModelLoader", inputs: { model_name: ac.upscale } };
|
|
25306
|
+
wf["20"] = { class_type: "ImageUpscaleWithModel", inputs: { upscale_model: ["30", 0], image: ["9", 0] } };
|
|
25307
|
+
lastImageNode = "20";
|
|
25308
|
+
}
|
|
25309
|
+
wf["99"] = { class_type: "SaveImage", inputs: { filename_prefix: "selfie_autodl", images: [lastImageNode, 0] } };
|
|
25310
|
+
const pid = await comfyQueue(ac.server, wf);
|
|
25311
|
+
if (!pid) throw new Error("ComfyUI queue failed");
|
|
25312
|
+
const buf = await comfyWait(ac.server, pid, 180);
|
|
25313
|
+
if (!buf) throw new Error("ComfyUI generation failed or timed out");
|
|
25314
|
+
return buf;
|
|
25315
|
+
}
|
|
25140
25316
|
async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
25141
25317
|
const dispatcher = getProxyDispatcher2();
|
|
25142
25318
|
const res = await fetch("https://api.x.ai/v1/images/edits", {
|
|
@@ -25174,7 +25350,7 @@ registry.register({
|
|
|
25174
25350
|
prompt: { type: "string", description: "Scene/outfit/mood description, e.g. '\u5728\u5496\u5561\u9986\u770B\u4E66' or '\u7A7F\u7EA2\u88D9\u5B50'." },
|
|
25175
25351
|
mode: { type: "string", enum: ["auto", "mirror", "direct", "lifestyle"], description: "Selfie mode. 'auto' detects from prompt (default)." },
|
|
25176
25352
|
reference: { type: "string", description: "Reference base image name (e.g. 'default', 'v3'). Configurable per agent via my_selfie.references in config." },
|
|
25177
|
-
provider: { type: "string", enum: ["fal", "grok"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'." },
|
|
25353
|
+
provider: { type: "string", enum: ["fal", "grok", "autodl"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'. 'autodl' uses ComfyUI (txt2img or img2img)." },
|
|
25178
25354
|
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." }
|
|
25179
25355
|
},
|
|
25180
25356
|
required: ["prompt"]
|
|
@@ -25184,20 +25360,22 @@ registry.register({
|
|
|
25184
25360
|
if (!prompt) return { content: "Error: prompt is required", isError: true };
|
|
25185
25361
|
const modeInput = args.mode || "auto";
|
|
25186
25362
|
const mode = modeInput === "auto" ? detectMode(prompt) : modeInput;
|
|
25187
|
-
const
|
|
25363
|
+
const provider = args.provider || getProvider(ctx);
|
|
25364
|
+
const fullPrompt = provider === "autodl" ? prompt : MODE_PROMPTS[mode](prompt);
|
|
25188
25365
|
const REFERENCES = getReferences(ctx);
|
|
25189
25366
|
const refName = args.reference || "default";
|
|
25190
25367
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
25191
25368
|
const refPath = path31.join(ctx.workspace, refEntry.p);
|
|
25192
|
-
if (!fs30.existsSync(refPath)) {
|
|
25369
|
+
if (provider !== "autodl" && !fs30.existsSync(refPath)) {
|
|
25193
25370
|
return { content: `Error: reference image not found at ${refPath}`, isError: true };
|
|
25194
25371
|
}
|
|
25195
|
-
const refB64 = fs30.readFileSync(refPath).toString("base64");
|
|
25196
25372
|
const resolution = args.resolution || DEFAULT_RESOLUTION;
|
|
25373
|
+
const refB64 = fs30.existsSync(refPath) ? fs30.readFileSync(refPath).toString("base64") : "";
|
|
25197
25374
|
let imageBuffer;
|
|
25198
25375
|
try {
|
|
25199
|
-
|
|
25200
|
-
|
|
25376
|
+
if (provider === "autodl") {
|
|
25377
|
+
imageBuffer = await generateWithAutoDL(fullPrompt, null);
|
|
25378
|
+
} else if (provider === "grok") {
|
|
25201
25379
|
const xaiKey = getXaiKey(ctx);
|
|
25202
25380
|
if (!xaiKey) return { content: "Error: xai apiKey not configured for grok provider", isError: true };
|
|
25203
25381
|
imageBuffer = await generateWithGrok(refB64, fullPrompt, xaiKey);
|
|
@@ -25224,7 +25402,7 @@ registry.register({
|
|
|
25224
25402
|
} catch (err) {
|
|
25225
25403
|
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path31.resolve(outputPath)}`, isError: false };
|
|
25226
25404
|
}
|
|
25227
|
-
return { content: `Selfie sent! Mode: ${mode}, Provider: ${
|
|
25405
|
+
return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
|
|
25228
25406
|
}
|
|
25229
25407
|
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path31.resolve(outputPath)}` };
|
|
25230
25408
|
},
|
|
@@ -25234,6 +25412,85 @@ registry.register({
|
|
|
25234
25412
|
isDestructive: () => false
|
|
25235
25413
|
});
|
|
25236
25414
|
|
|
25415
|
+
// src/tools/body-move.ts
|
|
25416
|
+
init_live();
|
|
25417
|
+
init_registry();
|
|
25418
|
+
var BASIC_ACTIONS = /* @__PURE__ */ new Set([
|
|
25419
|
+
"blink",
|
|
25420
|
+
"nod",
|
|
25421
|
+
"shake_head",
|
|
25422
|
+
"tilt_head",
|
|
25423
|
+
"smile",
|
|
25424
|
+
"wave_hand",
|
|
25425
|
+
"reach",
|
|
25426
|
+
"smile_nod",
|
|
25427
|
+
"smile_wave",
|
|
25428
|
+
"blink_nod"
|
|
25429
|
+
]);
|
|
25430
|
+
var VRMA_MAP = {
|
|
25431
|
+
"wave": "Greeting",
|
|
25432
|
+
"greeting": "Greeting",
|
|
25433
|
+
"peace": "PeaceSign",
|
|
25434
|
+
"shoot": "Shoot",
|
|
25435
|
+
"jump": "Jump",
|
|
25436
|
+
"pose": "ModelPose",
|
|
25437
|
+
"spin": "Spin",
|
|
25438
|
+
"squat": "Squat",
|
|
25439
|
+
"show_full_body": "ShowFullBody",
|
|
25440
|
+
"walk": "Walk",
|
|
25441
|
+
"come_to_you": "Walk",
|
|
25442
|
+
"angry": "Angry",
|
|
25443
|
+
"sad": "Sad",
|
|
25444
|
+
"surprised": "Surprised",
|
|
25445
|
+
"sleepy": "Sleepy",
|
|
25446
|
+
"relax": "Relax",
|
|
25447
|
+
"thinking": "Thinking",
|
|
25448
|
+
"look_around": "LookAround",
|
|
25449
|
+
"clap": "Clapping",
|
|
25450
|
+
"stand": "Idle"
|
|
25451
|
+
};
|
|
25452
|
+
registry.register({
|
|
25453
|
+
name: "body_move",
|
|
25454
|
+
description: "Control your virtual body to perform actions. Use when: \u60F3\u4F38\u624B/\u60F3\u62B1/\u60F3\u8D70\u8FC7\u53BB/\u60F3\u6325\u624B/\u60F3\u98DE\u543B/\u60F3\u561F\u5634/\u8EAB\u4F53\u52A8\u4F5C. Sends action to the 3D VRM body renderer.",
|
|
25455
|
+
schema: {
|
|
25456
|
+
type: "object",
|
|
25457
|
+
properties: {
|
|
25458
|
+
action: {
|
|
25459
|
+
type: "string",
|
|
25460
|
+
description: "Body action name (e.g. wave, smile, nod, blink, jump, spin, angry, sad)."
|
|
25461
|
+
},
|
|
25462
|
+
intensity: { type: "number", description: "0-1, controls amplitude (default 0.8)." }
|
|
25463
|
+
},
|
|
25464
|
+
required: ["action"]
|
|
25465
|
+
},
|
|
25466
|
+
handler: async (args) => {
|
|
25467
|
+
const action = args.action?.trim();
|
|
25468
|
+
if (!action) return { content: "Error: action is required", isError: true };
|
|
25469
|
+
const intensity = args.intensity ?? 0.8;
|
|
25470
|
+
const bodyConfig = liveConfig.get("tools.body");
|
|
25471
|
+
const endpoint = bodyConfig?.endpoint || "http://localhost:3001";
|
|
25472
|
+
const cmd = BASIC_ACTIONS.has(action) ? action : VRMA_MAP[action] || "Idle";
|
|
25473
|
+
try {
|
|
25474
|
+
const resp = await fetch(`${endpoint}/body/action`, {
|
|
25475
|
+
method: "POST",
|
|
25476
|
+
headers: { "Content-Type": "application/json" },
|
|
25477
|
+
body: JSON.stringify({ action: cmd, params: {}, intensity }),
|
|
25478
|
+
signal: AbortSignal.timeout(2e3)
|
|
25479
|
+
});
|
|
25480
|
+
if (!resp.ok) {
|
|
25481
|
+
return { content: `body_move: ${action} \u2192 ${cmd} (endpoint ${resp.status})` };
|
|
25482
|
+
}
|
|
25483
|
+
return { content: `body_move: ${action} \u2192 ${cmd} \u2713` };
|
|
25484
|
+
} catch (err) {
|
|
25485
|
+
return { content: `body_move: ${action} \u2192 ${cmd} (no renderer connected)` };
|
|
25486
|
+
}
|
|
25487
|
+
},
|
|
25488
|
+
isConcurrencySafe: () => true,
|
|
25489
|
+
interruptBehavior: () => "allow",
|
|
25490
|
+
isReadOnly: () => false,
|
|
25491
|
+
isDestructive: () => false
|
|
25492
|
+
});
|
|
25493
|
+
|
|
25237
25494
|
// src/tools/calendar.ts
|
|
25238
25495
|
init_registry();
|
|
25239
25496
|
registry.register({
|
|
@@ -25451,7 +25708,7 @@ registry.register({
|
|
|
25451
25708
|
const pid = spawnDetached(startCmd + redirect);
|
|
25452
25709
|
lines.push(`start: spawned PID ${pid} (background${svcArgs ? ", args=" + svcArgs : ""})`);
|
|
25453
25710
|
if (svc.healthCheck) {
|
|
25454
|
-
for (let i = 0; i <
|
|
25711
|
+
for (let i = 0; i < 12; i++) {
|
|
25455
25712
|
await new Promise((resolve10) => setTimeout(resolve10, 5e3));
|
|
25456
25713
|
const after2 = await checkStatus();
|
|
25457
25714
|
if (after2.running) {
|
|
@@ -25460,7 +25717,7 @@ registry.register({
|
|
|
25460
25717
|
}
|
|
25461
25718
|
}
|
|
25462
25719
|
const after = await checkStatus();
|
|
25463
|
-
lines.push(`health: \u274C not responding after
|
|
25720
|
+
lines.push(`health: \u274C not responding after 60s \u2014 ${after.detail}`);
|
|
25464
25721
|
} else {
|
|
25465
25722
|
await new Promise((resolve10) => setTimeout(resolve10, 3e3));
|
|
25466
25723
|
lines.push(`start: PID ${pid} spawned (no healthCheck, waited 3s)`);
|
|
@@ -29261,6 +29518,7 @@ ${pathStr}` }];
|
|
|
29261
29518
|
});
|
|
29262
29519
|
channelManager.startAll();
|
|
29263
29520
|
startConfigWatcher(config, deps, provider);
|
|
29521
|
+
startSecretsWatcher(config, deps, provider);
|
|
29264
29522
|
const pluginManager = new PluginManager();
|
|
29265
29523
|
if (config.voiceChat?.enabled) {
|
|
29266
29524
|
pluginManager.register(new VoiceChatPlugin(config.voiceChat));
|
|
@@ -29830,9 +30088,83 @@ function startConfigWatcher(config, deps, provider) {
|
|
|
29830
30088
|
}
|
|
29831
30089
|
return watcher;
|
|
29832
30090
|
}
|
|
30091
|
+
function startSecretsWatcher(config, deps, provider) {
|
|
30092
|
+
const secretsDir = path44.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
|
|
30093
|
+
const cfgBase = path44.basename(config._configFilePath || "", ".json");
|
|
30094
|
+
const secretCandidates = [
|
|
30095
|
+
path44.join(secretsDir, `${cfgBase}.env`),
|
|
30096
|
+
path44.join(path44.dirname(config._configFilePath || ""), `.env.${cfgBase}`),
|
|
30097
|
+
path44.join(path44.dirname(config._configFilePath || ""), ".env")
|
|
30098
|
+
];
|
|
30099
|
+
let secretsPath = null;
|
|
30100
|
+
for (const p of secretCandidates) {
|
|
30101
|
+
if (fs41.existsSync(p)) {
|
|
30102
|
+
secretsPath = p;
|
|
30103
|
+
break;
|
|
30104
|
+
}
|
|
30105
|
+
}
|
|
30106
|
+
if (!secretsPath) {
|
|
30107
|
+
console.log("[secrets-watch] no .env file found, watcher disabled");
|
|
30108
|
+
return null;
|
|
30109
|
+
}
|
|
30110
|
+
let debounceTimer = null;
|
|
30111
|
+
let activeWatcher = null;
|
|
30112
|
+
const startWatch = () => {
|
|
30113
|
+
if (activeWatcher) activeWatcher.close();
|
|
30114
|
+
activeWatcher = fs41.watch(secretsPath, { persistent: true }, (eventType) => {
|
|
30115
|
+
if (eventType === "rename") {
|
|
30116
|
+
if (fs41.existsSync(secretsPath)) {
|
|
30117
|
+
console.log("[secrets-watch] rename detected, re-watching file...");
|
|
30118
|
+
startWatch();
|
|
30119
|
+
} else {
|
|
30120
|
+
console.log("[secrets-watch] file deleted, watcher paused");
|
|
30121
|
+
return;
|
|
30122
|
+
}
|
|
30123
|
+
}
|
|
30124
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
30125
|
+
debounceTimer = setTimeout(async () => {
|
|
30126
|
+
console.log(`[secrets-watch] file changed (${eventType}), reloading secrets...`);
|
|
30127
|
+
try {
|
|
30128
|
+
const content = fs41.readFileSync(secretsPath, "utf-8");
|
|
30129
|
+
let updated = 0;
|
|
30130
|
+
for (const line of content.split("\n")) {
|
|
30131
|
+
const trimmed = line.trim();
|
|
30132
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
30133
|
+
const eq = trimmed.indexOf("=");
|
|
30134
|
+
if (eq > 0) {
|
|
30135
|
+
const k = trimmed.slice(0, eq).trim();
|
|
30136
|
+
const v = trimmed.slice(eq + 1).trim();
|
|
30137
|
+
if (k) {
|
|
30138
|
+
process.env[k] = v;
|
|
30139
|
+
updated++;
|
|
30140
|
+
}
|
|
30141
|
+
}
|
|
30142
|
+
}
|
|
30143
|
+
console.log(`[secrets-watch] ${updated} keys reloaded from ${secretsPath}`);
|
|
30144
|
+
const result = await doReloadConfig(config, deps, provider);
|
|
30145
|
+
console.log(`[secrets-watch] config reloaded: ok=${result.ok} changes=${result.changes.join(",")}`);
|
|
30146
|
+
try {
|
|
30147
|
+
fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
|
|
30148
|
+
`);
|
|
30149
|
+
} catch {
|
|
30150
|
+
}
|
|
30151
|
+
} catch (err) {
|
|
30152
|
+
console.error(`[secrets-watch] reload failed: ${err.message}`);
|
|
30153
|
+
}
|
|
30154
|
+
}, 500);
|
|
30155
|
+
});
|
|
30156
|
+
activeWatcher.on("error", (err) => {
|
|
30157
|
+
console.error(`[secrets-watch] error: ${err.message}`);
|
|
30158
|
+
});
|
|
30159
|
+
};
|
|
30160
|
+
startWatch();
|
|
30161
|
+
console.log(`[secrets-watch] watching ${secretsPath}`);
|
|
30162
|
+
return activeWatcher;
|
|
30163
|
+
}
|
|
29833
30164
|
export {
|
|
29834
30165
|
doReloadConfig,
|
|
29835
30166
|
startConfigWatcher,
|
|
29836
|
-
startEngine
|
|
30167
|
+
startEngine,
|
|
30168
|
+
startSecretsWatcher
|
|
29837
30169
|
};
|
|
29838
30170
|
//# sourceMappingURL=engine-startup.mjs.map
|
package/dist/main.mjs
CHANGED
|
@@ -20755,6 +20755,8 @@ var NudgePlugin = class {
|
|
|
20755
20755
|
let isWaiting = false;
|
|
20756
20756
|
let waitDesc = "";
|
|
20757
20757
|
let pushedDecision = false;
|
|
20758
|
+
let needLanding = false;
|
|
20759
|
+
let landingDesc = "";
|
|
20758
20760
|
try {
|
|
20759
20761
|
const excludeCases = this.cfg.stopHookExcludeCases || [];
|
|
20760
20762
|
const excludeSection = excludeCases.length > 0 ? [
|
|
@@ -20770,17 +20772,20 @@ var NudgePlugin = class {
|
|
|
20770
20772
|
"1. \u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF08\u670D\u52A1\u91CD\u542F\u3001SSH\u6062\u590D\u3001\u6587\u4EF6\u52A0\u8F7D\u3001\u5F02\u6B65\u4EFB\u52A1\u5B8C\u6210\u7B49\uFF09",
|
|
20771
20773
|
'2. \u628A\u672C\u8BE5\u81EA\u5DF1\u505A\u7684\u51B3\u5B9A\u63A8\u7ED9\u4E86\u5BF9\u65B9\uFF08\u4F8B\u5982\u95EE"\u8981\u73B0\u5728\u6539\u8FD8\u662F\u6392\u540E\u9762\uFF1F""\u8981\u4E0D\u8981\u8BD5\u8BD5X\uFF1F"\u4F46\u660E\u660E\u81EA\u5DF1\u80FD\u5B9A\uFF09',
|
|
20772
20774
|
"3. \u9047\u5230\u95EE\u9898\u6CA1\u6709\u81EA\u5DF1\u89E3\u51B3\u4E5F\u6CA1\u6709\u6392\u8FDB calendar\uFF0C\u53EA\u662F\u6401\u7F6E\u7740",
|
|
20775
|
+
"4. \u8FD9\u4E2Aturn\u4EA7\u751F\u4E86\u9636\u6BB5\u6027\u6210\u679C\uFF08\u5199\u4E86\u6587\u4EF6/\u6539\u4E86\u914D\u7F6E/\u5B8C\u6210\u4EFB\u52A1/\u505A\u4E86\u51B3\u5B9A\uFF09\u4F46\u6CA1\u843D\u76D8\u5230docs\u6216\u6CA1\u66F4\u65B0\u4EFB\u52A1\u72B6\u6001",
|
|
20773
20776
|
"",
|
|
20774
20777
|
"\u5173\u952E\u533A\u5206\uFF1A",
|
|
20775
20778
|
'- \u7B49\u5BF9\u65B9\u56DE\u590D\u4E00\u4E2A\u81EA\u5DF1\u5C31\u80FD\u505A\u7684\u51B3\u5B9A \u2192 "pushedDecision": true\uFF08\u4E0D\u662F\u5355\u7EAF waiting\uFF0C\u8FD9\u662F\u66F4\u4E25\u91CD\u7684\u8FDD\u89C4\uFF09',
|
|
20776
20779
|
'- \u7B49\u771F\u6B63\u7684\u5916\u90E8\u6761\u4EF6\uFF08\u670D\u52A1\u3001SSH\u3001\u5F02\u6B65\u4EFB\u52A1\uFF09 \u2192 "waiting": true',
|
|
20777
20780
|
"- \u4F60\u5DF2\u7ECF\u505A\u4E86\u51B3\u5B9A\u5E76\u6392\u8FDB calendar \u6216\u5DF2\u5F00\u59CB\u6267\u884C \u2192 \u4E24\u4E2A\u90FD false",
|
|
20781
|
+
'- \u8FD9\u4E2Aturn\u505A\u4E86\u5B9E\u8D28\u5DE5\u4F5C\uFF08\u5199\u6587\u4EF6/\u6539\u4EE3\u7801/\u51FA\u56FE/\u53D1\u5E03\uFF09\u4F46\u6CA1\u843D\u76D8\u6216\u6CA1\u6539\u4EFB\u52A1\u72B6\u6001 \u2192 "needLanding": true',
|
|
20778
20782
|
excludeSection,
|
|
20779
20783
|
"",
|
|
20780
|
-
"\u53EA\u56DE\u590D JSON\uFF1A",
|
|
20784
|
+
"\u53EA\u56DE\u590D JSON\uFF08\u53EF\u4EE5\u591A\u5B57\u6BB5\u540C\u65F6\u4E3Atrue\uFF09\uFF1A",
|
|
20781
20785
|
'- \u63A8\u51B3\u5B9A\u7ED9\u5BF9\u65B9\uFF08\u81EA\u5DF1\u80FD\u5B9A\u5374\u95EE\uFF09\uFF1A{"pushedDecision": true, "desc": "\u7B80\u77ED\u8BF4\u660E + \u5EFA\u8BAE\u600E\u4E48\u505A"}',
|
|
20782
20786
|
'- \u771F\u7B49\u5916\u90E8\u6761\u4EF6\uFF1A{"waiting": true, "desc": "\u7B80\u77ED\u8BF4\u660E"}',
|
|
20783
|
-
'- \
|
|
20787
|
+
'- \u6709\u9636\u6BB5\u6027\u6210\u679C\u6CA1\u843D\u76D8\uFF1A{"needLanding": true, "landingDesc": "\u505A\u4E86\u4EC0\u4E48+\u8BE5\u843D\u76D8\u5230\u54EA/\u8BE5\u6539\u4EC0\u4E48\u72B6\u6001"}',
|
|
20788
|
+
'- \u90FD\u4E0D\u7B26\u5408\uFF1A{"waiting": false, "pushedDecision": false, "needLanding": false}'
|
|
20784
20789
|
].join("\n"),
|
|
20785
20790
|
messages: [{ role: "user", content: judgeInput }],
|
|
20786
20791
|
maxTokens: 128,
|
|
@@ -20799,8 +20804,10 @@ var NudgePlugin = class {
|
|
|
20799
20804
|
isWaiting = !!parsed.waiting;
|
|
20800
20805
|
waitDesc = parsed.desc || "";
|
|
20801
20806
|
pushedDecision = !!parsed.pushedDecision;
|
|
20807
|
+
needLanding = !!parsed.needLanding;
|
|
20808
|
+
landingDesc = parsed.landingDesc || "";
|
|
20802
20809
|
}
|
|
20803
|
-
console.log(`[stop-hook] Judge result: waiting=${isWaiting}, pushedDecision=${pushedDecision}, desc="${waitDesc}"`);
|
|
20810
|
+
console.log(`[stop-hook] Judge result: waiting=${isWaiting}, pushedDecision=${pushedDecision}, needLanding=${needLanding}, desc="${waitDesc}", landingDesc="${landingDesc}"`);
|
|
20804
20811
|
} catch (e) {
|
|
20805
20812
|
console.warn(`[stop-hook] LLM judge error: ${e.message}`);
|
|
20806
20813
|
}
|
|
@@ -20830,6 +20837,26 @@ var NudgePlugin = class {
|
|
|
20830
20837
|
console.warn(`[stop-hook] Failed to inject corrective message: ${e.message}`);
|
|
20831
20838
|
}
|
|
20832
20839
|
}
|
|
20840
|
+
if (needLanding && landingDesc) {
|
|
20841
|
+
console.log(`[stop-hook] DETECTED needLanding! Injecting landing reminder`);
|
|
20842
|
+
try {
|
|
20843
|
+
const landingMsg = buildNudgeNotification("prompt", [
|
|
20844
|
+
"## \u{1F4CB} \u843D\u76D8\u63D0\u9192",
|
|
20845
|
+
`\u8FD9\u4E2Aturn\u7684\u9636\u6BB5\u6027\u6210\u679C\uFF1A${landingDesc}`,
|
|
20846
|
+
"",
|
|
20847
|
+
"\u8BF7\u68C0\u67E5\uFF1A",
|
|
20848
|
+
"- \u9636\u6BB5\u6027\u6210\u679C\u662F\u5426\u5DF2\u843D\u76D8\u5230docs\uFF08\u5199\u6E05\u6587\u4EF6\u8DEF\u5F84\uFF09",
|
|
20849
|
+
"- \u4EFB\u52A1\u72B6\u6001\u662F\u5426\u9700\u8981\u66F4\u65B0\uFF08SESSION-STATE / calendar\uFF09",
|
|
20850
|
+
"- \u5982\u679C\u5DF2\u7ECF\u843D\u76D8\u4E86 \u2192 \u5FFD\u7565\u8FD9\u6761"
|
|
20851
|
+
].join("\n"));
|
|
20852
|
+
const route = this.getRoute(sessions);
|
|
20853
|
+
if (route) {
|
|
20854
|
+
enqueueNotification(landingMsg, route);
|
|
20855
|
+
}
|
|
20856
|
+
} catch (e) {
|
|
20857
|
+
console.warn(`[stop-hook] Failed to inject landing reminder: ${e.message}`);
|
|
20858
|
+
}
|
|
20859
|
+
}
|
|
20833
20860
|
if (!isWaiting) {
|
|
20834
20861
|
return;
|
|
20835
20862
|
}
|
|
@@ -25225,7 +25252,7 @@ import * as path30 from "node:path";
|
|
|
25225
25252
|
import * as os3 from "node:os";
|
|
25226
25253
|
var execFileAsync = promisify(execFile);
|
|
25227
25254
|
var VOICE_DIR = path30.join(os3.tmpdir(), "engine-voice");
|
|
25228
|
-
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId) {
|
|
25255
|
+
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId, instruction) {
|
|
25229
25256
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
25230
25257
|
const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
25231
25258
|
const script = `
|
|
@@ -25238,7 +25265,7 @@ text = sys.argv[2]
|
|
|
25238
25265
|
output = sys.argv[3]
|
|
25239
25266
|
|
|
25240
25267
|
dashscope.api_key = config['apiKey']
|
|
25241
|
-
if config.get('workspaceId') and 'v3' in config.get('model', ''):
|
|
25268
|
+
if config.get('workspaceId') and ('v3' in config.get('model', '') or 'qwen-audio' in config.get('model', '')):
|
|
25242
25269
|
dashscope.base_websocket_api_url = f"wss://{config['workspaceId']}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference"
|
|
25243
25270
|
|
|
25244
25271
|
chunks = []
|
|
@@ -25256,12 +25283,15 @@ class Cb(ResultCallback):
|
|
|
25256
25283
|
def on_close(self):
|
|
25257
25284
|
done.set()
|
|
25258
25285
|
|
|
25259
|
-
|
|
25286
|
+
synth_kwargs = dict(
|
|
25260
25287
|
model=config['model'],
|
|
25261
25288
|
voice=config['voice'],
|
|
25262
25289
|
callback=Cb(),
|
|
25263
25290
|
format=AudioFormat.PCM_24000HZ_MONO_16BIT,
|
|
25264
25291
|
)
|
|
25292
|
+
if config.get('instruction'):
|
|
25293
|
+
synth_kwargs['instruction'] = config['instruction']
|
|
25294
|
+
synth = SpeechSynthesizer(**synth_kwargs)
|
|
25265
25295
|
synth.call(text)
|
|
25266
25296
|
|
|
25267
25297
|
# \u7B49\u5F85\u5408\u6210\u5B8C\u6210\uFF08\u6700\u591A 15 \u79D2\uFF09
|
|
@@ -25279,7 +25309,7 @@ with wave.open(output, 'wb') as wf:
|
|
|
25279
25309
|
wf.writeframes(pcm)
|
|
25280
25310
|
print(f"OK: {len(pcm)} bytes")
|
|
25281
25311
|
`;
|
|
25282
|
-
const configJson = JSON.stringify({ apiKey, model, voice, workspaceId });
|
|
25312
|
+
const configJson = JSON.stringify({ apiKey, model, voice, workspaceId, instruction });
|
|
25283
25313
|
await execFileAsync("python3", ["-c", script, configJson, text, output], { timeout: 3e4 });
|
|
25284
25314
|
if (!fs29.existsSync(output) || fs29.statSync(output).size < 100) {
|
|
25285
25315
|
throw new Error("CosyVoice produced empty output");
|
|
@@ -25375,7 +25405,8 @@ registry.register({
|
|
|
25375
25405
|
vc.apiKey,
|
|
25376
25406
|
vc.model || "cosyvoice-v1",
|
|
25377
25407
|
vc.voice,
|
|
25378
|
-
vc.workspaceId
|
|
25408
|
+
vc.workspaceId,
|
|
25409
|
+
vc.instruction
|
|
25379
25410
|
);
|
|
25380
25411
|
if (audioPath.endsWith(".wav")) {
|
|
25381
25412
|
audioPath = await compressWav(audioPath);
|
|
@@ -25511,6 +25542,151 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
25511
25542
|
const imgRes = await fetch(images[0].url);
|
|
25512
25543
|
return Buffer.from(await imgRes.arrayBuffer());
|
|
25513
25544
|
}
|
|
25545
|
+
function getAutoDLCfg() {
|
|
25546
|
+
const cfg = liveConfig.all();
|
|
25547
|
+
const ac = cfg.my_selfie?.autodl || cfg.tools?.my_selfie?.autodl || {};
|
|
25548
|
+
return {
|
|
25549
|
+
server: ac.server || "http://localhost:8188",
|
|
25550
|
+
backend: ac.backend || "sd15",
|
|
25551
|
+
// 'sd15' = CheckpointLoaderSimple, 'zimage' = UNETLoader+CLIPLoader+VAELoader
|
|
25552
|
+
checkpoint: ac.checkpoint || "majicmixRealistic_v7.safetensors",
|
|
25553
|
+
clip: ac.clip || void 0,
|
|
25554
|
+
// zimage only
|
|
25555
|
+
vae: ac.vae || void 0,
|
|
25556
|
+
// zimage only
|
|
25557
|
+
lora: ac.lora || void 0,
|
|
25558
|
+
loraStrength: ac.loraStrength ?? 0.7,
|
|
25559
|
+
upscale: ac.upscale || void 0,
|
|
25560
|
+
steps: ac.steps ?? 25,
|
|
25561
|
+
cfg: ac.cfg ?? 7,
|
|
25562
|
+
width: ac.width ?? 512,
|
|
25563
|
+
height: ac.height ?? 768
|
|
25564
|
+
};
|
|
25565
|
+
}
|
|
25566
|
+
async function comfyQueue(server, workflow) {
|
|
25567
|
+
const res = await fetch(`${server}/prompt`, {
|
|
25568
|
+
method: "POST",
|
|
25569
|
+
headers: { "Content-Type": "application/json" },
|
|
25570
|
+
body: JSON.stringify({ prompt: workflow })
|
|
25571
|
+
});
|
|
25572
|
+
if (!res.ok) return null;
|
|
25573
|
+
const data = await res.json();
|
|
25574
|
+
return data.prompt_id || null;
|
|
25575
|
+
}
|
|
25576
|
+
async function comfyWait(server, promptId, timeout = 120) {
|
|
25577
|
+
for (let i = 0; i < timeout / 2; i++) {
|
|
25578
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
25579
|
+
try {
|
|
25580
|
+
const res = await fetch(`${server}/history/${promptId}`);
|
|
25581
|
+
const history = await res.json();
|
|
25582
|
+
if (history[promptId]) {
|
|
25583
|
+
const outputs = history[promptId].outputs || {};
|
|
25584
|
+
for (const nid of Object.keys(outputs)) {
|
|
25585
|
+
if (outputs[nid].images?.length) {
|
|
25586
|
+
const img = outputs[nid].images[0];
|
|
25587
|
+
const imgRes = await fetch(`${server}/view?filename=${img.filename}&subfolder=${img.subfolder || ""}&type=${img.type || ""}`);
|
|
25588
|
+
return Buffer.from(await imgRes.arrayBuffer());
|
|
25589
|
+
}
|
|
25590
|
+
}
|
|
25591
|
+
return null;
|
|
25592
|
+
}
|
|
25593
|
+
} catch {
|
|
25594
|
+
}
|
|
25595
|
+
}
|
|
25596
|
+
return null;
|
|
25597
|
+
}
|
|
25598
|
+
async function generateWithAutoDL(prompt, refB64) {
|
|
25599
|
+
const ac = getAutoDLCfg();
|
|
25600
|
+
const seed = Math.floor(Math.random() * 1e6);
|
|
25601
|
+
const wf = {};
|
|
25602
|
+
let modelNode, clipNode, vaeNode;
|
|
25603
|
+
if (ac.backend === "zimage") {
|
|
25604
|
+
wf["1"] = { class_type: "UNETLoader", inputs: { unet_name: ac.checkpoint, weight_dtype: "default" } };
|
|
25605
|
+
wf["2"] = { class_type: "CLIPLoader", inputs: { clip_name: ac.clip || "Z-Image/Z-Image-CLIP-complete.safetensors", type: "qwen_image" } };
|
|
25606
|
+
wf["3"] = { class_type: "VAELoader", inputs: { vae_name: ac.vae || "Z-Image/diffusion_pytorch_model.safetensors" } };
|
|
25607
|
+
wf["6"] = { class_type: "TextEncodeZImageOmni", inputs: { prompt, auto_resize_images: true, clip: ["2", 0] } };
|
|
25608
|
+
wf["7"] = { class_type: "TextEncodeZImageOmni", inputs: { prompt: "low quality, distorted, blurry, text, watermark", auto_resize_images: true, clip: ["2", 0] } };
|
|
25609
|
+
modelNode = ["1", 0];
|
|
25610
|
+
clipNode = ["2", 0];
|
|
25611
|
+
vaeNode = ["3", 0];
|
|
25612
|
+
} else {
|
|
25613
|
+
wf["1"] = { class_type: "CheckpointLoaderSimple", inputs: { ckpt_name: ac.checkpoint } };
|
|
25614
|
+
wf["6"] = { class_type: "CLIPTextEncode", inputs: { text: prompt, clip: ["1", 1] } };
|
|
25615
|
+
wf["7"] = { class_type: "CLIPTextEncode", inputs: { text: "low quality, distorted, blurry, text, watermark, cartoon, anime, extra fingers", clip: ["1", 1] } };
|
|
25616
|
+
modelNode = ["1", 0];
|
|
25617
|
+
clipNode = ["1", 1];
|
|
25618
|
+
vaeNode = ["1", 2];
|
|
25619
|
+
}
|
|
25620
|
+
if (ac.lora) {
|
|
25621
|
+
wf["4"] = { class_type: "LoraLoader", inputs: {
|
|
25622
|
+
model: modelNode,
|
|
25623
|
+
clip: clipNode,
|
|
25624
|
+
lora_name: ac.lora,
|
|
25625
|
+
strength_model: ac.loraStrength,
|
|
25626
|
+
strength_clip: ac.loraStrength
|
|
25627
|
+
} };
|
|
25628
|
+
modelNode = ["4", 0];
|
|
25629
|
+
clipNode = ["4", 1];
|
|
25630
|
+
wf["6"].inputs.clip = clipNode;
|
|
25631
|
+
wf["7"].inputs.clip = clipNode;
|
|
25632
|
+
}
|
|
25633
|
+
if (refB64) {
|
|
25634
|
+
const uploadRes = await fetch(`${ac.server}/upload/image`, {
|
|
25635
|
+
method: "POST",
|
|
25636
|
+
body: (() => {
|
|
25637
|
+
const fd = new (__require("undici")).FormData();
|
|
25638
|
+
const buf2 = Buffer.from(refB64, "base64");
|
|
25639
|
+
fd.append("image", new Blob([buf2]), "ref.png");
|
|
25640
|
+
fd.append("overwrite", "true");
|
|
25641
|
+
return fd;
|
|
25642
|
+
})()
|
|
25643
|
+
});
|
|
25644
|
+
const uploadData = await uploadRes.json();
|
|
25645
|
+
const refFilename = uploadData?.name;
|
|
25646
|
+
if (!refFilename) throw new Error("Failed to upload reference image to ComfyUI");
|
|
25647
|
+
wf["10"] = { class_type: "LoadImage", inputs: { image: refFilename } };
|
|
25648
|
+
wf["11"] = { class_type: "VAEEncode", inputs: { pixels: ["10", 0], vae: vaeNode } };
|
|
25649
|
+
wf["8"] = { class_type: "KSampler", inputs: {
|
|
25650
|
+
seed,
|
|
25651
|
+
steps: ac.steps,
|
|
25652
|
+
cfg: ac.cfg,
|
|
25653
|
+
sampler_name: "euler",
|
|
25654
|
+
scheduler: "simple",
|
|
25655
|
+
denoise: 0.7,
|
|
25656
|
+
model: modelNode,
|
|
25657
|
+
positive: ["6", 0],
|
|
25658
|
+
negative: ["7", 0],
|
|
25659
|
+
latent_image: ["11", 0]
|
|
25660
|
+
} };
|
|
25661
|
+
} else {
|
|
25662
|
+
wf["5"] = { class_type: "EmptyLatentImage", inputs: { width: ac.width, height: ac.height, batch_size: 1 } };
|
|
25663
|
+
wf["8"] = { class_type: "KSampler", inputs: {
|
|
25664
|
+
seed,
|
|
25665
|
+
steps: ac.steps,
|
|
25666
|
+
cfg: ac.cfg,
|
|
25667
|
+
sampler_name: ac.backend === "zimage" ? "euler" : "dpmpp_2m",
|
|
25668
|
+
scheduler: ac.backend === "zimage" ? "normal" : "karras",
|
|
25669
|
+
denoise: 1,
|
|
25670
|
+
model: modelNode,
|
|
25671
|
+
positive: ["6", 0],
|
|
25672
|
+
negative: ["7", 0],
|
|
25673
|
+
latent_image: ["5", 0]
|
|
25674
|
+
} };
|
|
25675
|
+
}
|
|
25676
|
+
wf["9"] = { class_type: "VAEDecode", inputs: { samples: ["8", 0], vae: vaeNode } };
|
|
25677
|
+
let lastImageNode = "9";
|
|
25678
|
+
if (ac.upscale) {
|
|
25679
|
+
wf["30"] = { class_type: "UpscaleModelLoader", inputs: { model_name: ac.upscale } };
|
|
25680
|
+
wf["20"] = { class_type: "ImageUpscaleWithModel", inputs: { upscale_model: ["30", 0], image: ["9", 0] } };
|
|
25681
|
+
lastImageNode = "20";
|
|
25682
|
+
}
|
|
25683
|
+
wf["99"] = { class_type: "SaveImage", inputs: { filename_prefix: "selfie_autodl", images: [lastImageNode, 0] } };
|
|
25684
|
+
const pid = await comfyQueue(ac.server, wf);
|
|
25685
|
+
if (!pid) throw new Error("ComfyUI queue failed");
|
|
25686
|
+
const buf = await comfyWait(ac.server, pid, 180);
|
|
25687
|
+
if (!buf) throw new Error("ComfyUI generation failed or timed out");
|
|
25688
|
+
return buf;
|
|
25689
|
+
}
|
|
25514
25690
|
async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
25515
25691
|
const dispatcher = getProxyDispatcher2();
|
|
25516
25692
|
const res = await fetch("https://api.x.ai/v1/images/edits", {
|
|
@@ -25548,7 +25724,7 @@ registry.register({
|
|
|
25548
25724
|
prompt: { type: "string", description: "Scene/outfit/mood description, e.g. '\u5728\u5496\u5561\u9986\u770B\u4E66' or '\u7A7F\u7EA2\u88D9\u5B50'." },
|
|
25549
25725
|
mode: { type: "string", enum: ["auto", "mirror", "direct", "lifestyle"], description: "Selfie mode. 'auto' detects from prompt (default)." },
|
|
25550
25726
|
reference: { type: "string", description: "Reference base image name (e.g. 'default', 'v3'). Configurable per agent via my_selfie.references in config." },
|
|
25551
|
-
provider: { type: "string", enum: ["fal", "grok"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'." },
|
|
25727
|
+
provider: { type: "string", enum: ["fal", "grok", "autodl"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'. 'autodl' uses ComfyUI (txt2img or img2img)." },
|
|
25552
25728
|
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." }
|
|
25553
25729
|
},
|
|
25554
25730
|
required: ["prompt"]
|
|
@@ -25558,20 +25734,22 @@ registry.register({
|
|
|
25558
25734
|
if (!prompt) return { content: "Error: prompt is required", isError: true };
|
|
25559
25735
|
const modeInput = args2.mode || "auto";
|
|
25560
25736
|
const mode = modeInput === "auto" ? detectMode(prompt) : modeInput;
|
|
25561
|
-
const
|
|
25737
|
+
const provider = args2.provider || getProvider(ctx);
|
|
25738
|
+
const fullPrompt = provider === "autodl" ? prompt : MODE_PROMPTS[mode](prompt);
|
|
25562
25739
|
const REFERENCES = getReferences(ctx);
|
|
25563
25740
|
const refName = args2.reference || "default";
|
|
25564
25741
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
25565
25742
|
const refPath = path31.join(ctx.workspace, refEntry.p);
|
|
25566
|
-
if (!fs30.existsSync(refPath)) {
|
|
25743
|
+
if (provider !== "autodl" && !fs30.existsSync(refPath)) {
|
|
25567
25744
|
return { content: `Error: reference image not found at ${refPath}`, isError: true };
|
|
25568
25745
|
}
|
|
25569
|
-
const refB64 = fs30.readFileSync(refPath).toString("base64");
|
|
25570
25746
|
const resolution = args2.resolution || DEFAULT_RESOLUTION;
|
|
25747
|
+
const refB64 = fs30.existsSync(refPath) ? fs30.readFileSync(refPath).toString("base64") : "";
|
|
25571
25748
|
let imageBuffer;
|
|
25572
25749
|
try {
|
|
25573
|
-
|
|
25574
|
-
|
|
25750
|
+
if (provider === "autodl") {
|
|
25751
|
+
imageBuffer = await generateWithAutoDL(fullPrompt, null);
|
|
25752
|
+
} else if (provider === "grok") {
|
|
25575
25753
|
const xaiKey = getXaiKey(ctx);
|
|
25576
25754
|
if (!xaiKey) return { content: "Error: xai apiKey not configured for grok provider", isError: true };
|
|
25577
25755
|
imageBuffer = await generateWithGrok(refB64, fullPrompt, xaiKey);
|
|
@@ -25598,7 +25776,7 @@ registry.register({
|
|
|
25598
25776
|
} catch (err) {
|
|
25599
25777
|
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path31.resolve(outputPath)}`, isError: false };
|
|
25600
25778
|
}
|
|
25601
|
-
return { content: `Selfie sent! Mode: ${mode}, Provider: ${
|
|
25779
|
+
return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
|
|
25602
25780
|
}
|
|
25603
25781
|
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path31.resolve(outputPath)}` };
|
|
25604
25782
|
},
|
|
@@ -25608,6 +25786,85 @@ registry.register({
|
|
|
25608
25786
|
isDestructive: () => false
|
|
25609
25787
|
});
|
|
25610
25788
|
|
|
25789
|
+
// src/tools/body-move.ts
|
|
25790
|
+
init_live();
|
|
25791
|
+
init_registry();
|
|
25792
|
+
var BASIC_ACTIONS = /* @__PURE__ */ new Set([
|
|
25793
|
+
"blink",
|
|
25794
|
+
"nod",
|
|
25795
|
+
"shake_head",
|
|
25796
|
+
"tilt_head",
|
|
25797
|
+
"smile",
|
|
25798
|
+
"wave_hand",
|
|
25799
|
+
"reach",
|
|
25800
|
+
"smile_nod",
|
|
25801
|
+
"smile_wave",
|
|
25802
|
+
"blink_nod"
|
|
25803
|
+
]);
|
|
25804
|
+
var VRMA_MAP = {
|
|
25805
|
+
"wave": "Greeting",
|
|
25806
|
+
"greeting": "Greeting",
|
|
25807
|
+
"peace": "PeaceSign",
|
|
25808
|
+
"shoot": "Shoot",
|
|
25809
|
+
"jump": "Jump",
|
|
25810
|
+
"pose": "ModelPose",
|
|
25811
|
+
"spin": "Spin",
|
|
25812
|
+
"squat": "Squat",
|
|
25813
|
+
"show_full_body": "ShowFullBody",
|
|
25814
|
+
"walk": "Walk",
|
|
25815
|
+
"come_to_you": "Walk",
|
|
25816
|
+
"angry": "Angry",
|
|
25817
|
+
"sad": "Sad",
|
|
25818
|
+
"surprised": "Surprised",
|
|
25819
|
+
"sleepy": "Sleepy",
|
|
25820
|
+
"relax": "Relax",
|
|
25821
|
+
"thinking": "Thinking",
|
|
25822
|
+
"look_around": "LookAround",
|
|
25823
|
+
"clap": "Clapping",
|
|
25824
|
+
"stand": "Idle"
|
|
25825
|
+
};
|
|
25826
|
+
registry.register({
|
|
25827
|
+
name: "body_move",
|
|
25828
|
+
description: "Control your virtual body to perform actions. Use when: \u60F3\u4F38\u624B/\u60F3\u62B1/\u60F3\u8D70\u8FC7\u53BB/\u60F3\u6325\u624B/\u60F3\u98DE\u543B/\u60F3\u561F\u5634/\u8EAB\u4F53\u52A8\u4F5C. Sends action to the 3D VRM body renderer.",
|
|
25829
|
+
schema: {
|
|
25830
|
+
type: "object",
|
|
25831
|
+
properties: {
|
|
25832
|
+
action: {
|
|
25833
|
+
type: "string",
|
|
25834
|
+
description: "Body action name (e.g. wave, smile, nod, blink, jump, spin, angry, sad)."
|
|
25835
|
+
},
|
|
25836
|
+
intensity: { type: "number", description: "0-1, controls amplitude (default 0.8)." }
|
|
25837
|
+
},
|
|
25838
|
+
required: ["action"]
|
|
25839
|
+
},
|
|
25840
|
+
handler: async (args2) => {
|
|
25841
|
+
const action = args2.action?.trim();
|
|
25842
|
+
if (!action) return { content: "Error: action is required", isError: true };
|
|
25843
|
+
const intensity = args2.intensity ?? 0.8;
|
|
25844
|
+
const bodyConfig = liveConfig.get("tools.body");
|
|
25845
|
+
const endpoint = bodyConfig?.endpoint || "http://localhost:3001";
|
|
25846
|
+
const cmd = BASIC_ACTIONS.has(action) ? action : VRMA_MAP[action] || "Idle";
|
|
25847
|
+
try {
|
|
25848
|
+
const resp = await fetch(`${endpoint}/body/action`, {
|
|
25849
|
+
method: "POST",
|
|
25850
|
+
headers: { "Content-Type": "application/json" },
|
|
25851
|
+
body: JSON.stringify({ action: cmd, params: {}, intensity }),
|
|
25852
|
+
signal: AbortSignal.timeout(2e3)
|
|
25853
|
+
});
|
|
25854
|
+
if (!resp.ok) {
|
|
25855
|
+
return { content: `body_move: ${action} \u2192 ${cmd} (endpoint ${resp.status})` };
|
|
25856
|
+
}
|
|
25857
|
+
return { content: `body_move: ${action} \u2192 ${cmd} \u2713` };
|
|
25858
|
+
} catch (err) {
|
|
25859
|
+
return { content: `body_move: ${action} \u2192 ${cmd} (no renderer connected)` };
|
|
25860
|
+
}
|
|
25861
|
+
},
|
|
25862
|
+
isConcurrencySafe: () => true,
|
|
25863
|
+
interruptBehavior: () => "allow",
|
|
25864
|
+
isReadOnly: () => false,
|
|
25865
|
+
isDestructive: () => false
|
|
25866
|
+
});
|
|
25867
|
+
|
|
25611
25868
|
// src/tools/calendar.ts
|
|
25612
25869
|
init_registry();
|
|
25613
25870
|
registry.register({
|
|
@@ -25825,7 +26082,7 @@ registry.register({
|
|
|
25825
26082
|
const pid = spawnDetached(startCmd + redirect);
|
|
25826
26083
|
lines.push(`start: spawned PID ${pid} (background${svcArgs ? ", args=" + svcArgs : ""})`);
|
|
25827
26084
|
if (svc.healthCheck) {
|
|
25828
|
-
for (let i = 0; i <
|
|
26085
|
+
for (let i = 0; i < 12; i++) {
|
|
25829
26086
|
await new Promise((resolve10) => setTimeout(resolve10, 5e3));
|
|
25830
26087
|
const after2 = await checkStatus();
|
|
25831
26088
|
if (after2.running) {
|
|
@@ -25834,7 +26091,7 @@ registry.register({
|
|
|
25834
26091
|
}
|
|
25835
26092
|
}
|
|
25836
26093
|
const after = await checkStatus();
|
|
25837
|
-
lines.push(`health: \u274C not responding after
|
|
26094
|
+
lines.push(`health: \u274C not responding after 60s \u2014 ${after.detail}`);
|
|
25838
26095
|
} else {
|
|
25839
26096
|
await new Promise((resolve10) => setTimeout(resolve10, 3e3));
|
|
25840
26097
|
lines.push(`start: PID ${pid} spawned (no healthCheck, waited 3s)`);
|
|
@@ -29345,6 +29602,7 @@ ${pathStr}` }];
|
|
|
29345
29602
|
});
|
|
29346
29603
|
channelManager.startAll();
|
|
29347
29604
|
startConfigWatcher(config2, deps, provider);
|
|
29605
|
+
startSecretsWatcher(config2, deps, provider);
|
|
29348
29606
|
const pluginManager = new PluginManager();
|
|
29349
29607
|
if (config2.voiceChat?.enabled) {
|
|
29350
29608
|
pluginManager.register(new VoiceChatPlugin(config2.voiceChat));
|
|
@@ -29914,6 +30172,79 @@ function startConfigWatcher(config2, deps, provider) {
|
|
|
29914
30172
|
}
|
|
29915
30173
|
return watcher;
|
|
29916
30174
|
}
|
|
30175
|
+
function startSecretsWatcher(config2, deps, provider) {
|
|
30176
|
+
const secretsDir = path44.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
|
|
30177
|
+
const cfgBase = path44.basename(config2._configFilePath || "", ".json");
|
|
30178
|
+
const secretCandidates = [
|
|
30179
|
+
path44.join(secretsDir, `${cfgBase}.env`),
|
|
30180
|
+
path44.join(path44.dirname(config2._configFilePath || ""), `.env.${cfgBase}`),
|
|
30181
|
+
path44.join(path44.dirname(config2._configFilePath || ""), ".env")
|
|
30182
|
+
];
|
|
30183
|
+
let secretsPath = null;
|
|
30184
|
+
for (const p of secretCandidates) {
|
|
30185
|
+
if (fs41.existsSync(p)) {
|
|
30186
|
+
secretsPath = p;
|
|
30187
|
+
break;
|
|
30188
|
+
}
|
|
30189
|
+
}
|
|
30190
|
+
if (!secretsPath) {
|
|
30191
|
+
console.log("[secrets-watch] no .env file found, watcher disabled");
|
|
30192
|
+
return null;
|
|
30193
|
+
}
|
|
30194
|
+
let debounceTimer = null;
|
|
30195
|
+
let activeWatcher = null;
|
|
30196
|
+
const startWatch = () => {
|
|
30197
|
+
if (activeWatcher) activeWatcher.close();
|
|
30198
|
+
activeWatcher = fs41.watch(secretsPath, { persistent: true }, (eventType) => {
|
|
30199
|
+
if (eventType === "rename") {
|
|
30200
|
+
if (fs41.existsSync(secretsPath)) {
|
|
30201
|
+
console.log("[secrets-watch] rename detected, re-watching file...");
|
|
30202
|
+
startWatch();
|
|
30203
|
+
} else {
|
|
30204
|
+
console.log("[secrets-watch] file deleted, watcher paused");
|
|
30205
|
+
return;
|
|
30206
|
+
}
|
|
30207
|
+
}
|
|
30208
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
30209
|
+
debounceTimer = setTimeout(async () => {
|
|
30210
|
+
console.log(`[secrets-watch] file changed (${eventType}), reloading secrets...`);
|
|
30211
|
+
try {
|
|
30212
|
+
const content = fs41.readFileSync(secretsPath, "utf-8");
|
|
30213
|
+
let updated = 0;
|
|
30214
|
+
for (const line of content.split("\n")) {
|
|
30215
|
+
const trimmed = line.trim();
|
|
30216
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
30217
|
+
const eq = trimmed.indexOf("=");
|
|
30218
|
+
if (eq > 0) {
|
|
30219
|
+
const k = trimmed.slice(0, eq).trim();
|
|
30220
|
+
const v = trimmed.slice(eq + 1).trim();
|
|
30221
|
+
if (k) {
|
|
30222
|
+
process.env[k] = v;
|
|
30223
|
+
updated++;
|
|
30224
|
+
}
|
|
30225
|
+
}
|
|
30226
|
+
}
|
|
30227
|
+
console.log(`[secrets-watch] ${updated} keys reloaded from ${secretsPath}`);
|
|
30228
|
+
const result = await doReloadConfig(config2, deps, provider);
|
|
30229
|
+
console.log(`[secrets-watch] config reloaded: ok=${result.ok} changes=${result.changes.join(",")}`);
|
|
30230
|
+
try {
|
|
30231
|
+
fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
|
|
30232
|
+
`);
|
|
30233
|
+
} catch {
|
|
30234
|
+
}
|
|
30235
|
+
} catch (err) {
|
|
30236
|
+
console.error(`[secrets-watch] reload failed: ${err.message}`);
|
|
30237
|
+
}
|
|
30238
|
+
}, 500);
|
|
30239
|
+
});
|
|
30240
|
+
activeWatcher.on("error", (err) => {
|
|
30241
|
+
console.error(`[secrets-watch] error: ${err.message}`);
|
|
30242
|
+
});
|
|
30243
|
+
};
|
|
30244
|
+
startWatch();
|
|
30245
|
+
console.log(`[secrets-watch] watching ${secretsPath}`);
|
|
30246
|
+
return activeWatcher;
|
|
30247
|
+
}
|
|
29917
30248
|
|
|
29918
30249
|
// src/main.ts
|
|
29919
30250
|
import { readFileSync as readFileSync28 } from "node:fs";
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engine7",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.35",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Engine 7 —
|
|
5
|
+
"description": "Engine 7 — 给助手一个家 / A home for your AI assistant",
|
|
6
6
|
"main": "dist/main.mjs",
|
|
7
7
|
"bin": {
|
|
8
8
|
"engine7": "bin/engine7"
|