dsh-agentic-pet 0.0.0-stage → 0.3.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.
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: "The dsh desktop pet: an agentic pet plugin that serves the pet renderer over the web composition, drives it from in-process dsh events, and answers approvals and questions from the desktop."
3
+ kind: "package-bundle"
4
+ ---
package/README.md CHANGED
@@ -1,3 +1,50 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "The dsh desktop pet: an agentic pet plugin that serves the pet renderer over the web composition, drives it from in-process dsh events, and answers approvals and questions from the desktop."
3
+ kind: "package-bundle"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # dsh-agentic-pet
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ `dsh-agentic-pet` is a desktop-pet plugin for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) ecosystem: the pet lives on your desktop as a transparent always-on-top window, follows your agent sessions (idle / thinking / working / error), races the web GUI to answer approvals and user questions (first answer wins), collects a per-pet touch-affection ledger, and exposes expressive tools (`pet_say`, `pet_do`, `pet_digest`, `pet_interactions`, `pet_state`) so the model can speak and act through the pet. Multiple pet implementations can run side by side — a directory registry (`~/.dsh/pet-http.d/`) discovers every live pet and routes state, interactions, and ledgers per pet.
13
+
14
+ The pet itself is a dumb display with zero dsh knowledge. Everything that understands the runtime — event convergence, interaction waterfalls, multi-pet routing, the model tools — lives in the plugin's driver, and the pet only implements a small local HTTP control interface. Any pet implementation that speaks the same HTTP contract can be adopted without changing this plugin.
15
+
16
+ ## What's in this repo
17
+
18
+ | Path | What it is |
19
+ |---|---|
20
+ | `src/` | The plugin (Cordis bundle): `/pet` route, event driver, registry, `PetService` seam, model tools, settings-page routes |
21
+ | `client/` | The desktop sidebar settings page (client half) |
22
+ | `carrier/` | The Electron carrier **build artifacts** shipped with the package (`lib/`, `dist-renderer/`) |
23
+ | `carrier-src/` | The carrier **sources** (mirrored from the dev upstream; media assets live in [dsh-pet-assets](https://github.com/beyondgjc/dsh-pet-assets)) |
24
+ | `vendor/typert-plugin/` | Vendored typert decorator plugin used by the packaging config |
25
+ | `tests/` | Vitest specs (pure-function coverage of the registry routing and the `/status` field-matrix normalization) |
26
+
27
+ ## Install
28
+
29
+ From the DeepSeek Harness desktop app's plugin page, or:
30
+
31
+ ```bash
32
+ dsh plugin --profile <name> add dsh-agentic-pet
33
+ ```
34
+
35
+ The animation media ship as a separate package, `dsh-agentic-pet-assets`, and are pulled in automatically as a dependency.
36
+
37
+ ## Development
38
+
39
+ The TypeScript build still runs inside the dsh monorepo workspace (the bundled `@deepseek-ai/dsh-*` deps are not published to npm). The flow:
40
+
41
+ 1. In the monorepo: `pnpm run build` (plugin `lib/`) and `pnpm --filter @deepseek-ai/dsh-pet run build` (carrier `lib/` + `dist-renderer/`).
42
+ 2. Here: `scripts\sync-from-monorepo.cmd` — mirrors sources and build outputs into this repo.
43
+ 3. Commit here, then `npm publish` (assets package first; see [dsh-pet-assets/PUBLISH.md](../dsh-pet-assets/PUBLISH.md) for the release manual).
44
+
45
+ Tests: `npx vitest run tests/registry-service.spec.ts` runs self-contained (needs only `cordis` and `vitest`); the full suite including `tests/pet-app.spec.ts` runs in the monorepo workspace.
46
+
47
+ ## Documentation
48
+
49
+ - Control-plane contract (the frozen consumer API every pet implementation speaks): see the multi-pet guide in the project documentation.
50
+ - Known limitations and design notes live in the repo wiki and the project's engineering docs.
package/README.zh.md ADDED
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: "dsh 桌面宠物:经由 web 组合层伺服宠物页面、由进程内 dsh 事件驱动、可从桌面应答审批与提问的 agentic 宠物插件。"
3
+ kind: "package-bundle"
4
+ ---
5
+
6
+ # dsh-agentic-pet
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 简介
11
+
12
+ `dsh-agentic-pet` 是 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 生态的桌面宠物插件:宠物以透明置顶窗常驻桌面,跟随 agent 会话状态(idle / thinking / working / error),与 web 界面竞速应答审批与提问(先答者赢),维护每宠独立的触摸好感账本,并向模型暴露表达工具(`pet_say`、`pet_do`、`pet_digest`、`pet_interactions`、`pet_state`)——模型可以借宠物说话、做动作、感知被触摸。多只宠物实现可以并存:目录注册表(`~/.dsh/pet-http.d/`)发现全部在线宠物,状态、交互、账本按宠路由。
13
+
14
+ 宠物本身是零 DSH 知识的纯显示端。理解运行时的一切——事件收敛、交互瀑布、多宠路由、模型工具——都在插件的 driver 里,宠物只实现一个小型本地 HTTP 控制接口。任何讲同一 HTTP 契约的宠物实现都能被采纳,插件零改动。
15
+
16
+ ## 仓库结构
17
+
18
+ | 路径 | 内容 |
19
+ |---|---|
20
+ | `src/` | 插件(Cordis bundle):`/pet` 路由、事件 driver、注册表、`PetService` 接缝、模型工具、设置页路由 |
21
+ | `client/` | 桌面侧边栏设置页(client 半边) |
22
+ | `carrier/` | 随包分发的 Electron 载体**构建产物**(`lib/`、`dist-renderer/`) |
23
+ | `carrier-src/` | 载体**源码**(自开发上游镜像;素材在 [dsh-pet-assets](https://github.com/beyondgjc/dsh-pet-assets)) |
24
+ | `vendor/typert-plugin/` | 打包配置使用的 typert 装饰器插件(vendored) |
25
+ | `tests/` | Vitest 用例(注册表路由与 `/status` 字段矩阵的纯函数覆盖) |
26
+
27
+ ## 安装
28
+
29
+ 在 DeepSeek Harness 桌面端插件页安装,或:
30
+
31
+ ```bash
32
+ dsh plugin --profile <name> add dsh-agentic-pet
33
+ ```
34
+
35
+ 动画素材以独立包 `dsh-agentic-pet-assets` 分发,作为依赖自动带入。
36
+
37
+ ## 开发
38
+
39
+ TypeScript 构建仍在 dsh monorepo workspace 内进行(被打包的 `@deepseek-ai/dsh-*` 依赖未发布 npm)。流程:
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. 本仓库提交,然后 `npm publish`(先素材包后插件包;发布手册见 [dsh-pet-assets/PUBLISH.md](../dsh-pet-assets/PUBLISH.md))。
44
+
45
+ 测试:`npx vitest run tests/registry-service.spec.ts` 自持可跑(只需 `cordis` 与 `vitest`);含 `tests/pet-app.spec.ts` 的全量测试在 monorepo workspace 内跑。
46
+
47
+ ## 文档
48
+
49
+ - 控制面契约(所有宠物实现共同讲述的冻结消费方 API)见项目文档的多宠指南。
50
+ - 已知边界与设计笔记见仓库 wiki 与项目工程文档。
@@ -0,0 +1 @@
1
+ :root{color-scheme:light;background:0 0!important}html,body{user-select:none;width:100%;height:100%;margin:0;padding:0;font-family:Segoe UI,Microsoft YaHei,system-ui,sans-serif;overflow:hidden;background:0 0!important}#root{width:100%;height:100%;position:relative}#pet{cursor:grab;width:340px;height:200px;position:absolute;bottom:12px;left:50%;transform:translate(-50%)}#pet video{display:none}#pet canvas{object-fit:contain;width:100%;height:100%;display:block}#bubble{box-sizing:border-box;z-index:10;color:#1c2733;background:#fff;border:1px solid #d8e0ea;border-radius:12px;width:400px;max-height:300px;padding:12px 14px;font-size:13px;line-height:1.5;position:absolute;bottom:224px;left:50%;overflow-y:auto;transform:translate(-50%);box-shadow:0 6px 24px #1e283c2e}#bubble[hidden]{display:none}.bubble-title{margin:0 0 6px;font-weight:600}.bubble-detail{color:#5a6b7d;word-break:break-all;margin:0 0 8px}.bubble-question{margin:0 0 8px;font-weight:600}.bubble-question .bubble-detail{margin:2px 0 6px;font-weight:400}.bubble-options{flex-direction:column;gap:6px;margin:0 0 10px;display:flex}.bubble-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.bubble-button{color:#1c2733;cursor:pointer;background:#f4f7fa;border:1px solid #c9d4e0;border-radius:8px;padding:5px 14px;font-size:13px}.bubble-button:hover{background:#e8eff6}.bubble-button.primary{color:#fff;background:#2f6fed;border-color:#2f6fed}.bubble-button.primary:hover{background:#255fd2}.bubble-button.danger:hover{background:#fbe9e9}.bubble-queue{color:#8a97a5;margin:8px 0 0;font-size:12px}.bubble-chat{align-items:center;gap:8px;display:flex}.bubble-chat-input{color:#1c2733;background:#fff;border:1px solid #c9d4e0;border-radius:8px;outline:none;flex:1;min-width:0;padding:6px 10px;font-family:inherit;font-size:13px}.bubble-chat-input:focus{border-color:#2f6fed}.bubble-chat-input::placeholder{color:#9aa7b4}
@@ -0,0 +1 @@
1
+ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`whale`,t=globalThis,n={idle:[{dir:`idle`,file:`待机呼吸休闲`}],thinking:[{dir:`random`,file:`深度思考碎碎念`}],working:[{dir:`random`,file:`工作状态-忙碌点按`},{dir:`random`,file:`写代码`},{dir:`random`,file:`原地敲击桌面互动`}],waiting:[{dir:`turn`,file:`东张西望`}],error:[{dir:`random`,file:`被吓一跳`}],celebrate:[{dir:`click`,file:`点击回应-开心跃动`}],touch:[{dir:`click`,file:`点击回应-开心跃动`},{dir:`click`,file:`点击回应-元气挥手`},{dir:`click`,file:`点击回应-害羞惊讶`},{dir:`click`,file:`点击回应-傲娇生气`},{dir:`click`,file:`点击回应-挠痒咯咯笑`}]},r=new Set([`深度思考碎碎念`,`工作状态-忙碌点按`,`写代码`,`原地敲击桌面互动`,`被吓一跳`]);function i(e){let t=document.querySelector(e);if(t===null)throw Error(`pet renderer: missing element ${e}`);return t}var a=i(`#video`);a.addEventListener(`error`,()=>{console.error(`[pet-video] source failed: ${a.src} ${String(a.error?.message??``)}`)});var o=i(`#stage`),s=i(`#bubble`);function c(t){let r=n[t];if(r===void 0)throw Error(`pet renderer: unknown animation ${t}`);let i=r[Math.floor(Math.random()*r.length)]??r[0];return`pet-asset:///${e}/videos/${i.dir}/${encodeURIComponent(i.file)}.webm`}function l(e,t){let n=t.getContext(`webgl`,{alpha:!0,premultipliedAlpha:!1});if(n===null){t.replaceWith(e),e.style.display=`block`;return}let r=(e,t)=>{let r=n.createShader(e);if(r===null)throw Error(`pet renderer: shader allocation failed`);if(n.shaderSource(r,t),n.compileShader(r),!n.getShaderParameter(r,n.COMPILE_STATUS))throw Error(`pet renderer: shader compile failed: ${String(n.getShaderInfoLog(r))}`);return r},i=n.createProgram();if(i===null)throw Error(`pet renderer: program allocation failed`);if(n.attachShader(i,r(n.VERTEX_SHADER,`attribute vec2 p; varying vec2 v;void main(){ v = vec2(p.x * 0.5 + 0.5, 0.5 - p.y * 0.5); gl_Position = vec4(p, 0.0, 1.0); }`)),n.attachShader(i,r(n.FRAGMENT_SHADER,`precision highp float; varying vec2 v; uniform sampler2D t;void main(){ gl_FragColor = texture2D(t, v); }`)),n.linkProgram(i),!n.getProgramParameter(i,n.LINK_STATUS))throw Error(`pet renderer: program link failed: ${String(n.getProgramInfoLog(i))}`);n.useProgram(i);let a=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,a),n.bufferData(n.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),n.STATIC_DRAW);let o=n.getAttribLocation(i,`p`);n.enableVertexAttribArray(o),n.vertexAttribPointer(o,2,n.FLOAT,!1,0,0);let s=n.createTexture();n.bindTexture(n.TEXTURE_2D,s),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.enable(n.BLEND),n.blendFunc(n.SRC_ALPHA,n.ONE_MINUS_SRC_ALPHA),n.clearColor(0,0,0,0);let c=()=>{e.readyState<2||e.videoWidth===0||((t.width!==e.videoWidth||t.height!==e.videoHeight)&&(t.width=e.videoWidth,t.height=e.videoHeight),n.viewport(0,0,t.width,t.height),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,e),n.clear(n.COLOR_BUFFER_BIT),n.drawArrays(n.TRIANGLE_STRIP,0,4))},l=e,u=()=>{typeof l.requestVideoFrameCallback==`function`?l.requestVideoFrameCallback(()=>{c(),u()}):requestAnimationFrame(()=>{c(),u()})};u()}var u=`idle`,d=null,f=0;function p(e){let t=c(e);a.src!==t&&(a.src=t,a.loop=e!==`celebrate`,a.play().catch(()=>{}))}function m(){p(Date.now()<f?`error`:d===null?u===`working`?`working`:u:`waiting`),T(),U()}function h(e){if(u===e)return;let t=u===`working`&&e===`idle`;if(u=e,e!==`idle`&&x!==void 0&&(clearTimeout(x),x=void 0),t){g(`celebrate`);return}m()}function g(e,t){_(n[e]?.[0]??{dir:`click`,file:e},t)}function _(n,r){let i=`pet-asset:///${e}/videos/${n.dir}/${encodeURIComponent(n.file)}.webm`;if(a.src===i)return;C=!0;let o=()=>{a.onended=null,a.onerror=null,C=!1,t.petDesktop?.reportActionDone({action:n.file,durationMs:Number.isFinite(a.duration)?Math.round(a.duration*1e3):void 0,...r===void 0?{}:{requestId:r}}),m()};a.onended=o,a.onerror=o,a.loop=!1,a.src=i,a.play().catch(()=>{})}function v(){f=Date.now()+6e3,m(),setTimeout(m,6100)}var y=new Map;t.petDesktop?.onCatalog?.(e=>{let t=e.actions;if(Array.isArray(t)){y.clear();for(let e of t){let t=e;typeof t.id==`string`&&typeof t.dir==`string`&&typeof t.file==`string`&&y.set(t.id,{dir:t.dir,file:t.file})}T()}});var b={enabled:!1,gapMinutes:10},x,S,C=!1;function w(){return[...y.values()].filter(e=>e.dir===`random`&&!r.has(e.file)&&e.file!==S)}function T(){x===void 0&&(!b.enabled||u!==`idle`||d!==null||Date.now()<f||w().length!==0&&(x=setTimeout(()=>{if(x=void 0,!b.enabled||u!==`idle`||d!==null||C||Date.now()<f){T();return}let e=w(),t=e[Math.floor(Math.random()*e.length)];t!==void 0&&(S=t.file,_(t))},Math.max(1,b.gapMinutes)*6e4)))}var E=new Map;function D(){s.hidden=!0,s.replaceChildren(),t.petDesktop?.setBubbleVisible(!1)}function O(e){if(e===void 0||typeof e==`string`)return e;let t=navigator.languages??[navigator.language];for(let n of t){let t=e[n.split(`-`)[0]??``];if(typeof t==`string`)return t}return typeof e.en==`string`?e.en:void 0}function k(e,n){s.replaceChildren();let r=document.createElement(`p`);r.className=`bubble-title`,r.textContent=e.toolName===void 0||e.toolName===``?`请求执行`:`请求执行:${e.toolName}`;let i=document.createElement(`p`);i.className=`bubble-detail`,i.textContent=O(e.displayReason)??e.reason??``;let a=document.createElement(`div`);a.className=`bubble-actions`;let o=document.createElement(`button`);o.className=`bubble-button primary`,o.textContent=`允许`,o.addEventListener(`click`,()=>{D(),n(`allowed-once`)});let c=document.createElement(`button`);c.className=`bubble-button danger`,c.textContent=`拒绝`,c.addEventListener(`click`,()=>{D(),n(`rejected`)}),a.append(o,c),s.append(r,i,a),s.hidden=!1,t.petDesktop?.setBubbleVisible(!0),m()}function A(e,n){E.clear(),s.replaceChildren();for(let t of e){E.set(t.id,[]);let r=document.createElement(`p`);r.className=`bubble-question`;let i=O(t.question)??``,a=O(t.header);r.textContent=a===void 0||a===``?i:`${a} · ${i}`,s.append(r);let o=O(t.detail);if(o!==void 0&&o!==``){let e=document.createElement(`p`);e.className=`bubble-detail`,e.textContent=o,s.append(e)}let c=document.createElement(`div`);c.className=`bubble-options`;for(let r of t.options??[]){let i=O(r.label)??``,a=document.createElement(`button`);a.className=`bubble-button`,a.textContent=i;let o=O(r.description);o!==void 0&&(a.title=o),a.addEventListener(`click`,()=>{E.set(t.id,[i]);for(let e of c.children)e.classList.remove(`primary`);a.classList.add(`primary`),e.every(e=>(E.get(e.id)??[]).length>0)&&n({answers:e.map(e=>({id:e.id,selected:E.get(e.id)??[]}))})}),c.append(a)}s.append(c)}s.hidden=!1,t.petDesktop?.setBubbleVisible(!0),m()}var j=0,M,N=[];function P(){l(a,o),m()}var F=i(`#pet`);function I(e,t){let n=F.getBoundingClientRect(),r=Math.min(n.width/640,n.height/360),i=640*r,a=360*r,o=(e-n.left-(n.width-i)/2)/r,s=(t-n.top-(n.height-a)/2)/r,c=e=>o>=e[0]&&o<=e[2]&&s>=e[1]&&s<=e[3];return c([185,60,436,195])?`head`:c([212,60,428,330])?`body`:`unknown`}var L=5,R=500,z;function B(e,r){let i=I(e,r),a=n.touch;a!==void 0&&a.length>0&&d===null&&Date.now()>=f&&_(a[Math.floor(Math.random()*a.length)]),z!==void 0&&clearTimeout(z),z=setTimeout(()=>{z=void 0,t.petDesktop?.reportTouch({part:i})},260)}F.addEventListener(`pointerdown`,e=>{if(e.button!==0)return;try{F.setPointerCapture(e.pointerId)}catch{}let n=e.screenX,r=e.screenY,i=Date.now();t.petDesktop?.dragStart(n,r);let a=e=>{t.petDesktop?.dragMove(e.screenX,e.screenY)},o=e=>{F.removeEventListener(`pointermove`,a),F.removeEventListener(`pointerup`,o),F.removeEventListener(`pointercancel`,o),t.petDesktop?.dragEnd(),Math.hypot(e.screenX-n,e.screenY-r)<=L&&Date.now()-i<=R&&B(e.clientX,e.clientY)};F.addEventListener(`pointermove`,a),F.addEventListener(`pointerup`,o),F.addEventListener(`pointercancel`,o)});function V(){if(d!==null||Date.now()<f)return;s.replaceChildren();let e=document.createElement(`div`);e.className=`bubble-chat`;let n=document.createElement(`input`);n.className=`bubble-chat-input`,n.maxLength=2e3,n.placeholder=`和鲸鱼娘说点什么…`;let r=document.createElement(`button`);r.className=`bubble-button primary`,r.textContent=`发送`;let i=document.createElement(`button`);i.className=`bubble-button`,i.textContent=`取消`,i.addEventListener(`click`,()=>{D()});let a=async()=>{let e=n.value.trim();if(e===``||r.disabled)return;r.disabled=!0,n.disabled=!0;let i=await t.petDesktop?.sendChat(e);if(i?.ok===!0){D();return}r.disabled=!1,n.disabled=!1,n.placeholder=i?.reason===`busy`?`上一条还没送达,稍等一下…`:`没发出去,再试一次`,n.focus()};r.addEventListener(`click`,()=>{a()}),n.addEventListener(`keydown`,e=>{e.key===`Enter`?(e.preventDefault(),a()):e.key===`Escape`&&D()}),e.append(n,r,i),s.append(e),s.hidden=!1,t.petDesktop?.setBubbleVisible(!0),m(),n.focus()}F.addEventListener(`dblclick`,e=>{e.stopPropagation(),z!==void 0&&(clearTimeout(z),z=void 0),V()}),t.petDesktop?.onSay(e=>{if(d!==null||e.text===``)return;s.replaceChildren();let n=document.createElement(`p`);n.className=`bubble-detail`,n.textContent=e.text,s.append(n),s.hidden=!1,t.petDesktop?.setBubbleVisible(!0),setTimeout(()=>{d===null&&s.textContent===e.text&&D()},4e3+Math.min(e.text.length*60,4e3))});var H=``,U=()=>{let e=JSON.stringify({state:u,override:d,animation:decodeURIComponent(a.src.split(`/`).pop()??``)});e!==H&&(H=e,t.petDesktop?.reportStatus(JSON.parse(e)))};t.petDesktop?.onState(e=>{let t=e,n=t.autonomy;if(typeof n==`object`&&n){let e=n,t=typeof e.idleAnimation==`boolean`?e.idleAnimation:b.enabled,r=typeof e.gapMinutes==`number`&&Number.isFinite(e.gapMinutes)?Math.min(120,Math.max(1,Math.round(e.gapMinutes))):b.gapMinutes;(t!==b.enabled||r!==b.gapMinutes)&&(b={enabled:t,gapMinutes:r},x!==void 0&&(clearTimeout(x),x=void 0),T())}let r=t.state;r===`idle`||r===`thinking`||r===`working`?h(r):r===`error`&&v()}),t.petDesktop?.onAction(e=>{let t=e,n=t.action,r=typeof t.requestId==`string`?t.requestId:void 0;if(n===`celebrate`)g(`celebrate`,r);else if(n===`error`)v();else if(typeof n==`string`){let e=y.get(n);e!==void 0&&_(e,r)}});function W(){let e=s.querySelector(`.bubble-queue`);if(N.length===0){e?.remove();return}let t=`还有 ${String(N.length)} 个待处理`;if(e!==null){e.textContent=t;return}let n=document.createElement(`p`);n.className=`bubble-queue`,n.textContent=t,s.append(n)}function G(e,n){let r=++j;M=e;let i=n=>{if(r!==j)return;D(),d=null,M=void 0,m(),t.petDesktop?.answerInteraction(e,n);let i=N.shift();i!==void 0&&G(i.id,i.message)};n.kind===`question`?(d=`waiting_question`,m(),A(n.questions??[],i)):(d=`waiting_approval`,m(),k({toolName:n.toolName,reason:n.reason,...n.displayReason===void 0?{}:{displayReason:n.displayReason}},i)),W()}t.petDesktop?.onInteraction(e=>{let t=e,n=t.id;if(typeof n==`string`){if(M!==void 0){N.push({id:n,message:t}),W();return}G(n,t)}}),t.petDesktop?.onInteractionCancel(e=>{let t=N.findIndex(t=>t.id===e.id);if(t!==-1){N.splice(t,1),W();return}if(e.id!==M)return;j+=1,M=void 0,D(),d=null,m();let n=N.shift();n!==void 0&&G(n.id,n.message)}),P();
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>dsh pet</title>
7
+ <script type="module" crossorigin src="./assets/index-C61Q_dyJ.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-BHG2MI7t.css">
9
+ </head>
10
+ <body>
11
+ <div id="root">
12
+ <div id="bubble" data-interactive hidden></div>
13
+ <div id="pet" data-interactive>
14
+ <video id="video" muted playsinline crossorigin="anonymous"></video>
15
+ <canvas id="stage"></canvas>
16
+ </div>
17
+ </div>
18
+ </body>
19
+ </html>
@@ -0,0 +1,49 @@
1
+ /**
2
+ * dsh-pet profile runner child. Runs the `pet` profile through the shared CLI
3
+ * profile runner inside Electron's Node mode and reports the authenticated
4
+ * web URL to the carrier over Node IPC. Mirrors apps/desktop-host minus the
5
+ * desktop-only machinery (office engine, file drag, update tasks, quit
6
+ * inspection): the pet surface needs the webserver, Connection, and the API
7
+ * gateway stack and nothing else.
8
+ */
9
+ import { loadLayeredEnv } from '@deepseek-ai/dsh-app-boot';
10
+ import { runProfile } from '@deepseek-ai/dsh/profile-boot';
11
+ async function main() {
12
+ const application = await runProfile({
13
+ environment: loadLayeredEnv('dsh'),
14
+ profile: 'pet',
15
+ patchFiles: [],
16
+ args: ['--no-open', '--host', '127.0.0.1', '--port', '0'],
17
+ });
18
+ const { ctx, shutdown } = application;
19
+ const webServer = ctx.get('webServer');
20
+ const connection = ctx.get('connection');
21
+ if (webServer === undefined || connection === undefined) {
22
+ throw new Error('pet host: the pet profile booted without the web surface stack');
23
+ }
24
+ const url = connection.authenticatedUrl(`${webServer.protocol}//127.0.0.1:${String(webServer.port)}`);
25
+ process.send?.({ type: 'ready', url });
26
+ let stopping;
27
+ const stop = () => {
28
+ stopping ??= (async () => {
29
+ try {
30
+ await shutdown.shutdown(0);
31
+ }
32
+ finally {
33
+ process.send?.({ type: 'shutdown-complete' });
34
+ process.disconnect();
35
+ process.exit(0);
36
+ }
37
+ })();
38
+ return stopping;
39
+ };
40
+ process.on('message', (message) => {
41
+ if (message?.type === 'shutdown')
42
+ void stop();
43
+ });
44
+ // The carrier dying takes the profile with it: a disconnected IPC channel
45
+ // means nobody owns this tree anymore.
46
+ process.once('disconnect', () => { void stop(); });
47
+ }
48
+ await main();
49
+ //# sourceMappingURL=host-child.js.map