@nelsonlongxiang/dsh-open-a2a-net 0.6.42 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/README_zh.md +1 -0
- package/assets/nexusDist/assets/index-CA_YZTWG.js +9 -0
- package/assets/nexusDist/index.html +1 -1
- package/cli/a2a.mjs +43 -7
- package/lib/a2a-client.js +10 -2
- package/lib/admitted-bookkeeping.js +7 -0
- package/lib/canvas-discovery.js +11 -1
- package/lib/canvas-member.js +43 -0
- package/lib/canvas-store.js +315 -17
- package/lib/client.js +62 -72
- package/lib/client.js.map +1 -1
- package/lib/config.js +1 -0
- package/lib/direct-forward.js +9 -7
- package/lib/faces/admin.js +150 -67
- package/lib/faces/files.js +2 -1
- package/lib/faces/wire.js +569 -469
- package/lib/followup-delivery.js +89 -0
- package/lib/idempotency-store.js +8 -0
- package/lib/inbound-verification.js +23 -0
- package/lib/index.js +534 -166
- package/lib/nudge-observability.js +34 -0
- package/lib/origin-claim.js +36 -9
- package/lib/outbound-scope.js +4 -2
- package/lib/peer-directory.js +15 -5
- package/lib/peer-store.js +31 -17
- package/lib/queue-guard.js +4 -3
- package/lib/receipt-ladder.js +1 -1
- package/lib/receipt-retry.js +58 -0
- package/lib/reconciliation-reminders.js +148 -0
- package/lib/reconciliation-state.js +27 -0
- package/lib/session-transcript.js +46 -18
- package/lib/skill/SKILL.md +5 -0
- package/lib/skill/references/cli.md +4 -1
- package/lib/skill/references/routing.md +31 -1
- package/lib/task-auth.js +3 -3
- package/lib/task-delivery.js +23 -0
- package/lib/task-ledger-persistence.js +51 -0
- package/lib/task-ledger-rows.js +5 -0
- package/lib/task-ledger.js +219 -20
- package/lib/tools/info.js +37 -7
- package/lib/types/a2a-client.d.ts +1 -1
- package/lib/types/admitted-bookkeeping.d.ts +5 -0
- package/lib/types/canvas-member.d.ts +10 -0
- package/lib/types/canvas-store.d.ts +56 -1
- package/lib/types/config.d.ts +2 -0
- package/lib/types/faces/admin.d.ts +21 -0
- package/lib/types/faces/files.d.ts +1 -1
- package/lib/types/faces/wire.d.ts +21 -14
- package/lib/types/followup-delivery.d.ts +25 -0
- package/lib/types/idempotency-store.d.ts +2 -0
- package/lib/types/inbound-verification.d.ts +14 -0
- package/lib/types/index.d.ts +5 -0
- package/lib/types/nudge-observability.d.ts +17 -0
- package/lib/types/origin-claim.d.ts +7 -1
- package/lib/types/outbound-scope.d.ts +1 -0
- package/lib/types/peer-store.d.ts +4 -0
- package/lib/types/queue-guard.d.ts +9 -2
- package/lib/types/receipt-ladder.d.ts +2 -0
- package/lib/types/receipt-retry.d.ts +22 -0
- package/lib/types/reconciliation-reminders.d.ts +41 -0
- package/lib/types/reconciliation-state.d.ts +32 -0
- package/lib/types/session-transcript.d.ts +6 -1
- package/lib/types/task-auth.d.ts +7 -0
- package/lib/types/task-delivery.d.ts +17 -0
- package/lib/types/task-ledger-persistence.d.ts +3 -0
- package/lib/types/task-ledger-rows.d.ts +19 -2
- package/lib/types/task-ledger.d.ts +48 -1
- package/lib/types/tools/info.d.ts +3 -0
- package/lib/types/types.d.ts +3 -0
- package/nexus-stage/src/federation.ts +1 -0
- package/nexus-stage/src/main.ts +17 -2
- package/nexus-stage/src/member-provenance-picker.ts +13 -0
- package/nexus-stage/src/planning-member-handles.ts +4 -1
- package/nexus-stage/src/planning-view.ts +3 -3
- package/nexus-stage/src/workbench.ts +51 -7
- package/nexus-stage/src/world.ts +1 -0
- package/package.json +3 -2
- package/assets/nexusDist/assets/index-NkNSeIx6.js +0 -9
package/README.md
CHANGED
|
@@ -170,6 +170,7 @@ Row config overlays in the profile's patch layers; every key has a schema defaul
|
|
|
170
170
|
| `originClaimWindow` | `true` | OriginClaim transition window: deliveries without an envelope still walk the caller gate (counted as `legacyInbound`); `false` rejects them like failed signatures. |
|
|
171
171
|
| `originClaimUrl` | `''` | Override for the envelope's `callerUrl` (pocket-tunneled hosts); `''` derives it. |
|
|
172
172
|
| `originClaimUnboundWindow` | `true` | OriginClaim audience-binding window: also sign and accept the legacy envelope that names no audience (accepted ones counted as `unboundInbound`; those whose bound form missed here also as `boundMissInbound`); `false` signs and accepts the audience-bound form only. |
|
|
173
|
+
| `canvasLegacyBareMembers` | `true` | #335 migration window: unsuspended historical bare canvas members retain legacy inference; suspended slots never route or authorize. Close only by owner decision after migration and observation. |
|
|
173
174
|
| `originClaimAudiences` | `[]` | Extra origins this host answers to as an envelope audience, for port-mapped or proxied hosts whose dialed URL is neither the card URL nor the arrival address. |
|
|
174
175
|
| `linkKeys` | `{}` | Link keys (#317): `{ "<peer origin>": "<key>" }`. Requests to that peer carry this key instead of `apiKey`; this host's peer faces (`/a2a/direct`, `/a2a/log`, `/a2a/blob`) accept it only from a caller whose OriginClaim verifies and names that origin (a POST must be Sig3 body-bound); the control routes never accept it. Lets a public node link to the fleet without holding the fleet apiKey; both ends configure the same link key. |
|
|
175
176
|
| `gateAgentLessCalls` | `'audit'` | Host-side (agent-less) routing calls: audit them, or `refuse`. |
|
package/README_zh.md
CHANGED
|
@@ -128,6 +128,7 @@ a2a-net inbox --new # 未读来信;--wait
|
|
|
128
128
|
| `originClaimWindow` | `true` | OriginClaim 过渡窗口:不带信封的投递仍走调用方门(计入 `legacyInbound`);`false` 则按验签失败拒绝。 |
|
|
129
129
|
| `originClaimUrl` | `''` | 信封 `callerUrl` 的覆盖值(pocket 隧道宿主用);`''` 自动推导。 |
|
|
130
130
|
| `originClaimUnboundWindow` | `true` | OriginClaim 受众绑定过渡窗口:同时签发并接受不带受众的旧信封(接受数计入 `unboundInbound`;其中绑定形式在本机未命中的另计 `boundMissInbound`);`false` 只签发、只接受绑定受众的信封。 |
|
|
131
|
+
| `canvasLegacyBareMembers` | `true` | #335 迁移窗口:未暂停历史裸成员保持旧推断;暂停槽位永不路由/授权,迁移与观察完成后仅属主裁定关窗。 |
|
|
131
132
|
| `originClaimAudiences` | `[]` | 本宿主额外认作信封受众的 origin,用于端口映射或反向代理宿主(对端拨号的 URL 既不是卡片 URL 也不是到达地址)。 |
|
|
132
133
|
| `linkKeys` | `{}` | 链路 key(#317):`{ "<对端 origin>": "<key>" }`。发往该对端的请求改带这把 key;本机对端面(`/a2a/direct`、`/a2a/log`、`/a2a/blob`)只在调用方 OriginClaim 验签通过、且签名者正是该 origin 时接受它(POST 须 Sig3 绑定请求体);控制路由不接受。用于公网节点与舰队互联而不持有舰队 apiKey,两端配同一把链路 key。 |
|
|
133
134
|
| `gateAgentLessCalls` | `'audit'` | 宿主侧(无 agent)路由调用:审计,或 `refuse` 拒绝。 |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var To=Object.defineProperty;var _o=(e,t,o)=>t in e?To(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o;var Lt=(e,t,o)=>_o(e,typeof t!="symbol"?t+"":t,o);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))s(n);new MutationObserver(n=>{for(const i of n)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 o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();const un=6e4;function Oo(e,t,o=Date.now){let s=-un;return{fault(n){const i=o();i-s>=un&&(t(`[nexus] ${n}`),s=i),e.show(n)},clear(){e.hide(),s=-un}}}function bt(e){return e.name??e.team??e.label??""}function lo(e,t){var i,r;const o=(c,l)=>c!==void 0&&c.trim()!==""?{name:c,remote:l}:void 0,s=(e.sessions??[]).find(c=>c.team===t);if(s!==void 0)return o(bt(s),!1);const n=(e.remote??[]).find(c=>c.team===t);return n!==void 0?o(bt(n),!0):o((r=(((i=e.canvas)==null?void 0:i.teams)??[]).find(c=>c.team===t))==null?void 0:r.name,!1)}function Fo(e,t){const o=lo(e,t);return o===void 0?t:o.remote?`${o.name}(远端)`:o.name}function Qt(e,t,o,s=""){const n=lo(t,o);if(e.textContent=`${s}${(n==null?void 0:n.name)??o}`,e.title=o,(n==null?void 0:n.remote)===!0){const i=e.appendChild(document.createElement("span"));i.className="wb-row-remote",i.textContent="远端",i.title="名字由对端自行发布"}}const Po=60*6e4;function Tt(e){var t;return e.direction==="inbound"||e.direction===void 0&&((t=e.peer)==null?void 0:t.startsWith("local:"))===!0}function On(e){var t;return((t=e.peer)==null?void 0:t.startsWith("local:"))===!0?e.peer.slice(6):void 0}function Bt(e,t){return e.settleable!==void 0?e.settleable===!1:t-e.startedAt>=Po}function vn(e,t){const o=(e.tasks??[]).filter(r=>r.receiptExpected!==!1),s=o.filter(r=>!Tt(r)&&Bt(r,t)),n=o.filter(r=>Tt(r)&&Bt(r,t)),i=e.tasksDead??[];return{stale:s,owedOverdue:n,deadLetters:i.filter(r=>r.reason!=="fire-forget"),expired:i.filter(r=>r.reason==="fire-forget")}}function $e(e,t){const o=document.createElement(e);return t!==void 0&&(o.className=t),o}function Fn(e,t){const o=Math.max(0,Math.round((t-e)/1e3));if(o<90)return`${o}s`;const s=Math.round(o/60);if(s<90)return`${s}m`;const n=Math.round(s/60);return n<48?`${n}h`:`${Math.round(n/24)}d`}function Kt(e,t,o,s,n){const i=$e("div",`wb-led-tile${n!==void 0?` is-${n}`:""}`);i.title=s,i.appendChild($e("span","wb-led-tile-v")).textContent=String(o),i.appendChild($e("span","wb-led-tile-k")).textContent=t,e.appendChild(i)}const yn=[{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 Ro(e,t,o){const s=$e("div","wb-led-split"),n=$e("div","wb-led-split-bar");n.setAttribute("role","img");const i=$e("div","wb-led-split-legend"),r=new Set(yn.map(u=>u.key)),c=yn.map(u=>[u.tone,u.label,t[u.key]??0]);for(const[u,h]of Object.entries(t))r.has(u)||c.push(["other",u,h]);const l=[];for(const[u,h,m]of c){if(m<=0)continue;const C=$e("span",`wb-led-seg is-${u}`);C.style.width=`${Math.max(2,m/o*100)}%`,n.appendChild(C);const N=$e("span","wb-led-split-item");N.appendChild($e("i",`wb-led-swatch is-${u}`)),N.appendChild(document.createTextNode(`${h} ${m}`)),i.appendChild(N),l.push(`${h} ${m}`)}n.setAttribute("aria-label",`归档结局:${l.join(",")}`),s.append(n,i),e.appendChild(s)}function Do(e,t){var Se;e.replaceChildren();const{state:o,now:s}=t,n=o.tasks??[],i=o.tasksDead??[],r=o.archivedCount??0,c=vn(o,s),l=n.filter(R=>R.receiptExpected===!1),u=n.filter(R=>R.receiptExpected!==!1),h=u.filter(R=>Tt(R)),m=u.filter(R=>!Tt(R)),C=c.stale,N=m.filter(R=>!Bt(R,s)),J=c.stale.length,ne=$e("div","wb-led-tiles");Kt(ne,"待回执",u.length,`要回执、仍未结算的任务(含陈旧与本机欠对方的入站行)${l.length>0?`;另有免回执 ${l.length} 条不计`:""}`),Kt(ne,"陈旧",J,"我方派发、要回执、已等待 ≥1h 仍未回执(可催办);本机欠对方的入站行在「待本机回执」组另计,免回执行不算陈旧",J>0?"warn":void 0),Kt(ne,"死信",c.deadLetters.length,"超过 24h TTL 仍未结算(unconsumed);需清账(abandon)才能移出。免回执到期另列,不算死信",c.deadLetters.length>0?"danger":void 0),Kt(ne,"已归档",r,"已离开台账并留档的任务(结算、拒绝或放弃),用于审计"),e.appendChild(ne),r>0&&o.archivedSplit!==void 0&&Ro(e,o.archivedSplit,r);const G=(R,q)=>{const le=$e("div",`wb-led-card is-${q.tone}`),Ke=$e("div","wb-led-card-head");Ke.appendChild($e("span",`wb-led-dir${q.inbound?" is-in":""}`)).textContent=q.inbound?"↙":"↗";const Me=$e("span","wb-led-brief");Me.textContent=q.brief!==void 0&&q.brief!==""?q.brief:"(无摘要——派发未带 brief)",(q.brief===void 0||q.brief==="")&&Me.classList.add("is-missing"),Ke.appendChild(Me),le.appendChild(Ke);const de=$e("div","wb-led-meta"),Ue=de.appendChild($e("span","wb-led-team"));q.inbound?q.sender!==void 0?Qt(Ue,o,q.sender,"来自 "):Ue.textContent="来自 对端":Qt(Ue,o,q.team,"发往 "),q.inbound&&Qt(de.appendChild($e("span","wb-led-to")),o,q.team,"至 "),de.appendChild($e("span")).textContent=q.atText,de.appendChild($e("span","wb-led-task")).textContent=q.taskId;for(const ue of q.chips)de.appendChild($e("span","wb-led-chip")).textContent=ue;if(le.appendChild(de),le.title=`${q.taskId}
|
|
2
|
+
${q.team}`,q.dead!==void 0){const ue=q.dead,V=ue.reason==="fire-forget",ce=$e("button",`wb-btn wb-led-abandon${V?" is-quiet":""}`);ce.type="button",ce.textContent=V?"移出":"清账",ce.title=V?"可选:把这条已过期的免回执记录移出台账(不是失败,不处理也无妨)":"从台账移出这条死信(abandon)",ce.addEventListener("click",ie=>{ie.stopPropagation(),ce.disabled=!0,t.abandon(ue.taskId,"workbench dead-letter cleanup").then(ee=>{ce.disabled=!1,ce.textContent=ee?"已清":"失败"})}),le.appendChild(ce)}if(t.canOpenTeam(q.team)){le.classList.add("is-link"),le.setAttribute("role","button"),le.tabIndex=0,le.setAttribute("aria-label",`${Me.textContent},打开 ${q.team} 的对话`);const ue=()=>{t.openTeam(q.team)};le.addEventListener("click",ue),le.addEventListener("keydown",V=>{(V.key==="Enter"||V.key===" ")&&(V.preventDefault(),ue())})}R.appendChild(le)},$=(R,q,le,Ke)=>{if(q===0)return null;const Me=$e("div",`wb-led-group is-${le}`);Me.dataset.group=R;const de=$e("div","wb-led-group-head");return de.appendChild($e("span")).textContent=`${R} · ${q}`,de.appendChild($e("span","wb-led-group-hint")).textContent=Ke,Me.appendChild(de),e.appendChild(Me),Me},E=(R,q)=>{const le=Tt(R);return{brief:R.brief,team:R.team,sender:On(R),taskId:R.taskId,atText:`${Fn(R.startedAt,s)} 前${le?"收到":"派发"}`,inbound:le,tone:q,chips:[...R.settleable===!0?["可结算"]:[],...R.receiptExpected===!1?["无回执债"]:[],...le?["入站"]:[],...R.status!=="pending"?[R.status]:[]]}},S=(R,q,le)=>({brief:R.brief,team:R.team,sender:On(R),taskId:R.taskId,atText:`${Fn(R.deadAt,s)} 前${le}`,inbound:Tt(R),tone:q,dead:R,chips:R.reason!==void 0&&R.reason!==""&&R.reason!=="unspecified"&&R.reason!=="fire-forget"?[R.reason]:[]}),w=$("死信",c.deadLetters.length,"danger","需清账");if(w!==null)for(const R of[...c.deadLetters].sort((q,le)=>le.deadAt-q.deadAt))G(w,S(R,"danger","死信"));const oe=$("陈旧",C.length,"warn","我方派发 ≥1 小时仍无回执,可催办");if(oe!==null)for(const R of[...C].sort((q,le)=>q.startedAt-le.startedAt))G(oe,E(R,"warn"));const y=c.owedOverdue.length,b=$("待本机回执",h.length,y>0?"warn":"quiet",y>0?`对方在等本机回执 · ${y} 条超过 1 小时`:"对方在等本机回执");if(b!==null)for(const R of[...h].sort((q,le)=>q.startedAt-le.startedAt))G(b,E(R,Bt(R,s)?"warn":"quiet"));const M=$("等待回执",N.length,"quiet","1 小时内");if(M!==null)for(const R of[...N].sort((q,le)=>le.startedAt-q.startedAt))G(M,E(R,"quiet"));const f=$("免回执",l.length,"muted","不等回执,24h 后自动归档");if(f!==null)for(const R of l)G(f,E(R,"muted"));const D=$("已过期(免回执)",c.expired.length,"muted","正常记账,不是失败");if(D!==null)for(const R of[...c.expired].sort((q,le)=>le.deadAt-q.deadAt))G(D,S(R,"muted","过期"));n.length+i.length===0&&(e.appendChild($e("div","wb-led-empty")).textContent="台账为空——没有待回执的任务");const K=o.idempotency,X=(Se=o.reconcile)==null?void 0:Se.state;if(K!==void 0||X!==void 0){const R=$e("details","wb-led-forensics wb-forensics");R.appendChild($e("summary")).textContent="取证";const q=$e("div","wb-led-forensics-body");K!==void 0&&(q.appendChild($e("div")).textContent=`幂等 claims ${K.claimsFresh??0} · replays ${K.replays??0} · conflicts ${K.conflicts??0}`),X!==void 0&&(q.appendChild($e("div")).textContent=`reconcile ${X}`),R.appendChild(q),e.appendChild(R)}const ye=u.length+c.deadLetters.length;return{badge:ye>0?String(ye):"",tone:c.deadLetters.length>0?"danger":J>0||y>0?"warn":void 0}}const Pt=4e3,Ho=32;function xe(e,t){const o=document.createElement(e);return t!==void 0&&(o.className=t),o}function Xt(e,t,o=""){const s=xe("button",`wb-icon-btn wb-chat-act ${o}`.trim());return s.type="button",s.setAttribute("aria-label",e),s.title=e,s.textContent=t,s}function fn(e,t){const o=Math.max(0,Math.round((t-e)/1e3));if(o<60)return"刚刚";const s=Math.round(o/60);if(s<60)return`${s} 分钟前`;const n=Math.round(s/60);return n<48?`${n} 小时前`:`${Math.round(n/24)} 天前`}function jo(e){return e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${Math.round(e/1e3)} KB`:`${e} B`}function pn(e){const t=e.kind==="session"||e.kind==="remote"?e.team:void 0;return t!==void 0&&t!==""?t:void 0}function Pn(e){return e.kind==="remote"?`remote:${e.team}`:`${e.kind}:${e.id}`}function Bo(e){let t=null,o=null,s=!1,n=!1;const i=new Map,r=new Map,c=new Map,l=new Map,u=new Set,h=new Map,m=new Map,C=xe("aside","wb-chat");C.setAttribute("aria-label","对话");const N=xe("div","wb-chat-head"),J=Xt("返回会话列表","‹","wb-chat-back"),ne=xe("span","wb-chat-dot"),G=xe("div","wb-chat-titles"),$=xe("span","wb-chat-title"),E=xe("span","wb-chat-sub");G.append($,E);const S=Xt("刷新对话","↻","wb-chat-refresh"),w=Xt("会话详情","ⓘ","wb-chat-info-btn");w.setAttribute("aria-expanded","false");const oe=Xt("关闭对话","✕","wb-chat-close");N.append(J,ne,G,S,w,oe);const y=xe("div","wb-chat-info");y.hidden=!0;const b=xe("div","wb-chat-thread");b.setAttribute("role","log"),b.setAttribute("aria-live","polite"),b.tabIndex=0;const M=xe("div","wb-chat-notice"),f=xe("div","wb-chat-log"),D=xe("div","wb-chat-sent");b.append(M,f,D);const K=xe("div","wb-chat-foot");C.append(N,y,b,K),J.addEventListener("click",()=>{(t==null?void 0:t.kind)==="peer"&&(t.picked=void 0,Ye())}),S.addEventListener("click",()=>{if(t===null)return;const d=X(t);d!==void 0&&i.delete(d),t.kind==="peer"&&t.picked===void 0&&t.url!==void 0&&c.delete(`peer:${t.url}`),ce(!0)}),w.addEventListener("click",()=>{n=!n,y.hidden=!n,w.setAttribute("aria-expanded",String(n))}),oe.addEventListener("click",()=>ft()),b.addEventListener("scroll",()=>{const d=t!==null?X(t):void 0;d!==void 0&&r.set(d,b.scrollTop)}),document.addEventListener("keydown",d=>{if(d.key!=="Escape"||!C.classList.contains("is-open"))return;const _=d.target;if(_ instanceof HTMLElement){if(!C.contains(_)){const I=_ instanceof HTMLTextAreaElement||_ instanceof HTMLInputElement||_.isContentEditable,Z=_.closest(".wb-stage-board")!==null;if(I||Z)return}ft()}});function X(d){if(d.kind==="session"||d.kind==="log")return e.sessionLog!==void 0?`local:${d.id}`:void 0;if(d.kind==="remote")return e.remoteSessionLog!==void 0?`remote:${d.team}`:void 0;if(d.picked!==void 0&&d.url!==void 0&&e.remoteSessionLog!==void 0)return`peer:${d.url}:${d.picked}`}function ye(d){var _,I,Z;return d.kind==="session"||d.kind==="log"?((_=e.sessionLog)==null?void 0:_.call(e,d.id))??Promise.resolve({ok:!1,error:"sessionLog unavailable"}):d.kind==="remote"?((I=e.remoteSessionLog)==null?void 0:I.call(e,{team:d.team}))??Promise.resolve({ok:!1,error:"remoteSessionLog unavailable"}):((Z=e.remoteSessionLog)==null?void 0:Z.call(e,{peer:d.url,id:d.picked}))??Promise.resolve({ok:!1,error:"remoteSessionLog unavailable"})}function Se(){const d=t;if(d===null)return;const _=d.kind==="log"||d.kind==="remote"||d.kind==="session"&&d.live===!1;ne.className=`wb-chat-dot ${d.kind==="peer"?"is-peer":_?"is-cold":"is-live"}`,ne.title=d.kind==="peer"?"对端主机":d.kind==="log"?"只读日志":d.kind==="remote"?"远端会话":d.live===!1?"冷会话":"在线",$.textContent=d.kind==="peer"&&d.picked!==void 0?`…${d.picked.slice(-12)}`:d.title,E.textContent=d.kind==="peer"&&d.picked!==void 0?d.title:d.sub,J.hidden=!(d.kind==="peer"&&d.picked!==void 0)}function R(d,_){const I=xe("div","wb-kv");I.appendChild(xe("span","wb-kv-k")).textContent=d,I.appendChild(xe("span","wb-kv-v")).textContent=_,y.appendChild(I)}function q(){var _,I,Z;y.replaceChildren();const d=t;d!==null&&(d.kind==="session"?(R("会话",d.id),d.team!==void 0&&R("路由",d.team),R("来源","本机会话日志")):d.kind==="log"?(R("会话",d.id),R("来源","本机会话日志(不在网络目录)")):d.kind==="remote"?(R("路由",d.team),((_=d.row)==null?void 0:_.origin)!==void 0&&R("来源",d.row.origin),((I=d.row)==null?void 0:I.via)!==void 0&&R("经",d.row.via),((Z=d.row)==null?void 0:Z.workspace)!==void 0&&R("工作区",d.row.workspace)):(R("对端",d.url??d.sub),d.picked!==void 0&&R("会话",d.picked),R("来源","经本机代理读取对端 /a2a/log")))}function le(d){return d.kind==="log"?"只读日志 —— 此会话当前不在网络目录(可能已归档或未入网)。":d.kind==="remote"?e.remoteSessionLog!==void 0?"远端会话 —— 经本机代理从对端 /a2a/log 读取最近对话。":"远端节点的对话记录在对端机器上;本页的代理读取面未启用。":d.kind==="peer"?d.url===void 0||e.remoteSessionLog===void 0?`对端主机节点(${d.sub})—— 会话记录存放在对端机器上,本页只读本机会话日志。`:d.picked===void 0?"对端主机 —— 选择一个会话阅读(经本机代理读取对端 /a2a/log)。":"":e.sessionLog===void 0?"本页未接入会话日志读取,只显示从这里发出的消息。":""}function Ke(){return b.scrollHeight-b.scrollTop-b.clientHeight<=Ho}function Me(d){const _=()=>{b.scrollTop=b.scrollHeight};_(),requestAnimationFrame(_)}function de(d,_){const I=xe("div",`wb-chat-empty${_==="error"?" is-error":""}`);return I.textContent=d,I}function Ue(d,_){const I=d==="user",Z=xe("div",`wb-msg ${I?"wb-msg-user":"wb-msg-assistant"}`),Y=xe("span","wb-msg-who");Y.textContent=I?"问":"答";const he=xe("div","wb-msg-text");return he.textContent=_,Z.append(Y,he),Z}function ue(d){if(f.replaceChildren(),d===void 0){f.appendChild(de("读取中…"));return}if(d.ok!==!0||d.messages===void 0){f.appendChild(de(`无法读取(${d.error??"未知错误"})`,"error"));return}if(d.messages.length===0){f.appendChild(de("暂无对话记录"));return}for(const _ of d.messages)f.appendChild(Ue(_.role,_.text))}function V(d,_){if(f.replaceChildren(),_===void 0){f.appendChild(de("读取中…"));return}if(_.ok!==!0||_.sessions===void 0){f.appendChild(de(`无法读取(${_.error??"未知错误"})`,"error"));return}if(_.sessions.length===0){f.appendChild(de("对端没有可读会话"));return}const I=xe("div","wb-chat-sessions"),Z=e.now();for(const Y of _.sessions){const he=xe("button","wb-chat-session");he.type="button",he.appendChild(xe("span","wb-chat-session-id")).textContent=`…${Y.id.slice(-12)}`;const pe=[Y.mtimeMs!==void 0?fn(Y.mtimeMs,Z):"",jo(Y.sizeBytes)].filter(Boolean).join(" · ");he.appendChild(xe("span","wb-chat-session-meta")).textContent=pe,he.title=Y.id,he.addEventListener("click",()=>{(t==null?void 0:t.kind)!=="peer"||t.url!==d||(t.picked=Y.id,Ye())}),I.appendChild(he)}f.appendChild(I)}function ce(d=!1){const _=t;if(_===null)return;if(M.textContent=le(_),M.hidden=M.textContent==="",_.kind==="peer"&&_.picked===void 0){if(_.url===void 0||e.remoteSessionLog===void 0){f.replaceChildren();return}const Y=_.url,he=`peer:${Y}`;if(!d&&c.has(he)){V(Y,c.get(he));return}c.set(he,void 0),V(Y,void 0),e.remoteSessionLog({peer:Y}).then(pe=>{c.set(he,pe),(t==null?void 0:t.kind)==="peer"&&t.url===Y&&t.picked===void 0&&V(Y,pe)},pe=>{const we={ok:!1,error:pe instanceof Error?pe.message:String(pe)};c.set(he,we),(t==null?void 0:t.kind)==="peer"&&t.url===Y&&t.picked===void 0&&V(Y,we)});return}const I=X(_);if(I===void 0){f.replaceChildren(),Ze(!0);return}const Z=()=>{const Y=r.get(I);Y===void 0||d?Me():(b.scrollTop=Y,requestAnimationFrame(()=>{t!==null&&X(t)===I&&(b.scrollTop=Y)}))};if(!d&&i.has(I)&&i.get(I)!==void 0){ue(i.get(I)),Ze(!0),Z();return}d&&r.delete(I),i.set(I,void 0),ue(void 0),Ze(!0),ye(_).then(Y=>{var he;i.set(I,Y),(Y==null?void 0:Y.ok)===!0&&Y.messages!==void 0&&((he=e.onTranscript)==null||he.call(e,_,Y.messages)),!(t===null||X(t)!==I)&&(ue(Y),Ze(),Z())},Y=>{const he={ok:!1,error:Y instanceof Error?Y.message:String(Y)};i.set(I,he),t!==null&&X(t)===I&&ue(he)})}function ie(d){return((o==null?void 0:o.tasks)??[]).filter(_=>_.team===d)}function ee(d){return((o==null?void 0:o.tasksDead)??[]).filter(_=>_.team===d)}let ke="";function et(d){return d.reason==="fire-forget"?{text:"已过期(免回执)",tone:"muted",hint:"免回执消息到 24h TTL 归档,属正常记账"}:{text:"死信",tone:"danger",hint:d.reason!==void 0&&d.reason!=="unspecified"?`${d.reason}:超过 24h TTL 仍未结算,需在台账清账`:"超过 24h TTL 仍未结算,需在台账清账"}}function Oe(d,_){const I=d.direction==="inbound";if(d.receiptExpected===!1)return{text:I?"来信 · 无需回执":"已送达 · 无回执",tone:"muted",hint:"免回执消息:不等回执,24h 后自动归档"};const Z=Bt(d,_),Y=I?Z?"待本机回执 · 超 1 小时":"待本机回执":Z?"陈旧":"待回执",he=d.status!=="pending"?` · ${d.status}`:"";return{text:`${Y}${he}`,tone:Z?"warn":"wait",hint:`${I?"对方等待本机回执;":""}${I?"收到":"派发"}于 ${fn(d.startedAt,_)}`}}function tt(d,_){const I=`结算于 ${fn(d.resolvedAt,_)}`;if(d.receiptExpected===!1&&d.endedBy==="abandoned")return d.late===!0?{text:"已移出(免回执) · 回执迟到",tone:"warn",hint:`${I};免回执消息本机移出后对方回执才到,结局不改写`}:{text:"已移出(免回执)",tone:"muted",hint:`${I};免回执消息,本机移出台账,不是失败`};if(d.endedBy==="abandoned"||d.endedBy==="cancelled"){const pe=d.endedBy==="cancelled"?"撤回":"放弃";return d.late===!0?{text:`已${pe} · 回执迟到`,tone:"warn",hint:`${I};本机${pe}之后对方回执才到,结局不改写`}:{text:`已${pe}`,tone:"muted",hint:`${I};本机${pe},未等到回执`}}if(d.endedBy==="echo")return{text:"已结算 · 内容回显",tone:"ok",hint:`${I};对方回了内容但未带正式回执,按内容回显结算`};if(d.endedBy===void 0&&d.outcome==="abandoned")return{text:"已放弃(本机或对方)",tone:"muted",hint:`${I};本机放弃或撤回,或对方回执报告放弃`};const Z=d.summary!==void 0&&d.summary!==""?` · ${d.summary}`:"";if(d.outcome==="abandoned")return{text:"已回执 · 对方放弃",tone:"warn",hint:`${I};对方回执报告放弃${Z}`};const Y=yn.find(pe=>pe.key===d.outcome),he=(Y==null?void 0:Y.tone)==="completed"?"ok":(Y==null?void 0:Y.tone)==="failed"?"danger":(Y==null?void 0:Y.tone)==="rejected"?"warn":"muted";return d.outcome==="unclassified"?{text:"已回执",tone:"ok",hint:`${I};回执未带结论${Z}`}:{text:`已回执 · ${(Y==null?void 0:Y.label)??d.outcome}`,tone:he,hint:`${I}${Z}`}}function st(d,_,I){const Z=ee(_).find(pe=>pe.taskId===d);if(Z!==void 0)return et(Z);const Y=ie(_).find(pe=>pe.taskId===d);if(Y!==void 0)return Oe(Y,I);const he=((o==null?void 0:o.archived)??[]).find(pe=>pe.taskId===d&&pe.team===_);return he!==void 0?tt(he,I):u.has(d)?{text:"已离开台账",tone:"muted",hint:"已结算、放弃或撤回之一;主机未给出这条的结局(旧主机,或已超出最近 64 条归档)"}:{text:"已派发",tone:"muted",hint:"异步送达;回执到达后进台账"}}function St(d){const _=e.now(),I=l.get(d)??[],Z=new Set(I.map(me=>me.taskId).filter(me=>me!==void 0)),Y=me=>me!==void 0&&me!==""?me:"(无摘要)",he=ie(d).filter(me=>!Z.has(me.taskId)).map(me=>({at:me.startedAt,side:me.direction==="inbound"?"theirs":"mine",text:Y(me.brief),status:Oe(me,_),taskId:me.taskId})),pe=ee(d).filter(me=>!Z.has(me.taskId)).map(me=>{var Pe;const Fe=me.direction??h.get(me.taskId)??(((Pe=me.peer)==null?void 0:Pe.startsWith("local:"))===!0?"inbound":void 0);return{at:me.startedAt,side:Fe==="inbound"?"theirs":Fe==="outbound"?"mine":"system",text:Y(me.brief),status:et(me),taskId:me.taskId}}),we=I.map(me=>({at:me.at,side:"mine",text:me.text,status:me.state==="sending"?{text:"发送中…",tone:"muted"}:me.state==="failed"?{text:`失败:${me.error??"未知原因"}`,tone:"danger"}:me.taskId!==void 0?st(me.taskId,d,_):{text:"已派发",tone:"muted"},taskId:me.taskId}));return[...he,...pe,...we].sort((me,Fe)=>me.at-Fe.at)}function Ze(d=!1){const _=t,I=_!==null?pn(_):void 0,Z=I!==void 0?St(I):[],Y=JSON.stringify([I??"",...Z.map(we=>[we.taskId??"",we.side,we.text,we.status.text])]);if(!d&&Y===ke)return;ke=Y;const he=Ke();if(D.replaceChildren(),Z.length===0)return;const pe=xe("div","wb-chat-divider");pe.textContent="台账往来 · 异步送达,回执进台账",D.appendChild(pe);for(const we of Z){const me=we.side==="mine"?"wb-msg wb-msg-user wb-msg-out":we.side==="theirs"?"wb-msg wb-msg-assistant wb-msg-in":"wb-msg wb-msg-ledger",Fe=xe("div",me);Fe.appendChild(xe("span","wb-msg-who")).textContent=we.side==="mine"?"我":we.side==="theirs"?"对方":"台账记录",Fe.appendChild(xe("div","wb-msg-text")).textContent=we.text;const Pe=xe("div","wb-msg-meta");we.taskId!==void 0&&(Pe.appendChild(xe("span","wb-msg-task")).textContent=we.taskId);const Je=xe("span",`wb-msg-status is-${we.status.tone}`);Je.textContent=we.status.text,we.status.hint!==void 0&&(Je.title=we.status.hint),Pe.appendChild(Je),Fe.appendChild(Pe),D.appendChild(Fe)}he&&Me()}function Ve(d){const _=d.text.value.length;d.note.classList.remove("is-error"),d.note.textContent=_>Pt*.9?`${_}/${Pt}`:"Enter 发送 · Shift+Enter 换行",_>Pt&&d.note.classList.add("is-error"),d.submit.disabled=d.text.value.trim()===""||_>Pt}function Nt(d){d.style.height="auto",d.style.height=`${Math.min(d.scrollHeight,160)}px`}function Ot(d){const _=xe("form","wb-dispatch-form wb-chat-composer"),I=document.createElement("textarea");I.className="wb-dispatch-text",I.rows=1,I.placeholder="发一句话任务给这个节点…",I.setAttribute("aria-label","发送消息");const Z=xe("button","wb-dispatch-submit");Z.type="submit",Z.textContent="发送",Z.setAttribute("aria-label","发送");const Y=xe("div","wb-dispatch-note");Y.setAttribute("aria-live","polite");const he=xe("div","wb-chat-composer-row");he.append(I,Z),_.append(he,Y);const pe={team:d,form:_,text:I,note:Y,submit:Z};return I.addEventListener("input",()=>{Nt(I),Ve(pe)}),I.addEventListener("keydown",we=>{we.key!=="Enter"||we.shiftKey||we.ctrlKey||we.altKey||we.metaKey||we.isComposing||(we.preventDefault(),_.requestSubmit())}),_.addEventListener("submit",we=>{we.preventDefault();const me=I.value.trim();if(me===""||me.length>Pt||e.dispatchQuick===void 0)return;const Fe={text:me,at:e.now(),state:"sending"},Pe=l.get(d)??[];Pe.push(Fe),l.set(d,Pe),I.value="",Nt(I),Ve(pe),I.focus({preventScroll:!0}),Ze(),Me(),e.dispatchQuick(d,me).then(Je=>Je,Je=>({ok:!1,error:Je instanceof Error?Je.message:String(Je)})).then(Je=>{Je.ok===!0?(Fe.state="sent",Fe.taskId=Je.task_id,Y.classList.remove("is-error"),Y.textContent=`已派发${Je.task_id!==void 0?` · ${Je.task_id}`:""} · 回执将入台账`):(Fe.state="failed",Fe.error=Je.error??"未知原因",I.value===""&&(I.value=me,Nt(I)),Y.classList.add("is-error"),Y.textContent=`失败:${Fe.error}`,Z.disabled=I.value.trim()===""),t!==null&&pn(t)===d&&Ze()})}),Ve(pe),pe}function Re(){const d=t;if(K.replaceChildren(),d===null)return;const _=pn(d);if(_!==void 0&&e.dispatchQuick!==void 0){let Z=m.get(_);Z===void 0&&(Z=Ot(_),m.set(_,Z)),K.appendChild(Z.form),s&&(s=!1,Z.text.focus({preventScroll:!0}));return}s=!1;const I=xe("div","wb-chat-readonly");I.textContent=d.kind==="log"?"只读日志:此会话不在网络目录,不能从这里派发。":d.kind==="peer"?"对端主机:选择会话阅读;要派发请打开它的远端会话节点。":"此节点没有可派发的路由。",K.appendChild(I)}function Ye(){Se(),q(),ce(),Re()}function Ge(d){const _=t!==null&&Pn(t)===Pn(d);t=d,_||(s=!0),Ye(),C.classList.add("is-open")}function ft(){t=null,C.classList.remove("is-open")}function wt(d){o=d;for(const I of(d==null?void 0:d.tasks)??[])u.add(I.taskId),h.set(I.taskId,I.direction==="inbound"?"inbound":"outbound");const _=t;if(_!==null){if(_.kind==="session"&&d!==null){const I=(d.sessions??[]).find(Z=>Z.id===_.id);I!==void 0&&(_.title=bt(I),_.live=I.live)}Se(),Ze()}}return{root:C,open:Ge,close:ft,isOpen:()=>t!==null,update:wt}}function Yo(e,t,o){const s=document.createElement("section"),n=document.createElement("h4");n.textContent="会话席位名(仅人工设定)",s.appendChild(n);const i=document.createElement("p");i.className="wb-hint",i.textContent="稳定展示名,不改变会话标题、路由句柄或准入。留空并保存可清除。",s.appendChild(i);const r=document.createElement("select");r.setAttribute("aria-label","选择命名的本机会话");for(const N of new Set([...e.map(J=>J.id),...Object.keys(t)])){const J=e.find(G=>G.id===N),ne=document.createElement("option");ne.value=N,ne.textContent=(t[N]??(J==null?void 0:J.name)??(J==null?void 0:J.label)??N)+" · "+N,r.appendChild(ne)}const c=document.createElement("input");c.type="text",c.maxLength=64,c.setAttribute("aria-label","人工席位名"),c.placeholder="例如:开发席";const l=document.createElement("button");l.type="submit",l.textContent="保存席位名",l.className="wb-member-submit";const u=document.createElement("form");u.className="wb-member-add",u.append(r,c,l);const h=document.createElement("p");h.className="wb-hint",h.setAttribute("role","status"),h.setAttribute("aria-live","polite");let m=!1;const C=()=>{c.value=t[r.value]??"",l.disabled=m||r.value===""};return r.addEventListener("change",C),C(),u.addEventListener("submit",async N=>{if(N.preventDefault(),m||r.value==="")return;const J=r.value,ne=c.value.trim(),G={...t};ne===""?delete G[J]:G[J]=ne,m=!0,l.disabled=!0,r.disabled=!0,c.disabled=!0;try{const $=await o(G);$.ok?(delete t[J],ne!==""&&(t[J]=ne),h.textContent="席位名已保存;目录将随状态刷新。"):h.textContent=$.error??"保存失败"}catch($){h.textContent="保存失败:"+String($)}finally{m=!1,r.disabled=!1,c.disabled=!1,C()}}),s.append(u,h),s}function Wo(e,t){const o=document.createElement("form");o.className="wb-cli-home";const s=document.createElement("button");s.type="button",s.textContent="指定 home",s.className="wb-member-submit";const n=document.createElement("select");n.className="wb-member-pick",n.setAttribute("aria-label","选择 CLI home 主机"),n.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 l;if(!c){c=!0,s.disabled=!0,i.disabled=!0,r.textContent="正在现读主机名片…";try{const u=await e();if(!u.ok)throw Error(u.error??"候选读取失败");if(u.held){s.hidden=!0,n.hidden=!0,i.hidden=!0,n.replaceChildren(),r.textContent="本机持有,无需指定";return}n.replaceChildren(new Option("请选择 home 主机…","",!0,!0));for(const m of u.homes??[])n.appendChild(new Option(m,m));n.hidden=!1,i.hidden=!1,s.textContent="刷新候选";const h=((l=u.homes)==null?void 0:l.length)??0;r.textContent=(u.unreadable?"有 "+u.unreadable+" 台对端名片读不到;":"")+(h>1?"多台已链接主机列出该句柄,请显式选择。":h===1?"找到 1 台已链接主机;仍需你显式确认。":"当前没有已链接且列出该句柄的可选主机。")}catch(u){r.textContent="读取失败:"+String(u),n.hidden=!0,i.hidden=!0}finally{c=!1,s.disabled=!1}}}),n.addEventListener("change",()=>{i.disabled=c||n.value===""}),o.addEventListener("submit",async l=>{if(l.preventDefault(),!(c||n.value==="")){c=!0,s.disabled=!0,n.disabled=!0,i.disabled=!0;try{r.textContent=await t(n.value)?"home 已指定:"+n.value:"指定失败:主机可能已离线或不再列出该句柄,请刷新后重选。"}catch(u){r.textContent="指定失败:"+String(u)}finally{c=!1,s.disabled=!1,n.disabled=!1,i.disabled=n.value===""}}}),o.append(s,n,i,r),o}function Ko(e,t){const o=document.createElement("form");o.className="wb-member-provenance";const s=document.createElement("select");s.setAttribute("aria-label","确认成员来源"),s.appendChild(new Option("明确选择来源…","")),e.forEach((r,c)=>s.appendChild(new Option(r.label,String(c))));const n=document.createElement("button");n.type="submit",n.textContent="确认来源";const i=document.createElement("span");return i.textContent="确认不等于恢复;已暂停槽位仍保持暂停",o.append(s,n,i),o.addEventListener("submit",async r=>{if(r.preventDefault(),s.value==="")return;const c=e[Number(s.value)];if(!c)return;n.disabled=!0;const l=await t(c);l.ok?i.textContent="来源已确认;等待状态刷新(暂停状态不变)":(n.disabled=!1,i.textContent=l.error??"来源无法验证,原槽位保留")}),o}const mo="dsh-a2a.rail.tab";function Xo(){try{return window.localStorage.getItem(mo)}catch{return null}}function Uo(e){try{window.localStorage.setItem(mo,e)}catch{}}function Jo(e,t={}){var G;const o=document.createElement("aside");o.className="wb-rail";const s=document.createElement("div");s.className="wb-tabs",s.setAttribute("role","tablist"),s.setAttribute("aria-label","工作台侧栏");const n=document.createElement("div");n.className="wb-tabpanels",o.append(s,n);const i=new Map,r=new Map,c=new Map,l=new Set;for(const $ of e){const E=document.createElement("button");E.type="button",E.className="wb-tab",E.id=`wb-tab-${$.key}`,E.dataset.sec=$.key,E.setAttribute("role","tab"),E.setAttribute("aria-controls",`wb-panel-${$.key}`);const S=document.createElement("span");S.className="wb-tab-label",S.textContent=$.label;const w=document.createElement("span");w.className="wb-tab-badge",E.append(S,w),E.addEventListener("click",()=>N($.key)),s.appendChild(E);const oe=document.createElement("section");oe.className="wb-tabpanel",oe.id=`wb-panel-${$.key}`,oe.dataset.sec=$.key,oe.setAttribute("role","tabpanel"),oe.setAttribute("aria-labelledby",E.id),oe.tabIndex=-1,n.appendChild(oe),i.set($.key,E),r.set($.key,oe),c.set($.key,w)}const u=()=>e.map($=>$.key).filter($=>!l.has($)),h=Xo();let m=((G=e.find($=>$.key===h))==null?void 0:G.key)??e[0].key;s.addEventListener("keydown",$=>{const E=u(),S=E.indexOf(m);let w;$.key==="ArrowRight"?w=E[(S+1)%E.length]:$.key==="ArrowLeft"?w=E[(S-1+E.length)%E.length]:$.key==="Home"?w=E[0]:$.key==="End"&&(w=E[E.length-1]),w!==void 0&&($.preventDefault(),N(w,{focus:!0}))});function C(){for(const $ of e){const E=$.key===m,S=i.get($.key);S.setAttribute("aria-selected",String(E)),S.tabIndex=E?0:-1,S.classList.toggle("is-active",E),S.hidden=l.has($.key),r.get($.key).hidden=!E}}function N($,E={}){var w;if(l.has($))return;const S=$!==m;m=$,Uo($),C(),E.focus===!0&&i.get($).focus(),S&&((w=t.onChange)==null||w.call(t,$))}function J($,E,S){const w=c.get($);w.textContent=E,w.className=`wb-tab-badge${S!==void 0?` is-${S}`:""}`}function ne($,E){E?l.add($):l.delete($),l.has(m)&&(m=u()[0]??e[0].key),C()}return C(),{root:o,panel:$=>r.get($),select:N,selected:()=>m,setBadge:J,setHidden:ne}}const wn="application/x-dsh-session";function qo(e,t){e.draggable=!0,e.classList.add("is-draggable"),e.title=`${e.title===""?t:e.title} · 可拖入画布队`,e.addEventListener("dragstart",o=>{o.dataTransfer!==null&&(o.dataTransfer.setData(wn,t),o.dataTransfer.effectAllowed="copy")})}function zo(e,t){const o=n=>n.dataTransfer!==null&&[...n.dataTransfer.types].includes(wn),s=n=>{for(const[i,r]of t.frames())r.classList.toggle("drop-target",i===n)};e.addEventListener("dragover",n=>{if(!o(n))return;const i=t.frameAt(n.clientX,n.clientY);s(i),i!==void 0&&(n.preventDefault(),n.dataTransfer.dropEffect="copy")},t.signal),e.addEventListener("dragleave",n=>{o(n)&&(n.relatedTarget===null||!e.contains(n.relatedTarget))&&s(void 0)},t.signal),e.ownerDocument.addEventListener("dragend",()=>s(void 0),t.signal),e.addEventListener("drop",n=>{if(!o(n))return;s(void 0);const i=t.frameAt(n.clientX,n.clientY),r=n.dataTransfer.getData(wn);i===void 0||r===""||(n.preventDefault(),t.drop(i,r))},t.signal)}const Vo=[{key:"all",label:"全部",hint:"不过滤:目录全部条目"},{key:"local",label:"本机",hint:"本机会话节点(现场 + 冷启动)"},{key:"canvas",label:"队",hint:"画布队(人类手工分配的组队)"},{key:"remote",label:"远端",hint:"远端会话队(来自对端卡片)"},{key:"logs",label:"日志",hint:"本机日志:不在网络目录里的会话档案"}];function rt(e,t){const o=document.createElement(e);return t!==void 0&&(o.className=t),o}function Ut(...e){const t=[];for(const o of e)o!==void 0&&(typeof o=="string"?t.push(o):t.push(...o));return t.join(" ").toLowerCase()}function Go(e,t){const o=Math.max(0,Math.round((t-e)/1e3));if(o<60)return"刚刚";const s=Math.round(o/60);if(s<60)return`${s} 分钟前`;const n=Math.round(s/60);return n<48?`${n} 小时前`:`${Math.round(n/24)} 天前`}function Rn(e){return e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${Math.round(e/1e3)} KB`:`${e} B`}function Qo(e,t){const o=new Date(t);o.setHours(0,0,0,0);const s=o.getTime();return e>=s?"今天":e>=s-864e5?"昨天":"更早"}function Zo(e,t){var S,w,oe;e.replaceChildren();const{state:o,search:s,filter:n}=t,i=s!==""||n!=="all",r=(y,b)=>(n==="all"||n===y)&&(s===""||b.includes(s)),c=(y,b,M)=>{const f=rt("div","wb-dir-group"),D=rt("div","wb-dir-head");return D.appendChild(rt("span","wb-dir-head-label")).textContent=`${y} · ${b}`,M!==void 0&&(D.appendChild(rt("span","wb-dir-head-note")).textContent=M),f.appendChild(D),e.appendChild(f),f},l=(y,b,M,f,D,K,X)=>{const ye=rt("button","wb-item");ye.type="button",ye.appendChild(rt("span",`wb-dot ${b}`)).textContent=M;const Se=rt("span","wb-item-text");return Se.appendChild(rt("span","wb-item-label")).textContent=f,D!==""&&(Se.appendChild(rt("span","wb-item-sub")).textContent=D),ye.appendChild(Se),X!==void 0&&(ye.title=X),ye.addEventListener("click",K),y.appendChild(ye),ye},u=(y,b)=>{if(b===void 0||b.length===0)return;const M=rt("span","wb-item-badge"),f=b.map(D=>D.slice(D.lastIndexOf("/")+1));M.textContent=`▤ ${f.slice(0,2).join(" ")}${f.length>2?` +${f.length-2}`:""}`,M.title=`画布队:${b.join(" · ")}`,y.appendChild(M)},h=(o.sessions??[]).filter(y=>r("local",Ut(bt(y),y.team,y.label,y.id,y.canvasTeams))),m=h.filter(y=>y.live===!0),C=h.filter(y=>y.live!==!0);if(h.length>0){const y=c("本机会话",h.length,C.length>0?`${m.length} 在线 · ${C.length} 冷`:void 0);for(const b of[...m,...C]){const M=b.live===!0,f=b.nodePromptMissing===!0,D=l(y,M?"wb-dot-live":"wb-dot-cold",M?"●":"○",bt(b),M?b.team:`冷 · ${b.team}`,()=>t.openChat({kind:"session",id:b.id,title:bt(b),sub:b.team,team:b.team,live:b.live}),b.id);if(f){D.classList.add("is-node-prompt-missing");const K=rt("span","wb-node-warning");K.textContent="本会话的组装不含节点段",(S=D.querySelector(".wb-item-text"))==null||S.appendChild(K)}u(D,b.canvasTeams),qo(D,b.id)}}const N=(((w=o.canvas)==null?void 0:w.teams)??[]).filter(y=>r("canvas",Ut(y.name,y.team,y.members.map(b=>b.team??b.id))));if(N.length>0){const y=c("画布队",N.length);for(const b of N){const M=b.members.filter(f=>f.remote===!0).length;l(y,"wb-dot-canvas","▤",b.name,`${b.members.length} 成员${M>0?` · ${M} 远端`:""}`,()=>t.selectCanvas(b),b.team)}}const J=(o.remote??[]).filter(y=>r("remote",Ut(bt(y),y.team,y.origin,y.workspace,y.canvasTeams)));if(J.length>0){const y=c("远端节点",J.length);for(const b of J){const M=l(y,"wb-dot-remote","◇",bt(b),b.origin!==void 0&&b.origin!==""?b.origin:b.team,()=>t.openChat({kind:"remote",team:b.team,title:bt(b),sub:b.team,row:b}),b.team);if(b.unreachable===!0){M.classList.add("is-unreachable");const f=rt("span","wb-unreachable-note");f.textContent="对端未应答(显示的是最后已知状态)",(oe=M.querySelector(".wb-item-text"))==null||oe.appendChild(f)}u(M,b.canvasTeams)}}const ne=new Set((o.sessions??[]).map(y=>y.id)),G=t.logs.filter(y=>!ne.has(y.id)&&r("logs",Ut(y.id,t.titles.get(y.id))));if(G.length>0){const y=t.logTotal!==void 0&&t.logTotal>t.logs.length,b=c("本机日志",G.length,y?`显示 ${t.logs.length} / 共 ${t.logTotal}`:void 0);let M="";for(const f of[...G].sort((D,K)=>K.mtimeMs-D.mtimeMs)){const D=Qo(f.mtimeMs,t.now);D!==M&&(M=D,b.appendChild(rt("div","wb-dir-day")).textContent=D);const K=t.titles.get(f.id),X=l(b,"wb-dot-log","❐",K??`会话 …${f.id.slice(-12)}`,`${Go(f.mtimeMs,t.now)} · ${Rn(f.sizeBytes)}`,()=>t.openChat({kind:"log",id:f.id,title:K??`…${f.id.slice(-12)}`,sub:"本机日志"}),`${f.id}
|
|
3
|
+
${new Date(f.mtimeMs).toLocaleString()} · ${Rn(f.sizeBytes)}`);K===void 0&&X.classList.add("is-untitled")}}h.length+N.length+J.length+G.length===0&&(e.appendChild(rt("div","wb-dir-empty")).textContent=i?"无匹配条目":"目录为空");const $=o.sessions??[];return`${$.filter(y=>y.live===!0).length}/${$.length}`}const uo="dsh-a2a.logtitles",es=300,Dn=60;function ts(){try{const e=window.localStorage.getItem(uo),t=e===null?null:JSON.parse(e);if(t!==null&&typeof t=="object")return new Map(Object.entries(t).filter(o=>typeof o[1]=="string"))}catch{}return new Map}function ns(e,t,o){const s=o.find(r=>r.role==="user"&&r.text.trim()!=="")??o.find(r=>r.text.trim()!=="");if(s===void 0)return!1;const n=s.text.trim().split(`
|
|
4
|
+
`)[0].trim(),i=n.length>Dn?`${n.slice(0,Dn-1)}…`:n;if(e.get(t)===i)return!1;for(e.delete(t),e.set(t,i);e.size>es;)e.delete(e.keys().next().value);try{window.localStorage.setItem(uo,JSON.stringify(Object.fromEntries(e)))}catch{}return!0}function mt(e){return e.name??e.team??e.label??""}function Rt(e,t){const o=Math.max(0,Math.round((t-e)/1e3));if(o<90)return`${o}s`;const s=Math.round(o/60);if(s<90)return`${s}m`;const n=Math.round(s/60);return n<48?`${n}h`:`${Math.round(n/24)}d`}function L(e,t){const o=document.createElement(e);return t!==void 0&&(o.className=t),o}function Hn(e,t,o){const s=L("div","wb-kv");s.appendChild(L("span","wb-kv-k")).textContent=t,s.appendChild(L("span","wb-kv-v")).textContent=o,e.appendChild(s)}function os(e){try{return new URL(e).host}catch{return e}}const ss=[{key:"directory",label:"目录"},{key:"hosts",label:"主机"},{key:"ledger",label:"台账"},{key:"activity",label:"活动"},{key:"plan",label:"规划"}];function is(e){const t=e.board;let o="data",s,n=null,i="",r="all",c=null;const l=new Map,u=new Map;let h=null,m=[],C;const N=ts();let J=!1;const ne=L("div","wb");ne.setAttribute("data-testid","wb-root");const G=L("header","wb-header"),$=L("button","wb-icon-btn wb-menu");$.type="button",$.dataset.testid="wb-menu",$.setAttribute("aria-label","打开目录抽屉"),$.textContent="☰",$.addEventListener("click",()=>ne.classList.toggle("is-rail-open")),G.appendChild($);const E=L("div","wb-title");E.appendChild(L("span","wb-title-main")).textContent="A2A 工作台";const S=L("span","wb-badge");E.appendChild(S),G.appendChild(E);const w=L("button","wb-icon-btn wb-owner-btn");w.type="button",w.dataset.testid="wb-owner-btn",w.setAttribute("aria-label","主人设置"),w.title="主人设置:席位名 / 入网白名单 / 可见性 / 发布",w.textContent="⚙",w.addEventListener("click",()=>{I=!I,Y()}),G.appendChild(w);const oe=L("div","wb-header-stats");G.appendChild(oe);const y=L("button","wb-icon-btn wb-rail-toggle");y.type="button",y.dataset.testid="wb-rail-toggle",y.setAttribute("aria-label","折叠/展开侧栏"),y.textContent="◧",y.addEventListener("click",()=>{ne.classList.toggle("is-rail-collapsed"),o==="canvas"&&t!==void 0&&t.activate()}),G.appendChild(y);const b=L("div","wb-body"),M=Jo(ss),f=M.root,D=L("div","wb-directory"),K=L("div","wb-dir-tools"),X=document.createElement("input");X.className="wb-search",X.type="search",X.placeholder="搜索:名称 / 队 / 会话 / 节点",X.setAttribute("aria-label","统一搜索目录"),X.addEventListener("input",()=>{i=X.value.trim().toLowerCase(),st()}),K.appendChild(X);const ye=L("div","wb-filters");for(const k of Vo){const x=L("button","wb-filter");x.type="button",x.textContent=k.label,x.dataset.filter=k.key,x.title=k.hint,x.addEventListener("click",()=>{r=k.key,st()}),ye.appendChild(x)}K.appendChild(ye),M.panel("directory").appendChild(K),M.panel("directory").appendChild(D);const Se=L("section","wb-pane"),R=L("section","wb-pane"),q=L("section","wb-pane"),le=L("section","wb-pane"),Ke=[["ledger",Se],["activity",R],["plan",q],["hosts",le]],Me=L("main","wb-stage"),de=L("section","wb-stage-board");t!==void 0&&(de.appendChild(t.root),Me.appendChild(de));const Ue=L("div","wb-data"),ue=L("aside","wb-inspector"),V=L("div","wb-inspector-head");V.appendChild(L("span","wb-inspector-title")).textContent="详情";const ce=L("button","wb-icon-btn");ce.type="button",ce.setAttribute("aria-label","关闭详情"),ce.textContent="✕",ce.addEventListener("click",()=>tt(void 0)),V.appendChild(ce),ue.appendChild(V);const ie=L("div","wb-inspector-body");ue.appendChild(ie);const ee=Bo({now:e.now,sessionLog:e.sessionLog,remoteSessionLog:e.remoteSessionLog,dispatchQuick:e.dispatchQuick,onTranscript:(k,x)=>{k.kind!=="log"&&k.kind!=="session"||ns(N,k.id,x)&&st()}});Me.appendChild(Ue),b.appendChild(f),b.appendChild(Me),b.appendChild(ue),b.appendChild(ee.root),ne.appendChild(G),ne.appendChild(b);function ke(k){for(const[x,F]of Ke){const A=k==="canvas"?M.panel(x):Ue;M.setHidden(x,k!=="canvas"),F.parentElement!==A&&A.appendChild(F)}}function et(){const x=t!==void 0&&t.available()?"canvas":"data";ne.dataset.mode=x,de.classList.toggle("is-hidden",x!=="canvas"),Ue.classList.toggle("is-hidden",x==="canvas"),ke(x),x!==o&&(t!==void 0&&(x==="canvas"?t.activate():t.deactivate()),o=x)}function Oe(k){for(;k.firstChild!==null;)k.removeChild(k.firstChild);return k}function tt(k){s=k,((k==null?void 0:k.kind)!=="canvas"||(c==null?void 0:c.name)!==k.row.name)&&(c=null),(k==null?void 0:k.kind)!=="canvas"&&(h=null),pe(),ne.classList.remove("is-rail-open")}function st(){for(let x=0;x<ye.children.length;x++){const F=ye.children[x];F.classList.toggle("is-active",F.dataset.filter===r)}if(n===null){D.replaceChildren(L("div","wb-muted")),D.firstElementChild.textContent="载入中…",M.setBadge("directory","");return}const k=Zo(D,{state:n,logs:m,logTotal:C,titles:N,search:i,filter:r,now:e.now(),openChat:Fe,selectCanvas:x=>tt({kind:"canvas",row:x})});M.setBadge("directory",k)}function St(){const k=Oe(le);if(n===null){M.setBadge("hosts","");return}const x=e.now(),F=[...n.peers??[]].sort((A,T)=>{const j=Q=>Q.link==="linked"?0:1;return j(A)-j(T)||(T.score??0)-(A.score??0)});if(M.setBadge("hosts",F.length>0?`${F.filter(A=>A.link==="linked").length}/${F.length}`:""),F.length===0){k.appendChild(L("div","wb-nodelist-empty")).textContent="暂无 tracked peer——配 peers 种子或等 referral 到达";return}for(const A of F){const T=os(A.url),j=L("div","wb-host-row"),Q=L("span","wb-host-lamp"),se=A.lastContactAt,U=se===void 0?void 0:x-se,ge=A.lastContactOk;U===void 0?Q.classList.add("is-unknown"):ge===!1&&U<=5*6e4?Q.classList.add("is-fail"):U<=6e4?Q.classList.add("is-live"):U<=5*6e4?Q.classList.add("is-warm"):Q.classList.add("is-cold"),Q.title=se===void 0?"尚无接触记录":`最近接触 ${Rt(se,x)}${ge===!1?"(失败)":ge===!0?"(成功)":""}`,j.appendChild(Q),j.appendChild(L("span","wb-host-name")).textContent=A.name!==void 0&&A.name!==""?A.name:T;const De=L("span",`wb-host-badge ${A.link==="linked"?"is-linked":"is-stranger"}`);De.textContent=`${A.link==="linked"?"linked":"stranger"} ${A.verified===!0?"✓":"✗"}`,De.title=A.link==="linked"?`已建立运营者链接(互种或 canvas 引用);${A.verified===!0?"验签有效":"尚未持有验签通过的卡片"}`:"无运营者关系(验签 ≠ 关系)",j.appendChild(De),A.version!==void 0&&A.version!==""&&(j.appendChild(L("span","wb-host-ver")).textContent=A.version),j.appendChild(L("span","wb-host-url mono")).textContent=T,j.setAttribute("role","button"),j.tabIndex=0,j.title=`${A.url} · score ${String(A.score??0)}`;const Ie=()=>{Fe({kind:"peer",id:`peer-${T}`,title:A.name!==void 0&&A.name!==""?A.name:T,sub:T,url:A.url})};j.addEventListener("click",Ie),j.addEventListener("keydown",ot=>{(ot.key==="Enter"||ot.key===" ")&&(ot.preventDefault(),Ie())}),k.appendChild(j)}}function Ze(){if(n===null){Se.replaceChildren(),M.setBadge("ledger","");return}const k=n,x=A=>{const T=(k.sessions??[]).find(Q=>Q.team===A);if(T!==void 0)return{kind:"session",id:T.id,title:mt(T),sub:T.team,team:T.team,live:T.live};const j=(k.remote??[]).find(Q=>Q.team===A);if(j!==void 0)return{kind:"remote",team:j.team,title:mt(j),sub:j.team,row:j}},F=Do(Se,{state:k,now:e.now(),abandon:(A,T)=>e.abandon(A,T),canOpenTeam:A=>x(A)!==void 0,openTeam:A=>{const T=x(A);return T===void 0?!1:(Fe(T),!0)}});M.setBadge("ledger",F.badge,F.tone)}function Ve(k,x,F){Qt(k.appendChild(L("span","wb-row-team")),x,F)}function Nt(){var Q;const k=Oe(R);if(n===null)return;const x=e.now(),F=n.inFlight??[];M.setBadge("activity",F.length>0?String(F.length):"",F.length>0?"accent":void 0);const A=L("div","wb-pane-head");if(A.textContent=`在途 ${F.length} · 活动 ${((Q=n.activity)==null?void 0:Q.length)??0}`,k.appendChild(A),F.length>0){const se=L("div","wb-pane-subhead");se.textContent="在途路由",k.appendChild(se);for(const U of F){const ge=L("div","wb-row");ge.appendChild(L("span","wb-row-dir")).textContent="→",Ve(ge,n,U.team),ge.appendChild(L("span","wb-row-meta")).textContent=`${U.peer} · ${Rt(U.startedAt,x)}`,k.appendChild(ge)}}const T=L("div","wb-pane-subhead");T.textContent="活动环(最新在前)",k.appendChild(T);const j=n.activity??[];if(j.length===0){k.appendChild(L("div","wb-muted")).textContent="暂无活动";return}for(const se of j){const U=L("div","wb-row"),ge=L("span","wb-row-dir");ge.textContent=se.dir==="fwd"?"⇢":se.dir==="out"?"→":"←",se.ok!==!0&&ge.classList.add("is-fail"),U.appendChild(ge),Ve(U,n,se.team),U.appendChild(L("span","wb-row-meta")).textContent=`${se.peer} · ${Rt(se.ts,x)}`,U.appendChild(L("span","wb-row-badges")).textContent=se.ok===!0?"":"失败",k.appendChild(U)}}function Ot(k){var Q,se;const x=e.now(),F=[],A=vn(k,x);A.deadLetters.length>0&&F.push({severity:"high",text:`清账 ${A.deadLetters.length} 条死信(目标早已离开或死信化,无回执可期)`});for(const U of A.stale){const ge=U.brief===void 0||U.brief===""?"":`「${U.brief}」`;F.push({severity:"normal",text:`催办 ${U.taskId} → ${Fo(k,U.team)}(等待 ${Rt(U.startedAt,x)} 无回执)${ge}`,brief:`a2a_route 催收回执:task ${U.taskId}(team ${U.team})${ge===""?"":`原任务「${U.brief}」`}已派发 ${Rt(U.startedAt,x)},请回报结果或回执。`})}const T=(k.sessions??[]).filter(U=>U.live!==!0).map(U=>mt(U));if(T.length>0){const U=T.slice(0,3).join("、")+(T.length>3?` 等 ${T.length} 个`:"");F.push({severity:"normal",text:`冷会话 ${T.length} 个(${U}):在 GUI 打开即可唤醒加入面`})}for(const U of((Q=k.reconcile)==null?void 0:Q.rows)??[])if(U.needsRepair===!0){const ge=(k.sessions??[]).find(Ie=>Ie.id===U.id),De=ge!==void 0?`${mt(ge)}(${U.id})`:U.id;F.push({severity:"high",text:`reconcile 需修复:${De} ${U.error}`})}const j=((se=k.idempotency)==null?void 0:se.conflicts)??0;return j>0&&F.push({severity:"high",text:`幂等冲突 ${j} 例:人工核查 UNIQUE-ization 流量`}),F}function Re(){const k=Oe(q);if(n===null)return;const x=Ot(n);M.setBadge("plan",x.length>0?String(x.length):"",x.some(T=>T.severity==="high")?"danger":void 0);const F=L("div","wb-pane-head");F.textContent=x.length===0?"网络健康——无待办行动":`待办行动 ${x.length}`,k.appendChild(F);for(const T of x){const j=L("div",`wb-action${T.severity==="high"?" wb-action-high":""}`);if(j.appendChild(L("span","wb-action-text")).textContent=T.text,T.brief!==void 0){const Q=L("button","wb-btn");Q.type="button",Q.textContent="复制催办",Q.addEventListener("click",()=>e.copy(T.brief??"")),j.appendChild(Q)}k.appendChild(j)}const A=L("div","wb-pane-subhead");A.textContent="派单起草(复制后在任一会话粘贴执行)",k.appendChild(A),k.appendChild(Ye)}const Ye=L("div","wb-draft"),Ge=document.createElement("select");Ge.className="wb-select";const ft=document.createElement("textarea");ft.className="wb-textarea",ft.rows=3,ft.placeholder="任务正文:目标、验收、期限…";const wt=L("button","wb-btn wb-btn-primary");wt.type="button",wt.textContent="复制任务书",wt.addEventListener("click",()=>{const k=Ge.value,x=ft.value.trim();k===""||x===""||e.copy(`a2a_route 派单 → ${k}:
|
|
5
|
+
${x}`)}),Ye.appendChild(Ge),Ye.appendChild(ft),Ye.appendChild(wt);function d(){var T;const k=Ge.value;Oe(Ge);const x=document.createElement("option");if(x.value="",x.textContent="选择目标队…",Ge.appendChild(x),n===null)return;const F=n,A=new Map;for(const j of F.sessions??[])j.team!==void 0&&j.team!==""&&A.set(j.team,`${mt(j)}(会话队)`);for(const j of((T=F.canvas)==null?void 0:T.teams)??[])A.set(j.team,`${j.name}(canvas 队)`);for(const j of F.remote??[])A.set(j.team,`${mt(j)}(远端)`);for(const[j,Q]of A){const se=document.createElement("option");se.value=j,se.textContent=`${Q} · ${j}`,Ge.appendChild(se)}k!==""&&A.has(k)&&(Ge.value=k)}let _,I=!1,Z=null;function Y(){var k;pe(),I&&((k=e.ownerSettingsRead)==null||k.call(e).then(x=>{if(I){if(x.ok!==!0){Z=null,pe();return}_=void 0,Z={seatNames:x.seatNames??{},teamJoinAllowlist:x.teamJoinAllowlist??[],teamScopeInfo:x.teamScopeInfo!==!1,announce:x.announce===!0,announceRestartPending:x.announceRestartPending===!0},pe()}}))}function he(k){const x=L("div","wb-card"),F=L("div","wb-inspector-head");F.appendChild(L("span","wb-inspector-title")).textContent="主人设置";const A=L("button","wb-icon-btn");if(A.type="button",A.setAttribute("aria-label","关闭主人设置"),A.textContent="✕",A.addEventListener("click",()=>{I=!1,pe()}),F.appendChild(A),x.appendChild(F),e.ownerSettingsRead===void 0){const re=L("div","wb-hint");re.textContent="此宿主未提供主人设置面(旧版本)——升级后可用。",x.appendChild(re),k.appendChild(x);return}if(Z===null){const re=L("div","wb-hint");re.textContent="读取中…",x.appendChild(re),k.appendChild(x);return}const T=Z;_??(_=Yo((n==null?void 0:n.sessions)??[],T.seatNames,async re=>{var Ne;const Ce=await((Ne=e.ownerSettingsWrite)==null?void 0:Ne.call(e,{seatNames:re}));return Ce!=null&&Ce.ok?(T.seatNames=re,{ok:!0}):{ok:!1,error:(Ce==null?void 0:Ce.error)??"保存失败"}})),x.appendChild(_);const j=L("div","wb-side-head");j.textContent="入网白名单(精确团队名)",x.appendChild(j);for(const re of T.teamJoinAllowlist){const Ce=L("div","wb-member-row");Ce.appendChild(L("span","wb-member-label")).textContent=re;const Ne=L("button","wb-member-remove");Ne.type="button",Ne.textContent="移除",Ne.addEventListener("click",()=>{var it;(it=e.ownerSettingsWrite)==null||it.call(e,{teamJoinAllowlist:T.teamJoinAllowlist.filter(je=>je!==re)}).then(()=>Y())}),Ce.appendChild(Ne),x.appendChild(Ce)}const Q=L("div","wb-member-add"),se=L("input","wb-member-manual");se.type="text",se.placeholder="dsh/团队名(精确,无通配)",se.setAttribute("aria-label","新增白名单团队名");const U=L("button","wb-member-submit");U.type="button",U.textContent="加入";const ge=()=>{var Ce;const re=se.value.trim();re===""||T.teamJoinAllowlist.includes(re)||(Ce=e.ownerSettingsWrite)==null||Ce.call(e,{teamJoinAllowlist:[...T.teamJoinAllowlist,re]}).then(()=>Y())};U.addEventListener("click",ge),se.addEventListener("keydown",re=>{re.key==="Enter"&&ge()}),Q.append(se,U),x.appendChild(Q);const De=L("div","wb-side-head");De.textContent="目录可见性(teamScopeInfo)",x.appendChild(De);const Ie=L("div","wb-member-row"),ot=L("button","wb-member-submit");ot.type="button",ot.textContent=T.teamScopeInfo?"已开:仅显示已加入团队(点击放开为全舰队可见)":"已关:全舰队可见(点击收窄为已加入团队)",ot.addEventListener("click",()=>{var re;(re=e.ownerSettingsWrite)==null||re.call(e,{teamScopeInfo:!T.teamScopeInfo}).then(()=>Y())}),Ie.appendChild(ot),x.appendChild(Ie);const xt=L("div","wb-hint");xt.textContent="精度说明:teamScopeInfo 只管「目录显示范围」。关上它不会解锁任何通信准入——会话入网(S3)与目标级路由(F5)规则原样生效。",x.appendChild(xt);const yt=L("div","wb-side-head");yt.textContent="节点发布(announce)",x.appendChild(yt);const Ct=L("div","wb-member-row"),H=L("button","wb-member-submit");if(H.type="button",H.textContent=T.announce?"已开:本节点发布签名卡(点击关闭)":"已关:本节点不发布(点击开启——重启宿主后生效)",H.addEventListener("click",()=>{var re;(re=e.ownerSettingsWrite)==null||re.call(e,{announce:!T.announce}).then(()=>Y())}),Ct.appendChild(H),x.appendChild(Ct),T.announceRestartPending){const re=L("div","wb-hint");re.textContent="有 announce 变更待生效:重启宿主后按新配置发布。",x.appendChild(re)}k.appendChild(x)}function pe(){var A,T,j,Q,se;const k=Oe(ie);if(I){ue.classList.add("is-open"),he(k);return}if(ue.classList.toggle("is-open",s!==void 0),s===void 0)return;const x=s,F=L("div","wb-card");if(x.kind==="canvas"){const U=(((A=n==null?void 0:n.canvas)==null?void 0:A.teams)??[]).find(H=>H.name===x.row.name)??x.row;Hn(F,"Canvas 队",U.name),Hn(F,"路由队",U.team);const ge=L("div","wb-side-head");if(ge.textContent=`成员 · ${U.members.length}`,F.appendChild(ge),U.members.length===0){const H=L("div","wb-muted");H.textContent="空队——用下方表单添加成员",F.appendChild(H)}for(const H of U.members){const re=L("div","wb-member-row"),Ce=H.cliHeld===!0?"本机持有,无需指定":H.remote===!0?H.cliHome===null?"◇ 远端 · home 未确定":"◇ 远端":H.live===!0?"● 现场":H.joined===!0?"○ 冷启":"· 未入网";re.appendChild(L("span","wb-member-dot")).textContent=Ce+(H.provenance?" · 来源未确认"+(H.provenance==="legacy-inferred"?"(兼容推断)":""):"");const Ne=L("span","wb-member-label"),it=((n==null?void 0:n.sessions)??[]).find(Ee=>Ee.id===H.id);Ne.textContent=H.remote===!0?H.id:it!==void 0?mt(it):H.id,Ne.title=H.cliHeld===!0?"本机持有,无需指定":H.cliHome!==void 0?H.cliHome??"home 未确定":H.team??H.id,re.appendChild(Ne);const je=L("button","wb-member-remove");if(je.type="button",je.textContent="移除",je.addEventListener("click",async()=>{je.disabled=!0,await e.canvasOp("remove-member",U.name,H.id)||(je.disabled=!1)}),re.appendChild(je),F.appendChild(re),H.cliHomeCleared===!0){const Ee=L("div","wb-hint");Ee.textContent="本机落户已清除旧 home 钉;户口离线后也不会恢复旧钉。",F.appendChild(Ee)}if(H.cliHome===null&&H.cliHeld!==!0&&e.cliHomeOptions!==void 0){const Ee=JSON.stringify([U.name,H.id]);let ct=l.get(Ee);ct===void 0&&(ct=Wo(()=>e.cliHomeOptions(U.name,H.id),Ft=>e.canvasOp("add-member",U.name,H.id,Ft)),l.set(Ee,ct)),F.appendChild(ct)}}const De=(((T=n==null?void 0:n.canvas)==null?void 0:T.retainedMembers)??[]).filter(H=>H.team===U.name);for(const H of De){const re=L("div","wb-member-row");if(re.appendChild(L("span","wb-member-dot")).textContent=H.suspended?"已暂停 · 不参与路由":"来源未确认 · 窗口关闭",re.appendChild(L("span","wb-member-label")).textContent=H.member,H.suspended&&e.regulateSuspended&&((j=n==null?void 0:n.canvas)==null?void 0:j.revision)!==void 0)for(const Ce of["restore-suspended","delete-suspended"]){const Ne=L("button","wb-member-remove");Ne.type="button",Ne.textContent=Ce==="restore-suspended"?"显式恢复":"删除暂停位";const it=n.canvas.revision;Ne.addEventListener("click",async()=>{Ne.disabled=!0;const je=await e.regulateSuspended(Ce,U.name,H.member,it,H.records);je.ok||(Ne.disabled=!1,Ne.title=je.error??"状态已变,请刷新后重试")}),re.appendChild(Ne)}F.appendChild(re)}for(const H of((Q=n==null?void 0:n.canvas)==null?void 0:Q.memberSlots)??[]){if(H.name!==U.name||H.provenance!=="unknown"||!e.confirmMember||((se=n==null?void 0:n.canvas)==null?void 0:se.revision)===void 0)continue;const re=L("div","wb-hint");re.textContent=H.member+" · 来源未确认"+(H.compatibilityInference?" · 兼容推断中":"")+(H.suspended?" · 已暂停":""),F.appendChild(re);const Ce=(n.sessions??[]).filter(Ee=>Ee.joined===!0).map(Ee=>({replacement:Ee.id,label:mt(Ee)+" — 本机完整会话"}));for(const Ee of n.remote??[])Ee.team.includes("/")&&Ee.via&&Ce.push({replacement:Ee.team,label:mt(Ee)+" — "+Ee.via,peer:Ee.via});const Ne=n.canvas.revision,it=JSON.stringify([U.name,H.member,Ne,H.ownerKey,Ce]);let je=u.get(it);je||(je=Ko(Ce,Ee=>e.confirmMember(U.name,H.member,Ee.replacement,Ne,H.ownerKey,Ee.peer)),u.set(it,je)),F.appendChild(je)}if((c==null?void 0:c.name)!==U.name){const H=L("form","wb-member-add"),re=document.createElement("select");re.className="wb-member-pick",re.setAttribute("aria-label","选择成员");const Ce=document.createElement("input");Ce.type="text",Ce.className="wb-member-manual",Ce.placeholder="或输入远端句柄 zone/id8",Ce.setAttribute("aria-label","远端成员句柄");const Ne=L("button","wb-member-submit");Ne.type="submit",Ne.textContent="添加",H.appendChild(re),H.appendChild(Ce),H.appendChild(Ne),H.addEventListener("submit",async it=>{it.preventDefault();const je=Ce.value.trim(),Ee=je!==""?void 0:re.selectedOptions[0],ct=je!==""?je:(Ee==null?void 0:Ee.dataset.member)??re.value;if(ct==="")return;Ne.disabled=!0;const Ft=await e.canvasOp("add-member",U.name,ct,void 0,Ee==null?void 0:Ee.dataset.peer);Ne.disabled=!1,Ft&&(Ce.value="")}),c={name:U.name,form:H,pick:re,manual:Ce}}const Ie=c.pick,ot=Ie.value;Oe(Ie),Ie.appendChild(new Option("本机会话(现场/冷启动)…","",!0,!0));const xt=new Set(U.members.map(H=>H.id));for(const H of(n==null?void 0:n.sessions)??[])xt.has(H.id)||Ie.appendChild(new Option(`${mt(H)} — 本机`,H.id));let yt=!1;for(const H of(n==null?void 0:n.remote)??[]){if(!H.team.includes("/")||xt.has(H.team))continue;yt||(Ie.appendChild(new Option("远端会话队(目录卡片)…","",!0,!1)),yt=!0);const re=new Option(`${mt(H)} — ${H.via??"来源未确认"}`,JSON.stringify([H.team,H.via]));re.dataset.member=H.team,H.via&&(re.dataset.peer=H.via),Ie.appendChild(re)}yt||Ie.appendChild(new Option("(暂无可拉入的远端会话队——见下方说明)","",!0,!1)),ot!==""&&Array.from(Ie.options).some(H=>H.value===ot)&&(Ie.value=ot),F.appendChild(c.form);const Ct=L("div","wb-hint");Ct.textContent="成员由人类明确分配。本机写完整会话;远端必须通过已链接主机的有效名片核实并记录完整公钥来源。最小卡、离线或第三方无法验证时保留待确认,不能手输裸句柄绕过来源校验。来源确认不恢复暂停;默认兼容窗口不自动关闭。",F.appendChild(Ct),e.dispatchQuick!==void 0&&F.appendChild(me(U.team))}k.appendChild(F)}function we(k){const x=L("form","wb-dispatch-form"),F=document.createElement("textarea");F.className="wb-dispatch-text",F.placeholder="一句话任务…(≤4000 字符,异步送达,回执进台账)",F.setAttribute("aria-label","快派消息");const A=L("button","wb-dispatch-submit");A.type="submit",A.textContent="发送";const T=L("div","wb-dispatch-note");return x.appendChild(F),x.appendChild(A),x.appendChild(T),F.addEventListener("keydown",j=>{j.key!=="Enter"||j.shiftKey||j.ctrlKey||j.altKey||j.metaKey||j.isComposing||(j.preventDefault(),A.disabled||x.requestSubmit())}),x.addEventListener("submit",async j=>{var U;j.preventDefault();const Q=F.value.trim();if(Q===""||A.disabled)return;A.disabled=!0,T.textContent="派发中…";let se;try{se=await(((U=e.dispatchQuick)==null?void 0:U.call(e,k,Q))??Promise.resolve({ok:!1,error:"dispatch unavailable"}))}catch(ge){se={ok:!1,error:ge instanceof Error?ge.message:String(ge)}}A.disabled=!1,se.ok===!0&&(F.value=""),F.focus({preventScroll:!0}),T.textContent=se.ok===!0?`已派发${se.task_id!==void 0?` · ${se.task_id}`:""} · 回执将入台账(待回执)`:`失败:${se.error??"未知原因"}`}),{form:x,text:F,note:T}}function me(k){const x=L("div","wb-dispatch"),F=L("div","wb-side-head");return F.textContent="快派",x.appendChild(F),(h==null?void 0:h.team)!==k&&(h={team:k,...we(k)}),x.appendChild(h.form),x}function Fe(k){ee.open(k),ne.classList.remove("is-rail-open")}function Pe(k){if(n===null)return;const x=(n.sessions??[]).find(A=>A.id===k);if(x!==void 0){Fe({kind:"session",id:k,title:mt(x),sub:x.team,team:x.team,live:x.live});return}if(k.startsWith("peer-")){const A=k.slice(5),T=(n.peers??[]).find(j=>j.url.includes(A));Fe({kind:"peer",id:k,title:T!==void 0?T.url:A,sub:A,url:T==null?void 0:T.url});return}m.find(A=>A.id===k)!==void 0&&Fe({kind:"log",id:k,title:`…${k.slice(-12)}`,sub:"档案"})}function Je(){var U,ge;S.textContent=(n==null?void 0:n.version)!==void 0?`v${n.version}`:"",S.hidden=(n==null?void 0:n.version)===void 0;const k=Oe(oe);if(n===null)return;const x=[],F=vn(n,e.now()),A=F.deadLetters.length;A>0&&x.push({text:`死信 ${A}`,tone:"danger",title:"超过 24h TTL 仍未结算的任务(unconsumed),需在台账清账",tab:"ledger",group:"死信"});const T=F.stale.length;T>0&&x.push({text:`陈旧 ${T}`,tone:"warn",title:"我方派发、要回执、已等待 ≥1h 仍未回执(可催办;免回执行不算)",tab:"ledger",group:"陈旧"});const j=F.owedOverdue.length;j>0&&x.push({text:`欠回执 ${j}`,tone:"warn",title:"对方投递给本机、已等待本机回执超过 1 小时",tab:"ledger",group:"待本机回执"});const Q=((U=n.idempotency)==null?void 0:U.conflicts)??0;Q>0&&x.push({text:`幂等冲突 ${Q}`,tone:"danger",title:"同一幂等键对应了不同载荷,需人工核查",tab:"ledger",group:"取证"});const se=(((ge=n.reconcile)==null?void 0:ge.rows)??[]).filter(De=>De.needsRepair===!0).length;if(se>0&&x.push({text:`需修复 ${se}`,tone:"danger",title:"reconcile 无法自动恢复的会话,见行动计划",tab:"plan"}),x.length===0){k.appendChild(L("span","wb-header-ok")).textContent="● 无异常";return}for(const De of x){const Ie=L("button",`wb-alert wb-alert-${De.tone}`);Ie.type="button",Ie.textContent=De.text,Ie.title=`${De.title}(点击查看)`,Ie.addEventListener("click",()=>Wt(De.tab,De.group)),k.appendChild(Ie)}}function Wt(k,x){var T;const F=Ke.find(([j])=>j===k)[1];o==="canvas"&&(ne.classList.add("is-rail-open"),M.select(k,{focus:!1}));const A=x===void 0?F:x==="取证"?F.querySelector("details.wb-led-forensics"):Array.from(F.querySelectorAll(".wb-led-group")).find(j=>j.dataset.group===x)??null;A!==null&&(A instanceof HTMLDetailsElement&&(A.open=!0),(T=A.scrollIntoView)==null||T.call(A,{block:"start",behavior:"smooth"}))}function nt(k){var A,T,j;const x=new Set((((A=k==null?void 0:k.canvas)==null?void 0:A.teams)??[]).flatMap(Q=>Q.members.filter(se=>se.cliHome===null&&se.cliHeld!==!0).map(se=>JSON.stringify([Q.name,se.id]))));for(const Q of l.keys())x.has(Q)||l.delete(Q);const F=((T=k==null?void 0:k.canvas)==null?void 0:T.memberSlots)??[];for(const Q of u.keys()){const[se,U,ge]=JSON.parse(Q);(ge!==((j=k==null?void 0:k.canvas)==null?void 0:j.revision)||!F.some(De=>De.name===se&&De.member===U&&De.provenance==="unknown"))&&u.delete(Q)}n=k,e.sessionLogList!==void 0&&!J&&(J=!0,e.sessionLogList().then(Q=>{m=Q.ok&&Q.sessions!==void 0?Q.sessions:[],C=Q.ok?Q.count:void 0}).catch(()=>{}).finally(()=>{J=!1,st()})),Je(),st(),d(),St(),Ze(),ee.update(k),Nt(),Re(),pe(),et()}function pt(k){if(k==="board"){et();return}M.select(k)}return nt(null),{root:ne,update:nt,mode:()=>o,selectTab:pt,openNodeChat:Pe}}const kt=1e6,Nn=.25,$n=3,fo=256,po=96;function _t(e){const t=typeof e=="number"?e:Number.NaN;if(Number.isFinite(t))return Math.max(-kt,Math.min(kt,Math.round(t)))}function ho(e){if(e===null||typeof e!="object")return;const t=e,o=_t(t.x),s=_t(t.y);return o===void 0||s===void 0?void 0:{x:o,y:s}}function rs(e){const t=ho(e);if(t===void 0)return;const o=e,s=_t(o.w),n=_t(o.h);if(!(s===void 0||n===void 0||s<=0||n<=0))return{...t,w:Math.min(s,kt),h:Math.min(n,kt)}}const bo=e=>e!==""&&!e.includes("/")&&(e.startsWith("roster:")?e.length>7&&e.length<=47:e.length<=40);function go(e){if(e===null||typeof e!="object")return;const t=e;if(t.version!==1)return;const o=t.viewport;if(o===null||typeof o!="object")return;const s=o,n=_t(s.x),i=_t(s.y),r=s.scale,c=typeof r=="number"?r:Number.NaN;if(n===void 0||i===void 0||!Number.isFinite(c))return;const l=Math.max(Nn,Math.min($n,c)),u={};if(t.nodes!==null&&typeof t.nodes=="object")for(const[m,C]of Object.entries(t.nodes)){if(typeof m!="string"||m===""||m.length>128)continue;const N=ho(C);if(N!==void 0&&(u[m]=N),Object.keys(u).length>=fo)break}const h={};if(t.frames!==null&&typeof t.frames=="object")for(const[m,C]of Object.entries(t.frames)){if(!bo(m))continue;const N=rs(C);if(N!==void 0&&(h[m]=N),Object.keys(h).length>=po)break}return{version:1,viewport:{x:n,y:i,scale:l},nodes:u,frames:h}}function as(e,t,o){const s=r=>Math.max(-kt,Math.min(kt,Math.round(r))),n={};for(const[r,c]of t)if(!(typeof r!="string"||r===""||r.length>128)&&(n[r]={x:s(c.x),y:s(c.y)},Object.keys(n).length>=fo))break;const i={};for(const[r,c]of o){if(!bo(r))continue;const l=s(c.w),u=s(c.h);if(!(l<=0||u<=0)&&(i[r]={x:s(c.x),y:s(c.y),w:Math.min(l,kt),h:Math.min(u,kt)},Object.keys(i).length>=po))break}return{version:1,viewport:{x:s(e.x),y:s(e.y),scale:Math.max(Nn,Math.min($n,e.scale))},nodes:n,frames:i}}function vo(e){let t=2166136261;for(let o=0;o<e.length;o++)t^=e.charCodeAt(o),t=Math.imul(t,16777619)>>>0;return t>>>0}const ze=172,Be=56,$t=[150,290,430],ut=16;function cs(e){const t=vo(e),o=Math.floor(t/ut)%$t.length,n=t%ut/ut*Math.PI*2+o*(Math.PI/ut),i=$t[o];return{x:Math.round(Math.cos(n)*i),y:Math.round(Math.sin(n)*i*.6)}}function yo(e){const t=new Map;for(const o of e)for(const s of o.members)t.has(s.id)||t.set(s.id,o.name);return t}const jn=64,Bn=24,Yn=20,ls=4,Wn=24,Kn=44,ds=24;function ms(e,t,o=Number.POSITIVE_INFINITY){const s=new Set(t),n=yo(e),i=e.map(m=>[...new Set(m.members.map(C=>C.id))].filter(C=>s.has(C)&&n.get(C)===m.name));i.push(t.filter(m=>!n.has(m)));const r=new Map;let c=0,l=0,u=0,h=0;for(const m of i){if(m.length===0)continue;const C=Math.min(3,Math.max(m.length>1?2:1,Math.ceil(Math.sqrt(m.length)))),N=Math.ceil(m.length/C),J=Math.max(260,C*ze+(C-1)*Bn+Wn*2),ne=Math.max(160,N*Be+(N-1)*Yn+Kn+ds);h>0&&(h%ls===0||c+J>o)&&(c=0,l+=u+jn,u=0),m.forEach((G,$)=>{const E=$%C,S=Math.floor($/C);r.set(G,{x:c+Wn+ze/2+E*(ze+Bn),y:l+Kn+Be/2+S*(Be+Yn)})}),c+=J+jn,u=Math.max(u,ne),h+=1}return r}const Jt=12;function wo(e,t){const o=l=>t.every(u=>Math.abs(u.x-l.x)>=ze+Jt||Math.abs(u.y-l.y)>=Be+Jt),s=cs(e);if(o(s))return s;const n=vo(e),i=ut*$t.length,r=Math.floor(n/ut)%$t.length*ut+n%ut;for(let l=1;l<i;l++){const u=(r+l)%i,h=Math.floor(u/ut),m=u%ut/ut*Math.PI*2+h*(Math.PI/ut),C=$t[h],N={x:Math.round(Math.cos(m)*C),y:Math.round(Math.sin(m)*C*.6)};if(o(N))return N}const c=$t[$t.length-1];for(let l=0;;l++)for(let u=0;u<6;u++){const h={x:Math.round(-c+u*(ze+Jt)),y:Math.round(c*.6+Be*2+l*(Be+Jt))};if(o(h))return h}}function vt(e){return{x:e.x-ze/2,y:e.y-Be/2,w:ze,h:Be}}function Mn(e){if(e.length===0)return{x:0,y:0,w:260,h:160};let t=Number.POSITIVE_INFINITY,o=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,n=Number.NEGATIVE_INFINITY;for(const h of e)t=Math.min(t,h.x),o=Math.min(o,h.y),s=Math.max(s,h.x+h.w),n=Math.max(n,h.y+h.h);const i=24,r=44,c=24,l=Math.max(260,Math.round(s-t+i*2)),u=Math.max(160,Math.round(n-o+r+c));return{x:Math.round(t-i),y:Math.round(o-r),w:l,h:u}}class Sn{constructor(){Lt(this,"revisionCounter",0);Lt(this,"nodes",new Map);Lt(this,"frames",new Map);Lt(this,"selection",new Set);Lt(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,o,s){let n=!1;for(const i of t){const r=this.nodes.get(i);r!==void 0&&(r.x+=o,r.y+=s,n=!0)}n&&this.bump()}beginFrameDrag(t){const o=this.frames.get(t);if(o!==void 0)return{name:t,rect:{...o}}}applyFrameDrag(t,o,s){const n=this.frames.get(t.name);n!==void 0&&(n.x=t.rect.x+o,n.y=t.rect.y+s,this.bump())}nudge(t,o,s){this.dragNodes(t,o,s)}marqueeSelect(t,o){o||this.selection.clear();for(const s of this.nodes.values()){const n=vt(s);n.x<t.x+t.w&&n.x+n.w>t.x&&n.y<t.y+t.h&&n.y+n.h>t.y&&this.selection.add(s.id)}this.bump()}setSelection(t){this.selection=new Set(t),this.bump()}upsertNode(t){const o=this.nodes.get(t.id);if(o===void 0){this.nodes.set(t.id,t),this.bump();return}o.label=t.label,o.name=t.name,o.description=t.description,o.live=t.live,o.memberships=t.memberships,this.bump()}removeNode(t){this.nodes.delete(t)&&(this.selection.delete(t),this.bump())}setFrame(t,o){this.frames.set(t,o),this.bump()}removeFrame(t){this.frames.delete(t)&&this.bump()}upsertScore(t,o){const s=this.nodes.get(t);s===void 0||s.score===o||(s.score=o,this.bump())}positions(){const t=new Map;for(const[o,s]of this.nodes)t.set(o,{x:s.x,y:s.y});return t}frameRects(){const t=new Map;for(const[o,s]of this.frames)t.set(o,{...s});return t}teamMemberIds(t){const o=[];for(const s of this.nodes.values()){const n=s.memberships.find(i=>i.team===t);n!==void 0&&o.push({id:s.id,index:n.index})}return o.sort((s,n)=>s.index-n.index).map(s=>s.id)}setTeamMembers(t,o){for(const s of this.nodes.values()){const n=s.memberships.filter(r=>r.team!==t),i=o.indexOf(s.id);i>=0&&n.push({team:t,index:i}),s.memberships=n}this.bump()}contentBounds(){let t=Number.POSITIVE_INFINITY,o=Number.POSITIVE_INFINITY,s=Number.NEGATIVE_INFINITY,n=Number.NEGATIVE_INFINITY;const i=r=>{t=Math.min(t,r.x),o=Math.min(o,r.y),s=Math.max(s,r.x+r.w),n=Math.max(n,r.y+r.h)};for(const r of this.nodes.values())r.remote!==!0&&i(vt(r));for(const r of this.frames.values())i(r);return t===Number.POSITIVE_INFINITY?null:{minX:t,minY:o,maxX:s,maxY:n}}static fromState(t){const o=new Sn,s=go(t.layout),n=new Map;for(const i of t.teams)for(let r=0;r<i.members.length;r++){const c=i.members[r].id,l=n.get(c)??[];l.push({team:i.name,index:r}),n.set(c,l)}for(const i of t.sessions){if(i.joined!==!0)continue;const r=s==null?void 0:s.nodes[i.id];let c,l;if(r!==void 0&&typeof r.x=="number"&&typeof r.y=="number")c=r.x,l=r.y;else{const u=wo(i.id,[...o.nodes.values()]);c=u.x,l=u.y}o.nodes.set(i.id,{id:i.id,x:c,y:l,label:i.label,team:i.team,name:i.name,description:i.description,live:i.live!==!1,memberships:n.get(i.id)??[]})}for(const i of t.teams){const r=s==null?void 0:s.frames[i.name];if(r!==void 0){o.frames.set(i.name,{x:r.x,y:r.y,w:r.w,h:r.h});continue}const c=[];for(const l of i.members){const u=o.nodes.get(l.id);u!==void 0&&c.push(vt(u))}c.length>0&&o.frames.set(i.name,Mn(c))}return o.viewport=s!==void 0?{...s.viewport}:{x:0,y:0,scale:1},o}}const us=24,xo=-13,fs=xo+us;function ps(e,t,o){const s=[],n=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,l=r.y+fs;for(const u of i.members){const h=o.get(u.id);if(h===void 0)continue;const m=n.has(u.id);n.add(u.id),s.push({x1:c,y1:l,x2:h.x,y2:h.y-Be/2,dashed:m,team:i.name,id:u.id})}}return s}function hs(e,t){for(;e.firstChild!==null;)e.firstChild.remove();const o=t.allFrames().map(([s])=>({name:s,members:t.teamMemberIds(s).map(n=>({id:n}))}));for(const s of ps(o,new Map(t.allFrames()),t.positions())){const n=document.createElementNS("http://www.w3.org/2000/svg","line");n.setAttribute("class","e-member"+(s.dashed?" dashed":"")),n.setAttribute("x1",String(s.x1)),n.setAttribute("y1",String(s.y1)),n.setAttribute("x2",String(s.x2)),n.setAttribute("y2",String(s.y2)),e.appendChild(n)}}function bs(e){return`peer-${gt(e).replace(/[^A-Za-z0-9.:\[\]-]/g,"-")}`}function gt(e){try{return new URL(e).host}catch{return e}}function gs(e){const t=new Map;for(const o of e){const s=o.via!==void 0&&o.via!==""?gt(o.via):"",n=t.get(s)??[];n.push(o),t.set(s,n)}return t}const vs=140,ys=200,ws=90,xs=20,qt=60;function Cs(e,t){const o=t!==null?t.maxX+vs:ys,s=t!==null?t.minY:0;return e.map((n,i)=>({url:n.url,score:n.score,x:o,y:s+i*ws}))}function ks(e,t,o,s){const n=new Map;for(const r of e){if(!t.has(r.team))continue;const c=`${r.team}\0${r.peer}`,l=n.get(c);l===void 0?n.set(c,{team:r.team,peer:r.peer,startedAt:r.startedAt,count:1}):(l.startedAt=Math.min(l.startedAt,r.startedAt),l.count+=1)}const i=[];for(const r of n.values()){const c=t.get(r.team);if(c===void 0)continue;const l=o.find(h=>{try{return new URL(h.url).host===r.peer}catch{return!1}});if(l===void 0)continue;const u=Math.max(0,Math.round((s-r.startedAt)/1e3));i.push({x1:c.x,y1:c.y,x2:l.x,y2:l.y,label:`a2a_route · ${r.peer} · ${u}s`+(r.count>1?` ×${r.count}`:"")})}return i}function Es(e,t){const o=t!==null?{x:t.maxX+xs,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,n)=>({x1:o.x,y1:o.y,x2:s.x,y2:s.y,label:n===0?"gns referral":""}))}function Ns(e,t){if(e===null&&t.length===0)return null;let o=e!==null?e.minX:Number.POSITIVE_INFINITY,s=e!==null?e.minY:Number.POSITIVE_INFINITY,n=e!==null?e.maxX:Number.NEGATIVE_INFINITY,i=e!==null?e.maxY:Number.NEGATIVE_INFINITY;for(const r of t)o=Math.min(o,r.x-qt),s=Math.min(s,r.y-qt),n=Math.max(n,r.x+qt),i=Math.max(i,r.y+qt);return{minX:o,minY:s,maxX:n,maxY:i}}const $s=11;function Ms(e,t){let o=[],s=[],n=[];function i(c){for(const h of o)h.remove();o=[];const l=t.allNodes().filter(h=>h.remote===!0).map(h=>({url:h.peerUrl??h.id.slice(5),score:h.score,x:h.x,y:h.y}));s=l;const u=t.contentBounds();if(u!==null){const h=new Map;for(const[m,C]of t.allFrames())h.set(m,{name:m,x:C.x+C.w/2,y:C.y+$s});for(const m of ks(n,h,l,c))o.push(...r(m,"e-activity",m.label));for(const m of Es(l,u))o.push(...r(m,"e-federal",m.label))}}function r(c,l,u){const h=document.createElementNS("http://www.w3.org/2000/svg","line");h.setAttribute("class",l),h.setAttribute("x1",String(c.x1)),h.setAttribute("y1",String(c.y1)),h.setAttribute("x2",String(c.x2)),h.setAttribute("y2",String(c.y2)),e.appendChild(h);const m=[h];if(u!==""){const C=document.createElementNS("http://www.w3.org/2000/svg","text");C.setAttribute("class","edge-label mono"),C.setAttribute("x",String((c.x1+c.x2)/2+14)),C.setAttribute("y",String((c.y1+c.y2)/2+18)),C.textContent=u,e.appendChild(C),m.push(C)}return m}return{render:i,flyTo(c){return c(),s},placements:()=>s,setInFlight:c=>{n=c}}}function Co(e,t){const o=new Map,s=new Set;for(const i of e){if(i.joined!==!0)continue;const r=i.team.lastIndexOf("/");if(r<=0||i.team.slice(r+1)!==i.id.replace(/^session-/,"").slice(0,8))continue;const c=i.team;t.has(c)||s.has(c)||(o.has(c)?(o.delete(c),s.add(c)):o.set(c,i.id))}const n=new Map;for(const i of e)if(i.joined===!0&&i.memberRef){const r=n.get(i.memberRef)??[];r.push(i.id),n.set(i.memberRef,r)}for(const[i,r]of n)r.length===1&&o.set(i,r[0]);return o}function Ss(e,t,o,s){var r;const n=t.get(e);return n!==void 0?n:e.includes("/")?`member-${e.replace(/[^a-z0-9-]/gi,"-")}`:`member-${((r=o==null?void 0:o.find(c=>c.via===s))==null?void 0:r.team.split("/")[0])??"dsh"}-${e.replace(/^session-/,"").slice(0,8)}`}function As(e){const t=new Map;for(const o of e)for(let s=0;s<o.members.length;s++){const n=o.members[s].id,i=t.get(n)??[];i.push({team:o.name,index:s}),t.set(n,i)}return t}function Ls(e,t,o){const s=Co(t,new Set((o??[]).map(i=>i.team))),n=new Set;for(const i of e)for(const r of i.org)for(const c of r.members){const l=s.get(c);l!==void 0&&n.add(l)}return n}const Xn={x:0,y:0,scale:1};function on(e){return Math.max(Nn,Math.min($n,e))}function It(e){const t=Number.isFinite(e.scale)?e.scale:1,o=Number.isFinite(e.x)?e.x:0,s=Number.isFinite(e.y)?e.y:0;return{x:o,y:s,scale:on(t)}}function ht(e,t,o){return{x:e.x+t/e.scale,y:e.y+o/e.scale}}function hn(e,t,o){return{x:e.x+t/e.scale,y:e.y+o/e.scale,scale:e.scale}}function Un(e,t,o,s){const n=on(e.scale*t);return n===e.scale?e:{x:e.x+o/e.scale-o/n,y:e.y+s/e.scale-s/n,scale:n}}const Is=680,Ts=.8,_s=.6,Jn=16;function An(e,t,o,s=Number.POSITIVE_INFINITY){if(e===null||t<=0||o<=0)return Xn;const n=e.maxX-e.minX,i=e.maxY-e.minY;if(!(n>0)||!(i>0))return Xn;const r=.9*Math.min(t/n,o/i),c=Math.min(s,r),l=t<Is?Ts:_s;if(r<l){const h=on(Math.min(s,l));return{x:e.minX-Jn/h,y:e.minY-Jn/h,scale:h}}const u=on(c);return{x:(e.minX+e.maxX)/2-t/(2*u),y:(e.minY+e.maxY)/2-o/(2*u),scale:u}}function Os(e){return`translate(${-e.x*e.scale}px, ${-e.y*e.scale}px) scale(${e.scale})`}function qn(e,t){let o=e===null?null:{...e,minY:e.minY+xo};for(const s of t){if(s.remote!==!0)continue;const n=vt(s);o=o===null?{minX:n.x,minY:n.y,maxX:n.x+n.w,maxY:n.y+n.h}:{minX:Math.min(o.minX,n.x),minY:Math.min(o.minY,n.y),maxX:Math.max(o.maxX,n.x+n.w),maxY:Math.max(o.maxY,n.y+n.h)}}return o}function zn(e,t,o,s){const n=s>0&&s<o/2?s:0,i=An(e,t,o-n,1);return n===0||e===null?i:{...i,y:i.y-n/i.scale}}function Vn(...e){const t=Math.max(0,...e.map(o=>o.offsetHeight>0?o.offsetTop+o.offsetHeight:0));return t>0?t+8:0}function Yt(e){return e.type==="join-network"||e.type==="leave-network"?"$network":e.type==="create-team"||e.type==="remove-team"?e.name:e.team}function Fs(e,t){if(t.type==="create-team"){const n=t.ids.map(i=>e.getNode(i)).filter(i=>i!==void 0).map(i=>vt(i));return n.length>0&&e.setFrame(t.name,Mn(n)),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 n=e.teamMemberIds(t.team),i=t.ids.filter(r=>!n.includes(r));return e.setTeamMembers(t.team,[...n,...i]),()=>{e.setTeamMembers(t.team,e.teamMemberIds(t.team).filter(r=>!i.includes(r)))}}if(t.type==="remove-member"){const n=e.teamMemberIds(t.team);return e.setTeamMembers(t.team,n.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 n=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,n)}}if(t.type==="join-network"||t.type==="leave-network")return()=>{};const o=e.teamMemberIds(t.team);let s=[...o];for(const n of t.ops)n.op==="remove"?s=s.filter(i=>i!==n.id):s.push(n.id);return e.setTeamMembers(t.team,s),()=>{let n=e.teamMemberIds(t.team);for(let i=0;i<o.length;i++){const r=o[i];n[i]!==r&&n.includes(r)&&(n=n.filter(c=>c!==r),n.push(r))}e.setTeamMembers(t.team,n)}}function Gn(e,t,o,s,n){if(o||s(),!(t.type==="remove-team"&&o||t.type==="create-team"&&!o&&t.created===!0)||n===void 0||e.getFrame(Yt(t))!==void 0)return n;const r={...n.frames};return delete r[Yt(t)],{...n,frames:r}}function xn(e,t){const o=[...e],s=[];for(let n=0;n<t.length;n++){const i=t[n];o[n]!==i&&o.includes(i)&&(s.push({op:"remove",id:i}),s.push({op:"add",id:i}),o.splice(o.indexOf(i),1),o.push(i))}return s}function Ps(e,t){return e.teamMemberIds(t).map(o=>{var s;return{id:o,y:((s=e.getNode(o))==null?void 0:s.y)??0}}).sort((o,s)=>o.y-s.y).map(o=>o.id)}function zt(e,t){let o,s=Number.POSITIVE_INFINITY;for(const[n,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&&(o=n,s=r)}return o}const Qn=[9133302,3900150,1357990,16096779,15485081,8702998];function Rs(e){let t=0;for(let c=0;c<e.length;c++)t=(t*31+e.charCodeAt(c)&268435455)>>>0;const o=Qn[t%Qn.length],s="#"+o.toString(16).padStart(6,"0"),n=o>>16&255,i=o>>8&255,r=o&255;return{line:s,bg:`rgba(${n},${i},${r},0.05)`}}const sn="roster:",ko=e=>e.startsWith(sn)?e.slice(sn.length):void 0,Ht=e=>sn+e,rn=e=>{const t=ko(e);return t===void 0?e:`网络队 · roster · ${t}`};function Ds(e,t){return e.flatMap(o=>{if(t===""||!o.team.startsWith(t+"/")||!Array.isArray(o.members))return[];const s=o.team.slice(t.length+1);return s===""||s.length>40||s.includes("/")||s.startsWith(sn)||s.includes("*")?[]:[{name:Ht(s),team:o.team,kind:"roster",members:o.members.filter(n=>typeof n=="string"&&n!=="").map(n=>({id:n}))}]})}function Hs(e){const{root:t,netBtn:o,signal:s,model:n}=e,i=y=>e.isRoster(y)?rn(y):y;function r(){return n.selectedIds().map(y=>{var b;return{id:y,y:((b=n.getNode(y))==null?void 0:b.y)??0}}).sort((y,b)=>y.y-b.y).map(y=>y.id)}function c(){if(l!==null)return;const y=r().filter(b=>{const M=n.getNode(b);return M===void 0?!1:M.remote!==!0||M.remoteHandle!==void 0&&M.remoteHandle!==""});if(y.length<2){e.notice("info","框选至少 2 个会话节点");return}u(y)}let l=null;function u(y){const b=document.createElement("div");b.className="p-dialog";const M=document.createElement("div");M.className="p-dialog-panel",M.setAttribute("role","dialog"),M.setAttribute("aria-modal","true"),M.setAttribute("aria-labelledby","p-dialog-title");const f=document.createElement("div");f.id="p-dialog-title",f.className="p-dialog-title",f.textContent=`组建团队(${y.length} 个节点,自上而下即优先级)`;const D={value:"canvas"},K=document.createElement("div");K.className="p-dialog-kind",K.setAttribute("role","radiogroup"),K.setAttribute("aria-label","团队类型");const X=document.createElement("div");X.className="p-dialog-hint";const ye={canvas:"canvas 队:画布上的协作组,可以加入远端成员。",roster:"roster 网络队:只收本机已入网的会话;队名会写进属主的精确允许名单。"},Se=["canvas","roster"].map(ie=>{const ee=document.createElement("button");return ee.type="button",ee.dataset.kind=ie,ee.setAttribute("role","radio"),ee.textContent=ie==="canvas"?"canvas 队":"roster 网络队",ee.addEventListener("click",()=>R(ie)),K.appendChild(ee),ee});function R(ie){D.value=ie;for(const ee of Se){const ke=ee.dataset.kind===ie;ee.setAttribute("aria-checked",String(ke)),ee.tabIndex=ke?0:-1}X.textContent=ye[ie],le.textContent=""}K.addEventListener("keydown",ie=>{var ke;const ee=ie;["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(ee.key)&&(ee.preventDefault(),R(D.value==="canvas"?"roster":"canvas"),(ke=Se.find(et=>et.dataset.kind===D.value))==null||ke.focus())});const q=document.createElement("input");q.className="p-dialog-input",q.maxLength=40,q.setAttribute("aria-label","团队名");const le=document.createElement("div");le.className="p-dialog-err",le.setAttribute("aria-live","polite");const Ke=document.createElement("div");Ke.className="p-dialog-row";const Me=document.createElement("button");Me.type="button",Me.textContent="组建";const de=document.createElement("button");de.type="button",de.textContent="取消",Ke.append(Me,de),R("canvas"),M.append(f,K,X,q,le,Ke),b.appendChild(M),t.appendChild(b);const Ue=document.activeElement,ue=()=>{b.remove(),l=null,Ue==null||Ue.focus()},V=()=>{const ie=q.value.trim();return ie===""||ie.length>40||ie.includes("/")||ie.startsWith("roster:")||D.value==="roster"&&ie.includes("*")||/^\d+$/.test(ie)?"队名需 1..40 字,不含“/”、通配符或保留前缀,不为纯数字":D.value==="roster"&&n.getFrame(ie)!==void 0?"同名 canvas 队已存在,不能创建 roster 队":D.value==="roster"&&n.getFrame(Ht(ie))!==void 0&&!e.isRoster(Ht(ie))?"roster 框名已被 canvas 或镜像队占用":D.value==="canvas"&&n.getFrame(Ht(ie))!==void 0?"同名 roster 队已存在,不能创建 canvas 队":D.value==="roster"&&y.some(ee=>{var ke;return((ke=n.getNode(ee))==null?void 0:ke.remote)===!0||!e.joined(ee)})?"roster 仅接受本机已入网的会话":null},ce=()=>{const ie=V();if(ie!==null){le.textContent=ie,q.focus();return}const ee=D.value==="roster"?Ht(q.value.trim()):q.value.trim();if(ue(),n.getFrame(ee)!==void 0){e.notice("info",`已加入现有团队「${D.value==="roster"?rn(ee):ee}」`),e.emit({type:"add-member",team:ee,ids:y});return}e.emit({type:"create-team",name:ee,ids:y,created:!0,...D.value==="roster"?{roster:!0}:{}})};Me.addEventListener("click",ce),de.addEventListener("click",ue),q.addEventListener("keydown",ie=>{const ee=ie;ee.key==="Enter"&&(ee.preventDefault(),ce()),ee.key==="Escape"&&(ee.preventDefault(),ue())}),b.addEventListener("keydown",ie=>{const ee=ie;if(ee.key==="Escape"){ee.preventDefault(),ee.stopPropagation(),ue();return}if(ee.key!=="Tab")return;ee.preventDefault();const ke=Array.from(M.querySelectorAll('input, select, button:not([tabindex="-1"])')),et=ke.indexOf(document.activeElement),Oe=ee.shiftKey?ke[(et-1+ke.length)%ke.length]:ke[(et+1)%ke.length];Oe==null||Oe.focus()},s),l={wrap:b,restoreFocus:Ue},q.focus()}let h=null,m="root",C=null,N=null,J=[];function ne(){N==null||N.remove(),N=null}function G(){var M;if(N!==null){ne();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 f of J)y.appendChild(E(`${f.name!==void 0&&f.name!==""?f.name:f.label} — 入网`,!0,()=>{ne(),e.emit({type:"join-network",id:f.id})}));N=y,t.appendChild(y);const b=o.getBoundingClientRect();y.style.left=`${String(Math.min(b.left,window.innerWidth-220))}px`,y.style.top=`${String(Math.max(8,b.top-(J.length*30+10)))}px`,y.addEventListener("keydown",f=>{f.key==="Escape"&&(f.stopPropagation(),ne(),o.focus())}),(M=y.querySelector("button"))==null||M.focus(),document.addEventListener("pointerdown",f=>{if(N===null)return;const D=f.target;D!==null&&(D.closest(".p-netmenu")!==null||D===o||o.contains(D))||ne()},s)}function $(){h==null||h.remove(),h=null,m="root",C!==null&&C.isConnected?C.focus():t.focus(),C=null}function E(y,b,M){const f=document.createElement("button");return f.type="button",f.setAttribute("role","menuitem"),f.textContent=y,b?f.addEventListener("click",()=>{$(),M()}):(f.disabled=!0,f.setAttribute("aria-disabled","true")),f}function S(y,b,M,f="root",D){var Se,R,q,le,Ke,Me,de,Ue;D!==void 0&&(C=D),C===null&&(C=e.anchorFor(M)),h==null||h.remove(),m=f;const K=document.createElement("div");K.className="p-menu",K.setAttribute("role","menu"),K.setAttribute("aria-label","团队操作");let X=[];if(M.kind==="frame")X=[E(`解散团队「${i(M.name)}」`,!0,()=>e.emit({type:"remove-team",name:M.name}))];else{const ue=M.id;if(f==="root"){const V=E("组成团队…",n.selectedIds().length>=2,()=>u(r()));V.title="先框选或 Shift 加选至少 2 个节点",X.push(V);const ce=((Se=n.getNode(ue))==null?void 0:Se.memberships.map(ie=>ie.team))??[];e.teams().length>0&&(X.push(E("加入团队 ▸",!0,()=>S(y,b,M,"join"))),ce.length>0&&X.push(E("置顶路由 ▸",!0,()=>S(y,b,M,"promote")))),ce.length>0&&X.push(E("离队 ▸",!0,()=>S(y,b,M,"leave"))),((R=n.getNode(ue))==null?void 0:R.remote)!==!0&&X.push(E("退网",!0,()=>e.emit({type:"leave-network",id:ue})))}else{X.push(E("‹ 返回",!0,()=>S(y,b,M,"root")));for(const V of e.teams())if(f==="join"){const ce=((q=n.getNode(ue))==null?void 0:q.memberships.some(ke=>ke.team===V.name))??!0,ie=!e.isRoster(V.name)||e.joined(ue)&&((le=n.getNode(ue))==null?void 0:le.remote)!==!0,ee=E(ce?`${i(V.name)}(已加入)`:i(V.name),!ce&&ie,()=>e.emit({type:"add-member",team:V.name,ids:[ue]}));ie||(ee.title="roster 只接受本机已入网会话"),X.push(ee)}else if(f==="leave"){const ce=((Ke=n.getNode(ue))==null?void 0:Ke.memberships.some(ie=>ie.team===V.name))??!1;X.push(E(i(V.name),ce,()=>e.emit({type:"remove-member",team:V.name,ids:[ue]})))}else{const ce=((Me=n.getNode(ue))==null?void 0:Me.memberships.some(ie=>ie.team===V.name))??!1;if(e.isRoster(V.name))continue;X.push(E(i(V.name),ce,()=>{const ie=n.teamMemberIds(V.name),ee=[ue,...ie.filter(ke=>ke!==ue)];e.emit({type:"reorder",team:V.name,ops:xn(ie,ee)})}))}if(f==="leave"){const V=new Set(e.teams().map(ce=>ce.name));for(const ce of((de=n.getNode(ue))==null?void 0:de.memberships)??[])V.has(ce.team)||X.push(E(i(ce.team),!0,()=>e.emit({type:"remove-member",team:ce.team,ids:[ue]})))}}}K.append(...X),K.style.left=`${Math.min(y,window.innerWidth-200)}px`;const ye=X.length*30+8;K.style.top=`${Math.max(8,Math.min(b,window.innerHeight-ye-8))}px`,K.addEventListener("keydown",ue=>{var ee,ke,et,Oe;const V=ue,ce=Array.from(K.querySelectorAll("[role=menuitem]")),ie=ce.indexOf(document.activeElement);V.key==="ArrowDown"?(V.preventDefault(),(ee=ce[(ie+1+ce.length)%ce.length])==null||ee.focus()):V.key==="ArrowUp"?(V.preventDefault(),(ke=ce[(ie-1+ce.length)%ce.length])==null||ke.focus()):V.key==="Home"?(V.preventDefault(),(et=ce[0])==null||et.focus()):V.key==="End"?(V.preventDefault(),(Oe=ce[ce.length-1])==null||Oe.focus()):V.key==="Escape"&&(V.preventDefault(),V.stopPropagation(),m!=="root"?S(y,b,M,"root"):$())}),t.appendChild(K),h=K,(Ue=X[0])==null||Ue.focus()}function w(y,b,M){if(l!==null)return;const f=(M==null?void 0:M.closest(".p-node"))??null;if(f!==null){const X=n.getNode(f.dataset.id??"");if((X==null?void 0:X.remote)===!0&&(X.remoteHandle===void 0||X.remoteHandle===""))return}const D=f,K=(M==null?void 0:M.closest(".p-frame-head"))??null;if(D!==null){const X=D.dataset.id??"";n.isSelected(X)||n.setSelection([X]),C=D,S(y,b,{kind:"node",id:X})}else K!==null&&(C=K,S(y,b,{kind:"frame",name:K.dataset.frame??""}))}function oe(y){J=y,o.textContent=`未入网${J.length>0?`(${String(J.length)})`:""}`,o.disabled=J.length===0,o.setAttribute("aria-disabled",String(J.length===0)),J.length===0&&ne()}return{menuItem:E,isMenuOpen:()=>h!==null,isDialogOpen:()=>l!==null,closeMenu:$,openMenu:S,contextMenuAt:w,openNameDialog:u,startCreateFromSelection:c,toggleNetMenu:G,setUnjoined:oe}}function js(e,t){const o=t.trim().toLowerCase();return o===""?[]:e.filter(s=>s.name.toLowerCase().includes(o)||s.team.toLowerCase().includes(o)||(s.host??"").toLowerCase().includes(o)).slice(0,8)}function Bs(e,t){return e.allNodes().map(o=>{const s=o.id.slice(5),n=o.id.startsWith("member-")?o.name!==void 0&&o.name!==""?o.name:o.team:o.remote===!0?s:t(o);return{id:o.id,name:n,team:o.team,...o.remote===!0?{host:s}:{}}})}function Ys(e,t=1200){e!==void 0&&(e.classList.add("pulse"),setTimeout(()=>e.classList.remove("pulse"),t))}function Ws(e){return t=>{const o=e.model.getNode(t);if(o===void 0)return;const s=e.viewSize();e.setVp(An({minX:o.x,minY:o.y,maxX:o.x+ze,maxY:o.y+Be},s.w,s.h,1)),e.applyViewport(),e.markDirty(),Ys(e.cardEl(t))}}function Ks(e,t,o,s){const n=Xs({onPick:s});return e.appendChild(n.root),{...n,refresh:()=>n.setIndex(Bs(t,o))}}function Xs(e){const t=document.createElement("div");t.className="p-search";const o=document.createElement("input");o.type="search",o.className="p-search-input",o.placeholder="🔍 搜索(Ctrl+K)",o.title="搜索名字 / 句柄 / 宿主(Ctrl+K 聚焦,Esc 清空)",o.setAttribute("aria-label","搜索画板节点:名字、句柄或宿主");const s=document.createElement("div");s.className="p-search-list";let n=[],i=[],r=0;const c=()=>{s.style.display="none",s.textContent=""},l=()=>{if(s.textContent="",i.length===0){c();return}i.forEach((m,C)=>{const N=document.createElement("div");N.className="p-search-hit"+(C===r?" active":""),N.dataset.id=m.id,N.textContent=m.name===m.team?m.name:`${m.name} · ${m.team}`,N.addEventListener("mousedown",J=>{J.preventDefault(),e.onPick(m.id),u()}),s.appendChild(N)}),s.style.display="block"},u=()=>{o.value="",i=[],c()},h=()=>{i=js(n,o.value),r=0,l()};return o.addEventListener("input",h),o.addEventListener("keydown",m=>{if(m.key==="Escape"){u(),o.blur(),m.stopPropagation();return}if(m.key==="ArrowDown"||m.key==="ArrowUp"){if(i.length===0)return;m.preventDefault(),r=(r+(m.key==="ArrowDown"?1:i.length-1))%i.length,l();return}m.key==="Enter"&&i.length>0&&(m.preventDefault(),e.onPick(i[r].id),u())}),t.append(o,s),{root:t,setIndex(m){n=m,o.value!==""&&h()},focus(){o.focus(),o.select()},clear(){u()}}}const Vt=28,Zn=16,eo=14,to=48,Us=4,Js=180,qs=90,zs=1600;function Vs(e){const t=e.wrapWidth??zs,o=e.frames.map(l=>{const u=Math.max(l.soleIds.length,1),h=Math.min(Us,Math.max(1,Math.ceil(Math.sqrt(u)))),m=Math.ceil(u/h),C=Math.max(Js,Vt*2+h*ze+(h-1)*Zn),N=Math.max(qs,Vt*2+m*Be+(m-1)*eo);return{name:l.name,w:C,h:N,rows:m,perRow:h,soleIds:l.soleIds}}),s=[],n=[];let i=0,r=0,c=0;for(const l of o){i>0&&i+l.w>t&&(i=0,r+=c+to,c=0);const u={x:i,y:r,w:l.w,h:l.h};s.push({name:l.name,rect:u});const h=u.x+Vt+ze/2;let m=u.y+Vt+Be/2;l.soleIds.forEach((C,N)=>{const J=N%l.perRow;N>0&&J===0&&(m+=Be+eo),n.push({id:C,x:h+J*(ze+Zn),y:m})}),i+=l.w+to,c=Math.max(c,l.h)}return{frames:s,seats:n}}function Gs(e,t){const o=[];for(const[n,i]of e.allFrames()){if(t.has(n))continue;const r=e.teamMemberIds(n).map(c=>e.getNode(c)).filter(c=>c!==void 0).filter(c=>c.memberships.length===1).map(c=>c.id);o.push({name:n,rect:i,soleIds:r})}if(o.length===0)return!1;const s=Vs({frames:o});for(const n of s.frames)e.setFrame(n.name,n.rect);for(const n of s.seats){const i=e.getNode(n.id);i!==void 0&&e.dragNodes([n.id],n.x-i.x,n.y-i.y)}return!0}function Qs(e,t){if(e===void 0)return;const o=gt(e);return{text:t?`同步中… · ${o}`:`镜像 · ${o}`,title:t?`已把改动交给属主 ${o},等待它确认`:`这个团队属于 ${o},这里显示的是它的镜像;拖入或移出成员会经中继交给 ${o} 执行`,syncing:t}}const Zs=1e4;function ei(e=Date.now){const t=(s,n)=>`${s}\0${n}`,o=new Map;return{fork(s,n){return o.set(t(n,s.name),{url:n,name:s.name}),{...s,fleetPeer:n}},settled(s,n){if(s.type!=="remove-team"||s.name===void 0||s.fleetPeer===void 0)return;const i=o.get(t(s.fleetPeer,s.name));i!==void 0&&(n?i.acceptedAt=e():o.delete(t(s.fleetPeer,s.name)))},hides(s,n){return o.has(t(n,s))},sweep(s){var n;for(const[i,r]of o)(!(((n=s.find(l=>l.url===r.url))==null?void 0:n.org.some(l=>l.name===r.name))===!0)||r.acceptedAt!==void 0&&e()-r.acceptedAt>Zs)&&o.delete(i)}}}const Mt=24,ti=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 ni(e,t){const o=t.x+t.w+Mt-e.x,s=t.x-Mt-(e.x+e.w),n=t.y+t.h+Mt-e.y,i=t.y-Mt-(e.y+e.h),r=Math.abs(o)<=Math.abs(s)?o:s,c=Math.abs(n)<=Math.abs(i)?n:i;return Math.abs(r)<=Math.abs(c)?{dx:r,dy:0}:{dx:0,dy:c}}function oi(e,t,o,s){const n=e.beginFrameDrag(t);n!==void 0&&e.applyFrameDrag(n,o,s)}function si(e,t,o){let s=!1;for(let n=0;n<ti;n++){const i=e.allFrames();let r;for(let $=0;$<i.length&&r===void 0;$++)for(let E=$+1;E<i.length;E++){const[S,w]=i[$],[oe,y]=i[E];if(!(t.has(S)&&t.has(oe))&&Zt(w,y)){r=[S,w,oe,y];break}}if(r===void 0)return s;const[c,l,u,h]=r,m=$=>t.has($)?2:$===o?1:0,[C,N,J]=m(c)<m(u)?[c,l,h]:[u,h,l],{dx:ne,dy:G}=ni(N,J);oi(e,C,ne,G),s=!0}return s}const Et=28,en=16,an=14;function ii(e){const t=[];for(let o=e.y+Et;o+Be<=e.y+e.h-Et;o+=Be+an)for(let s=e.x+Et;s+ze<=e.x+e.w-Et;s+=ze+en)t.push({x:s+ze/2,y:o+Be/2});return t}function ri(e){let t=!1;const o=e.allFrames(),s=new Map(o);for(const n of e.allNodes()){const i=new Set(n.memberships.map(G=>G.team)),r=o.find(([G,$])=>!i.has(G)&&Zt(vt(n),$));if(r===void 0)continue;const c=r[1],l=G=>{const $=vt(G);return o.every(([E,S])=>i.has(E)||!Zt($,S))&&e.allNodes().every(E=>E.id===n.id||!Zt($,vt(E)))},h=n.memberships.map(G=>s.get(G.team)).filter(G=>G!==void 0).flatMap(ii).find(l),m={x:c.x+c.w+Mt+ze/2,y:n.y},C={x:n.x,y:c.y+c.h+Mt+Be/2},N=Math.max(...o.map(([,G])=>G.x+G.w))+Mt+ze/2,J=Array.from({length:2*e.allNodes().length+1},(G,$)=>({x:N,y:c.y+Be/2+$*(Be+an)})),ne=h??[m,C,...J].find(l)??J[J.length-1];e.dragNodes([n.id],ne.x-n.x,ne.y-n.y),t=!0}return t}function Gt(e,t,o){const s=si(e,t,o),n=ri(e);return s||n}function ai(e){for(const[t,o]of e.allFrames()){const s=e.teamMemberIds(t).map(u=>e.getNode(u)).filter(u=>u!==void 0).filter(u=>u.memberships.length===1);if(s.length===0)continue;const n=Math.max(1,Math.floor((o.w-2*Et+en)/(ze+en))),i=Math.ceil(s.length/n),r=Et*2+i*Be+(i-1)*an;r>o.h&&e.setFrame(t,{...o,h:Math.min(r,o.h+400)});const c=o.x+Et+ze/2;let l=o.y+Et+Be/2;s.forEach((u,h)=>{const m=h%n;h>0&&m===0&&(l+=Be+an);const C=c+m*(ze+en);e.dragNodes([u.id],C-u.x,l-u.y)})}}const ci=30;function li(e){const t=[],o=()=>({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 l=i.frames.get(r);return l!==void 0&&(l.x!==c.x||l.y!==c.y||l.w!==c.w||l.h!==c.h)}),n=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 l=i.frames.get(r);l!==void 0&&(l.x!==c.x||l.y!==c.y||l.w!==c.w||l.h!==c.h)&&e.setFrame(r,{...l})}};return{capture:o,commit(i){s(i)&&(t.push(i),t.length>ci&&t.shift())},undo(){const i=t.pop();return i===void 0?!1:(n(i),!0)},revert(i){return s(i)?(n(i),!0):!1},get depth(){return t.length}}}function di(e){const{root:t,trigger:o,signal:s,model:n}=e;let i=null,r="",c=null;function l(){c==null||c.remove(),c=null}function u(){l(),i==null||i.remove(),i=null}function h(){if(i!==null){u();return}const E=document.createElement("div");E.className="p-menu p-nodelist",E.setAttribute("role","dialog"),E.setAttribute("aria-label","节点清单"),i=E,t.appendChild(E);const S=o.getBoundingClientRect();E.style.left=`${String(Math.min(S.left,window.innerWidth-340))}px`,E.style.top=`${String(Math.max(8,S.top-320))}px`,E.addEventListener("keydown",w=>{w.key==="Escape"&&(w.stopPropagation(),u(),o.focus())}),m()}function m(){var M;const E=i;if(E===null)return;l(),E.textContent="";const S=document.createElement("div");S.className="p-nodelist-head";const w=document.createElement("span");w.className="p-nodelist-title",w.textContent="节点";const oe=document.createElement("select");oe.className="p-nodesel",oe.setAttribute("aria-label","选择 host 或对等节点");const y=document.createElement("option");y.value="",y.textContent="本机",oe.appendChild(y);for(const[f,D]of e.facts().remoteTeams){if(f==="")continue;const K=document.createElement("option");K.value=f,K.textContent=((M=D[0])==null?void 0:M.origin)!==void 0&&D[0].origin!==""?`${D[0].origin}(${f})`:f,oe.appendChild(K)}r!==""&&!e.facts().remoteTeams.has(r)&&(r=""),oe.value=r,oe.addEventListener("change",()=>{r=oe.value,m()}),S.append(w,oe);const b=document.createElement("div");b.className="p-nodelist-body",E.append(S,b),r===""?G(b):$(b,e.facts().remoteTeams.get(r)??[])}function C(E,S){const w=document.createElement("span");return w.className="p-nodedot"+(S!==!0?" off":E===!1?" cold":""),w}function N(E,S){const w=document.createElement("button");return w.type="button",w.className="p-listbtn",w.textContent=E,w.addEventListener("click",oe=>{oe.stopPropagation(),S()}),w}function J(E,S){const w=document.createElement("div");if(w.className="p-nodelist-group",w.textContent=E,S!==void 0){const oe=document.createElement("span");oe.className="p-nodelist-note",oe.textContent=S,w.appendChild(oe)}return w}function ne(E,S){var y,b;l();const w=document.createElement("div");w.className="p-menu",w.setAttribute("role","menu"),w.setAttribute("aria-label","加入团队");for(const M of e.facts().teams){const f=((y=n.getNode(S))==null?void 0:y.memberships.some(D=>D.team===M.name))??!1;w.appendChild(e.menuItem(f?`${M.name}(已加入)`:M.name,!f,()=>{u(),e.emit({type:"add-member",team:M.name,ids:[S]})}))}w.appendChild(e.menuItem("新团队…",!0,()=>{u(),e.openNameDialog([S])})),c=w,t.appendChild(w);const oe=E.getBoundingClientRect();w.style.left=`${String(Math.min(oe.right+4,window.innerWidth-180))}px`,w.style.top=`${String(Math.max(8,oe.top-60))}px`,w.addEventListener("keydown",M=>{M.key==="Escape"&&(M.stopPropagation(),l(),E.focus())}),(b=w.querySelector("button"))==null||b.focus()}function G(E){var b,M;const S=new Set(e.facts().canvasMemberIds);for(const f of e.facts().sessions)f.teams!==void 0&&f.teams.length>0&&S.add(f.id);const w=e.facts().sessions.filter(f=>f.joined===!0&&!S.has(f.id)),oe=e.facts().sessions.filter(f=>f.joined!==!0),y=e.facts().sessions.filter(f=>f.joined===!0&&S.has(f.id));E.appendChild(J(`未组队 (${String(w.length)})`,"无网络能力"));for(const f of w){const D=document.createElement("div");D.className="p-noderow";const K=document.createElement("span");K.className="p-nodename",K.textContent=f.name!==void 0&&f.name!==""?f.name:f.label,K.title=f.team;const X=document.createElement("span");X.className="p-nonet",X.textContent="无网络能力";const ye=N("入队▸",()=>ne(ye,f.id));D.append(C(f.live,!0),K,X,ye,N("退网",()=>{u(),e.emit({type:"leave-network",id:f.id})})),E.appendChild(D)}E.appendChild(J(`未入网 (${String(oe.length)})`));for(const f of oe){const D=document.createElement("div");D.className="p-noderow";const K=document.createElement("span");K.className="p-nodename",K.textContent=f.name!==void 0&&f.name!==""?f.name:f.label,K.title=f.id,D.append(C(f.live,!1),K,N("入网",()=>{u(),e.emit({type:"join-network",id:f.id})})),E.appendChild(D)}E.appendChild(J(`已组队 (${String(y.length)})`,"画布展示"));for(const f of y){const D=document.createElement("div");D.className="p-noderow";const K=document.createElement("span");K.className="p-nodename",K.textContent=f.name!==void 0&&f.name!==""?f.name:f.label,K.title=f.team;const X=((b=n.getNode(f.id))==null?void 0:b.memberships.length)??0,ye=((M=f.teams)==null?void 0:M.length)??0,Se=document.createElement("span");Se.className="p-teamcount",Se.textContent=`队×${String(Math.max(X,0)+ye)}`,D.append(C(f.live,!0),K,Se,N("定位",()=>{e.locate(f.id)})),E.appendChild(D)}if(e.facts().sessions.length===0){const f=document.createElement("div");f.className="p-nodelist-empty",f.textContent="无会话节点",E.appendChild(f)}}function $(E,S){var y;const w=S.filter(b=>b.teams===void 0||b.teams.length===0),oe=S.filter(b=>b.teams!==void 0&&b.teams.length>0);E.appendChild(J(`未组队 (${String(w.length)})`,"无网络能力 · 只读"));for(const b of w){const M=document.createElement("div");M.className="p-noderow";const f=document.createElement("span");f.className="p-nodename",f.textContent=b.name!==void 0&&b.name!==""?b.name:b.team,f.title=b.team;const D=document.createElement("span");D.className="p-nonet",D.textContent="无网络能力",M.append(C(!0,!0),f,D),E.appendChild(M)}E.appendChild(J(`已组队 (${String(oe.length)})`,"只读"));for(const b of oe){const M=document.createElement("div");M.className="p-noderow";const f=document.createElement("span");f.className="p-nodename",f.textContent=b.name!==void 0&&b.name!==""?b.name:b.team,f.title=b.team;const D=document.createElement("span");D.className="p-teamcount",D.textContent=`队×${String(((y=b.teams)==null?void 0:y.length)??0)}`,M.append(C(!0,!0),f,D),E.appendChild(M)}if(S.length===0){const b=document.createElement("div");b.className="p-nodelist-empty",b.textContent="该节点未发布会话",E.appendChild(b)}}return document.addEventListener("pointerdown",E=>{if(i===null)return;const S=E.target;if(S===null){u();return}S.closest(".p-nodelist")!==null||S.closest(".p-menu")!==null||S===o||o.contains(S)||u()},s),{toggle:h,close:u,refresh:()=>{i!==null&&m()}}}function mi(e){let t=e;const o=t.search(/\|| ```|^```/);return o>=0&&(t=t.slice(0,o)),t.replace(/\[([^\]]*)\]\([^)]*\)/g,"$1").replace(/(^|\s)#{1,6}\s+/g,"$1").replace(/\*\*|__|`/g,"").replace(/\s+/g," ").trim()}function ui(e,t){const o=e.map(i=>i.name!==void 0&&i.name!==""?i.name:i.team),s=o.slice(0,2).join("、"),n=o.length>2?` +${o.length-2}`:"";return(t!==void 0?`score ${t} · `:"")+`${e.length} 远端团队`+(s!==""?`:${s}${n}`:"")}function fi(e,t,o){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 l=e.id.slice(5),u=t.length===1&&t[0].name!==void 0&&t[0].name!==""?t[0].name:void 0,h=ui(t,o);return u!==void 0?{title:u,sub:`${l} · ${h}`,tip:`${u}
|
|
6
|
+
${l}
|
|
7
|
+
${h}`}:{title:l,sub:h,tip:`${l}
|
|
8
|
+
${h}`}}const s=bt(e),n=e.team+(e.name!==void 0&&e.name!==""?" · "+e.name:""),i=mi(e.description??""),r=i!==""?i:n===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!==""?`${n}
|
|
9
|
+
${i}`:n}}function pi(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 hi(e){const t=document.createElement("div");t.className="p-node",t.dataset.id=e,t.tabIndex=0;const o=document.createElement("div");o.className="nm";const s=document.createElement("span");s.className="dot";const n=document.createElement("span");n.className="nm-text",o.append(s,n);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 l=document.createElement("div");return l.className="sub-row",l.append(i,c),t.append(o,l,r),t}function bi(e,t,o){if(t.remote===!0){const s=(o==null?void 0:o.link)==="linked"?"lnk-linked":(o==null?void 0:o.link)==="stranger"?"lnk-stranger":"";e.className!==`prio mono ${s}`.trim()&&(e.className=`prio mono ${s}`.trim());const n=o===void 0?"":o.verified===!0?"✓":"✗",i=(t.id.startsWith("member-")?"成员":(o==null?void 0:o.link)??"peer")+n;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 no="dsh-a2a.board.legend";function gi(){const e=document.createElement("details");e.className="p-legend";const t=document.createElement("summary");t.textContent="图例",t.title="边的含义(点击展开 / 收起)";const o=document.createElement("div");o.className="p-legend-rows";const s=(n,i,r)=>{const c=document.createElement("span"),l=document.createElement("i");n&&(l.className="dashed"),r!==void 0&&(l.style.borderTopColor=r),c.append(l,document.createTextNode(i)),o.appendChild(c)};s(!1,"成员边(星形,框→成员)"),s(!0,"跨队多属(虚线)"),s(!0,"roster 网络队框(双线边框)"),s(!1,"活动边(inFlight 路由,瞬时)","var(--accent)"),s(!0,"联邦线(→peer)","var(--federal)"),e.append(t,o);try{e.open=window.localStorage.getItem(no)==="open"}catch{}return e.addEventListener("toggle",()=>{try{window.localStorage.setItem(no,e.open?"open":"closed")}catch{}}),e}const tn="对端未应答(显示的是最后已知状态)";function oo(e){const t=new Set;for(const s of e.remoteTeams??[])s.unreachable===!0&&s.via&&t.add(gt(s.via));for(const s of e.peerOrg??[])s.unreachable===!0&&t.add(gt(s.url));const o=new Set;for(const s of e.remoteTeams??[])s.via&&t.has(gt(s.via))&&o.add(`member-${s.team.replace(/[^a-z0-9-]/gi,"-")}`);return{hosts:t,members:o}}function vi(e,t){e.classList.toggle("is-unreachable",t);const o=e.querySelector(".p-frame-unreachable");if(t&&o===null){const s=document.createElement("span");s.className="p-frame-unreachable",s.textContent=tn,e.appendChild(s)}else t||o==null||o.remove()}function yi(e){return e<680?"narrow":e<1024?"medium":"wide"}function wi(e){const t=new Sn;let o,s={...t.viewport},n={kind:"none"},i=!1,r=-1,c=!0,l=!1;const u=li(t);let h,m=!1;const C=()=>yi(globalThis.innerWidth||tt),N=()=>{var a;(a=e.viewportStore)==null||a.set(C(),s),e.onDirty()},J=e.now??Date.now,ne=new AbortController,G=[];let $=null,E=!1;const S={signal:ne.signal},w=document.createElement("section");w.id="dsh-plan",w.setAttribute("role","tabpanel"),w.setAttribute("aria-label","A2A 规划画布"),w.tabIndex=0,w.style.display="none";const oe=document.createElement("div");oe.className="p-grid";const y=document.createElement("div");y.className="p-world";const b=document.createElementNS("http://www.w3.org/2000/svg","svg");b.setAttribute("class","p-edges");const M=document.createElement("div");M.className="p-layer p-frames";let f;const D=a=>{var p,g;f!==void 0&&((p=Ye.get(f))==null||p.classList.remove("selected")),f=a,a!==void 0&&((g=Ye.get(a))==null||g.classList.add("selected"))},K=document.createElement("div");K.className="p-layer p-nodes",K.setAttribute("role","listbox"),K.setAttribute("aria-multiselectable","true"),K.setAttribute("aria-label","会话节点"),y.append(b,M,K);const X=document.createElement("div");X.className="p-marquee";const ye=document.createElement("div");ye.className="p-toolbar";const Se=document.createElement("span");Se.className="p-zoom mono",Se.textContent="100%";const R=(a,p,g)=>{const O=document.createElement("button");return O.type="button",O.textContent=a,O.title=p,O.addEventListener("click",g,S),O},q=()=>{const a=document.createElement("span");return a.className="p-sep",a.setAttribute("role","separator"),a.setAttribute("aria-orientation","vertical"),a};ye.append(R("适配","适配视图(快捷键 0)",()=>Ce()),R("-","缩小",()=>H(1/1.2)),Se,R("+","放大",()=>H(1.2)),q(),R("建队","组成团队(G):先框选至少 2 个节点",()=>T.startCreateFromSelection()),R("整理成员","每队框内仅属一队的成员自动排齐(多队成员不动)",()=>Nt()),R("一键重排","全板队框+成员重排:框尺寸随内容自适应、框位行流式平铺(远端队框与多归属成员不动)",()=>Ot()),R("撤销","撤销上一次布局改动:拖动、缩放、整理、重排、载入时避让(Ctrl+Z;入队离队不撤销)",()=>re()));const le=R("未入网","未入网会话(点击选择入网)",()=>T.toggleNetMenu());le.className="p-netbtn",le.setAttribute("aria-haspopup","menu"),ye.append(q(),le);const Ke=Ks(ye,t,bt,a=>me(a)),Me=R("节点","节点清单:本机与对等节点(未组队/未入网在此管理)",()=>se.toggle());Me.className="p-nodebtn",Me.setAttribute("aria-haspopup","dialog"),ye.appendChild(Me);const de=document.createElement("span");de.setAttribute("aria-live","polite"),de.className="p-lamp";const Ue=document.createElement("i"),ue=document.createElement("span");ue.textContent="布局",de.append(Ue,ue),de.addEventListener("click",()=>{de.classList.contains("error")&&e.onLampClick()},S),de.addEventListener("keydown",a=>{const p=a;(p.key==="Enter"||p.key===" ")&&de.classList.contains("error")&&(p.preventDefault(),e.onLampClick())},S),ye.appendChild(de);const V=document.createElement("div");V.className="p-status";const ce=document.createElement("span"),ie=document.createElement("span"),ee=document.createElement("span");ee.className="mut";const ke=document.createElement("span");ke.className="mut",V.append(ce,ie,ee,ke),V.tabIndex=0,V.setAttribute("role","button"),V.setAttribute("aria-label","定位在途路由与联邦对端"),V.title="定位在途与联邦(回车)",V.addEventListener("click",()=>we(),S),V.addEventListener("keydown",a=>{const p=a;(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),we())},S);const et=gi(),Oe=document.createElement("div");Oe.className="p-notice-stack",Oe.setAttribute("role","log"),Oe.setAttribute("aria-live","polite"),w.append(oe,y,X,ye,V,et,Oe);let tt=0,st=0;const St=()=>e.viewSize?e.viewSize():{w:w.clientWidth,h:w.clientHeight},Ze=()=>{const a=St();tt=a.w,st=a.h};function Ve(){y.style.transform=Os(s);const a=24*s.scale;oe.style.backgroundSize=`${a}px ${a}px`,oe.style.backgroundPosition=`${-s.x*s.scale}px ${-s.y*s.scale}px`,Se.textContent=`${Math.round(s.scale*100)}%`}function Nt(){const a=u.capture();ai(t),Gt(t,new Set(nt.keys())),u.commit(a),N(),Re()}function Ot(){const a=u.capture();Gs(t,new Set(nt.keys()))&&(Gt(t,new Set(nt.keys())),u.commit(a),N(),Re())}function Re(){Ve(),t.revision!==r&&(r=t.revision,ft(),wt(),hs(b,t)),pe()}const Ye=new Map,Ge=new Map;function ft(){var p;const a=new Set;for(const[g,O]of t.allFrames()){a.add(g);let P=Ye.get(g);if(P===void 0){P=document.createElement("div"),P.className="p-frame",P.dataset.name=g;const te=document.createElement("div");te.className="p-frame-head",te.dataset.frame=g,te.tabIndex=0,te.setAttribute("role","button"),te.setAttribute("aria-haspopup","menu"),te.setAttribute("aria-label",`团队框 ${g}(拖动整组,回车菜单)`);const He=document.createElement("span");He.className="ttl";const Qe=document.createElement("span");Qe.className="cnt mono";const lt=document.createElement("span");lt.className="route mono",te.append(He,Qe,lt),P.appendChild(te);for(const dt of["n","s","e","w","ne","nw","se","sw"]){const At=document.createElement("div");At.className=`p-frame-handle p-h-${dt}`,At.dataset.frame=g,At.dataset.dir=dt,P.appendChild(At)}M.appendChild(P),Ye.set(g,P)}const fe=Rs(g);P.style.setProperty("--frame-line",fe.line),P.style.setProperty("--frame-bg",fe.bg),P.style.left=`${O.x}px`,P.style.top=`${O.y}px`,P.style.width=`${O.w}px`,P.style.height=`${O.h}px`;const W=P.querySelector(".ttl"),B=P.querySelector(".cnt"),z=P.querySelector(".route"),ae=pt.has(g),Te=ae?rn(g):g;W.textContent!==Te&&(W.textContent=Te),P.classList.toggle("is-roster",ae);let _e=0,Ae=0;for(const te of t.allNodes())te.memberships.find(Qe=>Qe.team===g)!==void 0&&(_e+=1,te.memberships.length>1&&(Ae+=1));const qe=`${_e} 成员`+(Ae>0?` +${Ae} 跨队`:"");B.textContent!==qe&&(B.textContent=qe);const v=Qs(nt.get(g),Pe.has(g)),ve=v!==void 0&&k.hosts.has(gt(nt.get(g))),be=(v==null?void 0:v.text)??((p=ct.find(te=>te.name===g))==null?void 0:p.team)??g;z.textContent!==be&&(z.textContent=be),z.title=`${(v==null?void 0:v.title)??""}${ve?` · ${tn}`:""}`,vi(P,ve),P.classList.toggle("is-mirror",v!==void 0),P.classList.toggle("is-syncing",(v==null?void 0:v.syncing)===!0);const We=`团队框 ${g}${v!==void 0?`(${v.text})`:""}${ve?`(${tn})`:""}(拖动整组,回车菜单)`,Le=P.querySelector(".p-frame-head");Le.getAttribute("aria-label")!==We&&Le.setAttribute("aria-label",We)}for(const[g,O]of[...Ye])a.has(g)||(O.remove(),Ye.delete(g))}function wt(){const a=new Set;for(const p of t.allNodes()){a.add(p.id);let g=Ge.get(p.id);g===void 0&&(g=hi(p.id),K.appendChild(g),Ge.set(p.id,g));const O=vt(p);g.style.left=`${O.x}px`,g.style.top=`${O.y}px`,g.classList.toggle("cold",!p.live),g.classList.toggle("remote",p.remote===!0);const P=p.live&&p.remote!==!0?I.get(p.id):void 0;g.classList.toggle("is-working",(P==null?void 0:P.status)==="running");const fe=pi(P),W=g.querySelector(".work");W.textContent!==fe&&(W.textContent=fe),g.classList.toggle("selected",t.isSelected(p.id)),g.setAttribute("aria-selected",String(t.isSelected(p.id)));const B=p.id.slice(5),z=fi(p,p.remote===!0?_.get(B)??[]:[],Z.get(B)),ae=p.remote===!0&&(p.id.startsWith("member-")?k.members.has(p.id):k.hosts.has(B));g.classList.toggle("is-unreachable",ae);const Te=g.querySelector(".nm-text");Te.textContent!==z.title&&(Te.textContent=z.title);const _e=g.querySelector(".sub"),Ae=ae?`未应答 · ${z.sub}`:z.sub;_e.textContent!==Ae&&(_e.textContent=Ae);const qe=ae?`${z.tip} · ${tn}`:z.tip;g.title!==qe&&(g.title=qe);const v=p.id.startsWith("member-")?he.get(p.remoteHandle??""):B;bi(g.querySelector(".prio"),p,p.remote===!0&&v!==void 0?Y.get(v):void 0)}for(const[p,g]of[...Ge])a.has(p)||(g.remove(),Ge.delete(p));Ke.refresh()}const d=Ms(b,t);let _=new Map,I=new Map;const Z=new Map,Y=new Map;let he=new Map;function pe(){d.render(J())}function we(){s=An(Ns(t.contentBounds(),d.placements()),w.clientWidth||tt,w.clientHeight||st),Ve(),N()}const me=Ws({model:t,viewSize:St,setVp:a=>{s=a},applyViewport:Ve,markDirty:N,cardEl:a=>Ge.get(a)});function Fe(a){var O;const p=a.sessions.filter(P=>P.live!==!1).length,g=(P,fe,W)=>{const B=document.createElement("b");B.className=fe,B.textContent=W,P.replaceChildren(B)};g(ce,"ok",`● ${p} live`),g(ie,"warn",`● ${a.sessions.length-p} cold`),ee.textContent=`${((O=a.inFlight)==null?void 0:O.length)??0} inFlight`,ke.className="mut",ke.textContent=`${a.teams.length} 队 · ${a.peerCount} peer`}const Pe=new Map,Je=a=>{Pe.set(a,(Pe.get(a)??0)+1),r=-1},Wt=a=>{r=-1;const p=(Pe.get(a)??0)-1;p<=0?Pe.delete(a):Pe.set(a,p)};let nt=new Map,pt=new Set,k=oo({});const x=ei(e.now);function F(a){const p=a.type==="create-team"&&a.roster===!0||pt.has(Yt(a));if(p&&a.type==="remove-team"&&!window.confirm(`解散网络队「${rn(a.name)}」并撤回成员与白名单?`))return;if(p&&a.type==="reorder"){A("error","roster 队不支持置顶路由");return}if(p&&(a.type==="create-team"||a.type==="add-member")&&a.ids.some(W=>{var B;return((B=t.getNode(W))==null?void 0:B.remote)===!0||!j.some(z=>z.id===W&&z.joined===!0)})){A("error","roster 只接受本机已入网会话,远端或未入网成员未写入");return}function g(W){const B=z=>{var Te;const ae=[];for(const _e of z){if(!_e.startsWith("peer-")&&!_e.startsWith("member-")){ae.push(_e);continue}const Ae=(Te=t.getNode(_e))==null?void 0:Te.remoteHandle;Ae!==void 0&&Ae!==""&&ae.push(Ae)}return ae};if(W.type==="add-member"||W.type==="remove-member"||W.type==="create-team"){const z=B(W.ids),ae=W.type!=="create-team"?nt.get(W.team):void 0;return z.length>0||ae!==void 0?{...W,ids:z,...ae!==void 0?{fleetPeer:ae}:{}}:W}return W.type==="remove-team"&&nt.has(W.name)?x.fork(W,nt.get(W.name)):W}if(a.type==="add-member"||a.type==="remove-member"||a.type==="reorder"||a.type==="create-team"){const W=a.type==="reorder"?a.ops.map(B=>B.id):[...a.ids];for(const B of W){if(B===""||B.startsWith("peer-")||B.startsWith("member-")||t.getNode(B)!==void 0)continue;const z=j.find(_e=>_e.id===B);if(z===void 0)continue;const ae=o==null?void 0:o.nodes[B],Te=ae!==void 0?{x:ae.x,y:ae.y}:Ln(B);t.upsertNode({id:B,x:Te.x,y:Te.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}:{}})}}p&&a.type==="create-team"&&pt.add(a.name);const O=Fs(t,a),P=Yt(a);Je(P),Re();const fe=g(p?{...a,roster:!0}:a);e.onCanvasAction(fe).then(W=>{Wt(P),!W&&a.type==="create-team"&&p&&pt.delete(P),o=Gn(t,a,W,O,o),N(),x.settled(fe,W),Re()},()=>{Wt(P),a.type==="create-team"&&p&&pt.delete(P),o=Gn(t,a,!1,O,o),N(),x.settled(fe,!1),A("error","操作通道异常,已回滚"),Re()})}function A(a,p){var O;for(;Oe.childElementCount>=3;)(O=Oe.firstElementChild)==null||O.remove();const g=document.createElement("div");g.className="p-notice"+(a==="error"?" error":""),g.setAttribute("role",a==="error"?"alert":"status"),g.textContent=p,Oe.appendChild(g),G.push(setTimeout(()=>g.remove(),4e3))}const T=Hs({root:w,netBtn:le,signal:S,model:t,teams:()=>ct,joined:a=>j.some(p=>p.id===a&&p.joined===!0),isRoster:a=>pt.has(a),anchorFor:a=>(a.kind==="node"?Ge.get(a.id):Ye.get(a.name))??null,emit:F,notice:A});let j=[],Q=new Set;const se=di({root:w,trigger:Me,signal:S,model:t,facts:()=>({sessions:j,canvasMemberIds:Q,remoteTeams:_,teams:ct}),menuItem:T.menuItem,emit:F,openNameDialog:T.openNameDialog,locate:U});zo(w,{signal:S,frames:()=>Ye,frameAt:(a,p)=>{const g=ge({clientX:a,clientY:p});return zt(new Map(t.allFrames()),ht(s,g.x,g.y))},drop:(a,p)=>{var g;return((g=t.getNode(p))==null?void 0:g.memberships.some(O=>O.team===a))===!0?A("info","该会话已在这个队里"):F({type:"add-member",team:a,ids:[p]})}});function U(a){var O;const p=t.getNode(a);if(p===void 0)return;se.close();const g=((O=e.viewSize)==null?void 0:O.call(e))??{w:w.clientWidth>0?w.clientWidth:800,h:w.clientHeight>0?w.clientHeight:600};s=It({scale:s.scale,x:p.x-g.w/(2*s.scale),y:p.y-g.h/(2*s.scale)}),Ve(),r=-1,Re()}function ge(a){const p=w.getBoundingClientRect();return{x:a.clientX-p.left,y:a.clientY-p.top}}function De(a,p){var W;const g=ge(p),O=ht(s,g.x,g.y),P=zt(new Map(t.allFrames()),O),fe=a.ids.filter(B=>{const z=t.getNode(B);return z===void 0?!1:z.remote!==!0?!0:z.remoteHandle!==void 0&&z.remoteHandle!==""});if(P!==void 0){const B=fe.filter(z=>{var ae;return!((ae=t.getNode(z))!=null&&ae.memberships.some(Te=>Te.team===P))});if(B.length>0){F({type:"add-member",team:P,ids:B});return}if(a.ids.length===1&&((W=t.getNode(a.clicked))==null?void 0:W.remote)!==!0){const z=t.teamMemberIds(P),ae=Ps(t,P);ae.join("\0")!==z.join("\0")&&F({type:"reorder",team:P,ops:xn(z,ae)})}}if(a.rosterOrigin!==void 0&&P!==a.rosterOrigin){const B=a.ids.filter(z=>t.teamMemberIds(a.rosterOrigin).includes(z));B.length>0&&F({type:"remove-member",team:a.rosterOrigin,ids:B})}}function Ie(a){if(tt===0&&Ze(),T.isDialogOpen()){a.preventDefault();return}const p=a.target;if(T.isMenuOpen()){if(p!==null&&p.closest(".p-menu")!==null)return;T.closeMenu(),a.preventDefault();return}if(p!==null&&p.closest("button, input, textarea, select, .p-toolbar, .p-search, .p-lamp, .p-status, .p-legend")!==null)return;h=u.capture();const g=ge(a);if(a.button===2){$={x:g.x,y:g.y,target:a.target,moved:!1},a.preventDefault();try{w.setPointerCapture(a.pointerId??0)}catch{}return}const O=p!==null?p.closest(".p-node"):null,P=p!==null?p.closest(".p-frame-head"):null;if((O!==null||P!==null)&&a.button===1){n={kind:"pan",last:g,moved:!1},a.preventDefault();try{w.setPointerCapture(a.pointerId??0)}catch{}return}const fe=p!==null?p.closest(".p-frame-handle"):null;if(f!==void 0){const W=Ye.get(f),B=p!==null&&W!==void 0&&(W.contains(p)||W===p),z=P!==null&&P.dataset.frame===f;!B&&!z&&D(void 0)}if(fe!==null&&a.button===0){const W=fe.dataset.frame??"";D(W);const B=t.getFrame(W);if(B===void 0)return;n={kind:"frame-resize",name:W,dir:fe.dataset.dir??"se",startRect:{...B},origin:ht(s,g.x,g.y),moved:!1},a.preventDefault();try{w.setPointerCapture(a.pointerId??0)}catch{}return}if(O!==null){if(a.button!==0)return;const W=O.dataset.id??"",B=t.isSelected(W);!B&&!a.shiftKey&&t.setSelection([W]);const z=a.shiftKey||B?t.isSelected(W)?t.selectedIds():[...t.selectedIds(),W]:[W],ae=zt(new Map(t.allFrames()),ht(s,g.x,g.y));n={kind:"node",clicked:W,ids:z,...ae&&pt.has(ae)&&t.teamMemberIds(ae).includes(W)?{rosterOrigin:ae}:{},last:ht(s,g.x,g.y),moved:!1,el:O},O.classList.add("dragging")}else if(P!==null){if(a.button!==0)return;const W=P.dataset.frame??"";D(W);const B=t.beginFrameDrag(W);if(B===void 0)return;n={kind:"frame",name:W,snap:B,origin:ht(s,g.x,g.y),moved:!1}}else if(a.button===1||a.button===0&&i)n={kind:"pan",last:g,moved:!1};else if(a.button===0)n={kind:"marquee",origin:g,last:g,additive:a.shiftKey},X.style.display="block",X.style.left=`${g.x}px`,X.style.top=`${g.y}px`,X.style.width="0px",X.style.height="0px";else return;a.preventDefault();try{w.setPointerCapture(a.pointerId??0)}catch{}}function ot(a){const p=ge(a);if($!==null){const g=p.x-$.x,O=p.y-$.y;Math.hypot(g,O)>4&&($.moved=!0,s=It(hn(s,-g,-O)),$.x=p.x,$.y=p.y,Ve());return}if(n.kind==="node"){const g=ht(s,p.x,p.y),O=g.x-n.last.x,P=g.y-n.last.y;if(O===0&&P===0)return;n.moved=!0,n.last=g,t.dragNodes(n.ids,O,P);const fe=zt(new Map(t.allFrames()),g),W=n.ids.filter(z=>{const ae=t.getNode(z);return ae===void 0?!1:ae.remote!==!0||ae.remoteHandle!==void 0&&ae.remoteHandle!==""}),B=fe!==void 0&&W.length>0;for(const[z,ae]of Ye)ae.classList.toggle("drop-target",B&&z===fe);Re()}else if(n.kind==="frame"){const g=ht(s,p.x,p.y),O=g.x-n.origin.x,P=g.y-n.origin.y;if(!n.moved&&O===0&&P===0)return;n.moved=!0,t.applyFrameDrag(n.snap,O,P),Re()}else if(n.kind==="frame-resize"){const g=ht(s,p.x,p.y),O=g.x-n.origin.x,P=g.y-n.origin.y;if(!n.moved&&O===0&&P===0)return;n.moved=!0;const fe=220,W=120,B={...n.startRect};let{x:z,y:ae,w:Te,h:_e}=B;n.dir.includes("e")&&(Te=Math.max(fe,B.w+O)),n.dir.includes("s")&&(_e=Math.max(W,B.h+P)),n.dir.includes("w")&&(Te=Math.max(fe,B.w-O),z=B.x+(B.w-Te)),n.dir.includes("n")&&(_e=Math.max(W,B.h-P),ae=B.y+(B.h-_e)),t.setFrame(n.name,{x:Math.round(z),y:Math.round(ae),w:Math.round(Te),h:Math.round(_e)}),Re()}else if(n.kind==="pan")s=hn(s,p.x-n.last.x,p.y-n.last.y),n.last=p,n.moved=!0,Ve();else if(n.kind==="marquee"){const g=Math.min(n.origin.x,p.x),O=Math.min(n.origin.y,p.y);X.style.left=`${g}px`,X.style.top=`${O}px`,X.style.width=`${Math.abs(p.x-n.origin.x)}px`,X.style.height=`${Math.abs(p.y-n.origin.y)}px`,n.last=p}}function xt(a){var p,g;if($!==null){const O=$.moved,P=$.target,fe=$.x,W=$.y;$=null,O?(E=!0,N()):P!==null&&T.contextMenuAt(fe,W,P);return}if(n.kind==="node"){const O=n.clicked;if(n.moved)De(n,a),N();else if(a.shiftKey){const P=t.selectedIds();t.setSelection(P.includes(O)?P.filter(fe=>fe!==O):[...P,O])}else t.setSelection([O]),(p=e.onNodePick)==null||p.call(e,O);for(const P of Ye.values())P.classList.remove("drop-target");(g=n.el)==null||g.classList.remove("dragging"),Re()}else if(n.kind==="frame"||n.kind==="frame-resize")n.moved&&(Gt(t,new Set(nt.keys()),n.name),N(),Re());else if(n.kind==="pan")n.moved&&N();else if(n.kind==="marquee"){const O=n.last,P=ht(s,Math.min(n.origin.x,O.x),Math.min(n.origin.y,O.y)),fe=ht(s,Math.max(n.origin.x,O.x),Math.max(n.origin.y,O.y));t.marqueeSelect({x:P.x,y:P.y,w:fe.x-P.x,h:fe.y-P.y},n.additive),X.style.display="none",Re()}n={kind:"none"},h!==void 0&&(u.commit(h),h=void 0);try{w.releasePointerCapture(a.pointerId??0)}catch{}}function yt(a){var g;for(const O of Ye.values())O.classList.remove("drop-target");n.kind==="node"&&((g=n.el)==null||g.classList.remove("dragging"));const p=n.kind==="pan"&&n.moved||($==null?void 0:$.moved)===!0;X.style.display="none",$=null,n={kind:"none"},h!==void 0&&u.revert(h)&&Re(),p&&N(),h=void 0;try{w.releasePointerCapture(a.pointerId??0)}catch{}}function Ct(a){T.isMenuOpen()&&T.closeMenu();const p=ge(a);if(a.shiftKey&&!a.ctrlKey){const g=a.deltaMode===1?16:1;s=It(hn(s,(a.deltaX??0)*g,(a.deltaY??0)*g))}else{const g=a.deltaMode===1?16:1,O=Math.exp(-((a.deltaY??0)*g)*.0015);s=Un(s,O,p.x,p.y)}a.preventDefault(),Ve(),N()}function H(a){Ze(),s=Un(s,a,tt/2,st/2),Ve(),N()}function re(){u.undo()?(N(),Re()):A("info","没有可撤销的布局改动")}function Ce(){Ze(),s=zn(qn(t.contentBounds(),t.allNodes()),tt,st,Vn(ye,V)),Ve(),N()}function Ne(a){var fe,W,B,z,ae,Te,_e;if(T.isDialogOpen()||((W=(fe=a.target)==null?void 0:fe.closest)==null?void 0:W.call(fe,".p-search"))!==null)return;if((a.ctrlKey||a.metaKey)&&a.key.toLowerCase()==="k"){a.preventDefault(),Ke.focus();return}if((a.ctrlKey||a.metaKey)&&!a.shiftKey&&a.key.toLowerCase()==="z"){a.preventDefault(),re();return}if(a.ctrlKey||a.metaKey)return;if(T.isMenuOpen()){a.key==="Escape"&&(T.closeMenu(),a.preventDefault());return}if(a.key===" "&&a.target===w){i=!0,a.preventDefault();return}const p=((z=(B=a.target)==null?void 0:B.closest)==null?void 0:z.call(B,".p-frame-head"))??null;if((a.key==="Enter"||a.key===" ")&&p!==null){a.preventDefault();const Ae=p.getBoundingClientRect();T.contextMenuAt(Ae.left+Ae.width/2,Ae.top+Ae.height/2,p);return}if(a.key===" "&&((Te=(ae=a.target)==null?void 0:ae.classList)!=null&&Te.contains("p-node"))){const qe=a.target.dataset.id??"",v=t.selectedIds();t.setSelection(v.includes(qe)?v.filter(ve=>ve!==qe):[...v,qe]),Re(),a.preventDefault();return}if(a.key==="F10"&&a.shiftKey||a.key==="ContextMenu"){a.preventDefault();const Ae=document.activeElement??null,qe=((_e=Ae==null?void 0:Ae.getBoundingClientRect)==null?void 0:_e.call(Ae))??{left:0,top:0,width:0,height:0};T.contextMenuAt(qe.left+qe.width/2,qe.top+qe.height/2,Ae);return}if(a.key==="Delete"||a.key==="Backspace"){a.preventDefault(),it();return}if(a.altKey&&(a.key==="ArrowUp"||a.key==="ArrowDown")){a.preventDefault(),je(a.key==="ArrowUp");return}if(a.key==="g"||a.key==="G"){T.startCreateFromSelection();return}if(a.key==="0"){Ce();return}if(a.key==="="||a.key==="+"){H(1.2);return}if(a.key==="-"){H(1/1.2);return}if(a.key==="Escape"){if(T.isMenuOpen()){T.closeMenu(),Re();return}t.setSelection([]),Re();return}const g=a.shiftKey?40:8,P={ArrowLeft:[-g,0],ArrowRight:[g,0],ArrowUp:[0,-g],ArrowDown:[0,g]}[a.key];if(P!==void 0){a.preventDefault();const Ae=t.selectedIds();if(Ae.length>0){const qe=u.capture();t.nudge(Ae,P[0],P[1]),u.commit(qe),Re(),N()}}}function it(){var O,P,fe;const a=document.activeElement,p=((O=a==null?void 0:a.closest)==null?void 0:O.call(a,".p-frame-head"))??null,g=((P=a==null?void 0:a.closest)==null?void 0:P.call(a,".p-node"))??null;if(p!==null){F({type:"remove-team",name:p.dataset.frame??""});return}if(g!==null){const W=g.dataset.id??"",B=((fe=t.getNode(W))==null?void 0:fe.memberships.map(z=>z.team))??[];if(B.length===0){A("info","该节点未加入任何团队");return}if(B.length===1){F({type:"remove-member",team:B[0],ids:[W]});return}T.openMenu(window.innerWidth/2,window.innerHeight/2,{kind:"node",id:W},"leave",g);return}A("info","焦点不在节点或团队框上")}function je(a){var Te,_e;const p=document.activeElement,g=((Te=p==null?void 0:p.closest)==null?void 0:Te.call(p,".p-node"))??null;if(g===null)return;const O=g.dataset.id??"",P=((_e=t.getNode(O))==null?void 0:_e.memberships)??[];if(P.length!==1)return;const fe=P[0].team,W=t.teamMemberIds(fe),B=W.indexOf(O),z=a?Math.max(0,B-1):Math.min(W.length-1,B+1);if(z===B)return;const ae=[...W];ae.splice(B,1),ae.splice(z,0,O),F({type:"reorder",team:fe,ops:xn(W,ae)})}function Ee(a){a.key===" "&&(i=!1)}w.addEventListener("pointerdown",a=>Ie(a),S),w.addEventListener("pointermove",a=>ot(a),S),w.addEventListener("pointerup",a=>xt(a),S),w.addEventListener("pointercancel",a=>yt(a),S),w.addEventListener("wheel",a=>Ct(a),{...S,passive:!1}),w.addEventListener("keydown",a=>Ne(a),S),w.addEventListener("keyup",a=>Ee(a),S),w.addEventListener("contextmenu",a=>{var g,O;const p=a;if(((O=(g=p.target)==null?void 0:g.closest)==null?void 0:O.call(g,"input, textarea, .p-search"))==null){if(p.preventDefault(),E){E=!1;return}T.contextMenuAt(p.clientX,p.clientY,p.target)}},S);let ct=[];function Ft(a){var qe;ct=a.teams,pt=new Set([...a.teams.filter(v=>v.kind==="roster").map(v=>v.name),...[...pt].filter(v=>Pe.has(v))]),a.peers,d.setInFlight(a.inFlight??[]),j=a.sessions,T.setUnjoined(a.sessions.filter(v=>v.joined!==!0&&!v.id.startsWith("peer-")));const p=As(a.teams);Q=new Set(p.keys());const g=new Set,O=v=>{const ve=p.get(v)??[];if(Pe.size===0)return ve;const be=ve.filter(Le=>!Pe.has(Le.team)),We=[];for(const[Le]of Pe){const He=t.teamMemberIds(Le).indexOf(v);He>=0&&We.push({team:Le,index:He})}return[...be,...We]};let P;if(o===void 0&&!t.allNodes().some(v=>v.remote!==!0)){const v=a.sessions.filter(ve=>{var be;return ve.joined===!0&&(O(ve.id).length>0||(((be=ve.teams)==null?void 0:be.length)??0)>0)}).map(ve=>ve.id);Ze(),P=ms(a.teams,v,tt>0?tt:void 0)}const fe=a.peerOrg!==void 0?Ls(a.peerOrg,a.sessions,a.remoteTeams):new Set;a.peerOrg!==void 0&&(Q=new Set([...Q,...fe]));for(const v of a.sessions){if(v.joined!==!0)continue;const ve=O(v.id);if(!(ve.length===0&&(v.teams===void 0||v.teams.length===0)&&!fe.has(v.id)))if(g.add(v.id),t.getNode(v.id)!==void 0)t.upsertNode({id:v.id,x:t.getNode(v.id).x,y:t.getNode(v.id).y,label:v.label,team:v.team,name:v.name,description:v.description,live:v.live!==!1,memberships:ve,...v.teams!==void 0&&v.teams.length>0?{teams:v.teams}:{}});else{const be=o==null?void 0:o.nodes[v.id];let We,Le;if(be!==void 0)We=be.x,Le=be.y;else{const te=(P==null?void 0:P.get(v.id))??Ln(v.id);We=te.x,Le=te.y}t.upsertNode({id:v.id,x:We,y:Le,label:v.label,team:v.team,name:v.name,description:v.description,live:v.live!==!1,memberships:ve,...v.teams!==void 0&&v.teams.length>0?{teams:v.teams}:{}})}}for(const v of t.nodeIds())!g.has(v)&&!v.startsWith("peer-")&&!v.startsWith("member-")&&t.removeNode(v);const W=new Set;_=gs(a.remoteTeams??[]),he=new Map([..._].flatMap(([v,ve])=>ve.map(be=>[be.team,v]))),I=new Map(a.sessions.map(v=>[v.id,{status:v.status,queued:v.queued}])),k=oo(a),Z.clear();for(const v of a.peers??[])Z.set(gt(v.url),v.score),Y.set(gt(v.url),{link:v.link,verified:v.verified});if(a.peers!==void 0){const v=t.contentBounds(),ve=Cs(a.peers,v);a.peers.forEach((be,We)=>{var In,Tn,_n;const Le=bs(be.url);W.add(Le);const te=o==null?void 0:o.nodes[Le],He=t.getNode(Le),Qe=He!==void 0?{x:He.x,y:He.y}:te!==void 0?{x:te.x,y:te.y}:{x:((In=ve[We])==null?void 0:In.x)??200,y:((Tn=ve[We])==null?void 0:Tn.y)??0},lt=gt(be.url),dt=(_n=_.get(lt))==null?void 0:_n.find(Io=>Io.team.includes("/")),At=(dt==null?void 0:dt.memberRef)??(dt==null?void 0:dt.team);t.upsertNode({id:Le,x:Qe.x,y:Qe.y,label:be.url,team:"",name:be.url,peerUrl:be.url,remoteHandle:At,live:!0,remote:!0,memberships:[]}),t.upsertScore(Le,be.score)})}for(const v of t.nodeIds())v.startsWith("peer-")&&!W.has(v)&&t.removeNode(v);const B=new Set;for(const[v,ve]of _){const be=t.allNodes().find(te=>te.remote===!0&>(te.peerUrl??"")===v&&!te.id.startsWith("member-")),We=((be==null?void 0:be.x)??400)+190;let Le=(be==null?void 0:be.y)??0;for(const te of ve){if(!te.team.includes("/"))continue;const He=`member-${(te.memberRef??te.team).replace(/[^a-z0-9-]/gi,"-")}`;if(B.has(He))continue;B.add(He);const Qe=o==null?void 0:o.nodes[He],lt=t.getNode(He),dt=lt!==void 0?{x:lt.x,y:lt.y}:Qe!==void 0?{x:Qe.x,y:Qe.y}:{x:We,y:Le};Le+=92,t.upsertNode({id:He,x:dt.x,y:dt.y,label:te.team,team:te.team,name:te.name!==void 0&&te.name!==""?te.name:te.team,remoteHandle:te.memberRef??te.team,live:!0,remote:!0,memberships:[]})}}for(const v of t.nodeIds())v.startsWith("member-")&&!B.has(v)&&t.removeNode(v);const z=new Map;for(const v of t.allNodes())v.remote!==!0||v.remoteHandle===void 0||v.remoteHandle===""||(!z.has(v.remoteHandle)||v.id.startsWith("member-"))&&z.set(v.remoteHandle,v.id);for(const v of a.teams){if(Pe.has(v.name))continue;const ve=[];for(const Le of v.members){const te=z.get(Le.id)??Le.id;t.getNode(te)!==void 0&&ve.push(te)}const be=[...t.teamMemberIds(v.name)].sort().join("|"),We=[...ve].sort().join("|");be!==We&&t.setTeamMembers(v.name,ve)}const ae=new Map,Te=nt;if(nt=ae,a.peerOrg!==void 0){let v=0;const ve=new Set(a.teams.map(be=>be.name));x.sweep(a.peerOrg);for(const be of a.peerOrg){const We=new Set((a.remoteTeams??[]).map(te=>te.team)),Le=Co(a.sessions,We);for(const te of be.org){if(te.name===""||ve.has(te.name)||x.hides(te.name,be.url))continue;const He=te.members.map(lt=>Ss(lt,Le,a.remoteTeams,be.url)).filter(lt=>t.getNode(lt)!==void 0),Qe=(qe=o==null?void 0:o.frames)==null?void 0:qe[te.name];t.getFrame(te.name)===void 0&&t.setFrame(te.name,Qe!==void 0?{x:Qe.x,y:Qe.y,w:Qe.w,h:Qe.h}:{x:200+v%4*280,y:940+Math.floor(v/4)*160,w:250,h:130}),t.setTeamMembers(te.name,He),ae.set(te.name,be.url),v+=1}}}for(const[v,ve]of Te)ae.has(v)||t.removeFrame(v);const _e=new Map;for(const v of t.allNodes())v.remote===!0&&v.remoteHandle!==void 0&&v.remoteHandle!==""&&_e.set(v.remoteHandle,v.id);const Ae=yo(a.teams);for(const v of a.teams){if(t.getFrame(v.name)!==void 0||Pe.has(v.name))continue;const ve=o==null?void 0:o.frames[v.name];if(ve!==void 0){t.setFrame(v.name,{...ve});continue}const be=[],We=[];for(const te of v.members){const He=t.getNode(_e.get(te.id)??te.id);He!==void 0&&(We.push(vt(He)),Ae.get(He.id)===v.name&&be.push(vt(He)))}const Le=be.length>0?be:We;Le.length>0&&t.setFrame(v.name,Mn(Le))}for(const[v]of t.allFrames())Pe.has(v)||!a.teams.some(ve=>ve.name===v)&&!ae.has(v)&&t.removeFrame(v);if(se.refresh(),Fe(a),!l&&n.kind==="none"&&t.nodeIds().length>0){l=!0;const v=u.capture();Gt(t,new Set(nt.keys())),u.commit(v)}c&&n.kind==="none"&&t.nodeIds().length>0&&(Ze(),tt>0&&st>0&&(c=!1,s=zn(qn(t.contentBounds(),t.allNodes()),tt,st,Vn(ye,V)))),Re()}function Ln(a){return wo(a,t.allNodes().filter(p=>p.remote!==!0))}function No(a){const p=go(a);if(p===void 0)return;o=p;const g=e.viewportStore;if(g===void 0)c=!1,n.kind==="none"&&(s=It({...p.viewport}));else if(!m&&n.kind==="none"){m=!0;const O=g.get(C());O!=null&&Number.isFinite(O.x)&&Number.isFinite(O.y)&&Number.isFinite(O.scale)?(s=It({x:O.x,y:O.y,scale:O.scale}),c=!1):C()==="wide"&&(s=It({...p.viewport}),c=!1)}r=-1,Re()}function $o(){return as(s,t.positions(),t.frameRects())}function Mo(a,p){de.classList.remove("pending","saved","error"),a==="pending"&&de.classList.add("pending"),a==="saved"&&de.classList.add("saved"),a==="error"&&de.classList.add("error"),a==="error"?(de.tabIndex=0,de.setAttribute("role","button"),de.setAttribute("aria-label","布局保存失败,按回车重试")):(de.removeAttribute("tabindex"),de.removeAttribute("role"),de.removeAttribute("aria-label"));const g=a==="pending"?"布局待保存…":a==="saved"?`布局已保存 ${p??""}`.trimEnd():a==="error"?"保存失败 · 回车重试":"布局";ue.textContent=g}function So(){w.style.display="block",Ze(),Ve(),w.focus()}function Ao(){w.style.display="none"}function Lo(){ne.abort();for(const a of G)clearTimeout(a)}return{root:w,reconcile:Ft,activate:So,deactivate:Ao,adoptExternalLayout:No,snapshotDoc:$o,setLamp:Mo,notice:A,destroy:Lo,seam:{pointerDown:Ie,pointerMove:ot,pointerUp:xt,pointerCancel:yt,wheel:Ct,key:Ne,keyUp:Ee,contextMenu(a){a.preventDefault(),T.contextMenuAt(a.clientX,a.clientY,a.target)}}}}const xi=800;function Ci(e){let t,o=!1,s=!1,n=!1,i="idle";function r(h){h!==i&&(i=h,e.onLamp(h))}function c(){t!==void 0&&(t(),t=void 0)}function l(){c(),t=e.schedule(()=>{t=void 0,u(!1)},xi)}async function u(h){if(o){s=!0;return}o=!0,c();const m=e.snapshot();let C;try{C=JSON.stringify(m)}catch{o=!1,r("error"),e.onHttpError("layout document is not JSON-serializable");return}try{const N=await e.send(m,{keepalive:h});if(N===null||typeof N!="object"||N.ok!==!0){r("error");const J=N!==null&&typeof N=="object"&&typeof N.error=="string"?N.error:"layout save refused";e.onHttpError(`layout save: ${J}`)}else JSON.stringify(e.snapshot())===C&&(e.onAdopt(N.layout),r("saved"))}catch(N){r("error"),e.onHttpError(`layout save unreachable: ${String((N==null?void 0:N.message)??N).slice(0,80)}`)}if(o=!1,i==="error"){n=!1;return}if(n){n=!1,u(!0);return}s&&(s=!1,r("pending"),l())}return{markDirty(){if(s=!1,r("pending"),o){s=!0;return}l()},retry(){i!=="error"||o||(r("pending"),u(!1))},flush(){if(o){n=!0,s=!0;return}c(),u(!0)},isBusy(){return o},dispose(){c()}}}function so(e){return e!=null&&typeof e.error=="string"?e.error:void 0}function ki(e){const t=new Map;let o=0;function s(r,c){o+=1;const l=t.get(r)??Promise.resolve(),u=async()=>{try{return await c()}finally{o-=1}},h=l.then(u,u);return t.set(r,h.then(()=>{},()=>{})),h}async function n(r){var ne,G,$,E;let c;try{c=await e.send(r)}catch(S){const w=String((S==null?void 0:S.message)??S).slice(0,80);return e.onNotice("error",`画布不可达:${w}`),!1}if(c.status>=400)return e.onNotice("error",so(c.body)??`HTTP ${c.status}`),!1;if(((ne=c.body)==null?void 0:ne.ok)===!0)return!0;const l=c.body!=null&&typeof c.body=="object"?so(c.body):void 0;if(l!==void 0)return e.onNotice("error",l),!1;const u=String(r.name??""),h=String(r.id??""),m=(G=c.body)==null?void 0:G.members,C=($=c.body)==null?void 0:$.teams,N=Array.isArray(m)&&m.every(S=>typeof S=="string"),J=Array.isArray(C)&&C.every(S=>typeof S=="string");return((E=c.body)==null?void 0:E.ok)===!1&&(r.action==="add-member"&&N&&m.includes(h)||r.action==="remove-member"&&N&&!m.includes(h)||r.action==="remove"&&J&&!C.includes(u))?!0:(e.onNotice("error","画布操作未确认:"+String(r.action)+"(可能队伍/成员已达上限,请重试)"),!1)}async function i(r,c){for(const l of c)if(!await n({...r,id:l}))return!1;return!0}return{createTeam(r,c){return s(r,async()=>{if(!await n({action:"create",name:r}))return!1;for(let l=0;l<c.length;l++)if(!await n({action:"add-member",name:r,id:c[l]}))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 l of c){const u=l.op==="remove"?{action:"remove-member",name:r,id:l.id}:{action:"add-member",name:r,id:l.id};if(!await n(u))return!1}return!0})},removeTeam(r){return s(r,()=>n({action:"remove",name:r}))},hasPending(){return o>0}}}const Eo=document.getElementById("app"),Ei=(()=>{try{return window.sessionStorage.getItem("dsh_a2a_key")??""}catch{return""}})(),Cn=window.__DSH_A2A_KEY__??Ei,Xe=(e,t={})=>fetch(e,{...t,headers:{...t.headers??{},...Cn!==""?{"X-API-Key":Cn}:{}}});Cn===""&&Xe("/__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 o=document.createElement("div");o.textContent="此面板经远程面打开(密钥不随页面下发)——输入控制密钥";const s=document.createElement("input");s.type="password",s.placeholder="X-API-Key",s.setAttribute("aria-label","控制密钥");const n=document.createElement("button");n.type="button",n.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(l=>{if(l.status===401){i.textContent="密钥不对,再试一次";return}try{window.sessionStorage.setItem("dsh_a2a_key",c)}catch{}window.location.reload()})};n.addEventListener("click",r),s.addEventListener("keydown",c=>{c.key==="Enter"&&r()}),t.append(o,s,n,i),document.body.appendChild(t)});const jt=document.createElement("div");jt.className="nexus-fault";Eo.appendChild(jt);const{fault:kn,clear:Ni}=Oo({show(e){jt.textContent=e,jt.style.display="block"},hide(){jt.style.display="none"}},e=>console.error(e));let nn;const io="dsh-a2a.viewport.",$i={get(e){try{const t=window.localStorage.getItem(io+e);return t===null?void 0:JSON.parse(t)}catch{return}},set(e,t){try{window.localStorage.setItem(io+e,JSON.stringify({x:t.x,y:t.y,scale:t.scale}))}catch{}}},at=wi({viewportStore:$i,onDirty:()=>cn.markDirty(),onLampClick:()=>cn.retry(),onNodePick:e=>{nn==null||nn(e)},onCanvasAction:async e=>{const t=await Si(e);return mn(),t}}),cn=Ci({snapshot:()=>at.snapshotDoc(),send:(e,t)=>Xe("/__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 o=>await o.json()),schedule:(e,t)=>{const o=setTimeout(e,t);return()=>{clearTimeout(o)}},now:Date.now,onLamp:e=>{En=e;const t=new Date,o=String(t.getHours()).padStart(2,"0")+":"+String(t.getMinutes()).padStart(2,"0");at.setLamp(e,o)},onAdopt:e=>{at.adoptExternalLayout(e)},onHttpError:e=>kn(e)});window.addEventListener("pagehide",()=>cn.flush());const Dt=ki({send:async e=>{const t=await Xe("/__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)=>at.notice(e,t)});async function ro(e,t,o,s){const n=await Xe("/__dsh_a2a/fleet-canvas-op",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({peer:e,op:t,name:o,...s!==void 0?{id:s}:{}}),cache:"no-store"}).catch(()=>{}),i=n!==void 0?await n.json().catch(()=>{}):void 0;return(i==null?void 0:i.ok)===!0&&t==="remove"&&i.removed===!1&&at.notice("info",`属主那边已没有「${o}」,无需解散`),(i==null?void 0:i.ok)===!0?!0:(at.notice("error",`跨主机团队操作失败:${String((i==null?void 0:i.error)??"对端无响应")}`),!1)}async function Mi(e,t){const o=async s=>{const n=await Xe("/__dsh_a2a/roster-op",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,...s})}).catch(()=>{}),i=n!==void 0?await n.json().catch(()=>{}):void 0;return n!=null&&n.ok&&(i==null?void 0:i.ok)===!0?!0:(at.notice("error",`roster 操作失败:${String((i==null?void 0:i.error)??(n?`HTTP ${n.status}`:"无响应"))}`),!1)};if(e.type==="create-team")return o({op:"create",ids:e.ids});if(e.type==="remove-team")return o({op:"dissolve"});if(e.type==="add-member"||e.type==="remove-member"){for(const s of e.ids)if(!await o({op:e.type,id:s}))return!1;return!0}return at.notice("error","roster 不支持该操作"),!1}function Si(e){if((e.type==="add-member"||e.type==="remove-member")&&e.fleetPeer!==void 0){const t=e.fleetPeer;return(async()=>{for(const o of e.ids)if(!await ro(t,e.type,e.team,o))return!1;return!0})()}if(e.type==="remove-team"&&e.fleetPeer!==void 0)return ro(e.fleetPeer,"remove",e.name);if(e.roster===!0){const t=ko(Yt(e));return t!==void 0?Mi(e,t):(at.notice("error","无效的 roster 框名"),Promise.resolve(!1))}switch(e.type){case"create-team":return Dt.createTeam(e.name,e.ids,e.created);case"add-member":return Dt.addMembers(e.team,e.ids);case"remove-member":return Dt.removeMembers(e.team,e.ids);case"remove-team":return Dt.removeTeam(e.name);case"reorder":return Dt.runRosterOps(e.team,e.ops);case"join-network":case"leave-network":return Ai(e.type==="join-network"?"join":"leave",e.id)}}async function Ai(e,t){const o=await Xe(`/__dsh_a2a/${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:t}),cache:"no-store"}).catch(()=>{});return o!==void 0&&o.ok}let En="idle",bn=new Set,ao=0,ln=!1,co,gn=!1;async function Li(){const e=await Xe("/__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 dn=is({now:()=>Date.now(),async ownerSettingsRead(){try{return await(await Xe("/__dsh_a2a/owner-settings")).json()}catch{return{ok:!1,error:"unreachable"}}},async ownerSettingsWrite(e){try{return await(await Xe("/__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 Xe("/__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 o=await Xe("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:"cli-home-options",name:e,id:t})}),s=await o.json();return{...s,ok:o.ok&&s.ok===!0}}catch(o){return{ok:!1,error:String(o)}}},async confirmMember(e,t,o,s,n,i){try{const r=await Xe("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:"confirm-member",name:e,id:t,replacement:o,revision:s,expectedOwner:n,...i?{peer:i}:{}})}),c=await r.json();return{ok:r.ok&&c.ok===!0,error:c.error}}catch(r){return{ok:!1,error:String(r)}}},async regulateSuspended(e,t,o,s,n){try{const i=await Xe("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:e,name:t,id:o,revision:s,expected:n})}),r=await i.json();return{ok:i.ok&&r.ok===!0,error:r.error}}catch(i){return{ok:!1,error:String(i)}}},async canvasOp(e,t,o,s,n){try{const i=await Xe("/__dsh_a2a/canvas",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:e,name:t,id:o,...s===void 0?{}:{home:s},...n===void 0?{}:{peer:n}})}),r=await i.json().catch(()=>({}));return i.ok&&r.ok!==!1?!0:(console.warn(`canvas ${e} ${t}: ${r.error??`HTTP ${i.status}`}`),!1)}catch{return!1}},async dispatchQuick(e,t){try{const o=await Xe("/__dsh_a2a/dispatch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({team:e,message:t})}),s=await o.json().catch(()=>({}));return{ok:o.ok&&s.ok===!0,task_id:s.task_id,reply:s.reply,error:s.error??`HTTP ${o.status}`}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}},async sessionLog(e){try{const t=await Xe(`/__dsh_a2a/session-log?id=${encodeURIComponent(e)}`),o=await t.json().catch(()=>({}));return{ok:t.ok&&o.ok===!0,messages:o.messages,error:o.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 Xe("/__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 o=await Xe(`/__dsh_a2a/remote-session-log?${t.toString()}`),s=await o.json().catch(()=>({}));return{ok:o.ok&&s.ok===!0,messages:s.messages,sessions:s.sessions,error:s.error??(o.ok?void 0:`HTTP ${o.status}`),via:s.via}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}},board:{root:at.root,activate:()=>at.activate(),deactivate:()=>at.deactivate(),available:()=>ln}});Eo.appendChild(dn.root);nn=e=>{dn.openNodeChat(e)};if(new URLSearchParams(window.location.search).get("mode")==="plan"){const e=window.setInterval(()=>{ln&&(dn.selectTab("board"),window.clearInterval(e))},300);window.setTimeout(()=>window.clearInterval(e),3e4)}async function mn(){var e,t;try{const o=await Xe("/__dsh_a2a/state",{cache:"no-store"});if(!o.ok)throw new Error(`/__dsh_a2a/state ${o.status}`);const s=await o.json();if(Date.now()-ao>=15e3){ao=Date.now();try{const m=await Xe("/__dsh_a2a/nodes",{cache:"no-store"});if(!m.ok)throw new Error(`nodes ${m.status}`);const C=await m.json();bn=new Set((C.bindings??[]).filter(N=>N.mounted!==null&&N.effective!==null&&(N.effective.core===!1||N.effective.mutable===!1)).map(N=>N.session))}catch{bn.clear()}}const n={...s,sessions:(s.sessions??[]).map(m=>({...m,...bn.has(m.id)?{nodePromptMissing:!0}:{}}))};if(Ni(),ln=s.canvas!==void 0,dn.update(n),ln)try{const m=await Li(),C=JSON.stringify(m??null);C!==co&&(co=C,(En==="idle"||En==="saved")&&!cn.isBusy()&&at.adoptExternalLayout(m)),gn=!0}catch(m){if(!gn){kn(`canvas layout 获取失败:${String(m)}`);return}}else gn=!0;const i=(s.sessions??[]).map(m=>({...m,joined:m.joined===!0})),r=[...((e=s.canvas)==null?void 0:e.teams)??[],...Ds(((t=s.registry)==null?void 0:t.teams)??[],s.zone??"")],c=(s.peers??[]).filter(m=>m!==null&&typeof m=="object"&&typeof m.url=="string"),l=(s.inFlight??[]).filter(m=>m!==null&&typeof m=="object"&&typeof m.team=="string"&&typeof m.peer=="string"&&Number.isFinite(m.startedAt)),u=(s.remote??[]).filter(m=>m!==null&&typeof m=="object"&&typeof m.team=="string"&&typeof m.via=="string").map(m=>({team:m.team,memberRef:m.memberRef,name:m.name,via:m.via,workspace:m.workspace,...m.unreachable===!0?{unreachable:!0}:{},...typeof m.origin=="string"&&m.origin!==""?{origin:m.origin}:{},...Array.isArray(m.teams)&&m.teams.length>0?{teams:m.teams.filter(C=>typeof C=="string"&&C!=="")}:{}})),h=(s.peerOrg??[]).filter(m=>m!==null&&typeof m=="object").filter(m=>typeof m.url=="string"&&Array.isArray(m.org)).map(m=>({url:m.url,...m.unreachable===!0?{unreachable:!0}:{},org:m.org.filter(C=>C!==null&&typeof C=="object").filter(C=>typeof C.team=="string"&&C.team!=="").map(C=>({name:typeof C.name=="string"?C.name:"",team:C.team,members:Array.isArray(C.members)?C.members.filter(N=>typeof N=="string"):[]}))}));at.reconcile({sessions:i,teams:r,peerCount:c.length,peers:c,inFlight:l,remoteTeams:u,peerOrg:h})}catch(o){kn(`state 轮询失败:${String(o)}`)}}mn();window.setInterval(()=>{document.hidden||mn()},3e3);document.addEventListener("visibilitychange",()=>{document.hidden||mn()});
|
|
@@ -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-
|
|
21
|
+
<script type="module" crossorigin src="./assets/index-CA_YZTWG.js"></script>
|
|
22
22
|
<link rel="stylesheet" crossorigin href="./assets/index-C7HagaGy.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
package/cli/a2a.mjs
CHANGED
|
@@ -801,11 +801,31 @@ async function cmdFileFetch() {
|
|
|
801
801
|
const text = positionals().slice(1).join(' ')
|
|
802
802
|
const ref = parseRef(text)
|
|
803
803
|
if (ref === undefined) usage('usage: a2a file fetch "<[A2A file] v1 … reference line>" [--out DIR]')
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
804
|
+
const homeBase = home()
|
|
805
|
+
const homeOrigin = new URL(homeBase).origin
|
|
806
|
+
let localSource = ref.from === homeOrigin
|
|
807
|
+
if (!localSource) {
|
|
808
|
+
// Ask only the configured home: neither a reference nor a remote card can
|
|
809
|
+
// authorize sending CLI credentials to another origin (#332). Old/denied
|
|
810
|
+
// metadata fails closed; same-origin fetches retain their no-lookup path.
|
|
811
|
+
const metadata = await homeFetch(`${homeBase}/__dsh_a2a/state`, { headers: keyHeaders(), signal: AbortSignal.timeout(5_000) })
|
|
812
|
+
if (metadata.status === 401 || metadata.status === 403) die(keyHint(metadata.status))
|
|
813
|
+
if (metadata.status === 200) {
|
|
814
|
+
const info = await replyOf(metadata)
|
|
815
|
+
const aliases = [info.ownerHost, ...(Array.isArray(info.host?.originClaimAudiences) ? info.host.originClaimAudiences : [])]
|
|
816
|
+
localSource = info.error === undefined && aliases.some(value => {
|
|
817
|
+
if (typeof value !== 'string') return false
|
|
818
|
+
try {
|
|
819
|
+
const url = new URL(value)
|
|
820
|
+
return ['http:', 'https:'].includes(url.protocol) && url.username === '' && url.password === '' && url.search === '' && url.hash === '' && url.origin === ref.from
|
|
821
|
+
} catch { return false }
|
|
822
|
+
})
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (!localSource) die(`[file fetch] the file is on ${ref.from}, not your home host ${homeOrigin}: a CLI identity proves itself only on its home host — ask the sender to send it through your home host, or fetch it from a session there`)
|
|
826
|
+
const target = `${homeBase}/a2a/blob`
|
|
807
827
|
const res = await homeFetch(`${target}?sha256=${ref.sha256}&caller_session=${encodeURIComponent(identity().handle)}`, {
|
|
808
|
-
headers: { ...originClaimFor(
|
|
828
|
+
headers: { ...originClaimFor(homeBase)(target, 'GET'), ...keyHeaders() },
|
|
809
829
|
})
|
|
810
830
|
if (res.status !== 200) {
|
|
811
831
|
const out = await replyOf(res)
|
|
@@ -870,6 +890,7 @@ async function printDelivery(verb, res) {
|
|
|
870
890
|
return
|
|
871
891
|
}
|
|
872
892
|
if (res.status === 401 || res.status === 403) die(`[${verb}] ${keyHint(res.status)}${out.error !== undefined ? ` (${String(out.error)})` : ''}`)
|
|
893
|
+
if(out.delivered===true&&out.receiverBookkeeping==='failed'){console.log(`[${verb}] 已送达、接收方记账失败,请对账,不要重派 · task ${String(out.task_id)} — delivered; receiver bookkeeping failed; reconcile, do not redispatch`);return}
|
|
873
894
|
// A sync round answers with the target's reply instead of a delivery marker.
|
|
874
895
|
if (out.routed === true && typeof out.result?.text === 'string') {
|
|
875
896
|
console.log(`[${verb}] reply from ${String(out.team)} · task ${String(out.task_id)}${typeof out.context_id === 'string' && out.context_id !== '' ? ` · context ${out.context_id}` : ''}`)
|
|
@@ -1000,11 +1021,11 @@ async function main() {
|
|
|
1000
1021
|
const archived = s.archived ?? []
|
|
1001
1022
|
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 }
|
|
1002
1023
|
console.log(`pending (${String(pending.length)}):`)
|
|
1003
|
-
listCapped(pending, 20, t => ` ${String(t.taskId)} → ${String(t.team)} ${String(t.direction ?? '')}${senderTail(t)}`)
|
|
1024
|
+
listCapped(pending, 20, t => ` ${String(t.taskId)} → ${String(t.team)} ${String(t.direction ?? '')}${t.consumed===undefined?'':t.consumed?' consumed':' delivered-unconsumed'}${t.receiptDelivery?' '+String(t.receiptDelivery):''}${senderTail(t)}${t.reminderAttempts!==undefined?` reminders=${t.reminderDelivered??0}/${t.reminderAttempts} last=${t.reminderLastAt??'?'}${t.repeatedlyUnreconciled?' 多次提醒未对账':''}`:''}`)
|
|
1004
1025
|
console.log(`dead (${String(dead.length)}):`)
|
|
1005
|
-
listCapped(dead, 20, t => ` ${String(t.taskId)} → ${String(t.team)} (${String(t.reason ?? '?')})${senderTail(t)}`)
|
|
1026
|
+
listCapped(dead, 20, t => ` ${String(t.taskId)} → ${String(t.team)} (${String(t.reason ?? '?')})${senderTail(t)}${t.reminderAttempts!==undefined?` reminders=${t.reminderDelivered??0}/${t.reminderAttempts} last=${t.reminderLastAt??'?'}${t.repeatedlyUnreconciled?' 多次提醒未对账':''}`:''}`)
|
|
1006
1027
|
console.log(`archived (${String(archived.length)}):`)
|
|
1007
|
-
listCapped(archived, 20, t => ` ${String(t.taskId)} ${String(t.outcome ?? '?')}${t.endedBy !== undefined && t.endedBy !== 'receipt' ? ` (${String(t.endedBy)})` : ''}${senderTail(t)}`)
|
|
1028
|
+
listCapped(archived, 20, t => ` ${String(t.taskId)} ${String(t.outcome ?? '?')}${t.endedBy !== undefined && t.endedBy !== 'receipt' ? ` (${String(t.endedBy)})` : ''}${t.withdrawalResult?` withdrawal=${String(t.withdrawalResult)}`:''}${senderTail(t)}${t.reminderAttempts!==undefined?` reminders=${t.reminderDelivered??0}/${t.reminderAttempts} last=${t.reminderLastAt??'?'}${t.repeatedlyUnreconciled?' 多次提醒未对账':''}`:''}`)
|
|
1008
1029
|
if (s.conservation != null) console.log(`conservation: conserved=${String(s.conservation.conserved)} delta=${String(s.conservation.delta)}`)
|
|
1009
1030
|
if (s.settlement != null) console.log(`settlement: mode=${String(s.settlement.mode)} refusalsTotal=${String(s.settlement.refusalsTotal)} offChain=${String(s.settlement.offChain)}`)
|
|
1010
1031
|
if (s.settlement?.enforceRefused) console.log(`enforce refused: ${String(s.settlement.enforceRefused.reason)}; blockers=${JSON.stringify(s.settlement.enforceRefused.blockers)}`)
|
|
@@ -1013,6 +1034,17 @@ async function main() {
|
|
|
1013
1034
|
if (cmd === 'status') {
|
|
1014
1035
|
const s = await state()
|
|
1015
1036
|
const list = await control({ action: 'list' })
|
|
1037
|
+
const safeCode=value=>typeof value==='string'&&['EACCES','EPERM','EBUSY','ENOSPC','ENOENT','EIO','EROFS','EMFILE','ENFILE','ETIMEDOUT','ECONNRESET','ECONNREFUSED','ABORT_ERR','LEDGER_WRITE_FAILED','NUDGE_QUEUE_FAILED','PROMPT_UNAVAILABLE'].includes(value)?value:'UNKNOWN_ERROR'
|
|
1038
|
+
const time=value=>typeof value==='number'&&Number.isFinite(value)&&value>=0?value:undefined
|
|
1039
|
+
const ledgerPersistence=s.ledgerPersistence&&typeof s.ledgerPersistence.ok==='boolean'?{ok:s.ledgerPersistence.ok,...(!s.ledgerPersistence.ok&&s.ledgerPersistence.lastError?{lastError:{at:time(s.ledgerPersistence.lastError.at),code:safeCode(s.ledgerPersistence.lastError.code)}}:{})}:undefined
|
|
1040
|
+
const n=s.nudgeEscalations
|
|
1041
|
+
const nudgeEscalations=n?{queued:Number.isSafeInteger(n.queued)&&n.queued>=0?n.queued:0,unavailable:Number.isSafeInteger(n.unavailable)&&n.unavailable>=0?n.unavailable:0,failed:Number.isSafeInteger(n.failed)&&n.failed>=0?n.failed:0,...(time(n.lastAt)===undefined?{}:{lastAt:n.lastAt}),...(n.lastError?{lastError:safeCode(n.lastError)}:{})}:undefined
|
|
1042
|
+
if(has('--json')){const {ledgerPersistence:_lp,nudgeEscalations:_ne,...snapshot}=s;console.log(JSON.stringify({...snapshot,...(ledgerPersistence?{ledgerPersistence}:{}),...(nudgeEscalations?{nudgeEscalations}:{}),deposit:list},null,2));return}
|
|
1043
|
+
if(ledgerPersistence)console.log(`ledger ${ledgerPersistence.ok?'ok':'!!! FAILED !!!'}${ledgerPersistence.lastError?` at=${String(ledgerPersistence.lastError.at??'?')} code=${ledgerPersistence.lastError.code}`:''}`)
|
|
1044
|
+
if(nudgeEscalations)console.log(`nudges queued=${nudgeEscalations.queued} unavailable=${nudgeEscalations.unavailable} failed=${nudgeEscalations.failed}${nudgeEscalations.lastAt===undefined?'':` lastAt=${nudgeEscalations.lastAt}`}${nudgeEscalations.lastError?` code=${nudgeEscalations.lastError}`:''}`)
|
|
1045
|
+
if(s.deliveryBookkeeping)console.log(`bookkeeping orphaned=${String(s.deliveryBookkeeping.orphaned??0)} failures=${String(s.deliveryBookkeeping.failures??0)}${s.deliveryBookkeeping.lastError?' code='+safeCode(s.deliveryBookkeeping.lastError.code):''}`)
|
|
1046
|
+
const reminders=(s.tasks??[]).filter(t=>t.reminderAttempts!==undefined)
|
|
1047
|
+
if(reminders.length)console.log(`reconcile ${reminders.length} task(s) reminded; ${reminders.filter(t=>t.repeatedlyUnreconciled).length} 多次提醒未对账; wait for target reconciliation, no automatic settlement`)
|
|
1016
1048
|
console.log(`host ${home()} version=${String(s.version ?? '?')} peers=${String((s.peers ?? []).length)}`)
|
|
1017
1049
|
if (s.settlement?.enforceRefused) console.log(`settlement mode=${String(s.settlement.mode)}; enforce refused: ${String(s.settlement.enforceRefused.reason)}; blockers=${JSON.stringify(s.settlement.enforceRefused.blockers)}`)
|
|
1018
1050
|
for (const p of s.peers ?? []) console.log(` peer ${String(p.url).padEnd(34)} link=${String(p.link ?? 'stranger')}`)
|
|
@@ -1029,6 +1061,10 @@ async function main() {
|
|
|
1029
1061
|
for (const m of (Array.isArray(s.boundMissRecent) ? s.boundMissRecent : []).slice(0, 5)) {
|
|
1030
1062
|
console.log(` miss ${String(m.caller)} dialled ${String(m.audience) || '(no origin)'} ×${String(m.count)}`)
|
|
1031
1063
|
}
|
|
1064
|
+
// Present but unverifiable: bad-ts is the sender's clock (±5 min); the caller fields are its own claim.
|
|
1065
|
+
for (const r of (Array.isArray(s.claimRejectsRecent) ? s.claimRejectsRecent : []).slice(0, 5)) {
|
|
1066
|
+
console.log(` reject ${String(r.reason)} from ${String(r.caller) || '(no caller)'} ${String(r.callerUrl) || ''} ×${String(r.count)}`)
|
|
1067
|
+
}
|
|
1032
1068
|
}
|
|
1033
1069
|
console.log(`deposit ${depositLine(list, identity().handle, 'OFFLINE')}`)
|
|
1034
1070
|
return
|
package/lib/a2a-client.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* tests drive them without a network.
|
|
6
6
|
* @module @nelsonlongxiang/dsh-open-a2a-net/a2a-client
|
|
7
7
|
*/
|
|
8
|
+
import { ADMITTED_BOOKKEEPING_NOTE } from "./admitted-bookkeeping.js";
|
|
9
|
+
import { RECEIPT_RETRYABLE } from "./receipt-retry.js";
|
|
8
10
|
import { verifyCard } from "./card.js";
|
|
9
11
|
import { MAX_ROUTE_BODY_BYTES, WIRE_ERROR_PAYLOAD_TOO_LARGE, withinRouteBodyCap } from "./transport-caps.js";
|
|
10
12
|
/** Direct-route HTTP budget. */
|
|
@@ -156,10 +158,12 @@ export class A2aClient {
|
|
|
156
158
|
* @param callerSession - caller label overriding the node's label (the calling session's node label).
|
|
157
159
|
* @returns the canonical route result (success or explicit failure).
|
|
158
160
|
*/
|
|
159
|
-
async routeDirect(baseUrl, team, message, contextId, signal, callerSession, asyncMode = false, taskIdFromCaller, callbackAddress, guard) {
|
|
161
|
+
async routeDirect(baseUrl, team, message, contextId, signal, callerSession, asyncMode = false, taskIdFromCaller, callbackAddress, guard, cancelTaskId) {
|
|
160
162
|
const args = { team, message, caller_session: callerSession ?? this.options.sessionId };
|
|
161
163
|
if (contextId !== undefined)
|
|
162
164
|
args.context_id = contextId;
|
|
165
|
+
if (cancelTaskId !== undefined)
|
|
166
|
+
args.cancel_task_id = cancelTaskId;
|
|
163
167
|
// wait:false asks the peer to steer and answer delivered immediately —
|
|
164
168
|
// the async half of the receipt contract for cross-host long tasks.
|
|
165
169
|
if (asyncMode)
|
|
@@ -237,7 +241,10 @@ export class A2aClient {
|
|
|
237
241
|
/* prose body — keep the generic code */
|
|
238
242
|
}
|
|
239
243
|
}
|
|
244
|
+
const status = error?.statusCode;
|
|
245
|
+
const transient = status === 503 && typeof bodyText === 'string' && bodyText.includes('task ledger write failed') || status === undefined && (abortClass || error instanceof TypeError || /ECONNREFUSED|ECONNRESET|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH/.test(String(error) + ' ' + String(error?.code ?? '') + ' ' + String(error?.cause?.code ?? '')));
|
|
240
246
|
const failure = {
|
|
247
|
+
...(transient ? { [RECEIPT_RETRYABLE]: true } : {}),
|
|
241
248
|
ok: false,
|
|
242
249
|
error: `direct route to peer failed: ${String(error)}`,
|
|
243
250
|
code,
|
|
@@ -273,7 +280,8 @@ export class A2aClient {
|
|
|
273
280
|
return {
|
|
274
281
|
ok: true,
|
|
275
282
|
team,
|
|
276
|
-
|
|
283
|
+
...(raw.receiverBookkeeping === 'failed' ? { receiverBookkeeping: 'failed' } : {}),
|
|
284
|
+
reply: raw.receiverBookkeeping === 'failed' ? ADMITTED_BOOKKEEPING_NOTE : bridge !== undefined
|
|
277
285
|
? `Delivered to ${team} (async native-teams round). It settles through the team's own routing chain and routes no A2A receipt in this slice — reconcile via context_id follow-ups.`
|
|
278
286
|
: consumed === false
|
|
279
287
|
? `Delivered to ${team} (async), but the target had not started a turn on it yet — the message is queued behind the session's current turn. Watch a2a_status activity or follow up with the context id; do not resend.`
|