@nelsonlongxiang/dsh-open-a2a-net 0.6.34 → 0.6.36

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 CHANGED
@@ -109,7 +109,7 @@ A canvas team is a user-composed, named, multi-member routing group: **one atomi
109
109
 
110
110
  - Storage: `<dsh-home>/a2a/canvas.json` (ordered team entries; caps: 64 teams × 32 members)
111
111
  - Control API: `POST /__dsh_a2a/canvas` with `action: create | remove | add-member | remove-member`; `cli-home-options` is a read-only owner action for an existing CLI member.
112
- - For a CLI member marked “home 未确定”, open its canvas team inspector and choose “指定 home”. The host freshly reads cards, lists only linked advertisers, and reports unreadable-card counts. No home is selected automatically; confirming calls the existing local `add-member` with explicit `home`, which freshly validates again. A locally held household (live or offline tombstone) instead shows “本机持有,无需指定”, returns `held: true` with no choices, and rejects explicit home writes until that household is revoked. Online local households receive receipts before any older pin. This does not weaken automatic pinning or admission.
112
+ - For a CLI member marked “home 未确定”, open its canvas team inspector and choose “指定 home”. The host freshly reads cards, lists only linked advertisers, and reports unreadable-card counts. No home is selected automatically; confirming calls the existing local `add-member` with explicit `home`, which freshly validates again. A locally held household (live or offline tombstone) instead shows “本机持有,无需指定”, returns `held: true` with no choices, and rejects explicit home writes until that household is revoked. Online local households receive receipts first. A successful local publish removes a conflicting older pin and leaves a durable inspector notice; expiry during local receipt delivery fails closed instead of resolving another advertiser. This does not weaken automatic pinning or admission.
113
113
  - Membership requires a joined session (live node or remembered intent) — no routing backdoor over unjoined sessions; leaving the network (leave/archive) drops every canvas membership
114
114
  - A member may also be a **remote** zone-qualified team handle (`zone/a1b2c3d4`) the operator adds by hand — the link that admits that remote caller here and entitles it to the full card
115
115
  - `a2a_teams` lists canvas teams as local rows with member/live counts; `/__dsh_a2a/state` serves per-member `joined`/`live` flags for local members and `{ id, team, remote: true }` for remote ones
@@ -164,7 +164,7 @@ Row config overlays in the profile's patch layers; every key has a schema defaul
164
164
  | `gateAgentLessCalls` | `'audit'` | Host-side (agent-less) routing calls: audit them, or `refuse`. |
