dsh-agentic-pet 0.4.3 → 0.4.5

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.zh.md CHANGED
@@ -36,13 +36,11 @@ dsh plugin --profile <name> add dsh-agentic-pet
36
36
 
37
37
  ## 开发
38
38
 
39
- TypeScript 构建仍在 dsh monorepo workspace 内进行(被打包的 `@deepseek-ai/dsh-*` 依赖未发布 npm)。流程:
39
+ 日常迭代在本仓库内完成:改 `src/` → 本地构建(tsc + tsdown,工具链与私有包依赖的准备见文档)→ 部署到桌面 profile 的 vendor 目录 → 重启 DeepSeek Harness。**完整手册(含工具链准备、部署/验证/清理命令与已知坑)见 [docs/DEV-DEPLOY.md](docs/DEV-DEPLOY.md)**。
40
40
 
41
- 1. monorepo 内:`pnpm run build`(插件 `lib/`)与 `pnpm --filter @deepseek-ai/dsh-pet run build`(载体 `lib/` + `dist-renderer/`)。
42
- 2. 本仓库:`scripts\sync-from-monorepo.cmd`——把源码与构建产物镜像进本仓库。
43
- 3. 本仓库提交,然后在本仓库发布(见下文「发布」;先素材包后插件包,手册见 [dsh-pet-assets/PUBLISH.md](../dsh-pet-assets/PUBLISH.md))。
41
+ 历史上的构建在 dsh monorepo workspace 内进行(被打包的 `@deepseek-ai/dsh-*` 依赖未发布 npm),经 `scripts\sync-from-monorepo.cmd` 镜像进本仓库——monorepo 侧副本已停在 `0.2.1-rc.3`,该链路已过期,仅留档;直接运行会用旧代码覆盖本仓库。
44
42
 
45
- 测试:`npx vitest run tests/registry-service.spec.ts` 自持可跑(只需 `cordis` 与 `vitest`);含 `tests/pet-app.spec.ts` 的全量测试在 monorepo workspace 内跑。
43
+ 测试:`node_modules/.bin/vitest run`(vitest 及 `js-yaml@4.1.0`、`@deepseek-ai/cordis-plugin-include` 为 ad-hoc 安装,见文档 §4);`tests/registry-service.spec.ts` 等纯函数规格只需 `cordis` 与 `vitest` 即可自持运行。
46
44
 
47
45
  ## 发布
48
46
 
@@ -60,6 +58,7 @@ git push --follow-tags
60
58
 
61
59
  ## 文档
62
60
 
61
+ - 开发与部署手册(本地构建工具链、vendor 部署、测试、发布、已知坑):[docs/DEV-DEPLOY.md](docs/DEV-DEPLOY.md)。
63
62
  - 控制面契约(所有宠物实现共同讲述的冻结消费方 API)见项目文档的多宠指南。
64
63
  - 说话朗读(BreezeBlue TTS)设计笔记见 monorepo 工作区的 `packages/bundle/pet-app/docs/tts.md`。
65
64
  - 已知边界与设计笔记见仓库 wiki 与项目工程文档。
@@ -233,14 +233,14 @@ window.__ModuleLoader__.load({
233
233
  update({ innerVoice: { enabled: !(iv.enabled !== false) } })
234
234
  } })),
235
235
  h('div', { style: row },
236
- '唤醒间隔',
236
+ '世界时钟间隔',
237
237
  h('input', { style: inputStyle, value: String(iv.wakeIntervalMinutes ?? 2), onChange: function (e) {
238
238
  var v = parseInt(e.target.value, 10)
239
239
  if (Number.isFinite(v)) update({ innerVoice: { wakeIntervalMinutes: v } })
240
240
  } }),
241
241
  '分钟'),
