claude-ws 0.3.94 → 0.3.95
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 +3 -17
- package/locales/de.json +4 -2
- package/locales/en.json +4 -2
- package/locales/es.json +4 -2
- package/locales/fr.json +4 -2
- package/locales/ja.json +4 -2
- package/locales/ko.json +4 -2
- package/locales/vi.json +4 -2
- package/locales/zh.json +4 -2
- package/next.config.ts +41 -15
- package/package.json +25 -20
- package/server.ts +91 -2
- package/src/app/[locale]/layout.tsx +4 -7
- package/src/app/[locale]/page.tsx +25 -5
- package/src/app/api/attempts/route.ts +1 -1
- package/src/app/api/uploads/[fileId]/route.ts +7 -7
- package/src/app/api/uploads/route.ts +4 -4
- package/src/app/globals.css +24 -0
- package/src/components/access-anywhere/api-access-key-setup-modal.tsx +122 -110
- package/src/components/auth/agent-provider-dialog.tsx +552 -576
- package/src/components/editor/code-editor-with-definitions.tsx +3 -2
- package/src/components/editor/code-editor-with-inline-edit.tsx +2 -1
- package/src/components/editor/code-mirror-editor.tsx +3 -2
- package/src/components/editor/extensions/cursor-selection-theme.ts +90 -0
- package/src/components/editor/extensions/inline-edit.ts +9 -4
- package/src/components/editor/inline-edit-dialog.tsx +11 -15
- package/src/components/header.tsx +45 -5
- package/src/components/kanban/board.tsx +467 -36
- package/src/components/kanban/column.tsx +31 -35
- package/src/components/providers/unified-setup-provider.tsx +166 -0
- package/src/components/settings/settings-page.tsx +110 -2
- package/src/components/setup/unified-setup-wizard.tsx +251 -0
- package/src/components/sidebar/sidebar-panel.tsx +29 -8
- package/src/components/task/conversation-view.tsx +160 -262
- package/src/components/task/floating-chat-window.tsx +32 -9
- package/src/components/task/floating-chat-windows-container.tsx +74 -0
- package/src/components/task/interactive-command/question-prompt.tsx +4 -1
- package/src/components/task/prompt-input.tsx +41 -8
- package/src/components/task/task-detail-panel.tsx +22 -11
- package/src/components/terminal/terminal-context-menu.tsx +54 -0
- package/src/components/terminal/terminal-instance.tsx +526 -0
- package/src/components/terminal/terminal-panel.tsx +261 -0
- package/src/components/terminal/terminal-shortcut-bar.tsx +236 -0
- package/src/components/terminal/terminal-tab-bar.tsx +136 -0
- package/src/components/ui/detachable-window.tsx +69 -12
- package/src/hooks/use-attempt-stream.ts +46 -2
- package/src/hooks/use-mobile-viewport.ts +24 -0
- package/src/lib/agent-manager.ts +36 -4
- package/src/lib/file-utils.ts +5 -4
- package/src/lib/session-manager.ts +23 -17
- package/src/lib/terminal-manager.ts +161 -0
- package/src/lib/terminal-shell-detect.ts +61 -0
- package/src/stores/project-store.ts +14 -2
- package/src/stores/terminal-store.ts +304 -0
- package/src/stores/tunnel-store.ts +0 -3
package/README.md
CHANGED
|
@@ -35,7 +35,9 @@ claude-ws
|
|
|
35
35
|
# Option 3: From source
|
|
36
36
|
git clone https://github.com/Claude-Workspace/claude-ws.git
|
|
37
37
|
cd claude-ws
|
|
38
|
-
pnpm install && pnpm dev
|
|
38
|
+
pnpm install && pnpm run dev
|
|
39
|
+
or
|
|
40
|
+
pnpm install && pmpm run build && pnpm start
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
Open http://localhost:8556
|
|
@@ -68,22 +70,6 @@ CLAUDE_PATH=/path/to/claude
|
|
|
68
70
|
| `ANTHROPIC_API_RETRY_TIMES` | Number of retry attempts for failed Anthropic API requests (5xx errors or network failures) | `3` |
|
|
69
71
|
| `ANTHROPIC_API_RETRY_DELAY_MS` | Delay between retry attempts in milliseconds | `10000` |
|
|
70
72
|
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## Production (PM2)
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
npm install claude-ws
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Go to a folder that you want to run you claude-ws instance
|
|
80
|
-
Config your .env (if prefferred)
|
|
81
|
-
Start claude-ws instance
|
|
82
|
-
```bash
|
|
83
|
-
claude-ws
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
You can use it with pm2 for auto startup with system starts.
|
|
87
73
|
|
|
88
74
|
---
|
|
89
75
|
|
package/locales/de.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "Spalte löschen",
|
|
57
57
|
"addColumn": "Spalte hinzufügen",
|
|
58
58
|
"noTasks": "Keine Aufgaben",
|
|
59
|
-
"deleteAllTasks": "Alle {count} Aufgabe(n) löschen?",
|
|
59
|
+
"deleteAllTasks": "Alle {count} Aufgabe(n) in \"{status}\" löschen?",
|
|
60
60
|
"newTaskShortcut": "Neue Aufgabe (Strg/⌘ + Leertaste)",
|
|
61
61
|
"addNew": "Neu",
|
|
62
62
|
"createNewTask": "Neue Aufgabe erstellen",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "Läuft...",
|
|
284
284
|
"attachFilesTitle": "Dateien anhängen",
|
|
285
285
|
"describeWhatYouWant": "Beschreiben Sie, was Claude tun soll...",
|
|
286
|
+
"interruptAndSend": "Nachricht senden, um Claude zu unterbrechen...",
|
|
286
287
|
"pasteImageOrText": "Bild oder Text einfügen",
|
|
287
288
|
"dropFilesHere": "Dateien hier ablegen",
|
|
288
289
|
"commandsHint": "Befehle",
|
|
289
290
|
"filesHint": "Dateien",
|
|
290
|
-
"pasteImageHint": "Bild einfügen"
|
|
291
|
+
"pasteImageHint": "Bild einfügen",
|
|
292
|
+
"cancelled": "Abgebrochen"
|
|
291
293
|
},
|
|
292
294
|
"agentProvider": {
|
|
293
295
|
"configureTitle": "Agent-Provider konfigurieren",
|
package/locales/en.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "Delete Column",
|
|
57
57
|
"addColumn": "Add Column",
|
|
58
58
|
"noTasks": "No tasks",
|
|
59
|
-
"deleteAllTasks": "Delete all {count} task(s)?",
|
|
59
|
+
"deleteAllTasks": "Delete all {count} task(s) in \"{status}\"?",
|
|
60
60
|
"newTaskShortcut": "New Task (Ctrl/⌘ + Space)",
|
|
61
61
|
"addNew": "New",
|
|
62
62
|
"createNewTask": "Create New Task",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "Running...",
|
|
284
284
|
"attachFilesTitle": "Attach files",
|
|
285
285
|
"describeWhatYouWant": "Describe what you want Claude to do...",
|
|
286
|
+
"interruptAndSend": "Send a message to interrupt Claude...",
|
|
286
287
|
"pasteImageOrText": "Paste image or text",
|
|
287
288
|
"dropFilesHere": "Drop files here",
|
|
288
289
|
"commandsHint": "commands",
|
|
289
290
|
"filesHint": "files",
|
|
290
|
-
"pasteImageHint": "paste image"
|
|
291
|
+
"pasteImageHint": "paste image",
|
|
292
|
+
"cancelled": "Cancelled"
|
|
291
293
|
},
|
|
292
294
|
"agentProvider": {
|
|
293
295
|
"configureTitle": "Configure Agent Provider",
|
package/locales/es.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "Eliminar Columna",
|
|
57
57
|
"addColumn": "Añadir Columna",
|
|
58
58
|
"noTasks": "Sin tareas",
|
|
59
|
-
"deleteAllTasks": "¿Eliminar {count} tarea(s)?",
|
|
59
|
+
"deleteAllTasks": "¿Eliminar {count} tarea(s) en \"{status}\"?",
|
|
60
60
|
"newTaskShortcut": "Nueva Tarea (Ctrl/⌘ + Espacio)",
|
|
61
61
|
"addNew": "Nuevo",
|
|
62
62
|
"createNewTask": "Crear Nueva Tarea",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "Ejecutándose...",
|
|
284
284
|
"attachFilesTitle": "Adjuntar archivos",
|
|
285
285
|
"describeWhatYouWant": "Describe lo que quieres que Claude haga...",
|
|
286
|
+
"interruptAndSend": "Enviar un mensaje para interrumpir a Claude...",
|
|
286
287
|
"pasteImageOrText": "Pegar imagen o texto",
|
|
287
288
|
"dropFilesHere": "Suelta archivos aquí",
|
|
288
289
|
"commandsHint": "comandos",
|
|
289
290
|
"filesHint": "archivos",
|
|
290
|
-
"pasteImageHint": "pegar imagen"
|
|
291
|
+
"pasteImageHint": "pegar imagen",
|
|
292
|
+
"cancelled": "Cancelado"
|
|
291
293
|
},
|
|
292
294
|
"accessAnywhere": {
|
|
293
295
|
"title": "Acceder Desde Cualquier Lugar",
|
package/locales/fr.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "Supprimer la colonne",
|
|
57
57
|
"addColumn": "Ajouter une colonne",
|
|
58
58
|
"noTasks": "Aucune tâche",
|
|
59
|
-
"deleteAllTasks": "Supprimer les {count} tâche(s) ?",
|
|
59
|
+
"deleteAllTasks": "Supprimer les {count} tâche(s) dans \"{status}\" ?",
|
|
60
60
|
"newTaskShortcut": "Nouvelle tâche (Ctrl/⌘ + Espace)",
|
|
61
61
|
"addNew": "Nouveau",
|
|
62
62
|
"createNewTask": "Créer une nouvelle tâche",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "En cours...",
|
|
284
284
|
"attachFilesTitle": "Joindre des fichiers",
|
|
285
285
|
"describeWhatYouWant": "Décrivez ce que vous voulez que Claude fasse...",
|
|
286
|
+
"interruptAndSend": "Envoyer un message pour interrompre Claude...",
|
|
286
287
|
"pasteImageOrText": "Coller une image ou du texte",
|
|
287
288
|
"dropFilesHere": "Déposez les fichiers ici",
|
|
288
289
|
"commandsHint": "commandes",
|
|
289
290
|
"filesHint": "fichiers",
|
|
290
|
-
"pasteImageHint": "coller image"
|
|
291
|
+
"pasteImageHint": "coller image",
|
|
292
|
+
"cancelled": "Annulé"
|
|
291
293
|
},
|
|
292
294
|
"accessAnywhere": {
|
|
293
295
|
"title": "Accès Partout",
|
package/locales/ja.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "列を削除",
|
|
57
57
|
"addColumn": "列を追加",
|
|
58
58
|
"noTasks": "タスクなし",
|
|
59
|
-
"deleteAllTasks": "
|
|
59
|
+
"deleteAllTasks": "「{status}」のすべての {count} 個のタスクを削除しますか?",
|
|
60
60
|
"newTaskShortcut": "新しいタスク (Ctrl/⌘ + Space)",
|
|
61
61
|
"addNew": "新規",
|
|
62
62
|
"createNewTask": "新しいタスクを作成",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "実行中...",
|
|
284
284
|
"attachFilesTitle": "ファイルを添付",
|
|
285
285
|
"describeWhatYouWant": "Claude にやりたいことを説明...",
|
|
286
|
+
"interruptAndSend": "メッセージを送信して Claude を中断...",
|
|
286
287
|
"pasteImageOrText": "画像またはテキストを貼り付け",
|
|
287
288
|
"dropFilesHere": "ここにファイルをドロップ",
|
|
288
289
|
"commandsHint": "コマンド",
|
|
289
290
|
"filesHint": "ファイル",
|
|
290
|
-
"pasteImageHint": "画像を貼り付け"
|
|
291
|
+
"pasteImageHint": "画像を貼り付け",
|
|
292
|
+
"cancelled": "キャンセルされました"
|
|
291
293
|
},
|
|
292
294
|
"accessAnywhere": {
|
|
293
295
|
"title": "どこからでもアクセス",
|
package/locales/ko.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "열 삭제",
|
|
57
57
|
"addColumn": "열 추가",
|
|
58
58
|
"noTasks": "작업 없음",
|
|
59
|
-
"deleteAllTasks": "모든 {count} 개의 작업을 삭제하시겠습니까?",
|
|
59
|
+
"deleteAllTasks": "\"{status}\"의 모든 {count} 개의 작업을 삭제하시겠습니까?",
|
|
60
60
|
"newTaskShortcut": "새 작업 (Ctrl/⌘ + Space)",
|
|
61
61
|
"addNew": "추가",
|
|
62
62
|
"createNewTask": "새 작업 만들기",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "실행 중...",
|
|
284
284
|
"attachFilesTitle": "파일 첨부",
|
|
285
285
|
"describeWhatYouWant": "Claude에게 원하는 것을 설명하세요...",
|
|
286
|
+
"interruptAndSend": "메시지를 보내 Claude를 중단...",
|
|
286
287
|
"pasteImageOrText": "이미지 또는 텍스트 붙여넣기",
|
|
287
288
|
"dropFilesHere": "파일을 여기에 놓으세요",
|
|
288
289
|
"commandsHint": "명령어",
|
|
289
290
|
"filesHint": "파일",
|
|
290
|
-
"pasteImageHint": "이미지 붙여넣기"
|
|
291
|
+
"pasteImageHint": "이미지 붙여넣기",
|
|
292
|
+
"cancelled": "취소됨"
|
|
291
293
|
},
|
|
292
294
|
"accessAnywhere": {
|
|
293
295
|
"title": "어디서나 접근",
|
package/locales/vi.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "Xóa cột",
|
|
57
57
|
"addColumn": "Thêm cột",
|
|
58
58
|
"noTasks": "Không có nhiệm vụ",
|
|
59
|
-
"deleteAllTasks": "Xóa tất cả {count} nhiệm
|
|
59
|
+
"deleteAllTasks": "Xóa tất cả {count} nhiệm vụ trong \"{status}\"?",
|
|
60
60
|
"newTaskShortcut": "Nhiệm vụ mới (Ctrl/⌘ + Space)",
|
|
61
61
|
"addNew": "Thêm",
|
|
62
62
|
"createNewTask": "Tạo nhiệm vụ mới",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "Đang chạy...",
|
|
284
284
|
"attachFilesTitle": "Đính kèm tập tin",
|
|
285
285
|
"describeWhatYouWant": "Mô tả những gì bạn muốn Claude làm...",
|
|
286
|
+
"interruptAndSend": "Gửi tin nhắn để ngắt Claude...",
|
|
286
287
|
"pasteImageOrText": "Dán hình ảnh hoặc văn bản",
|
|
287
288
|
"dropFilesHere": "Thả tập tin vào đây",
|
|
288
289
|
"commandsHint": "lệnh",
|
|
289
290
|
"filesHint": "tập tin",
|
|
290
|
-
"pasteImageHint": "dán hình ảnh"
|
|
291
|
+
"pasteImageHint": "dán hình ảnh",
|
|
292
|
+
"cancelled": "Đã hủy"
|
|
291
293
|
},
|
|
292
294
|
"agentProvider": {
|
|
293
295
|
"configureTitle": "Cấu hình Nhà cung cấp Agent",
|
package/locales/zh.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"deleteColumn": "删除列",
|
|
57
57
|
"addColumn": "添加列",
|
|
58
58
|
"noTasks": "无任务",
|
|
59
|
-
"deleteAllTasks": "
|
|
59
|
+
"deleteAllTasks": "删除\"{status}\"中的所有 {count} 个任务?",
|
|
60
60
|
"newTaskShortcut": "新任务 (Ctrl/⌘ + 空格)",
|
|
61
61
|
"addNew": "新建",
|
|
62
62
|
"createNewTask": "创建新任务",
|
|
@@ -283,11 +283,13 @@
|
|
|
283
283
|
"running": "运行中...",
|
|
284
284
|
"attachFilesTitle": "附加文件",
|
|
285
285
|
"describeWhatYouWant": "描述您希望 Claude 做什么...",
|
|
286
|
+
"interruptAndSend": "发送消息以中断 Claude...",
|
|
286
287
|
"pasteImageOrText": "粘贴图片或文本",
|
|
287
288
|
"dropFilesHere": "将文件拖放到此处",
|
|
288
289
|
"commandsHint": "命令",
|
|
289
290
|
"filesHint": "文件",
|
|
290
|
-
"pasteImageHint": "粘贴图片"
|
|
291
|
+
"pasteImageHint": "粘贴图片",
|
|
292
|
+
"cancelled": "已取消"
|
|
291
293
|
},
|
|
292
294
|
"accessAnywhere": {
|
|
293
295
|
"title": "随处访问",
|
package/next.config.ts
CHANGED
|
@@ -7,6 +7,8 @@ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
|
|
7
7
|
const nextConfig: NextConfig = {
|
|
8
8
|
// Enable gzip compression for responses
|
|
9
9
|
compress: true,
|
|
10
|
+
// Transpile xterm packages for proper CSS/ESM handling
|
|
11
|
+
transpilePackages: ['@xterm/xterm', '@xterm/addon-fit', '@xterm/addon-web-links'],
|
|
10
12
|
outputFileTracingRoot: path.join(__dirname),
|
|
11
13
|
outputFileTracingIncludes: {
|
|
12
14
|
'/': ['./src/**/*'],
|
|
@@ -33,21 +35,38 @@ const nextConfig: NextConfig = {
|
|
|
33
35
|
],
|
|
34
36
|
},
|
|
35
37
|
// Add caching headers for static assets - helps tunnel performance
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
// Only enable aggressive caching in production
|
|
39
|
+
headers: async () => {
|
|
40
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
41
|
+
|
|
42
|
+
if (isProduction) {
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
source: '/_next/static/:path*',
|
|
46
|
+
headers: [
|
|
47
|
+
{ key: 'Cache-Control', value: 'public, max-age=2592000, immutable' },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
source: '/fonts/:path*',
|
|
52
|
+
headers: [
|
|
53
|
+
{ key: 'Cache-Control', value: 'public, max-age=2592000, immutable' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Development: disable cache
|
|
60
|
+
return [
|
|
61
|
+
{
|
|
62
|
+
source: '/:path*',
|
|
63
|
+
headers: [
|
|
64
|
+
{ key: 'Cache-Control', value: 'no-store, no-cache, must-revalidate' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
},
|
|
69
|
+
webpack: (config, { isServer }) => {
|
|
51
70
|
// Force single instance of @codemirror packages to avoid instanceof issues
|
|
52
71
|
config.resolve.alias = {
|
|
53
72
|
...config.resolve.alias,
|
|
@@ -55,6 +74,13 @@ const nextConfig: NextConfig = {
|
|
|
55
74
|
'@codemirror/view': path.resolve(__dirname, 'node_modules/@codemirror/view'),
|
|
56
75
|
'@codemirror/language': path.resolve(__dirname, 'node_modules/@codemirror/language'),
|
|
57
76
|
};
|
|
77
|
+
|
|
78
|
+
// Externalize node-pty from server-side bundling (native addon)
|
|
79
|
+
if (isServer) {
|
|
80
|
+
config.externals = config.externals || [];
|
|
81
|
+
config.externals.push('node-pty');
|
|
82
|
+
}
|
|
83
|
+
|
|
58
84
|
return config;
|
|
59
85
|
},
|
|
60
86
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-ws",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.95",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A beautifully crafted workspace interface for Claude Code with real-time streaming and local SQLite database",
|
|
6
6
|
"keywords": [
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
]
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
|
+
"postinstall": "node -e \"try{require('fs').chmodSync(require('path').join('node_modules','node-pty','prebuilds',process.platform+'-'+process.arch,'spawn-helper'),0o755)}catch{}\"",
|
|
64
65
|
"dev": "tsx server.ts",
|
|
65
66
|
"build": "cross-env NODE_ENV=production next build",
|
|
66
67
|
"start": "cross-env NODE_ENV=production tsx server.ts",
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"db:fix": "tsx scripts/db-fix-columns.ts"
|
|
69
70
|
},
|
|
70
71
|
"dependencies": {
|
|
71
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
72
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
|
72
73
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
73
74
|
"@codemirror/lang-cpp": "^6.0.3",
|
|
74
75
|
"@codemirror/lang-css": "^6.3.1",
|
|
@@ -84,9 +85,9 @@
|
|
|
84
85
|
"@codemirror/lang-xml": "^6.1.0",
|
|
85
86
|
"@codemirror/lang-yaml": "^6.1.2",
|
|
86
87
|
"@codemirror/language": "^6.12.1",
|
|
87
|
-
"@codemirror/state": "^6.5.
|
|
88
|
+
"@codemirror/state": "^6.5.4",
|
|
88
89
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
89
|
-
"@codemirror/view": "^6.39.
|
|
90
|
+
"@codemirror/view": "^6.39.14",
|
|
90
91
|
"@dnd-kit/core": "^6.3.1",
|
|
91
92
|
"@dnd-kit/sortable": "^10.0.0",
|
|
92
93
|
"@dnd-kit/utilities": "^3.2.2",
|
|
@@ -103,16 +104,19 @@
|
|
|
103
104
|
"@radix-ui/react-toast": "^1.2.15",
|
|
104
105
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
105
106
|
"@radix-ui/react-visually-hidden": "^1.2.4",
|
|
106
|
-
"@tailwindcss/postcss": "^4",
|
|
107
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
107
108
|
"@types/adm-zip": "^0.5.7",
|
|
108
109
|
"@types/better-sqlite3": "^7.6.13",
|
|
109
110
|
"@types/compression": "^1.8.1",
|
|
110
|
-
"@types/node": "^20",
|
|
111
|
-
"@types/react": "^19",
|
|
112
|
-
"@types/react-dom": "^19",
|
|
111
|
+
"@types/node": "^20.19.33",
|
|
112
|
+
"@types/react": "^19.2.14",
|
|
113
|
+
"@types/react-dom": "^19.2.3",
|
|
113
114
|
"@uiw/react-codemirror": "^4.25.4",
|
|
115
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
116
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
117
|
+
"@xterm/xterm": "^6.0.0",
|
|
114
118
|
"adm-zip": "^0.5.16",
|
|
115
|
-
"better-sqlite3": "^12.
|
|
119
|
+
"better-sqlite3": "^12.6.2",
|
|
116
120
|
"class-variance-authority": "^0.7.1",
|
|
117
121
|
"clsx": "^2.1.1",
|
|
118
122
|
"cmdk": "^1.1.1",
|
|
@@ -121,20 +125,21 @@
|
|
|
121
125
|
"ctunnel": "^1.0.3",
|
|
122
126
|
"date-fns": "^4.1.0",
|
|
123
127
|
"diff": "^8.0.3",
|
|
124
|
-
"dotenv": "^17.2.
|
|
125
|
-
"drizzle-kit": "^0.31.
|
|
128
|
+
"dotenv": "^17.2.4",
|
|
129
|
+
"drizzle-kit": "^0.31.9",
|
|
126
130
|
"drizzle-orm": "^0.45.1",
|
|
127
|
-
"eslint": "^9",
|
|
128
|
-
"eslint-config-next": "^16.1.
|
|
131
|
+
"eslint": "^9.39.2",
|
|
132
|
+
"eslint-config-next": "^16.1.6",
|
|
129
133
|
"highlight.js": "^11.11.1",
|
|
130
134
|
"js-yaml": "^4.1.1",
|
|
131
135
|
"lowlight": "^3.3.0",
|
|
132
136
|
"lucide-react": "^0.562.0",
|
|
133
137
|
"nanoid": "^5.1.6",
|
|
134
|
-
"next": "^16.1.
|
|
135
|
-
"next-intl": "^4.
|
|
138
|
+
"next": "^16.1.6",
|
|
139
|
+
"next-intl": "^4.8.2",
|
|
136
140
|
"next-themes": "^0.4.6",
|
|
137
|
-
"
|
|
141
|
+
"node-pty": "^1.1.0",
|
|
142
|
+
"pino": "^10.3.1",
|
|
138
143
|
"pino-pretty": "^13.1.3",
|
|
139
144
|
"react": "19.2.3",
|
|
140
145
|
"react-dom": "19.2.3",
|
|
@@ -145,13 +150,13 @@
|
|
|
145
150
|
"socket.io-client": "^4.8.3",
|
|
146
151
|
"sonner": "^2.0.7",
|
|
147
152
|
"tailwind-merge": "^3.4.0",
|
|
148
|
-
"tailwindcss": "^4",
|
|
149
|
-
"tar": "^7.5.
|
|
153
|
+
"tailwindcss": "^4.1.18",
|
|
154
|
+
"tar": "^7.5.7",
|
|
150
155
|
"tsx": "^4.21.0",
|
|
151
156
|
"tw-animate-css": "^1.4.0",
|
|
152
|
-
"typescript": "^5",
|
|
157
|
+
"typescript": "^5.9.3",
|
|
153
158
|
"vscode-icons-js": "^11.6.1",
|
|
154
|
-
"zustand": "^5.0.
|
|
159
|
+
"zustand": "^5.0.11"
|
|
155
160
|
},
|
|
156
161
|
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48",
|
|
157
162
|
"devDependencies": {
|
package/server.ts
CHANGED
|
@@ -20,6 +20,10 @@ import { logCacheStats } from './src/lib/proxy-token-cache';
|
|
|
20
20
|
// Enable SDK file checkpointing globally
|
|
21
21
|
process.env.CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING = '1';
|
|
22
22
|
|
|
23
|
+
// Unset CLAUDECODE to prevent nested session detection (SDK v0.2.42+)
|
|
24
|
+
// claude-ws spawns Claude CLI from a server process that may itself run inside Claude Code
|
|
25
|
+
delete process.env.CLAUDECODE;
|
|
26
|
+
|
|
23
27
|
import { createServer } from 'http';
|
|
24
28
|
import { parse } from 'url';
|
|
25
29
|
import next from 'next';
|
|
@@ -30,6 +34,7 @@ import { sessionManager } from './src/lib/session-manager';
|
|
|
30
34
|
import { checkpointManager } from './src/lib/checkpoint-manager';
|
|
31
35
|
import { inlineEditManager } from './src/lib/inline-edit-manager';
|
|
32
36
|
import { shellManager } from './src/lib/shell-manager';
|
|
37
|
+
import { terminalManager } from './src/lib/terminal-manager';
|
|
33
38
|
import { db, schema } from './src/lib/db';
|
|
34
39
|
import { createLogger } from './src/lib/logger';
|
|
35
40
|
|
|
@@ -64,9 +69,11 @@ app.prepare().then(async () => {
|
|
|
64
69
|
const isProxyEndpoint = pathname.startsWith('/api/proxy/anthropic');
|
|
65
70
|
const isTunnelStatusEndpoint = pathname === '/api/tunnel/status';
|
|
66
71
|
const isApiAccessKeyEndpoint = pathname === '/api/settings/api-access-key';
|
|
72
|
+
// Uploads GET is public (for serving files), POST/DELETE require API key
|
|
73
|
+
const isUploadsGetEndpoint = pathname.startsWith('/api/uploads/') && req.method === 'GET';
|
|
67
74
|
|
|
68
|
-
// Skip auth for verify, tunnel status,
|
|
69
|
-
if (isApiRoute && !isVerifyEndpoint && !isProxyEndpoint && !isTunnelStatusEndpoint && !isApiAccessKeyEndpoint && apiAccessKey && apiAccessKey.length > 0) {
|
|
75
|
+
// Skip auth for verify, tunnel status, api-access-key, and uploads GET endpoints
|
|
76
|
+
if (isApiRoute && !isVerifyEndpoint && !isProxyEndpoint && !isTunnelStatusEndpoint && !isApiAccessKeyEndpoint && !isUploadsGetEndpoint && apiAccessKey && apiAccessKey.length > 0) {
|
|
70
77
|
const providedKey = req.headers['x-api-key'];
|
|
71
78
|
|
|
72
79
|
if (!providedKey || providedKey !== apiAccessKey) {
|
|
@@ -614,6 +621,71 @@ app.prepare().then(async () => {
|
|
|
614
621
|
}
|
|
615
622
|
});
|
|
616
623
|
|
|
624
|
+
// ========================================
|
|
625
|
+
// Interactive Terminal Socket Handlers
|
|
626
|
+
// ========================================
|
|
627
|
+
|
|
628
|
+
socket.on('terminal:create', async (
|
|
629
|
+
data: { projectId?: string; cols?: number; rows?: number },
|
|
630
|
+
ack?: (result: { success: boolean; terminalId?: string; error?: string }) => void
|
|
631
|
+
) => {
|
|
632
|
+
log.info('[Server] Creating terminal session');
|
|
633
|
+
try {
|
|
634
|
+
// Resolve CWD: try project path if projectId given, fallback to user CWD
|
|
635
|
+
let cwd = userCwd;
|
|
636
|
+
if (data.projectId) {
|
|
637
|
+
const project = await db.query.projects.findFirst({
|
|
638
|
+
where: eq(schema.projects.id, data.projectId),
|
|
639
|
+
});
|
|
640
|
+
if (project) cwd = project.path;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const terminalId = terminalManager.create({
|
|
644
|
+
projectId: data.projectId || 'global',
|
|
645
|
+
cwd,
|
|
646
|
+
cols: data.cols,
|
|
647
|
+
rows: data.rows,
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
socket.join(`terminal:${terminalId}`);
|
|
651
|
+
log.info({ terminalId, cwd }, '[Server] Terminal session created');
|
|
652
|
+
if (ack) ack({ success: true, terminalId });
|
|
653
|
+
} catch (error) {
|
|
654
|
+
const msg = error instanceof Error ? error.message : 'Failed to create terminal';
|
|
655
|
+
log.error({ error: msg }, '[Server] Terminal create error');
|
|
656
|
+
if (ack) ack({ success: false, error: msg });
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
socket.on('terminal:input', (data: { terminalId: string; data: string }) => {
|
|
661
|
+
terminalManager.write(data.terminalId, data.data);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
socket.on('terminal:resize', (data: { terminalId: string; cols: number; rows: number }) => {
|
|
665
|
+
terminalManager.resize(data.terminalId, data.cols, data.rows);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
socket.on('terminal:close', (
|
|
669
|
+
data: { terminalId: string },
|
|
670
|
+
ack?: (result: { success: boolean }) => void
|
|
671
|
+
) => {
|
|
672
|
+
log.info({ terminalId: data.terminalId }, '[Server] Closing terminal session');
|
|
673
|
+
const success = terminalManager.destroy(data.terminalId);
|
|
674
|
+
if (ack) ack({ success });
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
socket.on('terminal:subscribe', (data: { terminalId: string }) => {
|
|
678
|
+
socket.join(`terminal:${data.terminalId}`);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
socket.on('terminal:check', (
|
|
682
|
+
data: { terminalId: string },
|
|
683
|
+
ack?: (result: { alive: boolean }) => void
|
|
684
|
+
) => {
|
|
685
|
+
const alive = terminalManager.has(data.terminalId);
|
|
686
|
+
if (ack) ack({ alive });
|
|
687
|
+
});
|
|
688
|
+
|
|
617
689
|
socket.on('disconnect', () => {
|
|
618
690
|
log.info(`Client disconnected: ${socket.id}`);
|
|
619
691
|
});
|
|
@@ -678,6 +750,19 @@ app.prepare().then(async () => {
|
|
|
678
750
|
}
|
|
679
751
|
});
|
|
680
752
|
|
|
753
|
+
// ========================================
|
|
754
|
+
// Terminal Manager Event Handlers
|
|
755
|
+
// ========================================
|
|
756
|
+
|
|
757
|
+
terminalManager.on('output', ({ terminalId, data }) => {
|
|
758
|
+
io.to(`terminal:${terminalId}`).emit('terminal:output', { terminalId, data });
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
terminalManager.on('exit', ({ terminalId, exitCode, signal }) => {
|
|
762
|
+
log.info({ terminalId, exitCode, signal }, '[Server] Terminal exited');
|
|
763
|
+
io.to(`terminal:${terminalId}`).emit('terminal:exit', { terminalId, exitCode, signal });
|
|
764
|
+
});
|
|
765
|
+
|
|
681
766
|
// Forward AgentManager events to WebSocket clients
|
|
682
767
|
agentManager.on('started', ({ attemptId, taskId }) => {
|
|
683
768
|
log.info(`[Server] Agent started for attempt ${attemptId}, task ${taskId}`);
|
|
@@ -1174,6 +1259,10 @@ app.prepare().then(async () => {
|
|
|
1174
1259
|
agentManager.cancelAll();
|
|
1175
1260
|
log.info('> Cancelled all Claude agents');
|
|
1176
1261
|
|
|
1262
|
+
// Destroy all interactive terminal sessions
|
|
1263
|
+
terminalManager.destroyAll();
|
|
1264
|
+
log.info('> Destroyed all terminal sessions');
|
|
1265
|
+
|
|
1177
1266
|
// Close all socket connections
|
|
1178
1267
|
io.close(() => {
|
|
1179
1268
|
log.info('> Socket.io closed');
|
|
@@ -3,8 +3,7 @@ import { Geist, Geist_Mono } from 'next/font/google';
|
|
|
3
3
|
import { Toaster } from 'sonner';
|
|
4
4
|
import { ThemeProvider } from '@/components/providers/theme-provider';
|
|
5
5
|
import { SocketProvider } from '@/components/providers/socket-provider';
|
|
6
|
-
import {
|
|
7
|
-
import { RemoteAccessKeyProvider } from '@/components/providers/remote-access-key-provider';
|
|
6
|
+
import { UnifiedSetupProvider } from '@/components/providers/unified-setup-provider';
|
|
8
7
|
import { ApiKeyProvider } from '@/components/auth/api-key-dialog';
|
|
9
8
|
import { NextIntlClientProvider } from 'next-intl';
|
|
10
9
|
import { getMessages } from 'next-intl/server';
|
|
@@ -73,11 +72,9 @@ export default async function RootLayout({
|
|
|
73
72
|
<ApiKeyProvider>
|
|
74
73
|
<SocketProvider>
|
|
75
74
|
<ThemeProvider>
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</RemoteAccessKeyProvider>
|
|
80
|
-
</AgentProviderConfigProvider>
|
|
75
|
+
<UnifiedSetupProvider>
|
|
76
|
+
{children}
|
|
77
|
+
</UnifiedSetupProvider>
|
|
81
78
|
<Toaster position="top-right" richColors closeButton />
|
|
82
79
|
</ThemeProvider>
|
|
83
80
|
</SocketProvider>
|