dsh-agentic-pet 0.4.0 → 0.4.2
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 +17 -2
- package/README.zh.md +17 -2
- package/carrier/dist-renderer/assets/{index-C61Q_dyJ.js → index-BMH7auTh.js} +1 -1
- package/carrier/dist-renderer/index.html +1 -1
- package/carrier/lib/main.js +38 -20
- package/carrier/lib/preload.js +3 -1
- package/carrier/lib/tts.js +212 -0
- package/client/pet-settings.js +102 -1
- package/lib/index.js +86 -23
- package/lib/{service-D-b8UzM2.js → service-BjTawwUn.js} +184 -246
- package/lib/tools.js +2 -2
- package/lib/types/petdata.d.ts +16 -1
- package/package.json +12 -4
package/lib/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as globalFilePath, d as readJsonl, f as readPetName, h as writePetName, i as installPetDriver, l as readAffectionRaw, m as writeGlobalJson, n as installPetService, p as writeAffectionRaw, r as petHome, s as eventsFile, u as readGlobalJson } from "./service-BjTawwUn.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import { mkdir, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { dirname, extname, join, normalize, resolve, sep } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
@@ -38,6 +38,7 @@ const DEFAULT_SETTINGS = {
|
|
|
38
38
|
touchReplyCooldownSeconds: 30,
|
|
39
39
|
boundSessions: {}
|
|
40
40
|
},
|
|
41
|
+
tts: { enabled: false },
|
|
41
42
|
debug: { cdp: false }
|
|
42
43
|
};
|
|
43
44
|
const EMPTY_LEDGER = {
|
|
@@ -141,10 +142,30 @@ function mergeSettings(current, patch) {
|
|
|
141
142
|
const d = patchDebug;
|
|
142
143
|
if (typeof d.cdp === "boolean") debug.cdp = d.cdp;
|
|
143
144
|
}
|
|
145
|
+
const tts = { ...current.tts };
|
|
146
|
+
const patchTts = patch.tts;
|
|
147
|
+
if (typeof patchTts === "object" && patchTts !== null) {
|
|
148
|
+
const t = patchTts;
|
|
149
|
+
if (typeof t.enabled === "boolean") tts.enabled = t.enabled;
|
|
150
|
+
const setText = (key) => {
|
|
151
|
+
const value = t[key];
|
|
152
|
+
if (typeof value !== "string") return;
|
|
153
|
+
const trimmed = value.trim();
|
|
154
|
+
if (trimmed === "") Reflect.deleteProperty(tts, key);
|
|
155
|
+
else tts[key] = trimmed;
|
|
156
|
+
};
|
|
157
|
+
setText("apiKey");
|
|
158
|
+
setText("voiceId");
|
|
159
|
+
setText("modelId");
|
|
160
|
+
setText("languageCode");
|
|
161
|
+
if (typeof t.speed === "number" && Number.isFinite(t.speed)) tts.speed = Math.min(2, Math.max(.5, t.speed));
|
|
162
|
+
if (typeof t.volume === "number" && Number.isFinite(t.volume)) tts.volume = Math.min(2, Math.max(.01, t.volume));
|
|
163
|
+
}
|
|
144
164
|
return {
|
|
145
165
|
...current,
|
|
146
166
|
proactivity,
|
|
147
167
|
innerVoice,
|
|
168
|
+
tts,
|
|
148
169
|
debug
|
|
149
170
|
};
|
|
150
171
|
}
|
|
@@ -168,8 +189,7 @@ function openPathInFileManager(path) {
|
|
|
168
189
|
* @param home - the dsh home holding the settings/ledger/manifest files.
|
|
169
190
|
*/
|
|
170
191
|
function installSettingsPage(ctx, home, pet) {
|
|
171
|
-
const
|
|
172
|
-
const windowPath = join(home, "pet-window.json");
|
|
192
|
+
const windowPath = globalFilePath(home, "pet-window.json");
|
|
173
193
|
/** Summarize EVERY live pet (id, online flag, normalized /status). */
|
|
174
194
|
const petStatus = async () => {
|
|
175
195
|
return { pets: (await Promise.all((await pet.list()).map((entry) => pet.snapshot(entry)))).map(({ id, online, ...rest }) => ({
|
|
@@ -179,11 +199,7 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
179
199
|
})) };
|
|
180
200
|
};
|
|
181
201
|
const readSettings = async () => {
|
|
182
|
-
|
|
183
|
-
return mergeSettings(JSON.parse(await readFile(settingsPath, "utf8")), {});
|
|
184
|
-
} catch {
|
|
185
|
-
return mergeSettings({}, {});
|
|
186
|
-
}
|
|
202
|
+
return mergeSettings(await readGlobalJson(home, "pet-settings.json"), {});
|
|
187
203
|
};
|
|
188
204
|
const routeByPath = /* @__PURE__ */ new Map();
|
|
189
205
|
const route = (method, path, handler) => {
|
|
@@ -219,7 +235,7 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
219
235
|
route("GET", "/api/pet-settings/read", async (_req, res) => {
|
|
220
236
|
const [settings, windowPosition, status] = await Promise.all([
|
|
221
237
|
readSettings(),
|
|
222
|
-
|
|
238
|
+
readGlobalJson(home, "pet-window.json").then((raw) => Object.keys(raw).length > 0 ? raw : null),
|
|
223
239
|
petStatus()
|
|
224
240
|
]);
|
|
225
241
|
const affection = {};
|
|
@@ -238,7 +254,6 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
238
254
|
...settings,
|
|
239
255
|
names
|
|
240
256
|
};
|
|
241
|
-
const history = [];
|
|
242
257
|
const touchTotals = {};
|
|
243
258
|
for (const id of bookIds) {
|
|
244
259
|
const events = await readJsonl(eventsFile(home, id), 600);
|
|
@@ -253,18 +268,12 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
253
268
|
totals.byPart[part] = (totals.byPart[part] ?? 0) + 1;
|
|
254
269
|
}
|
|
255
270
|
if (totals.total > 0) touchTotals[id] = totals;
|
|
256
|
-
for (const call of await readJsonl(toolCallsFile(home, id), 12)) history.push({
|
|
257
|
-
...call,
|
|
258
|
-
petId: id
|
|
259
|
-
});
|
|
260
271
|
}
|
|
261
|
-
history.sort((a, b) => Number(b.at ?? 0) - Number(a.at ?? 0));
|
|
262
272
|
writeJson(res, 200, {
|
|
263
273
|
settings: composed,
|
|
264
274
|
affection,
|
|
265
275
|
windowPosition,
|
|
266
276
|
pets: status.pets,
|
|
267
|
-
history: history.slice(0, 12),
|
|
268
277
|
touchTotals
|
|
269
278
|
});
|
|
270
279
|
});
|
|
@@ -281,7 +290,7 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
281
290
|
delete patch.names;
|
|
282
291
|
}
|
|
283
292
|
const next = mergeSettings(await readSettings(), patch);
|
|
284
|
-
await
|
|
293
|
+
await writeGlobalJson(home, "pet-settings.json", next);
|
|
285
294
|
ctx.emit("pet-settings/changed");
|
|
286
295
|
writeJson(res, 200, {
|
|
287
296
|
ok: true,
|
|
@@ -296,7 +305,7 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
296
305
|
});
|
|
297
306
|
});
|
|
298
307
|
route("POST", "/api/pet-settings/reset-position", async (_req, res) => {
|
|
299
|
-
|
|
308
|
+
for (const p of [windowPath, join(home, "pet-window.json")]) await rm(p, { force: true });
|
|
300
309
|
writeJson(res, 200, { ok: true });
|
|
301
310
|
});
|
|
302
311
|
route("POST", "/api/pet-settings/open-logs", async (_req, res) => {
|
|
@@ -308,6 +317,59 @@ function installSettingsPage(ctx, home, pet) {
|
|
|
308
317
|
opened: target
|
|
309
318
|
});
|
|
310
319
|
});
|
|
320
|
+
route("GET", "/api/pet-settings/tts/voices", async (req, res) => {
|
|
321
|
+
const settings = await readSettings();
|
|
322
|
+
const apiKey = typeof settings.tts?.apiKey === "string" ? settings.tts.apiKey : "";
|
|
323
|
+
if (apiKey === "") {
|
|
324
|
+
writeJson(res, 400, { error: "尚未配置 BreezeBlue API key" });
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
328
|
+
const params = new URLSearchParams();
|
|
329
|
+
for (const key of [
|
|
330
|
+
"search",
|
|
331
|
+
"language_code",
|
|
332
|
+
"gender",
|
|
333
|
+
"accent"
|
|
334
|
+
]) {
|
|
335
|
+
const value = url.searchParams.get(key);
|
|
336
|
+
if (value !== null && value !== "") params.set(key, value);
|
|
337
|
+
}
|
|
338
|
+
const rawPageSize = url.searchParams.get("page_size");
|
|
339
|
+
const pageSize = rawPageSize !== null ? clampNumber(Number(rawPageSize), 1, 100) : void 0;
|
|
340
|
+
params.set("page_size", String(pageSize ?? 30));
|
|
341
|
+
try {
|
|
342
|
+
const response = await fetch(`https://api.breeze.blue/v1/voices?${params.toString()}`, {
|
|
343
|
+
headers: { "xi-api-key": apiKey },
|
|
344
|
+
signal: AbortSignal.timeout(1e4)
|
|
345
|
+
});
|
|
346
|
+
if (!response.ok) {
|
|
347
|
+
const detail = await response.text().catch(() => "");
|
|
348
|
+
writeJson(res, 502, { error: response.status === 401 ? "BreezeBlue 拒绝了这个 key(401):请到 breezeblue.ai 的 Developer Console 复制 brz_ 开头的 API key" : `BreezeBlue HTTP ${String(response.status)}${detail ? `: ${detail.slice(0, 200)}` : ""}` });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const raw = await response.json();
|
|
352
|
+
const voices = (Array.isArray(raw.voices) ? raw.voices : []).map((entry) => {
|
|
353
|
+
const voice = entry;
|
|
354
|
+
const str = (key) => typeof voice[key] === "string" ? voice[key] : null;
|
|
355
|
+
return {
|
|
356
|
+
voice_id: str("voice_id") ?? "",
|
|
357
|
+
name: str("name") ?? "",
|
|
358
|
+
language_code: str("language_code"),
|
|
359
|
+
gender: str("gender"),
|
|
360
|
+
tone: Array.isArray(voice.tone) ? voice.tone.filter((item) => typeof item === "string").slice(0, 3) : [],
|
|
361
|
+
accent: str("accent"),
|
|
362
|
+
preview_url: str("preview_url")
|
|
363
|
+
};
|
|
364
|
+
}).filter((voice) => voice.voice_id !== "");
|
|
365
|
+
writeJson(res, 200, {
|
|
366
|
+
voices,
|
|
367
|
+
total: typeof raw.total === "number" ? raw.total : voices.length
|
|
368
|
+
});
|
|
369
|
+
} catch (error) {
|
|
370
|
+
writeJson(res, 502, { error: `声音列表请求失败: ${error instanceof Error ? error.message : String(error)}` });
|
|
371
|
+
}
|
|
372
|
+
});
|
|
311
373
|
}
|
|
312
374
|
//#endregion
|
|
313
375
|
//#region lib/types/index.js
|
|
@@ -355,7 +417,7 @@ const PET_PATH_PREFIX = "/whale598";
|
|
|
355
417
|
* publisher, then falls back to spawning its own profile.
|
|
356
418
|
*/
|
|
357
419
|
function petLocatorPath() {
|
|
358
|
-
return join(process.env.DSH_HOME ?? join(homedir(), ".dsh"), "pet-surface-url.json");
|
|
420
|
+
return join(process.env.DSH_HOME ?? join(homedir(), ".dsh"), "whale598", "pet-surface-url.json");
|
|
359
421
|
}
|
|
360
422
|
const HTML_MIME = "text/html; charset=utf-8";
|
|
361
423
|
const MIME = {
|
|
@@ -623,9 +685,10 @@ function apply(ctx) {
|
|
|
623
685
|
const settled = ctx.get("loader")?.await();
|
|
624
686
|
if (settled === void 0) settledReady();
|
|
625
687
|
else settled.then(settledReady).catch(() => {});
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
688
|
+
const home = petHome();
|
|
689
|
+
const pet = installPetService(ctx, home);
|
|
690
|
+
installPetDriver(ctx, home, pet);
|
|
691
|
+
installSettingsPage(ctx, home, pet);
|
|
629
692
|
ctx.effect(() => () => {
|
|
630
693
|
readFile(petLocatorPath(), "utf8").then((raw) => {
|
|
631
694
|
if (JSON.parse(raw).pid === process.pid) return unlink(petLocatorPath());
|