242
242
  h('div', { style: row },
243
- '请求模型的概率',
243
+ '世界时钟唤醒概率',
244
244
  h('input', { style: inputStyle, value: String(Math.round(((typeof iv.speakProbability === 'number' ? iv.speakProbability : 0.5)) * 100)), onChange: function (e) {
245
245
  var v = parseFloat(e.target.value)
246
246
  if (Number.isFinite(v)) update({ innerVoice: { speakProbability: Math.min(100, Math.max(0, v)) / 100 } })
@@ -260,6 +260,25 @@ window.__ModuleLoader__.load({
260
260
  if (Number.isFinite(v) && v >= 0) update({ innerVoice: { touchReplyCooldownSeconds: Math.min(600, v) } })
261
261
  } }),
262
262
  '秒(0 = 不限)'),
263
+ h('div', { style: row }, '自主唤醒遇用户忙',
264
+ (function () {
265
+ var seg = function (active, side) {
266
+ return { padding: '4px 12px', fontSize: 12, cursor: 'pointer',
267
+ border: '1px solid ' + (active ? '#22c55e' : '#c9d4e0'),
268
+ background: active ? '#22c55e' : '#f4f7fa',
269
+ color: active ? '#ffffff' : '#1c2733',
270
+ borderRadius: side === 'left' ? '4px 0 0 4px' : '0 4px 4px 0',
271
+ marginLeft: side === 'right' ? -1 : 0 }
272
+ }
273
+ var policy = iv.selfWakeBusyPolicy === 'defer' ? 'defer' : 'fire'
274
+ return h('div', { style: { display: 'inline-flex' } },
275
+ h('button', { style: seg(policy === 'fire', 'left'), onClick: function () {
276
+ if (policy !== 'fire') update({ innerVoice: { selfWakeBusyPolicy: 'fire' } })
277
+ } }, '到点必醒'),
278
+ h('button', { style: seg(policy === 'defer', 'right'), onClick: function () {
279
+ if (policy !== 'defer') update({ innerVoice: { selfWakeBusyPolicy: 'defer' } })
280
+ } }, '忙时顺延'))
281
+ })()),
263
282
  h('div', { style: row }, '会话模式',
264
283
  (function () {
265
284
  var seg = function (active, side) {
@@ -286,7 +305,7 @@ window.__ModuleLoader__.load({
286
305
  else delete next.electron
287
306
  update({ innerVoice: { boundSessions: next } })
288
307
  } })),
289
- h(Hint, null, '宠物在自己专属的会话里醒来,用气泡和动作自主表达;那条「鲸鱼娘的内心戏」会话可在会话列表查看,也可以直接在那里和它聊天。用户忙于会话或宠物表达未完成时不会打扰。会话模式切换只对新建会话生效:归档旧会话后,她会在下次对话时自动换代重建(旧会话记忆不迁移)。指定会话立即生效——下一次对话就用新绑定的会话;清空后自动回落到她之前的会话(那条若已被归档,会自动创建新的)。')),
308
+ h(Hint, null, '宠物在自己专属的会话里醒来,用气泡和动作自主表达;那条「鲸鱼娘的内心戏」会话可在会话列表查看,也可以直接在那里和它聊天。醒来有两个来源:世界时钟是环境节拍,按上面的间隔和唤醒概率随机轻拍她,用户忙于会话或宠物表达未完成时不会打扰;自主唤醒是她用 pet_schedule_wake 给自己定的闹钟,到点带着当初的打算醒来,不受这个概率影响(上面的「自主唤醒遇用户忙」决定你正忙时她是否照醒,默认到点必醒)。会话模式切换只对新建会话生效:归档旧会话后,她会在下次对话时自动换代重建(旧会话记忆不迁移)。指定会话立即生效——下一次对话就用新绑定的会话;清空后自动回落到她之前的会话(那条若已被归档,会自动创建新的)。')),
290
309
 
291
310
  h(Section, { title: h('span', null, '自由活动 ', h('span', { style: { fontSize: 11, fontWeight: 400, color: 'rgba(127,127,127,.9)' } }, '逻辑驱动')) },
292
311
  h('div', { style: row }, '自由活动(宠物自娱)', h(Toggle, { value: pro.idleAnimation !== false, onChange: function () {
package/lib/index.js CHANGED
@@ -1,13 +1,13 @@
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";
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-R8qwDx1Q.js";
2
2
  import { createRequire } from "node:module";
3
- import { spawn, spawnSync } from "node:child_process";
3
+ import { spawn } from "node:child_process";
4
4
  import { createHash } from "node:crypto";
5
- import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
5
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
6
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";
10
- //#region lib/types/settings-page.js
10
+ // lib/types/settings-page.js
11
11
  /**
12
12
  * The settings page's host half: local HTTP routes backing the desktop
13
13
  * sidebar "桌宠" page (the client half lives in ./client/pet-settings.js and
@@ -36,6 +36,7 @@ const DEFAULT_SETTINGS = {
36
36
  speakProbability: .5,
37
37
  touchReplyProbability: .3,
38
38
  touchReplyCooldownSeconds: 30,
39
+ selfWakeBusyPolicy: "fire",
39
40
  boundSessions: {}
40
41
  },
41
42
  tts: { enabled: false },
@@ -78,7 +79,7 @@ async function readJsonBody(req) {
78
79
  let size = 0;
79
80
  for await (const chunk of req) {
80
81
  size += chunk.length;
81
- if (size > 1024 * 1024) return void 0;
82
+ if (size > 1048576) return void 0;
82
83
  chunks.push(chunk);
83
84
  }
84
85
  try {
@@ -130,6 +131,7 @@ function mergeSettings(current, patch) {
130
131
  if (typeof v.touchReplyProbability === "number" && Number.isFinite(v.touchReplyProbability)) innerVoice.touchReplyProbability = Math.min(1, Math.max(0, v.touchReplyProbability));
131
132
  const replyCooldown = clampNumber(v.touchReplyCooldownSeconds, 0, 600);
132
133
  if (replyCooldown !== void 0) innerVoice.touchReplyCooldownSeconds = replyCooldown;
134
+ if (v.selfWakeBusyPolicy === "defer" || v.selfWakeBusyPolicy === "fire") innerVoice.selfWakeBusyPolicy = v.selfWakeBusyPolicy;
133
135
  const bound = v.boundSessions;
134
136
  if (typeof bound === "object" && bound !== null) {
135
137
  const record = {};
@@ -177,7 +179,8 @@ function openPathInFileManager(path) {
177
179
  }).unref();
178
180
  return;
179
181
  }
180
- spawn(process.platform === "darwin" ? "open" : "xdg-open", [path], {
182
+ const command = process.platform === "darwin" ? "open" : "xdg-open";
183
+ spawn(command, [path], {
181
184
  detached: true,
182
185
  stdio: "ignore"
183
186
  }).unref();
@@ -371,8 +374,7 @@ function installSettingsPage(ctx, home, pet) {
371
374
  }
372
375
  });
373
376
  }
374
- //#endregion
375
- //#region lib/types/index.js
377
+ // lib/types/index.js
376
378
  /**
377
379
  * dsh-agentic-pet — the pet-surface bundle's runtime glue plugin plus
378
380
  * the bundle patch (`cordis.patch.yml`, declared by the `dsh.bundle.patch`
@@ -433,7 +435,7 @@ const MIME = {
433
435
  ".webmanifest": "application/manifest+json",
434
436
  ".woff2": "font/woff2"
435
437
  };
436
- const STATIC_MISS_CODES = new Set([
438
+ const STATIC_MISS_CODES = /* @__PURE__ */ new Set([
437
439
  "ENOENT",
438
440
  "EISDIR",
439
441
  "ENOTDIR"
@@ -477,7 +479,8 @@ function bytesBody(bytes) {
477
479
  async function servePetStatic(request, distRoot, distIndex, authorizeIndex) {
478
480
  const pathname = decodeURIComponent(new URL(request.url).pathname);
479
481
  if (!(pathname === "/whale598" || pathname.startsWith(`/whale598/`))) return new Response(null, { status: 404 });
480
- const target = resolve(normalize(join(distRoot, pathname === "/whale598" || pathname === `/whale598/` ? "/" : pathname.slice(9))));
482
+ const relative = pathname === "/whale598" || pathname === `/whale598/` ? "/" : pathname.slice(9);
483
+ const target = resolve(normalize(join(distRoot, relative)));
481
484
  if (target !== distRoot && !target.startsWith(distRoot + sep)) return new Response(null, { status: 403 });
482
485
  let body;
483
486
  let headers;
@@ -626,7 +629,8 @@ function apply(ctx) {
626
629
  */
627
630
  const resolveElectronBinary = () => {
628
631
  if (process.env.DSH_PET_ELECTRON !== void 0 && process.env.DSH_PET_ELECTRON !== "") return process.env.DSH_PET_ELECTRON;
629
- const staged = join(resolve(join(dirname(fileURLToPath(import.meta.url)), "..")), "electron-bin", process.platform === "win32" ? "electron.exe" : process.platform === "darwin" ? join("Electron.app", "Contents", "MacOS", "Electron") : "electron");
632
+ const pkgRoot = resolve(join(dirname(fileURLToPath(import.meta.url)), ".."));
633
+ const staged = join(pkgRoot, "electron-bin", process.platform === "win32" ? "electron.exe" : process.platform === "darwin" ? join("Electron.app", "Contents", "MacOS", "Electron") : "electron");
630
634
  if (existsSync(staged)) return staged;
631
635
  try {
632
636
  const require = createRequire(import.meta.url);
@@ -637,7 +641,8 @@ function apply(ctx) {
637
641
  let dir = dirname(fileURLToPath(import.meta.url));
638
642
  for (let i = 0; i < 8; i += 1) {
639
643
  const pkgDir = join(dir, "node_modules", "electron");
640
- const binary = join(pkgDir, "dist", readFileSync(join(pkgDir, "path.txt"), "utf8").trim());
644
+ const exe = readFileSync(join(pkgDir, "path.txt"), "utf8").trim();
645
+ const binary = join(pkgDir, "dist", exe);
641
646
  if (existsSync(binary)) return binary;
642
647
  const parent = dirname(dir);
643
648
  if (parent === dir) break;
@@ -656,31 +661,9 @@ function apply(ctx) {
656
661
  const carrierDir = resolve(join(dirname(fileURLToPath(import.meta.url)), "..", "carrier"));
657
662
  const electronBinary = resolveElectronBinary();
658
663
  if (process.env.DSH_PET_NO_SPAWN === "1" || electronBinary === void 0 || !existsSync(join(carrierDir, "package.json"))) return;
659
- let probeStatus = "unstarted";
660
- const startsSandboxed = (() => {
661
- try {
662
- const probe = spawnSync(electronBinary, ["--version"], {
663
- env: stripRunAsNode(process.env),
664
- stdio: "ignore",
665
- timeout: 5e3
666
- });
667
- probeStatus = probe.error === void 0 ? `status=${String(probe.status)}` : `error=${String(probe.error.code ?? probe.error.message)}`;
668
- return probe.status === 0;
669
- } catch (error) {
670
- probeStatus = `throw=${error instanceof Error ? error.message : String(error)}`;
671
- return false;
672
- }
673
- })();
674
664
  const launchArgs = [carrierDir];
675
- if (!startsSandboxed) {
676
- console.error("pet-surface: this machine cannot start a sandboxed Electron; launching the carrier with --no-sandbox");
677
- const carrierUserData = join(dirname(petLocatorPath()), "carrier-userdata");
678
- try {
679
- mkdirSync(carrierUserData, { recursive: true });
680
- } catch {}
681
- launchArgs.push("--no-sandbox", `--user-data-dir=${carrierUserData}`);
682
- }
683
- logCarrier(`spawn binary=${electronBinary} args=${JSON.stringify(launchArgs)} probe=${probeStatus} pid=${String(process.pid)}`);
665
+ if (process.env.DSH_PET_NO_SANDBOX === "1") launchArgs.push("--no-sandbox");
666
+ logCarrier(`spawn binary=${electronBinary} args=${JSON.stringify(launchArgs)} pid=${String(process.pid)}`);
684
667
  const child = (() => {
685
668
  try {
686
669
  return spawn(electronBinary, launchArgs, {
@@ -743,5 +726,4 @@ function apply(ctx) {
743
726
  }).catch(() => {});
744
727
  }, "pet-surface: locator withdrawal");
745
728
  }
746
- //#endregion
747
729
  export { PET_PATH_PREFIX, apply, inject, name, petLocatorPath, servePetStatic };