codex-weixin 0.2.2 → 0.2.3
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/CHANGELOG.md +13 -0
- package/README.en.md +11 -5
- package/README.md +13 -7
- package/dist/bridge/service.d.ts +6 -0
- package/dist/bridge/service.js +155 -11
- package/dist/bridge/service.js.map +1 -1
- package/dist/codex/exec-runner.d.ts +2 -0
- package/dist/codex/exec-runner.js +7 -1
- package/dist/codex/exec-runner.js.map +1 -1
- package/dist/server/account-manager.d.ts +3 -1
- package/dist/server/account-manager.js +15 -2
- package/dist/server/account-manager.js.map +1 -1
- package/dist/server/http-server.d.ts +1 -0
- package/dist/server/http-server.js +27 -2
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/login-manager.js +4 -4
- package/dist/server/login-manager.js.map +1 -1
- package/dist/state/runtime-state.d.ts +9 -0
- package/dist/state/runtime-state.js +48 -0
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/web/app.js +140 -2
- package/dist/web/index.html +12 -0
- package/dist/web/styles.css +30 -1
- package/dist/weixin/accounts.d.ts +6 -0
- package/dist/weixin/accounts.js +23 -0
- package/dist/weixin/accounts.js.map +1 -1
- package/dist/weixin/login.js +3 -3
- package/dist/weixin/login.js.map +1 -1
- package/package.json +1 -1
package/dist/web/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const state = {
|
|
2
|
+
version: "",
|
|
2
3
|
requestToken: "",
|
|
3
4
|
accounts: [],
|
|
4
5
|
sessions: [],
|
|
@@ -12,6 +13,7 @@ const state = {
|
|
|
12
13
|
loadedSessionKey: "",
|
|
13
14
|
loadingMessages: false,
|
|
14
15
|
sendingMessage: false,
|
|
16
|
+
savingSessionRuntime: false,
|
|
15
17
|
selectedAccountId: "",
|
|
16
18
|
chatFiles: []
|
|
17
19
|
};
|
|
@@ -24,6 +26,7 @@ const els = {};
|
|
|
24
26
|
document.addEventListener("DOMContentLoaded", () => {
|
|
25
27
|
Object.assign(els, {
|
|
26
28
|
accountsList: document.querySelector("#accountsList"),
|
|
29
|
+
productVersion: document.querySelector("#productVersion"),
|
|
27
30
|
sessionsList: document.querySelector("#sessionsList"),
|
|
28
31
|
sessionAccountTabs: document.querySelector("#sessionAccountTabs"),
|
|
29
32
|
sessionListCount: document.querySelector("#sessionListCount"),
|
|
@@ -37,6 +40,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
37
40
|
chatFileInput: document.querySelector("#chatFileInput"),
|
|
38
41
|
composerFiles: document.querySelector("#composerFiles"),
|
|
39
42
|
refreshMessagesButton: document.querySelector("#refreshMessagesButton"),
|
|
43
|
+
sessionRuntimeToolbar: document.querySelector("#sessionRuntimeToolbar"),
|
|
44
|
+
sessionModelInput: document.querySelector("#sessionModelInput"),
|
|
45
|
+
sessionEffortInput: document.querySelector("#sessionEffortInput"),
|
|
40
46
|
runningAccountMetric: document.querySelector("#runningAccountMetric"),
|
|
41
47
|
sessionMetric: document.querySelector("#sessionMetric"),
|
|
42
48
|
workspaceMetric: document.querySelector("#workspaceMetric"),
|
|
@@ -59,6 +65,8 @@ function bindEvents() {
|
|
|
59
65
|
document.querySelector("#newSessionButton").addEventListener("click", openNewSessionDialog);
|
|
60
66
|
document.querySelector("#settingsForm").addEventListener("submit", (event) => void saveSettings(event));
|
|
61
67
|
document.querySelector("#modelInput").addEventListener("change", () => renderEffortOptions(""));
|
|
68
|
+
els.sessionModelInput.addEventListener("change", () => void handleSessionModelChange());
|
|
69
|
+
els.sessionEffortInput.addEventListener("change", () => void saveSessionRuntimeSettings());
|
|
62
70
|
document.querySelector("#accountForm").addEventListener("submit", (event) => void saveAccountRemark(event));
|
|
63
71
|
document.querySelector("#sessionForm").addEventListener("submit", (event) => void saveSession(event));
|
|
64
72
|
document.querySelector("#sessionSenderInput").addEventListener("change", updateNewSessionDefaultTitle);
|
|
@@ -80,6 +88,7 @@ async function bootstrap() {
|
|
|
80
88
|
try {
|
|
81
89
|
const data = await api("/api/bootstrap", { token: false });
|
|
82
90
|
state.requestToken = data.requestToken;
|
|
91
|
+
state.version = data.version || "";
|
|
83
92
|
state.accounts = data.accounts;
|
|
84
93
|
state.sessions = data.sessions;
|
|
85
94
|
state.config = data.config;
|
|
@@ -125,6 +134,7 @@ async function refreshData(notify) {
|
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
function renderAll() {
|
|
137
|
+
renderProductVersion();
|
|
128
138
|
renderMetrics();
|
|
129
139
|
renderAccounts();
|
|
130
140
|
renderSessions();
|
|
@@ -132,6 +142,12 @@ function renderAll() {
|
|
|
132
142
|
drawIcons();
|
|
133
143
|
}
|
|
134
144
|
|
|
145
|
+
function renderProductVersion() {
|
|
146
|
+
const version = state.version.trim();
|
|
147
|
+
els.productVersion.hidden = !version;
|
|
148
|
+
els.productVersion.textContent = version ? `v${version.replace(/^v/i, "")}` : "";
|
|
149
|
+
}
|
|
150
|
+
|
|
135
151
|
function renderMetrics() {
|
|
136
152
|
els.runningAccountMetric.textContent = String(state.accounts.filter((account) => account.status === "running").length);
|
|
137
153
|
els.sessionMetric.textContent = String(state.sessions.length);
|
|
@@ -149,6 +165,11 @@ function renderMetrics() {
|
|
|
149
165
|
}
|
|
150
166
|
|
|
151
167
|
function renderAccounts() {
|
|
168
|
+
const expandedAccountIds = new Set(
|
|
169
|
+
[...els.accountsList.querySelectorAll(".account-identifiers[open]")]
|
|
170
|
+
.map((details) => details.dataset.accountId)
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
);
|
|
152
173
|
if (!state.accounts.length) {
|
|
153
174
|
els.accountsList.innerHTML = emptyState("scan-line", "还没有微信账号", "", `<button class="button button-primary" type="button" data-account-action="add"><i data-lucide="scan-line"></i><span>添加微信</span></button>`);
|
|
154
175
|
drawIcons();
|
|
@@ -175,6 +196,16 @@ function renderAccounts() {
|
|
|
175
196
|
<button class="icon-button is-danger" type="button" data-account-action="remove" data-account-id="${escapeAttr(account.accountId)}" title="移除账号" aria-label="移除账号"><i data-lucide="trash-2"></i></button>
|
|
176
197
|
</div>
|
|
177
198
|
</div>
|
|
199
|
+
<details class="account-identifiers" data-account-id="${escapeAttr(account.accountId)}"${expandedAccountIds.has(account.accountId) ? " open" : ""}>
|
|
200
|
+
<summary>
|
|
201
|
+
<span class="account-identifiers-title"><i data-lucide="fingerprint"></i><strong>账号 ID</strong><small>Bot ID 与 User ID</small></span>
|
|
202
|
+
<i class="account-identifiers-chevron" data-lucide="chevron-down"></i>
|
|
203
|
+
</summary>
|
|
204
|
+
<dl class="account-identifiers-grid">
|
|
205
|
+
<div><dt>Bot ID</dt><dd><code title="${escapeAttr(account.botId || account.accountId)}">${escapeHtml(account.botId || account.accountId)}</code></dd></div>
|
|
206
|
+
<div><dt>User ID</dt><dd><code title="${escapeAttr(account.userId || "未返回")}">${escapeHtml(account.userId || "未返回")}</code></dd></div>
|
|
207
|
+
</dl>
|
|
208
|
+
</details>
|
|
178
209
|
<div class="account-detail">${renderAuthorizationState(account, pendingSender)}</div>
|
|
179
210
|
${authorized && pendingSender ? `<div class="pending-access"><div><strong>新的微信访问请求</strong><span>当前授权不受影响,可选择允许新的访问</span></div><button class="button button-secondary" type="button" data-account-action="allow" data-account-id="${escapeAttr(account.accountId)}" data-sender-id="${escapeAttr(pendingSender)}"><i data-lucide="user-check"></i><span>允许访问</span></button></div>` : ""}
|
|
180
211
|
${account.error ? `<div class="pending-access"><div><strong>账号运行错误</strong><span>${escapeHtml(account.error)}</span></div></div>` : ""}
|
|
@@ -481,13 +512,14 @@ async function loadSelectedSessionMessages() {
|
|
|
481
512
|
|
|
482
513
|
function renderChatPanel() {
|
|
483
514
|
const session = selectedSession();
|
|
484
|
-
const enabled = Boolean(session) && !state.sendingMessage;
|
|
515
|
+
const enabled = Boolean(session) && !state.sendingMessage && !state.savingSessionRuntime;
|
|
485
516
|
els.chatInput.disabled = !enabled;
|
|
486
517
|
els.chatAttachButton.disabled = !enabled;
|
|
487
518
|
els.chatFileInput.disabled = !enabled;
|
|
488
519
|
els.refreshMessagesButton.disabled = !session || state.loadingMessages || state.sendingMessage;
|
|
489
520
|
renderComposerFiles();
|
|
490
521
|
updateComposerState();
|
|
522
|
+
renderSessionRuntimeControls(session);
|
|
491
523
|
if (!session) {
|
|
492
524
|
els.chatTitle.textContent = "选择一个会话";
|
|
493
525
|
els.chatContext.textContent = "查看历史消息并继续聊天";
|
|
@@ -522,6 +554,112 @@ function renderChatPanel() {
|
|
|
522
554
|
setChatMessagesHtml(html, renderKey);
|
|
523
555
|
}
|
|
524
556
|
|
|
557
|
+
function renderSessionRuntimeControls(session) {
|
|
558
|
+
const disabled = !session || state.sendingMessage || state.savingSessionRuntime;
|
|
559
|
+
els.sessionModelInput.disabled = disabled;
|
|
560
|
+
els.sessionEffortInput.disabled = disabled;
|
|
561
|
+
const renderKey = session ? [
|
|
562
|
+
sessionKey(session),
|
|
563
|
+
session.model || "",
|
|
564
|
+
session.effort || "",
|
|
565
|
+
state.config?.model || "",
|
|
566
|
+
state.config?.effort || "",
|
|
567
|
+
state.codexRuntime?.model || "",
|
|
568
|
+
state.codexRuntime?.effort || "",
|
|
569
|
+
state.codexModels.length
|
|
570
|
+
].join("|") : "none";
|
|
571
|
+
if (els.sessionRuntimeToolbar.dataset.renderKey === renderKey) return;
|
|
572
|
+
els.sessionRuntimeToolbar.dataset.renderKey = renderKey;
|
|
573
|
+
|
|
574
|
+
if (!session) {
|
|
575
|
+
els.sessionModelInput.innerHTML = '<option value="">选择会话后设置</option>';
|
|
576
|
+
els.sessionEffortInput.innerHTML = '<option value="">选择会话后设置</option>';
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const inheritedModel = state.config?.model || state.codexRuntime?.model || "";
|
|
581
|
+
const models = Array.isArray(state.codexModels) ? state.codexModels : [];
|
|
582
|
+
const options = [{
|
|
583
|
+
value: "",
|
|
584
|
+
label: inheritedModel ? `继承全局(${inheritedModel})` : "继承全局设置"
|
|
585
|
+
}, ...models.map((model) => ({
|
|
586
|
+
value: model.model,
|
|
587
|
+
label: model.displayName && model.displayName !== model.model
|
|
588
|
+
? `${model.displayName} · ${model.model}`
|
|
589
|
+
: model.model,
|
|
590
|
+
title: model.description || ""
|
|
591
|
+
}))];
|
|
592
|
+
if (session.model && !options.some((option) => option.value === session.model)) {
|
|
593
|
+
options.push({ value: session.model, label: `${session.model}(当前会话)`, title: "" });
|
|
594
|
+
}
|
|
595
|
+
els.sessionModelInput.innerHTML = options.map((option) => `<option value="${escapeAttr(option.value)}"${option.title ? ` title="${escapeAttr(option.title)}"` : ""}>${escapeHtml(option.label)}</option>`).join("");
|
|
596
|
+
els.sessionModelInput.value = session.model || "";
|
|
597
|
+
setSessionEffortOptions(session.model || "", session.effort || "");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function setSessionEffortOptions(modelOverride, preferredEffort) {
|
|
601
|
+
const effectiveModel = modelOverride || state.config?.model || state.codexRuntime?.model || "";
|
|
602
|
+
const model = state.codexModels.find((candidate) => candidate.model === effectiveModel);
|
|
603
|
+
const advertised = model?.supportedEfforts?.length
|
|
604
|
+
? model.supportedEfforts
|
|
605
|
+
: state.codexModels.flatMap((candidate) => candidate.supportedEfforts || []);
|
|
606
|
+
const efforts = [...new Map(advertised.map((option) => [option.effort, option])).values()]
|
|
607
|
+
.sort((a, b) => effortRank(a.effort) - effortRank(b.effort));
|
|
608
|
+
if (preferredEffort && !efforts.some((option) => option.effort === preferredEffort)) {
|
|
609
|
+
efforts.push({ effort: preferredEffort, description: "当前会话" });
|
|
610
|
+
}
|
|
611
|
+
const inheritedEffort = state.config?.effort || state.codexRuntime?.effort || model?.defaultEffort;
|
|
612
|
+
const inheritedLabel = inheritedEffort
|
|
613
|
+
? `继承全局(${effortInlineName(inheritedEffort)})`
|
|
614
|
+
: "继承全局设置";
|
|
615
|
+
els.sessionEffortInput.innerHTML = [
|
|
616
|
+
`<option value="">${escapeHtml(inheritedLabel)}</option>`,
|
|
617
|
+
...efforts.map((option) => `<option value="${escapeAttr(option.effort)}"${option.description ? ` title="${escapeAttr(option.description)}"` : ""}>${escapeHtml(effortDisplayName(option.effort))}</option>`)
|
|
618
|
+
].join("");
|
|
619
|
+
els.sessionEffortInput.value = preferredEffort;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async function handleSessionModelChange() {
|
|
623
|
+
const modelValue = els.sessionModelInput.value;
|
|
624
|
+
const model = state.codexModels.find((candidate) => candidate.model === (modelValue || state.config?.model || state.codexRuntime?.model));
|
|
625
|
+
const efforts = model?.supportedEfforts?.map((option) => option.effort) || [];
|
|
626
|
+
let effortValue = els.sessionEffortInput.value;
|
|
627
|
+
const inheritedEffort = state.config?.effort || state.codexRuntime?.effort || "";
|
|
628
|
+
const effectiveEffort = effortValue || inheritedEffort;
|
|
629
|
+
if (effectiveEffort && efforts.length && !efforts.includes(effectiveEffort)) {
|
|
630
|
+
effortValue = efforts.includes(model?.defaultEffort) ? model.defaultEffort : efforts[0] || "";
|
|
631
|
+
}
|
|
632
|
+
setSessionEffortOptions(modelValue, effortValue);
|
|
633
|
+
await saveSessionRuntimeSettings();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async function saveSessionRuntimeSettings() {
|
|
637
|
+
const session = selectedSession();
|
|
638
|
+
if (!session || state.savingSessionRuntime) return;
|
|
639
|
+
const key = sessionKey(session);
|
|
640
|
+
const model = els.sessionModelInput.value;
|
|
641
|
+
const effort = els.sessionEffortInput.value;
|
|
642
|
+
state.savingSessionRuntime = true;
|
|
643
|
+
renderChatPanel();
|
|
644
|
+
try {
|
|
645
|
+
const result = await api(`/api/sessions/${encodeURIComponent(session.accountId)}/${encodeURIComponent(session.id)}`, {
|
|
646
|
+
method: "PATCH",
|
|
647
|
+
body: { model: model || null, effort: effort || null }
|
|
648
|
+
});
|
|
649
|
+
const index = state.sessions.findIndex((candidate) => sessionKey(candidate) === key);
|
|
650
|
+
if (index >= 0) state.sessions[index] = result.session;
|
|
651
|
+
els.sessionRuntimeToolbar.dataset.renderKey = "";
|
|
652
|
+
renderSessions();
|
|
653
|
+
toast("会话模型设置已更新");
|
|
654
|
+
} catch (error) {
|
|
655
|
+
toast(error.message, true);
|
|
656
|
+
await refreshData(false);
|
|
657
|
+
} finally {
|
|
658
|
+
state.savingSessionRuntime = false;
|
|
659
|
+
renderChatPanel();
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
525
663
|
function renderTypingIndicator() {
|
|
526
664
|
return `<div class="chat-typing" role="status" aria-label="对方正在输入">
|
|
527
665
|
<span class="typing-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
|
@@ -659,7 +797,7 @@ function renderComposerFiles() {
|
|
|
659
797
|
}
|
|
660
798
|
|
|
661
799
|
function updateComposerState() {
|
|
662
|
-
const canCompose = Boolean(selectedSession()) && !state.sendingMessage;
|
|
800
|
+
const canCompose = Boolean(selectedSession()) && !state.sendingMessage && !state.savingSessionRuntime;
|
|
663
801
|
els.chatInput.disabled = !canCompose;
|
|
664
802
|
els.chatAttachButton.disabled = !canCompose;
|
|
665
803
|
els.chatFileInput.disabled = !canCompose;
|
package/dist/web/index.html
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<i data-lucide="message-circle"></i>
|
|
21
21
|
</span>
|
|
22
22
|
<span class="brand-name">codex-weixin</span>
|
|
23
|
+
<span class="brand-version" id="productVersion" aria-label="codex-weixin 版本" hidden></span>
|
|
23
24
|
</a>
|
|
24
25
|
<nav class="view-tabs" aria-label="管理视图">
|
|
25
26
|
<button class="tab is-active" type="button" data-view="accounts">微信账号</button>
|
|
@@ -92,6 +93,17 @@
|
|
|
92
93
|
<i data-lucide="refresh-cw"></i>
|
|
93
94
|
</button>
|
|
94
95
|
</header>
|
|
96
|
+
<div class="chat-runtime-toolbar" id="sessionRuntimeToolbar" aria-label="当前会话模型设置">
|
|
97
|
+
<span class="chat-runtime-title"><i data-lucide="sliders-horizontal"></i><span>会话设置</span></span>
|
|
98
|
+
<label>
|
|
99
|
+
<span>模型</span>
|
|
100
|
+
<select id="sessionModelInput" disabled><option value="">继承全局设置</option></select>
|
|
101
|
+
</label>
|
|
102
|
+
<label>
|
|
103
|
+
<span>推理强度</span>
|
|
104
|
+
<select id="sessionEffortInput" disabled><option value="">继承全局设置</option></select>
|
|
105
|
+
</label>
|
|
106
|
+
</div>
|
|
95
107
|
<div class="chat-messages" id="chatMessages" aria-live="polite"></div>
|
|
96
108
|
<form class="chat-composer" id="chatComposer">
|
|
97
109
|
<div class="composer-files" id="composerFiles" aria-live="polite" hidden></div>
|
package/dist/web/styles.css
CHANGED
|
@@ -38,6 +38,8 @@ button:disabled { cursor: not-allowed; opacity: .48; }
|
|
|
38
38
|
.brand-mark svg:first-child { left: 6px; top: 6px; }
|
|
39
39
|
.brand-mark svg:last-child { right: 3px; bottom: 3px; padding: 2px; color: white; border-radius: 50%; background: var(--green); }
|
|
40
40
|
.brand-name { overflow: hidden; font-size: 15px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
|
|
41
|
+
.brand-version { min-width: max-content; padding: 3px 6px; border: 1px solid #b9c5bd; border-radius: 4px; color: var(--ink-soft); background: #f5f7f5; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 9px; font-weight: 700; line-height: 1; }
|
|
42
|
+
.brand-version[hidden] { display: none; }
|
|
41
43
|
.view-tabs { height: 38px; display: inline-flex; align-items: center; padding: 3px; gap: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--canvas); }
|
|
42
44
|
.tab { min-width: 82px; height: 30px; border: 0; border-radius: 4px; color: var(--ink-soft); background: transparent; font-size: 13px; font-weight: 650; }
|
|
43
45
|
.tab:hover { color: var(--ink); }
|
|
@@ -94,6 +96,21 @@ h2 { margin: 0; font-size: 19px; line-height: 1.3; }
|
|
|
94
96
|
.account-stat span { display: block; color: var(--ink-soft); font-size: 11px; }
|
|
95
97
|
.account-stat strong { display: block; margin-top: 4px; font-size: 15px; }
|
|
96
98
|
.account-actions { display: flex; gap: 8px; }
|
|
99
|
+
.account-identifiers { border-top: 1px solid var(--line); background: #fcfdfc; }
|
|
100
|
+
.account-identifiers summary { min-height: 46px; padding: 9px 20px; color: var(--ink-soft); cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
101
|
+
.account-identifiers summary::-webkit-details-marker { display: none; }
|
|
102
|
+
.account-identifiers summary:hover { color: var(--ink); background: #f7f9f7; }
|
|
103
|
+
.account-identifiers-title { min-width: 0; display: flex; align-items: center; gap: 8px; }
|
|
104
|
+
.account-identifiers-title > svg { width: 15px; height: 15px; flex: 0 0 auto; color: var(--green-dark); }
|
|
105
|
+
.account-identifiers-title strong { color: var(--ink); font-size: 11px; }
|
|
106
|
+
.account-identifiers-title small { overflow: hidden; font-size: 9px; font-weight: 550; text-overflow: ellipsis; white-space: nowrap; }
|
|
107
|
+
.account-identifiers-chevron { width: 15px; height: 15px; flex: 0 0 auto; transition: transform .16s ease; }
|
|
108
|
+
.account-identifiers[open] .account-identifiers-chevron { transform: rotate(180deg); }
|
|
109
|
+
.account-identifiers-grid { margin: 0; padding: 2px 20px 15px 43px; display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; }
|
|
110
|
+
.account-identifiers-grid div { min-width: 0; }
|
|
111
|
+
.account-identifiers-grid dt { margin-bottom: 4px; color: var(--ink-soft); font-size: 9px; font-weight: 750; text-transform: uppercase; }
|
|
112
|
+
.account-identifiers-grid dd { margin: 0; }
|
|
113
|
+
.account-identifiers-grid code { color: #26302c; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 10px; line-height: 1.5; overflow-wrap: anywhere; user-select: all; }
|
|
97
114
|
.account-detail { padding: 14px 20px; border-top: 1px solid var(--line); background: #fafbfa; display: flex; align-items: center; justify-content: space-between; gap: 18px; }
|
|
98
115
|
.authorization-state { width: 100%; min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; }
|
|
99
116
|
.authorization-icon { width: 34px; height: 34px; border-radius: 50%; color: var(--ink-soft); background: var(--surface-muted); display: grid; place-items: center; }
|
|
@@ -143,10 +160,16 @@ h2 { margin: 0; font-size: 19px; line-height: 1.3; }
|
|
|
143
160
|
.session-actions .icon-button { width: 29px; height: 29px; background: transparent; }
|
|
144
161
|
.session-actions .icon-button svg { width: 15px; height: 15px; }
|
|
145
162
|
|
|
146
|
-
.chat-panel { min-width: 0; min-height: 650px; background: linear-gradient(180deg, #fff 0, #fbfcfb 100%); display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
|
|
163
|
+
.chat-panel { min-width: 0; min-height: 650px; background: linear-gradient(180deg, #fff 0, #fbfcfb 100%); display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; }
|
|
147
164
|
.chat-header { min-height: 82px; padding: 17px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 18px; }
|
|
148
165
|
.chat-header h2 { max-width: 620px; overflow: hidden; font-size: 17px; text-overflow: ellipsis; white-space: nowrap; }
|
|
149
166
|
.chat-context { max-width: 720px; margin: 5px 0 0; overflow: hidden; color: var(--ink-soft); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 9px; text-overflow: ellipsis; white-space: nowrap; }
|
|
167
|
+
.chat-runtime-toolbar { min-height: 54px; padding: 9px 20px; border-bottom: 1px solid var(--line); background: #f7f9f7; display: grid; grid-template-columns: auto minmax(190px, 1fr) minmax(170px, .72fr); align-items: center; gap: 14px; }
|
|
168
|
+
.chat-runtime-title { color: var(--ink-soft); display: inline-flex; align-items: center; gap: 6px; font-size: 9px; font-weight: 800; letter-spacing: .04em; white-space: nowrap; }
|
|
169
|
+
.chat-runtime-title svg { width: 14px; height: 14px; color: var(--green-dark); }
|
|
170
|
+
.chat-runtime-toolbar label { min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 7px; }
|
|
171
|
+
.chat-runtime-toolbar label > span { font-size: 9px; white-space: nowrap; }
|
|
172
|
+
.chat-runtime-toolbar select { min-width: 0; min-height: 32px; height: 32px; padding-inline: 9px 28px; font-size: 10px; }
|
|
150
173
|
.chat-messages { min-height: 0; max-height: 560px; padding: 26px 24px 30px; overflow-y: auto; scroll-behavior: smooth; background-image: radial-gradient(circle at 1px 1px, rgb(31 57 44 / 5%) 1px, transparent 0); background-size: 24px 24px; }
|
|
151
174
|
.chat-message { width: fit-content; min-width: 0; max-width: min(78%, 720px); margin-bottom: 22px; display: grid; gap: 6px; }
|
|
152
175
|
.chat-message.is-user { margin-left: auto; justify-items: end; }
|
|
@@ -284,6 +307,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--focus); outline:
|
|
|
284
307
|
.header-inner, .status-inner, .view { width: min(calc(100% - 24px), var(--content)); }
|
|
285
308
|
.header-inner { min-height: 62px; gap: 12px; }
|
|
286
309
|
.brand-name { max-width: 178px; font-size: 13px; }
|
|
310
|
+
.brand-version { padding-inline: 5px; font-size: 8px; }
|
|
287
311
|
.brand-mark { width: 34px; height: 34px; flex-basis: 34px; }
|
|
288
312
|
.header-action { width: 38px; padding: 0; }
|
|
289
313
|
.header-action span { display: none; }
|
|
@@ -297,6 +321,8 @@ input:focus, select:focus, textarea:focus { border-color: var(--focus); outline:
|
|
|
297
321
|
.account-main .account-stat { display: none; }
|
|
298
322
|
.account-actions { grid-column: 2; grid-row: 1; }
|
|
299
323
|
.account-detail, .pending-access { padding: 12px 16px; align-items: start; flex-direction: column; }
|
|
324
|
+
.account-identifiers summary { padding-inline: 16px; }
|
|
325
|
+
.account-identifiers-grid { padding: 2px 16px 14px 39px; grid-template-columns: 1fr; gap: 10px; }
|
|
300
326
|
.authorization-state { grid-template-columns: auto minmax(0, 1fr); }
|
|
301
327
|
.authorization-action { width: 100%; grid-column: 1 / -1; }
|
|
302
328
|
.pending-access .button { width: 100%; }
|
|
@@ -304,6 +330,9 @@ input:focus, select:focus, textarea:focus { border-color: var(--focus); outline:
|
|
|
304
330
|
.session-account-bar-label { padding-inline: 2px; }
|
|
305
331
|
.session-account-tabs { width: 100%; }
|
|
306
332
|
.chat-header { padding: 15px 16px; }
|
|
333
|
+
.chat-runtime-toolbar { padding: 10px 12px; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
334
|
+
.chat-runtime-title { grid-column: 1 / -1; }
|
|
335
|
+
.chat-runtime-toolbar label { grid-template-columns: 1fr; gap: 4px; }
|
|
307
336
|
.chat-messages { padding: 22px 14px 26px; }
|
|
308
337
|
.chat-message { max-width: 92%; }
|
|
309
338
|
.chat-composer { padding-inline: 12px; }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { StatePaths } from "../state/paths.js";
|
|
2
2
|
export type WeixinAccount = {
|
|
3
3
|
accountId: string;
|
|
4
|
+
botId?: string;
|
|
4
5
|
token: string;
|
|
5
6
|
baseUrl: string;
|
|
6
7
|
cdnBaseUrl: string;
|
|
@@ -13,6 +14,11 @@ export declare const DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
|
|
|
13
14
|
export declare const DEFAULT_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
14
15
|
export declare function normalizeAccountId(raw: string): string;
|
|
15
16
|
export declare function saveAccount(paths: StatePaths, account: WeixinAccount): void;
|
|
17
|
+
export type SaveScannedAccountResult = {
|
|
18
|
+
account: WeixinAccount;
|
|
19
|
+
reusedExisting: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function saveScannedAccount(paths: StatePaths, scanned: WeixinAccount, targetAccountId?: string): SaveScannedAccountResult;
|
|
16
22
|
export declare function listAccounts(paths: StatePaths): WeixinAccount[];
|
|
17
23
|
export declare function setAccountEnabled(paths: StatePaths, accountId: string, enabled: boolean): WeixinAccount;
|
|
18
24
|
export declare function setAccountDisplayName(paths: StatePaths, accountId: string, displayName: string): WeixinAccount;
|
package/dist/weixin/accounts.js
CHANGED
|
@@ -10,6 +10,29 @@ export function saveAccount(paths, account) {
|
|
|
10
10
|
ensureDir(paths.accountsDir);
|
|
11
11
|
writeJsonFile(path.join(paths.accountsDir, `${normalizeAccountId(account.accountId)}.json`), account);
|
|
12
12
|
}
|
|
13
|
+
export function saveScannedAccount(paths, scanned, targetAccountId) {
|
|
14
|
+
const accounts = listAccounts(paths);
|
|
15
|
+
const target = targetAccountId ? loadAccount(paths, targetAccountId) : undefined;
|
|
16
|
+
if (target?.userId && scanned.userId && target.userId !== scanned.userId) {
|
|
17
|
+
throw new Error("The scanned WeChat account does not match the existing account");
|
|
18
|
+
}
|
|
19
|
+
const exact = accounts.find((account) => account.accountId === scanned.accountId);
|
|
20
|
+
const sameUsers = scanned.userId
|
|
21
|
+
? accounts.filter((account) => account.userId === scanned.userId)
|
|
22
|
+
: [];
|
|
23
|
+
const existing = target ?? exact ?? (sameUsers.length === 1 ? sameUsers[0] : undefined);
|
|
24
|
+
const botId = scanned.botId ?? scanned.accountId;
|
|
25
|
+
const account = existing ? {
|
|
26
|
+
...scanned,
|
|
27
|
+
accountId: existing.accountId,
|
|
28
|
+
botId,
|
|
29
|
+
userId: scanned.userId ?? existing.userId,
|
|
30
|
+
...(existing.displayName ? { displayName: existing.displayName } : {}),
|
|
31
|
+
enabled: true
|
|
32
|
+
} : { ...scanned, botId };
|
|
33
|
+
saveAccount(paths, account);
|
|
34
|
+
return { account, reusedExisting: Boolean(existing) };
|
|
35
|
+
}
|
|
13
36
|
export function listAccounts(paths) {
|
|
14
37
|
ensureDir(paths.accountsDir);
|
|
15
38
|
return fs.readdirSync(paths.accountsDir)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/weixin/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/weixin/accounts.ts"],"names":[],"mappings":"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"}
|
package/dist/weixin/login.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_BASE_URL, DEFAULT_CDN_BASE_URL,
|
|
1
|
+
import { DEFAULT_BASE_URL, DEFAULT_CDN_BASE_URL, saveScannedAccount } from "./accounts.js";
|
|
2
2
|
export class WeixinQrLoginSession {
|
|
3
3
|
qrContent;
|
|
4
4
|
qrToken;
|
|
@@ -60,8 +60,7 @@ export async function loginWithQr(options) {
|
|
|
60
60
|
throw new Error("WeChat QR code expired. Start login again.");
|
|
61
61
|
}
|
|
62
62
|
if (update.status === "confirmed") {
|
|
63
|
-
|
|
64
|
-
return update.account;
|
|
63
|
+
return saveScannedAccount(options.paths, update.account).account;
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -71,6 +70,7 @@ function accountFromStatus(status, pollBaseUrl) {
|
|
|
71
70
|
}
|
|
72
71
|
return {
|
|
73
72
|
accountId: status.ilink_bot_id,
|
|
73
|
+
botId: status.ilink_bot_id,
|
|
74
74
|
token: status.bot_token,
|
|
75
75
|
userId: status.ilink_user_id,
|
|
76
76
|
baseUrl: status.baseurl ?? status.base_url ?? pollBaseUrl,
|
package/dist/weixin/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/weixin/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/weixin/login.ts"],"names":[],"mappings":"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"}
|