getkova 2026.4.21 → 2026.4.22

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.
Files changed (102) hide show
  1. package/dist/control-ui/apple-touch-icon.png +0 -0
  2. package/dist/control-ui/assets/agents-CLtFQRXd.js +918 -0
  3. package/dist/control-ui/assets/agents-CLtFQRXd.js.map +1 -0
  4. package/dist/control-ui/assets/anthropic-CFEPAL-v.js +37 -0
  5. package/dist/control-ui/assets/anthropic-CFEPAL-v.js.map +1 -0
  6. package/dist/control-ui/assets/azure-openai-responses-CxiWQLmZ.js +2 -0
  7. package/dist/control-ui/assets/azure-openai-responses-CxiWQLmZ.js.map +1 -0
  8. package/dist/control-ui/assets/briefing-DS4VWpUL.js +121 -0
  9. package/dist/control-ui/assets/briefing-DS4VWpUL.js.map +1 -0
  10. package/dist/control-ui/assets/canvas-BfC_2Nqy.js +17 -0
  11. package/dist/control-ui/assets/canvas-BfC_2Nqy.js.map +1 -0
  12. package/dist/control-ui/assets/channel-config-extras-YNNd-4PG.js +2 -0
  13. package/dist/control-ui/assets/channel-config-extras-YNNd-4PG.js.map +1 -0
  14. package/dist/control-ui/assets/channels-BKdM7i5r.js +471 -0
  15. package/dist/control-ui/assets/channels-BKdM7i5r.js.map +1 -0
  16. package/dist/control-ui/assets/cron-C11m3yJi.js +928 -0
  17. package/dist/control-ui/assets/cron-C11m3yJi.js.map +1 -0
  18. package/dist/control-ui/assets/de-rLAkQOBc.js +2 -0
  19. package/dist/control-ui/assets/de-rLAkQOBc.js.map +1 -0
  20. package/dist/control-ui/assets/debug-DFf2qCcM.js +94 -0
  21. package/dist/control-ui/assets/debug-DFf2qCcM.js.map +1 -0
  22. package/dist/control-ui/assets/dist-D8DZLmCF.js +18 -0
  23. package/dist/control-ui/assets/dist-D8DZLmCF.js.map +1 -0
  24. package/dist/control-ui/assets/employees-DV-5FV4K.js +104 -0
  25. package/dist/control-ui/assets/employees-DV-5FV4K.js.map +1 -0
  26. package/dist/control-ui/assets/es-CIeD3O54.js +2 -0
  27. package/dist/control-ui/assets/es-CIeD3O54.js.map +1 -0
  28. package/dist/control-ui/assets/event-stream-B8X6sYaV.js +2 -0
  29. package/dist/control-ui/assets/event-stream-B8X6sYaV.js.map +1 -0
  30. package/dist/control-ui/assets/format-BahKhiOC.js +2 -0
  31. package/dist/control-ui/assets/format-BahKhiOC.js.map +1 -0
  32. package/dist/control-ui/assets/github-copilot-headers-CrI0CIJ7.js +2 -0
  33. package/dist/control-ui/assets/github-copilot-headers-CrI0CIJ7.js.map +1 -0
  34. package/dist/control-ui/assets/google-BT0bmsh5.js +2 -0
  35. package/dist/control-ui/assets/google-BT0bmsh5.js.map +1 -0
  36. package/dist/control-ui/assets/google-gemini-cli-BpxbH95Q.js +3 -0
  37. package/dist/control-ui/assets/google-gemini-cli-BpxbH95Q.js.map +1 -0
  38. package/dist/control-ui/assets/google-shared-CbPHVnPr.js +12 -0
  39. package/dist/control-ui/assets/google-shared-CbPHVnPr.js.map +1 -0
  40. package/dist/control-ui/assets/google-vertex-lQwbjEII.js +2 -0
  41. package/dist/control-ui/assets/google-vertex-lQwbjEII.js.map +1 -0
  42. package/dist/control-ui/assets/hash-Bt1aVMQ3.js +2 -0
  43. package/dist/control-ui/assets/hash-Bt1aVMQ3.js.map +1 -0
  44. package/dist/control-ui/assets/inbox-C4tOnlJr.js +100 -0
  45. package/dist/control-ui/assets/inbox-C4tOnlJr.js.map +1 -0
  46. package/dist/control-ui/assets/index-DYMuTfvX.css +1 -0
  47. package/dist/control-ui/assets/index-XGDpaFxG.js +5482 -0
  48. package/dist/control-ui/assets/index-XGDpaFxG.js.map +1 -0
  49. package/dist/control-ui/assets/instances-Cyr-tbN6.js +57 -0
  50. package/dist/control-ui/assets/instances-Cyr-tbN6.js.map +1 -0
  51. package/dist/control-ui/assets/kova-logo.png +0 -0
  52. package/dist/control-ui/assets/lit-zdTgzAJI.js +3 -0
  53. package/dist/control-ui/assets/lit-zdTgzAJI.js.map +1 -0
  54. package/dist/control-ui/assets/local-storage-D3baoRWx.js +2 -0
  55. package/dist/control-ui/assets/local-storage-D3baoRWx.js.map +1 -0
  56. package/dist/control-ui/assets/logs-B7--7dYP.js +74 -0
  57. package/dist/control-ui/assets/logs-B7--7dYP.js.map +1 -0
  58. package/dist/control-ui/assets/meetings-DSqn6s7n.js +185 -0
  59. package/dist/control-ui/assets/meetings-DSqn6s7n.js.map +1 -0
  60. package/dist/control-ui/assets/mistral-CBrDC_Gv.js +8 -0
  61. package/dist/control-ui/assets/mistral-CBrDC_Gv.js.map +1 -0
  62. package/dist/control-ui/assets/nodes-Cvq_sAqT.js +430 -0
  63. package/dist/control-ui/assets/nodes-Cvq_sAqT.js.map +1 -0
  64. package/dist/control-ui/assets/openai-Cn7eGqwa.js +17 -0
  65. package/dist/control-ui/assets/openai-Cn7eGqwa.js.map +1 -0
  66. package/dist/control-ui/assets/openai-codex-responses-DuhESMYF.js +8 -0
  67. package/dist/control-ui/assets/openai-codex-responses-DuhESMYF.js.map +1 -0
  68. package/dist/control-ui/assets/openai-completions-Bv33lqKL.js +6 -0
  69. package/dist/control-ui/assets/openai-completions-Bv33lqKL.js.map +1 -0
  70. package/dist/control-ui/assets/openai-responses-BPxpapOg.js +2 -0
  71. package/dist/control-ui/assets/openai-responses-BPxpapOg.js.map +1 -0
  72. package/dist/control-ui/assets/openai-responses-shared-8nKH8ywL.js +11 -0
  73. package/dist/control-ui/assets/openai-responses-shared-8nKH8ywL.js.map +1 -0
  74. package/dist/control-ui/assets/pdf-BwYFZMZM.js +57 -0
  75. package/dist/control-ui/assets/pdf-BwYFZMZM.js.map +1 -0
  76. package/dist/control-ui/assets/pdf.worker.min-BmpgcBpm.js +2 -0
  77. package/dist/control-ui/assets/pdf.worker.min-BmpgcBpm.js.map +1 -0
  78. package/dist/control-ui/assets/pdf.worker.min-C8PGFc0r.mjs +28 -0
  79. package/dist/control-ui/assets/preload-helper-Chd9yIcd.js +1 -0
  80. package/dist/control-ui/assets/pt-BR-lSsBb08k.js +2 -0
  81. package/dist/control-ui/assets/pt-BR-lSsBb08k.js.map +1 -0
  82. package/dist/control-ui/assets/routing-DizI_FiJ.js +157 -0
  83. package/dist/control-ui/assets/routing-DizI_FiJ.js.map +1 -0
  84. package/dist/control-ui/assets/sessions-N9rgJP2R.js +236 -0
  85. package/dist/control-ui/assets/sessions-N9rgJP2R.js.map +1 -0
  86. package/dist/control-ui/assets/skills-D1vP4MkL.js +280 -0
  87. package/dist/control-ui/assets/skills-D1vP4MkL.js.map +1 -0
  88. package/dist/control-ui/assets/skills-shared-Bg0Qcnkp.js +11 -0
  89. package/dist/control-ui/assets/skills-shared-Bg0Qcnkp.js.map +1 -0
  90. package/dist/control-ui/assets/transform-messages-XKqwKV3D.js +2 -0
  91. package/dist/control-ui/assets/transform-messages-XKqwKV3D.js.map +1 -0
  92. package/dist/control-ui/assets/zh-CN-C5tPG8Eu.js +2 -0
  93. package/dist/control-ui/assets/zh-CN-C5tPG8Eu.js.map +1 -0
  94. package/dist/control-ui/assets/zh-TW-CPSoC7Wz.js +2 -0
  95. package/dist/control-ui/assets/zh-TW-CPSoC7Wz.js.map +1 -0
  96. package/dist/control-ui/favicon-32.png +0 -0
  97. package/dist/control-ui/favicon.ico +0 -0
  98. package/dist/control-ui/favicon.png +0 -0
  99. package/dist/control-ui/favicon.svg +22 -0
  100. package/dist/control-ui/index.html +73 -0
  101. package/dist/control-ui/openclaw-canvas-auth-sw.js +57 -0
  102. package/package.json +1 -1
