@wu529778790/open-im 1.11.1-beta.2 → 1.11.1-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -59
- package/README.zh-CN.md +75 -58
- package/dist/adapters/claude-sdk-adapter.d.ts +45 -19
- package/dist/adapters/claude-sdk-adapter.js +240 -414
- package/dist/adapters/registry.js +1 -2
- package/dist/adapters/tool-adapter.interface.d.ts +4 -0
- package/dist/clawbot/client.d.ts +1 -1
- package/dist/clawbot/client.js +71 -3
- package/dist/clawbot/event-handler.d.ts +1 -1
- package/dist/clawbot/event-handler.js +10 -3
- package/dist/commands/handler.d.ts +7 -0
- package/dist/commands/handler.js +270 -27
- package/dist/config-web-page-i18n.d.ts +96 -4
- package/dist/config-web-page-i18n.js +98 -4
- package/dist/config-web.js +15 -0
- package/dist/constants.js +0 -1
- package/dist/index.js +13 -4
- package/dist/logger.d.ts +5 -0
- package/dist/logger.js +21 -0
- package/dist/platform/handle-text-flow.d.ts +2 -0
- package/dist/platform/handle-text-flow.js +35 -2
- package/dist/session/session-manager.d.ts +9 -12
- package/dist/session/session-manager.js +31 -79
- package/dist/shared/ai-task.js +1 -0
- package/dist/shared/message-wal.d.ts +31 -0
- package/dist/shared/message-wal.js +91 -0
- package/package.json +4 -3
- package/web/dist/assets/index-B1c1xlbx.css +1 -0
- package/web/dist/assets/index-Dng7B2mE.js +57 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-7fl61tV1.css +0 -1
- package/web/dist/assets/index-BaLTMeeF.js +0 -57
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message WAL (Write-Ahead Log) — 消息持久化
|
|
3
|
+
*
|
|
4
|
+
* 进程崩溃时保留未处理的消息,重启后可重放。
|
|
5
|
+
* 实现:JSONL 文件,每条消息一行。
|
|
6
|
+
*/
|
|
7
|
+
export interface WALEntry {
|
|
8
|
+
msgId: string;
|
|
9
|
+
platform: string;
|
|
10
|
+
userId: string;
|
|
11
|
+
chatId: string;
|
|
12
|
+
text: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
status: 'pending' | 'done';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 写入消息到 WAL(处理前调用)
|
|
18
|
+
*/
|
|
19
|
+
export declare function walWrite(entry: Omit<WALEntry, 'status'>): void;
|
|
20
|
+
/**
|
|
21
|
+
* 标记消息为已处理(处理后调用)
|
|
22
|
+
*/
|
|
23
|
+
export declare function walCommit(msgId: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* 读取所有未处理的消息(重启时调用)
|
|
26
|
+
*/
|
|
27
|
+
export declare function walReadPending(): WALEntry[];
|
|
28
|
+
/**
|
|
29
|
+
* 清空 WAL 文件(所有消息处理完后调用)
|
|
30
|
+
*/
|
|
31
|
+
export declare function walClear(): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message WAL (Write-Ahead Log) — 消息持久化
|
|
3
|
+
*
|
|
4
|
+
* 进程崩溃时保留未处理的消息,重启后可重放。
|
|
5
|
+
* 实现:JSONL 文件,每条消息一行。
|
|
6
|
+
*/
|
|
7
|
+
import { createWriteStream, existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { APP_HOME } from '../constants.js';
|
|
10
|
+
import { createLogger } from '../logger.js';
|
|
11
|
+
const log = createLogger('MessageWAL');
|
|
12
|
+
const WAL_DIR = join(APP_HOME, 'data');
|
|
13
|
+
const WAL_FILE = join(WAL_DIR, 'messages.jsonl');
|
|
14
|
+
let stream = null;
|
|
15
|
+
function ensureDir() {
|
|
16
|
+
if (!existsSync(WAL_DIR))
|
|
17
|
+
mkdirSync(WAL_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 写入消息到 WAL(处理前调用)
|
|
21
|
+
*/
|
|
22
|
+
export function walWrite(entry) {
|
|
23
|
+
try {
|
|
24
|
+
ensureDir();
|
|
25
|
+
const line = JSON.stringify({ ...entry, status: 'pending' }) + '\n';
|
|
26
|
+
if (!stream) {
|
|
27
|
+
stream = createWriteStream(WAL_FILE, { flags: 'a' });
|
|
28
|
+
}
|
|
29
|
+
stream.write(line);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
log.warn('WAL write failed:', err);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 标记消息为已处理(处理后调用)
|
|
37
|
+
*/
|
|
38
|
+
export function walCommit(msgId) {
|
|
39
|
+
try {
|
|
40
|
+
ensureDir();
|
|
41
|
+
// 读取所有条目,过滤掉已完成的
|
|
42
|
+
const entries = readAllEntries();
|
|
43
|
+
const pending = entries.filter(e => !(e.msgId === msgId && e.status === 'pending'));
|
|
44
|
+
// 重写文件(只保留未完成的)
|
|
45
|
+
writeFileSync(WAL_FILE, pending.map(e => JSON.stringify(e)).join('\n') + (pending.length ? '\n' : ''), 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
log.warn('WAL commit failed:', err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 读取所有未处理的消息(重启时调用)
|
|
53
|
+
*/
|
|
54
|
+
export function walReadPending() {
|
|
55
|
+
try {
|
|
56
|
+
ensureDir();
|
|
57
|
+
const entries = readAllEntries();
|
|
58
|
+
return entries.filter(e => e.status === 'pending');
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
log.warn('WAL read failed:', err);
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 清空 WAL 文件(所有消息处理完后调用)
|
|
67
|
+
*/
|
|
68
|
+
export function walClear() {
|
|
69
|
+
try {
|
|
70
|
+
ensureDir();
|
|
71
|
+
writeFileSync(WAL_FILE, '', 'utf-8');
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
log.warn('WAL clear failed:', err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function readAllEntries() {
|
|
78
|
+
if (!existsSync(WAL_FILE))
|
|
79
|
+
return [];
|
|
80
|
+
const content = readFileSync(WAL_FILE, 'utf-8').trim();
|
|
81
|
+
if (!content)
|
|
82
|
+
return [];
|
|
83
|
+
return content.split('\n').map(line => {
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(line);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}).filter((e) => e !== null);
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wu529778790/open-im",
|
|
3
|
-
"version": "1.11.1-beta.
|
|
4
|
-
"description": "Multi-platform IM bridge for
|
|
3
|
+
"version": "1.11.1-beta.21",
|
|
4
|
+
"description": "Your AI coding assistant, in every chat app. Multi-platform IM bridge for Claude Code, Codex, and CodeBuddy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"url": "https://github.com/wu529778790/open-im/issues"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
54
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.179",
|
|
55
|
+
"@anthropic-ai/sdk": "^0.104.2",
|
|
55
56
|
"@aws-sdk/client-s3": "^3.1035.0",
|
|
56
57
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
57
58
|
"centrifuge": "^5.5.3",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--c-bg: #f4f5f7;--c-surface: #ffffff;--c-surface-alt: #f9fafb;--c-border: #e4e7ec;--c-border-foc: #c0c7d1;--c-text: #101828;--c-text-2: #475467;--c-text-3: #98a2b3;--c-accent: #2970ff;--c-accent-h: #155eef;--c-accent-bg: #eff4ff;--c-accent-bdr: #b2ddff;--c-ok: #12b76a;--c-ok-bg: #ecfdf3;--c-ok-bdr: #a6f4c5;--c-warn: #f79009;--c-warn-bg: #fffaeb;--c-warn-bdr: #fedf89;--c-err: #f04438;--c-err-bg: #fef3f2;--c-err-bdr: #fecdca;--c-sb-bg: #101828;--c-sb-text: #98a2b3;--c-sb-text-a: #f4f5f7;--c-sb-hover: rgba(255,255,255,.06);--c-sb-active: rgba(255,255,255,.1);--shadow-xs: 0 1px 2px rgba(16,24,40,.05);--shadow-s: 0 1px 3px rgba(16,24,40,.1), 0 1px 2px rgba(16,24,40,.06);--shadow-m: 0 4px 8px -2px rgba(16,24,40,.1), 0 2px 4px -2px rgba(16,24,40,.06);--shadow-l: 0 12px 16px -4px rgba(16,24,40,.08), 0 4px 6px -2px rgba(16,24,40,.03);--r-s: 6px;--r-m: 8px;--r-l: 12px;--r-xl: 16px;--font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--mono: "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--fast: .12s ease;--norm: .2s ease;--sidebar-w: 240px;--header-h: 56px}:root.dark{--c-bg: #0c0d0f;--c-surface: #16171a;--c-surface-alt: #1c1d21;--c-border: #2a2b30;--c-border-foc: #3e4047;--c-text: #f0f1f4;--c-text-2: #98a2b3;--c-text-3: #5d6573;--c-accent: #528bff;--c-accent-h: #79a6ff;--c-accent-bg: rgba(42,114,255,.12);--c-accent-bdr: rgba(42,114,255,.24);--c-ok-bg: rgba(18,183,108,.1);--c-ok-bdr: rgba(18,183,108,.2);--c-warn-bg: rgba(247,144,9,.1);--c-warn-bdr: rgba(247,144,9,.2);--c-err-bg: rgba(240,68,56,.1);--c-err-bdr: rgba(240,68,56,.2);--c-sb-bg: #08090b;--c-sb-hover: rgba(255,255,255,.04);--c-sb-active: rgba(255,255,255,.08)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}body{font-family:var(--font);font-size:14px;line-height:1.5;color:var(--c-text);background:var(--c-bg);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.app{display:flex;min-height:100vh}.main{flex:1;margin-left:var(--sidebar-w);display:flex;flex-direction:column;min-height:100vh;transition:margin-left var(--norm)}.content{flex:1;padding:32px 40px 64px;max-width:960px;width:100%;margin:0 auto}.sidebar{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-w);background:var(--c-sb-bg);color:var(--c-sb-text);display:flex;flex-direction:column;z-index:20;border-right:1px solid rgba(255,255,255,.06);overflow:hidden}.sidebar-header{padding:20px 16px 16px;display:flex;align-items:center;gap:10px}.sidebar-logo{width:32px;height:32px;border-radius:var(--r-m);background:var(--c-accent);display:flex;align-items:center;justify-content:center;flex-shrink:0}.sidebar-logo svg{width:18px;height:18px;color:#fff}.sidebar-brand{font-size:15px;font-weight:700;color:var(--c-sb-text-a);letter-spacing:-.01em;white-space:nowrap;overflow:hidden}.sidebar-nav{flex:1;padding:4px 8px;display:flex;flex-direction:column;gap:2px;overflow-y:auto}.sidebar-section-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-3);padding:16px 12px 6px}.nav-item{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:var(--r-m);color:var(--c-sb-text);font-size:13px;font-weight:500;cursor:pointer;border:none;background:transparent;width:100%;text-align:left;transition:background var(--fast),color var(--fast);white-space:nowrap;overflow:hidden}.nav-item:hover{background:var(--c-sb-hover);color:var(--c-sb-text-a)}.nav-item.active{background:var(--c-sb-active);color:var(--c-sb-text-a)}.nav-item svg{width:18px;height:18px;flex-shrink:0;opacity:.7}.nav-item.active svg,.nav-item:hover svg{opacity:1}.header{position:sticky;top:0;z-index:15;background:var(--c-surface);border-bottom:1px solid var(--c-border);padding:0 40px;height:var(--header-h);display:flex;align-items:center;justify-content:space-between;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.header-title{font-size:15px;font-weight:600;color:var(--c-text)}.header-actions{display:flex;align-items:center;gap:8px}.header-divider{height:1px;background:var(--c-border);margin:0}.header-toolbar{background:var(--c-surface-alt);border-bottom:1px solid var(--c-border);padding:10px 40px;display:flex;gap:8px;flex-wrap:wrap;align-items:center}.header-toolbar-spacer{flex:1}.header-toolbar-status{font-size:12px;font-weight:500;display:inline-flex;align-items:center;gap:6px;color:var(--c-text-2)}.header-toolbar-status .dot{width:6px;height:6px;border-radius:50%;display:inline-block}.header-toolbar-status .dot.running{background:var(--c-ok)}.header-toolbar-status .dot.idle{background:var(--c-text-3)}.stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:32px}.stat-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-l);padding:20px}.stat-label{font-size:12px;font-weight:500;color:var(--c-text-3);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}.stat-value{font-size:28px;font-weight:700;color:var(--c-text);line-height:1}.stat-meta{font-size:12px;color:var(--c-text-3);margin-top:4px}.section{margin-bottom:40px}.section-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:8px}.section-title{font-size:16px;font-weight:600;color:var(--c-text)}.section-desc{font-size:13px;color:var(--c-text-3)}.card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-l);overflow:hidden}.card-head{padding:16px 20px;border-bottom:1px solid var(--c-border);display:flex;justify-content:space-between;align-items:center;gap:12px}.card-title{font-size:14px;font-weight:600;color:var(--c-text)}.card-body{padding:20px}.card-foot{padding:12px 20px;border-top:1px solid var(--c-border);background:var(--c-surface-alt)}.platform-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.platform-card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-l);overflow:hidden;transition:border-color var(--fast),box-shadow var(--fast)}.platform-card:hover{box-shadow:var(--shadow-s)}.platform-card.disabled{opacity:.55}.platform-card-head{padding:14px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--c-border);gap:12px}.platform-card-name{font-size:14px;font-weight:600;color:var(--c-text);display:flex;align-items:center;gap:8px}.platform-card-chevron{font-size:12px;color:var(--c-text-3);margin-left:4px;transition:transform var(--fast)}.platform-card-name .dot{width:6px;height:6px;border-radius:50%;background:var(--c-text-3)}.platform-card.enabled .platform-card-name .dot{background:var(--c-ok)}.platform-card-body{padding:16px 20px 20px}.platform-card-hint{font-size:12px;color:var(--c-text-3);line-height:1.5;margin-bottom:16px}.platform-card-help{margin-top:16px;padding:10px 14px;background:var(--c-surface-alt);border-radius:var(--r-m);font-size:12px;color:var(--c-text-2);line-height:1.6}.platform-card-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px}.ai-grid{display:grid;grid-template-columns:280px 1fr;gap:16px}.config-stack{display:flex;flex-direction:column;gap:16px}.config-card .card-title{font-family:var(--mono);font-size:13px}.config-card textarea{width:100%}.config-card-toolbar{display:flex;justify-content:flex-end;gap:6px;margin-bottom:8px}.form-group{margin-bottom:14px}.form-group:last-child{margin-bottom:0}.form-label{display:block;font-size:12px;font-weight:500;color:var(--c-text-2);margin-bottom:5px}.form-input,.form-select,.form-textarea{width:100%;padding:8px 10px;font-family:inherit;font-size:13px;color:var(--c-text);background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-s);transition:border-color var(--fast),box-shadow var(--fast);outline:none}.form-input:focus,.form-select:focus,.form-textarea:focus{border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-bg)}.form-input.mono,.form-textarea.mono{font-family:var(--mono);font-size:12px}.form-textarea{min-height:80px;resize:vertical}.form-hint{font-size:11px;color:var(--c-text-3);margin-top:4px;line-height:1.4}.form-hint a{color:var(--c-accent)}.form-hint code{font-family:var(--mono);font-size:10px;padding:1px 4px;background:var(--c-surface-alt);border-radius:3px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:7px 14px;font-family:inherit;font-size:13px;font-weight:500;border-radius:var(--r-s);border:1px solid transparent;cursor:pointer;text-decoration:none;transition:all var(--fast);white-space:nowrap}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-p{background:var(--c-accent);color:#fff}.btn-p:hover:not(:disabled){background:var(--c-accent-h)}.btn-s{background:var(--c-surface);color:var(--c-text);border-color:var(--c-border)}.btn-s:hover:not(:disabled){background:var(--c-surface-alt);border-color:var(--c-border-foc)}.btn-g{background:transparent;color:var(--c-text-2)}.btn-g:hover:not(:disabled){background:var(--c-surface-alt);color:var(--c-text)}.btn-d{background:var(--c-err-bg);color:var(--c-err);border-color:var(--c-err-bdr)}.btn-d:hover:not(:disabled){background:var(--c-err);color:#fff}.btn-w{background:var(--c-warn-bg);color:var(--c-warn);border-color:var(--c-warn-bdr)}.btn-w:hover:not(:disabled){background:var(--c-warn);color:#fff}.btn-sm{padding:5px 10px;font-size:12px}.btn-lg{padding:10px 20px;font-size:14px}.toggle{display:inline-flex;align-items:center;gap:8px;cursor:pointer;flex-shrink:0}.toggle-input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.toggle-track{position:relative;width:36px;height:20px;background:var(--c-border-foc);border-radius:999px;transition:background var(--fast);flex-shrink:0}.toggle-track:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform var(--fast);box-shadow:var(--shadow-xs)}.toggle-input:checked+.toggle-track{background:var(--c-accent)}.toggle-input:checked+.toggle-track:after{transform:translate(16px)}.tabs{display:inline-flex;gap:2px;padding:3px;background:var(--c-surface-alt);border:1px solid var(--c-border);border-radius:var(--r-m)}.tab{padding:6px 12px;font-size:12px;font-weight:500;color:var(--c-text-2);background:transparent;border:none;border-radius:var(--r-s);cursor:pointer;transition:all var(--fast)}.tab:hover{color:var(--c-text)}.tab.active{background:var(--c-surface);color:var(--c-text);box-shadow:var(--shadow-xs)}.msg{padding:10px 14px;border-radius:var(--r-m);font-size:13px;line-height:1.5}.msg-ok{background:var(--c-ok-bg);color:var(--c-ok)}.msg-err{background:var(--c-err-bg);color:var(--c-err)}.msg-w{background:var(--c-warn-bg);color:var(--c-warn)}.badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:11px;font-weight:500;border-radius:999px}.badge-ok{background:var(--c-ok-bg);color:var(--c-ok)}.badge-err{background:var(--c-err-bg);color:var(--c-err)}.badge-w{background:var(--c-warn-bg);color:var(--c-warn)}.badge-info{background:var(--c-accent-bg);color:var(--c-accent)}.field-tip{font-size:11px;color:var(--c-text-3);line-height:1.45;margin-top:5px}.field-tip a{color:var(--c-accent)}.field-tip code{font-family:var(--mono);font-size:10px;padding:1px 4px;background:var(--c-surface-alt);border-radius:3px}.flash{margin-bottom:20px}.mt-4{margin-top:16px}.mt-6{margin-top:24px}.gap-2{gap:8px}.gap-3{gap:12px}.text-center{text-align:center}.mono{font-family:var(--mono)}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.wizard-wrap{display:flex;justify-content:center;padding-top:16px}.wizard{width:100%;max-width:580px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-xl);box-shadow:var(--shadow-m);overflow:hidden}.wizard-bar{height:3px;background:var(--c-border)}.wizard-bar-fill{height:100%;background:var(--c-accent);transition:width .4s cubic-bezier(.4,0,.2,1)}.wizard-steps{display:flex;padding:20px 24px 0;gap:0}.wizard-step-ind{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;position:relative}.wizard-step-ind+.wizard-step-ind:before{content:"";position:absolute;top:12px;right:calc(50% + 16px);width:calc(100% - 32px);height:1px;background:var(--c-border)}.wizard-step-ind.done+.wizard-step-ind:before{background:var(--c-accent)}.wizard-step-dot{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;background:var(--c-surface-alt);color:var(--c-text-3);border:2px solid var(--c-border);position:relative;z-index:1;transition:all var(--norm)}.wizard-step-ind.active .wizard-step-dot{background:var(--c-accent);color:#fff;border-color:var(--c-accent)}.wizard-step-ind.done .wizard-step-dot{background:var(--c-ok);color:#fff;border-color:var(--c-ok)}.wizard-step-label{font-size:11px;font-weight:500;color:var(--c-text-3);white-space:nowrap}.wizard-step-ind.active .wizard-step-label{color:var(--c-accent);font-weight:600}.wizard-step-ind.done .wizard-step-label{color:var(--c-ok)}.wizard-body{padding:28px 32px;min-height:260px}.wizard-icon{font-size:36px;margin-bottom:12px}.wizard-title{font-size:18px;font-weight:700;color:var(--c-text);margin-bottom:6px;letter-spacing:-.01em}.wizard-desc{font-size:13px;color:var(--c-text-2);line-height:1.6;margin-bottom:24px}.wizard-checklist{display:flex;flex-direction:column;gap:10px}.wizard-check{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--c-text)}.wizard-check-num{width:24px;height:24px;border-radius:50%;background:var(--c-accent-bg);color:var(--c-accent);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;flex-shrink:0}.wizard-chips{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}.wizard-chip{display:flex;flex-direction:column;gap:4px;padding:14px;background:var(--c-surface);border:2px solid var(--c-border);border-radius:var(--r-l);cursor:pointer;text-align:left;transition:all var(--fast);color:var(--c-text);font-family:inherit;font-size:inherit}.wizard-chip:hover{border-color:var(--c-accent-bdr)}.wizard-chip.on{border-color:var(--c-accent);background:var(--c-accent-bg)}.wizard-chip-name{font-weight:600;font-size:13px}.wizard-chip-hint{font-size:11px;color:var(--c-text-3);line-height:1.3}.wizard-cred{background:var(--c-surface-alt);border:1px solid var(--c-border);border-radius:var(--r-l);padding:20px;margin-bottom:14px}.wizard-cred:last-child{margin-bottom:0}.wizard-cred-name{font-size:14px;font-weight:600;margin-bottom:14px;color:var(--c-text)}.wizard-review-block{margin-bottom:20px}.wizard-review-head{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--c-text-3);margin-bottom:10px}.wizard-kv{display:flex;justify-content:space-between;align-items:center;padding:7px 0;border-bottom:1px solid var(--c-border);font-size:12px}.wizard-kv span{color:var(--c-text-2)}.wizard-kv code{font-family:var(--mono);font-size:11px;color:var(--c-text);background:var(--c-surface-alt);padding:2px 6px;border-radius:4px}.wizard-platforms{display:flex;flex-wrap:wrap;gap:6px}.wizard-platform-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--c-surface-alt);border-radius:999px;font-size:12px;font-weight:500}.wizard-platform-pill .cmd{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--c-accent-bg);color:var(--c-accent);font-weight:500}.wizard-done{text-align:center;padding:20px 0}.wizard-pulse{animation:wiz-pulse 1.2s ease-in-out infinite}@keyframes wiz-pulse{0%,to{opacity:1}50%{opacity:.4}}.wizard-radio-group{display:flex;flex-direction:column;gap:8px}.wizard-radio{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:2px solid var(--c-border);border-radius:var(--r-m);cursor:pointer;transition:all var(--fast)}.wizard-radio:hover{border-color:var(--c-accent-bdr)}.wizard-radio.on{border-color:var(--c-accent);background:var(--c-accent-bg)}.wizard-radio input[type=radio]{margin-top:2px;accent-color:var(--c-accent)}.wizard-radio-body{display:flex;flex-direction:column;gap:2px}.wizard-radio-label{font-size:13px;font-weight:600;color:var(--c-text)}.wizard-radio-desc{font-size:12px;color:var(--c-text-3)}.wizard-nav{display:flex;align-items:center;padding:16px 32px 24px;gap:10px}.wizard-nav-spacer{flex:1}.conn{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:32px}.conn-card{width:100%;max-width:420px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r-xl);box-shadow:var(--shadow-l);padding:32px}.conn-logo{width:40px;height:40px;border-radius:var(--r-m);background:var(--c-accent);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.conn-logo svg{width:22px;height:22px;color:#fff}.conn-title{font-size:18px;font-weight:700;color:var(--c-text);margin-bottom:6px}.conn-desc{font-size:13px;color:var(--c-text-2);margin-bottom:24px;line-height:1.5}.conn-row{display:flex;gap:8px}.conn-input{flex:1;padding:8px 10px;font-family:var(--mono);font-size:13px;color:var(--c-text);background:var(--c-surface-alt);border:1px solid var(--c-border);border-radius:var(--r-s);outline:none;transition:border-color var(--fast)}.conn-input:focus{border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-bg)}.conn-status{font-size:12px;margin-top:10px;color:var(--c-text-3)}.conn-status.ok{color:var(--c-ok)}.conn-status.err{color:var(--c-err)}.lang-btn{padding:4px 10px;font-size:12px;font-weight:500;border-radius:var(--r-s);border:1px solid var(--c-border);background:var(--c-surface);color:var(--c-text-2);cursor:pointer;transition:all var(--fast)}.lang-btn:hover{border-color:var(--c-border-foc);color:var(--c-text)}.dark-btn{padding:4px 8px;border-radius:var(--r-s);border:1px solid var(--c-border);background:var(--c-surface);color:var(--c-text-2);cursor:pointer;font-size:14px;line-height:1;transition:all var(--fast);display:inline-flex;align-items:center;justify-content:center}.dark-btn:hover{border-color:var(--c-border-foc);color:var(--c-text)}@media(max-width:1024px){.ai-grid,.stats-grid{grid-template-columns:1fr}}@media(max-width:768px){.sidebar{display:none}.main{margin-left:0}.content{padding:20px 16px 40px}.header{padding:0 16px}.header-toolbar{padding:10px 16px}.platform-grid{grid-template-columns:1fr}.wizard-chips{grid-template-columns:1fr 1fr}}
|