orc-server 1.0.5
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/__tests__/auth.test.js +49 -0
- package/dist/__tests__/watcher.test.js +58 -0
- package/dist/claude/chatService.js +175 -0
- package/dist/claude/sessionBrowser.js +743 -0
- package/dist/claude/watcher.js +242 -0
- package/dist/config.js +44 -0
- package/dist/files/browser.js +227 -0
- package/dist/files/reader.js +159 -0
- package/dist/files/search.js +124 -0
- package/dist/git/gitHandler.js +177 -0
- package/dist/git/gitService.js +299 -0
- package/dist/git/index.js +8 -0
- package/dist/http/server.js +96 -0
- package/dist/index.js +77 -0
- package/dist/ssh/index.js +9 -0
- package/dist/ssh/sshHandler.js +205 -0
- package/dist/ssh/sshManager.js +329 -0
- package/dist/terminal/index.js +11 -0
- package/dist/terminal/localTerminalHandler.js +176 -0
- package/dist/terminal/localTerminalManager.js +497 -0
- package/dist/terminal/terminalWebSocket.js +136 -0
- package/dist/types.js +2 -0
- package/dist/utils/logger.js +42 -0
- package/dist/websocket/auth.js +18 -0
- package/dist/websocket/server.js +631 -0
- package/package.json +66 -0
- package/web-dist/assets/highlight-l0sNRNKZ.js +1 -0
- package/web-dist/assets/index-C8TJGN-T.css +41 -0
- package/web-dist/assets/index-DjLLxjMD.js +39 -0
- package/web-dist/assets/markdown-C_j0ZeeY.js +51 -0
- package/web-dist/assets/react-vendor-CqP5oCk4.js +9 -0
- package/web-dist/assets/xterm-BCk906R6.js +9 -0
- package/web-dist/icon-192.png +0 -0
- package/web-dist/icon-512.png +0 -0
- package/web-dist/index.html +23 -0
- package/web-dist/manifest.json +24 -0
- package/web-dist/sw.js +35 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Mock CONFIG before importing AuthManager
|
|
4
|
+
jest.mock('../config', () => ({
|
|
5
|
+
CONFIG: {
|
|
6
|
+
port: 8080,
|
|
7
|
+
authToken: '',
|
|
8
|
+
claudeHome: '/home/user/.claude',
|
|
9
|
+
maxFileSize: 10485760,
|
|
10
|
+
searchTimeout: 5000,
|
|
11
|
+
logLevel: 'info',
|
|
12
|
+
}
|
|
13
|
+
}));
|
|
14
|
+
const auth_1 = require("../websocket/auth");
|
|
15
|
+
const config_1 = require("../config");
|
|
16
|
+
describe('AuthManager', () => {
|
|
17
|
+
let authManager;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
authManager = new auth_1.AuthManager();
|
|
20
|
+
});
|
|
21
|
+
describe('validateToken', () => {
|
|
22
|
+
it('should return true for valid token', () => {
|
|
23
|
+
config_1.CONFIG.authToken = 'test-token';
|
|
24
|
+
const result = authManager.validateToken('test-token');
|
|
25
|
+
expect(result).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it('should return false for invalid token', () => {
|
|
28
|
+
config_1.CONFIG.authToken = 'test-token';
|
|
29
|
+
const result = authManager.validateToken('wrong-token');
|
|
30
|
+
expect(result).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('should return true when no token configured', () => {
|
|
33
|
+
config_1.CONFIG.authToken = '';
|
|
34
|
+
const result = authManager.validateToken('any-token');
|
|
35
|
+
expect(result).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('generateClientId', () => {
|
|
39
|
+
it('should generate unique client IDs', () => {
|
|
40
|
+
const id1 = authManager.generateClientId();
|
|
41
|
+
const id2 = authManager.generateClientId();
|
|
42
|
+
expect(id1).not.toBe(id2);
|
|
43
|
+
});
|
|
44
|
+
it('should generate IDs with correct format', () => {
|
|
45
|
+
const id = authManager.generateClientId();
|
|
46
|
+
expect(id).toMatch(/^client_\d+_[a-z0-9]+$/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const events_1 = require("events");
|
|
4
|
+
// Mock chokidar before importing ClaudeWatcher
|
|
5
|
+
jest.mock('chokidar', () => ({
|
|
6
|
+
default: {
|
|
7
|
+
watch: jest.fn(() => ({
|
|
8
|
+
on: jest.fn(),
|
|
9
|
+
close: jest.fn()
|
|
10
|
+
}))
|
|
11
|
+
}
|
|
12
|
+
}));
|
|
13
|
+
const watcher_1 = require("../claude/watcher");
|
|
14
|
+
describe('ClaudeWatcher', () => {
|
|
15
|
+
let watcher;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
watcher = new watcher_1.ClaudeWatcher();
|
|
18
|
+
});
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
watcher.stop();
|
|
21
|
+
});
|
|
22
|
+
it('should be an EventEmitter', () => {
|
|
23
|
+
expect(watcher).toBeInstanceOf(events_1.EventEmitter);
|
|
24
|
+
});
|
|
25
|
+
it('should emit user_input events', (done) => {
|
|
26
|
+
watcher.on('user_input', (data) => {
|
|
27
|
+
expect(data).toHaveProperty('message');
|
|
28
|
+
expect(data).toHaveProperty('timestamp');
|
|
29
|
+
expect(data).toHaveProperty('sessionId');
|
|
30
|
+
done();
|
|
31
|
+
});
|
|
32
|
+
// Simulate user input event
|
|
33
|
+
watcher.emit('user_input', {
|
|
34
|
+
message: 'test message',
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
sessionId: 'test-session',
|
|
37
|
+
project: '/test/project'
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it('should handle queue processing', async () => {
|
|
41
|
+
const events = [];
|
|
42
|
+
watcher.on('user_input', (data) => {
|
|
43
|
+
events.push(data);
|
|
44
|
+
});
|
|
45
|
+
// Simulate multiple rapid events
|
|
46
|
+
for (let i = 0; i < 5; i++) {
|
|
47
|
+
watcher.emit('user_input', {
|
|
48
|
+
message: `message ${i}`,
|
|
49
|
+
timestamp: Date.now(),
|
|
50
|
+
sessionId: 'test-session',
|
|
51
|
+
project: '/test/project'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Wait for processing
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
56
|
+
expect(events.length).toBe(5);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.claudeChatService = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const os_1 = require("os");
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
9
|
+
function resolveClaudePath() {
|
|
10
|
+
// 1. 尝试 which/where 从当前 PATH 解析
|
|
11
|
+
try {
|
|
12
|
+
const resolved = (0, child_process_1.execSync)('which claude', { encoding: 'utf-8' }).trim();
|
|
13
|
+
if (resolved)
|
|
14
|
+
return resolved;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// which 失败,继续检查常见位置
|
|
18
|
+
}
|
|
19
|
+
// 2. 检查常见安装位置
|
|
20
|
+
const home = (0, os_1.homedir)();
|
|
21
|
+
const candidates = [
|
|
22
|
+
(0, path_1.join)(home, '.local', 'bin', 'claude'),
|
|
23
|
+
(0, path_1.join)(home, '.npm-global', 'bin', 'claude'),
|
|
24
|
+
'/usr/local/bin/claude',
|
|
25
|
+
'/usr/bin/claude',
|
|
26
|
+
];
|
|
27
|
+
for (const p of candidates) {
|
|
28
|
+
if ((0, fs_1.existsSync)(p))
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
class ClaudeChatService {
|
|
34
|
+
constructor() {
|
|
35
|
+
// 存储正在运行的进程,按 sessionId 索引
|
|
36
|
+
this.runningProcesses = new Map();
|
|
37
|
+
this.claudePath = null;
|
|
38
|
+
this.claudePath = resolveClaudePath();
|
|
39
|
+
if (this.claudePath) {
|
|
40
|
+
logger_1.logger.info(`[ChatService] Claude CLI found at: ${this.claudePath}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
logger_1.logger.warn('[ChatService] Claude CLI not found. Reply functionality will be unavailable.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
getClaudePath() {
|
|
47
|
+
return this.claudePath;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 发送消息给 Claude CLI
|
|
51
|
+
* 使用 -p (print) 模式,消息通过 stdin 传入,响应通过 jsonl 文件监听获取
|
|
52
|
+
*/
|
|
53
|
+
async sendMessage(options) {
|
|
54
|
+
const { workspaceDirName, sessionId, newSessionId, message, cwd, allowedTools } = options;
|
|
55
|
+
// 确定实际使用的 sessionId
|
|
56
|
+
const effectiveSessionId = sessionId || newSessionId;
|
|
57
|
+
// 检查该会话是否已有进程在运行
|
|
58
|
+
if (effectiveSessionId && this.runningProcesses.has(effectiveSessionId)) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: 'A message is already being processed for this session',
|
|
62
|
+
sessionId: effectiveSessionId,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const args = ['-p'];
|
|
66
|
+
// 恢复现有会话
|
|
67
|
+
if (sessionId) {
|
|
68
|
+
args.push('--resume', sessionId);
|
|
69
|
+
}
|
|
70
|
+
// 新建会话并指定 ID
|
|
71
|
+
else if (newSessionId) {
|
|
72
|
+
args.push('--session-id', newSessionId);
|
|
73
|
+
}
|
|
74
|
+
// 添加允许的工具参数
|
|
75
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
76
|
+
args.push('--allowedTools', allowedTools.join(','));
|
|
77
|
+
}
|
|
78
|
+
logger_1.logger.info(`[ChatService] Sending message to Claude CLI`);
|
|
79
|
+
logger_1.logger.info(`[ChatService] Args: ${args.join(' ')}`);
|
|
80
|
+
logger_1.logger.info(`[ChatService] CWD: ${cwd || 'default'}`);
|
|
81
|
+
logger_1.logger.info(`[ChatService] Message length: ${message.length}`);
|
|
82
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
83
|
+
logger_1.logger.info(`[ChatService] Allowed tools: ${allowedTools.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
if (!this.claudePath) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: 'Claude CLI not found. Please install it and restart the server.',
|
|
89
|
+
sessionId: effectiveSessionId,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
const proc = (0, child_process_1.spawn)(this.claudePath, args, {
|
|
94
|
+
cwd: cwd || process.cwd(),
|
|
95
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
96
|
+
env: {
|
|
97
|
+
...process.env,
|
|
98
|
+
TERM: 'dumb',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
// 记录进程
|
|
102
|
+
if (effectiveSessionId) {
|
|
103
|
+
this.runningProcesses.set(effectiveSessionId, proc);
|
|
104
|
+
}
|
|
105
|
+
// 写入消息到 stdin
|
|
106
|
+
proc.stdin.write(message);
|
|
107
|
+
proc.stdin.end();
|
|
108
|
+
// 收集输出(主要用于错误检测)
|
|
109
|
+
let stdout = '';
|
|
110
|
+
let stderr = '';
|
|
111
|
+
proc.stdout.on('data', (data) => {
|
|
112
|
+
stdout += data.toString();
|
|
113
|
+
// 可以选择性地记录 stdout,但主要响应通过 jsonl 监听获取
|
|
114
|
+
logger_1.logger.debug(`[ChatService] stdout: ${data.toString().substring(0, 200)}...`);
|
|
115
|
+
});
|
|
116
|
+
proc.stderr.on('data', (data) => {
|
|
117
|
+
stderr += data.toString();
|
|
118
|
+
logger_1.logger.warn(`[ChatService] stderr: ${data.toString()}`);
|
|
119
|
+
});
|
|
120
|
+
proc.on('close', (code) => {
|
|
121
|
+
// 移除进程记录
|
|
122
|
+
if (effectiveSessionId) {
|
|
123
|
+
this.runningProcesses.delete(effectiveSessionId);
|
|
124
|
+
}
|
|
125
|
+
if (code === 0) {
|
|
126
|
+
logger_1.logger.info(`[ChatService] Claude CLI completed successfully`);
|
|
127
|
+
resolve({
|
|
128
|
+
success: true,
|
|
129
|
+
sessionId: effectiveSessionId,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
logger_1.logger.error(`[ChatService] Claude CLI exited with code ${code}`);
|
|
134
|
+
resolve({
|
|
135
|
+
success: false,
|
|
136
|
+
error: stderr || `Exit code: ${code}`,
|
|
137
|
+
sessionId: effectiveSessionId,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
proc.on('error', (err) => {
|
|
142
|
+
// 移除进程记录
|
|
143
|
+
if (effectiveSessionId) {
|
|
144
|
+
this.runningProcesses.delete(effectiveSessionId);
|
|
145
|
+
}
|
|
146
|
+
logger_1.logger.error(`[ChatService] Failed to spawn Claude CLI:`, err);
|
|
147
|
+
resolve({
|
|
148
|
+
success: false,
|
|
149
|
+
error: err.message,
|
|
150
|
+
sessionId: effectiveSessionId,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 检查指定会话是否有正在进行的消息处理
|
|
157
|
+
*/
|
|
158
|
+
isProcessing(sessionId) {
|
|
159
|
+
return this.runningProcesses.has(sessionId);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 取消正在进行的消息处理
|
|
163
|
+
*/
|
|
164
|
+
cancelMessage(sessionId) {
|
|
165
|
+
const proc = this.runningProcesses.get(sessionId);
|
|
166
|
+
if (proc) {
|
|
167
|
+
proc.kill('SIGTERM');
|
|
168
|
+
this.runningProcesses.delete(sessionId);
|
|
169
|
+
logger_1.logger.info(`[ChatService] Cancelled message processing for session ${sessionId}`);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.claudeChatService = new ClaudeChatService();
|