agentstudio 0.1.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.
Files changed (115) hide show
  1. package/.env +15 -0
  2. package/README.md +85 -0
  3. package/dist/bin/agentstudio.d.ts +3 -0
  4. package/dist/bin/agentstudio.d.ts.map +1 -0
  5. package/dist/bin/agentstudio.js +141 -0
  6. package/dist/bin/agentstudio.js.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +87 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/middleware/auth.d.ts +7 -0
  12. package/dist/middleware/auth.d.ts.map +1 -0
  13. package/dist/middleware/auth.js +21 -0
  14. package/dist/middleware/auth.js.map +1 -0
  15. package/dist/routes/agents.d.ts +4 -0
  16. package/dist/routes/agents.d.ts.map +1 -0
  17. package/dist/routes/agents.js +804 -0
  18. package/dist/routes/agents.js.map +1 -0
  19. package/dist/routes/auth.d.ts +4 -0
  20. package/dist/routes/auth.d.ts.map +1 -0
  21. package/dist/routes/auth.js +60 -0
  22. package/dist/routes/auth.js.map +1 -0
  23. package/dist/routes/files.d.ts +4 -0
  24. package/dist/routes/files.d.ts.map +1 -0
  25. package/dist/routes/files.js +301 -0
  26. package/dist/routes/files.js.map +1 -0
  27. package/dist/routes/mcp.d.ts +4 -0
  28. package/dist/routes/mcp.d.ts.map +1 -0
  29. package/dist/routes/mcp.js +652 -0
  30. package/dist/routes/mcp.js.map +1 -0
  31. package/dist/routes/media.d.ts +5 -0
  32. package/dist/routes/media.d.ts.map +1 -0
  33. package/dist/routes/media.js +117 -0
  34. package/dist/routes/media.js.map +1 -0
  35. package/dist/routes/slides.d.ts +4 -0
  36. package/dist/routes/slides.d.ts.map +1 -0
  37. package/dist/routes/slides.js +146 -0
  38. package/dist/routes/slides.js.map +1 -0
  39. package/dist/services/claudeSession.d.ts +83 -0
  40. package/dist/services/claudeSession.d.ts.map +1 -0
  41. package/dist/services/claudeSession.js +255 -0
  42. package/dist/services/claudeSession.js.map +1 -0
  43. package/dist/services/messageQueue.d.ts +31 -0
  44. package/dist/services/messageQueue.d.ts.map +1 -0
  45. package/dist/services/messageQueue.js +67 -0
  46. package/dist/services/messageQueue.js.map +1 -0
  47. package/dist/services/sessionManager.d.ts +132 -0
  48. package/dist/services/sessionManager.d.ts.map +1 -0
  49. package/dist/services/sessionManager.js +439 -0
  50. package/dist/services/sessionManager.js.map +1 -0
  51. package/dist/types/claude-history.d.ts +48 -0
  52. package/dist/types/claude-history.d.ts.map +1 -0
  53. package/dist/types/claude-history.js +2 -0
  54. package/dist/types/claude-history.js.map +1 -0
  55. package/dist/types/claude-versions.d.ts +31 -0
  56. package/dist/types/claude-versions.d.ts.map +1 -0
  57. package/dist/types/claude-versions.js +2 -0
  58. package/dist/types/claude-versions.js.map +1 -0
  59. package/dist/types/commands.d.ts +32 -0
  60. package/dist/types/commands.d.ts.map +1 -0
  61. package/dist/types/commands.js +2 -0
  62. package/dist/types/commands.js.map +1 -0
  63. package/dist/types/index.d.ts +81 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +150 -0
  66. package/dist/types/index.js.map +1 -0
  67. package/dist/types/subagents.d.ts +88 -0
  68. package/dist/types/subagents.d.ts.map +1 -0
  69. package/dist/types/subagents.js +2 -0
  70. package/dist/types/subagents.js.map +1 -0
  71. package/dist/utils/agentStorage.d.ts +19 -0
  72. package/dist/utils/agentStorage.d.ts.map +1 -0
  73. package/dist/utils/agentStorage.js +110 -0
  74. package/dist/utils/agentStorage.js.map +1 -0
  75. package/dist/utils/claudeVersionStorage.d.ts +33 -0
  76. package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
  77. package/dist/utils/claudeVersionStorage.js +168 -0
  78. package/dist/utils/claudeVersionStorage.js.map +1 -0
  79. package/dist/utils/jwt.d.ts +15 -0
  80. package/dist/utils/jwt.d.ts.map +1 -0
  81. package/dist/utils/jwt.js +28 -0
  82. package/dist/utils/jwt.js.map +1 -0
  83. package/dist/utils/projectMetadataStorage.d.ts +21 -0
  84. package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
  85. package/dist/utils/projectMetadataStorage.js +68 -0
  86. package/dist/utils/projectMetadataStorage.js.map +1 -0
  87. package/frontend/dist/index.html +86 -0
  88. package/package.json +66 -0
  89. package/src/bin/agentstudio.ts +161 -0
  90. package/src/index.ts +100 -0
  91. package/src/middleware/auth.ts +26 -0
  92. package/src/routes/agents.ts +885 -0
  93. package/src/routes/auth.ts +73 -0
  94. package/src/routes/commands.ts.bak +441 -0
  95. package/src/routes/files.ts +352 -0
  96. package/src/routes/mcp.ts +751 -0
  97. package/src/routes/media.ts +140 -0
  98. package/src/routes/projects.ts.bak +601 -0
  99. package/src/routes/sessions.ts.bak +809 -0
  100. package/src/routes/settings.ts.bak +718 -0
  101. package/src/routes/slides.ts +170 -0
  102. package/src/routes/subagents.ts.bak +364 -0
  103. package/src/services/claudeSession.ts +293 -0
  104. package/src/services/messageQueue.ts +71 -0
  105. package/src/services/sessionManager.ts +532 -0
  106. package/src/types/claude-history.ts +50 -0
  107. package/src/types/claude-versions.ts +33 -0
  108. package/src/types/commands.ts +35 -0
  109. package/src/types/index.ts +248 -0
  110. package/src/types/subagents.ts +106 -0
  111. package/src/utils/agentStorage.ts +126 -0
  112. package/src/utils/claudeVersionStorage.ts +199 -0
  113. package/src/utils/jwt.ts +36 -0
  114. package/src/utils/projectMetadataStorage.ts +86 -0
  115. package/tsconfig.json +26 -0