@@ -0,0 +1,430 @@
1
+ import{i as e,n as t}from"./lit-zdTgzAJI.js";import{l as n,r,t as i}from"./format-BahKhiOC.js";function a(e){let t=e?.agents??{},n=Array.isArray(t.list)?t.list:[],r=[];return n.forEach((e,t)=>{if(!e||typeof e!=`object`)return;let n=e,i=typeof n.id==`string`?n.id.trim():``;if(!i)return;let a=typeof n.name==`string`?n.name.trim():void 0,o=n.default===!0;r.push({id:i,name:a||void 0,isDefault:o,index:t,record:n})}),r}function o(e,t){let n=new Set(t),r=[];for(let t of e){if(!(Array.isArray(t.commands)?t.commands:[]).some(e=>n.has(String(e))))continue;let e=typeof t.nodeId==`string`?t.nodeId.trim():``;if(!e)continue;let i=typeof t.displayName==`string`&&t.displayName.trim()?t.displayName.trim():e;r.push({id:e,label:i===e?e:`${i} · ${e}`})}return r.sort((e,t)=>e.label.localeCompare(t.label)),r}var s=`__defaults__`,c=[{value:`deny`,label:`Deny`},{value:`allowlist`,label:`Allowlist`},{value:`full`,label:`Full`}],l=[{value:`off`,label:`Off`},{value:`on-miss`,label:`On miss`},{value:`always`,label:`Always`}];function u(e){return e===`allowlist`||e===`full`||e===`deny`?e:`deny`}function d(e){return e===`always`||e===`off`||e===`on-miss`?e:`on-miss`}function f(e){let t=e?.defaults??{};return{security:u(t.security),ask:d(t.ask),askFallback:u(t.askFallback??`deny`),autoAllowSkills:!!(t.autoAllowSkills??!1)}}function p(e){return a(e).map(e=>({id:e.id,name:e.name,isDefault:e.isDefault}))}function m(e,t){let n=p(e),r=Object.keys(t?.agents??{}),i=new Map;n.forEach(e=>i.set(e.id,e)),r.forEach(e=>{i.has(e)||i.set(e,{id:e})});let a=Array.from(i.values());return a.length===0&&a.push({id:`main`,isDefault:!0}),a.sort((e,t)=>{if(e.isDefault&&!t.isDefault)return-1;if(!e.isDefault&&t.isDefault)return 1;let n=e.name?.trim()?e.name:e.id,r=t.name?.trim()?t.name:t.id;return n.localeCompare(r)}),a}function h(e,t){return e===s?s:e&&t.some(t=>t.id===e)?e:s}function g(e){let t=e.execApprovalsForm??e.execApprovalsSnapshot?.file??null,n=!!t,r=f(t),i=m(e.configForm,t),a=C(e.nodes),o=e.execApprovalsTarget,c=o===`node`&&e.execApprovalsTargetNodeId?e.execApprovalsTargetNodeId:null;o===`node`&&c&&!a.some(e=>e.id===c)&&(c=null);let l=h(e.execApprovalsSelectedAgent,i),u=l===s?null:(t?.agents??{})[l]??null,d=Array.isArray(u?.allowlist)?u.allowlist??[]:[];return{ready:n,disabled:e.execApprovalsSaving||e.execApprovalsLoading,dirty:e.execApprovalsDirty,loading:e.execApprovalsLoading,saving:e.execApprovalsSaving,form:t,defaults:r,selectedScope:l,selectedAgent:u,agents:i,allowlist:d,target:o,targetNodeId:c,targetNodes:a,onSelectScope:e.onExecApprovalsSelectAgent,onSelectTarget:e.onExecApprovalsTargetChange,onPatch:e.onExecApprovalsPatch,onRemove:e.onExecApprovalsRemove,onLoad:e.onLoadExecApprovals,onSave:e.onSaveExecApprovals}}function _(n){let r=n.ready,i=n.target!==`node`||!!n.targetNodeId;return e`
2
+ <section class="card">
3
+ <div class="row" style="justify-content: space-between; align-items: center;">
4
+ <div>
5
+ <div class="card-title">Exec approvals</div>
6
+ <div class="card-sub">
7
+ Allowlist and approval policy for <span class="mono">exec host=gateway/node</span>.
8
+ </div>
9
+ </div>
10
+ <button
11
+ class="btn"
12
+ ?disabled=${n.disabled||!n.dirty||!i}
13
+ @click=${n.onSave}
14
+ >
15
+ ${n.saving?`Saving…`:`Save`}
16
+ </button>
17
+ </div>
18
+
19
+ ${v(n)}
20
+ ${r?e`
21
+ ${y(n)} ${b(n)}
22
+ ${n.selectedScope===s?t:x(n)}
23
+ `:e`<div class="row" style="margin-top: 12px; gap: 12px;">
24
+ <div class="muted">Load exec approvals to edit allowlists.</div>
25
+ <button class="btn" ?disabled=${n.loading||!i} @click=${n.onLoad}>
26
+ ${n.loading?`Loading…`:`Load approvals`}
27
+ </button>
28
+ </div>`}
29
+ </section>
30
+ `}function v(n){let r=n.targetNodes.length>0,i=n.targetNodeId??``;return e`
31
+ <div class="list" style="margin-top: 12px;">
32
+ <div class="list-item">
33
+ <div class="list-main">
34
+ <div class="list-title">Target</div>
35
+ <div class="list-sub">Gateway edits local approvals; node edits the selected node.</div>
36
+ </div>
37
+ <div class="list-meta">
38
+ <label class="field">
39
+ <span>Host</span>
40
+ <select
41
+ ?disabled=${n.disabled}
42
+ @change=${e=>{if(e.target.value===`node`){let e=n.targetNodes[0]?.id??null;n.onSelectTarget(`node`,i||e)}else n.onSelectTarget(`gateway`,null)}}
43
+ >
44
+ <option value="gateway" ?selected=${n.target===`gateway`}>Gateway</option>
45
+ <option value="node" ?selected=${n.target===`node`}>Node</option>
46
+ </select>
47
+ </label>
48
+ ${n.target===`node`?e`
49
+ <label class="field">
50
+ <span>Node</span>
51
+ <select
52
+ ?disabled=${n.disabled||!r}
53
+ @change=${e=>{let t=e.target.value.trim();n.onSelectTarget(`node`,t||null)}}
54
+ >
55
+ <option value="" ?selected=${i===``}>Select node</option>
56
+ ${n.targetNodes.map(t=>e`<option value=${t.id} ?selected=${i===t.id}>
57
+ ${t.label}
58
+ </option>`)}
59
+ </select>
60
+ </label>
61
+ `:t}
62
+ </div>
63
+ </div>
64
+ ${n.target===`node`&&!r?e` <div class="muted">No nodes advertise exec approvals yet.</div> `:t}
65
+ </div>
66
+ `}function y(t){return e`
67
+ <div class="row" style="margin-top: 12px; gap: 8px; flex-wrap: wrap;">
68
+ <span class="label">Scope</span>
69
+ <div class="row" style="gap: 8px; flex-wrap: wrap;">
70
+ <button
71
+ class="btn btn--sm ${t.selectedScope===s?`active`:``}"
72
+ @click=${()=>t.onSelectScope(s)}
73
+ >
74
+ Defaults
75
+ </button>
76
+ ${t.agents.map(n=>{let r=n.name?.trim()?`${n.name} (${n.id})`:n.id;return e`
77
+ <button
78
+ class="btn btn--sm ${t.selectedScope===n.id?`active`:``}"
79
+ @click=${()=>t.onSelectScope(n.id)}
80
+ >
81
+ ${r}
82
+ </button>
83
+ `})}
84
+ </div>
85
+ </div>
86
+ `}function b(n){let r=n.selectedScope===s,i=n.defaults,a=n.selectedAgent??{},o=r?[`defaults`]:[`agents`,n.selectedScope],u=typeof a.security==`string`?a.security:void 0,d=typeof a.ask==`string`?a.ask:void 0,f=typeof a.askFallback==`string`?a.askFallback:void 0,p=r?i.security:u??`__default__`,m=r?i.ask:d??`__default__`,h=r?i.askFallback:f??`__default__`,g=typeof a.autoAllowSkills==`boolean`?a.autoAllowSkills:void 0,_=g??i.autoAllowSkills,v=g==null;return e`
87
+ <div class="list" style="margin-top: 16px;">
88
+ <div class="list-item">
89
+ <div class="list-main">
90
+ <div class="list-title">Security</div>
91
+ <div class="list-sub">
92
+ ${r?`Default security mode.`:`Default: ${i.security}.`}
93
+ </div>
94
+ </div>
95
+ <div class="list-meta">
96
+ <label class="field">
97
+ <span>Mode</span>
98
+ <select
99
+ ?disabled=${n.disabled}
100
+ @change=${e=>{let t=e.target.value;!r&&t===`__default__`?n.onRemove([...o,`security`]):n.onPatch([...o,`security`],t)}}
101
+ >
102
+ ${r?t:e`<option value="__default__" ?selected=${p===`__default__`}>
103
+ Use default (${i.security})
104
+ </option>`}
105
+ ${c.map(t=>e`<option value=${t.value} ?selected=${p===t.value}>
106
+ ${t.label}
107
+ </option>`)}
108
+ </select>
109
+ </label>
110
+ </div>
111
+ </div>
112
+
113
+ <div class="list-item">
114
+ <div class="list-main">
115
+ <div class="list-title">Ask</div>
116
+ <div class="list-sub">
117
+ ${r?`Default prompt policy.`:`Default: ${i.ask}.`}
118
+ </div>
119
+ </div>
120
+ <div class="list-meta">
121
+ <label class="field">
122
+ <span>Mode</span>
123
+ <select
124
+ ?disabled=${n.disabled}
125
+ @change=${e=>{let t=e.target.value;!r&&t===`__default__`?n.onRemove([...o,`ask`]):n.onPatch([...o,`ask`],t)}}
126
+ >
127
+ ${r?t:e`<option value="__default__" ?selected=${m===`__default__`}>
128
+ Use default (${i.ask})
129
+ </option>`}
130
+ ${l.map(t=>e`<option value=${t.value} ?selected=${m===t.value}>
131
+ ${t.label}
132
+ </option>`)}
133
+ </select>
134
+ </label>
135
+ </div>
136
+ </div>
137
+
138
+ <div class="list-item">
139
+ <div class="list-main">
140
+ <div class="list-title">Ask fallback</div>
141
+ <div class="list-sub">
142
+ ${r?`Applied when the UI prompt is unavailable.`:`Default: ${i.askFallback}.`}
143
+ </div>
144
+ </div>
145
+ <div class="list-meta">
146
+ <label class="field">
147
+ <span>Fallback</span>
148
+ <select
149
+ ?disabled=${n.disabled}
150
+ @change=${e=>{let t=e.target.value;!r&&t===`__default__`?n.onRemove([...o,`askFallback`]):n.onPatch([...o,`askFallback`],t)}}
151
+ >
152
+ ${r?t:e`<option value="__default__" ?selected=${h===`__default__`}>
153
+ Use default (${i.askFallback})
154
+ </option>`}
155
+ ${c.map(t=>e`<option value=${t.value} ?selected=${h===t.value}>
156
+ ${t.label}
157
+ </option>`)}
158
+ </select>
159
+ </label>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="list-item">
164
+ <div class="list-main">
165
+ <div class="list-title">Auto-allow skill CLIs</div>
166
+ <div class="list-sub">
167
+ ${r?`Allow skill executables listed by the Gateway.`:v?`Using default (${i.autoAllowSkills?`on`:`off`}).`:`Override (${_?`on`:`off`}).`}
168
+ </div>
169
+ </div>
170
+ <div class="list-meta">
171
+ <label class="field">
172
+ <span>Enabled</span>
173
+ <input
174
+ type="checkbox"
175
+ ?disabled=${n.disabled}
176
+ .checked=${_}
177
+ @change=${e=>{let t=e.target;n.onPatch([...o,`autoAllowSkills`],t.checked)}}
178
+ />
179
+ </label>
180
+ ${!r&&!v?e`<button
181
+ class="btn btn--sm"
182
+ ?disabled=${n.disabled}
183
+ @click=${()=>n.onRemove([...o,`autoAllowSkills`])}
184
+ >
185
+ Use default
186
+ </button>`:t}
187
+ </div>
188
+ </div>
189
+ </div>
190
+ `}function x(t){let n=[`agents`,t.selectedScope,`allowlist`],r=t.allowlist;return e`
191
+ <div class="row" style="margin-top: 18px; justify-content: space-between;">
192
+ <div>
193
+ <div class="card-title">Allowlist</div>
194
+ <div class="card-sub">Case-insensitive glob patterns.</div>
195
+ </div>
196
+ <button
197
+ class="btn btn--sm"
198
+ ?disabled=${t.disabled}
199
+ @click=${()=>{let e=[...r,{pattern:``}];t.onPatch(n,e)}}
200
+ >
201
+ Add pattern
202
+ </button>
203
+ </div>
204
+ <div class="list" style="margin-top: 12px;">
205
+ ${r.length===0?e` <div class="muted">No allowlist entries yet.</div> `:r.map((e,n)=>S(t,e,n))}
206
+ </div>
207
+ `}function S(r,a,o){let s=a.lastUsedAt?n(a.lastUsedAt):`never`,c=a.lastUsedCommand?i(a.lastUsedCommand,120):null,l=a.lastResolvedPath?i(a.lastResolvedPath,120):null;return e`
208
+ <div class="list-item">
209
+ <div class="list-main">
210
+ <div class="list-title">${a.pattern?.trim()?a.pattern:`New pattern`}</div>
211
+ <div class="list-sub">Last used: ${s}</div>
212
+ ${c?e`<div class="list-sub mono">${c}</div>`:t}
213
+ ${l?e`<div class="list-sub mono">${l}</div>`:t}
214
+ </div>
215
+ <div class="list-meta">
216
+ <label class="field">
217
+ <span>Pattern</span>
218
+ <input
219
+ type="text"
220
+ .value=${a.pattern??``}
221
+ ?disabled=${r.disabled}
222
+ @input=${e=>{let t=e.target;r.onPatch([`agents`,r.selectedScope,`allowlist`,o,`pattern`],t.value)}}
223
+ />
224
+ </label>
225
+ <button
226
+ class="btn btn--sm danger"
227
+ ?disabled=${r.disabled}
228
+ @click=${()=>{if(r.allowlist.length<=1){r.onRemove([`agents`,r.selectedScope,`allowlist`]);return}r.onRemove([`agents`,r.selectedScope,`allowlist`,o])}}
229
+ >
230
+ Remove
231
+ </button>
232
+ </div>
233
+ </div>
234
+ `}function C(e){return o(e,[`system.execApprovals.get`,`system.execApprovals.set`])}function w(t){let n=k(t);return e`
235
+ ${_(g(t))} ${A(n)} ${T(t)}
236
+ <section class="card">
237
+ <div class="row" style="justify-content: space-between;">
238
+ <div>
239
+ <div class="card-title">Nodes</div>
240
+ <div class="card-sub">Paired devices and live links.</div>
241
+ </div>
242
+ <button class="btn" ?disabled=${t.loading} @click=${t.onRefresh}>
243
+ ${t.loading?`Loading…`:`Refresh`}
244
+ </button>
245
+ </div>
246
+ <div class="list" style="margin-top: 16px;">
247
+ ${t.nodes.length===0?e` <div class="muted">No nodes found.</div> `:t.nodes.map(e=>P(e))}
248
+ </div>
249
+ </section>
250
+ `}function T(n){let r=n.devicesList??{pending:[],paired:[]},i=Array.isArray(r.pending)?r.pending:[],a=Array.isArray(r.paired)?r.paired:[];return e`
251
+ <section class="card">
252
+ <div class="row" style="justify-content: space-between;">
253
+ <div>
254
+ <div class="card-title">Devices</div>
255
+ <div class="card-sub">Pairing requests + role tokens.</div>
256
+ </div>
257
+ <button class="btn" ?disabled=${n.devicesLoading} @click=${n.onDevicesRefresh}>
258
+ ${n.devicesLoading?`Loading…`:`Refresh`}
259
+ </button>
260
+ </div>
261
+ ${n.devicesError?e`<div class="callout danger" style="margin-top: 12px;">${n.devicesError}</div>`:t}
262
+ <div class="list" style="margin-top: 16px;">
263
+ ${i.length>0?e`
264
+ <div class="muted" style="margin-bottom: 8px;">Pending</div>
265
+ ${i.map(e=>E(e,n))}
266
+ `:t}
267
+ ${a.length>0?e`
268
+ <div class="muted" style="margin-top: 12px; margin-bottom: 8px;">Paired</div>
269
+ ${a.map(e=>D(e,n))}
270
+ `:t}
271
+ ${i.length===0&&a.length===0?e` <div class="muted">No paired devices.</div> `:t}
272
+ </div>
273
+ </section>
274
+ `}function E(t,i){let a=t.displayName?.trim()||t.deviceId,o=typeof t.ts==`number`?n(t.ts):`n/a`,s=t.role?.trim()||r(t.roles),c=r(t.scopes),l=t.isRepair?` · repair`:``,u=t.remoteIp?` · ${t.remoteIp}`:``;return e`
275
+ <div class="list-item">
276
+ <div class="list-main">
277
+ <div class="list-title">${a}</div>
278
+ <div class="list-sub">${t.deviceId}${u}</div>
279
+ <div class="muted" style="margin-top: 6px;">
280
+ role: ${s} · scopes: ${c} · requested ${o}${l}
281
+ </div>
282
+ </div>
283
+ <div class="list-meta">
284
+ <div class="row" style="justify-content: flex-end; gap: 8px; flex-wrap: wrap;">
285
+ <button class="btn btn--sm primary" @click=${()=>i.onDeviceApprove(t.requestId)}>
286
+ Approve
287
+ </button>
288
+ <button class="btn btn--sm" @click=${()=>i.onDeviceReject(t.requestId)}>
289
+ Reject
290
+ </button>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ `}function D(t,n){let i=t.displayName?.trim()||t.deviceId,a=t.remoteIp?` · ${t.remoteIp}`:``,o=`roles: ${r(t.roles)}`,s=`scopes: ${r(t.scopes)}`,c=Array.isArray(t.tokens)?t.tokens:[];return e`
295
+ <div class="list-item">
296
+ <div class="list-main">
297
+ <div class="list-title">${i}</div>
298
+ <div class="list-sub">${t.deviceId}${a}</div>
299
+ <div class="muted" style="margin-top: 6px;">${o} · ${s}</div>
300
+ ${c.length===0?e` <div class="muted" style="margin-top: 6px">Tokens: none</div> `:e`
301
+ <div class="muted" style="margin-top: 10px;">Tokens</div>
302
+ <div style="display: flex; flex-direction: column; gap: 8px; margin-top: 6px;">
303
+ ${c.map(e=>O(t.deviceId,e,n))}
304
+ </div>
305
+ `}
306
+ </div>
307
+ </div>
308
+ `}function O(i,a,o){let s=a.revokedAtMs?`revoked`:`active`,c=`scopes: ${r(a.scopes)}`,l=n(a.rotatedAtMs??a.createdAtMs??a.lastUsedAtMs??null);return e`
309
+ <div class="row" style="justify-content: space-between; gap: 8px;">
310
+ <div class="list-sub">${a.role} · ${s} · ${c} · ${l}</div>
311
+ <div class="row" style="justify-content: flex-end; gap: 6px; flex-wrap: wrap;">
312
+ <button
313
+ class="btn btn--sm"
314
+ @click=${()=>o.onDeviceRotate(i,a.role,a.scopes)}
315
+ >
316
+ Rotate
317
+ </button>
318
+ ${a.revokedAtMs?t:e`
319
+ <button
320
+ class="btn btn--sm danger"
321
+ @click=${()=>o.onDeviceRevoke(i,a.role)}
322
+ >
323
+ Revoke
324
+ </button>
325
+ `}
326
+ </div>
327
+ </div>
328
+ `}function k(e){let t=e.configForm,n=M(e.nodes),{defaultBinding:r,agents:i}=N(t);return{ready:!!t,disabled:e.configSaving||e.configFormMode===`raw`,configDirty:e.configDirty,configLoading:e.configLoading,configSaving:e.configSaving,defaultBinding:r,agents:i,nodes:n,onBindDefault:e.onBindDefault,onBindAgent:e.onBindAgent,onSave:e.onSaveBindings,onLoadConfig:e.onLoadConfig,formMode:e.configFormMode}}function A(n){let r=n.nodes.length>0,i=n.defaultBinding??``;return e`
329
+ <section class="card">
330
+ <div class="row" style="justify-content: space-between; align-items: center;">
331
+ <div>
332
+ <div class="card-title">Exec node binding</div>
333
+ <div class="card-sub">
334
+ Pin agents to a specific node when using <span class="mono">exec host=node</span>.
335
+ </div>
336
+ </div>
337
+ <button
338
+ class="btn"
339
+ ?disabled=${n.disabled||!n.configDirty}
340
+ @click=${n.onSave}
341
+ >
342
+ ${n.configSaving?`Saving…`:`Save`}
343
+ </button>
344
+ </div>
345
+
346
+ ${n.formMode===`raw`?e`
347
+ <div class="callout warn" style="margin-top: 12px">
348
+ Switch the Config tab to <strong>Form</strong> mode to edit bindings here.
349
+ </div>
350
+ `:t}
351
+ ${n.ready?e`
352
+ <div class="list" style="margin-top: 16px;">
353
+ <div class="list-item">
354
+ <div class="list-main">
355
+ <div class="list-title">Default binding</div>
356
+ <div class="list-sub">Used when agents do not override a node binding.</div>
357
+ </div>
358
+ <div class="list-meta">
359
+ <label class="field">
360
+ <span>Node</span>
361
+ <select
362
+ ?disabled=${n.disabled||!r}
363
+ @change=${e=>{let t=e.target.value.trim();n.onBindDefault(t||null)}}
364
+ >
365
+ <option value="" ?selected=${i===``}>Any node</option>
366
+ ${n.nodes.map(t=>e`<option value=${t.id} ?selected=${i===t.id}>
367
+ ${t.label}
368
+ </option>`)}
369
+ </select>
370
+ </label>
371
+ ${r?t:e` <div class="muted">No nodes with system.run available.</div> `}
372
+ </div>
373
+ </div>
374
+
375
+ ${n.agents.length===0?e` <div class="muted">No agents found.</div> `:n.agents.map(e=>j(e,n))}
376
+ </div>
377
+ `:e`<div class="row" style="margin-top: 12px; gap: 12px;">
378
+ <div class="muted">Load config to edit bindings.</div>
379
+ <button class="btn" ?disabled=${n.configLoading} @click=${n.onLoadConfig}>
380
+ ${n.configLoading?`Loading…`:`Load config`}
381
+ </button>
382
+ </div>`}
383
+ </section>
384
+ `}function j(t,n){let r=t.binding??`__default__`,i=t.name?.trim()?`${t.name} (${t.id})`:t.id,a=n.nodes.length>0;return e`
385
+ <div class="list-item">
386
+ <div class="list-main">
387
+ <div class="list-title">${i}</div>
388
+ <div class="list-sub">
389
+ ${t.isDefault?`default agent`:`agent`} ·
390
+ ${r===`__default__`?`uses default (${n.defaultBinding??`any`})`:`override: ${t.binding}`}
391
+ </div>
392
+ </div>
393
+ <div class="list-meta">
394
+ <label class="field">
395
+ <span>Binding</span>
396
+ <select
397
+ ?disabled=${n.disabled||!a}
398
+ @change=${e=>{let r=e.target.value.trim();n.onBindAgent(t.index,r===`__default__`?null:r)}}
399
+ >
400
+ <option value="__default__" ?selected=${r===`__default__`}>
401
+ Use default
402
+ </option>
403
+ ${n.nodes.map(t=>e`<option value=${t.id} ?selected=${r===t.id}>
404
+ ${t.label}
405
+ </option>`)}
406
+ </select>
407
+ </label>
408
+ </div>
409
+ </div>
410
+ `}function M(e){return o(e,[`system.run`])}function N(e){let t={id:`main`,name:void 0,index:0,isDefault:!0,binding:null};if(!e||typeof e!=`object`)return{defaultBinding:null,agents:[t]};let n=(e.tools??{}).exec??{},r=typeof n.node==`string`&&n.node.trim()?n.node.trim():null,i=e.agents??{};if(!Array.isArray(i.list)||i.list.length===0)return{defaultBinding:r,agents:[t]};let o=a(e).map(e=>{let t=(e.record.tools??{}).exec??{},n=typeof t.node==`string`&&t.node.trim()?t.node.trim():null;return{id:e.id,name:e.name,index:e.index,isDefault:e.isDefault,binding:n}});return o.length===0&&o.push(t),{defaultBinding:r,agents:o}}function P(t){let n=!!t.connected,r=!!t.paired,i=typeof t.displayName==`string`&&t.displayName.trim()||(typeof t.nodeId==`string`?t.nodeId:`unknown`),a=Array.isArray(t.caps)?t.caps:[],o=Array.isArray(t.commands)?t.commands:[];return e`
411
+ <div class="list-item">
412
+ <div class="list-main">
413
+ <div class="list-title">${i}</div>
414
+ <div class="list-sub">
415
+ ${typeof t.nodeId==`string`?t.nodeId:``}
416
+ ${typeof t.remoteIp==`string`?` · ${t.remoteIp}`:``}
417
+ ${typeof t.version==`string`?` · ${t.version}`:``}
418
+ </div>
419
+ <div class="chip-row" style="margin-top: 6px;">
420
+ <span class="chip">${r?`paired`:`unpaired`}</span>
421
+ <span class="chip ${n?`chip-ok`:`chip-warn`}">
422
+ ${n?`connected`:`offline`}
423
+ </span>
424
+ ${a.slice(0,12).map(t=>e`<span class="chip">${String(t)}</span>`)}
425
+ ${o.slice(0,8).map(t=>e`<span class="chip">${String(t)}</span>`)}
426
+ </div>
427
+ </div>
428
+ </div>
429
+ `}export{w as renderNodes};
430
+ //# sourceMappingURL=nodes-Cvq_sAqT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodes-Cvq_sAqT.js","names":[],"sources":["../../../ui/src/ui/views/nodes-shared.ts","../../../ui/src/ui/views/nodes-exec-approvals.ts","../../../ui/src/ui/views/nodes.ts"],"sourcesContent":["export type NodeTargetOption = {\n id: string;\n label: string;\n};\n\nexport type ConfigAgentOption = {\n id: string;\n name?: string;\n isDefault: boolean;\n index: number;\n record: Record<string, unknown>;\n};\n\nexport function resolveConfigAgents(config: Record<string, unknown> | null): ConfigAgentOption[] {\n const agentsNode = (config?.agents ?? {}) as Record<string, unknown>;\n const list = Array.isArray(agentsNode.list) ? agentsNode.list : [];\n const agents: ConfigAgentOption[] = [];\n\n list.forEach((entry, index) => {\n if (!entry || typeof entry !== \"object\") {\n return;\n }\n const record = entry as Record<string, unknown>;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n if (!id) {\n return;\n }\n const name = typeof record.name === \"string\" ? record.name.trim() : undefined;\n const isDefault = record.default === true;\n agents.push({ id, name: name || undefined, isDefault, index, record });\n });\n\n return agents;\n}\n\nexport function resolveNodeTargets(\n nodes: Array<Record<string, unknown>>,\n requiredCommands: string[],\n): NodeTargetOption[] {\n const required = new Set(requiredCommands);\n const list: NodeTargetOption[] = [];\n\n for (const node of nodes) {\n const commands = Array.isArray(node.commands) ? node.commands : [];\n const supports = commands.some((cmd) => required.has(String(cmd)));\n if (!supports) {\n continue;\n }\n\n const nodeId = typeof node.nodeId === \"string\" ? node.nodeId.trim() : \"\";\n if (!nodeId) {\n continue;\n }\n\n const displayName =\n typeof node.displayName === \"string\" && node.displayName.trim()\n ? node.displayName.trim()\n : nodeId;\n list.push({\n id: nodeId,\n label: displayName === nodeId ? nodeId : `${displayName} · ${nodeId}`,\n });\n }\n\n list.sort((a, b) => a.label.localeCompare(b.label));\n return list;\n}\n","import { html, nothing } from \"lit\";\nimport type {\n ExecApprovalsAllowlistEntry,\n ExecApprovalsFile,\n} from \"../controllers/exec-approvals.ts\";\nimport { clampText, formatRelativeTimestamp } from \"../format.ts\";\nimport {\n resolveConfigAgents as resolveSharedConfigAgents,\n resolveNodeTargets,\n type NodeTargetOption,\n} from \"./nodes-shared.ts\";\nimport type { NodesProps } from \"./nodes.ts\";\n\ntype ExecSecurity = \"deny\" | \"allowlist\" | \"full\";\ntype ExecAsk = \"off\" | \"on-miss\" | \"always\";\n\ntype ExecApprovalsResolvedDefaults = {\n security: ExecSecurity;\n ask: ExecAsk;\n askFallback: ExecSecurity;\n autoAllowSkills: boolean;\n};\n\ntype ExecApprovalsAgentOption = {\n id: string;\n name?: string;\n isDefault?: boolean;\n};\n\ntype ExecApprovalsTargetNode = NodeTargetOption;\n\ntype ExecApprovalsState = {\n ready: boolean;\n disabled: boolean;\n dirty: boolean;\n loading: boolean;\n saving: boolean;\n form: ExecApprovalsFile | null;\n defaults: ExecApprovalsResolvedDefaults;\n selectedScope: string;\n selectedAgent: Record<string, unknown> | null;\n agents: ExecApprovalsAgentOption[];\n allowlist: ExecApprovalsAllowlistEntry[];\n target: \"gateway\" | \"node\";\n targetNodeId: string | null;\n targetNodes: ExecApprovalsTargetNode[];\n onSelectScope: (agentId: string) => void;\n onSelectTarget: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onPatch: (path: Array<string | number>, value: unknown) => void;\n onRemove: (path: Array<string | number>) => void;\n onLoad: () => void;\n onSave: () => void;\n};\n\nconst EXEC_APPROVALS_DEFAULT_SCOPE = \"__defaults__\";\n\nconst SECURITY_OPTIONS: Array<{ value: ExecSecurity; label: string }> = [\n { value: \"deny\", label: \"Deny\" },\n { value: \"allowlist\", label: \"Allowlist\" },\n { value: \"full\", label: \"Full\" },\n];\n\nconst ASK_OPTIONS: Array<{ value: ExecAsk; label: string }> = [\n { value: \"off\", label: \"Off\" },\n { value: \"on-miss\", label: \"On miss\" },\n { value: \"always\", label: \"Always\" },\n];\n\nfunction normalizeSecurity(value?: string): ExecSecurity {\n if (value === \"allowlist\" || value === \"full\" || value === \"deny\") {\n return value;\n }\n return \"deny\";\n}\n\nfunction normalizeAsk(value?: string): ExecAsk {\n if (value === \"always\" || value === \"off\" || value === \"on-miss\") {\n return value;\n }\n return \"on-miss\";\n}\n\nfunction resolveExecApprovalsDefaults(\n form: ExecApprovalsFile | null,\n): ExecApprovalsResolvedDefaults {\n const defaults = form?.defaults ?? {};\n return {\n security: normalizeSecurity(defaults.security),\n ask: normalizeAsk(defaults.ask),\n askFallback: normalizeSecurity(defaults.askFallback ?? \"deny\"),\n autoAllowSkills: Boolean(defaults.autoAllowSkills ?? false),\n };\n}\n\nfunction resolveConfigAgents(config: Record<string, unknown> | null): ExecApprovalsAgentOption[] {\n return resolveSharedConfigAgents(config).map((entry) => ({\n id: entry.id,\n name: entry.name,\n isDefault: entry.isDefault,\n }));\n}\n\nfunction resolveExecApprovalsAgents(\n config: Record<string, unknown> | null,\n form: ExecApprovalsFile | null,\n): ExecApprovalsAgentOption[] {\n const configAgents = resolveConfigAgents(config);\n const approvalsAgents = Object.keys(form?.agents ?? {});\n const merged = new Map<string, ExecApprovalsAgentOption>();\n configAgents.forEach((agent) => merged.set(agent.id, agent));\n approvalsAgents.forEach((id) => {\n if (merged.has(id)) {\n return;\n }\n merged.set(id, { id });\n });\n const agents = Array.from(merged.values());\n if (agents.length === 0) {\n agents.push({ id: \"main\", isDefault: true });\n }\n agents.sort((a, b) => {\n if (a.isDefault && !b.isDefault) {\n return -1;\n }\n if (!a.isDefault && b.isDefault) {\n return 1;\n }\n const aLabel = a.name?.trim() ? a.name : a.id;\n const bLabel = b.name?.trim() ? b.name : b.id;\n return aLabel.localeCompare(bLabel);\n });\n return agents;\n}\n\nfunction resolveExecApprovalsScope(\n selected: string | null,\n agents: ExecApprovalsAgentOption[],\n): string {\n if (selected === EXEC_APPROVALS_DEFAULT_SCOPE) {\n return EXEC_APPROVALS_DEFAULT_SCOPE;\n }\n if (selected && agents.some((agent) => agent.id === selected)) {\n return selected;\n }\n return EXEC_APPROVALS_DEFAULT_SCOPE;\n}\n\nexport function resolveExecApprovalsState(props: NodesProps): ExecApprovalsState {\n const form = props.execApprovalsForm ?? props.execApprovalsSnapshot?.file ?? null;\n const ready = Boolean(form);\n const defaults = resolveExecApprovalsDefaults(form);\n const agents = resolveExecApprovalsAgents(props.configForm, form);\n const targetNodes = resolveExecApprovalsNodes(props.nodes);\n const target = props.execApprovalsTarget;\n let targetNodeId =\n target === \"node\" && props.execApprovalsTargetNodeId ? props.execApprovalsTargetNodeId : null;\n if (target === \"node\" && targetNodeId && !targetNodes.some((node) => node.id === targetNodeId)) {\n targetNodeId = null;\n }\n const selectedScope = resolveExecApprovalsScope(props.execApprovalsSelectedAgent, agents);\n const selectedAgent =\n selectedScope !== EXEC_APPROVALS_DEFAULT_SCOPE\n ? (((form?.agents ?? {})[selectedScope] as Record<string, unknown> | undefined) ?? null)\n : null;\n const allowlist = Array.isArray((selectedAgent as { allowlist?: unknown })?.allowlist)\n ? ((selectedAgent as { allowlist?: ExecApprovalsAllowlistEntry[] }).allowlist ?? [])\n : [];\n return {\n ready,\n disabled: props.execApprovalsSaving || props.execApprovalsLoading,\n dirty: props.execApprovalsDirty,\n loading: props.execApprovalsLoading,\n saving: props.execApprovalsSaving,\n form,\n defaults,\n selectedScope,\n selectedAgent,\n agents,\n allowlist,\n target,\n targetNodeId,\n targetNodes,\n onSelectScope: props.onExecApprovalsSelectAgent,\n onSelectTarget: props.onExecApprovalsTargetChange,\n onPatch: props.onExecApprovalsPatch,\n onRemove: props.onExecApprovalsRemove,\n onLoad: props.onLoadExecApprovals,\n onSave: props.onSaveExecApprovals,\n };\n}\n\nexport function renderExecApprovals(state: ExecApprovalsState) {\n const ready = state.ready;\n const targetReady = state.target !== \"node\" || Boolean(state.targetNodeId);\n return html`\n <section class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between; align-items: center;\">\n <div>\n <div class=\"card-title\">Exec approvals</div>\n <div class=\"card-sub\">\n Allowlist and approval policy for <span class=\"mono\">exec host=gateway/node</span>.\n </div>\n </div>\n <button\n class=\"btn\"\n ?disabled=${state.disabled || !state.dirty || !targetReady}\n @click=${state.onSave}\n >\n ${state.saving ? \"Saving…\" : \"Save\"}\n </button>\n </div>\n\n ${renderExecApprovalsTarget(state)}\n ${!ready\n ? html`<div class=\"row\" style=\"margin-top: 12px; gap: 12px;\">\n <div class=\"muted\">Load exec approvals to edit allowlists.</div>\n <button class=\"btn\" ?disabled=${state.loading || !targetReady} @click=${state.onLoad}>\n ${state.loading ? \"Loading…\" : \"Load approvals\"}\n </button>\n </div>`\n : html`\n ${renderExecApprovalsTabs(state)} ${renderExecApprovalsPolicy(state)}\n ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE\n ? nothing\n : renderExecApprovalsAllowlist(state)}\n `}\n </section>\n `;\n}\n\nfunction renderExecApprovalsTarget(state: ExecApprovalsState) {\n const hasNodes = state.targetNodes.length > 0;\n const nodeValue = state.targetNodeId ?? \"\";\n return html`\n <div class=\"list\" style=\"margin-top: 12px;\">\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Target</div>\n <div class=\"list-sub\">Gateway edits local approvals; node edits the selected node.</div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Host</span>\n <select\n ?disabled=${state.disabled}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value;\n if (value === \"node\") {\n const first = state.targetNodes[0]?.id ?? null;\n state.onSelectTarget(\"node\", nodeValue || first);\n } else {\n state.onSelectTarget(\"gateway\", null);\n }\n }}\n >\n <option value=\"gateway\" ?selected=${state.target === \"gateway\"}>Gateway</option>\n <option value=\"node\" ?selected=${state.target === \"node\"}>Node</option>\n </select>\n </label>\n ${state.target === \"node\"\n ? html`\n <label class=\"field\">\n <span>Node</span>\n <select\n ?disabled=${state.disabled || !hasNodes}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value.trim();\n state.onSelectTarget(\"node\", value ? value : null);\n }}\n >\n <option value=\"\" ?selected=${nodeValue === \"\"}>Select node</option>\n ${state.targetNodes.map(\n (node) =>\n html`<option value=${node.id} ?selected=${nodeValue === node.id}>\n ${node.label}\n </option>`,\n )}\n </select>\n </label>\n `\n : nothing}\n </div>\n </div>\n ${state.target === \"node\" && !hasNodes\n ? html` <div class=\"muted\">No nodes advertise exec approvals yet.</div> `\n : nothing}\n </div>\n `;\n}\n\nfunction renderExecApprovalsTabs(state: ExecApprovalsState) {\n return html`\n <div class=\"row\" style=\"margin-top: 12px; gap: 8px; flex-wrap: wrap;\">\n <span class=\"label\">Scope</span>\n <div class=\"row\" style=\"gap: 8px; flex-wrap: wrap;\">\n <button\n class=\"btn btn--sm ${state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE\n ? \"active\"\n : \"\"}\"\n @click=${() => state.onSelectScope(EXEC_APPROVALS_DEFAULT_SCOPE)}\n >\n Defaults\n </button>\n ${state.agents.map((agent) => {\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n return html`\n <button\n class=\"btn btn--sm ${state.selectedScope === agent.id ? \"active\" : \"\"}\"\n @click=${() => state.onSelectScope(agent.id)}\n >\n ${label}\n </button>\n `;\n })}\n </div>\n </div>\n `;\n}\n\nfunction renderExecApprovalsPolicy(state: ExecApprovalsState) {\n const isDefaults = state.selectedScope === EXEC_APPROVALS_DEFAULT_SCOPE;\n const defaults = state.defaults;\n const agent = state.selectedAgent ?? {};\n const basePath = isDefaults ? [\"defaults\"] : [\"agents\", state.selectedScope];\n const agentSecurity = typeof agent.security === \"string\" ? agent.security : undefined;\n const agentAsk = typeof agent.ask === \"string\" ? agent.ask : undefined;\n const agentAskFallback = typeof agent.askFallback === \"string\" ? agent.askFallback : undefined;\n const securityValue = isDefaults ? defaults.security : (agentSecurity ?? \"__default__\");\n const askValue = isDefaults ? defaults.ask : (agentAsk ?? \"__default__\");\n const askFallbackValue = isDefaults ? defaults.askFallback : (agentAskFallback ?? \"__default__\");\n const autoOverride =\n typeof agent.autoAllowSkills === \"boolean\" ? agent.autoAllowSkills : undefined;\n const autoEffective = autoOverride ?? defaults.autoAllowSkills;\n const autoIsDefault = autoOverride == null;\n\n return html`\n <div class=\"list\" style=\"margin-top: 16px;\">\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Security</div>\n <div class=\"list-sub\">\n ${isDefaults ? \"Default security mode.\" : `Default: ${defaults.security}.`}\n </div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Mode</span>\n <select\n ?disabled=${state.disabled}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value;\n if (!isDefaults && value === \"__default__\") {\n state.onRemove([...basePath, \"security\"]);\n } else {\n state.onPatch([...basePath, \"security\"], value);\n }\n }}\n >\n ${!isDefaults\n ? html`<option value=\"__default__\" ?selected=${securityValue === \"__default__\"}>\n Use default (${defaults.security})\n </option>`\n : nothing}\n ${SECURITY_OPTIONS.map(\n (option) =>\n html`<option value=${option.value} ?selected=${securityValue === option.value}>\n ${option.label}\n </option>`,\n )}\n </select>\n </label>\n </div>\n </div>\n\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Ask</div>\n <div class=\"list-sub\">\n ${isDefaults ? \"Default prompt policy.\" : `Default: ${defaults.ask}.`}\n </div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Mode</span>\n <select\n ?disabled=${state.disabled}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value;\n if (!isDefaults && value === \"__default__\") {\n state.onRemove([...basePath, \"ask\"]);\n } else {\n state.onPatch([...basePath, \"ask\"], value);\n }\n }}\n >\n ${!isDefaults\n ? html`<option value=\"__default__\" ?selected=${askValue === \"__default__\"}>\n Use default (${defaults.ask})\n </option>`\n : nothing}\n ${ASK_OPTIONS.map(\n (option) =>\n html`<option value=${option.value} ?selected=${askValue === option.value}>\n ${option.label}\n </option>`,\n )}\n </select>\n </label>\n </div>\n </div>\n\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Ask fallback</div>\n <div class=\"list-sub\">\n ${isDefaults\n ? \"Applied when the UI prompt is unavailable.\"\n : `Default: ${defaults.askFallback}.`}\n </div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Fallback</span>\n <select\n ?disabled=${state.disabled}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value;\n if (!isDefaults && value === \"__default__\") {\n state.onRemove([...basePath, \"askFallback\"]);\n } else {\n state.onPatch([...basePath, \"askFallback\"], value);\n }\n }}\n >\n ${!isDefaults\n ? html`<option value=\"__default__\" ?selected=${askFallbackValue === \"__default__\"}>\n Use default (${defaults.askFallback})\n </option>`\n : nothing}\n ${SECURITY_OPTIONS.map(\n (option) =>\n html`<option value=${option.value} ?selected=${askFallbackValue === option.value}>\n ${option.label}\n </option>`,\n )}\n </select>\n </label>\n </div>\n </div>\n\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Auto-allow skill CLIs</div>\n <div class=\"list-sub\">\n ${isDefaults\n ? \"Allow skill executables listed by the Gateway.\"\n : autoIsDefault\n ? `Using default (${defaults.autoAllowSkills ? \"on\" : \"off\"}).`\n : `Override (${autoEffective ? \"on\" : \"off\"}).`}\n </div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Enabled</span>\n <input\n type=\"checkbox\"\n ?disabled=${state.disabled}\n .checked=${autoEffective}\n @change=${(event: Event) => {\n const target = event.target as HTMLInputElement;\n state.onPatch([...basePath, \"autoAllowSkills\"], target.checked);\n }}\n />\n </label>\n ${!isDefaults && !autoIsDefault\n ? html`<button\n class=\"btn btn--sm\"\n ?disabled=${state.disabled}\n @click=${() => state.onRemove([...basePath, \"autoAllowSkills\"])}\n >\n Use default\n </button>`\n : nothing}\n </div>\n </div>\n </div>\n `;\n}\n\nfunction renderExecApprovalsAllowlist(state: ExecApprovalsState) {\n const allowlistPath = [\"agents\", state.selectedScope, \"allowlist\"];\n const entries = state.allowlist;\n return html`\n <div class=\"row\" style=\"margin-top: 18px; justify-content: space-between;\">\n <div>\n <div class=\"card-title\">Allowlist</div>\n <div class=\"card-sub\">Case-insensitive glob patterns.</div>\n </div>\n <button\n class=\"btn btn--sm\"\n ?disabled=${state.disabled}\n @click=${() => {\n const next = [...entries, { pattern: \"\" }];\n state.onPatch(allowlistPath, next);\n }}\n >\n Add pattern\n </button>\n </div>\n <div class=\"list\" style=\"margin-top: 12px;\">\n ${entries.length === 0\n ? html` <div class=\"muted\">No allowlist entries yet.</div> `\n : entries.map((entry, index) => renderAllowlistEntry(state, entry, index))}\n </div>\n `;\n}\n\nfunction renderAllowlistEntry(\n state: ExecApprovalsState,\n entry: ExecApprovalsAllowlistEntry,\n index: number,\n) {\n const lastUsed = entry.lastUsedAt ? formatRelativeTimestamp(entry.lastUsedAt) : \"never\";\n const lastCommand = entry.lastUsedCommand ? clampText(entry.lastUsedCommand, 120) : null;\n const lastPath = entry.lastResolvedPath ? clampText(entry.lastResolvedPath, 120) : null;\n return html`\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">${entry.pattern?.trim() ? entry.pattern : \"New pattern\"}</div>\n <div class=\"list-sub\">Last used: ${lastUsed}</div>\n ${lastCommand ? html`<div class=\"list-sub mono\">${lastCommand}</div>` : nothing}\n ${lastPath ? html`<div class=\"list-sub mono\">${lastPath}</div>` : nothing}\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Pattern</span>\n <input\n type=\"text\"\n .value=${entry.pattern ?? \"\"}\n ?disabled=${state.disabled}\n @input=${(event: Event) => {\n const target = event.target as HTMLInputElement;\n state.onPatch(\n [\"agents\", state.selectedScope, \"allowlist\", index, \"pattern\"],\n target.value,\n );\n }}\n />\n </label>\n <button\n class=\"btn btn--sm danger\"\n ?disabled=${state.disabled}\n @click=${() => {\n if (state.allowlist.length <= 1) {\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\"]);\n return;\n }\n state.onRemove([\"agents\", state.selectedScope, \"allowlist\", index]);\n }}\n >\n Remove\n </button>\n </div>\n </div>\n `;\n}\n\nfunction resolveExecApprovalsNodes(\n nodes: Array<Record<string, unknown>>,\n): ExecApprovalsTargetNode[] {\n return resolveNodeTargets(nodes, [\"system.execApprovals.get\", \"system.execApprovals.set\"]);\n}\n","import { html, nothing } from \"lit\";\nimport type {\n DevicePairingList,\n DeviceTokenSummary,\n PairedDevice,\n PendingDevice,\n} from \"../controllers/devices.ts\";\nimport type { ExecApprovalsFile, ExecApprovalsSnapshot } from \"../controllers/exec-approvals.ts\";\nimport { formatRelativeTimestamp, formatList } from \"../format.ts\";\nimport { renderExecApprovals, resolveExecApprovalsState } from \"./nodes-exec-approvals.ts\";\nimport { resolveConfigAgents, resolveNodeTargets, type NodeTargetOption } from \"./nodes-shared.ts\";\nexport type NodesProps = {\n loading: boolean;\n nodes: Array<Record<string, unknown>>;\n devicesLoading: boolean;\n devicesError: string | null;\n devicesList: DevicePairingList | null;\n configForm: Record<string, unknown> | null;\n configLoading: boolean;\n configSaving: boolean;\n configDirty: boolean;\n configFormMode: \"form\" | \"raw\";\n execApprovalsLoading: boolean;\n execApprovalsSaving: boolean;\n execApprovalsDirty: boolean;\n execApprovalsSnapshot: ExecApprovalsSnapshot | null;\n execApprovalsForm: ExecApprovalsFile | null;\n execApprovalsSelectedAgent: string | null;\n execApprovalsTarget: \"gateway\" | \"node\";\n execApprovalsTargetNodeId: string | null;\n onRefresh: () => void;\n onDevicesRefresh: () => void;\n onDeviceApprove: (requestId: string) => void;\n onDeviceReject: (requestId: string) => void;\n onDeviceRotate: (deviceId: string, role: string, scopes?: string[]) => void;\n onDeviceRevoke: (deviceId: string, role: string) => void;\n onLoadConfig: () => void;\n onLoadExecApprovals: () => void;\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSaveBindings: () => void;\n onExecApprovalsTargetChange: (kind: \"gateway\" | \"node\", nodeId: string | null) => void;\n onExecApprovalsSelectAgent: (agentId: string) => void;\n onExecApprovalsPatch: (path: Array<string | number>, value: unknown) => void;\n onExecApprovalsRemove: (path: Array<string | number>) => void;\n onSaveExecApprovals: () => void;\n};\n\nexport function renderNodes(props: NodesProps) {\n const bindingState = resolveBindingsState(props);\n const approvalsState = resolveExecApprovalsState(props);\n return html`\n ${renderExecApprovals(approvalsState)} ${renderBindings(bindingState)} ${renderDevices(props)}\n <section class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between;\">\n <div>\n <div class=\"card-title\">Nodes</div>\n <div class=\"card-sub\">Paired devices and live links.</div>\n </div>\n <button class=\"btn\" ?disabled=${props.loading} @click=${props.onRefresh}>\n ${props.loading ? \"Loading…\" : \"Refresh\"}\n </button>\n </div>\n <div class=\"list\" style=\"margin-top: 16px;\">\n ${props.nodes.length === 0\n ? html` <div class=\"muted\">No nodes found.</div> `\n : props.nodes.map((n) => renderNode(n))}\n </div>\n </section>\n `;\n}\n\nfunction renderDevices(props: NodesProps) {\n const list = props.devicesList ?? { pending: [], paired: [] };\n const pending = Array.isArray(list.pending) ? list.pending : [];\n const paired = Array.isArray(list.paired) ? list.paired : [];\n return html`\n <section class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between;\">\n <div>\n <div class=\"card-title\">Devices</div>\n <div class=\"card-sub\">Pairing requests + role tokens.</div>\n </div>\n <button class=\"btn\" ?disabled=${props.devicesLoading} @click=${props.onDevicesRefresh}>\n ${props.devicesLoading ? \"Loading…\" : \"Refresh\"}\n </button>\n </div>\n ${props.devicesError\n ? html`<div class=\"callout danger\" style=\"margin-top: 12px;\">${props.devicesError}</div>`\n : nothing}\n <div class=\"list\" style=\"margin-top: 16px;\">\n ${pending.length > 0\n ? html`\n <div class=\"muted\" style=\"margin-bottom: 8px;\">Pending</div>\n ${pending.map((req) => renderPendingDevice(req, props))}\n `\n : nothing}\n ${paired.length > 0\n ? html`\n <div class=\"muted\" style=\"margin-top: 12px; margin-bottom: 8px;\">Paired</div>\n ${paired.map((device) => renderPairedDevice(device, props))}\n `\n : nothing}\n ${pending.length === 0 && paired.length === 0\n ? html` <div class=\"muted\">No paired devices.</div> `\n : nothing}\n </div>\n </section>\n `;\n}\n\nfunction renderPendingDevice(req: PendingDevice, props: NodesProps) {\n const name = req.displayName?.trim() || req.deviceId;\n const age = typeof req.ts === \"number\" ? formatRelativeTimestamp(req.ts) : \"n/a\";\n const roleValue = req.role?.trim() || formatList(req.roles);\n const scopesValue = formatList(req.scopes);\n const repair = req.isRepair ? \" · repair\" : \"\";\n const ip = req.remoteIp ? ` · ${req.remoteIp}` : \"\";\n return html`\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">${name}</div>\n <div class=\"list-sub\">${req.deviceId}${ip}</div>\n <div class=\"muted\" style=\"margin-top: 6px;\">\n role: ${roleValue} · scopes: ${scopesValue} · requested ${age}${repair}\n </div>\n </div>\n <div class=\"list-meta\">\n <div class=\"row\" style=\"justify-content: flex-end; gap: 8px; flex-wrap: wrap;\">\n <button class=\"btn btn--sm primary\" @click=${() => props.onDeviceApprove(req.requestId)}>\n Approve\n </button>\n <button class=\"btn btn--sm\" @click=${() => props.onDeviceReject(req.requestId)}>\n Reject\n </button>\n </div>\n </div>\n </div>\n `;\n}\n\nfunction renderPairedDevice(device: PairedDevice, props: NodesProps) {\n const name = device.displayName?.trim() || device.deviceId;\n const ip = device.remoteIp ? ` · ${device.remoteIp}` : \"\";\n const roles = `roles: ${formatList(device.roles)}`;\n const scopes = `scopes: ${formatList(device.scopes)}`;\n const tokens = Array.isArray(device.tokens) ? device.tokens : [];\n return html`\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">${name}</div>\n <div class=\"list-sub\">${device.deviceId}${ip}</div>\n <div class=\"muted\" style=\"margin-top: 6px;\">${roles} · ${scopes}</div>\n ${tokens.length === 0\n ? html` <div class=\"muted\" style=\"margin-top: 6px\">Tokens: none</div> `\n : html`\n <div class=\"muted\" style=\"margin-top: 10px;\">Tokens</div>\n <div style=\"display: flex; flex-direction: column; gap: 8px; margin-top: 6px;\">\n ${tokens.map((token) => renderTokenRow(device.deviceId, token, props))}\n </div>\n `}\n </div>\n </div>\n `;\n}\n\nfunction renderTokenRow(deviceId: string, token: DeviceTokenSummary, props: NodesProps) {\n const status = token.revokedAtMs ? \"revoked\" : \"active\";\n const scopes = `scopes: ${formatList(token.scopes)}`;\n const when = formatRelativeTimestamp(\n token.rotatedAtMs ?? token.createdAtMs ?? token.lastUsedAtMs ?? null,\n );\n return html`\n <div class=\"row\" style=\"justify-content: space-between; gap: 8px;\">\n <div class=\"list-sub\">${token.role} · ${status} · ${scopes} · ${when}</div>\n <div class=\"row\" style=\"justify-content: flex-end; gap: 6px; flex-wrap: wrap;\">\n <button\n class=\"btn btn--sm\"\n @click=${() => props.onDeviceRotate(deviceId, token.role, token.scopes)}\n >\n Rotate\n </button>\n ${token.revokedAtMs\n ? nothing\n : html`\n <button\n class=\"btn btn--sm danger\"\n @click=${() => props.onDeviceRevoke(deviceId, token.role)}\n >\n Revoke\n </button>\n `}\n </div>\n </div>\n `;\n}\n\ntype BindingAgent = {\n id: string;\n name: string | undefined;\n index: number;\n isDefault: boolean;\n binding: string | null;\n};\n\ntype BindingNode = NodeTargetOption;\n\ntype BindingState = {\n ready: boolean;\n disabled: boolean;\n configDirty: boolean;\n configLoading: boolean;\n configSaving: boolean;\n defaultBinding?: string | null;\n agents: BindingAgent[];\n nodes: BindingNode[];\n onBindDefault: (nodeId: string | null) => void;\n onBindAgent: (agentIndex: number, nodeId: string | null) => void;\n onSave: () => void;\n onLoadConfig: () => void;\n formMode: \"form\" | \"raw\";\n};\n\nfunction resolveBindingsState(props: NodesProps): BindingState {\n const config = props.configForm;\n const nodes = resolveExecNodes(props.nodes);\n const { defaultBinding, agents } = resolveAgentBindings(config);\n const ready = Boolean(config);\n const disabled = props.configSaving || props.configFormMode === \"raw\";\n return {\n ready,\n disabled,\n configDirty: props.configDirty,\n configLoading: props.configLoading,\n configSaving: props.configSaving,\n defaultBinding,\n agents,\n nodes,\n onBindDefault: props.onBindDefault,\n onBindAgent: props.onBindAgent,\n onSave: props.onSaveBindings,\n onLoadConfig: props.onLoadConfig,\n formMode: props.configFormMode,\n };\n}\n\nfunction renderBindings(state: BindingState) {\n const supportsBinding = state.nodes.length > 0;\n const defaultValue = state.defaultBinding ?? \"\";\n return html`\n <section class=\"card\">\n <div class=\"row\" style=\"justify-content: space-between; align-items: center;\">\n <div>\n <div class=\"card-title\">Exec node binding</div>\n <div class=\"card-sub\">\n Pin agents to a specific node when using <span class=\"mono\">exec host=node</span>.\n </div>\n </div>\n <button\n class=\"btn\"\n ?disabled=${state.disabled || !state.configDirty}\n @click=${state.onSave}\n >\n ${state.configSaving ? \"Saving…\" : \"Save\"}\n </button>\n </div>\n\n ${state.formMode === \"raw\"\n ? html`\n <div class=\"callout warn\" style=\"margin-top: 12px\">\n Switch the Config tab to <strong>Form</strong> mode to edit bindings here.\n </div>\n `\n : nothing}\n ${!state.ready\n ? html`<div class=\"row\" style=\"margin-top: 12px; gap: 12px;\">\n <div class=\"muted\">Load config to edit bindings.</div>\n <button class=\"btn\" ?disabled=${state.configLoading} @click=${state.onLoadConfig}>\n ${state.configLoading ? \"Loading…\" : \"Load config\"}\n </button>\n </div>`\n : html`\n <div class=\"list\" style=\"margin-top: 16px;\">\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">Default binding</div>\n <div class=\"list-sub\">Used when agents do not override a node binding.</div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Node</span>\n <select\n ?disabled=${state.disabled || !supportsBinding}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value.trim();\n state.onBindDefault(value ? value : null);\n }}\n >\n <option value=\"\" ?selected=${defaultValue === \"\"}>Any node</option>\n ${state.nodes.map(\n (node) =>\n html`<option value=${node.id} ?selected=${defaultValue === node.id}>\n ${node.label}\n </option>`,\n )}\n </select>\n </label>\n ${!supportsBinding\n ? html` <div class=\"muted\">No nodes with system.run available.</div> `\n : nothing}\n </div>\n </div>\n\n ${state.agents.length === 0\n ? html` <div class=\"muted\">No agents found.</div> `\n : state.agents.map((agent) => renderAgentBinding(agent, state))}\n </div>\n `}\n </section>\n `;\n}\n\nfunction renderAgentBinding(agent: BindingAgent, state: BindingState) {\n const bindingValue = agent.binding ?? \"__default__\";\n const label = agent.name?.trim() ? `${agent.name} (${agent.id})` : agent.id;\n const supportsBinding = state.nodes.length > 0;\n return html`\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">${label}</div>\n <div class=\"list-sub\">\n ${agent.isDefault ? \"default agent\" : \"agent\"} ·\n ${bindingValue === \"__default__\"\n ? `uses default (${state.defaultBinding ?? \"any\"})`\n : `override: ${agent.binding}`}\n </div>\n </div>\n <div class=\"list-meta\">\n <label class=\"field\">\n <span>Binding</span>\n <select\n ?disabled=${state.disabled || !supportsBinding}\n @change=${(event: Event) => {\n const target = event.target as HTMLSelectElement;\n const value = target.value.trim();\n state.onBindAgent(agent.index, value === \"__default__\" ? null : value);\n }}\n >\n <option value=\"__default__\" ?selected=${bindingValue === \"__default__\"}>\n Use default\n </option>\n ${state.nodes.map(\n (node) =>\n html`<option value=${node.id} ?selected=${bindingValue === node.id}>\n ${node.label}\n </option>`,\n )}\n </select>\n </label>\n </div>\n </div>\n `;\n}\n\nfunction resolveExecNodes(nodes: Array<Record<string, unknown>>): BindingNode[] {\n return resolveNodeTargets(nodes, [\"system.run\"]);\n}\n\nfunction resolveAgentBindings(config: Record<string, unknown> | null): {\n defaultBinding?: string | null;\n agents: BindingAgent[];\n} {\n const fallbackAgent: BindingAgent = {\n id: \"main\",\n name: undefined,\n index: 0,\n isDefault: true,\n binding: null,\n };\n if (!config || typeof config !== \"object\") {\n return { defaultBinding: null, agents: [fallbackAgent] };\n }\n const tools = (config.tools ?? {}) as Record<string, unknown>;\n const exec = (tools.exec ?? {}) as Record<string, unknown>;\n const defaultBinding =\n typeof exec.node === \"string\" && exec.node.trim() ? exec.node.trim() : null;\n\n const agentsNode = (config.agents ?? {}) as Record<string, unknown>;\n if (!Array.isArray(agentsNode.list) || agentsNode.list.length === 0) {\n return { defaultBinding, agents: [fallbackAgent] };\n }\n\n const agents = resolveConfigAgents(config).map((entry) => {\n const toolsEntry = (entry.record.tools ?? {}) as Record<string, unknown>;\n const execEntry = (toolsEntry.exec ?? {}) as Record<string, unknown>;\n const binding =\n typeof execEntry.node === \"string\" && execEntry.node.trim() ? execEntry.node.trim() : null;\n return {\n id: entry.id,\n name: entry.name,\n index: entry.index,\n isDefault: entry.isDefault,\n binding,\n };\n });\n\n if (agents.length === 0) {\n agents.push(fallbackAgent);\n }\n\n return { defaultBinding, agents };\n}\n\nfunction renderNode(node: Record<string, unknown>) {\n const connected = Boolean(node.connected);\n const paired = Boolean(node.paired);\n const title =\n (typeof node.displayName === \"string\" && node.displayName.trim()) ||\n (typeof node.nodeId === \"string\" ? node.nodeId : \"unknown\");\n const caps = Array.isArray(node.caps) ? (node.caps as unknown[]) : [];\n const commands = Array.isArray(node.commands) ? (node.commands as unknown[]) : [];\n return html`\n <div class=\"list-item\">\n <div class=\"list-main\">\n <div class=\"list-title\">${title}</div>\n <div class=\"list-sub\">\n ${typeof node.nodeId === \"string\" ? node.nodeId : \"\"}\n ${typeof node.remoteIp === \"string\" ? ` · ${node.remoteIp}` : \"\"}\n ${typeof node.version === \"string\" ? ` · ${node.version}` : \"\"}\n </div>\n <div class=\"chip-row\" style=\"margin-top: 6px;\">\n <span class=\"chip\">${paired ? \"paired\" : \"unpaired\"}</span>\n <span class=\"chip ${connected ? \"chip-ok\" : \"chip-warn\"}\">\n ${connected ? \"connected\" : \"offline\"}\n </span>\n ${caps.slice(0, 12).map((c) => html`<span class=\"chip\">${String(c)}</span>`)}\n ${commands.slice(0, 8).map((c) => html`<span class=\"chip\">${String(c)}</span>`)}\n </div>\n </div>\n </div>\n `;\n}\n"],"mappings":"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"}