165
165
  | `settlementAuth` | `'observe'` | Settlement authorization (#114): `off`, `observe` (record what would be refused, settle as before), or `enforce`. |
166
166
  | `asyncNudgeDelayMs` | `120000` | Delay before a delivered-but-unconsumed async route re-wakes its target with a one-line nudge; `0` disables. |
167
- | `targetQueueCap` | `3` | At most this many receipt-owing tasks queue for one target on this host; the next delivery is refused visibly (`-32009 target-queue-full`, nothing delivered). A receipt for a task this host booked and CLI mailboxes are never refused; a canvas team counts as one target. A refused task id is recorded as failed — retry with a new id. `0` disables (#224). |
167
+ | `targetQueueCap` | `3` | At most this many unanswered, receipt-owing tasks queue for one target on this host; the next delivery is refused visibly (`-32009 target-queue-full`, nothing delivered). A receipt for a task this host booked and CLI mailboxes are never refused; a canvas team counts as one target. A refused task id is recorded as failed — retry with a new id. A target-attributed final or its own receipt attempt records `answeredAt` and releases queue capacity even if receipt delivery fails; the row remains owed, with unchanged expiry, late-receipt and conservation semantics. State/board queue counts use this same rule. `0` disables (#224/#253). |
168
168
  | `taskStaleTtlMs` | `86400000` | Owed-task stale TTL (24h): past it a pending row auto-dead-letters (a late receipt can still settle it). |
169
169
  | `dshHome` | `''` | DSH home directory override for the plugin's state (`<home>/a2a/…`); `''` resolves the default home. |
170
170
  | `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | see schema | Card lifetime (48h) and route timing budgets. |
package/README_zh.md CHANGED
@@ -69,7 +69,7 @@ a2a-net inbox --new # 未读来信;--wait
69
69
 
70
70
  - 存储:`<dsh-home>/a2a/canvas.json`(有序队伍条目;上限:64 队 × 32 成员)
71
71
  - 控制 API:`POST /__dsh_a2a/canvas`,`action: create | remove | add-member | remove-member`;`cli-home-options` 是仅查询已有 CLI 成员候选的属主操作。
72
- - CLI 成员标「home 未确定」时,打开其画布队详情并点「指定 home」。宿主现读名片,只列已链接且列出该句柄的主机,并显示不可读名片数;不会自动选中。确认后复用本机 `add-member` 的显式 `home`,提交时再次现读校验,不放宽自动钉或准入。本机持有在线户口或离线墓碑时显示「本机持有,无需指定」,候选返回 `held:true` 且为空,显式 home 写入提示先撤销本机户口;回执优先投在线本机信箱,无在线户口才按既有 pin 投递。
72
+ - CLI 成员标「home 未确定」时,打开其画布队详情并点「指定 home」。宿主现读名片,只列已链接且列出该句柄的主机,并显示不可读名片数;不会自动选中。确认后复用本机 `add-member` 的显式 `home`,提交时再次现读校验,不放宽自动钉或准入。本机持有在线户口或离线墓碑时显示「本机持有,无需指定」,候选返回 `held:true` 且为空,显式 home 写入提示先撤销本机户口;回执优先投在线本机信箱;本机 publish 成功会清除冲突旧钉并在面板提示,户口离线不复活旧钉;本地投递中途到期即报错,不回落对端目录。
73
73
  - 成员必须是 joined 会话(活节点或已记意图)——未加入会话没有路由后门;退出网络(leave/archive)自动清空全部画布成员关系
74
74
  - 成员也可以是操作者手工加入的**远端**带 zone 团队句柄(`zone/a1b2c3d4`)——这就是让该远端调用方在本机获准入、并有权拿完整卡片的链接
75
75
  - `a2a_teams` 以本地行列出画布队伍(含成员数/活数);`/__dsh_a2a/state` 对本地成员提供 `joined`/`live` 标记,对远端成员提供 `{ id, team, remote: true }`
@@ -122,7 +122,7 @@ a2a-net inbox --new # 未读来信;--wait
122
122
  | `gateAgentLessCalls` | `'audit'` | 宿主侧(无 agent)路由调用:审计,或 `refuse` 拒绝。 |
123
123
  | `settlementAuth` | `'observe'` | 结算授权(#114):`off`、`observe`(记下本会拒绝的,照旧结算)或 `enforce`。 |
124
124
  | `asyncNudgeDelayMs` | `120000` | 已投递但未被消费的异步路由,在此延迟后用一行催办重新唤醒目标;`0` 关闭。 |
125
- | `targetQueueCap` | `3` | 本宿主上同一个目标最多排队这么多条等回执的任务;再来新投递就可见拒收(`-32009 target-queue-full`,不投递)。本宿主自己记过账的任务的回执、以及 CLI 信箱从不拦;一个画布队算一个目标。被拒的 task id 记为失败,重试要换新 id。`0` 关闭(#224)。 |
125
+ | `targetQueueCap` | `3` | 本宿主上同一个目标最多排队这么多条目标尚未回应且等回执的任务;再来新投递就可见拒收(`-32009 target-queue-full`,不投递)。本宿主自己记过账的任务的回执、以及 CLI 信箱从不拦;一个画布队算一个目标。被拒的 task id 记为失败,重试要换新 id。目标消费本任务后的 final 或目标自己发回执会记 `answeredAt` 并释放名额,即使回执投递失败;行仍欠回执,死信、迟到回执和守恒不变,状态面/画布排队数采用同一口径。`0` 关闭(#224/#253)。 |
126
126
  | `taskStaleTtlMs` | `86400000` | 欠账任务的 stale TTL(24h):过期后 pending 行自动转死信(迟到回执仍可结清)。 |
127
127
  | `dshHome` | `''` | 插件状态所用的 DSH home 目录覆盖(`<home>/a2a/…`);`''` 解析默认 home。 |
128
128
  | `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | 见 schema | 卡片生命周期(48h)与路由时序预算。 |
@@ -0,0 +1,9 @@
1
+ var Ao=Object.defineProperty;var Lo=(e,t,n)=>t in e?Ao(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Ct=(e,t,n)=>Lo(e,typeof t!="symbol"?t+"":t,n);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))s(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&s(r)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const an=6e4;function Io(e,t,n=Date.now){let s=-an;return{fault(o){const i=n();i-s>=an&&(t(`[nexus] ${o}`),s=i),e.show(o)},clear(){e.hide(),s=-an}}}function it(e){return e.name??e.team??e.label??""}function so(e,t){var i,r;const n=(c,d)=>c!==void 0&&c.trim()!==""?{name:c,remote:d}:void 0,s=(e.sessions??[]).find(c=>c.team===t);if(s!==void 0)return n(it(s),!1);const o=(e.remote??[]).find(c=>c.team===t);return o!==void 0?n(it(o),!0):n((r=(((i=e.canvas)==null?void 0:i.teams)??[]).find(c=>c.team===t))==null?void 0:r.name,!1)}function To(e,t){const n=so(e,t);return n===void 0?t:n.remote?`${n.name}(远端)`:n.name}function qt(e,t,n,s=""){const o=so(t,n);if(e.textContent=`${s}${(o==null?void 0:o.name)??n}`,e.title=n,(o==null?void 0:o.remote)===!0){const i=e.appendChild(document.createElement("span"));i.className="wb-row-remote",i.textContent="远端",i.title="名字由对端自行发布"}}const _o=60*6e4;function Et(e){var t;return e.direction==="inbound"||e.direction===void 0&&((t=e.peer)==null?void 0:t.startsWith("local:"))===!0}function An(e){var t;return((t=e.peer)==null?void 0:t.startsWith("local:"))===!0?e.peer.slice(6):void 0}function Ft(e,t){return e.settleable!==void 0?e.settleable===!1:t-e.startedAt>=_o}function pn(e,t){const n=(e.tasks??[]).filter(r=>r.receiptExpected!==!1),s=n.filter(r=>!Et(r)&&Ft(r,t)),o=n.filter(r=>Et(r)&&Ft(r,t)),i=e.tasksDead??[];return{stale:s,owedOverdue:o,deadLetters:i.filter(r=>r.reason!=="fire-forget"),expired:i.filter(r=>r.reason==="fire-forget")}}function Ee(e,t){const n=document.createElement(e);return t!==void 0&&(n.className=t),n}function Ln(e,t){const n=Math.max(0,Math.round((t-e)/1e3));if(n<90)return`${n}s`;const s=Math.round(n/60);if(s<90)return`${s}m`;const o=Math.round(s/60);return o<48?`${o}h`:`${Math.round(o/24)}d`}function jt(e,t,n,s,o){const i=Ee("div",`wb-led-tile${o!==void 0?` is-${o}`:""}`);i.title=s,i.appendChild(Ee("span","wb-led-tile-v")).textContent=String(n),i.appendChild(Ee("span","wb-led-tile-k")).textContent=t,e.appendChild(i)}const hn=[{key:"completed",label:"完成",tone:"completed"},{key:"sync_completed",label:"同步完成",tone:"completed"},{key:"rejected",label:"拒绝",tone:"rejected"},{key:"failed",label:"失败",tone:"failed"},{key:"expired",label:"过期",tone:"abandoned"},{key:"abandoned",label:"放弃",tone:"abandoned"},{key:"unknown",label:"结论不明",tone:"unclassified"},{key:"unclassified",label:"未分类",tone:"unclassified"}];function Oo(e,t,n){const s=Ee("div","wb-led-split"),o=Ee("div","wb-led-split-bar");o.setAttribute("role","img");const i=Ee("div","wb-led-split-legend"),r=new Set(hn.map(m=>m.key)),c=hn.map(m=>[m.tone,m.label,t[m.key]??0]);for(const[m,g]of Object.entries(t))r.has(m)||c.push(["other",m,g]);const d=[];for(const[m,g,u]of c){if(u<=0)continue;const E=Ee("span",`wb-led-seg is-${m}`);E.style.width=`${Math.max(2,u/n*100)}%`,o.appendChild(E);const M=Ee("span","wb-led-split-item");M.appendChild(Ee("i",`wb-led-swatch is-${m}`)),M.appendChild(document.createTextNode(`${g} ${u}`)),i.appendChild(M),d.push(`${g} ${u}`)}o.setAttribute("aria-label",`归档结局:${d.join(",")}`),s.append(o,i),e.appendChild(s)}function Fo(e,t){var Ae;e.replaceChildren();const{state:n,now:s}=t,o=n.tasks??[],i=n.tasksDead??[],r=n.archivedCount??0,c=pn(n,s),d=o.filter(P=>P.receiptExpected===!1),m=o.filter(P=>P.receiptExpected!==!1),g=m.filter(P=>Et(P)),u=m.filter(P=>!Et(P)),E=c.stale,M=u.filter(P=>!Ft(P,s)),J=c.stale.length,fe=Ee("div","wb-led-tiles");jt(fe,"待回执",m.length,`要回执、仍未结算的任务(含陈旧与本机欠对方的入站行)${d.length>0?`;另有免回执 ${d.length} 条不计`:""}`),jt(fe,"陈旧",J,"我方派发、要回执、已等待 ≥1h 仍未回执(可催办);本机欠对方的入站行在「待本机回执」组另计,免回执行不算陈旧",J>0?"warn":void 0),jt(fe,"死信",c.deadLetters.length,"超过 24h TTL 仍未结算(unconsumed);需清账(abandon)才能移出。免回执到期另列,不算死信",c.deadLetters.length>0?"danger":void 0),jt(fe,"已归档",r,"已离开台账并留档的任务(结算、拒绝或放弃),用于审计"),e.appendChild(fe),r>0&&n.archivedSplit!==void 0&&Oo(e,n.archivedSplit,r);const ne=(P,U)=>{const ie=Ee("div",`wb-led-card is-${U.tone}`),Re=Ee("div","wb-led-card-head");Re.appendChild(Ee("span",`wb-led-dir${U.inbound?" is-in":""}`)).textContent=U.inbound?"↙":"↗";const $e=Ee("span","wb-led-brief");$e.textContent=U.brief!==void 0&&U.brief!==""?U.brief:"(无摘要——派发未带 brief)",(U.brief===void 0||U.brief==="")&&$e.classList.add("is-missing"),Re.appendChild($e),ie.appendChild(Re);const re=Ee("div","wb-led-meta"),Ce=re.appendChild(Ee("span","wb-led-team"));U.inbound?U.sender!==void 0?qt(Ce,n,U.sender,"来自 "):Ce.textContent="来自 对端":qt(Ce,n,U.team,"发往 "),U.inbound&&qt(re.appendChild(Ee("span","wb-led-to")),n,U.team,"至 "),re.appendChild(Ee("span")).textContent=U.atText,re.appendChild(Ee("span","wb-led-task")).textContent=U.taskId;for(const Q of U.chips)re.appendChild(Ee("span","wb-led-chip")).textContent=Q;if(ie.appendChild(re),ie.title=`${U.taskId}
2
+ ${U.team}`,U.dead!==void 0){const Q=U.dead,oe=Q.reason==="fire-forget",xe=Ee("button",`wb-btn wb-led-abandon${oe?" is-quiet":""}`);xe.type="button",xe.textContent=oe?"移出":"清账",xe.title=oe?"可选:把这条已过期的免回执记录移出台账(不是失败,不处理也无妨)":"从台账移出这条死信(abandon)",xe.addEventListener("click",le=>{le.stopPropagation(),xe.disabled=!0,t.abandon(Q.taskId,"workbench dead-letter cleanup").then(te=>{xe.disabled=!1,xe.textContent=te?"已清":"失败"})}),ie.appendChild(xe)}if(t.canOpenTeam(U.team)){ie.classList.add("is-link"),ie.setAttribute("role","button"),ie.tabIndex=0,ie.setAttribute("aria-label",`${$e.textContent},打开 ${U.team} 的对话`);const Q=()=>{t.openTeam(U.team)};ie.addEventListener("click",Q),ie.addEventListener("keydown",oe=>{(oe.key==="Enter"||oe.key===" ")&&(oe.preventDefault(),Q())})}P.appendChild(ie)},S=(P,U,ie,Re)=>{if(U===0)return null;const $e=Ee("div",`wb-led-group is-${ie}`);$e.dataset.group=P;const re=Ee("div","wb-led-group-head");return re.appendChild(Ee("span")).textContent=`${P} · ${U}`,re.appendChild(Ee("span","wb-led-group-hint")).textContent=Re,$e.appendChild(re),e.appendChild($e),$e},C=(P,U)=>{const ie=Et(P);return{brief:P.brief,team:P.team,sender:An(P),taskId:P.taskId,atText:`${Ln(P.startedAt,s)} 前${ie?"收到":"派发"}`,inbound:ie,tone:U,chips:[...P.settleable===!0?["可结算"]:[],...P.receiptExpected===!1?["无回执债"]:[],...ie?["入站"]:[],...P.status!=="pending"?[P.status]:[]]}},$=(P,U,ie)=>({brief:P.brief,team:P.team,sender:An(P),taskId:P.taskId,atText:`${Ln(P.deadAt,s)} 前${ie}`,inbound:Et(P),tone:U,dead:P,chips:P.reason!==void 0&&P.reason!==""&&P.reason!=="unspecified"&&P.reason!=="fire-forget"?[P.reason]:[]}),k=S("死信",c.deadLetters.length,"danger","需清账");if(k!==null)for(const P of[...c.deadLetters].sort((U,ie)=>ie.deadAt-U.deadAt))ne(k,$(P,"danger","死信"));const V=S("陈旧",E.length,"warn","我方派发 ≥1 小时仍无回执,可催办");if(V!==null)for(const P of[...E].sort((U,ie)=>U.startedAt-ie.startedAt))ne(V,C(P,"warn"));const y=c.owedOverdue.length,f=S("待本机回执",g.length,y>0?"warn":"quiet",y>0?`对方在等本机回执 · ${y} 条超过 1 小时`:"对方在等本机回执");if(f!==null)for(const P of[...g].sort((U,ie)=>U.startedAt-ie.startedAt))ne(f,C(P,Ft(P,s)?"warn":"quiet"));const _=S("等待回执",M.length,"quiet","1 小时内");if(_!==null)for(const P of[...M].sort((U,ie)=>ie.startedAt-U.startedAt))ne(_,C(P,"quiet"));const p=S("免回执",d.length,"muted","不等回执,24h 后自动归档");if(p!==null)for(const P of d)ne(p,C(P,"muted"));const R=S("已过期(免回执)",c.expired.length,"muted","正常记账,不是失败");if(R!==null)for(const P of[...c.expired].sort((U,ie)=>ie.deadAt-U.deadAt))ne(R,$(P,"muted","过期"));o.length+i.length===0&&(e.appendChild(Ee("div","wb-led-empty")).textContent="台账为空——没有待回执的任务");const j=n.idempotency,K=(Ae=n.reconcile)==null?void 0:Ae.state;if(j!==void 0||K!==void 0){const P=Ee("details","wb-led-forensics wb-forensics");P.appendChild(Ee("summary")).textContent="取证";const U=Ee("div","wb-led-forensics-body");j!==void 0&&(U.appendChild(Ee("div")).textContent=`幂等 claims ${j.claimsFresh??0} · replays ${j.replays??0} · conflicts ${j.conflicts??0}`),K!==void 0&&(U.appendChild(Ee("div")).textContent=`reconcile ${K}`),P.appendChild(U),e.appendChild(P)}const we=m.length+c.deadLetters.length;return{badge:we>0?String(we):"",tone:c.deadLetters.length>0?"danger":J>0||y>0?"warn":void 0}}const Lt=4e3,Po=32;function ye(e,t){const n=document.createElement(e);return t!==void 0&&(n.className=t),n}function Bt(e,t,n=""){const s=ye("button",`wb-icon-btn wb-chat-act ${n}`.trim());return s.type="button",s.setAttribute("aria-label",e),s.title=e,s.textContent=t,s}function cn(e,t){const n=Math.max(0,Math.round((t-e)/1e3));if(n<60)return"刚刚";const s=Math.round(n/60);if(s<60)return`${s} 分钟前`;const o=Math.round(s/60);return o<48?`${o} 小时前`:`${Math.round(o/24)} 天前`}function Ro(e){return e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${Math.round(e/1e3)} KB`:`${e} B`}function ln(e){const t=e.kind==="session"||e.kind==="remote"?e.team:void 0;return t!==void 0&&t!==""?t:void 0}function In(e){return e.kind==="remote"?`remote:${e.team}`:`${e.kind}:${e.id}`}function Do(e){let t=null,n=null,s=!1,o=!1;const i=new Map,r=new Map,c=new Map,d=new Map,m=new Set,g=new Map,u=new Map,E=ye("aside","wb-chat");E.setAttribute("aria-label","对话");const M=ye("div","wb-chat-head"),J=Bt("返回会话列表","‹","wb-chat-back"),fe=ye("span","wb-chat-dot"),ne=ye("div","wb-chat-titles"),S=ye("span","wb-chat-title"),C=ye("span","wb-chat-sub");ne.append(S,C);const $=Bt("刷新对话","↻","wb-chat-refresh"),k=Bt("会话详情","ⓘ","wb-chat-info-btn");k.setAttribute("aria-expanded","false");const V=Bt("关闭对话","✕","wb-chat-close");M.append(J,fe,ne,$,k,V);const y=ye("div","wb-chat-info");y.hidden=!0;const f=ye("div","wb-chat-thread");f.setAttribute("role","log"),f.setAttribute("aria-live","polite"),f.tabIndex=0;const _=ye("div","wb-chat-notice"),p=ye("div","wb-chat-log"),R=ye("div","wb-chat-sent");f.append(_,p,R);const j=ye("div","wb-chat-foot");E.append(M,y,f,j),J.addEventListener("click",()=>{(t==null?void 0:t.kind)==="peer"&&(t.picked=void 0,Oe())}),$.addEventListener("click",()=>{if(t===null)return;const l=K(t);l!==void 0&&i.delete(l),t.kind==="peer"&&t.picked===void 0&&t.url!==void 0&&c.delete(`peer:${t.url}`),xe(!0)}),k.addEventListener("click",()=>{o=!o,y.hidden=!o,k.setAttribute("aria-expanded",String(o))}),V.addEventListener("click",()=>lt()),f.addEventListener("scroll",()=>{const l=t!==null?K(t):void 0;l!==void 0&&r.set(l,f.scrollTop)}),document.addEventListener("keydown",l=>{if(l.key!=="Escape"||!E.classList.contains("is-open"))return;const A=l.target;if(A instanceof HTMLElement){if(!E.contains(A)){const L=A instanceof HTMLTextAreaElement||A instanceof HTMLInputElement||A.isContentEditable,Z=A.closest(".wb-stage-board")!==null;if(L||Z)return}lt()}});function K(l){if(l.kind==="session"||l.kind==="log")return e.sessionLog!==void 0?`local:${l.id}`:void 0;if(l.kind==="remote")return e.remoteSessionLog!==void 0?`remote:${l.team}`:void 0;if(l.picked!==void 0&&l.url!==void 0&&e.remoteSessionLog!==void 0)return`peer:${l.url}:${l.picked}`}function we(l){var A,L,Z;return l.kind==="session"||l.kind==="log"?((A=e.sessionLog)==null?void 0:A.call(e,l.id))??Promise.resolve({ok:!1,error:"sessionLog unavailable"}):l.kind==="remote"?((L=e.remoteSessionLog)==null?void 0:L.call(e,{team:l.team}))??Promise.resolve({ok:!1,error:"remoteSessionLog unavailable"}):((Z=e.remoteSessionLog)==null?void 0:Z.call(e,{peer:l.url,id:l.picked}))??Promise.resolve({ok:!1,error:"remoteSessionLog unavailable"})}function Ae(){const l=t;if(l===null)return;const A=l.kind==="log"||l.kind==="remote"||l.kind==="session"&&l.live===!1;fe.className=`wb-chat-dot ${l.kind==="peer"?"is-peer":A?"is-cold":"is-live"}`,fe.title=l.kind==="peer"?"对端主机":l.kind==="log"?"只读日志":l.kind==="remote"?"远端会话":l.live===!1?"冷会话":"在线",S.textContent=l.kind==="peer"&&l.picked!==void 0?`…${l.picked.slice(-12)}`:l.title,C.textContent=l.kind==="peer"&&l.picked!==void 0?l.title:l.sub,J.hidden=!(l.kind==="peer"&&l.picked!==void 0)}function P(l,A){const L=ye("div","wb-kv");L.appendChild(ye("span","wb-kv-k")).textContent=l,L.appendChild(ye("span","wb-kv-v")).textContent=A,y.appendChild(L)}function U(){var A,L,Z;y.replaceChildren();const l=t;l!==null&&(l.kind==="session"?(P("会话",l.id),l.team!==void 0&&P("路由",l.team),P("来源","本机会话日志")):l.kind==="log"?(P("会话",l.id),P("来源","本机会话日志(不在网络目录)")):l.kind==="remote"?(P("路由",l.team),((A=l.row)==null?void 0:A.origin)!==void 0&&P("来源",l.row.origin),((L=l.row)==null?void 0:L.via)!==void 0&&P("经",l.row.via),((Z=l.row)==null?void 0:Z.workspace)!==void 0&&P("工作区",l.row.workspace)):(P("对端",l.url??l.sub),l.picked!==void 0&&P("会话",l.picked),P("来源","经本机代理读取对端 /a2a/log")))}function ie(l){return l.kind==="log"?"只读日志 —— 此会话当前不在网络目录(可能已归档或未入网)。":l.kind==="remote"?e.remoteSessionLog!==void 0?"远端会话 —— 经本机代理从对端 /a2a/log 读取最近对话。":"远端节点的对话记录在对端机器上;本页的代理读取面未启用。":l.kind==="peer"?l.url===void 0||e.remoteSessionLog===void 0?`对端主机节点(${l.sub})—— 会话记录存放在对端机器上,本页只读本机会话日志。`:l.picked===void 0?"对端主机 —— 选择一个会话阅读(经本机代理读取对端 /a2a/log)。":"":e.sessionLog===void 0?"本页未接入会话日志读取,只显示从这里发出的消息。":""}function Re(){return f.scrollHeight-f.scrollTop-f.clientHeight<=Po}function $e(l){const A=()=>{f.scrollTop=f.scrollHeight};A(),requestAnimationFrame(A)}function re(l,A){const L=ye("div",`wb-chat-empty${A==="error"?" is-error":""}`);return L.textContent=l,L}function Ce(l,A){const L=l==="user",Z=ye("div",`wb-msg ${L?"wb-msg-user":"wb-msg-assistant"}`),B=ye("span","wb-msg-who");B.textContent=L?"问":"答";const he=ye("div","wb-msg-text");return he.textContent=A,Z.append(B,he),Z}function Q(l){if(p.replaceChildren(),l===void 0){p.appendChild(re("读取中…"));return}if(l.ok!==!0||l.messages===void 0){p.appendChild(re(`无法读取(${l.error??"未知错误"})`,"error"));return}if(l.messages.length===0){p.appendChild(re("暂无对话记录"));return}for(const A of l.messages)p.appendChild(Ce(A.role,A.text))}function oe(l,A){if(p.replaceChildren(),A===void 0){p.appendChild(re("读取中…"));return}if(A.ok!==!0||A.sessions===void 0){p.appendChild(re(`无法读取(${A.error??"未知错误"})`,"error"));return}if(A.sessions.length===0){p.appendChild(re("对端没有可读会话"));return}const L=ye("div","wb-chat-sessions"),Z=e.now();for(const B of A.sessions){const he=ye("button","wb-chat-session");he.type="button",he.appendChild(ye("span","wb-chat-session-id")).textContent=`…${B.id.slice(-12)}`;const ge=[B.mtimeMs!==void 0?cn(B.mtimeMs,Z):"",Ro(B.sizeBytes)].filter(Boolean).join(" · ");he.appendChild(ye("span","wb-chat-session-meta")).textContent=ge,he.title=B.id,he.addEventListener("click",()=>{(t==null?void 0:t.kind)!=="peer"||t.url!==l||(t.picked=B.id,Oe())}),L.appendChild(he)}p.appendChild(L)}function xe(l=!1){const A=t;if(A===null)return;if(_.textContent=ie(A),_.hidden=_.textContent==="",A.kind==="peer"&&A.picked===void 0){if(A.url===void 0||e.remoteSessionLog===void 0){p.replaceChildren();return}const B=A.url,he=`peer:${B}`;if(!l&&c.has(he)){oe(B,c.get(he));return}c.set(he,void 0),oe(B,void 0),e.remoteSessionLog({peer:B}).then(ge=>{c.set(he,ge),(t==null?void 0:t.kind)==="peer"&&t.url===B&&t.picked===void 0&&oe(B,ge)},ge=>{const pe={ok:!1,error:ge instanceof Error?ge.message:String(ge)};c.set(he,pe),(t==null?void 0:t.kind)==="peer"&&t.url===B&&t.picked===void 0&&oe(B,pe)});return}const L=K(A);if(L===void 0){p.replaceChildren(),ze(!0);return}const Z=()=>{const B=r.get(L);B===void 0||l?$e():(f.scrollTop=B,requestAnimationFrame(()=>{t!==null&&K(t)===L&&(f.scrollTop=B)}))};if(!l&&i.has(L)&&i.get(L)!==void 0){Q(i.get(L)),ze(!0),Z();return}l&&r.delete(L),i.set(L,void 0),Q(void 0),ze(!0),we(A).then(B=>{var he;i.set(L,B),(B==null?void 0:B.ok)===!0&&B.messages!==void 0&&((he=e.onTranscript)==null||he.call(e,A,B.messages)),!(t===null||K(t)!==L)&&(Q(B),ze(),Z())},B=>{const he={ok:!1,error:B instanceof Error?B.message:String(B)};i.set(L,he),t!==null&&K(t)===L&&Q(he)})}function le(l){return((n==null?void 0:n.tasks)??[]).filter(A=>A.team===l)}function te(l){return((n==null?void 0:n.tasksDead)??[]).filter(A=>A.team===l)}let Ne="";function je(l){return l.reason==="fire-forget"?{text:"已过期(免回执)",tone:"muted",hint:"免回执消息到 24h TTL 归档,属正常记账"}:{text:"死信",tone:"danger",hint:l.reason!==void 0&&l.reason!=="unspecified"?`${l.reason}:超过 24h TTL 仍未结算,需在台账清账`:"超过 24h TTL 仍未结算,需在台账清账"}}function qe(l,A){const L=l.direction==="inbound";if(l.receiptExpected===!1)return{text:L?"来信 · 无需回执":"已送达 · 无回执",tone:"muted",hint:"免回执消息:不等回执,24h 后自动归档"};const Z=Ft(l,A),B=L?Z?"待本机回执 · 超 1 小时":"待本机回执":Z?"陈旧":"待回执",he=l.status!=="pending"?` · ${l.status}`:"";return{text:`${B}${he}`,tone:Z?"warn":"wait",hint:`${L?"对方等待本机回执;":""}${L?"收到":"派发"}于 ${cn(l.startedAt,A)}`}}function Ke(l,A){const L=`结算于 ${cn(l.resolvedAt,A)}`;if(l.receiptExpected===!1&&l.endedBy==="abandoned")return l.late===!0?{text:"已移出(免回执) · 回执迟到",tone:"warn",hint:`${L};免回执消息本机移出后对方回执才到,结局不改写`}:{text:"已移出(免回执)",tone:"muted",hint:`${L};免回执消息,本机移出台账,不是失败`};if(l.endedBy==="abandoned"||l.endedBy==="cancelled"){const ge=l.endedBy==="cancelled"?"撤回":"放弃";return l.late===!0?{text:`已${ge} · 回执迟到`,tone:"warn",hint:`${L};本机${ge}之后对方回执才到,结局不改写`}:{text:`已${ge}`,tone:"muted",hint:`${L};本机${ge},未等到回执`}}if(l.endedBy==="echo")return{text:"已结算 · 内容回显",tone:"ok",hint:`${L};对方回了内容但未带正式回执,按内容回显结算`};if(l.endedBy===void 0&&l.outcome==="abandoned")return{text:"已放弃(本机或对方)",tone:"muted",hint:`${L};本机放弃或撤回,或对方回执报告放弃`};const Z=l.summary!==void 0&&l.summary!==""?` · ${l.summary}`:"";if(l.outcome==="abandoned")return{text:"已回执 · 对方放弃",tone:"warn",hint:`${L};对方回执报告放弃${Z}`};const B=hn.find(ge=>ge.key===l.outcome),he=(B==null?void 0:B.tone)==="completed"?"ok":(B==null?void 0:B.tone)==="failed"?"danger":(B==null?void 0:B.tone)==="rejected"?"warn":"muted";return l.outcome==="unclassified"?{text:"已回执",tone:"ok",hint:`${L};回执未带结论${Z}`}:{text:`已回执 · ${(B==null?void 0:B.label)??l.outcome}`,tone:he,hint:`${L}${Z}`}}function ct(l,A,L){const Z=te(A).find(ge=>ge.taskId===l);if(Z!==void 0)return je(Z);const B=le(A).find(ge=>ge.taskId===l);if(B!==void 0)return qe(B,L);const he=((n==null?void 0:n.archived)??[]).find(ge=>ge.taskId===l&&ge.team===A);return he!==void 0?Ke(he,L):m.has(l)?{text:"已离开台账",tone:"muted",hint:"已结算、放弃或撤回之一;主机未给出这条的结局(旧主机,或已超出最近 64 条归档)"}:{text:"已派发",tone:"muted",hint:"异步送达;回执到达后进台账"}}function xt(l){const A=e.now(),L=d.get(l)??[],Z=new Set(L.map(ae=>ae.taskId).filter(ae=>ae!==void 0)),B=ae=>ae!==void 0&&ae!==""?ae:"(无摘要)",he=le(l).filter(ae=>!Z.has(ae.taskId)).map(ae=>({at:ae.startedAt,side:ae.direction==="inbound"?"theirs":"mine",text:B(ae.brief),status:qe(ae,A),taskId:ae.taskId})),ge=te(l).filter(ae=>!Z.has(ae.taskId)).map(ae=>{var Te;const Be=ae.direction??g.get(ae.taskId)??(((Te=ae.peer)==null?void 0:Te.startsWith("local:"))===!0?"inbound":void 0);return{at:ae.startedAt,side:Be==="inbound"?"theirs":Be==="outbound"?"mine":"system",text:B(ae.brief),status:je(ae),taskId:ae.taskId}}),pe=L.map(ae=>({at:ae.at,side:"mine",text:ae.text,status:ae.state==="sending"?{text:"发送中…",tone:"muted"}:ae.state==="failed"?{text:`失败:${ae.error??"未知原因"}`,tone:"danger"}:ae.taskId!==void 0?ct(ae.taskId,l,A):{text:"已派发",tone:"muted"},taskId:ae.taskId}));return[...he,...ge,...pe].sort((ae,Be)=>ae.at-Be.at)}function ze(l=!1){const A=t,L=A!==null?ln(A):void 0,Z=L!==void 0?xt(L):[],B=JSON.stringify([L??"",...Z.map(pe=>[pe.taskId??"",pe.side,pe.text,pe.status.text])]);if(!l&&B===Ne)return;Ne=B;const he=Re();if(R.replaceChildren(),Z.length===0)return;const ge=ye("div","wb-chat-divider");ge.textContent="台账往来 · 异步送达,回执进台账",R.appendChild(ge);for(const pe of Z){const ae=pe.side==="mine"?"wb-msg wb-msg-user wb-msg-out":pe.side==="theirs"?"wb-msg wb-msg-assistant wb-msg-in":"wb-msg wb-msg-ledger",Be=ye("div",ae);Be.appendChild(ye("span","wb-msg-who")).textContent=pe.side==="mine"?"我":pe.side==="theirs"?"对方":"台账记录",Be.appendChild(ye("div","wb-msg-text")).textContent=pe.text;const Te=ye("div","wb-msg-meta");pe.taskId!==void 0&&(Te.appendChild(ye("span","wb-msg-task")).textContent=pe.taskId);const Ye=ye("span",`wb-msg-status is-${pe.status.tone}`);Ye.textContent=pe.status.text,pe.status.hint!==void 0&&(Ye.title=pe.status.hint),Te.appendChild(Ye),Be.appendChild(Te),R.appendChild(Be)}he&&$e()}function Ve(l){const A=l.text.value.length;l.note.classList.remove("is-error"),l.note.textContent=A>Lt*.9?`${A}/${Lt}`:"Enter 发送 · Shift+Enter 换行",A>Lt&&l.note.classList.add("is-error"),l.submit.disabled=l.text.value.trim()===""||A>Lt}function bt(l){l.style.height="auto",l.style.height=`${Math.min(l.scrollHeight,160)}px`}function Nt(l){const A=ye("form","wb-dispatch-form wb-chat-composer"),L=document.createElement("textarea");L.className="wb-dispatch-text",L.rows=1,L.placeholder="发一句话任务给这个节点…",L.setAttribute("aria-label","发送消息");const Z=ye("button","wb-dispatch-submit");Z.type="submit",Z.textContent="发送",Z.setAttribute("aria-label","发送");const B=ye("div","wb-dispatch-note");B.setAttribute("aria-live","polite");const he=ye("div","wb-chat-composer-row");he.append(L,Z),A.append(he,B);const ge={team:l,form:A,text:L,note:B,submit:Z};return L.addEventListener("input",()=>{bt(L),Ve(ge)}),L.addEventListener("keydown",pe=>{pe.key!=="Enter"||pe.shiftKey||pe.ctrlKey||pe.altKey||pe.metaKey||pe.isComposing||(pe.preventDefault(),A.requestSubmit())}),A.addEventListener("submit",pe=>{pe.preventDefault();const ae=L.value.trim();if(ae===""||ae.length>Lt||e.dispatchQuick===void 0)return;const Be={text:ae,at:e.now(),state:"sending"},Te=d.get(l)??[];Te.push(Be),d.set(l,Te),L.value="",bt(L),Ve(ge),L.focus({preventScroll:!0}),ze(),$e(),e.dispatchQuick(l,ae).then(Ye=>Ye,Ye=>({ok:!1,error:Ye instanceof Error?Ye.message:String(Ye)})).then(Ye=>{Ye.ok===!0?(Be.state="sent",Be.taskId=Ye.task_id,B.classList.remove("is-error"),B.textContent=`已派发${Ye.task_id!==void 0?` · ${Ye.task_id}`:""} · 回执将入台账`):(Be.state="failed",Be.error=Ye.error??"未知原因",L.value===""&&(L.value=ae,bt(L)),B.classList.add("is-error"),B.textContent=`失败:${Be.error}`,Z.disabled=L.value.trim()===""),t!==null&&ln(t)===l&&ze()})}),Ve(ge),ge}function Le(){const l=t;if(j.replaceChildren(),l===null)return;const A=ln(l);if(A!==void 0&&e.dispatchQuick!==void 0){let Z=u.get(A);Z===void 0&&(Z=Nt(A),u.set(A,Z)),j.appendChild(Z.form),s&&(s=!1,Z.text.focus({preventScroll:!0}));return}s=!1;const L=ye("div","wb-chat-readonly");L.textContent=l.kind==="log"?"只读日志:此会话不在网络目录,不能从这里派发。":l.kind==="peer"?"对端主机:选择会话阅读;要派发请打开它的远端会话节点。":"此节点没有可派发的路由。",j.appendChild(L)}function Oe(){Ae(),U(),xe(),Le()}function et(l){const A=t!==null&&In(t)===In(l);t=l,A||(s=!0),Oe(),E.classList.add("is-open")}function lt(){t=null,E.classList.remove("is-open")}function Mt(l){n=l;for(const L of(l==null?void 0:l.tasks)??[])m.add(L.taskId),g.set(L.taskId,L.direction==="inbound"?"inbound":"outbound");const A=t;if(A!==null){if(A.kind==="session"&&l!==null){const L=(l.sessions??[]).find(Z=>Z.id===A.id);L!==void 0&&(A.title=it(L),A.live=L.live)}Ae(),ze()}}return{root:E,open:et,close:lt,isOpen:()=>t!==null,update:Mt}}function Ho(e,t){const n=document.createElement("form");n.className="wb-cli-home";const s=document.createElement("button");s.type="button",s.textContent="指定 home",s.className="wb-member-submit";const o=document.createElement("select");o.className="wb-member-pick",o.setAttribute("aria-label","选择 CLI home 主机"),o.hidden=!0;const i=document.createElement("button");i.type="submit",i.textContent="确认 home",i.className="wb-member-submit",i.hidden=!0,i.disabled=!0;const r=document.createElement("div");r.className="wb-hint",r.setAttribute("role","status"),r.setAttribute("aria-live","polite"),r.textContent="home 未确定;点击指定 home 现读已链接主机名片。";let c=!1;return s.addEventListener("click",async()=>{var d;if(!c){c=!0,s.disabled=!0,i.disabled=!0,r.textContent="正在现读主机名片…";try{const m=await e();if(!m.ok)throw Error(m.error??"候选读取失败");if(m.held){s.hidden=!0,o.hidden=!0,i.hidden=!0,o.replaceChildren(),r.textContent="本机持有,无需指定";return}o.replaceChildren(new Option("请选择 home 主机…","",!0,!0));for(const u of m.homes??[])o.appendChild(new Option(u,u));o.hidden=!1,i.hidden=!1,s.textContent="刷新候选";const g=((d=m.homes)==null?void 0:d.length)??0;r.textContent=(m.unreadable?"有 "+m.unreadable+" 台对端名片读不到;":"")+(g>1?"多台已链接主机列出该句柄,请显式选择。":g===1?"找到 1 台已链接主机;仍需你显式确认。":"当前没有已链接且列出该句柄的可选主机。")}catch(m){r.textContent="读取失败:"+String(m),o.hidden=!0,i.hidden=!0}finally{c=!1,s.disabled=!1}}}),o.addEventListener("change",()=>{i.disabled=c||o.value===""}),n.addEventListener("submit",async d=>{if(d.preventDefault(),!(c||o.value==="")){c=!0,s.disabled=!0,o.disabled=!0,i.disabled=!0;try{r.textContent=await t(o.value)?"home 已指定:"+o.value:"指定失败:主机可能已离线或不再列出该句柄,请刷新后重选。"}catch(m){r.textContent="指定失败:"+String(m)}finally{c=!1,s.disabled=!1,o.disabled=!1,i.disabled=o.value===""}}}),n.append(s,o,i,r),n}const io="dsh-a2a.rail.tab";function jo(){try{return window.localStorage.getItem(io)}catch{return null}}function Bo(e){try{window.localStorage.setItem(io,e)}catch{}}function Yo(e,t={}){var ne;const n=document.createElement("aside");n.className="wb-rail";const s=document.createElement("div");s.className="wb-tabs",s.setAttribute("role","tablist"),s.setAttribute("aria-label","工作台侧栏");const o=document.createElement("div");o.className="wb-tabpanels",n.append(s,o);const i=new Map,r=new Map,c=new Map,d=new Set;for(const S of e){const C=document.createElement("button");C.type="button",C.className="wb-tab",C.id=`wb-tab-${S.key}`,C.dataset.sec=S.key,C.setAttribute("role","tab"),C.setAttribute("aria-controls",`wb-panel-${S.key}`);const $=document.createElement("span");$.className="wb-tab-label",$.textContent=S.label;const k=document.createElement("span");k.className="wb-tab-badge",C.append($,k),C.addEventListener("click",()=>M(S.key)),s.appendChild(C);const V=document.createElement("section");V.className="wb-tabpanel",V.id=`wb-panel-${S.key}`,V.dataset.sec=S.key,V.setAttribute("role","tabpanel"),V.setAttribute("aria-labelledby",C.id),V.tabIndex=-1,o.appendChild(V),i.set(S.key,C),r.set(S.key,V),c.set(S.key,k)}const m=()=>e.map(S=>S.key).filter(S=>!d.has(S)),g=jo();let u=((ne=e.find(S=>S.key===g))==null?void 0:ne.key)??e[0].key;s.addEventListener("keydown",S=>{const C=m(),$=C.indexOf(u);let k;S.key==="ArrowRight"?k=C[($+1)%C.length]:S.key==="ArrowLeft"?k=C[($-1+C.length)%C.length]:S.key==="Home"?k=C[0]:S.key==="End"&&(k=C[C.length-1]),k!==void 0&&(S.preventDefault(),M(k,{focus:!0}))});function E(){for(const S of e){const C=S.key===u,$=i.get(S.key);$.setAttribute("aria-selected",String(C)),$.tabIndex=C?0:-1,$.classList.toggle("is-active",C),$.hidden=d.has(S.key),r.get(S.key).hidden=!C}}function M(S,C={}){var k;if(d.has(S))return;const $=S!==u;u=S,Bo(S),E(),C.focus===!0&&i.get(S).focus(),$&&((k=t.onChange)==null||k.call(t,S))}function J(S,C,$){const k=c.get(S);k.textContent=C,k.className=`wb-tab-badge${$!==void 0?` is-${$}`:""}`}function fe(S,C){C?d.add(S):d.delete(S),d.has(u)&&(u=m()[0]??e[0].key),E()}return E(),{root:n,panel:S=>r.get(S),select:M,selected:()=>u,setBadge:J,setHidden:fe}}const Wo=[{key:"all",label:"全部",hint:"不过滤:目录全部条目"},{key:"local",label:"本机",hint:"本机会话节点(现场 + 冷启动)"},{key:"canvas",label:"队",hint:"画布队(人类手工分配的组队)"},{key:"remote",label:"远端",hint:"远端会话队(来自对端卡片)"},{key:"logs",label:"日志",hint:"本机日志:不在网络目录里的会话档案"}];function nt(e,t){const n=document.createElement(e);return t!==void 0&&(n.className=t),n}function Yt(...e){const t=[];for(const n of e)n!==void 0&&(typeof n=="string"?t.push(n):t.push(...n));return t.join(" ").toLowerCase()}function Ko(e,t){const n=Math.max(0,Math.round((t-e)/1e3));if(n<60)return"刚刚";const s=Math.round(n/60);if(s<60)return`${s} 分钟前`;const o=Math.round(s/60);return o<48?`${o} 小时前`:`${Math.round(o/24)} 天前`}function Tn(e){return e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${Math.round(e/1e3)} KB`:`${e} B`}function Xo(e,t){const n=new Date(t);n.setHours(0,0,0,0);const s=n.getTime();return e>=s?"今天":e>=s-864e5?"昨天":"更早"}function Uo(e,t){var $,k,V;e.replaceChildren();const{state:n,search:s,filter:o}=t,i=s!==""||o!=="all",r=(y,f)=>(o==="all"||o===y)&&(s===""||f.includes(s)),c=(y,f,_)=>{const p=nt("div","wb-dir-group"),R=nt("div","wb-dir-head");return R.appendChild(nt("span","wb-dir-head-label")).textContent=`${y} · ${f}`,_!==void 0&&(R.appendChild(nt("span","wb-dir-head-note")).textContent=_),p.appendChild(R),e.appendChild(p),p},d=(y,f,_,p,R,j,K)=>{const we=nt("button","wb-item");we.type="button",we.appendChild(nt("span",`wb-dot ${f}`)).textContent=_;const Ae=nt("span","wb-item-text");return Ae.appendChild(nt("span","wb-item-label")).textContent=p,R!==""&&(Ae.appendChild(nt("span","wb-item-sub")).textContent=R),we.appendChild(Ae),K!==void 0&&(we.title=K),we.addEventListener("click",j),y.appendChild(we),we},m=(y,f)=>{if(f===void 0||f.length===0)return;const _=nt("span","wb-item-badge"),p=f.map(R=>R.slice(R.lastIndexOf("/")+1));_.textContent=`▤ ${p.slice(0,2).join(" ")}${p.length>2?` +${p.length-2}`:""}`,_.title=`画布队:${f.join(" · ")}`,y.appendChild(_)},g=(n.sessions??[]).filter(y=>r("local",Yt(it(y),y.team,y.label,y.id,y.canvasTeams))),u=g.filter(y=>y.live===!0),E=g.filter(y=>y.live!==!0);if(g.length>0){const y=c("本机会话",g.length,E.length>0?`${u.length} 在线 · ${E.length} 冷`:void 0);for(const f of[...u,...E]){const _=f.live===!0,p=f.nodePromptMissing===!0,R=d(y,_?"wb-dot-live":"wb-dot-cold",_?"●":"○",it(f),_?f.team:`冷 · ${f.team}`,()=>t.openChat({kind:"session",id:f.id,title:it(f),sub:f.team,team:f.team,live:f.live}),f.id);if(p){R.classList.add("is-node-prompt-missing");const j=nt("span","wb-node-warning");j.textContent="本会话的组装不含节点段",($=R.querySelector(".wb-item-text"))==null||$.appendChild(j)}m(R,f.canvasTeams)}}const M=(((k=n.canvas)==null?void 0:k.teams)??[]).filter(y=>r("canvas",Yt(y.name,y.team,y.members.map(f=>f.team??f.id))));if(M.length>0){const y=c("画布队",M.length);for(const f of M){const _=f.members.filter(p=>p.remote===!0).length;d(y,"wb-dot-canvas","▤",f.name,`${f.members.length} 成员${_>0?` · ${_} 远端`:""}`,()=>t.selectCanvas(f),f.team)}}const J=(n.remote??[]).filter(y=>r("remote",Yt(it(y),y.team,y.origin,y.workspace,y.canvasTeams)));if(J.length>0){const y=c("远端节点",J.length);for(const f of J){const _=d(y,"wb-dot-remote","◇",it(f),f.origin!==void 0&&f.origin!==""?f.origin:f.team,()=>t.openChat({kind:"remote",team:f.team,title:it(f),sub:f.team,row:f}),f.team);if(f.unreachable===!0){_.classList.add("is-unreachable");const p=nt("span","wb-unreachable-note");p.textContent="对端未应答(显示的是最后已知状态)",(V=_.querySelector(".wb-item-text"))==null||V.appendChild(p)}m(_,f.canvasTeams)}}const fe=new Set((n.sessions??[]).map(y=>y.id)),ne=t.logs.filter(y=>!fe.has(y.id)&&r("logs",Yt(y.id,t.titles.get(y.id))));if(ne.length>0){const y=t.logTotal!==void 0&&t.logTotal>t.logs.length,f=c("本机日志",ne.length,y?`显示 ${t.logs.length} / 共 ${t.logTotal}`:void 0);let _="";for(const p of[...ne].sort((R,j)=>j.mtimeMs-R.mtimeMs)){const R=Xo(p.mtimeMs,t.now);R!==_&&(_=R,f.appendChild(nt("div","wb-dir-day")).textContent=R);const j=t.titles.get(p.id),K=d(f,"wb-dot-log","❐",j??`会话 …${p.id.slice(-12)}`,`${Ko(p.mtimeMs,t.now)} · ${Tn(p.sizeBytes)}`,()=>t.openChat({kind:"log",id:p.id,title:j??`…${p.id.slice(-12)}`,sub:"本机日志"}),`${p.id}
3
+ ${new Date(p.mtimeMs).toLocaleString()} · ${Tn(p.sizeBytes)}`);j===void 0&&K.classList.add("is-untitled")}}g.length+M.length+J.length+ne.length===0&&(e.appendChild(nt("div","wb-dir-empty")).textContent=i?"无匹配条目":"目录为空");const S=n.sessions??[];return`${S.filter(y=>y.live===!0).length}/${S.length}`}const ro="dsh-a2a.logtitles",qo=300,_n=60;function zo(){try{const e=window.localStorage.getItem(ro),t=e===null?null:JSON.parse(e);if(t!==null&&typeof t=="object")return new Map(Object.entries(t).filter(n=>typeof n[1]=="string"))}catch{}return new Map}function Jo(e,t,n){const s=n.find(r=>r.role==="user"&&r.text.trim()!=="")??n.find(r=>r.text.trim()!=="");if(s===void 0)return!1;const o=s.text.trim().split(`
4
+ `)[0].trim(),i=o.length>_n?`${o.slice(0,_n-1)}…`:o;if(e.get(t)===i)return!1;for(e.delete(t),e.set(t,i);e.size>qo;)e.delete(e.keys().next().value);try{window.localStorage.setItem(ro,JSON.stringify(Object.fromEntries(e)))}catch{}return!0}function ut(e){return e.name??e.team??e.label??""}function It(e,t){const n=Math.max(0,Math.round((t-e)/1e3));if(n<90)return`${n}s`;const s=Math.round(n/60);if(s<90)return`${s}m`;const o=Math.round(s/60);return o<48?`${o}h`:`${Math.round(o/24)}d`}function T(e,t){const n=document.createElement(e);return t!==void 0&&(n.className=t),n}function On(e,t,n){const s=T("div","wb-kv");s.appendChild(T("span","wb-kv-k")).textContent=t,s.appendChild(T("span","wb-kv-v")).textContent=n,e.appendChild(s)}function Vo(e){try{return new URL(e).host}catch{return e}}const Go=[{key:"directory",label:"目录"},{key:"hosts",label:"主机"},{key:"ledger",label:"台账"},{key:"activity",label:"活动"},{key:"plan",label:"规划"}];function Qo(e){const t=e.board;let n="data",s,o=null,i="",r="all",c=null;const d=new Map;let m=null,g=[],u;const E=zo();let M=!1;const J=T("div","wb");J.setAttribute("data-testid","wb-root");const fe=T("header","wb-header"),ne=T("button","wb-icon-btn wb-menu");ne.type="button",ne.dataset.testid="wb-menu",ne.setAttribute("aria-label","打开目录抽屉"),ne.textContent="☰",ne.addEventListener("click",()=>J.classList.toggle("is-rail-open")),fe.appendChild(ne);const S=T("div","wb-title");S.appendChild(T("span","wb-title-main")).textContent="A2A 工作台";const C=T("span","wb-badge");S.appendChild(C),fe.appendChild(S);const $=T("button","wb-icon-btn wb-owner-btn");$.type="button",$.dataset.testid="wb-owner-btn",$.setAttribute("aria-label","主人设置(三键面板)"),$.title="主人设置:入网白名单 / 可见性 / 发布",$.textContent="⚙",$.addEventListener("click",()=>{l=!l,L()}),fe.appendChild($);const k=T("div","wb-header-stats");fe.appendChild(k);const V=T("button","wb-icon-btn wb-rail-toggle");V.type="button",V.dataset.testid="wb-rail-toggle",V.setAttribute("aria-label","折叠/展开侧栏"),V.textContent="◧",V.addEventListener("click",()=>{J.classList.toggle("is-rail-collapsed"),n==="canvas"&&t!==void 0&&t.activate()}),fe.appendChild(V);const y=T("div","wb-body"),f=Yo(Go),_=f.root,p=T("div","wb-directory"),R=T("div","wb-dir-tools"),j=document.createElement("input");j.className="wb-search",j.type="search",j.placeholder="搜索:名称 / 队 / 会话 / 节点",j.setAttribute("aria-label","统一搜索目录"),j.addEventListener("input",()=>{i=j.value.trim().toLowerCase(),Ke()}),R.appendChild(j);const K=T("div","wb-filters");for(const x of Wo){const w=T("button","wb-filter");w.type="button",w.textContent=x.label,w.dataset.filter=x.key,w.title=x.hint,w.addEventListener("click",()=>{r=x.key,Ke()}),K.appendChild(w)}R.appendChild(K),f.panel("directory").appendChild(R),f.panel("directory").appendChild(p);const we=T("section","wb-pane"),Ae=T("section","wb-pane"),P=T("section","wb-pane"),U=T("section","wb-pane"),ie=[["ledger",we],["activity",Ae],["plan",P],["hosts",U]],Re=T("main","wb-stage"),$e=T("section","wb-stage-board");t!==void 0&&($e.appendChild(t.root),Re.appendChild($e));const re=T("div","wb-data"),Ce=T("aside","wb-inspector"),Q=T("div","wb-inspector-head");Q.appendChild(T("span","wb-inspector-title")).textContent="详情";const oe=T("button","wb-icon-btn");oe.type="button",oe.setAttribute("aria-label","关闭详情"),oe.textContent="✕",oe.addEventListener("click",()=>qe(void 0)),Q.appendChild(oe),Ce.appendChild(Q);const xe=T("div","wb-inspector-body");Ce.appendChild(xe);const le=Do({now:e.now,sessionLog:e.sessionLog,remoteSessionLog:e.remoteSessionLog,dispatchQuick:e.dispatchQuick,onTranscript:(x,w)=>{x.kind!=="log"&&x.kind!=="session"||Jo(E,x.id,w)&&Ke()}});Re.appendChild(re),y.appendChild(_),y.appendChild(Re),y.appendChild(Ce),y.appendChild(le.root),J.appendChild(fe),J.appendChild(y);function te(x){for(const[w,D]of ie){const N=x==="canvas"?f.panel(w):re;f.setHidden(w,x!=="canvas"),D.parentElement!==N&&N.appendChild(D)}}function Ne(){const w=t!==void 0&&t.available()?"canvas":"data";J.dataset.mode=w,$e.classList.toggle("is-hidden",w!=="canvas"),re.classList.toggle("is-hidden",w==="canvas"),te(w),w!==n&&(t!==void 0&&(w==="canvas"?t.activate():t.deactivate()),n=w)}function je(x){for(;x.firstChild!==null;)x.removeChild(x.firstChild);return x}function qe(x){s=x,((x==null?void 0:x.kind)!=="canvas"||(c==null?void 0:c.name)!==x.row.name)&&(c=null),(x==null?void 0:x.kind)!=="canvas"&&(m=null),B(),J.classList.remove("is-rail-open")}function Ke(){for(let w=0;w<K.children.length;w++){const D=K.children[w];D.classList.toggle("is-active",D.dataset.filter===r)}if(o===null){p.replaceChildren(T("div","wb-muted")),p.firstElementChild.textContent="载入中…",f.setBadge("directory","");return}const x=Uo(p,{state:o,logs:g,logTotal:u,titles:E,search:i,filter:r,now:e.now(),openChat:pe,selectCanvas:w=>qe({kind:"canvas",row:w})});f.setBadge("directory",x)}function ct(){const x=je(U);if(o===null){f.setBadge("hosts","");return}const w=e.now(),D=[...o.peers??[]].sort((N,I)=>{const H=X=>X.link==="linked"?0:1;return H(N)-H(I)||(I.score??0)-(N.score??0)});if(f.setBadge("hosts",D.length>0?`${D.filter(N=>N.link==="linked").length}/${D.length}`:""),D.length===0){x.appendChild(T("div","wb-nodelist-empty")).textContent="暂无 tracked peer——配 peers 种子或等 referral 到达";return}for(const N of D){const I=Vo(N.url),H=T("div","wb-host-row"),X=T("span","wb-host-lamp"),se=N.lastContactAt,ee=se===void 0?void 0:w-se,ve=N.lastContactOk;ee===void 0?X.classList.add("is-unknown"):ve===!1&&ee<=5*6e4?X.classList.add("is-fail"):ee<=6e4?X.classList.add("is-live"):ee<=5*6e4?X.classList.add("is-warm"):X.classList.add("is-cold"),X.title=se===void 0?"尚无接触记录":`最近接触 ${It(se,w)}${ve===!1?"(失败)":ve===!0?"(成功)":""}`,H.appendChild(X),H.appendChild(T("span","wb-host-name")).textContent=N.name!==void 0&&N.name!==""?N.name:I;const De=T("span",`wb-host-badge ${N.link==="linked"?"is-linked":"is-stranger"}`);De.textContent=`${N.link==="linked"?"linked":"stranger"} ${N.verified===!0?"✓":"✗"}`,De.title=N.link==="linked"?`已建立运营者链接(互种或 canvas 引用);${N.verified===!0?"验签有效":"尚未持有验签通过的卡片"}`:"无运营者关系(验签 ≠ 关系)",H.appendChild(De),N.version!==void 0&&N.version!==""&&(H.appendChild(T("span","wb-host-ver")).textContent=N.version),H.appendChild(T("span","wb-host-url mono")).textContent=I,H.setAttribute("role","button"),H.tabIndex=0,H.title=`${N.url} · score ${String(N.score??0)}`;const q=()=>{pe({kind:"peer",id:`peer-${I}`,title:N.name!==void 0&&N.name!==""?N.name:I,sub:I,url:N.url})};H.addEventListener("click",q),H.addEventListener("keydown",He=>{(He.key==="Enter"||He.key===" ")&&(He.preventDefault(),q())}),x.appendChild(H)}}function xt(){if(o===null){we.replaceChildren(),f.setBadge("ledger","");return}const x=o,w=N=>{const I=(x.sessions??[]).find(X=>X.team===N);if(I!==void 0)return{kind:"session",id:I.id,title:ut(I),sub:I.team,team:I.team,live:I.live};const H=(x.remote??[]).find(X=>X.team===N);if(H!==void 0)return{kind:"remote",team:H.team,title:ut(H),sub:H.team,row:H}},D=Fo(we,{state:x,now:e.now(),abandon:(N,I)=>e.abandon(N,I),canOpenTeam:N=>w(N)!==void 0,openTeam:N=>{const I=w(N);return I===void 0?!1:(pe(I),!0)}});f.setBadge("ledger",D.badge,D.tone)}function ze(x,w,D){qt(x.appendChild(T("span","wb-row-team")),w,D)}function Ve(){var X;const x=je(Ae);if(o===null)return;const w=e.now(),D=o.inFlight??[];f.setBadge("activity",D.length>0?String(D.length):"",D.length>0?"accent":void 0);const N=T("div","wb-pane-head");if(N.textContent=`在途 ${D.length} · 活动 ${((X=o.activity)==null?void 0:X.length)??0}`,x.appendChild(N),D.length>0){const se=T("div","wb-pane-subhead");se.textContent="在途路由",x.appendChild(se);for(const ee of D){const ve=T("div","wb-row");ve.appendChild(T("span","wb-row-dir")).textContent="→",ze(ve,o,ee.team),ve.appendChild(T("span","wb-row-meta")).textContent=`${ee.peer} · ${It(ee.startedAt,w)}`,x.appendChild(ve)}}const I=T("div","wb-pane-subhead");I.textContent="活动环(最新在前)",x.appendChild(I);const H=o.activity??[];if(H.length===0){x.appendChild(T("div","wb-muted")).textContent="暂无活动";return}for(const se of H){const ee=T("div","wb-row"),ve=T("span","wb-row-dir");ve.textContent=se.dir==="fwd"?"⇢":se.dir==="out"?"→":"←",se.ok!==!0&&ve.classList.add("is-fail"),ee.appendChild(ve),ze(ee,o,se.team),ee.appendChild(T("span","wb-row-meta")).textContent=`${se.peer} · ${It(se.ts,w)}`,ee.appendChild(T("span","wb-row-badges")).textContent=se.ok===!0?"":"失败",x.appendChild(ee)}}function bt(x){var X,se;const w=e.now(),D=[],N=pn(x,w);N.deadLetters.length>0&&D.push({severity:"high",text:`清账 ${N.deadLetters.length} 条死信(目标早已离开或死信化,无回执可期)`});for(const ee of N.stale){const ve=ee.brief===void 0||ee.brief===""?"":`「${ee.brief}」`;D.push({severity:"normal",text:`催办 ${ee.taskId} → ${To(x,ee.team)}(等待 ${It(ee.startedAt,w)} 无回执)${ve}`,brief:`a2a_route 催收回执:task ${ee.taskId}(team ${ee.team})${ve===""?"":`原任务「${ee.brief}」`}已派发 ${It(ee.startedAt,w)},请回报结果或回执。`})}const I=(x.sessions??[]).filter(ee=>ee.live!==!0).map(ee=>ut(ee));if(I.length>0){const ee=I.slice(0,3).join("、")+(I.length>3?` 等 ${I.length} 个`:"");D.push({severity:"normal",text:`冷会话 ${I.length} 个(${ee}):在 GUI 打开即可唤醒加入面`})}for(const ee of((X=x.reconcile)==null?void 0:X.rows)??[])if(ee.needsRepair===!0){const ve=(x.sessions??[]).find(q=>q.id===ee.id),De=ve!==void 0?`${ut(ve)}(${ee.id})`:ee.id;D.push({severity:"high",text:`reconcile 需修复:${De} ${ee.error}`})}const H=((se=x.idempotency)==null?void 0:se.conflicts)??0;return H>0&&D.push({severity:"high",text:`幂等冲突 ${H} 例:人工核查 UNIQUE-ization 流量`}),D}function Nt(){const x=je(P);if(o===null)return;const w=bt(o);f.setBadge("plan",w.length>0?String(w.length):"",w.some(I=>I.severity==="high")?"danger":void 0);const D=T("div","wb-pane-head");D.textContent=w.length===0?"网络健康——无待办行动":`待办行动 ${w.length}`,x.appendChild(D);for(const I of w){const H=T("div",`wb-action${I.severity==="high"?" wb-action-high":""}`);if(H.appendChild(T("span","wb-action-text")).textContent=I.text,I.brief!==void 0){const X=T("button","wb-btn");X.type="button",X.textContent="复制催办",X.addEventListener("click",()=>e.copy(I.brief??"")),H.appendChild(X)}x.appendChild(H)}const N=T("div","wb-pane-subhead");N.textContent="派单起草(复制后在任一会话粘贴执行)",x.appendChild(N),x.appendChild(Le)}const Le=T("div","wb-draft"),Oe=document.createElement("select");Oe.className="wb-select";const et=document.createElement("textarea");et.className="wb-textarea",et.rows=3,et.placeholder="任务正文:目标、验收、期限…";const lt=T("button","wb-btn wb-btn-primary");lt.type="button",lt.textContent="复制任务书",lt.addEventListener("click",()=>{const x=Oe.value,w=et.value.trim();x===""||w===""||e.copy(`a2a_route 派单 → ${x}:
5
+ ${w}`)}),Le.appendChild(Oe),Le.appendChild(et),Le.appendChild(lt);function Mt(){var I;const x=Oe.value;je(Oe);const w=document.createElement("option");if(w.value="",w.textContent="选择目标队…",Oe.appendChild(w),o===null)return;const D=o,N=new Map;for(const H of D.sessions??[])H.team!==void 0&&H.team!==""&&N.set(H.team,`${ut(H)}(会话队)`);for(const H of((I=D.canvas)==null?void 0:I.teams)??[])N.set(H.team,`${H.name}(canvas 队)`);for(const H of D.remote??[])N.set(H.team,`${ut(H)}(远端)`);for(const[H,X]of N){const se=document.createElement("option");se.value=H,se.textContent=`${X} · ${H}`,Oe.appendChild(se)}x!==""&&N.has(x)&&(Oe.value=x)}let l=!1,A=null;function L(){var x;B(),l&&((x=e.ownerSettingsRead)==null||x.call(e).then(w=>{if(l){if(w.ok!==!0){A=null,B();return}A={teamJoinAllowlist:w.teamJoinAllowlist??[],teamScopeInfo:w.teamScopeInfo!==!1,announce:w.announce===!0,announceRestartPending:w.announceRestartPending===!0},B()}}))}function Z(x){const w=T("div","wb-card"),D=T("div","wb-inspector-head");D.appendChild(T("span","wb-inspector-title")).textContent="主人设置";const N=T("button","wb-icon-btn");if(N.type="button",N.setAttribute("aria-label","关闭主人设置"),N.textContent="✕",N.addEventListener("click",()=>{l=!1,B()}),D.appendChild(N),w.appendChild(D),e.ownerSettingsRead===void 0){const be=T("div","wb-hint");be.textContent="此宿主未提供主人设置面(旧版本)——升级后可用。",w.appendChild(be),x.appendChild(w);return}if(A===null){const be=T("div","wb-hint");be.textContent="读取中…",w.appendChild(be),x.appendChild(w);return}const I=A,H=T("div","wb-side-head");H.textContent="入网白名单(精确团队名)",w.appendChild(H);for(const be of I.teamJoinAllowlist){const Xe=T("div","wb-member-row");Xe.appendChild(T("span","wb-member-label")).textContent=be;const ft=T("button","wb-member-remove");ft.type="button",ft.textContent="移除",ft.addEventListener("click",()=>{var St;(St=e.ownerSettingsWrite)==null||St.call(e,{teamJoinAllowlist:I.teamJoinAllowlist.filter(Dt=>Dt!==be)}).then(()=>L())}),Xe.appendChild(ft),w.appendChild(Xe)}const X=T("div","wb-member-add"),se=T("input","wb-member-manual");se.type="text",se.placeholder="dsh/团队名(精确,无通配)",se.setAttribute("aria-label","新增白名单团队名");const ee=T("button","wb-member-submit");ee.type="button",ee.textContent="加入";const ve=()=>{var Xe;const be=se.value.trim();be===""||I.teamJoinAllowlist.includes(be)||(Xe=e.ownerSettingsWrite)==null||Xe.call(e,{teamJoinAllowlist:[...I.teamJoinAllowlist,be]}).then(()=>L())};ee.addEventListener("click",ve),se.addEventListener("keydown",be=>{be.key==="Enter"&&ve()}),X.append(se,ee),w.appendChild(X);const De=T("div","wb-side-head");De.textContent="目录可见性(teamScopeInfo)",w.appendChild(De);const q=T("div","wb-member-row"),He=T("button","wb-member-submit");He.type="button",He.textContent=I.teamScopeInfo?"已开:仅显示已加入团队(点击放开为全舰队可见)":"已关:全舰队可见(点击收窄为已加入团队)",He.addEventListener("click",()=>{var be;(be=e.ownerSettingsWrite)==null||be.call(e,{teamScopeInfo:!I.teamScopeInfo}).then(()=>L())}),q.appendChild(He),w.appendChild(q);const Ge=T("div","wb-hint");Ge.textContent="精度说明:teamScopeInfo 只管「目录显示范围」。关上它不会解锁任何通信准入——会话入网(S3)与目标级路由(F5)规则原样生效。",w.appendChild(Ge);const Ze=T("div","wb-side-head");Ze.textContent="节点发布(announce)",w.appendChild(Ze);const dt=T("div","wb-member-row"),Je=T("button","wb-member-submit");if(Je.type="button",Je.textContent=I.announce?"已开:本节点发布签名卡(点击关闭)":"已关:本节点不发布(点击开启——重启宿主后生效)",Je.addEventListener("click",()=>{var be;(be=e.ownerSettingsWrite)==null||be.call(e,{announce:!I.announce}).then(()=>L())}),dt.appendChild(Je),w.appendChild(dt),I.announceRestartPending){const be=T("div","wb-hint");be.textContent="有 announce 变更待生效:重启宿主后按新配置发布。",w.appendChild(be)}x.appendChild(w)}function B(){var N;const x=je(xe);if(l){Ce.classList.add("is-open"),Z(x);return}if(Ce.classList.toggle("is-open",s!==void 0),s===void 0)return;const w=s,D=T("div","wb-card");if(w.kind==="canvas"){const I=(((N=o==null?void 0:o.canvas)==null?void 0:N.teams)??[]).find(q=>q.name===w.row.name)??w.row;On(D,"Canvas 队",I.name),On(D,"路由队",I.team);const H=T("div","wb-side-head");if(H.textContent=`成员 · ${I.members.length}`,D.appendChild(H),I.members.length===0){const q=T("div","wb-muted");q.textContent="空队——用下方表单添加成员",D.appendChild(q)}for(const q of I.members){const He=T("div","wb-member-row"),Ge=q.cliHeld===!0?"本机持有,无需指定":q.remote===!0?q.cliHome===null?"◇ 远端 · home 未确定":"◇ 远端":q.live===!0?"● 现场":q.joined===!0?"○ 冷启":"· 未入网";He.appendChild(T("span","wb-member-dot")).textContent=Ge;const Ze=T("span","wb-member-label"),dt=((o==null?void 0:o.sessions)??[]).find(be=>be.id===q.id);Ze.textContent=q.remote===!0?q.id:dt!==void 0?ut(dt):q.id,Ze.title=q.cliHeld===!0?"本机持有,无需指定":q.cliHome!==void 0?q.cliHome??"home 未确定":q.team??q.id,He.appendChild(Ze);const Je=T("button","wb-member-remove");if(Je.type="button",Je.textContent="移除",Je.addEventListener("click",async()=>{Je.disabled=!0,await e.canvasOp("remove-member",I.name,q.id)||(Je.disabled=!1)}),He.appendChild(Je),D.appendChild(He),q.cliHomeCleared===!0){const be=T("div","wb-hint");be.textContent="本机落户已清除旧 home 钉;户口离线后也不会恢复旧钉。",D.appendChild(be)}if(q.cliHome===null&&q.cliHeld!==!0&&e.cliHomeOptions!==void 0){const be=JSON.stringify([I.name,q.id]);let Xe=d.get(be);Xe===void 0&&(Xe=Ho(()=>e.cliHomeOptions(I.name,q.id),ft=>e.canvasOp("add-member",I.name,q.id,ft)),d.set(be,Xe)),D.appendChild(Xe)}}if((c==null?void 0:c.name)!==I.name){const q=T("form","wb-member-add"),He=document.createElement("select");He.className="wb-member-pick",He.setAttribute("aria-label","选择成员");const Ge=document.createElement("input");Ge.type="text",Ge.className="wb-member-manual",Ge.placeholder="或输入远端句柄 zone/id8",Ge.setAttribute("aria-label","远端成员句柄");const Ze=T("button","wb-member-submit");Ze.type="submit",Ze.textContent="添加",q.appendChild(He),q.appendChild(Ge),q.appendChild(Ze),q.addEventListener("submit",async dt=>{dt.preventDefault();const Je=Ge.value.trim(),be=Je!==""?Je:He.value;if(be==="")return;Ze.disabled=!0;const Xe=await e.canvasOp("add-member",I.name,be);Ze.disabled=!1,Xe&&(Ge.value="")}),c={name:I.name,form:q,pick:He,manual:Ge}}const X=c.pick,se=X.value;je(X),X.appendChild(new Option("本机会话(现场/冷启动)…","",!0,!0));const ee=new Set(I.members.map(q=>q.id));for(const q of(o==null?void 0:o.sessions)??[])ee.has(q.id)||X.appendChild(new Option(`${ut(q)} — 本机`,q.id));let ve=!1;for(const q of(o==null?void 0:o.remote)??[])q.team.includes("/")&&(ee.has(q.team)||(ve||(X.appendChild(new Option("远端会话队(目录卡片)…","",!0,!1)),ve=!0),X.appendChild(new Option(`${ut(q)} — 远端`,q.team))));ve||X.appendChild(new Option("(暂无可拉入的远端会话队——见下方说明)","",!0,!1)),se!==""&&Array.from(X.options).some(q=>q.value===se)&&(X.value=se),D.appendChild(c.form);const De=T("div","wb-hint");De.textContent="成员由人类在此手工分配(agent 不可自组队)。远端成员走对端目录路由,不做本机冷唤醒;地址由目录在派发时重解析。远端下拉只列 zone/id8 会话句柄——若为空,说明目录里还没有已链接对端的会话队(未链接的对端只给最小卡);此时可在输入框手输句柄(如 dsh/xxxxxxxx)先把人拉进来,句柄本身即链接手势。",D.appendChild(De),e.dispatchQuick!==void 0&&D.appendChild(ge(I.team))}x.appendChild(D)}function he(x){const w=T("form","wb-dispatch-form"),D=document.createElement("textarea");D.className="wb-dispatch-text",D.placeholder="一句话任务…(≤4000 字符,异步送达,回执进台账)",D.setAttribute("aria-label","快派消息");const N=T("button","wb-dispatch-submit");N.type="submit",N.textContent="发送";const I=T("div","wb-dispatch-note");return w.appendChild(D),w.appendChild(N),w.appendChild(I),D.addEventListener("keydown",H=>{H.key!=="Enter"||H.shiftKey||H.ctrlKey||H.altKey||H.metaKey||H.isComposing||(H.preventDefault(),N.disabled||w.requestSubmit())}),w.addEventListener("submit",async H=>{var ee;H.preventDefault();const X=D.value.trim();if(X===""||N.disabled)return;N.disabled=!0,I.textContent="派发中…";let se;try{se=await(((ee=e.dispatchQuick)==null?void 0:ee.call(e,x,X))??Promise.resolve({ok:!1,error:"dispatch unavailable"}))}catch(ve){se={ok:!1,error:ve instanceof Error?ve.message:String(ve)}}N.disabled=!1,se.ok===!0&&(D.value=""),D.focus({preventScroll:!0}),I.textContent=se.ok===!0?`已派发${se.task_id!==void 0?` · ${se.task_id}`:""} · 回执将入台账(待回执)`:`失败:${se.error??"未知原因"}`}),{form:w,text:D,note:I}}function ge(x){const w=T("div","wb-dispatch"),D=T("div","wb-side-head");return D.textContent="快派",w.appendChild(D),(m==null?void 0:m.team)!==x&&(m={team:x,...he(x)}),w.appendChild(m.form),w}function pe(x){le.open(x),J.classList.remove("is-rail-open")}function ae(x){if(o===null)return;const w=(o.sessions??[]).find(N=>N.id===x);if(w!==void 0){pe({kind:"session",id:x,title:ut(w),sub:w.team,team:w.team,live:w.live});return}if(x.startsWith("peer-")){const N=x.slice(5),I=(o.peers??[]).find(H=>H.url.includes(N));pe({kind:"peer",id:x,title:I!==void 0?I.url:N,sub:N,url:I==null?void 0:I.url});return}g.find(N=>N.id===x)!==void 0&&pe({kind:"log",id:x,title:`…${x.slice(-12)}`,sub:"档案"})}function Be(){var ee,ve;C.textContent=(o==null?void 0:o.version)!==void 0?`v${o.version}`:"",C.hidden=(o==null?void 0:o.version)===void 0;const x=je(k);if(o===null)return;const w=[],D=pn(o,e.now()),N=D.deadLetters.length;N>0&&w.push({text:`死信 ${N}`,tone:"danger",title:"超过 24h TTL 仍未结算的任务(unconsumed),需在台账清账",tab:"ledger",group:"死信"});const I=D.stale.length;I>0&&w.push({text:`陈旧 ${I}`,tone:"warn",title:"我方派发、要回执、已等待 ≥1h 仍未回执(可催办;免回执行不算)",tab:"ledger",group:"陈旧"});const H=D.owedOverdue.length;H>0&&w.push({text:`欠回执 ${H}`,tone:"warn",title:"对方投递给本机、已等待本机回执超过 1 小时",tab:"ledger",group:"待本机回执"});const X=((ee=o.idempotency)==null?void 0:ee.conflicts)??0;X>0&&w.push({text:`幂等冲突 ${X}`,tone:"danger",title:"同一幂等键对应了不同载荷,需人工核查",tab:"ledger",group:"取证"});const se=(((ve=o.reconcile)==null?void 0:ve.rows)??[]).filter(De=>De.needsRepair===!0).length;if(se>0&&w.push({text:`需修复 ${se}`,tone:"danger",title:"reconcile 无法自动恢复的会话,见行动计划",tab:"plan"}),w.length===0){x.appendChild(T("span","wb-header-ok")).textContent="● 无异常";return}for(const De of w){const q=T("button",`wb-alert wb-alert-${De.tone}`);q.type="button",q.textContent=De.text,q.title=`${De.title}(点击查看)`,q.addEventListener("click",()=>Te(De.tab,De.group)),x.appendChild(q)}}function Te(x,w){var I;const D=ie.find(([H])=>H===x)[1];n==="canvas"&&(J.classList.add("is-rail-open"),f.select(x,{focus:!1}));const N=w===void 0?D:w==="取证"?D.querySelector("details.wb-led-forensics"):Array.from(D.querySelectorAll(".wb-led-group")).find(H=>H.dataset.group===w)??null;N!==null&&(N instanceof HTMLDetailsElement&&(N.open=!0),(I=N.scrollIntoView)==null||I.call(N,{block:"start",behavior:"smooth"}))}function Ye(x){var D;const w=new Set((((D=x==null?void 0:x.canvas)==null?void 0:D.teams)??[]).flatMap(N=>N.members.filter(I=>I.cliHome===null&&I.cliHeld!==!0).map(I=>JSON.stringify([N.name,I.id]))));for(const N of d.keys())w.has(N)||d.delete(N);o=x,e.sessionLogList!==void 0&&!M&&(M=!0,e.sessionLogList().then(N=>{g=N.ok&&N.sessions!==void 0?N.sessions:[],u=N.ok?N.count:void 0}).catch(()=>{}).finally(()=>{M=!1,Ke()})),Be(),Ke(),Mt(),ct(),xt(),le.update(x),Ve(),Nt(),B(),Ne()}function Rt(x){if(x==="board"){Ne();return}f.select(x)}return Ye(null),{root:J,update:Ye,mode:()=>n,selectTab:Rt,openNodeChat:ae}}const pt=1e6,wn=.25,xn=3,ao=256,co=96;function $t(e){const t=typeof e=="number"?e:Number.NaN;if(Number.isFinite(t))return Math.max(-pt,Math.min(pt,Math.round(t)))}function lo(e){if(e===null||typeof e!="object")return;const t=e,n=$t(t.x),s=$t(t.y);return n===void 0||s===void 0?void 0:{x:n,y:s}}function Zo(e){const t=lo(e);if(t===void 0)return;const n=e,s=$t(n.w),o=$t(n.h);if(!(s===void 0||o===void 0||s<=0||o<=0))return{...t,w:Math.min(s,pt),h:Math.min(o,pt)}}const uo=e=>e!==""&&!e.includes("/")&&(e.startsWith("roster:")?e.length>7&&e.length<=47:e.length<=40);function mo(e){if(e===null||typeof e!="object")return;const t=e;if(t.version!==1)return;const n=t.viewport;if(n===null||typeof n!="object")return;const s=n,o=$t(s.x),i=$t(s.y),r=s.scale,c=typeof r=="number"?r:Number.NaN;if(o===void 0||i===void 0||!Number.isFinite(c))return;const d=Math.max(wn,Math.min(xn,c)),m={};if(t.nodes!==null&&typeof t.nodes=="object")for(const[u,E]of Object.entries(t.nodes)){if(typeof u!="string"||u===""||u.length>128)continue;const M=lo(E);if(M!==void 0&&(m[u]=M),Object.keys(m).length>=ao)break}const g={};if(t.frames!==null&&typeof t.frames=="object")for(const[u,E]of Object.entries(t.frames)){if(!uo(u))continue;const M=Zo(E);if(M!==void 0&&(g[u]=M),Object.keys(g).length>=co)break}return{version:1,viewport:{x:o,y:i,scale:d},nodes:m,frames:g}}function es(e,t,n){const s=r=>Math.max(-pt,Math.min(pt,Math.round(r))),o={};for(const[r,c]of t)if(!(typeof r!="string"||r===""||r.length>128)&&(o[r]={x:s(c.x),y:s(c.y)},Object.keys(o).length>=ao))break;const i={};for(const[r,c]of n){if(!uo(r))continue;const d=s(c.w),m=s(c.h);if(!(d<=0||m<=0)&&(i[r]={x:s(c.x),y:s(c.y),w:Math.min(d,pt),h:Math.min(m,pt)},Object.keys(i).length>=co))break}return{version:1,viewport:{x:s(e.x),y:s(e.y),scale:Math.max(wn,Math.min(xn,e.scale))},nodes:o,frames:i}}function fo(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619)>>>0;return t>>>0}const We=172,Pe=56,yt=[150,290,430],st=16;function ts(e){const t=fo(e),n=Math.floor(t/st)%yt.length,o=t%st/st*Math.PI*2+n*(Math.PI/st),i=yt[n];return{x:Math.round(Math.cos(o)*i),y:Math.round(Math.sin(o)*i*.6)}}function po(e){const t=new Map;for(const n of e)for(const s of n.members)t.has(s.id)||t.set(s.id,n.name);return t}const Fn=64,Pn=24,Rn=20,ns=4,Dn=24,Hn=44,os=24;function ss(e,t,n=Number.POSITIVE_INFINITY){const s=new Set(t),o=po(e),i=e.map(u=>[...new Set(u.members.map(E=>E.id))].filter(E=>s.has(E)&&o.get(E)===u.name));i.push(t.filter(u=>!o.has(u)));const r=new Map;let c=0,d=0,m=0,g=0;for(const u of i){if(u.length===0)continue;const E=Math.min(3,Math.max(u.length>1?2:1,Math.ceil(Math.sqrt(u.length)))),M=Math.ceil(u.length/E),J=Math.max(260,E*We+(E-1)*Pn+Dn*2),fe=Math.max(160,M*Pe+(M-1)*Rn+Hn+os);g>0&&(g%ns===0||c+J>n)&&(c=0,d+=m+Fn,m=0),u.forEach((ne,S)=>{const C=S%E,$=Math.floor(S/E);r.set(ne,{x:c+Dn+We/2+C*(We+Pn),y:d+Hn+Pe/2+$*(Pe+Rn)})}),c+=J+Fn,m=Math.max(m,fe),g+=1}return r}const Wt=12;function ho(e,t){const n=d=>t.every(m=>Math.abs(m.x-d.x)>=We+Wt||Math.abs(m.y-d.y)>=Pe+Wt),s=ts(e);if(n(s))return s;const o=fo(e),i=st*yt.length,r=Math.floor(o/st)%yt.length*st+o%st;for(let d=1;d<i;d++){const m=(r+d)%i,g=Math.floor(m/st),u=m%st/st*Math.PI*2+g*(Math.PI/st),E=yt[g],M={x:Math.round(Math.cos(u)*E),y:Math.round(Math.sin(u)*E*.6)};if(n(M))return M}const c=yt[yt.length-1];for(let d=0;;d++)for(let m=0;m<6;m++){const g={x:Math.round(-c+m*(We+Wt)),y:Math.round(c*.6+Pe*2+d*(Pe+Wt))};if(n(g))return g}}function at(e){return{x:e.x-We/2,y:e.y-Pe/2,w:We,h:Pe}}function Cn(e){if(e.length===0)return{x:0,y:0,w:260,h:160};let t=Number.POSITIVE_INFINITY,n=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY;for(const g of e)t=Math.min(t,g.x),n=Math.min(n,g.y),s=Math.max(s,g.x+g.w),o=Math.max(o,g.y+g.h);const i=24,r=44,c=24,d=Math.max(260,Math.round(s-t+i*2)),m=Math.max(160,Math.round(o-n+r+c));return{x:Math.round(t-i),y:Math.round(n-r),w:d,h:m}}class kn{constructor(){Ct(this,"revisionCounter",0);Ct(this,"nodes",new Map);Ct(this,"frames",new Map);Ct(this,"selection",new Set);Ct(this,"viewport",{x:0,y:0,scale:1})}get revision(){return this.revisionCounter}getNode(t){return this.nodes.get(t)}allNodes(){return[...this.nodes.values()]}nodeIds(){return[...this.nodes.keys()]}getFrame(t){return this.frames.get(t)}allFrames(){return[...this.frames.entries()]}isSelected(t){return this.selection.has(t)}selectedIds(){return[...this.selection]}bump(){this.revisionCounter+=1}dragNodes(t,n,s){let o=!1;for(const i of t){const r=this.nodes.get(i);r!==void 0&&(r.x+=n,r.y+=s,o=!0)}o&&this.bump()}beginFrameDrag(t){const n=this.frames.get(t);if(n!==void 0)return{name:t,rect:{...n}}}applyFrameDrag(t,n,s){const o=this.frames.get(t.name);o!==void 0&&(o.x=t.rect.x+n,o.y=t.rect.y+s,this.bump())}nudge(t,n,s){this.dragNodes(t,n,s)}marqueeSelect(t,n){n||this.selection.clear();for(const s of this.nodes.values()){const o=at(s);o.x<t.x+t.w&&o.x+o.w>t.x&&o.y<t.y+t.h&&o.y+o.h>t.y&&this.selection.add(s.id)}this.bump()}setSelection(t){this.selection=new Set(t),this.bump()}upsertNode(t){const n=this.nodes.get(t.id);if(n===void 0){this.nodes.set(t.id,t),this.bump();return}n.label=t.label,n.name=t.name,n.description=t.description,n.live=t.live,n.memberships=t.memberships,this.bump()}removeNode(t){this.nodes.delete(t)&&(this.selection.delete(t),this.bump())}setFrame(t,n){this.frames.set(t,n),this.bump()}removeFrame(t){this.frames.delete(t)&&this.bump()}upsertScore(t,n){const s=this.nodes.get(t);s===void 0||s.score===n||(s.score=n,this.bump())}positions(){const t=new Map;for(const[n,s]of this.nodes)t.set(n,{x:s.x,y:s.y});return t}frameRects(){const t=new Map;for(const[n,s]of this.frames)t.set(n,{...s});return t}teamMemberIds(t){const n=[];for(const s of this.nodes.values()){const o=s.memberships.find(i=>i.team===t);o!==void 0&&n.push({id:s.id,index:o.index})}return n.sort((s,o)=>s.index-o.index).map(s=>s.id)}setTeamMembers(t,n){for(const s of this.nodes.values()){const o=s.memberships.filter(r=>r.team!==t),i=n.indexOf(s.id);i>=0&&o.push({team:t,index:i}),s.memberships=o}this.bump()}contentBounds(){let t=Number.POSITIVE_INFINITY,n=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY;const i=r=>{t=Math.min(t,r.x),n=Math.min(n,r.y),s=Math.max(s,r.x+r.w),o=Math.max(o,r.y+r.h)};for(const r of this.nodes.values())r.remote!==!0&&i(at(r));for(const r of this.frames.values())i(r);return t===Number.POSITIVE_INFINITY?null:{minX:t,minY:n,maxX:s,maxY:o}}static fromState(t){const n=new kn,s=mo(t.layout),o=new Map;for(const i of t.teams)for(let r=0;r<i.members.length;r++){const c=i.members[r].id,d=o.get(c)??[];d.push({team:i.name,index:r}),o.set(c,d)}for(const i of t.sessions){if(i.joined!==!0)continue;const r=s==null?void 0:s.nodes[i.id];let c,d;if(r!==void 0&&typeof r.x=="number"&&typeof r.y=="number")c=r.x,d=r.y;else{const m=ho(i.id,[...n.nodes.values()]);c=m.x,d=m.y}n.nodes.set(i.id,{id:i.id,x:c,y:d,label:i.label,team:i.team,name:i.name,description:i.description,live:i.live!==!1,memberships:o.get(i.id)??[]})}for(const i of t.teams){const r=s==null?void 0:s.frames[i.name];if(r!==void 0){n.frames.set(i.name,{x:r.x,y:r.y,w:r.w,h:r.h});continue}const c=[];for(const d of i.members){const m=n.nodes.get(d.id);m!==void 0&&c.push(at(m))}c.length>0&&n.frames.set(i.name,Cn(c))}return n.viewport=s!==void 0?{...s.viewport}:{x:0,y:0,scale:1},n}}const is=24,bo=-13,rs=bo+is;function as(e,t,n){const s=[],o=new Set;for(const i of e){const r=t.get(i.name);if(r===void 0)continue;const c=r.x+r.w/2,d=r.y+rs;for(const m of i.members){const g=n.get(m.id);if(g===void 0)continue;const u=o.has(m.id);o.add(m.id),s.push({x1:c,y1:d,x2:g.x,y2:g.y-Pe/2,dashed:u,team:i.name,id:m.id})}}return s}function cs(e,t){for(;e.firstChild!==null;)e.firstChild.remove();const n=t.allFrames().map(([s])=>({name:s,members:t.teamMemberIds(s).map(o=>({id:o}))}));for(const s of as(n,new Map(t.allFrames()),t.positions())){const o=document.createElementNS("http://www.w3.org/2000/svg","line");o.setAttribute("class","e-member"+(s.dashed?" dashed":"")),o.setAttribute("x1",String(s.x1)),o.setAttribute("y1",String(s.y1)),o.setAttribute("x2",String(s.x2)),o.setAttribute("y2",String(s.y2)),e.appendChild(o)}}function ls(e){return`peer-${rt(e).replace(/[^A-Za-z0-9.:\[\]-]/g,"-")}`}function rt(e){try{return new URL(e).host}catch{return e}}function ds(e){const t=new Map;for(const n of e){const s=n.via!==void 0&&n.via!==""?rt(n.via):"",o=t.get(s)??[];o.push(n),t.set(s,o)}return t}const us=140,ms=200,fs=90,ps=20,Kt=60;function hs(e,t){const n=t!==null?t.maxX+us:ms,s=t!==null?t.minY:0;return e.map((o,i)=>({url:o.url,score:o.score,x:n,y:s+i*fs}))}function bs(e,t,n,s){const o=new Map;for(const r of e){if(!t.has(r.team))continue;const c=`${r.team}\0${r.peer}`,d=o.get(c);d===void 0?o.set(c,{team:r.team,peer:r.peer,startedAt:r.startedAt,count:1}):(d.startedAt=Math.min(d.startedAt,r.startedAt),d.count+=1)}const i=[];for(const r of o.values()){const c=t.get(r.team);if(c===void 0)continue;const d=n.find(g=>{try{return new URL(g.url).host===r.peer}catch{return!1}});if(d===void 0)continue;const m=Math.max(0,Math.round((s-r.startedAt)/1e3));i.push({x1:c.x,y1:c.y,x2:d.x,y2:d.y,label:`a2a_route · ${r.peer} · ${m}s`+(r.count>1?` ×${r.count}`:"")})}return i}function gs(e,t){const n=t!==null?{x:t.maxX+ps,y:e.length>0?(e[0].y+e[e.length-1].y)/2:(t.minY+t.maxY)/2}:{x:-80,y:0};return e.map((s,o)=>({x1:n.x,y1:n.y,x2:s.x,y2:s.y,label:o===0?"gns referral":""}))}function vs(e,t){if(e===null&&t.length===0)return null;let n=e!==null?e.minX:Number.POSITIVE_INFINITY,s=e!==null?e.minY:Number.POSITIVE_INFINITY,o=e!==null?e.maxX:Number.NEGATIVE_INFINITY,i=e!==null?e.maxY:Number.NEGATIVE_INFINITY;for(const r of t)n=Math.min(n,r.x-Kt),s=Math.min(s,r.y-Kt),o=Math.max(o,r.x+Kt),i=Math.max(i,r.y+Kt);return{minX:n,minY:s,maxX:o,maxY:i}}const ys=11;function ws(e,t){let n=[],s=[],o=[];function i(c){for(const g of n)g.remove();n=[];const d=t.allNodes().filter(g=>g.remote===!0).map(g=>({url:g.peerUrl??g.id.slice(5),score:g.score,x:g.x,y:g.y}));s=d;const m=t.contentBounds();if(m!==null){const g=new Map;for(const[u,E]of t.allFrames())g.set(u,{name:u,x:E.x+E.w/2,y:E.y+ys});for(const u of bs(o,g,d,c))n.push(...r(u,"e-activity",u.label));for(const u of gs(d,m))n.push(...r(u,"e-federal",u.label))}}function r(c,d,m){const g=document.createElementNS("http://www.w3.org/2000/svg","line");g.setAttribute("class",d),g.setAttribute("x1",String(c.x1)),g.setAttribute("y1",String(c.y1)),g.setAttribute("x2",String(c.x2)),g.setAttribute("y2",String(c.y2)),e.appendChild(g);const u=[g];if(m!==""){const E=document.createElementNS("http://www.w3.org/2000/svg","text");E.setAttribute("class","edge-label mono"),E.setAttribute("x",String((c.x1+c.x2)/2+14)),E.setAttribute("y",String((c.y1+c.y2)/2+18)),E.textContent=m,e.appendChild(E),u.push(E)}return u}return{render:i,flyTo(c){return c(),s},placements:()=>s,setInFlight:c=>{o=c}}}function xs(e,t){const n=new Map,s=new Set;for(const o of e){if(o.joined!==!0)continue;const i=o.team.lastIndexOf("/");if(i<=0||o.team.slice(i+1)!==o.id.replace(/^session-/,"").slice(0,8))continue;const r=o.team;t.has(r)||s.has(r)||(n.has(r)?(n.delete(r),s.add(r)):n.set(r,o.id))}return n}function Cs(e,t,n,s){var r;const o=t.get(e);return o!==void 0?o:e.includes("/")?`member-${e.replace(/[^a-z0-9-]/gi,"-")}`:`member-${((r=n==null?void 0:n.find(c=>c.via===s))==null?void 0:r.team.split("/")[0])??"dsh"}-${e.replace(/^session-/,"").slice(0,8)}`}const jn={x:0,y:0,scale:1};function Qt(e){return Math.max(wn,Math.min(xn,e))}function kt(e){const t=Number.isFinite(e.scale)?e.scale:1,n=Number.isFinite(e.x)?e.x:0,s=Number.isFinite(e.y)?e.y:0;return{x:n,y:s,scale:Qt(t)}}function mt(e,t,n){return{x:e.x+t/e.scale,y:e.y+n/e.scale}}function dn(e,t,n){return{x:e.x+t/e.scale,y:e.y+n/e.scale,scale:e.scale}}function Bn(e,t,n,s){const o=Qt(e.scale*t);return o===e.scale?e:{x:e.x+n/e.scale-n/o,y:e.y+s/e.scale-s/o,scale:o}}const ks=680,Es=.8,$s=.6,Yn=16;function En(e,t,n,s=Number.POSITIVE_INFINITY){if(e===null||t<=0||n<=0)return jn;const o=e.maxX-e.minX,i=e.maxY-e.minY;if(!(o>0)||!(i>0))return jn;const r=.9*Math.min(t/o,n/i),c=Math.min(s,r),d=t<ks?Es:$s;if(r<d){const g=Qt(Math.min(s,d));return{x:e.minX-Yn/g,y:e.minY-Yn/g,scale:g}}const m=Qt(c);return{x:(e.minX+e.maxX)/2-t/(2*m),y:(e.minY+e.maxY)/2-n/(2*m),scale:m}}function Ns(e){return`translate(${-e.x*e.scale}px, ${-e.y*e.scale}px) scale(${e.scale})`}function Wn(e,t){let n=e===null?null:{...e,minY:e.minY+bo};for(const s of t){if(s.remote!==!0)continue;const o=at(s);n=n===null?{minX:o.x,minY:o.y,maxX:o.x+o.w,maxY:o.y+o.h}:{minX:Math.min(n.minX,o.x),minY:Math.min(n.minY,o.y),maxX:Math.max(n.maxX,o.x+o.w),maxY:Math.max(n.maxY,o.y+o.h)}}return n}function Kn(e,t,n,s){const o=s>0&&s<n/2?s:0,i=En(e,t,n-o,1);return o===0||e===null?i:{...i,y:i.y-o/i.scale}}function Xn(...e){const t=Math.max(0,...e.map(n=>n.offsetHeight>0?n.offsetTop+n.offsetHeight:0));return t>0?t+8:0}function Pt(e){return e.type==="join-network"||e.type==="leave-network"?"$network":e.type==="create-team"||e.type==="remove-team"?e.name:e.team}function Ms(e,t){if(t.type==="create-team"){const o=t.ids.map(i=>e.getNode(i)).filter(i=>i!==void 0).map(i=>at(i));return o.length>0&&e.setFrame(t.name,Cn(o)),e.setTeamMembers(t.name,t.ids),()=>{e.setTeamMembers(t.name,e.teamMemberIds(t.name).filter(i=>!t.ids.includes(i))),e.removeFrame(t.name)}}if(t.type==="add-member"){const o=e.teamMemberIds(t.team),i=t.ids.filter(r=>!o.includes(r));return e.setTeamMembers(t.team,[...o,...i]),()=>{e.setTeamMembers(t.team,e.teamMemberIds(t.team).filter(r=>!i.includes(r)))}}if(t.type==="remove-member"){const o=e.teamMemberIds(t.team);return e.setTeamMembers(t.team,o.filter(i=>!t.ids.includes(i))),()=>{const i=e.teamMemberIds(t.team);e.setTeamMembers(t.team,[...i,...t.ids.filter(r=>!i.includes(r))])}}if(t.type==="remove-team"){const o=e.teamMemberIds(t.name),i=e.getFrame(t.name),r=i!==void 0?{...i}:void 0;return e.setTeamMembers(t.name,[]),e.removeFrame(t.name),()=>{r!==void 0&&e.setFrame(t.name,r),e.setTeamMembers(t.name,o)}}if(t.type==="join-network"||t.type==="leave-network")return()=>{};const n=e.teamMemberIds(t.team);let s=[...n];for(const o of t.ops)o.op==="remove"?s=s.filter(i=>i!==o.id):s.push(o.id);return e.setTeamMembers(t.team,s),()=>{let o=e.teamMemberIds(t.team);for(let i=0;i<n.length;i++){const r=n[i];o[i]!==r&&o.includes(r)&&(o=o.filter(c=>c!==r),o.push(r))}e.setTeamMembers(t.team,o)}}function Un(e,t,n,s,o){if(n||s(),!(t.type==="remove-team"&&n||t.type==="create-team"&&!n&&t.created===!0)||o===void 0||e.getFrame(Pt(t))!==void 0)return o;const r={...o.frames};return delete r[Pt(t)],{...o,frames:r}}function bn(e,t){const n=[...e],s=[];for(let o=0;o<t.length;o++){const i=t[o];n[o]!==i&&n.includes(i)&&(s.push({op:"remove",id:i}),s.push({op:"add",id:i}),n.splice(n.indexOf(i),1),n.push(i))}return s}function Ss(e,t){return e.teamMemberIds(t).map(n=>{var s;return{id:n,y:((s=e.getNode(n))==null?void 0:s.y)??0}}).sort((n,s)=>n.y-s.y).map(n=>n.id)}function un(e,t){let n,s=Number.POSITIVE_INFINITY;for(const[o,i]of e){if(t.x<i.x||t.x>i.x+i.w||t.y<i.y||t.y>i.y+i.h)continue;const r=i.w*i.h;r<s&&(n=o,s=r)}return n}const qn=[9133302,3900150,1357990,16096779,15485081,8702998];function As(e){let t=0;for(let c=0;c<e.length;c++)t=(t*31+e.charCodeAt(c)&268435455)>>>0;const n=qn[t%qn.length],s="#"+n.toString(16).padStart(6,"0"),o=n>>16&255,i=n>>8&255,r=n&255;return{line:s,bg:`rgba(${o},${i},${r},0.05)`}}const Zt="roster:",go=e=>e.startsWith(Zt)?e.slice(Zt.length):void 0,_t=e=>Zt+e,en=e=>{const t=go(e);return t===void 0?e:`网络队 · roster · ${t}`};function Ls(e,t){return e.flatMap(n=>{if(t===""||!n.team.startsWith(t+"/")||!Array.isArray(n.members))return[];const s=n.team.slice(t.length+1);return s===""||s.length>40||s.includes("/")||s.startsWith(Zt)||s.includes("*")?[]:[{name:_t(s),team:n.team,kind:"roster",members:n.members.filter(o=>typeof o=="string"&&o!=="").map(o=>({id:o}))}]})}function Is(e){const{root:t,netBtn:n,signal:s,model:o}=e,i=y=>e.isRoster(y)?en(y):y;function r(){return o.selectedIds().map(y=>{var f;return{id:y,y:((f=o.getNode(y))==null?void 0:f.y)??0}}).sort((y,f)=>y.y-f.y).map(y=>y.id)}function c(){if(d!==null)return;const y=r().filter(f=>{const _=o.getNode(f);return _===void 0?!1:_.remote!==!0||_.remoteHandle!==void 0&&_.remoteHandle!==""});if(y.length<2){e.notice("info","框选至少 2 个会话节点");return}m(y)}let d=null;function m(y){const f=document.createElement("div");f.className="p-dialog";const _=document.createElement("div");_.className="p-dialog-panel",_.setAttribute("role","dialog"),_.setAttribute("aria-modal","true"),_.setAttribute("aria-labelledby","p-dialog-title");const p=document.createElement("div");p.id="p-dialog-title",p.className="p-dialog-title",p.textContent=`组建团队(${y.length} 个节点,自上而下即优先级)`;const R={value:"canvas"},j=document.createElement("div");j.className="p-dialog-kind",j.setAttribute("role","radiogroup"),j.setAttribute("aria-label","团队类型");const K=document.createElement("div");K.className="p-dialog-hint";const we={canvas:"canvas 队:画布上的协作组,可以加入远端成员。",roster:"roster 网络队:只收本机已入网的会话;队名会写进属主的精确允许名单。"},Ae=["canvas","roster"].map(le=>{const te=document.createElement("button");return te.type="button",te.dataset.kind=le,te.setAttribute("role","radio"),te.textContent=le==="canvas"?"canvas 队":"roster 网络队",te.addEventListener("click",()=>P(le)),j.appendChild(te),te});function P(le){R.value=le;for(const te of Ae){const Ne=te.dataset.kind===le;te.setAttribute("aria-checked",String(Ne)),te.tabIndex=Ne?0:-1}K.textContent=we[le],ie.textContent=""}j.addEventListener("keydown",le=>{var Ne;const te=le;["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(te.key)&&(te.preventDefault(),P(R.value==="canvas"?"roster":"canvas"),(Ne=Ae.find(je=>je.dataset.kind===R.value))==null||Ne.focus())});const U=document.createElement("input");U.className="p-dialog-input",U.maxLength=40,U.setAttribute("aria-label","团队名");const ie=document.createElement("div");ie.className="p-dialog-err",ie.setAttribute("aria-live","polite");const Re=document.createElement("div");Re.className="p-dialog-row";const $e=document.createElement("button");$e.type="button",$e.textContent="组建";const re=document.createElement("button");re.type="button",re.textContent="取消",Re.append($e,re),P("canvas"),_.append(p,j,K,U,ie,Re),f.appendChild(_),t.appendChild(f);const Ce=document.activeElement,Q=()=>{f.remove(),d=null,Ce==null||Ce.focus()},oe=()=>{const le=U.value.trim();return le===""||le.length>40||le.includes("/")||le.startsWith("roster:")||R.value==="roster"&&le.includes("*")||/^\d+$/.test(le)?"队名需 1..40 字,不含“/”、通配符或保留前缀,不为纯数字":R.value==="roster"&&o.getFrame(le)!==void 0?"同名 canvas 队已存在,不能创建 roster 队":R.value==="roster"&&o.getFrame(_t(le))!==void 0&&!e.isRoster(_t(le))?"roster 框名已被 canvas 或镜像队占用":R.value==="canvas"&&o.getFrame(_t(le))!==void 0?"同名 roster 队已存在,不能创建 canvas 队":R.value==="roster"&&y.some(te=>{var Ne;return((Ne=o.getNode(te))==null?void 0:Ne.remote)===!0||!e.joined(te)})?"roster 仅接受本机已入网的会话":null},xe=()=>{const le=oe();if(le!==null){ie.textContent=le,U.focus();return}const te=R.value==="roster"?_t(U.value.trim()):U.value.trim();if(Q(),o.getFrame(te)!==void 0){e.notice("info",`已加入现有团队「${R.value==="roster"?en(te):te}」`),e.emit({type:"add-member",team:te,ids:y});return}e.emit({type:"create-team",name:te,ids:y,created:!0,...R.value==="roster"?{roster:!0}:{}})};$e.addEventListener("click",xe),re.addEventListener("click",Q),U.addEventListener("keydown",le=>{const te=le;te.key==="Enter"&&(te.preventDefault(),xe()),te.key==="Escape"&&(te.preventDefault(),Q())}),f.addEventListener("keydown",le=>{const te=le;if(te.key==="Escape"){te.preventDefault(),te.stopPropagation(),Q();return}if(te.key!=="Tab")return;te.preventDefault();const Ne=Array.from(_.querySelectorAll('input, select, button:not([tabindex="-1"])')),je=Ne.indexOf(document.activeElement),qe=te.shiftKey?Ne[(je-1+Ne.length)%Ne.length]:Ne[(je+1)%Ne.length];qe==null||qe.focus()},s),d={wrap:f,restoreFocus:Ce},U.focus()}let g=null,u="root",E=null,M=null,J=[];function fe(){M==null||M.remove(),M=null}function ne(){var _;if(M!==null){fe();return}if(J.length===0)return;const y=document.createElement("div");y.className="p-menu p-netmenu",y.setAttribute("role","menu"),y.setAttribute("aria-label","未入网会话");for(const p of J)y.appendChild(C(`${p.name!==void 0&&p.name!==""?p.name:p.label} — 入网`,!0,()=>{fe(),e.emit({type:"join-network",id:p.id})}));M=y,t.appendChild(y);const f=n.getBoundingClientRect();y.style.left=`${String(Math.min(f.left,window.innerWidth-220))}px`,y.style.top=`${String(Math.max(8,f.top-(J.length*30+10)))}px`,y.addEventListener("keydown",p=>{p.key==="Escape"&&(p.stopPropagation(),fe(),n.focus())}),(_=y.querySelector("button"))==null||_.focus(),document.addEventListener("pointerdown",p=>{if(M===null)return;const R=p.target;R!==null&&(R.closest(".p-netmenu")!==null||R===n||n.contains(R))||fe()},s)}function S(){g==null||g.remove(),g=null,u="root",E!==null&&E.isConnected?E.focus():t.focus(),E=null}function C(y,f,_){const p=document.createElement("button");return p.type="button",p.setAttribute("role","menuitem"),p.textContent=y,f?p.addEventListener("click",()=>{S(),_()}):(p.disabled=!0,p.setAttribute("aria-disabled","true")),p}function $(y,f,_,p="root",R){var Ae,P,U,ie,Re,$e,re;R!==void 0&&(E=R),E===null&&(E=e.anchorFor(_)),g==null||g.remove(),u=p;const j=document.createElement("div");j.className="p-menu",j.setAttribute("role","menu"),j.setAttribute("aria-label","团队操作");let K=[];if(_.kind==="frame")K=[C(`解散团队「${i(_.name)}」`,!0,()=>e.emit({type:"remove-team",name:_.name}))];else{const Ce=_.id;if(p==="root"){const Q=C("组成团队…",o.selectedIds().length>=2,()=>m(r()));Q.title="先框选或 Shift 加选至少 2 个节点",K.push(Q),e.teams().length>0&&(K.push(C("加入团队 ▸",!0,()=>$(y,f,_,"join"))),(((Ae=o.getNode(Ce))==null?void 0:Ae.memberships.map(xe=>xe.team))??[]).length>0&&(K.push(C("置顶路由 ▸",!0,()=>$(y,f,_,"promote"))),K.push(C("离队 ▸",!0,()=>$(y,f,_,"leave"))))),((P=o.getNode(Ce))==null?void 0:P.remote)!==!0&&K.push(C("退网",!0,()=>e.emit({type:"leave-network",id:Ce})))}else{K.push(C("‹ 返回",!0,()=>$(y,f,_,"root")));for(const Q of e.teams())if(p==="join"){const oe=((U=o.getNode(Ce))==null?void 0:U.memberships.some(te=>te.team===Q.name))??!0,xe=!e.isRoster(Q.name)||e.joined(Ce)&&((ie=o.getNode(Ce))==null?void 0:ie.remote)!==!0,le=C(oe?`${i(Q.name)}(已加入)`:i(Q.name),!oe&&xe,()=>e.emit({type:"add-member",team:Q.name,ids:[Ce]}));xe||(le.title="roster 只接受本机已入网会话"),K.push(le)}else if(p==="leave"){const oe=((Re=o.getNode(Ce))==null?void 0:Re.memberships.some(xe=>xe.team===Q.name))??!1;K.push(C(i(Q.name),oe,()=>e.emit({type:"remove-member",team:Q.name,ids:[Ce]})))}else{const oe=(($e=o.getNode(Ce))==null?void 0:$e.memberships.some(xe=>xe.team===Q.name))??!1;if(e.isRoster(Q.name))continue;K.push(C(i(Q.name),oe,()=>{const xe=o.teamMemberIds(Q.name),le=[Ce,...xe.filter(te=>te!==Ce)];e.emit({type:"reorder",team:Q.name,ops:bn(xe,le)})}))}}}j.append(...K),j.style.left=`${Math.min(y,window.innerWidth-200)}px`;const we=K.length*30+8;j.style.top=`${Math.max(8,Math.min(f,window.innerHeight-we-8))}px`,j.addEventListener("keydown",Ce=>{var le,te,Ne,je;const Q=Ce,oe=Array.from(j.querySelectorAll("[role=menuitem]")),xe=oe.indexOf(document.activeElement);Q.key==="ArrowDown"?(Q.preventDefault(),(le=oe[(xe+1+oe.length)%oe.length])==null||le.focus()):Q.key==="ArrowUp"?(Q.preventDefault(),(te=oe[(xe-1+oe.length)%oe.length])==null||te.focus()):Q.key==="Home"?(Q.preventDefault(),(Ne=oe[0])==null||Ne.focus()):Q.key==="End"?(Q.preventDefault(),(je=oe[oe.length-1])==null||je.focus()):Q.key==="Escape"&&(Q.preventDefault(),Q.stopPropagation(),u!=="root"?$(y,f,_,"root"):S())}),t.appendChild(j),g=j,(re=K[0])==null||re.focus()}function k(y,f,_){if(d!==null)return;const p=(_==null?void 0:_.closest(".p-node"))??null;if(p!==null){const K=o.getNode(p.dataset.id??"");if((K==null?void 0:K.remote)===!0&&(K.remoteHandle===void 0||K.remoteHandle===""))return}const R=p,j=(_==null?void 0:_.closest(".p-frame-head"))??null;if(R!==null){const K=R.dataset.id??"";o.isSelected(K)||o.setSelection([K]),E=R,$(y,f,{kind:"node",id:K})}else j!==null&&(E=j,$(y,f,{kind:"frame",name:j.dataset.frame??""}))}function V(y){J=y,n.textContent=`未入网${J.length>0?`(${String(J.length)})`:""}`,n.disabled=J.length===0,n.setAttribute("aria-disabled",String(J.length===0)),J.length===0&&fe()}return{menuItem:C,isMenuOpen:()=>g!==null,isDialogOpen:()=>d!==null,closeMenu:S,openMenu:$,contextMenuAt:k,openNameDialog:m,startCreateFromSelection:c,toggleNetMenu:ne,setUnjoined:V}}function Ts(e,t){const n=t.trim().toLowerCase();return n===""?[]:e.filter(s=>s.name.toLowerCase().includes(n)||s.team.toLowerCase().includes(n)||(s.host??"").toLowerCase().includes(n)).slice(0,8)}function _s(e,t){return e.allNodes().map(n=>{const s=n.id.slice(5),o=n.id.startsWith("member-")?n.name!==void 0&&n.name!==""?n.name:n.team:n.remote===!0?s:t(n);return{id:n.id,name:o,team:n.team,...n.remote===!0?{host:s}:{}}})}function Os(e,t=1200){e!==void 0&&(e.classList.add("pulse"),setTimeout(()=>e.classList.remove("pulse"),t))}function Fs(e){return t=>{const n=e.model.getNode(t);if(n===void 0)return;const s=e.viewSize();e.setVp(En({minX:n.x,minY:n.y,maxX:n.x+We,maxY:n.y+Pe},s.w,s.h,1)),e.applyViewport(),e.markDirty(),Os(e.cardEl(t))}}function Ps(e,t,n,s){const o=Rs({onPick:s});return e.appendChild(o.root),{...o,refresh:()=>o.setIndex(_s(t,n))}}function Rs(e){const t=document.createElement("div");t.className="p-search";const n=document.createElement("input");n.type="search",n.className="p-search-input",n.placeholder="🔍 搜索(Ctrl+K)",n.title="搜索名字 / 句柄 / 宿主(Ctrl+K 聚焦,Esc 清空)",n.setAttribute("aria-label","搜索画板节点:名字、句柄或宿主");const s=document.createElement("div");s.className="p-search-list";let o=[],i=[],r=0;const c=()=>{s.style.display="none",s.textContent=""},d=()=>{if(s.textContent="",i.length===0){c();return}i.forEach((u,E)=>{const M=document.createElement("div");M.className="p-search-hit"+(E===r?" active":""),M.dataset.id=u.id,M.textContent=u.name===u.team?u.name:`${u.name} · ${u.team}`,M.addEventListener("mousedown",J=>{J.preventDefault(),e.onPick(u.id),m()}),s.appendChild(M)}),s.style.display="block"},m=()=>{n.value="",i=[],c()},g=()=>{i=Ts(o,n.value),r=0,d()};return n.addEventListener("input",g),n.addEventListener("keydown",u=>{if(u.key==="Escape"){m(),n.blur(),u.stopPropagation();return}if(u.key==="ArrowDown"||u.key==="ArrowUp"){if(i.length===0)return;u.preventDefault(),r=(r+(u.key==="ArrowDown"?1:i.length-1))%i.length,d();return}u.key==="Enter"&&i.length>0&&(u.preventDefault(),e.onPick(i[r].id),m())}),t.append(n,s),{root:t,setIndex(u){o=u,n.value!==""&&g()},focus(){n.focus(),n.select()},clear(){m()}}}const Xt=28,zn=16,Jn=14,Vn=48,Ds=4,Hs=180,js=90,Bs=1600;function Ys(e){const t=e.wrapWidth??Bs,n=e.frames.map(d=>{const m=Math.max(d.soleIds.length,1),g=Math.min(Ds,Math.max(1,Math.ceil(Math.sqrt(m)))),u=Math.ceil(m/g),E=Math.max(Hs,Xt*2+g*We+(g-1)*zn),M=Math.max(js,Xt*2+u*Pe+(u-1)*Jn);return{name:d.name,w:E,h:M,rows:u,perRow:g,soleIds:d.soleIds}}),s=[],o=[];let i=0,r=0,c=0;for(const d of n){i>0&&i+d.w>t&&(i=0,r+=c+Vn,c=0);const m={x:i,y:r,w:d.w,h:d.h};s.push({name:d.name,rect:m});const g=m.x+Xt+We/2;let u=m.y+Xt+Pe/2;d.soleIds.forEach((E,M)=>{const J=M%d.perRow;M>0&&J===0&&(u+=Pe+Jn),o.push({id:E,x:g+J*(We+zn),y:u})}),i+=d.w+Vn,c=Math.max(c,d.h)}return{frames:s,seats:o}}function Ws(e,t){const n=[];for(const[o,i]of e.allFrames()){if(t.has(o))continue;const r=e.teamMemberIds(o).map(c=>e.getNode(c)).filter(c=>c!==void 0).filter(c=>c.memberships.length===1).map(c=>c.id);n.push({name:o,rect:i,soleIds:r})}if(n.length===0)return!1;const s=Ys({frames:n});for(const o of s.frames)e.setFrame(o.name,o.rect);for(const o of s.seats){const i=e.getNode(o.id);i!==void 0&&e.dragNodes([o.id],o.x-i.x,o.y-i.y)}return!0}function Ks(e,t){if(e===void 0)return;const n=rt(e);return{text:t?`同步中… · ${n}`:`镜像 · ${n}`,title:t?`已把改动交给属主 ${n},等待它确认`:`这个团队属于 ${n},这里显示的是它的镜像;拖入或移出成员会经中继交给 ${n} 执行`,syncing:t}}const Xs=1e4;function Us(e=Date.now){const t=(s,o)=>`${s}\0${o}`,n=new Map;return{fork(s,o){return n.set(t(o,s.name),{url:o,name:s.name}),{...s,fleetPeer:o}},settled(s,o){if(s.type!=="remove-team"||s.name===void 0||s.fleetPeer===void 0)return;const i=n.get(t(s.fleetPeer,s.name));i!==void 0&&(o?i.acceptedAt=e():n.delete(t(s.fleetPeer,s.name)))},hides(s,o){return n.has(t(o,s))},sweep(s){var o;for(const[i,r]of n)(!(((o=s.find(d=>d.url===r.url))==null?void 0:o.org.some(d=>d.name===r.name))===!0)||r.acceptedAt!==void 0&&e()-r.acceptedAt>Xs)&&n.delete(i)}}}const wt=24,qs=200,zt=(e,t)=>e.x<t.x+t.w&&t.x<e.x+e.w&&e.y<t.y+t.h&&t.y<e.y+e.h;function zs(e,t){const n=t.x+t.w+wt-e.x,s=t.x-wt-(e.x+e.w),o=t.y+t.h+wt-e.y,i=t.y-wt-(e.y+e.h),r=Math.abs(n)<=Math.abs(s)?n:s,c=Math.abs(o)<=Math.abs(i)?o:i;return Math.abs(r)<=Math.abs(c)?{dx:r,dy:0}:{dx:0,dy:c}}function Js(e,t,n,s){const o=e.beginFrameDrag(t);o!==void 0&&e.applyFrameDrag(o,n,s)}function Vs(e,t,n){let s=!1;for(let o=0;o<qs;o++){const i=e.allFrames();let r;for(let S=0;S<i.length&&r===void 0;S++)for(let C=S+1;C<i.length;C++){const[$,k]=i[S],[V,y]=i[C];if(!(t.has($)&&t.has(V))&&zt(k,y)){r=[$,k,V,y];break}}if(r===void 0)return s;const[c,d,m,g]=r,u=S=>t.has(S)?2:S===n?1:0,[E,M,J]=u(c)<u(m)?[c,d,g]:[m,g,d],{dx:fe,dy:ne}=zs(M,J);Js(e,E,fe,ne),s=!0}return s}const ht=28,Jt=16,tn=14;function Gs(e){const t=[];for(let n=e.y+ht;n+Pe<=e.y+e.h-ht;n+=Pe+tn)for(let s=e.x+ht;s+We<=e.x+e.w-ht;s+=We+Jt)t.push({x:s+We/2,y:n+Pe/2});return t}function Qs(e){let t=!1;const n=e.allFrames(),s=new Map(n);for(const o of e.allNodes()){const i=new Set(o.memberships.map(ne=>ne.team)),r=n.find(([ne,S])=>!i.has(ne)&&zt(at(o),S));if(r===void 0)continue;const c=r[1],d=ne=>{const S=at(ne);return n.every(([C,$])=>i.has(C)||!zt(S,$))&&e.allNodes().every(C=>C.id===o.id||!zt(S,at(C)))},g=o.memberships.map(ne=>s.get(ne.team)).filter(ne=>ne!==void 0).flatMap(Gs).find(d),u={x:c.x+c.w+wt+We/2,y:o.y},E={x:o.x,y:c.y+c.h+wt+Pe/2},M=Math.max(...n.map(([,ne])=>ne.x+ne.w))+wt+We/2,J=Array.from({length:2*e.allNodes().length+1},(ne,S)=>({x:M,y:c.y+Pe/2+S*(Pe+tn)})),fe=g??[u,E,...J].find(d)??J[J.length-1];e.dragNodes([o.id],fe.x-o.x,fe.y-o.y),t=!0}return t}function Ut(e,t,n){const s=Vs(e,t,n),o=Qs(e);return s||o}function Zs(e){for(const[t,n]of e.allFrames()){const s=e.teamMemberIds(t).map(m=>e.getNode(m)).filter(m=>m!==void 0).filter(m=>m.memberships.length===1);if(s.length===0)continue;const o=Math.max(1,Math.floor((n.w-2*ht+Jt)/(We+Jt))),i=Math.ceil(s.length/o),r=ht*2+i*Pe+(i-1)*tn;r>n.h&&e.setFrame(t,{...n,h:Math.min(r,n.h+400)});const c=n.x+ht+We/2;let d=n.y+ht+Pe/2;s.forEach((m,g)=>{const u=g%o;g>0&&u===0&&(d+=Pe+tn);const E=c+u*(We+Jt);e.dragNodes([m.id],E-m.x,d-m.y)})}}const ei=30;function ti(e){const t=[],n=()=>({nodes:new Map(e.allNodes().map(i=>[i.id,{x:i.x,y:i.y}])),frames:new Map(e.allFrames().map(([i,r])=>[i,{...r}]))}),s=i=>e.allNodes().some(r=>{const c=i.nodes.get(r.id);return c!==void 0&&(c.x!==r.x||c.y!==r.y)})||e.allFrames().some(([r,c])=>{const d=i.frames.get(r);return d!==void 0&&(d.x!==c.x||d.y!==c.y||d.w!==c.w||d.h!==c.h)}),o=i=>{for(const r of e.allNodes()){const c=i.nodes.get(r.id);c!==void 0&&(c.x!==r.x||c.y!==r.y)&&e.dragNodes([r.id],c.x-r.x,c.y-r.y)}for(const[r,c]of e.allFrames()){const d=i.frames.get(r);d!==void 0&&(d.x!==c.x||d.y!==c.y||d.w!==c.w||d.h!==c.h)&&e.setFrame(r,{...d})}};return{capture:n,commit(i){s(i)&&(t.push(i),t.length>ei&&t.shift())},undo(){const i=t.pop();return i===void 0?!1:(o(i),!0)},revert(i){return s(i)?(o(i),!0):!1},get depth(){return t.length}}}function ni(e){const{root:t,trigger:n,signal:s,model:o}=e;let i=null,r="",c=null;function d(){c==null||c.remove(),c=null}function m(){d(),i==null||i.remove(),i=null}function g(){if(i!==null){m();return}const C=document.createElement("div");C.className="p-menu p-nodelist",C.setAttribute("role","dialog"),C.setAttribute("aria-label","节点清单"),i=C,t.appendChild(C);const $=n.getBoundingClientRect();C.style.left=`${String(Math.min($.left,window.innerWidth-340))}px`,C.style.top=`${String(Math.max(8,$.top-320))}px`,C.addEventListener("keydown",k=>{k.key==="Escape"&&(k.stopPropagation(),m(),n.focus())}),u()}function u(){var _;const C=i;if(C===null)return;d(),C.textContent="";const $=document.createElement("div");$.className="p-nodelist-head";const k=document.createElement("span");k.className="p-nodelist-title",k.textContent="节点";const V=document.createElement("select");V.className="p-nodesel",V.setAttribute("aria-label","选择 host 或对等节点");const y=document.createElement("option");y.value="",y.textContent="本机",V.appendChild(y);for(const[p,R]of e.facts().remoteTeams){if(p==="")continue;const j=document.createElement("option");j.value=p,j.textContent=((_=R[0])==null?void 0:_.origin)!==void 0&&R[0].origin!==""?`${R[0].origin}(${p})`:p,V.appendChild(j)}r!==""&&!e.facts().remoteTeams.has(r)&&(r=""),V.value=r,V.addEventListener("change",()=>{r=V.value,u()}),$.append(k,V);const f=document.createElement("div");f.className="p-nodelist-body",C.append($,f),r===""?ne(f):S(f,e.facts().remoteTeams.get(r)??[])}function E(C,$){const k=document.createElement("span");return k.className="p-nodedot"+($!==!0?" off":C===!1?" cold":""),k}function M(C,$){const k=document.createElement("button");return k.type="button",k.className="p-listbtn",k.textContent=C,k.addEventListener("click",V=>{V.stopPropagation(),$()}),k}function J(C,$){const k=document.createElement("div");if(k.className="p-nodelist-group",k.textContent=C,$!==void 0){const V=document.createElement("span");V.className="p-nodelist-note",V.textContent=$,k.appendChild(V)}return k}function fe(C,$){var y,f;d();const k=document.createElement("div");k.className="p-menu",k.setAttribute("role","menu"),k.setAttribute("aria-label","加入团队");for(const _ of e.facts().teams){const p=((y=o.getNode($))==null?void 0:y.memberships.some(R=>R.team===_.name))??!1;k.appendChild(e.menuItem(p?`${_.name}(已加入)`:_.name,!p,()=>{m(),e.emit({type:"add-member",team:_.name,ids:[$]})}))}k.appendChild(e.menuItem("新团队…",!0,()=>{m(),e.openNameDialog([$])})),c=k,t.appendChild(k);const V=C.getBoundingClientRect();k.style.left=`${String(Math.min(V.right+4,window.innerWidth-180))}px`,k.style.top=`${String(Math.max(8,V.top-60))}px`,k.addEventListener("keydown",_=>{_.key==="Escape"&&(_.stopPropagation(),d(),C.focus())}),(f=k.querySelector("button"))==null||f.focus()}function ne(C){var f,_;const $=new Set(e.facts().canvasMemberIds);for(const p of e.facts().sessions)p.teams!==void 0&&p.teams.length>0&&$.add(p.id);const k=e.facts().sessions.filter(p=>p.joined===!0&&!$.has(p.id)),V=e.facts().sessions.filter(p=>p.joined!==!0),y=e.facts().sessions.filter(p=>p.joined===!0&&$.has(p.id));C.appendChild(J(`未组队 (${String(k.length)})`,"无网络能力"));for(const p of k){const R=document.createElement("div");R.className="p-noderow";const j=document.createElement("span");j.className="p-nodename",j.textContent=p.name!==void 0&&p.name!==""?p.name:p.label,j.title=p.team;const K=document.createElement("span");K.className="p-nonet",K.textContent="无网络能力";const we=M("入队▸",()=>fe(we,p.id));R.append(E(p.live,!0),j,K,we,M("退网",()=>{m(),e.emit({type:"leave-network",id:p.id})})),C.appendChild(R)}C.appendChild(J(`未入网 (${String(V.length)})`));for(const p of V){const R=document.createElement("div");R.className="p-noderow";const j=document.createElement("span");j.className="p-nodename",j.textContent=p.name!==void 0&&p.name!==""?p.name:p.label,j.title=p.id,R.append(E(p.live,!1),j,M("入网",()=>{m(),e.emit({type:"join-network",id:p.id})})),C.appendChild(R)}C.appendChild(J(`已组队 (${String(y.length)})`,"画布展示"));for(const p of y){const R=document.createElement("div");R.className="p-noderow";const j=document.createElement("span");j.className="p-nodename",j.textContent=p.name!==void 0&&p.name!==""?p.name:p.label,j.title=p.team;const K=((f=o.getNode(p.id))==null?void 0:f.memberships.length)??0,we=((_=p.teams)==null?void 0:_.length)??0,Ae=document.createElement("span");Ae.className="p-teamcount",Ae.textContent=`队×${String(Math.max(K,0)+we)}`,R.append(E(p.live,!0),j,Ae,M("定位",()=>{e.locate(p.id)})),C.appendChild(R)}if(e.facts().sessions.length===0){const p=document.createElement("div");p.className="p-nodelist-empty",p.textContent="无会话节点",C.appendChild(p)}}function S(C,$){var y;const k=$.filter(f=>f.teams===void 0||f.teams.length===0),V=$.filter(f=>f.teams!==void 0&&f.teams.length>0);C.appendChild(J(`未组队 (${String(k.length)})`,"无网络能力 · 只读"));for(const f of k){const _=document.createElement("div");_.className="p-noderow";const p=document.createElement("span");p.className="p-nodename",p.textContent=f.name!==void 0&&f.name!==""?f.name:f.team,p.title=f.team;const R=document.createElement("span");R.className="p-nonet",R.textContent="无网络能力",_.append(E(!0,!0),p,R),C.appendChild(_)}C.appendChild(J(`已组队 (${String(V.length)})`,"只读"));for(const f of V){const _=document.createElement("div");_.className="p-noderow";const p=document.createElement("span");p.className="p-nodename",p.textContent=f.name!==void 0&&f.name!==""?f.name:f.team,p.title=f.team;const R=document.createElement("span");R.className="p-teamcount",R.textContent=`队×${String(((y=f.teams)==null?void 0:y.length)??0)}`,_.append(E(!0,!0),p,R),C.appendChild(_)}if($.length===0){const f=document.createElement("div");f.className="p-nodelist-empty",f.textContent="该节点未发布会话",C.appendChild(f)}}return document.addEventListener("pointerdown",C=>{if(i===null)return;const $=C.target;if($===null){m();return}$.closest(".p-nodelist")!==null||$.closest(".p-menu")!==null||$===n||n.contains($)||m()},s),{toggle:g,close:m,refresh:()=>{i!==null&&u()}}}function oi(e){let t=e;const n=t.search(/\|| ```|^```/);return n>=0&&(t=t.slice(0,n)),t.replace(/\[([^\]]*)\]\([^)]*\)/g,"$1").replace(/(^|\s)#{1,6}\s+/g,"$1").replace(/\*\*|__|`/g,"").replace(/\s+/g," ").trim()}function si(e,t){const n=e.map(i=>i.name!==void 0&&i.name!==""?i.name:i.team),s=n.slice(0,2).join("、"),o=n.length>2?` +${n.length-2}`:"";return(t!==void 0?`score ${t} · `:"")+`${e.length} 远端团队`+(s!==""?`:${s}${o}`:"")}function ii(e,t,n){if(e.id.startsWith("member-"))return{title:e.name!==void 0&&e.name!==""?e.name:e.team,sub:e.team,tip:e.team};if(e.remote===!0){const d=e.id.slice(5),m=t.length===1&&t[0].name!==void 0&&t[0].name!==""?t[0].name:void 0,g=si(t,n);return m!==void 0?{title:m,sub:`${d} · ${g}`,tip:`${m}
6
+ ${d}
7
+ ${g}`}:{title:d,sub:g,tip:`${d}
8
+ ${g}`}}const s=it(e),o=e.team+(e.name!==void 0&&e.name!==""?" · "+e.name:""),i=oi(e.description??""),r=i!==""?i:o===s?"":e.team,c=(e.memberships.length>1?` · 跨队×${e.memberships.length}`:"")+(e.teams!==void 0&&e.teams.length>0?` · 网络队×${e.teams.length}`:"");return{title:s,sub:r+c,tip:i!==""?`${o}
9
+ ${i}`:o}}function ri(e){if((e==null?void 0:e.status)===void 0)return"";const t=e.status==="running"?"工作中":"空闲";return typeof e.queued=="number"&&e.queued>0?`${t} · 排队 ${String(e.queued)}`:t}function ai(e){const t=document.createElement("div");t.className="p-node",t.dataset.id=e,t.tabIndex=0;const n=document.createElement("div");n.className="nm";const s=document.createElement("span");s.className="dot";const o=document.createElement("span");o.className="nm-text",n.append(s,o);const i=document.createElement("div");i.className="sub mono";const r=document.createElement("span");r.className="prio mono";const c=document.createElement("span");c.className="work";const d=document.createElement("div");return d.className="sub-row",d.append(i,c),t.append(n,d,r),t}function ci(e,t,n){if(t.remote===!0){const s=(n==null?void 0:n.link)==="linked"?"lnk-linked":(n==null?void 0:n.link)==="stranger"?"lnk-stranger":"";e.className!==`prio mono ${s}`.trim()&&(e.className=`prio mono ${s}`.trim());const o=n===void 0?"":n.verified===!0?"✓":"✗",i=(t.id.startsWith("member-")?"成员":(n==null?void 0:n.link)??"peer")+o;e.textContent!==i&&(e.textContent=i)}else{e.className!=="prio mono"&&(e.className="prio mono");const s=t.memberships[0]!==void 0?`P${t.memberships[0].index}`:"";e.textContent!==s&&(e.textContent=s)}}const Gn="dsh-a2a.board.legend";function li(){const e=document.createElement("details");e.className="p-legend";const t=document.createElement("summary");t.textContent="图例",t.title="边的含义(点击展开 / 收起)";const n=document.createElement("div");n.className="p-legend-rows";const s=(o,i,r)=>{const c=document.createElement("span"),d=document.createElement("i");o&&(d.className="dashed"),r!==void 0&&(d.style.borderTopColor=r),c.append(d,document.createTextNode(i)),n.appendChild(c)};s(!1,"成员边(星形,框→成员)"),s(!0,"跨队多属(虚线)"),s(!0,"roster 网络队框(双线边框)"),s(!1,"活动边(inFlight 路由,瞬时)","var(--accent)"),s(!0,"联邦线(→peer)","var(--federal)"),e.append(t,n);try{e.open=window.localStorage.getItem(Gn)==="open"}catch{}return e.addEventListener("toggle",()=>{try{window.localStorage.setItem(Gn,e.open?"open":"closed")}catch{}}),e}const Vt="对端未应答(显示的是最后已知状态)";function Qn(e){const t=new Set;for(const s of e.remoteTeams??[])s.unreachable===!0&&s.via&&t.add(rt(s.via));for(const s of e.peerOrg??[])s.unreachable===!0&&t.add(rt(s.url));const n=new Set;for(const s of e.remoteTeams??[])s.via&&t.has(rt(s.via))&&n.add(`member-${s.team.replace(/[^a-z0-9-]/gi,"-")}`);return{hosts:t,members:n}}function di(e,t){e.classList.toggle("is-unreachable",t);const n=e.querySelector(".p-frame-unreachable");if(t&&n===null){const s=document.createElement("span");s.className="p-frame-unreachable",s.textContent=Vt,e.appendChild(s)}else t||n==null||n.remove()}function ui(e){return e<680?"narrow":e<1024?"medium":"wide"}function mi(e){const t=new kn;let n,s={...t.viewport},o={kind:"none"},i=!1,r=-1,c=!0,d=!1;const m=ti(t);let g,u=!1;const E=()=>ui(globalThis.innerWidth||Ke),M=()=>{var a;(a=e.viewportStore)==null||a.set(E(),s),e.onDirty()},J=e.now??Date.now,fe=new AbortController,ne=[];let S=null,C=!1;const $={signal:fe.signal},k=document.createElement("section");k.id="dsh-plan",k.setAttribute("role","tabpanel"),k.setAttribute("aria-label","A2A 规划画布"),k.tabIndex=0,k.style.display="none";const V=document.createElement("div");V.className="p-grid";const y=document.createElement("div");y.className="p-world";const f=document.createElementNS("http://www.w3.org/2000/svg","svg");f.setAttribute("class","p-edges");const _=document.createElement("div");_.className="p-layer p-frames";let p;const R=a=>{var h,v;p!==void 0&&((h=Oe.get(p))==null||h.classList.remove("selected")),p=a,a!==void 0&&((v=Oe.get(a))==null||v.classList.add("selected"))},j=document.createElement("div");j.className="p-layer p-nodes",j.setAttribute("role","listbox"),j.setAttribute("aria-multiselectable","true"),j.setAttribute("aria-label","会话节点"),y.append(f,_,j);const K=document.createElement("div");K.className="p-marquee";const we=document.createElement("div");we.className="p-toolbar";const Ae=document.createElement("span");Ae.className="p-zoom mono",Ae.textContent="100%";const P=(a,h,v)=>{const F=document.createElement("button");return F.type="button",F.textContent=a,F.title=h,F.addEventListener("click",v,$),F},U=()=>{const a=document.createElement("span");return a.className="p-sep",a.setAttribute("role","separator"),a.setAttribute("aria-orientation","vertical"),a};we.append(P("适配","适配视图(快捷键 0)",()=>St()),P("-","缩小",()=>Xe(1/1.2)),Ae,P("+","放大",()=>Xe(1.2)),U(),P("建队","组成团队(G):先框选至少 2 个节点",()=>X.startCreateFromSelection()),P("整理成员","每队框内仅属一队的成员自动排齐(多队成员不动)",()=>bt()),P("一键重排","全板队框+成员重排:框尺寸随内容自适应、框位行流式平铺(远端队框与多归属成员不动)",()=>Nt()),P("撤销","撤销上一次布局改动:拖动、缩放、整理、重排、载入时避让(Ctrl+Z;入队离队不撤销)",()=>ft()));const ie=P("未入网","未入网会话(点击选择入网)",()=>X.toggleNetMenu());ie.className="p-netbtn",ie.setAttribute("aria-haspopup","menu"),we.append(U(),ie);const Re=Ps(we,t,it,a=>ae(a)),$e=P("节点","节点清单:本机与对等节点(未组队/未入网在此管理)",()=>ve.toggle());$e.className="p-nodebtn",$e.setAttribute("aria-haspopup","dialog"),we.appendChild($e);const re=document.createElement("span");re.setAttribute("aria-live","polite"),re.className="p-lamp";const Ce=document.createElement("i"),Q=document.createElement("span");Q.textContent="布局",re.append(Ce,Q),re.addEventListener("click",()=>{re.classList.contains("error")&&e.onLampClick()},$),re.addEventListener("keydown",a=>{const h=a;(h.key==="Enter"||h.key===" ")&&re.classList.contains("error")&&(h.preventDefault(),e.onLampClick())},$),we.appendChild(re);const oe=document.createElement("div");oe.className="p-status";const xe=document.createElement("span"),le=document.createElement("span"),te=document.createElement("span");te.className="mut";const Ne=document.createElement("span");Ne.className="mut",oe.append(xe,le,te,Ne),oe.tabIndex=0,oe.setAttribute("role","button"),oe.setAttribute("aria-label","定位在途路由与联邦对端"),oe.title="定位在途与联邦(回车)",oe.addEventListener("click",()=>pe(),$),oe.addEventListener("keydown",a=>{const h=a;(h.key==="Enter"||h.key===" ")&&(h.preventDefault(),pe())},$);const je=li(),qe=document.createElement("div");qe.className="p-notice-stack",qe.setAttribute("role","log"),qe.setAttribute("aria-live","polite"),k.append(V,y,K,we,oe,je,qe);let Ke=0,ct=0;const xt=()=>e.viewSize?e.viewSize():{w:k.clientWidth,h:k.clientHeight},ze=()=>{const a=xt();Ke=a.w,ct=a.h};function Ve(){y.style.transform=Ns(s);const a=24*s.scale;V.style.backgroundSize=`${a}px ${a}px`,V.style.backgroundPosition=`${-s.x*s.scale}px ${-s.y*s.scale}px`,Ae.textContent=`${Math.round(s.scale*100)}%`}function bt(){const a=m.capture();Zs(t),Ut(t,new Set(x.keys())),m.commit(a),M(),Le()}function Nt(){const a=m.capture();Ws(t,new Set(x.keys()))&&(Ut(t,new Set(x.keys())),m.commit(a),M(),Le())}function Le(){Ve(),t.revision!==r&&(r=t.revision,lt(),Mt(),cs(f,t)),ge()}const Oe=new Map,et=new Map;function lt(){var h;const a=new Set;for(const[v,F]of t.allFrames()){a.add(v);let O=Oe.get(v);if(O===void 0){O=document.createElement("div"),O.className="p-frame",O.dataset.name=v;const ke=document.createElement("div");ke.className="p-frame-head",ke.dataset.frame=v,ke.tabIndex=0,ke.setAttribute("role","button"),ke.setAttribute("aria-haspopup","menu"),ke.setAttribute("aria-label",`团队框 ${v}(拖动整组,回车菜单)`);const Qe=document.createElement("span");Qe.className="ttl";const tt=document.createElement("span");tt.className="cnt mono";const gt=document.createElement("span");gt.className="route mono",ke.append(Qe,tt,gt),O.appendChild(ke);for(const At of["n","s","e","w","ne","nw","se","sw"]){const vt=document.createElement("div");vt.className=`p-frame-handle p-h-${At}`,vt.dataset.frame=v,vt.dataset.dir=At,O.appendChild(vt)}_.appendChild(O),Oe.set(v,O)}const ue=As(v);O.style.setProperty("--frame-line",ue.line),O.style.setProperty("--frame-bg",ue.bg),O.style.left=`${F.x}px`,O.style.top=`${F.y}px`,O.style.width=`${F.w}px`,O.style.height=`${F.h}px`;const Y=O.querySelector(".ttl"),W=O.querySelector(".cnt"),z=O.querySelector(".route"),ce=w.has(v),Ie=ce?en(v):v;Y.textContent!==Ie&&(Y.textContent=Ie),O.classList.toggle("is-roster",ce);let _e=0,Me=0;for(const ke of t.allNodes())ke.memberships.find(tt=>tt.team===v)!==void 0&&(_e+=1,ke.memberships.length>1&&(Me+=1));const b=`${_e} 成员`+(Me>0?` +${Me} 跨队`:"");W.textContent!==b&&(W.textContent=b);const G=Ks(x.get(v),Te.has(v)),de=G!==void 0&&D.hosts.has(rt(x.get(v))),Fe=(G==null?void 0:G.text)??((h=Ht.find(ke=>ke.name===v))==null?void 0:h.team)??v;z.textContent!==Fe&&(z.textContent=Fe),z.title=`${(G==null?void 0:G.title)??""}${de?` · ${Vt}`:""}`,di(O,de),O.classList.toggle("is-mirror",G!==void 0),O.classList.toggle("is-syncing",(G==null?void 0:G.syncing)===!0);const Se=`团队框 ${v}${G!==void 0?`(${G.text})`:""}${de?`(${Vt})`:""}(拖动整组,回车菜单)`,me=O.querySelector(".p-frame-head");me.getAttribute("aria-label")!==Se&&me.setAttribute("aria-label",Se)}for(const[v,F]of[...Oe])a.has(v)||(F.remove(),Oe.delete(v))}function Mt(){const a=new Set;for(const h of t.allNodes()){a.add(h.id);let v=et.get(h.id);v===void 0&&(v=ai(h.id),j.appendChild(v),et.set(h.id,v));const F=at(h);v.style.left=`${F.x}px`,v.style.top=`${F.y}px`,v.classList.toggle("cold",!h.live),v.classList.toggle("remote",h.remote===!0);const O=h.live&&h.remote!==!0?L.get(h.id):void 0;v.classList.toggle("is-working",(O==null?void 0:O.status)==="running");const ue=ri(O),Y=v.querySelector(".work");Y.textContent!==ue&&(Y.textContent=ue),v.classList.toggle("selected",t.isSelected(h.id)),v.setAttribute("aria-selected",String(t.isSelected(h.id)));const W=h.id.slice(5),z=ii(h,h.remote===!0?A.get(W)??[]:[],Z.get(W)),ce=h.remote===!0&&(h.id.startsWith("member-")?D.members.has(h.id):D.hosts.has(W));v.classList.toggle("is-unreachable",ce);const Ie=v.querySelector(".nm-text");Ie.textContent!==z.title&&(Ie.textContent=z.title);const _e=v.querySelector(".sub"),Me=ce?`未应答 · ${z.sub}`:z.sub;_e.textContent!==Me&&(_e.textContent=Me);const b=ce?`${z.tip} · ${Vt}`:z.tip;v.title!==b&&(v.title=b);const G=h.id.startsWith("member-")?he.get(h.remoteHandle??""):W;ci(v.querySelector(".prio"),h,h.remote===!0&&G!==void 0?B.get(G):void 0)}for(const[h,v]of[...et])a.has(h)||(v.remove(),et.delete(h));Re.refresh()}const l=ws(f,t);let A=new Map,L=new Map;const Z=new Map,B=new Map;let he=new Map;function ge(){l.render(J())}function pe(){s=En(vs(t.contentBounds(),l.placements()),k.clientWidth||Ke,k.clientHeight||ct),Ve(),M()}const ae=Fs({model:t,viewSize:xt,setVp:a=>{s=a},applyViewport:Ve,markDirty:M,cardEl:a=>et.get(a)});function Be(a){var F;const h=a.sessions.filter(O=>O.live!==!1).length,v=(O,ue,Y)=>{const W=document.createElement("b");W.className=ue,W.textContent=Y,O.replaceChildren(W)};v(xe,"ok",`● ${h} live`),v(le,"warn",`● ${a.sessions.length-h} cold`),te.textContent=`${((F=a.inFlight)==null?void 0:F.length)??0} inFlight`,Ne.className="mut",Ne.textContent=`${a.teams.length} 队 · ${a.peerCount} peer`}const Te=new Map,Ye=a=>{Te.set(a,(Te.get(a)??0)+1),r=-1},Rt=a=>{r=-1;const h=(Te.get(a)??0)-1;h<=0?Te.delete(a):Te.set(a,h)};let x=new Map,w=new Set,D=Qn({});const N=Us(e.now);function I(a){const h=a.type==="create-team"&&a.roster===!0||w.has(Pt(a));if(h&&a.type==="remove-team"&&!window.confirm(`解散网络队「${en(a.name)}」并撤回成员与白名单?`))return;if(h&&a.type==="reorder"){H("error","roster 队不支持置顶路由");return}if(h&&(a.type==="create-team"||a.type==="add-member")&&a.ids.some(Y=>{var W;return((W=t.getNode(Y))==null?void 0:W.remote)===!0||!se.some(z=>z.id===Y&&z.joined===!0)})){H("error","roster 只接受本机已入网会话,远端或未入网成员未写入");return}function v(Y){const W=z=>{var Ie;const ce=[];for(const _e of z){if(!_e.startsWith("peer-")&&!_e.startsWith("member-")){ce.push(_e);continue}const Me=(Ie=t.getNode(_e))==null?void 0:Ie.remoteHandle;Me!==void 0&&Me!==""&&ce.push(Me)}return ce};if(Y.type==="add-member"||Y.type==="remove-member"||Y.type==="create-team"){const z=W(Y.ids),ce=Y.type!=="create-team"?x.get(Y.team):void 0;return z.length>0||ce!==void 0?{...Y,ids:z,...ce!==void 0?{fleetPeer:ce}:{}}:Y}return Y.type==="remove-team"&&x.has(Y.name)?N.fork(Y,x.get(Y.name)):Y}if(a.type==="add-member"||a.type==="remove-member"||a.type==="reorder"||a.type==="create-team"){const Y=a.type==="reorder"?a.ops.map(W=>W.id):[...a.ids];for(const W of Y){if(W===""||W.startsWith("peer-")||W.startsWith("member-")||t.getNode(W)!==void 0)continue;const z=se.find(_e=>_e.id===W);if(z===void 0)continue;const ce=n==null?void 0:n.nodes[W],Ie=ce!==void 0?{x:ce.x,y:ce.y}:Nn(W);t.upsertNode({id:W,x:Ie.x,y:Ie.y,label:z.label,team:z.team,name:z.name,description:z.description,live:z.live!==!1,memberships:[],...z.teams!==void 0&&z.teams.length>0?{teams:z.teams}:{}})}}h&&a.type==="create-team"&&w.add(a.name);const F=Ms(t,a),O=Pt(a);Ye(O),Le();const ue=v(h?{...a,roster:!0}:a);e.onCanvasAction(ue).then(Y=>{Rt(O),!Y&&a.type==="create-team"&&h&&w.delete(O),n=Un(t,a,Y,F,n),M(),N.settled(ue,Y),Le()},()=>{Rt(O),a.type==="create-team"&&h&&w.delete(O),n=Un(t,a,!1,F,n),M(),N.settled(ue,!1),H("error","操作通道异常,已回滚"),Le()})}function H(a,h){var F;for(;qe.childElementCount>=3;)(F=qe.firstElementChild)==null||F.remove();const v=document.createElement("div");v.className="p-notice"+(a==="error"?" error":""),v.setAttribute("role",a==="error"?"alert":"status"),v.textContent=h,qe.appendChild(v),ne.push(setTimeout(()=>v.remove(),4e3))}const X=Is({root:k,netBtn:ie,signal:$,model:t,teams:()=>Ht,joined:a=>se.some(h=>h.id===a&&h.joined===!0),isRoster:a=>w.has(a),anchorFor:a=>(a.kind==="node"?et.get(a.id):Oe.get(a.name))??null,emit:I,notice:H});let se=[],ee=new Set;const ve=ni({root:k,trigger:$e,signal:$,model:t,facts:()=>({sessions:se,canvasMemberIds:ee,remoteTeams:A,teams:Ht}),menuItem:X.menuItem,emit:I,openNameDialog:X.openNameDialog,locate:De});function De(a){var F;const h=t.getNode(a);if(h===void 0)return;ve.close();const v=((F=e.viewSize)==null?void 0:F.call(e))??{w:k.clientWidth>0?k.clientWidth:800,h:k.clientHeight>0?k.clientHeight:600};s=kt({scale:s.scale,x:h.x-v.w/(2*s.scale),y:h.y-v.h/(2*s.scale)}),Ve(),r=-1,Le()}function q(a){const h=k.getBoundingClientRect();return{x:a.clientX-h.left,y:a.clientY-h.top}}function He(a,h){var Y;const v=q(h),F=mt(s,v.x,v.y),O=un(new Map(t.allFrames()),F),ue=a.ids.filter(W=>{const z=t.getNode(W);return z===void 0?!1:z.remote!==!0?!0:z.remoteHandle!==void 0&&z.remoteHandle!==""});if(O!==void 0){const W=ue.filter(z=>{var ce;return!((ce=t.getNode(z))!=null&&ce.memberships.some(Ie=>Ie.team===O))});if(W.length>0){I({type:"add-member",team:O,ids:W});return}if(a.ids.length===1&&((Y=t.getNode(a.clicked))==null?void 0:Y.remote)!==!0){const z=t.teamMemberIds(O),ce=Ss(t,O);ce.join("\0")!==z.join("\0")&&I({type:"reorder",team:O,ops:bn(z,ce)})}}if(a.rosterOrigin!==void 0&&O!==a.rosterOrigin){const W=a.ids.filter(z=>t.teamMemberIds(a.rosterOrigin).includes(z));W.length>0&&I({type:"remove-member",team:a.rosterOrigin,ids:W})}}function Ge(a){if(Ke===0&&ze(),X.isDialogOpen()){a.preventDefault();return}const h=a.target;if(X.isMenuOpen()){if(h!==null&&h.closest(".p-menu")!==null)return;X.closeMenu(),a.preventDefault();return}if(h!==null&&h.closest("button, input, textarea, select, .p-toolbar, .p-search, .p-lamp, .p-status, .p-legend")!==null)return;g=m.capture();const v=q(a);if(a.button===2){S={x:v.x,y:v.y,target:a.target,moved:!1},a.preventDefault();try{k.setPointerCapture(a.pointerId??0)}catch{}return}const F=h!==null?h.closest(".p-node"):null,O=h!==null?h.closest(".p-frame-head"):null;if((F!==null||O!==null)&&a.button===1){o={kind:"pan",last:v,moved:!1},a.preventDefault();try{k.setPointerCapture(a.pointerId??0)}catch{}return}const ue=h!==null?h.closest(".p-frame-handle"):null;if(p!==void 0){const Y=Oe.get(p),W=h!==null&&Y!==void 0&&(Y.contains(h)||Y===h),z=O!==null&&O.dataset.frame===p;!W&&!z&&R(void 0)}if(ue!==null&&a.button===0){const Y=ue.dataset.frame??"";R(Y);const W=t.getFrame(Y);if(W===void 0)return;o={kind:"frame-resize",name:Y,dir:ue.dataset.dir??"se",startRect:{...W},origin:mt(s,v.x,v.y),moved:!1},a.preventDefault();try{k.setPointerCapture(a.pointerId??0)}catch{}return}if(F!==null){if(a.button!==0)return;const Y=F.dataset.id??"",W=t.isSelected(Y);!W&&!a.shiftKey&&t.setSelection([Y]);const z=a.shiftKey||W?t.isSelected(Y)?t.selectedIds():[...t.selectedIds(),Y]:[Y],ce=un(new Map(t.allFrames()),mt(s,v.x,v.y));o={kind:"node",clicked:Y,ids:z,...ce&&w.has(ce)&&t.teamMemberIds(ce).includes(Y)?{rosterOrigin:ce}:{},last:mt(s,v.x,v.y),moved:!1,el:F},F.classList.add("dragging")}else if(O!==null){if(a.button!==0)return;const Y=O.dataset.frame??"";R(Y);const W=t.beginFrameDrag(Y);if(W===void 0)return;o={kind:"frame",name:Y,snap:W,origin:mt(s,v.x,v.y),moved:!1}}else if(a.button===1||a.button===0&&i)o={kind:"pan",last:v,moved:!1};else if(a.button===0)o={kind:"marquee",origin:v,last:v,additive:a.shiftKey},K.style.display="block",K.style.left=`${v.x}px`,K.style.top=`${v.y}px`,K.style.width="0px",K.style.height="0px";else return;a.preventDefault();try{k.setPointerCapture(a.pointerId??0)}catch{}}function Ze(a){const h=q(a);if(S!==null){const v=h.x-S.x,F=h.y-S.y;Math.hypot(v,F)>4&&(S.moved=!0,s=kt(dn(s,-v,-F)),S.x=h.x,S.y=h.y,Ve());return}if(o.kind==="node"){const v=mt(s,h.x,h.y),F=v.x-o.last.x,O=v.y-o.last.y;if(F===0&&O===0)return;o.moved=!0,o.last=v,t.dragNodes(o.ids,F,O);const ue=un(new Map(t.allFrames()),v),Y=o.ids.filter(z=>{const ce=t.getNode(z);return ce===void 0?!1:ce.remote!==!0||ce.remoteHandle!==void 0&&ce.remoteHandle!==""}),W=ue!==void 0&&Y.length>0;for(const[z,ce]of Oe)ce.classList.toggle("drop-target",W&&z===ue);Le()}else if(o.kind==="frame"){const v=mt(s,h.x,h.y),F=v.x-o.origin.x,O=v.y-o.origin.y;if(!o.moved&&F===0&&O===0)return;o.moved=!0,t.applyFrameDrag(o.snap,F,O),Le()}else if(o.kind==="frame-resize"){const v=mt(s,h.x,h.y),F=v.x-o.origin.x,O=v.y-o.origin.y;if(!o.moved&&F===0&&O===0)return;o.moved=!0;const ue=220,Y=120,W={...o.startRect};let{x:z,y:ce,w:Ie,h:_e}=W;o.dir.includes("e")&&(Ie=Math.max(ue,W.w+F)),o.dir.includes("s")&&(_e=Math.max(Y,W.h+O)),o.dir.includes("w")&&(Ie=Math.max(ue,W.w-F),z=W.x+(W.w-Ie)),o.dir.includes("n")&&(_e=Math.max(Y,W.h-O),ce=W.y+(W.h-_e)),t.setFrame(o.name,{x:Math.round(z),y:Math.round(ce),w:Math.round(Ie),h:Math.round(_e)}),Le()}else if(o.kind==="pan")s=dn(s,h.x-o.last.x,h.y-o.last.y),o.last=h,o.moved=!0,Ve();else if(o.kind==="marquee"){const v=Math.min(o.origin.x,h.x),F=Math.min(o.origin.y,h.y);K.style.left=`${v}px`,K.style.top=`${F}px`,K.style.width=`${Math.abs(h.x-o.origin.x)}px`,K.style.height=`${Math.abs(h.y-o.origin.y)}px`,o.last=h}}function dt(a){var h,v;if(S!==null){const F=S.moved,O=S.target,ue=S.x,Y=S.y;S=null,F?(C=!0,M()):O!==null&&X.contextMenuAt(ue,Y,O);return}if(o.kind==="node"){const F=o.clicked;if(o.moved)He(o,a),M();else if(a.shiftKey){const O=t.selectedIds();t.setSelection(O.includes(F)?O.filter(ue=>ue!==F):[...O,F])}else t.setSelection([F]),(h=e.onNodePick)==null||h.call(e,F);for(const O of Oe.values())O.classList.remove("drop-target");(v=o.el)==null||v.classList.remove("dragging"),Le()}else if(o.kind==="frame"||o.kind==="frame-resize")o.moved&&(Ut(t,new Set(x.keys()),o.name),M(),Le());else if(o.kind==="pan")o.moved&&M();else if(o.kind==="marquee"){const F=o.last,O=mt(s,Math.min(o.origin.x,F.x),Math.min(o.origin.y,F.y)),ue=mt(s,Math.max(o.origin.x,F.x),Math.max(o.origin.y,F.y));t.marqueeSelect({x:O.x,y:O.y,w:ue.x-O.x,h:ue.y-O.y},o.additive),K.style.display="none",Le()}o={kind:"none"},g!==void 0&&(m.commit(g),g=void 0);try{k.releasePointerCapture(a.pointerId??0)}catch{}}function Je(a){var v;for(const F of Oe.values())F.classList.remove("drop-target");o.kind==="node"&&((v=o.el)==null||v.classList.remove("dragging"));const h=o.kind==="pan"&&o.moved||(S==null?void 0:S.moved)===!0;K.style.display="none",S=null,o={kind:"none"},g!==void 0&&m.revert(g)&&Le(),h&&M(),g=void 0;try{k.releasePointerCapture(a.pointerId??0)}catch{}}function be(a){X.isMenuOpen()&&X.closeMenu();const h=q(a);if(a.shiftKey&&!a.ctrlKey){const v=a.deltaMode===1?16:1;s=kt(dn(s,(a.deltaX??0)*v,(a.deltaY??0)*v))}else{const v=a.deltaMode===1?16:1,F=Math.exp(-((a.deltaY??0)*v)*.0015);s=Bn(s,F,h.x,h.y)}a.preventDefault(),Ve(),M()}function Xe(a){ze(),s=Bn(s,a,Ke/2,ct/2),Ve(),M()}function ft(){m.undo()?(M(),Le()):H("info","没有可撤销的布局改动")}function St(){ze(),s=Kn(Wn(t.contentBounds(),t.allNodes()),Ke,ct,Xn(we,oe)),Ve(),M()}function Dt(a){var ue,Y,W,z,ce,Ie,_e;if(X.isDialogOpen()||((Y=(ue=a.target)==null?void 0:ue.closest)==null?void 0:Y.call(ue,".p-search"))!==null)return;if((a.ctrlKey||a.metaKey)&&a.key.toLowerCase()==="k"){a.preventDefault(),Re.focus();return}if((a.ctrlKey||a.metaKey)&&!a.shiftKey&&a.key.toLowerCase()==="z"){a.preventDefault(),ft();return}if(a.ctrlKey||a.metaKey)return;if(X.isMenuOpen()){a.key==="Escape"&&(X.closeMenu(),a.preventDefault());return}if(a.key===" "&&a.target===k){i=!0,a.preventDefault();return}const h=((z=(W=a.target)==null?void 0:W.closest)==null?void 0:z.call(W,".p-frame-head"))??null;if((a.key==="Enter"||a.key===" ")&&h!==null){a.preventDefault();const Me=h.getBoundingClientRect();X.contextMenuAt(Me.left+Me.width/2,Me.top+Me.height/2,h);return}if(a.key===" "&&((Ie=(ce=a.target)==null?void 0:ce.classList)!=null&&Ie.contains("p-node"))){const b=a.target.dataset.id??"",G=t.selectedIds();t.setSelection(G.includes(b)?G.filter(de=>de!==b):[...G,b]),Le(),a.preventDefault();return}if(a.key==="F10"&&a.shiftKey||a.key==="ContextMenu"){a.preventDefault();const Me=document.activeElement??null,b=((_e=Me==null?void 0:Me.getBoundingClientRect)==null?void 0:_e.call(Me))??{left:0,top:0,width:0,height:0};X.contextMenuAt(b.left+b.width/2,b.top+b.height/2,Me);return}if(a.key==="Delete"||a.key==="Backspace"){a.preventDefault(),yo();return}if(a.altKey&&(a.key==="ArrowUp"||a.key==="ArrowDown")){a.preventDefault(),wo(a.key==="ArrowUp");return}if(a.key==="g"||a.key==="G"){X.startCreateFromSelection();return}if(a.key==="0"){St();return}if(a.key==="="||a.key==="+"){Xe(1.2);return}if(a.key==="-"){Xe(1/1.2);return}if(a.key==="Escape"){if(X.isMenuOpen()){X.closeMenu(),Le();return}t.setSelection([]),Le();return}const v=a.shiftKey?40:8,O={ArrowLeft:[-v,0],ArrowRight:[v,0],ArrowUp:[0,-v],ArrowDown:[0,v]}[a.key];if(O!==void 0){a.preventDefault();const Me=t.selectedIds();if(Me.length>0){const b=m.capture();t.nudge(Me,O[0],O[1]),m.commit(b),Le(),M()}}}function yo(){var F,O,ue;const a=document.activeElement,h=((F=a==null?void 0:a.closest)==null?void 0:F.call(a,".p-frame-head"))??null,v=((O=a==null?void 0:a.closest)==null?void 0:O.call(a,".p-node"))??null;if(h!==null){I({type:"remove-team",name:h.dataset.frame??""});return}if(v!==null){const Y=v.dataset.id??"",W=((ue=t.getNode(Y))==null?void 0:ue.memberships.map(z=>z.team))??[];if(W.length===0){H("info","该节点未加入任何团队");return}if(W.length===1){I({type:"remove-member",team:W[0],ids:[Y]});return}X.openMenu(window.innerWidth/2,window.innerHeight/2,{kind:"node",id:Y},"leave",v);return}H("info","焦点不在节点或团队框上")}function wo(a){var Ie,_e;const h=document.activeElement,v=((Ie=h==null?void 0:h.closest)==null?void 0:Ie.call(h,".p-node"))??null;if(v===null)return;const F=v.dataset.id??"",O=((_e=t.getNode(F))==null?void 0:_e.memberships)??[];if(O.length!==1)return;const ue=O[0].team,Y=t.teamMemberIds(ue),W=Y.indexOf(F),z=a?Math.max(0,W-1):Math.min(Y.length-1,W+1);if(z===W)return;const ce=[...Y];ce.splice(W,1),ce.splice(z,0,F),I({type:"reorder",team:ue,ops:bn(Y,ce)})}function $n(a){a.key===" "&&(i=!1)}k.addEventListener("pointerdown",a=>Ge(a),$),k.addEventListener("pointermove",a=>Ze(a),$),k.addEventListener("pointerup",a=>dt(a),$),k.addEventListener("pointercancel",a=>Je(a),$),k.addEventListener("wheel",a=>be(a),{...$,passive:!1}),k.addEventListener("keydown",a=>Dt(a),$),k.addEventListener("keyup",a=>$n(a),$),k.addEventListener("contextmenu",a=>{var v,F;const h=a;if(((F=(v=h.target)==null?void 0:v.closest)==null?void 0:F.call(v,"input, textarea, .p-search"))==null){if(h.preventDefault(),C){C=!1;return}X.contextMenuAt(h.clientX,h.clientY,h.target)}},$);let Ht=[];function xo(a){var Me;Ht=a.teams,w=new Set([...a.teams.filter(b=>b.kind==="roster").map(b=>b.name),...[...w].filter(b=>Te.has(b))]),a.peers,l.setInFlight(a.inFlight??[]),se=a.sessions,X.setUnjoined(a.sessions.filter(b=>b.joined!==!0&&!b.id.startsWith("peer-")));const h=new Map;for(const b of a.teams)for(let G=0;G<b.members.length;G++){const de=b.members[G].id,Fe=h.get(de)??[];Fe.push({team:b.name,index:G}),h.set(de,Fe)}ee=new Set(h.keys());const v=new Set,F=b=>{const G=h.get(b)??[];if(Te.size===0)return G;const de=G.filter(Se=>!Te.has(Se.team)),Fe=[];for(const[Se]of Te){const ke=t.teamMemberIds(Se).indexOf(b);ke>=0&&Fe.push({team:Se,index:ke})}return[...de,...Fe]};let O;if(n===void 0&&!t.allNodes().some(b=>b.remote!==!0)){const b=a.sessions.filter(G=>{var de;return G.joined===!0&&(F(G.id).length>0||(((de=G.teams)==null?void 0:de.length)??0)>0)}).map(G=>G.id);ze(),O=ss(a.teams,b,Ke>0?Ke:void 0)}for(const b of a.sessions){if(b.joined!==!0)continue;const G=F(b.id);if(!(G.length===0&&(b.teams===void 0||b.teams.length===0)))if(v.add(b.id),t.getNode(b.id)!==void 0)t.upsertNode({id:b.id,x:t.getNode(b.id).x,y:t.getNode(b.id).y,label:b.label,team:b.team,name:b.name,description:b.description,live:b.live!==!1,memberships:G,...b.teams!==void 0&&b.teams.length>0?{teams:b.teams}:{}});else{const de=n==null?void 0:n.nodes[b.id];let Fe,Se;if(de!==void 0)Fe=de.x,Se=de.y;else{const me=(O==null?void 0:O.get(b.id))??Nn(b.id);Fe=me.x,Se=me.y}t.upsertNode({id:b.id,x:Fe,y:Se,label:b.label,team:b.team,name:b.name,description:b.description,live:b.live!==!1,memberships:G,...b.teams!==void 0&&b.teams.length>0?{teams:b.teams}:{}})}}for(const b of t.nodeIds())!v.has(b)&&!b.startsWith("peer-")&&!b.startsWith("member-")&&t.removeNode(b);const ue=new Set;A=ds(a.remoteTeams??[]),he=new Map([...A].flatMap(([b,G])=>G.map(de=>[de.team,b]))),L=new Map(a.sessions.map(b=>[b.id,{status:b.status,queued:b.queued}])),D=Qn(a),Z.clear();for(const b of a.peers??[])Z.set(rt(b.url),b.score),B.set(rt(b.url),{link:b.link,verified:b.verified});if(a.peers!==void 0){const b=t.contentBounds(),G=hs(a.peers,b);a.peers.forEach((de,Fe)=>{var At,vt,Mn,Sn;const Se=ls(de.url);ue.add(Se);const me=n==null?void 0:n.nodes[Se],ke=t.getNode(Se),Qe=ke!==void 0?{x:ke.x,y:ke.y}:me!==void 0?{x:me.x,y:me.y}:{x:((At=G[Fe])==null?void 0:At.x)??200,y:((vt=G[Fe])==null?void 0:vt.y)??0},tt=rt(de.url),gt=(Sn=(Mn=A.get(tt))==null?void 0:Mn.find(So=>So.team.includes("/")))==null?void 0:Sn.team;t.upsertNode({id:Se,x:Qe.x,y:Qe.y,label:de.url,team:"",name:de.url,peerUrl:de.url,remoteHandle:gt,live:!0,remote:!0,memberships:[]}),t.upsertScore(Se,de.score)})}for(const b of t.nodeIds())b.startsWith("peer-")&&!ue.has(b)&&t.removeNode(b);const Y=new Set;for(const[b,G]of A){const de=t.allNodes().find(me=>me.remote===!0&&rt(me.peerUrl??"")===b&&!me.id.startsWith("member-")),Fe=((de==null?void 0:de.x)??400)+190;let Se=(de==null?void 0:de.y)??0;for(const me of G){if(!me.team.includes("/"))continue;const ke=`member-${me.team.replace(/[^a-z0-9-]/gi,"-")}`;if(Y.has(ke))continue;Y.add(ke);const Qe=n==null?void 0:n.nodes[ke],tt=t.getNode(ke),gt=tt!==void 0?{x:tt.x,y:tt.y}:Qe!==void 0?{x:Qe.x,y:Qe.y}:{x:Fe,y:Se};Se+=92,t.upsertNode({id:ke,x:gt.x,y:gt.y,label:me.team,team:me.team,name:me.name!==void 0&&me.name!==""?me.name:me.team,remoteHandle:me.team,live:!0,remote:!0,memberships:[]})}}for(const b of t.nodeIds())b.startsWith("member-")&&!Y.has(b)&&t.removeNode(b);const W=new Map;for(const b of t.allNodes())b.remote!==!0||b.remoteHandle===void 0||b.remoteHandle===""||(!W.has(b.remoteHandle)||b.id.startsWith("member-"))&&W.set(b.remoteHandle,b.id);for(const b of a.teams){if(Te.has(b.name))continue;const G=[];for(const Se of b.members){const me=W.get(Se.id)??Se.id;t.getNode(me)!==void 0&&G.push(me)}const de=[...t.teamMemberIds(b.name)].sort().join("|"),Fe=[...G].sort().join("|");de!==Fe&&t.setTeamMembers(b.name,G)}const z=new Map,ce=x;if(x=z,a.peerOrg!==void 0){let b=0;const G=new Set(a.teams.map(de=>de.name));N.sweep(a.peerOrg);for(const de of a.peerOrg){const Fe=new Set((a.remoteTeams??[]).map(me=>me.team)),Se=xs(a.sessions,Fe);for(const me of de.org){if(me.name===""||G.has(me.name)||N.hides(me.name,de.url))continue;const ke=me.members.map(tt=>Cs(tt,Se,a.remoteTeams,de.url)).filter(tt=>t.getNode(tt)!==void 0),Qe=(Me=n==null?void 0:n.frames)==null?void 0:Me[me.name];t.getFrame(me.name)===void 0&&t.setFrame(me.name,Qe!==void 0?{x:Qe.x,y:Qe.y,w:Qe.w,h:Qe.h}:{x:200+b%4*280,y:940+Math.floor(b/4)*160,w:250,h:130}),t.setTeamMembers(me.name,ke),z.set(me.name,de.url),b+=1}}}for(const[b,G]of ce)z.has(b)||t.removeFrame(b);const Ie=new Map;for(const b of t.allNodes())b.remote===!0&&b.remoteHandle!==void 0&&b.remoteHandle!==""&&Ie.set(b.remoteHandle,b.id);const _e=po(a.teams);for(const b of a.teams){if(t.getFrame(b.name)!==void 0||Te.has(b.name))continue;const G=n==null?void 0:n.frames[b.name];if(G!==void 0){t.setFrame(b.name,{...G});continue}const de=[],Fe=[];for(const me of b.members){const ke=t.getNode(Ie.get(me.id)??me.id);ke!==void 0&&(Fe.push(at(ke)),_e.get(ke.id)===b.name&&de.push(at(ke)))}const Se=de.length>0?de:Fe;Se.length>0&&t.setFrame(b.name,Cn(Se))}for(const[b]of t.allFrames())Te.has(b)||!a.teams.some(G=>G.name===b)&&!z.has(b)&&t.removeFrame(b);if(ve.refresh(),Be(a),!d&&o.kind==="none"&&t.nodeIds().length>0){d=!0;const b=m.capture();Ut(t,new Set(x.keys())),m.commit(b)}c&&o.kind==="none"&&t.nodeIds().length>0&&(ze(),Ke>0&&ct>0&&(c=!1,s=Kn(Wn(t.contentBounds(),t.allNodes()),Ke,ct,Xn(we,oe)))),Le()}function Nn(a){return ho(a,t.allNodes().filter(h=>h.remote!==!0))}function Co(a){const h=mo(a);if(h===void 0)return;n=h;const v=e.viewportStore;if(v===void 0)c=!1,o.kind==="none"&&(s=kt({...h.viewport}));else if(!u&&o.kind==="none"){u=!0;const F=v.get(E());F!=null&&Number.isFinite(F.x)&&Number.isFinite(F.y)&&Number.isFinite(F.scale)?(s=kt({x:F.x,y:F.y,scale:F.scale}),c=!1):E()==="wide"&&(s=kt({...h.viewport}),c=!1)}r=-1,Le()}function ko(){return es(s,t.positions(),t.frameRects())}function Eo(a,h){re.classList.remove("pending","saved","error"),a==="pending"&&re.classList.add("pending"),a==="saved"&&re.classList.add("saved"),a==="error"&&re.classList.add("error"),a==="error"?(re.tabIndex=0,re.setAttribute("role","button"),re.setAttribute("aria-label","布局保存失败,按回车重试")):(re.removeAttribute("tabindex"),re.removeAttribute("role"),re.removeAttribute("aria-label"));const v=a==="pending"?"布局待保存…":a==="saved"?`布局已保存 ${h??""}`.trimEnd():a==="error"?"保存失败 · 回车重试":"布局";Q.textContent=v}function $o(){k.style.display="block",ze(),Ve(),k.focus()}function No(){k.style.display="none"}function Mo(){fe.abort();for(const a of ne)clearTimeout(a)}return{root:k,reconcile:xo,activate:$o,deactivate:No,adoptExternalLayout:Co,snapshotDoc:ko,setLamp:Eo,notice:H,destroy:Mo,seam:{pointerDown:Ge,pointerMove:Ze,pointerUp:dt,pointerCancel:Je,wheel:be,key:Dt,keyUp:$n,contextMenu(a){a.preventDefault(),X.contextMenuAt(a.clientX,a.clientY,a.target)}}}}const fi=800;function pi(e){let t,n=!1,s=!1,o=!1,i="idle";function r(g){g!==i&&(i=g,e.onLamp(g))}function c(){t!==void 0&&(t(),t=void 0)}function d(){c(),t=e.schedule(()=>{t=void 0,m(!1)},fi)}async function m(g){if(n){s=!0;return}n=!0,c();const u=e.snapshot();let E;try{E=JSON.stringify(u)}catch{n=!1,r("error"),e.onHttpError("layout document is not JSON-serializable");return}try{const M=await e.send(u,{keepalive:g});if(M===null||typeof M!="object"||M.ok!==!0){r("error");const J=M!==null&&typeof M=="object"&&typeof M.error=="string"?M.error:"layout save refused";e.onHttpError(`layout save: ${J}`)}else JSON.stringify(e.snapshot())===E&&(e.onAdopt(M.layout),r("saved"))}catch(M){r("error"),e.onHttpError(`layout save unreachable: ${String((M==null?void 0:M.message)??M).slice(0,80)}`)}if(n=!1,i==="error"){o=!1;return}if(o){o=!1,m(!0);return}s&&(s=!1,r("pending"),d())}return{markDirty(){if(s=!1,r("pending"),n){s=!0;return}d()},retry(){i!=="error"||n||(r("pending"),m(!1))},flush(){if(n){o=!0,s=!0;return}c(),m(!0)},isBusy(){return n},dispose(){c()}}}function Zn(e){return e!=null&&typeof e.error=="string"?e.error:void 0}function hi(e){const t=new Map;let n=0;function s(r,c){n+=1;const d=t.get(r)??Promise.resolve(),m=async()=>{try{return await c()}finally{n-=1}},g=d.then(m,m);return t.set(r,g.then(()=>{},()=>{})),g}async function o(r){var fe,ne,S,C;let c;try{c=await e.send(r)}catch($){const k=String(($==null?void 0:$.message)??$).slice(0,80);return e.onNotice("error",`画布不可达:${k}`),!1}if(c.status>=400)return e.onNotice("error",Zn(c.body)??`HTTP ${c.status}`),!1;if(((fe=c.body)==null?void 0:fe.ok)===!0)return!0;const d=c.body!=null&&typeof c.body=="object"?Zn(c.body):void 0;if(d!==void 0)return e.onNotice("error",d),!1;const m=String(r.name??""),g=String(r.id??""),u=(ne=c.body)==null?void 0:ne.members,E=(S=c.body)==null?void 0:S.teams,M=Array.isArray(u)&&u.every($=>typeof $=="string"),J=Array.isArray(E)&&E.every($=>typeof $=="string");return((C=c.body)==null?void 0:C.ok)===!1&&(r.action==="add-member"&&M&&u.includes(g)||r.action==="remove-member"&&M&&!u.includes(g)||r.action==="remove"&&J&&!E.includes(m))?!0:(e.onNotice("error","画布操作未确认:"+String(r.action)+"(可能队伍/成员已达上限,请重试)"),!1)}async function i(r,c){for(const d of c)if(!await o({...r,id:d}))return!1;return!0}return{createTeam(r,c){return s(r,async()=>{if(!await o({action:"create",name:r}))return!1;for(let d=0;d<c.length;d++)if(!await o({action:"add-member",name:r,id:c[d]}))return!1;return!0})},addMembers(r,c){return s(r,()=>i({action:"add-member",name:r},c))},removeMembers(r,c){return s(r,()=>i({action:"remove-member",name:r},c))},runRosterOps(r,c){return s(r,async()=>{for(const d of c){const m=d.op==="remove"?{action:"remove-member",name:r,id:d.id}:{action:"add-member",name:r,id:d.id};if(!await o(m))return!1}return!0})},removeTeam(r){return s(r,()=>o({action:"remove",name:r}))},hasPending(){return n>0}}}const vo=document.getElementById("app"),bi=(()=>{try{return window.sessionStorage.getItem("dsh_a2a_key")??""}catch{return""}})(),gn=window.__DSH_A2A_KEY__??bi,Ue=(e,t={})=>fetch(e,{...t,headers:{...t.headers??{},...gn!==""?{"X-API-Key":gn}:{}}});gn===""&&Ue("/__dsh_a2a/owner-settings").then(e=>{if(e.status!==401)return;const t=document.createElement("div");t.className="nexus-keygate",t.style.cssText="position:fixed;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:10px;background:#fff;z-index:9999;font:14px/1.5 system-ui,sans-serif;color:#000";const n=document.createElement("div");n.textContent="此面板经远程面打开(密钥不随页面下发)——输入控制密钥";const s=document.createElement("input");s.type="password",s.placeholder="X-API-Key",s.setAttribute("aria-label","控制密钥");const o=document.createElement("button");o.type="button",o.textContent="进入面板";const i=document.createElement("div");i.style.color="#b00";const r=()=>{const c=s.value.trim();c!==""&&fetch("/__dsh_a2a/owner-settings",{headers:{"X-API-Key":c}}).then(d=>{if(d.status===401){i.textContent="密钥不对,再试一次";return}try{window.sessionStorage.setItem("dsh_a2a_key",c)}catch{}window.location.reload()})};o.addEventListener("click",r),s.addEventListener("keydown",c=>{c.key==="Enter"&&r()}),t.append(n,s,o,i),document.body.appendChild(t)});const Ot=document.createElement("div");Ot.className="nexus-fault";vo.appendChild(Ot);const{fault:vn,clear:gi}=Io({show(e){Ot.textContent=e,Ot.style.display="block"},hide(){Ot.style.display="none"}},e=>console.error(e));let Gt;const eo="dsh-a2a.viewport.",vi={get(e){try{const t=window.localStorage.getItem(eo+e);return t===null?void 0:JSON.parse(t)}catch{return}},set(e,t){try{window.localStorage.setItem(eo+e,JSON.stringify({x:t.x,y:t.y,scale:t.scale}))}catch{}}},ot=mi({viewportStore:vi,onDirty:()=>nn.markDirty(),onLampClick:()=>nn.retry(),onNodePick:e=>{Gt==null||Gt(e)},onCanvasAction:async e=>{const t=await wi(e);return rn(),t}}),nn=pi({snapshot:()=>ot.snapshotDoc(),send:(e,t)=>Ue("/__dsh_a2a/canvas-layout",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"save",layout:e}),cache:"no-store",keepalive:t.keepalive}).then(async n=>await n.json()),schedule:(e,t)=>{const n=setTimeout(e,t);return()=>{clearTimeout(n)}},now:Date.now,onLamp:e=>{yn=e;const t=new Date,n=String(t.getHours()).padStart(2,"0")+":"+String(t.getMinutes()).padStart(2,"0");ot.setLamp(e,n)},onAdopt:e=>{ot.adoptExternalLayout(e)},onHttpError:e=>vn(e)});window.addEventListener("pagehide",()=>nn.flush());const Tt=hi({send:async e=>{const t=await Ue("/__dsh_a2a/canvas",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),cache:"no-store"});return{status:t.status,body:await t.json()}},onNotice:(e,t)=>ot.notice(e,t)});async function to(e,t,n,s){const o=await Ue("/__dsh_a2a/fleet-canvas-op",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({peer:e,op:t,name:n,...s!==void 0?{id:s}:{}}),cache:"no-store"}).catch(()=>{}),i=o!==void 0?await o.json().catch(()=>{}):void 0;return(i==null?void 0:i.ok)===!0&&t==="remove"&&i.removed===!1&&ot.notice("info",`属主那边已没有「${n}」,无需解散`),(i==null?void 0:i.ok)===!0?!0:(ot.notice("error",`跨主机团队操作失败:${String((i==null?void 0:i.error)??"对端无响应")}`),!1)}async function yi(e,t){const n=async s=>{const o=await Ue("/__dsh_a2a/roster-op",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,...s})}).catch(()=>{}),i=o!==void 0?await o.json().catch(()=>{}):void 0;return o!=null&&o.ok&&(i==null?void 0:i.ok)===!0?!0:(ot.notice("error",`roster 操作失败:${String((i==null?void 0:i.error)??(o?`HTTP ${o.status}`:"无响应"))}`),!1)};if(e.type==="create-team")return n({op:"create",ids:e.ids});if(e.type==="remove-team")return n({op:"dissolve"});if(e.type==="add-member"||e.type==="remove-member"){for(const s of e.ids)if(!await n({op:e.type,id:s}))return!1;return!0}return ot.notice("error","roster 不支持该操作"),!1}function wi(e){if((e.type==="add-member"||e.type==="remove-member")&&e.fleetPeer!==void 0){const t=e.fleetPeer;return(async()=>{for(const n of e.ids)if(!await to(t,e.type,e.team,n))return!1;return!0})()}if(e.type==="remove-team"&&e.fleetPeer!==void 0)return to(e.fleetPeer,"remove",e.name);if(e.roster===!0){const t=go(Pt(e));return t!==void 0?yi(e,t):(ot.notice("error","无效的 roster 框名"),Promise.resolve(!1))}switch(e.type){case"create-team":return Tt.createTeam(e.name,e.ids,e.created);case"add-member":return Tt.addMembers(e.team,e.ids);case"remove-member":return Tt.removeMembers(e.team,e.ids);case"remove-team":return Tt.removeTeam(e.name);case"reorder":return Tt.runRosterOps(e.team,e.ops);case"join-network":case"leave-network":return xi(e.type==="join-network"?"join":"leave",e.id)}}async function xi(e,t){const n=await Ue(`/__dsh_a2a/${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:t}),cache:"no-store"}).catch(()=>{});return n!==void 0&&n.ok}let yn="idle",mn=new Set,no=0,on=!1,oo,fn=!1;async function Ci(){const e=await Ue("/__dsh_a2a/canvas-layout",{cache:"no-store"});if(!e.ok)throw new Error(`/__dsh_a2a/canvas-layout ${e.status}`);return(await e.json()).layout??null}const sn=Qo({now:()=>Date.now(),async ownerSettingsRead(){try{return await(await Ue("/__dsh_a2a/owner-settings")).json()}catch{return{ok:!1,error:"unreachable"}}},async ownerSettingsWrite(e){try{return await(await Ue("/__dsh_a2a/owner-settings",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})).json()}catch{return{ok:!1,error:"unreachable"}}},async abandon(e,t){try{return(await Ue("/__dsh_a2a/tasks/abandon",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({task_id:e,reason:t})})).ok}catch{return!1}},copy(e){var t;(t=navigator.clipboard)==null||t.writeText(e).catch(()=>{})},async cliHomeOptions(e,t){try{const n=await Ue("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:"cli-home-options",name:e,id:t})}),s=await n.json();return{...s,ok:n.ok&&s.ok===!0}}catch(n){return{ok:!1,error:String(n)}}},async canvasOp(e,t,n,s){try{const o=await Ue("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:e,name:t,id:n,...s===void 0?{}:{home:s}})}),i=await o.json().catch(()=>({}));return o.ok&&i.ok!==!1?!0:(console.warn(`canvas ${e} ${t}: ${i.error??`HTTP ${o.status}`}`),!1)}catch{return!1}},async dispatchQuick(e,t){try{const n=await Ue("/__dsh_a2a/dispatch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({team:e,message:t})}),s=await n.json().catch(()=>({}));return{ok:n.ok&&s.ok===!0,task_id:s.task_id,reply:s.reply,error:s.error??`HTTP ${n.status}`}}catch(n){return{ok:!1,error:n instanceof Error?n.message:String(n)}}},async sessionLog(e){try{const t=await Ue(`/__dsh_a2a/session-log?id=${encodeURIComponent(e)}`),n=await t.json().catch(()=>({}));return{ok:t.ok&&n.ok===!0,messages:n.messages,error:n.error??(t.ok?void 0:`HTTP ${t.status}`)}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}},async sessionLogList(){try{const e=await Ue("/__dsh_a2a/session-log"),t=await e.json().catch(()=>({}));return{ok:e.ok&&t.ok===!0,sessions:t.sessions,count:typeof t.count=="number"?t.count:void 0,error:t.error??(e.ok?void 0:`HTTP ${e.status}`)}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}},async remoteSessionLog(e){try{const t=new URLSearchParams;e.team!==void 0&&t.set("team",e.team),e.peer!==void 0&&t.set("peer",e.peer),e.id!==void 0&&t.set("id",e.id),e.limit!==void 0&&t.set("limit",String(e.limit));const n=await Ue(`/__dsh_a2a/remote-session-log?${t.toString()}`),s=await n.json().catch(()=>({}));return{ok:n.ok&&s.ok===!0,messages:s.messages,sessions:s.sessions,error:s.error??(n.ok?void 0:`HTTP ${n.status}`),via:s.via}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}},board:{root:ot.root,activate:()=>ot.activate(),deactivate:()=>ot.deactivate(),available:()=>on}});vo.appendChild(sn.root);Gt=e=>{sn.openNodeChat(e)};if(new URLSearchParams(window.location.search).get("mode")==="plan"){const e=window.setInterval(()=>{on&&(sn.selectTab("board"),window.clearInterval(e))},300);window.setTimeout(()=>window.clearInterval(e),3e4)}async function rn(){var e,t;try{const n=await Ue("/__dsh_a2a/state",{cache:"no-store"});if(!n.ok)throw new Error(`/__dsh_a2a/state ${n.status}`);const s=await n.json();if(Date.now()-no>=15e3){no=Date.now();try{const u=await Ue("/__dsh_a2a/nodes",{cache:"no-store"});if(!u.ok)throw new Error(`nodes ${u.status}`);const E=await u.json();mn=new Set((E.bindings??[]).filter(M=>M.mounted!==null&&M.effective!==null&&(M.effective.core===!1||M.effective.mutable===!1)).map(M=>M.session))}catch{mn.clear()}}const o={...s,sessions:(s.sessions??[]).map(u=>({...u,...mn.has(u.id)?{nodePromptMissing:!0}:{}}))};if(gi(),on=s.canvas!==void 0,sn.update(o),on)try{const u=await Ci(),E=JSON.stringify(u??null);E!==oo&&(oo=E,(yn==="idle"||yn==="saved")&&!nn.isBusy()&&ot.adoptExternalLayout(u)),fn=!0}catch(u){if(!fn){vn(`canvas layout 获取失败:${String(u)}`);return}}else fn=!0;const i=(s.sessions??[]).map(u=>({...u,joined:u.joined===!0})),r=[...((e=s.canvas)==null?void 0:e.teams)??[],...Ls(((t=s.registry)==null?void 0:t.teams)??[],s.zone??"")],c=(s.peers??[]).filter(u=>u!==null&&typeof u=="object"&&typeof u.url=="string"),d=(s.inFlight??[]).filter(u=>u!==null&&typeof u=="object"&&typeof u.team=="string"&&typeof u.peer=="string"&&Number.isFinite(u.startedAt)),m=(s.remote??[]).filter(u=>u!==null&&typeof u=="object"&&typeof u.team=="string"&&typeof u.via=="string").map(u=>({team:u.team,name:u.name,via:u.via,workspace:u.workspace,...u.unreachable===!0?{unreachable:!0}:{},...typeof u.origin=="string"&&u.origin!==""?{origin:u.origin}:{},...Array.isArray(u.teams)&&u.teams.length>0?{teams:u.teams.filter(E=>typeof E=="string"&&E!=="")}:{}})),g=(s.peerOrg??[]).filter(u=>u!==null&&typeof u=="object").filter(u=>typeof u.url=="string"&&Array.isArray(u.org)).map(u=>({url:u.url,...u.unreachable===!0?{unreachable:!0}:{},org:u.org.filter(E=>E!==null&&typeof E=="object").filter(E=>typeof E.team=="string"&&E.team!=="").map(E=>({name:typeof E.name=="string"?E.name:"",team:E.team,members:Array.isArray(E.members)?E.members.filter(M=>typeof M=="string"):[]}))}));ot.reconcile({sessions:i,teams:r,peerCount:c.length,peers:c,inFlight:d,remoteTeams:m,peerOrg:g})}catch(n){vn(`state 轮询失败:${String(n)}`)}}rn();window.setInterval(()=>{document.hidden||rn()},3e3);document.addEventListener("visibilitychange",()=>{document.hidden||rn()});
@@ -18,7 +18,7 @@ html,body{margin:0;height:100%;background:var(--n-bg);color:var(--n-ink);
18
18
  font-family:"Segoe UI",system-ui,sans-serif;}
19
19
  #app{width:100%;height:100%;position:relative}
20
20
  </style>
21
- <script type="module" crossorigin src="./assets/index-D2BGWOeU.js"></script>
21
+ <script type="module" crossorigin src="./assets/index-DIQNDBy6.js"></script>
22
22
  <link rel="stylesheet" crossorigin href="./assets/index-z33lHtLw.css">
23
23
  </head>
24
24
  <body>
package/cli/a2a.mjs CHANGED
@@ -959,13 +959,15 @@ async function main() {
959
959
  const pending = s.tasks ?? []
960
960
  const dead = s.tasksDead ?? []
961
961
  const archived = s.archived ?? []
962
- if (has('--json')) { console.log(JSON.stringify({ pending, dead, archived }, null, 2)); return }
962
+ if (has('--json')) { console.log(JSON.stringify({ pending, dead, archived, ...(s.settlement === undefined ? {} : { settlement: s.settlement }), ...(s.conservation === undefined ? {} : { conservation: s.conservation }) }, null, 2)); return }
963
963
  console.log(`pending (${String(pending.length)}):`)
964
964
  listCapped(pending, 20, t => ` ${String(t.taskId)} → ${String(t.team)} ${String(t.direction ?? '')}${senderTail(t)}`)
965
965
  console.log(`dead (${String(dead.length)}):`)
966
966
  listCapped(dead, 20, t => ` ${String(t.taskId)} → ${String(t.team)} (${String(t.reason ?? '?')})${senderTail(t)}`)
967
967
  console.log(`archived (${String(archived.length)}):`)
968
968
  listCapped(archived, 20, t => ` ${String(t.taskId)} ${String(t.outcome ?? '?')}${t.endedBy !== undefined && t.endedBy !== 'receipt' ? ` (${String(t.endedBy)})` : ''}${senderTail(t)}`)
969
+ if (s.conservation != null) console.log(`conservation: conserved=${String(s.conservation.conserved)} delta=${String(s.conservation.delta)}`)
970
+ if (s.settlement != null) console.log(`settlement: mode=${String(s.settlement.mode)} refusalsTotal=${String(s.settlement.refusalsTotal)} offChain=${String(s.settlement.offChain)}`)
969
971
  return
970
972
  }
971
973
  if (cmd === 'status') {
@@ -64,6 +64,7 @@ export class CanvasStore {
64
64
  /** Team entries in creation order; members in add order (routing priority). */
65
65
  teams = [];
66
66
  cliHome = {};
67
+ cliHomeCleared = new Set();
67
68
  path;
68
69
  listeners = new Set();
69
70
  /**
@@ -111,6 +112,11 @@ export class CanvasStore {
111
112
  this.cliHome[handle] = home;
112
113
  }
113
114
  }
115
+ if (Array.isArray(snapshot.cliHomeCleared))
116
+ for (const handle of snapshot.cliHomeCleared) {
117
+ if (typeof handle === 'string' && CLI_HANDLE_RE.test(handle) && this.cliHome[handle] === undefined && this.teams.some(t => t.members.includes(handle)))
118
+ this.cliHomeCleared.add(handle);
119
+ }
114
120
  }
115
121
  catch {
116
122
  // A corrupt store is not fatal: the user re-creates teams; routing
@@ -132,7 +138,7 @@ export class CanvasStore {
132
138
  return;
133
139
  try {
134
140
  mkdirSync(dirname(this.path), { recursive: true });
135
- const snapshot = { teams: this.teams.map(t => ({ name: t.name, members: [...t.members] })), ...(Object.keys(this.cliHome).length > 0 ? { cliHome: { ...this.cliHome } } : {}) };
141
+ const snapshot = { teams: this.teams.map(t => ({ name: t.name, members: [...t.members] })), ...(Object.keys(this.cliHome).length > 0 ? { cliHome: { ...this.cliHome } } : {}), ...(this.cliHomeCleared.size > 0 ? { cliHomeCleared: [...this.cliHomeCleared] } : {}) };
136
142
  writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 });
137
143
  }
138
144
  catch {
@@ -156,6 +162,15 @@ export class CanvasStore {
156
162
  /** Frozen, operator-linked CLI home; undefined means unknown or ambiguous. */
157
163
  cliHomeOf(handle) { return this.cliHome[handle]; }
158
164
  pinnedCliHomes() { return Object.entries(this.cliHome); }
165
+ wasCliHomeCleared(handle) { return this.cliHomeCleared.has(handle); }
166
+ clearCliHomeForLocal(handle) {
167
+ if (this.cliHome[handle] === undefined)
168
+ return false;
169
+ delete this.cliHome[handle];
170
+ this.cliHomeCleared.add(handle);
171
+ this.persist();
172
+ return true;
173
+ }
159
174
  /**
160
175
  * The named team's remote member refs (zone-qualified team handles) in
161
176
  * routing-priority order — the failover tier after the local live/cold
@@ -218,6 +233,7 @@ export class CanvasStore {
218
233
  if (entry.members.includes(id)) {
219
234
  if (home !== undefined && this.cliHome[id] !== home) {
220
235
  this.cliHome[id] = home;
236
+ this.cliHomeCleared.delete(id);
221
237
  this.persist();
222
238
  }
223
239
  return true;
@@ -225,8 +241,10 @@ export class CanvasStore {
225
241
  if (entry.members.length >= CANVAS_MEMBER_CAP)
226
242
  return false;
227
243
  entry.members.push(id);
228
- if (home !== undefined)
244
+ if (home !== undefined) {
229
245
  this.cliHome[id] = home;
246
+ this.cliHomeCleared.delete(id);
247
+ }
230
248
  this.persist();
231
249
  return true;
232
250
  }
@@ -246,8 +264,10 @@ export class CanvasStore {
246
264
  return true;
247
265
  }
248
266
  forgetUnusedHome(id) {
249
- if (!this.teams.some(team => team.members.includes(id)))
267
+ if (!this.teams.some(team => team.members.includes(id))) {
250
268
  delete this.cliHome[id];
269
+ this.cliHomeCleared.delete(id);
270
+ }
251
271
  }
252
272
  /**
253
273
  * Drop one id from every team — the leave and archive paths: a session