@@ -0,0 +1,293 @@
1
+ import { query, Options } from '@anthropic-ai/claude-code';
2
+ import { MessageQueue } from './messageQueue.js';
3
+
4
+ /**
5
+ * Claude 会话包装器 - 使用 Streaming Input Mode
6
+ * 一次构造 query,通过 async generator 持续提供用户输入
7
+ */
8
+ export class ClaudeSession {
9
+ private agentId: string;
10
+ private claudeSessionId: string | null = null;
11
+ private messageQueue: MessageQueue;
12
+ private queryStream: AsyncIterable<any> | null = null;
13
+ private queryObject: any | null = null; // 保存 query 对象(带有 interrupt 方法)
14
+ private isActive = true;
15
+ private lastActivity = Date.now();
16
+ private options: Options;
17
+ private isInitialized = false;
18
+ private resumeSessionId: string | null = null;
19
+ private projectPath: string | null = null;
20
+ private claudeVersionId: string | undefined = undefined;
21
+
22
+ // 响应分发器相关
23
+ private responseCallbacks: Map<string, (response: any) => void> = new Map();
24
+ private nextRequestId = 0;
25
+ private isBackgroundRunning = false;
26
+
27
+ constructor(agentId: string, options: Options, resumeSessionId?: string, claudeVersionId?: string) {
28
+ console.log(`🔧 [DEBUG] ClaudeSession constructor started for agent: ${agentId}, resumeSessionId: ${resumeSessionId}, claudeVersionId: ${claudeVersionId}`);
29
+ this.agentId = agentId;
30
+ this.options = { ...options };
31
+ this.messageQueue = new MessageQueue();
32
+ this.resumeSessionId = resumeSessionId || null;
33
+ this.claudeVersionId = claudeVersionId;
34
+ // 从 options.cwd 获取项目路径
35
+ this.projectPath = options.cwd || null;
36
+
37
+ // 如果提供了 resumeSessionId,设置为当前 claudeSessionId
38
+ if (this.resumeSessionId) {
39
+ this.claudeSessionId = this.resumeSessionId;
40
+ console.log(`🔧 [DEBUG] Set claudeSessionId to resumeSessionId: ${this.claudeSessionId}`);
41
+ }
42
+
43
+ console.log(`🔧 [DEBUG] About to call initializeClaudeStream for agent: ${agentId}`);
44
+ // 立即初始化 Claude 流(Streaming Input Mode)
45
+ this.initializeClaudeStream();
46
+ console.log(`🔧 [DEBUG] ClaudeSession constructor completed for agent: ${agentId}`);
47
+ }
48
+
49
+ /**
50
+ * 获取 Claude SDK 返回的真实 sessionId
51
+ */
52
+ getClaudeSessionId(): string | null {
53
+ return this.claudeSessionId;
54
+ }
55
+
56
+ /**
57
+ * 设置 Claude sessionId
58
+ */
59
+ setClaudeSessionId(sessionId: string): void {
60
+ this.claudeSessionId = sessionId;
61
+ }
62
+
63
+ /**
64
+ * 获取 agentId
65
+ */
66
+ getAgentId(): string {
67
+ return this.agentId;
68
+ }
69
+
70
+ /**
71
+ * 获取项目路径
72
+ */
73
+ getProjectPath(): string | null {
74
+ return this.projectPath;
75
+ }
76
+
77
+ /**
78
+ * 获取 Claude 版本ID
79
+ */
80
+ getClaudeVersionId(): string | undefined {
81
+ return this.claudeVersionId;
82
+ }
83
+
84
+ /**
85
+ * 检查会话是否活跃
86
+ */
87
+ isSessionActive(): boolean {
88
+ return this.isActive;
89
+ }
90
+
91
+
92
+ /**
93
+ * 初始化 Claude 流 - 只调用一次,启动持续会话
94
+ */
95
+ private initializeClaudeStream(): void {
96
+ if (this.isInitialized) {
97
+ return;
98
+ }
99
+
100
+ try {
101
+ if (this.resumeSessionId) {
102
+ console.log(`🔄 Resuming persistent Claude session ${this.resumeSessionId} for agent: ${this.agentId}`);
103
+ } else {
104
+ console.log(`🆕 Starting new persistent Claude session for agent: ${this.agentId}`);
105
+ }
106
+
107
+ // 如果有 resumeSessionId,添加到 options 中
108
+ const queryOptions = { ...this.options };
109
+ if (this.resumeSessionId) {
110
+ queryOptions.resume = this.resumeSessionId;
111
+ console.log(`🔄 Setting resume parameter: ${this.resumeSessionId} for agent: ${this.agentId}`);
112
+ console.log(`📋 Full queryOptions for resume:`, JSON.stringify({
113
+ ...queryOptions,
114
+ customSystemPrompt: queryOptions.customSystemPrompt ? `${queryOptions.customSystemPrompt.substring(0, 100)}...` : 'none'
115
+ }, null, 2));
116
+ } else {
117
+ console.log(`🆕 No resume parameter, starting fresh session for agent: ${this.agentId}`);
118
+ }
119
+
120
+ // 使用 Streaming Input Mode - 只构造一次 query
121
+ // 这个 query 对象会持续运行,通过 messageQueue 接收新的用户输入
122
+ console.log(`🔧 [DEBUG] About to call query() for agent: ${this.agentId}`);
123
+ console.log(`🔧 [DEBUG] MessageQueue ready: ${!!this.messageQueue}, queryOptions ready: ${!!queryOptions}`);
124
+
125
+ // query 返回的对象既是 AsyncGenerator 又有 interrupt() 等方法
126
+ this.queryObject = query({
127
+ prompt: this.messageQueue, // messageQueue 实现了 AsyncIterable
128
+ options: queryOptions
129
+ });
130
+
131
+ // queryObject 本身就是 AsyncIterable,可以直接赋值给 queryStream
132
+ this.queryStream = this.queryObject;
133
+
134
+ console.log(`🔧 [DEBUG] query() called, queryObject created: ${!!this.queryObject}, has interrupt: ${typeof this.queryObject?.interrupt === 'function'} for agent: ${this.agentId}`);
135
+
136
+ this.isInitialized = true;
137
+ const action = this.resumeSessionId ? 'Resumed' : 'Initialized';
138
+ console.log(`✨ ${action} persistent Claude streaming session for agent: ${this.agentId}`);
139
+ } catch (error) {
140
+ console.error(`Failed to initialize Claude session for agent ${this.agentId}:`, error);
141
+ this.isActive = false;
142
+ throw error;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * 发送消息到 Claude 会话,返回请求ID用于响应分发
148
+ * @param message 要发送的消息
149
+ * @param responseCallback 响应回调函数
150
+ */
151
+ async sendMessage(message: any, responseCallback: (response: any) => void): Promise<string> {
152
+ console.log(`🔧 [DEBUG] sendMessage called for agent: ${this.agentId}, isActive: ${this.isActive}, isBackgroundRunning: ${this.isBackgroundRunning}`);
153
+
154
+ if (!this.isActive) {
155
+ throw new Error('Session is not active');
156
+ }
157
+
158
+ this.lastActivity = Date.now();
159
+
160
+ // 生成唯一的请求ID
161
+ const requestId = `req_${this.nextRequestId++}_${Date.now()}`;
162
+ console.log(`🔧 [DEBUG] Generated requestId: ${requestId} for agent: ${this.agentId}`);
163
+
164
+ // 注册响应回调
165
+ this.responseCallbacks.set(requestId, responseCallback);
166
+ console.log(`🔧 [DEBUG] Registered callback for requestId: ${requestId}, total callbacks: ${this.responseCallbacks.size}`);
167
+
168
+ // 启动后台响应处理器(如果还没有启动)
169
+ if (!this.isBackgroundRunning) {
170
+ console.log(`🔧 [DEBUG] Starting background response handler for agent: ${this.agentId}`);
171
+ this.startBackgroundResponseHandler();
172
+ } else {
173
+ console.log(`🔧 [DEBUG] Background response handler already running for agent: ${this.agentId}`);
174
+ }
175
+
176
+ // 将消息推送到队列中,Claude 会通过 async generator 接收
177
+ console.log(`🔧 [DEBUG] About to push message to queue for agent: ${this.agentId}, queueSize before: ${this.messageQueue.size()}`);
178
+ this.messageQueue.push(message);
179
+ console.log(`📨 Queued message for agent: ${this.agentId}, requestId: ${requestId}, queueSize: ${this.messageQueue.size()}`);
180
+
181
+ return requestId;
182
+ }
183
+
184
+ /**
185
+ * 启动后台响应处理器,按顺序分发响应给各个请求
186
+ */
187
+ private async startBackgroundResponseHandler(): Promise<void> {
188
+ if (this.isBackgroundRunning || !this.queryStream) {
189
+ return;
190
+ }
191
+
192
+ this.isBackgroundRunning = true;
193
+ console.log(`🚀 Starting background response handler for agent: ${this.agentId}`);
194
+
195
+ try {
196
+ console.log(`🔧 [DEBUG] About to start for-await loop for agent: ${this.agentId}, queryStream: ${!!this.queryStream}`);
197
+
198
+ for await (const response of this.queryStream) {
199
+ console.log(`🔧 [DEBUG] Received response in background handler for agent: ${this.agentId}, type: ${response.type}`);
200
+ this.lastActivity = Date.now();
201
+
202
+ // 捕获 SDK 返回的 sessionId
203
+ const sessionId = response.session_id || response.sessionId;
204
+ if (response.type === 'system' && response.subtype === 'init' && sessionId) {
205
+ this.claudeSessionId = sessionId;
206
+ console.log(`📝 Captured Claude sessionId: ${this.claudeSessionId} for agent: ${this.agentId}`);
207
+ }
208
+
209
+ // 获取当前最早的请求ID(FIFO队列)
210
+ const requestIds = Array.from(this.responseCallbacks.keys());
211
+ const currentRequestId = requestIds.length > 0 ? requestIds[0] : null;
212
+
213
+ console.log(`🔧 [DEBUG] Current pending requests: ${requestIds.length}, processing: ${currentRequestId}`);
214
+
215
+ // 分发响应给对应的请求
216
+ if (currentRequestId && this.responseCallbacks.has(currentRequestId)) {
217
+ const callback = this.responseCallbacks.get(currentRequestId)!;
218
+ callback(response);
219
+
220
+ // 如果是 result 事件,该请求完成,从队列中移除
221
+ if (response.type === 'result') {
222
+ console.log(`✅ Request ${currentRequestId} completed, removing from queue`);
223
+ this.responseCallbacks.delete(currentRequestId);
224
+ }
225
+ } else {
226
+ console.log(`⚠️ No callback found for current request: ${currentRequestId}`);
227
+ }
228
+ }
229
+
230
+ console.log(`🔧 [DEBUG] For-await loop ended for agent: ${this.agentId}`);
231
+ this.isBackgroundRunning = false; // 重要:循环结束时重置状态
232
+ } catch (error) {
233
+ console.error(`Error in background response handler for agent ${this.agentId}:`, error);
234
+ this.isActive = false;
235
+ this.isBackgroundRunning = false;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 取消指定请求的回调
241
+ */
242
+ cancelRequest(requestId: string): void {
243
+ if (this.responseCallbacks.has(requestId)) {
244
+ this.responseCallbacks.delete(requestId);
245
+ console.log(`🧹 Cleaned up request callback: ${requestId}`);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * 检查会话是否空闲
251
+ */
252
+ isIdle(idleTimeoutMs: number = 30 * 60 * 1000): boolean {
253
+ return Date.now() - this.lastActivity > idleTimeoutMs;
254
+ }
255
+
256
+ /**
257
+ * 获取最后活动时间
258
+ */
259
+ getLastActivity(): number {
260
+ return this.lastActivity;
261
+ }
262
+
263
+ /**
264
+ * 中断当前正在执行的 Claude 请求
265
+ * 调用 query 对象的 interrupt() 方法停止当前任务
266
+ */
267
+ async interrupt(): Promise<void> {
268
+ console.log(`🛑 Interrupting Claude session for agent: ${this.agentId}, sessionId: ${this.claudeSessionId}`);
269
+
270
+ if (!this.queryObject || typeof this.queryObject.interrupt !== 'function') {
271
+ throw new Error('Query object does not support interrupt');
272
+ }
273
+
274
+ try {
275
+ await this.queryObject.interrupt();
276
+ console.log(`✅ Successfully interrupted Claude session for agent: ${this.agentId}, sessionId: ${this.claudeSessionId}`);
277
+ } catch (error) {
278
+ console.error(`❌ Failed to interrupt Claude session for agent ${this.agentId}:`, error);
279
+ throw error;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * 关闭会话
285
+ */
286
+ async close(): Promise<void> {
287
+ console.log(`🔚 Closing Claude session for agent: ${this.agentId}, sessionId: ${this.claudeSessionId}`);
288
+ this.isActive = false;
289
+
290
+ // 结束消息队列,这会让 async generator 完成
291
+ this.messageQueue.end();
292
+ }
293
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 异步消息队列,用于持续向 Claude 提供用户输入
3
+ * 实现 Streaming Input Mode 的核心组件
4
+ */
5
+ export class MessageQueue {
6
+ private queue: any[] = [];
7
+ private resolvers: Array<(value: any) => void> = [];
8
+ private isEnded = false;
9
+
10
+ /**
11
+ * 异步迭代器实现,用于 Claude SDK 的 streaming input
12
+ */
13
+ async *[Symbol.asyncIterator](): AsyncIterableIterator<any> {
14
+ while (!this.isEnded || this.queue.length > 0) {
15
+ if (this.queue.length > 0) {
16
+ yield this.queue.shift();
17
+ } else if (!this.isEnded) {
18
+ // 等待新消息
19
+ yield new Promise<any>(resolve => this.resolvers.push(resolve));
20
+ }
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 向队列中添加消息
26
+ * @param message 要添加的消息
27
+ */
28
+ push(message: any): void {
29
+ console.log(`🔧 [QUEUE] push called, isEnded: ${this.isEnded}, resolvers: ${this.resolvers.length}, queue: ${this.queue.length}`);
30
+
31
+ if (this.isEnded) {
32
+ console.warn('Cannot push to ended message queue');
33
+ return;
34
+ }
35
+
36
+ if (this.resolvers.length > 0) {
37
+ // 有等待的消费者,直接解析
38
+ console.log(`🔧 [QUEUE] Resolving waiting consumer`);
39
+ const resolve = this.resolvers.shift()!;
40
+ resolve(message);
41
+ } else {
42
+ // 没有等待的消费者,加入队列
43
+ console.log(`🔧 [QUEUE] Adding to queue`);
44
+ this.queue.push(message);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * 结束队列,不再接收新消息
50
+ */
51
+ end(): void {
52
+ this.isEnded = true;
53
+ // 解析所有等待的 promise
54
+ this.resolvers.forEach(resolve => resolve(null));
55
+ this.resolvers = [];
56
+ }
57
+
58
+ /**
59
+ * 检查队列是否已结束
60
+ */
61
+ isFinished(): boolean {
62
+ return this.isEnded;
63
+ }
64
+
65
+ /**
66
+ * 获取当前队列长度
67
+ */
68
+ size(): number {
69
+ return this.queue.length;
70
+ }
71
+ }