@lmctl-ai/lmctl 0.2.437 → 0.2.439
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/dist/cli/index.js +804 -1191
- package/dist/cli/lmscript.js +620 -422
- package/dist/cli/schema.sql +99 -0
- package/package.json +2 -6
- package/bin/lmbee +0 -32
- package/build-info.json +0 -1
- package/dist/lmbee.js +0 -2992
- package/dist/public/app.js +0 -379
- package/dist/public/index.html +0 -78
- package/dist/public/styles.css +0 -160
- package/dist/schema.sql +0 -472
- package/dist/vendor-webui/index.html +0 -18
- package/dist/vendor-webui/local.js +0 -231
- package/dist/vendor-webui/styles.css +0 -1852
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";(()=>{function S(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function C(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${S(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${S(t)}</code>, ${r}s ago</p>`}function F(t,e,r){return`
|
|
2
|
-
${C(e,r)}
|
|
3
|
-
<p data-local-account-whoami>
|
|
4
|
-
<strong>${S(t.label)}</strong> (<code>${S(t.userId)}</code>) \xB7 appkey source: ${S(t.appkeySource)}
|
|
5
|
-
</p>
|
|
6
|
-
`}function N(t){return`<p class="form-error" role="alert" data-local-account-whoami-error>${S(t)}</p>`}function O(t,e,r){return`
|
|
7
|
-
${C(e,r)}
|
|
8
|
-
<table class="local-table" data-local-account-mail-table>
|
|
9
|
-
<thead><tr><th>STATE</th><th>COUNT</th></tr></thead>
|
|
10
|
-
<tbody>
|
|
11
|
-
<tr><td>persisted</td><td>${t.counts.persisted}</td></tr>
|
|
12
|
-
<tr><td>dispatched</td><td>${t.counts.dispatched}</td></tr>
|
|
13
|
-
<tr><td>replied</td><td>${t.counts.replied}</td></tr>
|
|
14
|
-
<tr><td>failed</td><td>${t.counts.failed}</td></tr>
|
|
15
|
-
</tbody>
|
|
16
|
-
</table>
|
|
17
|
-
<p class="muted">retry-eligible: ${t.retryEligibleCount} \xB7 permanent: ${t.permanentCount} \xB7 due now: ${t.dueNowCount} \xB7 delivery errors: ${t.deliveryErrorCount}</p>
|
|
18
|
-
`}function W(t){return`<p class="form-error" role="alert" data-local-account-mail-error>${S(t)}</p>`}var U={id:"account",label:"Account",mount(t,e){let r=new AbortController;t.innerHTML=`
|
|
19
|
-
<section class="local-pane" data-local-pane="account">
|
|
20
|
-
<header><h2>Account</h2></header>
|
|
21
|
-
<div data-local-account-whoami-host><p class="muted" role="status">Loading identity\u2026</p></div>
|
|
22
|
-
<div data-local-account-mail-host><p class="muted" role="status">Loading mail status\u2026</p></div>
|
|
23
|
-
</section>
|
|
24
|
-
`;let a=t.querySelector("[data-local-account-whoami-host]"),s=t.querySelector("[data-local-account-mail-host]");return(async()=>{let i=await e.transport.read({kind:"whoami"},r.signal);if(!(r.signal.aborted||!a)){if(i.status==="error"){a.innerHTML=N(i.error.message);return}i.status!=="pending"&&(a.innerHTML=F(i.data,i.ranCommand,i.ranAt))}})(),(async()=>{let i=await e.transport.read({kind:"mail-status"},r.signal);if(!(r.signal.aborted||!s)){if(i.status==="error"){s.innerHTML=W(i.error.message);return}i.status!=="pending"&&(s.innerHTML=O(i.data,i.ranCommand,i.ranAt))}})(),()=>{r.abort()}}},E=U;function x(t){return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function P(t,e){if(t&&typeof t=="object"&&"error"in t){let r=t.error;if(r&&typeof r=="object"){let a=r;if(typeof a.code=="string"&&typeof a.message=="string")return{code:a.code,message:a.message}}if(typeof r=="string")return{code:"http_error",message:r}}return{code:"http_error",message:`HTTP ${e}`}}function Q(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.name=="string"&&(e.channel==="local"||e.channel==="lmmail")}function B(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="number"&&typeof e.from_id=="string"&&typeof e.to_id=="string"&&typeof e.status=="string"}function G(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="number"&&typeof e.ts=="string"&&typeof e.team=="string"}function J(t){return!t||typeof t!="object"?!1:typeof t.team=="string"}function K(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="number"&&typeof e.path=="string"}function V(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.provider=="string"&&typeof e.sessionId=="string"&&Array.isArray(e.messages)}function z(t){if(!t||typeof t!="object")return!1;let e=t;return Array.isArray(e.running)&&Array.isArray(e.dead)&&Array.isArray(e.idle)}function X(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.userId=="string"&&typeof e.label=="string"}function Y(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.counts=="object"&&e.counts!==null}function Z(t){return!t||typeof t!="object"?!1:typeof t.team=="string"}function tt(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.live=="boolean"&&Array.isArray(e.teams)&&e.teams.every(Z)}var L=class{apiBase;constructor(e=""){this.apiBase=e}url(e){return`${this.apiBase}${e}`}async read(e,r){let a=()=>new Date().toISOString();if(e.kind==="teams")return this.get(this.url("api/teams"),"lmauto teams --json",n=>{let o=n;return Array.isArray(o.teams)?{teams:o.teams.filter(Q)}:null},a,r);if(e.kind==="queue-list"){let l=e.team?`?to=${encodeURIComponent(e.team)}`:"",n=e.team?`lmauto queue list ${e.team} --json`:"lmauto queue list --json";return this.get(this.url(`api/queue${l}`),n,o=>{let d=o;return Array.isArray(d.queue)?{queue:d.queue.filter(B)}:null},a,r)}if(e.kind==="history"){let l=new URLSearchParams;e.team&&l.set("team",e.team),e.limit!==void 0&&l.set("limit",String(e.limit));let n=l.toString(),o=`lmauto history${e.team?` ${e.team}`:""}${e.limit!==void 0?` ${e.limit}`:""} --json`;return this.get(this.url(`api/history${n?`?${n}`:""}`),o,d=>{let u=d;return Array.isArray(u.events)?{events:u.events.filter(G)}:null},a,r)}if(e.kind==="state-list")return this.get(this.url("api/state"),"lmauto state list --json",n=>{let o=n;return!Array.isArray(o.teams)||!Array.isArray(o.active)?null:{teams:o.teams.filter(J),active:o.active.filter(d=>typeof d=="string")}},a,r);if(e.kind==="sources")return this.get(this.url("api/sources"),"lmauto source list --json",n=>{let o=n;return typeof o.defaultTeamsConfPath!="string"||!Array.isArray(o.sources)?null:{defaultTeamsConfPath:o.defaultTeamsConfPath,sources:o.sources.filter(K)}},a,r);if(e.kind==="session"){let l=`lmctl session ${e.team} ${e.alias}`;return this.get(this.url(`api/session/${encodeURIComponent(e.team)}/${encodeURIComponent(e.alias)}`),l,n=>V(n)?n:null,a,r)}if(e.kind==="running")return this.get(this.url("api/running"),"lmctl running --json",n=>z(n)?n:null,a,r);if(e.kind==="whoami")return this.get(this.url("api/whoami"),"lmauto whoami --json",n=>X(n)?n:null,a,r);if(e.kind==="mail-status")return this.get(this.url("api/mail/status"),"lmauto mail status --json",n=>Y(n)?n:null,a,r);let s=e.live?"?live=1":"",i=e.live?"lmauto status --live --json":"lmauto status --json";return this.get(this.url(`api/status${s}`),i,l=>tt(l)?l:null,a,r)}async get(e,r,a,s,i){try{let l=await fetch(e,{method:"GET",signal:i}),n=null;try{n=await l.json()}catch{n=null}if(!l.ok)return{status:"error",data:null,ranCommand:r,ranAt:null,error:P(n,l.status)};let o=a(n);return o===null?{status:"error",data:null,ranCommand:r,ranAt:null,error:{code:"invalid_response",message:"Unexpected response shape."}}:{status:"ready",data:o,ranCommand:r,ranAt:s()}}catch(l){if(i.aborted)throw l;return{status:"error",data:null,ranCommand:r,ranAt:null,error:{code:"network_error",message:l instanceof Error?l.message:"Network error."}}}}async write(e,r){if(e.kind==="enqueue"){let s=`lmauto enqueue ${e.team} "${x(e.message)}" --from ${e.from}`;return this.post(this.url("api/enqueue"),{to:e.team,from:e.from,prompt:e.message},s,i=>{let l=i;return typeof l.id!="number"?null:`queued #${l.id} -> ${e.team}`},r)}if(e.kind==="send"){let s=`lmauto send ${e.team} "${x(e.message)}"`;return this.post(this.url("api/send"),{team:e.team,prompt:e.message},s,i=>{let l=i;return typeof l.status!="string"?null:`${e.team}: ${l.status}`},r)}if(e.kind==="source-add"){let s=e.label?`lmauto source add ${e.path} "${x(e.label)}"`:`lmauto source add ${e.path}`,i=e.label?{path:e.path,label:e.label}:{path:e.path};return this.post(this.url("api/sources"),i,s,l=>l.ok!==!0?null:`added ${e.path}`,r)}let a=`lmauto source remove ${e.path}`;return this.del(this.url(`api/sources?path=${encodeURIComponent(e.path)}`),a,s=>s.ok!==!0?null:`removed ${e.path}`,r)}async del(e,r,a,s){let i=new Date().toISOString();try{let l=await fetch(e,{method:"DELETE",signal:s}),n=null;try{n=await l.json()}catch{n=null}if(!l.ok){let d=P(n,l.status);return{ok:!1,ranCommand:r,ranAt:i,summary:d.message,raw:n,error:d}}let o=a(n);if(o===null){let d={code:"invalid_response",message:"Unexpected response shape."};return{ok:!1,ranCommand:r,ranAt:i,summary:d.message,raw:n,error:d}}return{ok:!0,ranCommand:r,ranAt:i,summary:o,raw:n}}catch(l){if(s.aborted)throw l;let n={code:"network_error",message:l instanceof Error?l.message:"Network error."};return{ok:!1,ranCommand:r,ranAt:i,summary:n.message,raw:null,error:n}}}async post(e,r,a,s,i){let l=new Date().toISOString();try{let n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r),signal:i}),o=null;try{o=await n.json()}catch{o=null}if(!n.ok){let u=P(o,n.status);return{ok:!1,ranCommand:a,ranAt:l,summary:u.message,raw:o,error:u}}let d=s(o);if(d===null){let u={code:"invalid_response",message:"Unexpected response shape."};return{ok:!1,ranCommand:a,ranAt:l,summary:u.message,raw:o,error:u}}return{ok:!0,ranCommand:a,ranAt:l,summary:d,raw:o}}catch(n){if(i.aborted)throw n;let o={code:"network_error",message:n instanceof Error?n.message:"Network error."};return{ok:!1,ranCommand:a,ranAt:l,summary:o.message,raw:null,error:o}}}};function p(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function et(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${p(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${p(t)}</code>, ${r}s ago</p>`}function I(t){return t===null?"":p(t)}function rt(t){return`<tr data-local-running-row="${t.id}"><td>${p(t.kind)}</td><td>${I(t.target)}</td><td>${p(t.started_at)}</td><td>${t.running_for_ms}</td><td>${p(t.holder_liveness)}</td></tr>`}function nt(t){return`<tr data-local-dead-row="${t.id}"><td>${p(t.kind)}</td><td>${I(t.target)}</td><td>${p(t.started_at)}</td><td>${t.age_ms}</td></tr>`}function at(t){return`<tr data-local-idle-row="${t.id}"><td>${p(t.kind)}</td><td>${I(t.target)}</td><td>${p(t.status)}</td><td>${t.idle_ms}</td><td>${t.error?p(t.error):""}</td></tr>`}function ot(t,e,r,a,s,i){t.innerHTML=`
|
|
25
|
-
<section class="local-pane" data-local-pane="running">
|
|
26
|
-
<header><h2>Running</h2></header>
|
|
27
|
-
${et(s,i)}
|
|
28
|
-
<h3>Running (${e.length})</h3>
|
|
29
|
-
${e.length===0?'<p class="muted">Nothing running.</p>':`<table class="local-table" data-local-running-table><thead><tr><th>KIND</th><th>TARGET</th><th>STARTED</th><th>RUNNING-FOR-MS</th><th>LIVENESS</th></tr></thead><tbody>${e.map(rt).join("")}</tbody></table>`}
|
|
30
|
-
<h3>Dead (${r.length})</h3>
|
|
31
|
-
${r.length===0?'<p class="muted">None.</p>':`<table class="local-table" data-local-dead-table><thead><tr><th>KIND</th><th>TARGET</th><th>STARTED</th><th>AGE-MS</th></tr></thead><tbody>${r.map(nt).join("")}</tbody></table>`}
|
|
32
|
-
<h3>Idle (${a.length})</h3>
|
|
33
|
-
${a.length===0?'<p class="muted">None.</p>':`<table class="local-table" data-local-idle-table><thead><tr><th>KIND</th><th>TARGET</th><th>STATUS</th><th>IDLE-MS</th><th>ERROR</th></tr></thead><tbody>${a.map(at).join("")}</tbody></table>`}
|
|
34
|
-
</section>
|
|
35
|
-
`}function st(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-running-error>${p(e)}</p>`}var lt={id:"running",label:"Running",mount(t,e){let r=new AbortController;return t.innerHTML='<p class="muted" role="status">Loading running\u2026</p>',(async()=>{let a=await e.transport.read({kind:"running"},r.signal);if(!r.signal.aborted){if(a.status==="error"){st(t,a.error.message);return}a.status!=="pending"&&ot(t,a.data.running,a.data.dead,a.data.idle,a.ranCommand,a.ranAt)}})(),()=>{r.abort()}}},$=lt;function j(t,e,r){t.innerHTML=`
|
|
36
|
-
<div class="local-shell">
|
|
37
|
-
<nav class="local-shell-nav" data-local-shell-nav></nav>
|
|
38
|
-
<main class="local-shell-pane" data-local-shell-pane></main>
|
|
39
|
-
</div>
|
|
40
|
-
`;let a=t.querySelector("[data-local-shell-nav]"),s=t.querySelector("[data-local-shell-pane]");if(!a||!s)return()=>{};let i=null,l=null;function n(d){if(i===d)return;l?.(),l=null,i=d;for(let c of a?.querySelectorAll("[data-local-pane-button]")??[])c.setAttribute("aria-current",c.dataset.localPaneButton===d?"true":"false");let u=r.find(c=>c.id===d);!u||!s||(l=u.pane.mount(s,{transport:e}))}a.innerHTML=r.map(d=>`<button type="button" class="local-pane-button" data-local-pane-button="${d.id}">${d.label}</button>`).join("");for(let d of a.querySelectorAll("[data-local-pane-button]"))d.addEventListener("click",()=>n(d.dataset.localPaneButton??""));let o=r[0];return o&&n(o.id),()=>{l?.(),l=null}}function y(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function it(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${y(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${y(t)}</code>, ${r}s ago</p>`}function dt(t){return`
|
|
41
|
-
<tr data-local-queue-row="${t.id}">
|
|
42
|
-
<td>${t.id}</td>
|
|
43
|
-
<td>${y(t.from_id)}</td>
|
|
44
|
-
<td>${y(t.to_id)}</td>
|
|
45
|
-
<td>${y(t.status)}</td>
|
|
46
|
-
<td>${y(t.created_ts)}</td>
|
|
47
|
-
</tr>
|
|
48
|
-
`}function ut(t,e){t.innerHTML=`
|
|
49
|
-
<section class="local-pane" data-local-pane="queue">
|
|
50
|
-
<header>
|
|
51
|
-
<h2>Queue</h2>
|
|
52
|
-
<form class="local-filter" data-local-queue-filter>
|
|
53
|
-
<input type="text" name="team" placeholder="Filter by team (optional)" value="${y(e)}" />
|
|
54
|
-
<button type="submit">Load</button>
|
|
55
|
-
</form>
|
|
56
|
-
</header>
|
|
57
|
-
<div data-local-queue-read><p class="muted" role="status">Loading queue\u2026</p></div>
|
|
58
|
-
<form class="local-write-form" data-local-queue-enqueue>
|
|
59
|
-
<h3>Enqueue</h3>
|
|
60
|
-
<label>Team <input type="text" name="team" required /></label>
|
|
61
|
-
<label>From (sender id) <input type="text" name="from" required /></label>
|
|
62
|
-
<label>Prompt <textarea name="message" required></textarea></label>
|
|
63
|
-
<button type="submit">Enqueue</button>
|
|
64
|
-
<p class="form-error" data-local-queue-write-error role="alert" hidden></p>
|
|
65
|
-
<p class="muted" data-local-queue-write-status hidden></p>
|
|
66
|
-
</form>
|
|
67
|
-
</section>
|
|
68
|
-
`}function ct(t,e,r,a){t.innerHTML=`
|
|
69
|
-
${it(r,a)}
|
|
70
|
-
${e.length===0?'<p class="muted">No queued items.</p>':`
|
|
71
|
-
<table class="local-table" data-local-queue-table>
|
|
72
|
-
<thead><tr><th>ID</th><th>FROM</th><th>TO</th><th>STATUS</th><th>CREATED</th></tr></thead>
|
|
73
|
-
<tbody>${e.map(dt).join("")}</tbody>
|
|
74
|
-
</table>
|
|
75
|
-
`}
|
|
76
|
-
`}function mt(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-queue-read-error>${y(e)}</p>`}var pt={id:"queue",label:"Queue",mount(t,e){let r=new AbortController,a="";ut(t,a);let s=t.querySelector("[data-local-queue-read]");async function i(){if(!s)return;let n=a?await e.transport.read({kind:"queue-list",team:a},r.signal):await e.transport.read({kind:"queue-list"},r.signal);if(!r.signal.aborted){if(n.status==="error"){mt(s,n.error.message);return}n.status!=="pending"&&ct(s,n.data.queue,n.ranCommand,n.ranAt)}}return t.querySelector("[data-local-queue-filter]")?.addEventListener("submit",n=>{n.preventDefault(),a=n.currentTarget.elements.namedItem("team")?.value.trim()??"",i()}),t.querySelector("[data-local-queue-enqueue]")?.addEventListener("submit",n=>{n.preventDefault();let o=n.currentTarget,d=o.querySelector("[data-local-queue-write-error]"),u=o.querySelector("[data-local-queue-write-status]");d&&(d.hidden=!0),u&&(u.hidden=!0);let c=o.elements.namedItem("team")?.value.trim()??"",h=o.elements.namedItem("from")?.value.trim()??"",m=o.elements.namedItem("message")?.value.trim()??"";!c||!h||!m||(async()=>{let w=await e.transport.write({kind:"enqueue",team:c,from:h,message:m},r.signal);if(r.signal.aborted)return;if(!w.ok){d&&(d.textContent=w.summary,d.hidden=!1);return}u&&(u.textContent=`${w.summary} \u2014 ran: ${w.ranCommand}`,u.hidden=!1),o.reset(),a=c;let _=t.querySelector('[data-local-queue-filter] input[name="team"]');_&&(_.value=c),i()})()}),i(),()=>{r.abort()}}},R=pt;function b(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function gt(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${b(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${b(t)}</code>, ${r}s ago</p>`}function ft(t){return`
|
|
77
|
-
<tr data-local-history-row="${t.id}">
|
|
78
|
-
<td>${t.id}</td>
|
|
79
|
-
<td>${b(t.ts)}</td>
|
|
80
|
-
<td>${b(t.team)}</td>
|
|
81
|
-
<td>${b(t.status)}</td>
|
|
82
|
-
<td>${t.note?b(t.note):""}</td>
|
|
83
|
-
</tr>
|
|
84
|
-
`}function ht(t,e){t.innerHTML=`
|
|
85
|
-
<section class="local-pane" data-local-pane="send">
|
|
86
|
-
<header>
|
|
87
|
-
<h2>Send</h2>
|
|
88
|
-
<form class="local-filter" data-local-history-filter>
|
|
89
|
-
<input type="text" name="team" placeholder="Filter by team (optional)" value="${b(e)}" />
|
|
90
|
-
<button type="submit">Load</button>
|
|
91
|
-
</form>
|
|
92
|
-
</header>
|
|
93
|
-
<div data-local-history-read><p class="muted" role="status">Loading history\u2026</p></div>
|
|
94
|
-
<form class="local-write-form" data-local-send-form>
|
|
95
|
-
<h3>Send</h3>
|
|
96
|
-
<label>Team <input type="text" name="team" required /></label>
|
|
97
|
-
<label>Prompt <textarea name="message" required></textarea></label>
|
|
98
|
-
<button type="submit">Send</button>
|
|
99
|
-
<p class="form-error" data-local-send-write-error role="alert" hidden></p>
|
|
100
|
-
<p class="muted" data-local-send-write-status hidden></p>
|
|
101
|
-
</form>
|
|
102
|
-
</section>
|
|
103
|
-
`}function yt(t,e,r,a){t.innerHTML=`
|
|
104
|
-
${gt(r,a)}
|
|
105
|
-
${e.length===0?'<p class="muted">No history yet.</p>':`
|
|
106
|
-
<table class="local-table" data-local-history-table>
|
|
107
|
-
<thead><tr><th>ID</th><th>TS</th><th>TEAM</th><th>STATUS</th><th>NOTE</th></tr></thead>
|
|
108
|
-
<tbody>${e.map(ft).join("")}</tbody>
|
|
109
|
-
</table>
|
|
110
|
-
`}
|
|
111
|
-
`}function bt(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-history-error>${b(e)}</p>`}var Tt={id:"send",label:"Send",mount(t,e){let r=new AbortController,a="";ht(t,a);let s=t.querySelector("[data-local-history-read]");async function i(){if(!s)return;let n=a?await e.transport.read({kind:"history",team:a},r.signal):await e.transport.read({kind:"history"},r.signal);if(!r.signal.aborted){if(n.status==="error"){bt(s,n.error.message);return}n.status!=="pending"&&yt(s,n.data.events,n.ranCommand,n.ranAt)}}return t.querySelector("[data-local-history-filter]")?.addEventListener("submit",n=>{n.preventDefault(),a=n.currentTarget.elements.namedItem("team")?.value.trim()??"",i()}),t.querySelector("[data-local-send-form]")?.addEventListener("submit",n=>{n.preventDefault();let o=n.currentTarget,d=o.querySelector("[data-local-send-write-error]"),u=o.querySelector("[data-local-send-write-status]");d&&(d.hidden=!0),u&&(u.hidden=!0);let c=o.elements.namedItem("team")?.value.trim()??"",h=o.elements.namedItem("message")?.value.trim()??"";!c||!h||(async()=>{let m=await e.transport.write({kind:"send",team:c,message:h},r.signal);if(r.signal.aborted)return;if(!m.ok){d&&(d.textContent=m.summary,d.hidden=!1);return}u&&(u.textContent=`${m.summary} \u2014 ran: ${m.ranCommand}`,u.hidden=!1),o.reset(),a=c;let w=t.querySelector('[data-local-history-filter] input[name="team"]');w&&(w.value=c),i()})()}),i(),()=>{r.abort()}}},k=Tt;function T(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function wt(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${T(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${T(t)}</code>, ${r}s ago</p>`}function St(t){let e=typeof t.content=="string"?t.content:JSON.stringify(t.content);return`
|
|
112
|
-
<li data-local-session-message>
|
|
113
|
-
<strong>${T(t.role)}</strong>
|
|
114
|
-
<span>${T(e)}</span>
|
|
115
|
-
</li>
|
|
116
|
-
`}function vt(t){t.innerHTML=`
|
|
117
|
-
<section class="local-pane" data-local-pane="session">
|
|
118
|
-
<header>
|
|
119
|
-
<h2>Session</h2>
|
|
120
|
-
<form class="local-filter" data-local-session-form>
|
|
121
|
-
<input type="text" name="team" placeholder="team" required />
|
|
122
|
-
<input type="text" name="alias" placeholder="alias" required />
|
|
123
|
-
<button type="submit">Load</button>
|
|
124
|
-
</form>
|
|
125
|
-
</header>
|
|
126
|
-
<div data-local-session-read>
|
|
127
|
-
<p class="muted">Enter a team and alias to load its session.</p>
|
|
128
|
-
</div>
|
|
129
|
-
<p class="muted local-write-gap" data-local-session-write-gap>
|
|
130
|
-
Answering from the web isn't available yet \u2014 no HTTP route for <code>lmctl chat</code>. The
|
|
131
|
-
transcript's <code>tail</code> view isn't wired here either: <code>/api/transcript</code>
|
|
132
|
-
re-parses text instead of passing through <code>lmctl tail --json</code> cleanly (a known
|
|
133
|
-
gap \u2014 see pane-command-contract.md) \u2014 this pane uses <code>/api/session</code> instead.
|
|
134
|
-
</p>
|
|
135
|
-
</section>
|
|
136
|
-
`}function Et(t,e,r,a){let s=e.modifiedAt===void 0?"unknown":T(e.modifiedAt);t.innerHTML=`
|
|
137
|
-
${wt(r,a)}
|
|
138
|
-
<p class="muted">
|
|
139
|
-
${T(e.provider)} \xB7 <code>${T(e.sessionId)}</code> \xB7 modified ${s}
|
|
140
|
-
</p>
|
|
141
|
-
${e.messages.length===0?'<p class="muted">No messages.</p>':`<ul class="local-session-messages" data-local-session-messages>${e.messages.map(St).join("")}</ul>`}
|
|
142
|
-
`}function Lt(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-session-error>${T(e)}</p>`}var $t={id:"session",label:"Session",mount(t,e){let r=new AbortController;vt(t);let a=t.querySelector("[data-local-session-read]");async function s(i,l){if(!a)return;a.innerHTML='<p class="muted" role="status">Loading session\u2026</p>';let n=await e.transport.read({kind:"session",team:i,alias:l},r.signal);if(!r.signal.aborted){if(n.status==="error"){Lt(a,n.error.message);return}n.status!=="pending"&&Et(a,n.data,n.ranCommand,n.ranAt)}}return t.querySelector("[data-local-session-form]")?.addEventListener("submit",i=>{i.preventDefault();let l=i.currentTarget,n=l.elements.namedItem("team")?.value.trim()??"",o=l.elements.namedItem("alias")?.value.trim()??"";!n||!o||s(n,o)}),()=>{r.abort()}}},M=$t;function v(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Rt(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${v(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${v(t)}</code>, ${r}s ago</p>`}function kt(t){return`
|
|
143
|
-
<tr data-local-source-row="${t.id}">
|
|
144
|
-
<td>${t.id}</td>
|
|
145
|
-
<td>${v(t.path)}</td>
|
|
146
|
-
<td>${t.label===null?"":v(t.label)}</td>
|
|
147
|
-
<td><button type="button" class="local-source-remove" data-local-source-remove="${v(t.path)}">Remove</button></td>
|
|
148
|
-
</tr>
|
|
149
|
-
`}function Mt(t){t.innerHTML=`
|
|
150
|
-
<section class="local-pane" data-local-pane="sources">
|
|
151
|
-
<header>
|
|
152
|
-
<h2>Sources</h2>
|
|
153
|
-
</header>
|
|
154
|
-
<div data-local-sources-read><p class="muted" role="status">Loading sources\u2026</p></div>
|
|
155
|
-
<form class="local-write-form" data-local-source-add>
|
|
156
|
-
<h3>Add source</h3>
|
|
157
|
-
<label>Path <input type="text" name="path" required /></label>
|
|
158
|
-
<label>Label (optional) <input type="text" name="label" /></label>
|
|
159
|
-
<button type="submit">Add</button>
|
|
160
|
-
<p class="form-error" data-local-source-add-error role="alert" hidden></p>
|
|
161
|
-
<p class="muted" data-local-source-add-status hidden></p>
|
|
162
|
-
</form>
|
|
163
|
-
</section>
|
|
164
|
-
`}function Ht(t,e,r,a,s){t.innerHTML=`
|
|
165
|
-
${Rt(a,s)}
|
|
166
|
-
<p class="muted">default: <code>${v(e)}</code></p>
|
|
167
|
-
${r.length===0?'<p class="muted">No additional sources.</p>':`
|
|
168
|
-
<table class="local-table" data-local-sources-table>
|
|
169
|
-
<thead><tr><th>ID</th><th>PATH</th><th>LABEL</th><th></th></tr></thead>
|
|
170
|
-
<tbody>${r.map(kt).join("")}</tbody>
|
|
171
|
-
</table>
|
|
172
|
-
`}
|
|
173
|
-
<p class="form-error" data-local-source-remove-error role="alert" hidden></p>
|
|
174
|
-
`}function At(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-sources-error>${v(e)}</p>`}var qt={id:"sources",label:"Sources",mount(t,e){let r=new AbortController;Mt(t);let a=t.querySelector("[data-local-sources-read]");async function s(){if(!a)return;let n=await e.transport.read({kind:"sources"},r.signal);if(!r.signal.aborted){if(n.status==="error"){At(a,n.error.message);return}n.status!=="pending"&&(Ht(a,n.data.defaultTeamsConfPath,n.data.sources,n.ranCommand,n.ranAt),a.querySelectorAll("[data-local-source-remove]").forEach(o=>{o.addEventListener("click",()=>{let d=o.dataset.localSourceRemove;d&&i(d)})}))}}async function i(n){let o=a?.querySelector("[data-local-source-remove-error]");o&&(o.hidden=!0);let d=await e.transport.write({kind:"source-remove",path:n},r.signal);if(!r.signal.aborted){if(!d.ok){o&&(o.textContent=d.summary,o.hidden=!1);return}s()}}return t.querySelector("[data-local-source-add]")?.addEventListener("submit",n=>{n.preventDefault();let o=n.currentTarget,d=o.querySelector("[data-local-source-add-error]"),u=o.querySelector("[data-local-source-add-status]");d&&(d.hidden=!0),u&&(u.hidden=!0);let c=o.elements.namedItem("path")?.value.trim()??"",h=o.elements.namedItem("label")?.value.trim()??"";c&&(async()=>{let m=await e.transport.write(h?{kind:"source-add",path:c,label:h}:{kind:"source-add",path:c},r.signal);if(!r.signal.aborted){if(!m.ok){d&&(d.textContent=m.summary,d.hidden=!1);return}u&&(u.textContent=`${m.summary} \u2014 ran: ${m.ranCommand}`,u.hidden=!1),o.reset(),s()}})()}),s(),()=>{r.abort()}}},H=qt;function g(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function xt(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${g(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${g(t)}</code>, ${r}s ago</p>`}function Pt(t){return`
|
|
175
|
-
<tr data-local-state-row="${g(t.team)}">
|
|
176
|
-
<td>${g(t.team)}</td>
|
|
177
|
-
<td>${t.state?g(t.state.status):""}</td>
|
|
178
|
-
<td>${t.state?g(t.state.changed_ts):""}</td>
|
|
179
|
-
<td>${t.progress?.progress_status?g(t.progress.progress_status):""}</td>
|
|
180
|
-
<td>${t.progress?.confirmed_done_ts?g(t.progress.confirmed_done_ts):""}</td>
|
|
181
|
-
</tr>
|
|
182
|
-
`}function It(t,e,r,a,s,i){t.innerHTML=`
|
|
183
|
-
<section class="local-pane" data-local-pane="state">
|
|
184
|
-
<header>
|
|
185
|
-
<h2>State</h2>
|
|
186
|
-
<button type="button" class="local-refresh" data-local-state-refresh>Refresh</button>
|
|
187
|
-
</header>
|
|
188
|
-
${xt(a,s)}
|
|
189
|
-
<p class="muted" data-local-state-active>active: ${r.length?g(r.join(", ")):"(none)"}</p>
|
|
190
|
-
${e.length===0?'<p class="muted">No teams registered.</p>':`
|
|
191
|
-
<table class="local-table" data-local-state-table>
|
|
192
|
-
<thead><tr><th>TEAM</th><th>STATUS</th><th>CHANGED</th><th>PROGRESS</th><th>CONFIRMED-DONE</th></tr></thead>
|
|
193
|
-
<tbody>${e.map(Pt).join("")}</tbody>
|
|
194
|
-
</table>
|
|
195
|
-
`}
|
|
196
|
-
<p class="muted local-write-gap" data-local-state-write-gap>
|
|
197
|
-
Setting state isn't available from the web yet \u2014 no HTTP route exists (CLI only: <code>lmauto state set <team> <status></code>).
|
|
198
|
-
</p>
|
|
199
|
-
</section>
|
|
200
|
-
`,t.querySelector("[data-local-state-refresh]")?.addEventListener("click",i)}function _t(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-state-error>${g(e)}</p>`}var Ct={id:"state",label:"State",mount(t,e){let r=new AbortController;t.innerHTML='<p class="muted" role="status">Loading state\u2026</p>';async function a(){let s=await e.transport.read({kind:"state-list"},r.signal);if(!r.signal.aborted){if(s.status==="error"){_t(t,s.error.message);return}s.status!=="pending"&&It(t,s.data.teams,s.data.active,s.ranCommand,s.ranAt,()=>void a())}}return a(),()=>{r.abort()}}},A=Ct;function D(t){return t.channel==="local"}function f(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function jt(t,e){if(!e)return`<p class="muted" data-local-ran>ran: <code>${f(t)}</code></p>`;let r=Math.max(0,Math.round((Date.now()-Date.parse(e))/1e3));return`<p class="muted" data-local-ran>ran: <code>${f(t)}</code>, ${r}s ago</p>`}function Dt(t){return D(t)?`
|
|
201
|
-
<tr data-local-team-row="${f(t.name)}">
|
|
202
|
-
<td>${f(t.name)}</td>
|
|
203
|
-
<td>local</td>
|
|
204
|
-
<td>${f(t.leadAlias)}</td>
|
|
205
|
-
<td>${f(t.description)}</td>
|
|
206
|
-
</tr>
|
|
207
|
-
`:`
|
|
208
|
-
<tr data-local-team-row="${f(t.name)}">
|
|
209
|
-
<td>${f(t.name)}</td>
|
|
210
|
-
<td>remote (${f(t.peerSite)})</td>
|
|
211
|
-
<td>--</td>
|
|
212
|
-
<td>--</td>
|
|
213
|
-
</tr>
|
|
214
|
-
`}function Ft(t,e,r,a,s){t.innerHTML=`
|
|
215
|
-
<section class="local-pane" data-local-pane="teams">
|
|
216
|
-
<header>
|
|
217
|
-
<h2>Teams</h2>
|
|
218
|
-
<button type="button" class="local-refresh" data-local-teams-refresh>Refresh</button>
|
|
219
|
-
</header>
|
|
220
|
-
${jt(r,a)}
|
|
221
|
-
${e.length===0?'<p class="muted">No teams registered.</p>':`
|
|
222
|
-
<table class="local-table" data-local-teams-table>
|
|
223
|
-
<thead><tr><th>TEAM</th><th>CHANNEL</th><th>LEAD</th><th>DESCRIPTION</th></tr></thead>
|
|
224
|
-
<tbody>${e.map(Dt).join("")}</tbody>
|
|
225
|
-
</table>
|
|
226
|
-
`}
|
|
227
|
-
<p class="muted local-write-gap" data-local-teams-write-gap>
|
|
228
|
-
Nudge isn't available from the web yet \u2014 no HTTP route exists (CLI only: <code>lmauto nudge <team></code>).
|
|
229
|
-
</p>
|
|
230
|
-
</section>
|
|
231
|
-
`,t.querySelector("[data-local-teams-refresh]")?.addEventListener("click",s)}function Nt(t,e){t.innerHTML=`<p class="form-error" role="alert" data-local-teams-error>${f(e)}</p>`}var Ot={id:"teams",label:"Teams",mount(t,e){let r=new AbortController;t.innerHTML='<p class="muted" role="status">Loading teams\u2026</p>';async function a(){let s=await e.transport.read({kind:"teams"},r.signal);if(!r.signal.aborted){if(s.status==="error"){Nt(t,s.error.message);return}s.status!=="pending"&&Ft(t,s.data.teams,s.ranCommand,s.ranAt,()=>void a())}}return a(),()=>{r.abort()}}},q=Ot;var Wt=document.getElementById("app")??document.createElement("main"),Ut=new L(""),Qt=[{id:q.id,label:q.label,pane:q},{id:R.id,label:R.label,pane:R},{id:k.id,label:k.label,pane:k},{id:A.id,label:A.label,pane:A},{id:H.id,label:H.label,pane:H},{id:M.id,label:M.label,pane:M},{id:$.id,label:$.label,pane:$},{id:E.id,label:E.label,pane:E}];j(Wt,Ut,Qt);})();
|