botmux 2.13.2 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +10 -4
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +65 -3
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +6 -1
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/scheduler.d.ts +1 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +1 -0
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +90 -52
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +18 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +22 -22
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +131 -80
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/operator-selector.d.ts +17 -0
- package/dist/dashboard/operator-selector.d.ts.map +1 -0
- package/dist/dashboard/operator-selector.js +37 -0
- package/dist/dashboard/operator-selector.js.map +1 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -1
- package/dist/dashboard/web/groups.js +61 -7
- package/dist/dashboard/web/groups.js.map +1 -1
- package/dist/dashboard-web/app.js +60 -53
- package/dist/dashboard-web/style.css +10 -1
- package/dist/dashboard.js +119 -34
- package/dist/dashboard.js.map +1 -1
- package/dist/im/lark/card-handler.js +8 -8
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +20 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +65 -8
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +39 -4
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +84 -49
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/services/groups-store.d.ts +22 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +39 -1
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/schedule-store.d.ts +1 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +1 -0
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface SelectorSession {
|
|
2
|
+
ownerOpenId?: string;
|
|
3
|
+
larkAppId?: string;
|
|
4
|
+
status?: string;
|
|
5
|
+
lastMessageAt?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface OperatorPick {
|
|
8
|
+
openId: string;
|
|
9
|
+
larkAppId: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Return the most-recent active session whose owner can be auto-invited
|
|
13
|
+
* (sessions whose larkAppId points at a daemon that's currently online).
|
|
14
|
+
* Closed sessions and sessions without ownerOpenId/larkAppId are skipped.
|
|
15
|
+
*/
|
|
16
|
+
export declare function pickOperatorForCreate(sessions: Iterable<SelectorSession>, isOnline: (larkAppId: string) => boolean): OperatorPick | null;
|
|
17
|
+
//# sourceMappingURL=operator-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operator-selector.d.ts","sourceRoot":"","sources":["../../src/dashboard/operator-selector.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,EACnC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GACvC,YAAY,GAAG,IAAI,CAYrB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/dashboard/operator-selector.ts
|
|
2
|
+
//
|
|
3
|
+
// Helper for the "Create new group" flow: when the dashboard auto-invites the
|
|
4
|
+
// operator into the freshly-created chat, the operator's `open_id` and the
|
|
5
|
+
// creator bot must come from the SAME Lark app — Feishu open_ids are app-
|
|
6
|
+
// scoped, so using bot A as creator with bot B's open_id of the user lands the
|
|
7
|
+
// user in `invalid_user_id_list` and the chat ends up with only bots in it.
|
|
8
|
+
//
|
|
9
|
+
// This module isolates the selection logic so it's unit-testable independent
|
|
10
|
+
// of the rest of dashboard.ts (which is a pm2 entry script, awkward to test).
|
|
11
|
+
//
|
|
12
|
+
// Selection rule: from the aggregator's session cache, pick the most-recent
|
|
13
|
+
// non-closed session whose `larkAppId` corresponds to a currently-online
|
|
14
|
+
// daemon. Both the operator's `open_id` and the creator daemon are derived
|
|
15
|
+
// from that single session so the scope matches.
|
|
16
|
+
/**
|
|
17
|
+
* Return the most-recent active session whose owner can be auto-invited
|
|
18
|
+
* (sessions whose larkAppId points at a daemon that's currently online).
|
|
19
|
+
* Closed sessions and sessions without ownerOpenId/larkAppId are skipped.
|
|
20
|
+
*/
|
|
21
|
+
export function pickOperatorForCreate(sessions, isOnline) {
|
|
22
|
+
let best = null;
|
|
23
|
+
for (const s of sessions) {
|
|
24
|
+
if (s.status === 'closed')
|
|
25
|
+
continue;
|
|
26
|
+
if (!s.ownerOpenId || !s.larkAppId)
|
|
27
|
+
continue;
|
|
28
|
+
if (!isOnline(s.larkAppId))
|
|
29
|
+
continue;
|
|
30
|
+
const ts = s.lastMessageAt ?? 0;
|
|
31
|
+
if (!best || ts > best.lastMessageAt) {
|
|
32
|
+
best = { openId: s.ownerOpenId, larkAppId: s.larkAppId, lastMessageAt: ts };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return best ? { openId: best.openId, larkAppId: best.larkAppId } : null;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=operator-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operator-selector.js","sourceRoot":"","sources":["../../src/dashboard/operator-selector.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAC1E,+EAA+E;AAC/E,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,iDAAiD;AAcjD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAmC,EACnC,QAAwC;IAExC,IAAI,IAAI,GAAwE,IAAI,CAAC;IACrF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QACpC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,SAAS;YAAE,SAAS;QAC7C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAAE,SAAS;QACrC,MAAM,EAAE,GAAG,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YACrC,IAAI,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;QAC9E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"AA8BA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"AA8BA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,iBAyPvD"}
|
|
@@ -97,24 +97,78 @@ export async function renderGroupsPage(root) {
|
|
|
97
97
|
});
|
|
98
98
|
const respBody = await r.json();
|
|
99
99
|
if (respBody.ok && respBody.chatId) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
rerender();
|
|
100
|
+
renderCreateSuccess(respBody);
|
|
101
|
+
// Refresh matrix in the background so the new chat eventually shows
|
|
102
|
+
// up — won't block the success drawer.
|
|
103
|
+
void loadGroups().then(rerender).catch(() => { });
|
|
105
104
|
}
|
|
106
105
|
else {
|
|
107
106
|
alert(`Failed: ${respBody.error ?? r.status}`);
|
|
107
|
+
drawer.close();
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
catch (e) {
|
|
111
111
|
alert('Network error: ' + e);
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
112
|
drawer.close();
|
|
115
113
|
}
|
|
116
114
|
};
|
|
117
115
|
}
|
|
116
|
+
function renderCreateSuccess(resp) {
|
|
117
|
+
const chatId = String(resp.chatId);
|
|
118
|
+
const appLink = `https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(chatId)}`;
|
|
119
|
+
const invalidBots = (resp.invalidBotIds ?? []);
|
|
120
|
+
const invalidUsers = (resp.invalidUserIds ?? []);
|
|
121
|
+
const auto = resp.autoInvitedOpenId;
|
|
122
|
+
const rejected = !!resp.autoInviteRejected;
|
|
123
|
+
const ownerTo = resp.ownerTransferredTo;
|
|
124
|
+
const transferErr = resp.transferError;
|
|
125
|
+
const notifyMsgId = resp.notifyMessageId;
|
|
126
|
+
const notifyErr = resp.notifyError;
|
|
127
|
+
let inviteNote;
|
|
128
|
+
if (auto) {
|
|
129
|
+
const transferLine = ownerTo
|
|
130
|
+
? `<br><small>群主已从机器人转让给你。</small>`
|
|
131
|
+
: transferErr
|
|
132
|
+
? `<br><small class="hint-warn-inline">⚠ 自动转让群主失败(${escapeHtml(transferErr)}),你现在是成员但群主仍是机器人。</small>`
|
|
133
|
+
: '';
|
|
134
|
+
const notifyLine = notifyMsgId
|
|
135
|
+
? `<br><small>机器人已在群里 @ 了你(消息 id <code>${escapeHtml(notifyMsgId)}</code>),看飞书通知就能进群。</small>`
|
|
136
|
+
: notifyErr
|
|
137
|
+
? `<br><small class="hint-warn-inline">⚠ 自动 @ 通知失败(${escapeHtml(notifyErr)}),新群可能不会主动出现在你侧边栏,建议从下面按钮跳进去。</small>`
|
|
138
|
+
: '';
|
|
139
|
+
inviteNote = `<p class="hint-ok">已自动邀请你(<code>${escapeHtml(auto)}</code>)作为成员。${transferLine}${notifyLine}</p>`;
|
|
140
|
+
}
|
|
141
|
+
else if (rejected) {
|
|
142
|
+
inviteNote = `<p class="hint-warn">飞书拒绝了自动邀请(你的 open_id 在创建者 bot 的 scope 下不可用)。<strong>你目前不是新群成员</strong>,需要让群里的某个机器人手动把你加进来。</p>`;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
inviteNote = `<p class="hint-warn">没在 dashboard 缓存里找到 ownerOpenId,<strong>没有自动邀请你</strong>。点开下面链接前,先让群里任一机器人手动把你加进去。</p>`;
|
|
146
|
+
}
|
|
147
|
+
const invalidNote = [
|
|
148
|
+
invalidBots.length ? `<li>无效 bot id: <code>${invalidBots.map(escapeHtml).join(', ')}</code></li>` : '',
|
|
149
|
+
invalidUsers.length ? `<li>无效用户 open_id: <code>${invalidUsers.map(escapeHtml).join(', ')}</code></li>` : '',
|
|
150
|
+
].filter(Boolean).join('');
|
|
151
|
+
drawer.innerHTML = `
|
|
152
|
+
<article>
|
|
153
|
+
<header><h3>群创建成功</h3></header>
|
|
154
|
+
<p><b>chatId:</b> <code>${escapeHtml(chatId)}</code> <button type="button" data-copy="${escapeHtml(chatId)}">copy</button></p>
|
|
155
|
+
<p><b>创建者:</b> <code>${escapeHtml(resp.creator ?? '?')}</code></p>
|
|
156
|
+
${inviteNote}
|
|
157
|
+
${invalidNote ? `<ul>${invalidNote}</ul>` : ''}
|
|
158
|
+
<div class="actions">
|
|
159
|
+
<a class="btn-link primary" href="${appLink}" target="_blank" rel="noopener">↗ 打开新群</a>
|
|
160
|
+
<button type="button" id="g-create-close">关闭</button>
|
|
161
|
+
</div>
|
|
162
|
+
</article>`;
|
|
163
|
+
drawer.querySelectorAll('[data-copy]').forEach(b => {
|
|
164
|
+
b.onclick = () => {
|
|
165
|
+
navigator.clipboard.writeText(b.dataset.copy ?? '');
|
|
166
|
+
b.textContent = 'copied';
|
|
167
|
+
setTimeout(() => { b.textContent = 'copy'; }, 800);
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
drawer.querySelector('#g-create-close').onclick = () => drawer.close();
|
|
171
|
+
}
|
|
118
172
|
function renderHead() {
|
|
119
173
|
head.innerHTML = `<tr>
|
|
120
174
|
<th>chat</th>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups.js","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"groups.js","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
1
|
+
"use strict";(()=>{var A=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let r of t)this.sessions.set(r.sessionId,r);this.emit()}upsertSchedules(t){for(let r of t)this.schedules.set(r.id,r);this.emit()}applySse(t,r){if(t==="session.spawned")this.sessions.set(r.session.sessionId,r.session);else if(t==="session.update"){let u=this.sessions.get(r.sessionId);u&&this.sessions.set(r.sessionId,{...u,...r.patch})}else if(t==="session.exited"){let u=this.sessions.get(r.sessionId);u&&this.sessions.set(r.sessionId,{...u,status:"closed"})}else if(t==="schedule.created")this.schedules.set(r.schedule.id,r.schedule);else if(t==="schedule.updated"){let u=this.schedules.get(r.id);u&&this.schedules.set(r.id,{...u,...r.patch})}else if(t==="schedule.deleted")this.schedules.delete(r.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},b=new A;async function N(){let[o,t]=await Promise.all([fetch("/api/sessions").then(g=>g.json()),fetch("/api/schedules").then(g=>g.json())]);b.upsertSessions(o.sessions??[]),b.upsertSchedules(t.schedules??[]);let r=new EventSource("/events"),u=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let g of u)r.addEventListener(g,n=>{try{let f=JSON.parse(n.data);b.applySse(g,f.body??f)}catch{}});r.onerror=()=>b.setOnline(!1),r.onopen=()=>b.setOnline(!0)}var G=`
|
|
2
2
|
<form id="filters" class="filters">
|
|
3
3
|
<input type="search" name="q" placeholder="search workingDir / title / ids" />
|
|
4
4
|
<select name="cli" multiple size="4">
|
|
@@ -30,34 +30,34 @@
|
|
|
30
30
|
<tbody></tbody>
|
|
31
31
|
</table>
|
|
32
32
|
<dialog id="drawer"></dialog>
|
|
33
|
-
`;function
|
|
34
|
-
<td>${
|
|
35
|
-
<td><span class="badge cli-${
|
|
36
|
-
<td><span class="status status-${
|
|
37
|
-
<td>${
|
|
38
|
-
<td title="${
|
|
39
|
-
<td>${
|
|
40
|
-
<td>${
|
|
41
|
-
<td>${e.adopt?
|
|
33
|
+
`;function R(o){if(!o)return"-";let t=Date.now()-o;return t<6e4?"now":t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}function h(o){return o.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}var J="\u{1FA9E}",M="\u{1F4CD}",z="\u{1F5A5}\uFE0F";function D(o){o.innerHTML=G;let t=o.querySelector("#sessions-table tbody"),r=o.querySelector("#filters"),u=o.querySelector("#drawer");function g(e){return`<tr data-id="${h(e.sessionId)}">
|
|
34
|
+
<td>${h(e.botName??"")}</td>
|
|
35
|
+
<td><span class="badge cli-${h(e.cliId??"unknown")}">${h(e.cliId??"unknown")}</span></td>
|
|
36
|
+
<td><span class="status status-${h(e.status)}">${h(e.status)}</span></td>
|
|
37
|
+
<td>${h((e.title??"").slice(0,40))}</td>
|
|
38
|
+
<td title="${h(e.workingDir??"")}">${h((e.workingDir??"").slice(-30))}</td>
|
|
39
|
+
<td>${R(e.spawnedAt)}</td>
|
|
40
|
+
<td>${R(e.lastMessageAt)}</td>
|
|
41
|
+
<td>${e.adopt?J:""}</td>
|
|
42
42
|
<td><button class="open">\u22EF</button></td>
|
|
43
|
-
</tr>`}function
|
|
43
|
+
</tr>`}function n(){let e=new FormData(r),p=(e.get("q")??"").toLowerCase(),i=e.getAll("cli"),s=e.get("status"),a=e.get("adopt"),$=!!e.get("active");return[...b.sessions.values()].filter(l=>!i.length||i.includes(l.cliId??"unknown")).filter(l=>!s||l.status===s).filter(l=>!a||a==="yes"==!!l.adopt).filter(l=>!$||l.status!=="closed").filter(l=>!p||JSON.stringify(l).toLowerCase().includes(p)).sort((l,c)=>(c.lastMessageAt??0)-(l.lastMessageAt??0))}function f(){t.innerHTML=n().map(g).join("")}function w(e){let p=e.status==="closed";u.innerHTML=`
|
|
44
44
|
<article>
|
|
45
45
|
<header>
|
|
46
|
-
<h3>${
|
|
47
|
-
<code>${
|
|
46
|
+
<h3>${h(e.title??e.sessionId)}</h3>
|
|
47
|
+
<code>${h(e.sessionId)}</code> <button data-copy="${h(e.sessionId)}">copy</button>
|
|
48
48
|
</header>
|
|
49
|
-
<p><b>bot:</b> ${
|
|
50
|
-
<p><b>chatId:</b> <code>${
|
|
51
|
-
<p><b>rootMessageId:</b> <code>${
|
|
52
|
-
${e.threadId?`<p><b>threadId:</b> <code>${
|
|
53
|
-
<p><b>workingDir:</b> ${
|
|
49
|
+
<p><b>bot:</b> ${h(e.botName??"-")} \xB7 <b>cli:</b> ${h(e.cliId??"?")} \xB7 <b>status:</b> ${h(e.status)}</p>
|
|
50
|
+
<p><b>chatId:</b> <code>${h(e.chatId)}</code> <button data-copy="${h(e.chatId)}">copy</button></p>
|
|
51
|
+
<p><b>rootMessageId:</b> <code>${h(e.rootMessageId??"")}</code> <button data-copy="${h(e.rootMessageId??"")}">copy</button></p>
|
|
52
|
+
${e.threadId?`<p><b>threadId:</b> <code>${h(e.threadId)}</code></p>`:""}
|
|
53
|
+
<p><b>workingDir:</b> ${h(e.workingDir??"-")}</p>
|
|
54
54
|
<div class="actions">
|
|
55
|
-
<button id="locate-btn" type="button">${
|
|
56
|
-
${e.webPort?`<a class="btn-link" href="http://${
|
|
57
|
-
${
|
|
55
|
+
<button id="locate-btn" type="button">${M} \u5B9A\u4F4D\u5230\u98DE\u4E66\u8BDD\u9898</button>
|
|
56
|
+
${e.webPort?`<a class="btn-link" href="http://${h(location.hostname)}:${e.webPort}" target="_blank">${z} \u6253\u5F00 xterm</a>`:""}
|
|
57
|
+
${p?"":'<button id="close-btn" type="button" class="contrast">\u5173\u95ED\u4F1A\u8BDD</button>'}
|
|
58
58
|
</div>
|
|
59
59
|
<form method="dialog"><button>\u5173\u95ED</button></form>
|
|
60
|
-
</article>`,u.querySelectorAll("[data-copy]").forEach(
|
|
60
|
+
</article>`,u.querySelectorAll("[data-copy]").forEach(a=>{a.onclick=()=>{navigator.clipboard.writeText(a.dataset.copy??""),a.textContent="copied",setTimeout(()=>{a.textContent="copy"},800)}});let i=u.querySelector("#locate-btn");i&&(i.onclick=async()=>{i.disabled=!0,i.textContent=`${M} \u53D1\u9001\u4E2D...`;try{let a=await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/locate`,{method:"POST"}),$=await a.json();if($.ok){let l=30;i.textContent=`${M} (\u51B7\u5374 ${l}s)`;let c=setInterval(()=>{l-=1,l<=0?(clearInterval(c),i.disabled=!1,i.textContent=`${M} \u5B9A\u4F4D\u5230\u98DE\u4E66\u8BDD\u9898`):i.textContent=`${M} (\u51B7\u5374 ${l}s)`},1e3)}else{let l=$.error??a.status;alert("Locate failed: "+l),i.disabled=!1,i.textContent=`${M} \u5B9A\u4F4D\u5230\u98DE\u4E66\u8BDD\u9898`}}catch(a){alert("Locate error: "+a),i.disabled=!1,i.textContent=`${M} \u5B9A\u4F4D\u5230\u98DE\u4E66\u8BDD\u9898`}});let s=u.querySelector("#close-btn");s&&(s.onclick=async()=>{if(confirm("\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD?")){s.disabled=!0;try{await fetch(`/api/sessions/${encodeURIComponent(e.sessionId)}/close`,{method:"POST"})}finally{u.close()}}}),u.showModal()}t.addEventListener("click",e=>{let p=e.target.closest("tr[data-id]");if(!p)return;let i=p.dataset.id,s=b.sessions.get(i);s&&w(s)}),r.addEventListener("input",f),b.on(f),f()}var W=`
|
|
61
61
|
<form id="sched-filters" class="filters">
|
|
62
62
|
<input type="search" name="q" placeholder="search name / prompt / workingDir" />
|
|
63
63
|
<select name="kind">
|
|
@@ -75,19 +75,19 @@
|
|
|
75
75
|
</tr></thead>
|
|
76
76
|
<tbody id="schedules-tbody"></tbody>
|
|
77
77
|
</table>
|
|
78
|
-
`;function
|
|
79
|
-
<td>${
|
|
80
|
-
<td>${
|
|
81
|
-
<td><code>${
|
|
82
|
-
<td>${
|
|
83
|
-
<td>${
|
|
84
|
-
<td>${
|
|
85
|
-
<td>${
|
|
78
|
+
`;function C(o){return o.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function O(o){if(!o)return"\u2014";try{return new Date(o).toLocaleString()}catch{return o}}function P(o){o.innerHTML=W;let t=o.querySelector("#schedules-tbody"),r=o.querySelector("#sched-filters");function u(){let n=new FormData(r),f=(n.get("q")??"").toLowerCase(),w=n.get("kind"),e=!!n.get("enabled");return[...b.schedules.values()].filter(p=>!w||p.parsed?.kind===w).filter(p=>!e||p.enabled).filter(p=>!f||JSON.stringify(p).toLowerCase().includes(f)).sort((p,i)=>{if(p.enabled!==i.enabled)return p.enabled?-1:1;let s=p.nextRunAt?Date.parse(p.nextRunAt):1/0,a=i.nextRunAt?Date.parse(i.nextRunAt):1/0;return s-a})}function g(){t.innerHTML=u().map(n=>`<tr data-id="${C(n.id)}">
|
|
79
|
+
<td>${C(n.name??n.id)}</td>
|
|
80
|
+
<td>${C(n.botName??n.larkAppId??"-")}</td>
|
|
81
|
+
<td><code>${C(n.parsed?.display??"?")}</code></td>
|
|
82
|
+
<td>${O(n.nextRunAt)}</td>
|
|
83
|
+
<td>${O(n.lastRunAt)} ${n.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
|
|
84
|
+
<td>${n.repeat?`${n.repeat.completed}/${n.repeat.times??"\u221E"}`:"\u2014"}</td>
|
|
85
|
+
<td>${n.enabled?"\u2713":"\u2717"}</td>
|
|
86
86
|
<td class="actions-cell">
|
|
87
87
|
<button data-op="run" type="button">Run now</button>
|
|
88
|
-
${
|
|
88
|
+
${n.enabled?'<button data-op="pause" type="button">Pause</button>':'<button data-op="resume" type="button">Resume</button>'}
|
|
89
89
|
</td>
|
|
90
|
-
</tr>`).join("")||'<tr><td colspan="8" class="empty">No schedules.</td></tr>'}t.addEventListener("click",async
|
|
90
|
+
</tr>`).join("")||'<tr><td colspan="8" class="empty">No schedules.</td></tr>'}t.addEventListener("click",async n=>{let f=n.target.closest("button[data-op]");if(!f)return;let w=f.closest("tr[data-id]");if(!w)return;let e=w.dataset.id,p=f.dataset.op;f.disabled=!0;let i=f.textContent;f.textContent="...";try{let s=await fetch(`/api/schedules/${encodeURIComponent(e)}/${p}`,{method:"POST"}),a=await s.json().catch(()=>({}));(!s.ok||a.ok===!1)&&alert(`Failed: ${s.status} ${a?.error??""}`.trim())}catch(s){alert("Network error: "+s)}finally{f.disabled=!1,f.textContent=i}}),r.addEventListener("input",g),b.on(g),g()}var T={chats:[],bots:[]},K=`
|
|
91
91
|
<form id="g-filters" class="filters">
|
|
92
92
|
<input type="search" name="q" placeholder="search chat name / id / owner" />
|
|
93
93
|
<label><input type="checkbox" name="missing"> missing-bot only</label>
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
<tbody id="g-body"></tbody>
|
|
100
100
|
</table>
|
|
101
101
|
<dialog id="g-drawer"></dialog>
|
|
102
|
-
`;function
|
|
102
|
+
`;function m(o){return o.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}async function x(){T=await(await fetch("/api/groups")).json()}async function j(o){o.innerHTML=K;let t=o.querySelector("#g-head"),r=o.querySelector("#g-body"),u=o.querySelector("#g-filters"),g=o.querySelector("#g-refresh"),n=o.querySelector("#g-drawer");g.onclick=async()=>{g.disabled=!0;try{await x(),i()}finally{g.disabled=!1}};let f=o.querySelector("#g-create");f.onclick=()=>w(),await x();function w(){let s=T.bots;if(s.length===0){alert("No bots online. Restart the daemon first.");return}n.innerHTML=`
|
|
103
103
|
<article>
|
|
104
104
|
<header><h3>Create new group</h3></header>
|
|
105
105
|
<p>Pick bots to invite. The dashboard auto-selects an online daemon as the chat creator/owner; the rest are added as members in the same call.</p>
|
|
@@ -110,10 +110,10 @@
|
|
|
110
110
|
</label>
|
|
111
111
|
<fieldset>
|
|
112
112
|
<legend>Bots</legend>
|
|
113
|
-
${
|
|
113
|
+
${s.map(a=>`
|
|
114
114
|
<label class="checkbox-row">
|
|
115
|
-
<input type="checkbox" name="bot" value="${
|
|
116
|
-
${
|
|
115
|
+
<input type="checkbox" name="bot" value="${m(a.larkAppId)}">
|
|
116
|
+
${m(a.botName??a.larkAppId)} <small>(${m(a.larkAppId)})</small>
|
|
117
117
|
</label>
|
|
118
118
|
`).join("")}
|
|
119
119
|
</fieldset>
|
|
@@ -122,30 +122,37 @@
|
|
|
122
122
|
<button type="button" id="g-create-cancel">Cancel</button>
|
|
123
123
|
</div>
|
|
124
124
|
</form>
|
|
125
|
-
</article>`,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
chatId
|
|
129
|
-
|
|
125
|
+
</article>`,n.showModal(),n.querySelector("#g-create-cancel").onclick=()=>n.close(),n.querySelector("#g-createform").onsubmit=async a=>{a.preventDefault();let $=new FormData(a.target),l=($.get("name")??"").trim(),c=$.getAll("bot");if(c.length===0){alert("Pick at least one bot.");return}let y=a.target.querySelector("button[type=submit]");y&&(y.disabled=!0,y.textContent="Creating...");try{let d=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:l||void 0,larkAppIds:c})}),I=await d.json();I.ok&&I.chatId?(e(I),x().then(i).catch(()=>{})):(alert(`Failed: ${I.error??d.status}`),n.close())}catch(d){alert("Network error: "+d),n.close()}}}function e(s){let a=String(s.chatId),$=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(a)}`,l=s.invalidBotIds??[],c=s.invalidUserIds??[],y=s.autoInvitedOpenId,d=!!s.autoInviteRejected,I=s.ownerTransferredTo,v=s.transferError,L=s.notifyMessageId,k=s.notifyError,E;if(y){let H=I?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":v?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${m(v)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",U=L?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${m(L)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:k?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${m(k)}\uFF09\uFF0C\u65B0\u7FA4\u53EF\u80FD\u4E0D\u4F1A\u4E3B\u52A8\u51FA\u73B0\u5728\u4F60\u4FA7\u8FB9\u680F\uFF0C\u5EFA\u8BAE\u4ECE\u4E0B\u9762\u6309\u94AE\u8DF3\u8FDB\u53BB\u3002</small>`:"";E=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${m(y)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${H}${U}</p>`}else d?E='<p class="hint-warn">\u98DE\u4E66\u62D2\u7EDD\u4E86\u81EA\u52A8\u9080\u8BF7\uFF08\u4F60\u7684 open_id \u5728\u521B\u5EFA\u8005 bot \u7684 scope \u4E0B\u4E0D\u53EF\u7528\uFF09\u3002<strong>\u4F60\u76EE\u524D\u4E0D\u662F\u65B0\u7FA4\u6210\u5458</strong>\uFF0C\u9700\u8981\u8BA9\u7FA4\u91CC\u7684\u67D0\u4E2A\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u6765\u3002</p>':E='<p class="hint-warn">\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C<strong>\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60</strong>\u3002\u70B9\u5F00\u4E0B\u9762\u94FE\u63A5\u524D\uFF0C\u5148\u8BA9\u7FA4\u91CC\u4EFB\u4E00\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u53BB\u3002</p>';let S=[l.length?`<li>\u65E0\u6548 bot id: <code>${l.map(m).join(", ")}</code></li>`:"",c.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${c.map(m).join(", ")}</code></li>`:""].filter(Boolean).join("");n.innerHTML=`
|
|
126
|
+
<article>
|
|
127
|
+
<header><h3>\u7FA4\u521B\u5EFA\u6210\u529F</h3></header>
|
|
128
|
+
<p><b>chatId:</b> <code>${m(a)}</code> <button type="button" data-copy="${m(a)}">copy</button></p>
|
|
129
|
+
<p><b>\u521B\u5EFA\u8005:</b> <code>${m(s.creator??"?")}</code></p>
|
|
130
|
+
${E}
|
|
131
|
+
${S?`<ul>${S}</ul>`:""}
|
|
132
|
+
<div class="actions">
|
|
133
|
+
<a class="btn-link primary" href="${$}" target="_blank" rel="noopener">\u2197 \u6253\u5F00\u65B0\u7FA4</a>
|
|
134
|
+
<button type="button" id="g-create-close">\u5173\u95ED</button>
|
|
135
|
+
</div>
|
|
136
|
+
</article>`,n.querySelectorAll("[data-copy]").forEach(H=>{H.onclick=()=>{navigator.clipboard.writeText(H.dataset.copy??""),H.textContent="copied",setTimeout(()=>{H.textContent="copy"},800)}}),n.querySelector("#g-create-close").onclick=()=>n.close()}function p(){t.innerHTML=`<tr>
|
|
130
137
|
<th>chat</th>
|
|
131
|
-
${
|
|
138
|
+
${T.bots.map(s=>`<th>${m(s.botName??s.larkAppId)}</th>`).join("")}
|
|
132
139
|
<th>actions</th>
|
|
133
|
-
</tr>`}function
|
|
140
|
+
</tr>`}function i(){p();let s=new FormData(u),a=(s.get("q")??"").toLowerCase(),$=!!s.get("missing"),l=T.chats.filter(c=>!a||(c.name??"").toLowerCase().includes(a)||c.chatId.toLowerCase().includes(a)||(c.ownerId??"").toLowerCase().includes(a)).filter(c=>!$||c.memberBots.some(y=>!y.inChat));if(l.length===0){r.innerHTML=`<tr><td colspan="${T.bots.length+2}" class="empty">No chats match the filter.</td></tr>`;return}r.innerHTML=l.map(c=>`<tr data-chat="${m(c.chatId)}">
|
|
134
141
|
<td>
|
|
135
|
-
<strong>${
|
|
136
|
-
<small><code>${
|
|
142
|
+
<strong>${m(c.name??c.chatId)}</strong><br>
|
|
143
|
+
<small><code>${m(c.chatId)}</code></small>
|
|
137
144
|
</td>
|
|
138
|
-
${
|
|
145
|
+
${T.bots.map(y=>{let d=c.memberBots.find(L=>L.larkAppId===y.larkAppId),I=d?d.error?"!":d.inChat?"\u2713":"\u2717":"?";return`<td class="${d?d.error?"cell-error":d.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${m(d?.error??"")}">${I}</td>`}).join("")}
|
|
139
146
|
<td><button class="add-bots" type="button">Add bots</button></td>
|
|
140
|
-
</tr>`).join("")}
|
|
147
|
+
</tr>`).join("")}i(),r.addEventListener("click",async s=>{let a=s.target.closest("button.add-bots");if(!a)return;let l=a.closest("tr[data-chat]").dataset.chat,c=T.chats.find(d=>d.chatId===l);if(!c)return;let y=c.memberBots.filter(d=>!d.inChat);if(!y.length){alert("All configured bots are already in this chat.");return}n.innerHTML=`
|
|
141
148
|
<article>
|
|
142
|
-
<header><h3>Add bots to ${
|
|
149
|
+
<header><h3>Add bots to ${m(c.name??c.chatId)}</h3></header>
|
|
143
150
|
<p>Select bots to add. The dashboard will pick a bot that's already in the chat as the proxy.</p>
|
|
144
151
|
<form id="g-addform">
|
|
145
|
-
${
|
|
152
|
+
${y.map(d=>`
|
|
146
153
|
<label class="checkbox-row">
|
|
147
|
-
<input type="checkbox" name="bot" value="${
|
|
148
|
-
${
|
|
154
|
+
<input type="checkbox" name="bot" value="${m(d.larkAppId)}">
|
|
155
|
+
${m(d.botName??d.larkAppId)} <small>(${m(d.larkAppId)})</small>
|
|
149
156
|
</label>
|
|
150
157
|
`).join("")}
|
|
151
158
|
<div class="actions">
|
|
@@ -153,5 +160,5 @@ creator: ${$.creator??"?"}${k}`),await H(),l()}else alert(`Failed: ${$.error??c.
|
|
|
153
160
|
<button type="button" id="g-cancel">Cancel</button>
|
|
154
161
|
</div>
|
|
155
162
|
</form>
|
|
156
|
-
</article>`,
|
|
157
|
-
`);alert(
|
|
163
|
+
</article>`,n.showModal(),n.querySelector("#g-cancel").onclick=()=>n.close(),n.querySelector("#g-addform").onsubmit=async d=>{d.preventDefault();let v=new FormData(d.target).getAll("bot");if(v.length===0){alert("Pick at least one bot.");return}try{let k=await(await fetch(`/api/groups/${encodeURIComponent(l)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:v})})).json();if(k.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(k.result){let E=k.result.map(S=>`${S.id}: ${S.ok?"OK":`failed (${S.error??"unknown"})`}`).join(`
|
|
164
|
+
`);alert(E),await x(),i()}else alert(`Unexpected response: ${JSON.stringify(k)}`)}catch(L){alert("Network error: "+L)}finally{n.close()}}}),u.addEventListener("input",i)}var q=document.getElementById("root");function _(){let o=location.hash||"#/";o.startsWith("#/groups")?j(q):o.startsWith("#/schedules")?P(q):D(q);for(let t of document.querySelectorAll("header nav a"))t.classList.toggle("active",t.getAttribute("href")===(o||"#/")||o==="#/"&&t.dataset.route==="sessions")}var B=document.getElementById("status");function F(){B&&(B.textContent=b.online?"\u25CF live":"\u25CF disconnected",B.className="status "+(b.online?"online":"offline"))}b.on(F);F();(async()=>{try{await N()}catch(o){console.error("botmux dashboard bootstrap failed",o),b.setOnline(!1)}window.addEventListener("hashchange",_),_()})();})();
|
|
@@ -21,7 +21,12 @@ table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nu
|
|
|
21
21
|
th, td { padding: 0.4rem 0.6rem; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
|
|
22
22
|
th { background: var(--header-bg); font-weight: 600; }
|
|
23
23
|
tr[data-id]:hover { background: #f6f8fa; cursor: pointer; }
|
|
24
|
-
button { padding: 0.25rem 0.6rem; border: 1px solid var(--border); background: white; border-radius: 4px; cursor: pointer;
|
|
24
|
+
button { padding: 0.25rem 0.6rem; border: 1px solid var(--border); background: white; border-radius: 4px; cursor: pointer;
|
|
25
|
+
/* iOS Safari renders default <button> with -webkit-appearance: button which
|
|
26
|
+
can hide our text under a system gradient on small screens. Reset to
|
|
27
|
+
normalize so "Create" / "Cancel" / etc. render reliably across mobile. */
|
|
28
|
+
color: var(--fg); font-family: inherit; font-size: inherit; line-height: 1.2;
|
|
29
|
+
-webkit-appearance: none; appearance: none; }
|
|
25
30
|
button:disabled { opacity: 0.5; cursor: default; }
|
|
26
31
|
button.contrast { background: #cf222e; color: white; border-color: #cf222e; }
|
|
27
32
|
.btn-link { display: inline-block; padding: 0.25rem 0.6rem; border: 1px solid var(--border); border-radius: 4px; text-decoration: none; color: var(--fg); }
|
|
@@ -55,3 +60,7 @@ dialog#g-drawer { border: none; border-radius: 8px; padding: 0; max-width: 600px
|
|
|
55
60
|
dialog#g-drawer article { padding: 1rem 1.5rem; }
|
|
56
61
|
dialog#g-drawer fieldset { margin: 0.5rem 0; padding: 0.4rem 0.8rem; border: 1px solid var(--border); border-radius: 4px; }
|
|
57
62
|
dialog#g-drawer fieldset legend { padding: 0 0.3rem; color: var(--muted); font-size: 12px; }
|
|
63
|
+
.hint-ok { background: #ddf4e4; border-left: 3px solid #1a7f37; padding: 0.4rem 0.6rem; border-radius: 3px; margin: 0.5rem 0; font-size: 13px; }
|
|
64
|
+
.hint-warn { background: #fff8c5; border-left: 3px solid #bf8700; padding: 0.4rem 0.6rem; border-radius: 3px; margin: 0.5rem 0; font-size: 13px; }
|
|
65
|
+
.btn-link.primary { background: #1f6feb; color: white; border-color: #1f6feb; font-weight: 600; }
|
|
66
|
+
.hint-warn-inline { color: #9a6700; }
|