@ia-ccun/code-agent-claw 0.0.1 → 0.0.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/dist/public/index.html +6 -17
- package/package/.claude/plan/aicode-ui-npm.md +477 -0
- package/package/README.md +108 -0
- package/package/bin/cli.js +16 -0
- package/package/config/default.json +18 -0
- package/package/package.json +43 -0
- package/package/public/aicode.svg +1 -0
- package/package/public/index-v3.html +1757 -0
- package/package/public/index.html +1818 -0
- package/package/public/index_v3.html +1757 -0
- package/package/public/juejin.css +143 -0
- package/package/src/config.ts +239 -0
- package/package/src/index.ts +40 -0
- package/package/src/server/index.ts +103 -0
- package/package/src/server/routes.ts +342 -0
- package/package/src/server/websocket.ts +82 -0
- package/package/src/services/agent-rpc.ts +397 -0
- package/package/src/types/index.ts +60 -0
- package/package/src/utils/logger.ts +13 -0
- package/package/tsconfig.json +19 -0
- package/package.json +1 -1
- package/public/index.html +6 -17
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { AgentConfig, CommandResult, AgentEvent, EventCallback, AgentStatus } from '../types';
|
|
6
|
+
import { logger } from '../utils/logger';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* aicode RPC 服务
|
|
10
|
+
* 负责管理 aicode 子进程,并通过 JSONL 协议进行通信
|
|
11
|
+
*/
|
|
12
|
+
export class AgentRpcService {
|
|
13
|
+
private process: ChildProcess | null = null;
|
|
14
|
+
private rl: readline.Interface | null = null;
|
|
15
|
+
private pendingCommands: Map<string, {
|
|
16
|
+
resolve: (r: CommandResult) => void;
|
|
17
|
+
reject: (e: Error) => void;
|
|
18
|
+
}> = new Map();
|
|
19
|
+
private callbacks: Set<EventCallback> = new Set();
|
|
20
|
+
private config: AgentConfig | null = null;
|
|
21
|
+
private _status: AgentStatus = 'uninitialized';
|
|
22
|
+
private healthCheckInterval: NodeJS.Timeout | null = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 获取当前状态
|
|
26
|
+
*/
|
|
27
|
+
get status(): AgentStatus {
|
|
28
|
+
return this._status;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 获取当前配置
|
|
33
|
+
*/
|
|
34
|
+
get currentConfig(): AgentConfig | null {
|
|
35
|
+
return this.config;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 初始化 agent(保存配置并启动)
|
|
40
|
+
*/
|
|
41
|
+
async initialize(config: AgentConfig): Promise<CommandResult> {
|
|
42
|
+
logger.info('[AgentRpc] Initializing with config:', config);
|
|
43
|
+
|
|
44
|
+
// 验证命令是否存在
|
|
45
|
+
const commandPath = config.command;
|
|
46
|
+
if (!fs.existsSync(commandPath)) {
|
|
47
|
+
const error = `aicode command not found: ${commandPath}`;
|
|
48
|
+
logger.error('[AgentRpc]', error);
|
|
49
|
+
return { success: false, message: error };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 确保目录存在
|
|
53
|
+
this.ensureDir(config.sessionDir);
|
|
54
|
+
this.ensureDir(config.workingDir);
|
|
55
|
+
|
|
56
|
+
// 保存配置
|
|
57
|
+
this.config = config;
|
|
58
|
+
|
|
59
|
+
// 启动进程
|
|
60
|
+
return this.start();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 启动 aicode 进程
|
|
65
|
+
*/
|
|
66
|
+
async start(): Promise<CommandResult> {
|
|
67
|
+
if (!this.config) {
|
|
68
|
+
return { success: false, message: 'Config not set' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (this._status === 'running') {
|
|
72
|
+
return { success: true, message: 'Already running' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this._status = 'starting';
|
|
76
|
+
logger.info('[AgentRpc] Starting aicode process...');
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// 构建命令参数
|
|
80
|
+
const args: string[] = [
|
|
81
|
+
'--mode', 'rpc',
|
|
82
|
+
'--provider', this.config.provider,
|
|
83
|
+
'--model', this.config.model,
|
|
84
|
+
'--session-dir', this.config.sessionDir
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// noSession 模式下不传 session 参数或使用特殊处理
|
|
88
|
+
if (!this.config.noSession) {
|
|
89
|
+
// 如果需要会话模式,可以在这里添加
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logger.info(`[AgentRpc] Command: ${this.config.command} ${args.join(' ')}`);
|
|
93
|
+
logger.info(`[AgentRpc] Working dir: ${this.config.workingDir}`);
|
|
94
|
+
|
|
95
|
+
// 启动子进程
|
|
96
|
+
this.process = spawn(this.config.command, args, {
|
|
97
|
+
cwd: this.config.workingDir,
|
|
98
|
+
env: { ...process.env },
|
|
99
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 创建 readline 接口监听 stdout
|
|
103
|
+
this.rl = readline.createInterface({
|
|
104
|
+
input: this.process.stdout!,
|
|
105
|
+
crlfDelay: Infinity
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 监听 stdout 事件
|
|
109
|
+
this.rl.on('line', (line: string) => {
|
|
110
|
+
this.handleEvent(line);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 监听 stderr
|
|
114
|
+
this.process.stderr?.on('data', (data: Buffer) => {
|
|
115
|
+
const line = data.toString().replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
|
116
|
+
if (line.trim()) {
|
|
117
|
+
logger.warn(`[aicode-stderr] ${line}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// 监听进程退出
|
|
122
|
+
this.process.on('exit', (code, signal) => {
|
|
123
|
+
logger.info(`[AgentRpc] Process exited with code ${code}, signal ${signal}`);
|
|
124
|
+
this._status = 'stopped';
|
|
125
|
+
this.stopHealthCheck();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
this.process.on('error', (err) => {
|
|
129
|
+
logger.error('[AgentRpc] Process error:', err);
|
|
130
|
+
this._status = 'error';
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// 启动健康检查
|
|
134
|
+
this.startHealthCheck();
|
|
135
|
+
|
|
136
|
+
this._status = 'running';
|
|
137
|
+
logger.info('[AgentRpc] aicode process started successfully');
|
|
138
|
+
|
|
139
|
+
return { success: true, message: 'Agent started' };
|
|
140
|
+
} catch (error: any) {
|
|
141
|
+
this._status = 'error';
|
|
142
|
+
logger.error('[AgentRpc] Failed to start:', error);
|
|
143
|
+
return { success: false, message: error.message };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 处理事件
|
|
149
|
+
*/
|
|
150
|
+
private handleEvent(jsonLine: string): void {
|
|
151
|
+
try {
|
|
152
|
+
const trimmed = jsonLine.trim();
|
|
153
|
+
if (!trimmed || !trimmed.startsWith('{')) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const event = JSON.parse(jsonLine);
|
|
158
|
+
const type = event.type;
|
|
159
|
+
|
|
160
|
+
logger.debug(`[AgentRpc] <<< type=${type}`);
|
|
161
|
+
|
|
162
|
+
// 处理命令响应 - RPC 模式特有
|
|
163
|
+
if (type === 'response') {
|
|
164
|
+
this.handleCommandResponse(event);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 构建 AgentEvent
|
|
169
|
+
const agentEvent: AgentEvent = {
|
|
170
|
+
type,
|
|
171
|
+
data: event
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (type === 'message_update') {
|
|
175
|
+
const msgEvent = event.assistantMessageEvent;
|
|
176
|
+
if (msgEvent) {
|
|
177
|
+
agentEvent.messageType = msgEvent.type;
|
|
178
|
+
agentEvent.delta = msgEvent.delta;
|
|
179
|
+
}
|
|
180
|
+
} else if (type === 'agent_end') {
|
|
181
|
+
agentEvent.success = true;
|
|
182
|
+
agentEvent.usage = event.usage;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 通知回调
|
|
186
|
+
this.callbacks.forEach(cb => {
|
|
187
|
+
try {
|
|
188
|
+
cb(agentEvent);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
logger.error('[AgentRpc] Event callback error:', e);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
} catch (e) {
|
|
194
|
+
// 忽略解析错误
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 处理命令响应
|
|
200
|
+
*/
|
|
201
|
+
private handleCommandResponse(event: any): void {
|
|
202
|
+
const commandId = event.id;
|
|
203
|
+
const commandType = event.command;
|
|
204
|
+
const success = event.success;
|
|
205
|
+
|
|
206
|
+
logger.info(`[AgentRpc] Command response: id=${commandId}, command=${commandType}, success=${success}`);
|
|
207
|
+
|
|
208
|
+
const pending = this.pendingCommands.get(commandId);
|
|
209
|
+
if (pending) {
|
|
210
|
+
this.pendingCommands.delete(commandId);
|
|
211
|
+
if (success) {
|
|
212
|
+
pending.resolve({ success: true, message: commandType });
|
|
213
|
+
} else {
|
|
214
|
+
pending.resolve({ success: false, message: event.error || 'Command failed' });
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
logger.warn(`[AgentRpc] No pending command for id: ${commandId}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 发送提示
|
|
223
|
+
*/
|
|
224
|
+
async sendPrompt(message: string): Promise<CommandResult> {
|
|
225
|
+
return this.sendCommand('prompt', message);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 发送引导消息
|
|
230
|
+
*/
|
|
231
|
+
async sendSteer(message: string): Promise<CommandResult> {
|
|
232
|
+
return this.sendCommand('steer', message);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 发送后续消息
|
|
237
|
+
*/
|
|
238
|
+
async sendFollowUp(message: string): Promise<CommandResult> {
|
|
239
|
+
return this.sendCommand('follow_up', message);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 中止操作
|
|
244
|
+
*/
|
|
245
|
+
async sendAbort(): Promise<CommandResult> {
|
|
246
|
+
return this.sendCommand('abort');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 创建新会话
|
|
251
|
+
*/
|
|
252
|
+
async newSession(parentSession?: string): Promise<CommandResult> {
|
|
253
|
+
return this.sendCommand('new_session', undefined, parentSession);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 发送命令
|
|
258
|
+
*/
|
|
259
|
+
private sendCommand(type: string, message?: string, extra?: string): Promise<CommandResult> {
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
261
|
+
if (!this.isRunning() || !this.process || !this.process.stdin) {
|
|
262
|
+
return resolve({ success: false, message: 'Agent is not running' });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const command: any = { type };
|
|
266
|
+
if (message) command.message = message;
|
|
267
|
+
if (extra) command.parentSession = extra;
|
|
268
|
+
|
|
269
|
+
// 生成命令ID
|
|
270
|
+
const commandId = Date.now() + '-' + Math.floor(Math.random() * 10000);
|
|
271
|
+
command.id = commandId;
|
|
272
|
+
|
|
273
|
+
this.pendingCommands.set(commandId, { resolve, reject });
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
const jsonLine = JSON.stringify(command) + '\n';
|
|
277
|
+
logger.debug(`[AgentRpc] >>> ${jsonLine.trim()}`);
|
|
278
|
+
this.process.stdin.write(jsonLine);
|
|
279
|
+
} catch (e: any) {
|
|
280
|
+
this.pendingCommands.delete(commandId);
|
|
281
|
+
reject(e);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 检查是否运行中
|
|
288
|
+
*/
|
|
289
|
+
isRunning(): boolean {
|
|
290
|
+
return this._status === 'running' && this.process?.pid !== undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 停止 agent
|
|
295
|
+
*/
|
|
296
|
+
stop(): void {
|
|
297
|
+
logger.info('[AgentRpc] Stopping agent...');
|
|
298
|
+
this._status = 'stopped';
|
|
299
|
+
this.stopHealthCheck();
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
if (this.process?.stdin) {
|
|
303
|
+
this.process.stdin.end();
|
|
304
|
+
}
|
|
305
|
+
if (this.process) {
|
|
306
|
+
this.process.kill('SIGTERM');
|
|
307
|
+
setTimeout(() => {
|
|
308
|
+
if (this.process && !this.process.killed) {
|
|
309
|
+
this.process.kill('SIGKILL');
|
|
310
|
+
}
|
|
311
|
+
}, 3000);
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {
|
|
314
|
+
logger.error('[AgentRpc] Error stopping:', e);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this.process = null;
|
|
318
|
+
this.rl = null;
|
|
319
|
+
logger.info('[AgentRpc] Agent stopped');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 注册事件回调
|
|
324
|
+
*/
|
|
325
|
+
registerCallback(callback: EventCallback): void {
|
|
326
|
+
this.callbacks.add(callback);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 移除事件回调
|
|
331
|
+
*/
|
|
332
|
+
removeCallback(callback: EventCallback): void {
|
|
333
|
+
this.callbacks.delete(callback);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 启动健康检查
|
|
338
|
+
*/
|
|
339
|
+
private startHealthCheck(): void {
|
|
340
|
+
this.stopHealthCheck();
|
|
341
|
+
|
|
342
|
+
this.healthCheckInterval = setInterval(async () => {
|
|
343
|
+
if (!this.isRunning() && this._status === 'running') {
|
|
344
|
+
logger.warn('[AgentRpc] Process died, auto-restarting...');
|
|
345
|
+
this._status = 'restarting';
|
|
346
|
+
|
|
347
|
+
// 自动重启
|
|
348
|
+
if (this.config) {
|
|
349
|
+
try {
|
|
350
|
+
await this.start();
|
|
351
|
+
logger.info('[AgentRpc] Auto-restart successful');
|
|
352
|
+
} catch (e) {
|
|
353
|
+
logger.error('[AgentRpc] Auto-restart failed:', e);
|
|
354
|
+
this._status = 'error';
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
this._status = 'stopped';
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}, 5000);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 停止健康检查
|
|
365
|
+
*/
|
|
366
|
+
private stopHealthCheck(): void {
|
|
367
|
+
if (this.healthCheckInterval) {
|
|
368
|
+
clearInterval(this.healthCheckInterval);
|
|
369
|
+
this.healthCheckInterval = null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* 确保目录存在
|
|
375
|
+
*/
|
|
376
|
+
private ensureDir(dirPath: string): void {
|
|
377
|
+
if (!fs.existsSync(dirPath)) {
|
|
378
|
+
logger.info(`[AgentRpc] Creating directory: ${dirPath}`);
|
|
379
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 单例实例
|
|
386
|
+
*/
|
|
387
|
+
let agentRpcService: AgentRpcService | null = null;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* 获取 AgentRpcService 单例
|
|
391
|
+
*/
|
|
392
|
+
export function getAgentRpcService(): AgentRpcService {
|
|
393
|
+
if (!agentRpcService) {
|
|
394
|
+
agentRpcService = new AgentRpcService();
|
|
395
|
+
}
|
|
396
|
+
return agentRpcService;
|
|
397
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface ServerConfig {
|
|
2
|
+
port: number;
|
|
3
|
+
host: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface AgentConfig {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
command: string;
|
|
9
|
+
provider: string;
|
|
10
|
+
model: string;
|
|
11
|
+
noSession: boolean;
|
|
12
|
+
sessionDir: string;
|
|
13
|
+
workingDir: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LoggingConfig {
|
|
17
|
+
level: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Config {
|
|
21
|
+
server: ServerConfig;
|
|
22
|
+
agent: AgentConfig;
|
|
23
|
+
logging: LoggingConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CommandResult {
|
|
27
|
+
success: boolean;
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AgentEvent {
|
|
32
|
+
type: string;
|
|
33
|
+
data?: any;
|
|
34
|
+
messageType?: string;
|
|
35
|
+
delta?: string;
|
|
36
|
+
success?: boolean;
|
|
37
|
+
usage?: {
|
|
38
|
+
completionTokens?: number;
|
|
39
|
+
promptTokens?: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type EventCallback = (event: AgentEvent) => void;
|
|
44
|
+
|
|
45
|
+
export type AgentStatus = 'uninitialized' | 'starting' | 'running' | 'stopped' | 'error' | 'restarting';
|
|
46
|
+
|
|
47
|
+
export interface ConfigRequest {
|
|
48
|
+
command?: string;
|
|
49
|
+
provider?: string;
|
|
50
|
+
model?: string;
|
|
51
|
+
noSession?: boolean;
|
|
52
|
+
sessionDir?: string;
|
|
53
|
+
workingDir?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ApiResponse<T = any> {
|
|
57
|
+
success: boolean;
|
|
58
|
+
message?: string;
|
|
59
|
+
data?: T;
|
|
60
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
|
|
3
|
+
export const logger = pino({
|
|
4
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
5
|
+
transport: process.env.NODE_ENV !== 'production' ? {
|
|
6
|
+
target: 'pino-pretty',
|
|
7
|
+
options: {
|
|
8
|
+
colorize: true,
|
|
9
|
+
translateTime: 'HH:MM:ss',
|
|
10
|
+
ignore: 'pid,hostname'
|
|
11
|
+
}
|
|
12
|
+
} : undefined
|
|
13
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -3,12 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>AICode
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<link rel="stylesheet" href="juejin.css">
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
6
|
+
<title>AICode Claw</title>
|
|
12
7
|
<style>
|
|
13
8
|
/* ========================================
|
|
14
9
|
ANTHROPIC STYLE - AI Agent Console
|
|
@@ -64,8 +59,8 @@
|
|
|
64
59
|
--space-2xl: 32px;
|
|
65
60
|
|
|
66
61
|
/* Typography - Inter for clean readability */
|
|
67
|
-
--font-display:
|
|
68
|
-
--font-mono: '
|
|
62
|
+
--font-display: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
63
|
+
--font-mono: 'SF Mono', Consolas, 'PingFang SC', 'Microsoft YaHei Mono', monospace;
|
|
69
64
|
|
|
70
65
|
/* Effects */
|
|
71
66
|
--shadow-sm: 0 1px 3px rgba(61, 61, 58, 0.08);
|
|
@@ -262,7 +257,7 @@
|
|
|
262
257
|
background: var(--bg-tertiary);
|
|
263
258
|
padding: 2px 6px;
|
|
264
259
|
border-radius: 4px;
|
|
265
|
-
font-family:
|
|
260
|
+
font-family: var(--font-mono);
|
|
266
261
|
font-size: 0.75rem;
|
|
267
262
|
color: var(--accent-green);
|
|
268
263
|
}
|
|
@@ -1075,7 +1070,7 @@
|
|
|
1075
1070
|
<div class="header-left">
|
|
1076
1071
|
<div class="logo">
|
|
1077
1072
|
<img class="logo-icon" src="aicode.svg" alt="AICode">
|
|
1078
|
-
<span class="logo-text">AICode
|
|
1073
|
+
<span class="logo-text">AICode Claw</span>
|
|
1079
1074
|
</div>
|
|
1080
1075
|
<div class="status-badge">
|
|
1081
1076
|
<div class="status-dot" id="statusDot"></div>
|
|
@@ -1163,7 +1158,7 @@
|
|
|
1163
1158
|
<div id="responseTab" class="panel-content">
|
|
1164
1159
|
<div class="empty-state" id="emptyState">
|
|
1165
1160
|
<img class="empty-logo" src="aicode.svg" alt="AICode">
|
|
1166
|
-
<div class="empty-title">你好,我是 AICode
|
|
1161
|
+
<div class="empty-title">你好,我是 AICode Claw</div>
|
|
1167
1162
|
<div class="empty-desc">我可以帮助你完成以下任务:</div>
|
|
1168
1163
|
<div class="empty-features">
|
|
1169
1164
|
<div class="feature-item"><span class="feature-icon">📝</span> 代码编写与调试</div>
|
|
@@ -1236,12 +1231,6 @@
|
|
|
1236
1231
|
<script>
|
|
1237
1232
|
const API_BASE = '';
|
|
1238
1233
|
let messages = [];
|
|
1239
|
-
|
|
1240
|
-
// Configure marked
|
|
1241
|
-
marked.setOptions({
|
|
1242
|
-
breaks: true,
|
|
1243
|
-
gfm: true
|
|
1244
|
-
});
|
|
1245
1234
|
let isStreaming = false;
|
|
1246
1235
|
let lastRenderTime = 0;
|
|
1247
1236
|
const RENDER_THROTTLE = 100;
|