@siteboon/claude-code-ui 1.25.1 → 1.26.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.
- package/README.de.md +239 -0
- package/README.ja.md +115 -230
- package/README.ko.md +116 -231
- package/README.md +2 -1
- package/README.ru.md +75 -54
- package/README.zh-CN.md +121 -238
- package/dist/assets/index-C08k8QbP.css +32 -0
- package/dist/assets/{index-D6EffcRY.js → index-DnXcHp5q.js} +249 -242
- package/dist/index.html +2 -2
- package/dist/sw.js +59 -3
- package/package.json +3 -2
- package/server/claude-sdk.js +106 -62
- package/server/cli.js +10 -7
- package/server/cursor-cli.js +59 -73
- package/server/database/db.js +142 -1
- package/server/database/init.sql +28 -1
- package/server/gemini-cli.js +46 -48
- package/server/gemini-response-handler.js +12 -73
- package/server/index.js +82 -55
- package/server/middleware/auth.js +2 -2
- package/server/openai-codex.js +43 -28
- package/server/projects.js +1 -1
- package/server/providers/claude/adapter.js +278 -0
- package/server/providers/codex/adapter.js +248 -0
- package/server/providers/cursor/adapter.js +353 -0
- package/server/providers/gemini/adapter.js +186 -0
- package/server/providers/registry.js +44 -0
- package/server/providers/types.js +119 -0
- package/server/providers/utils.js +29 -0
- package/server/routes/agent.js +7 -5
- package/server/routes/cli-auth.js +38 -0
- package/server/routes/codex.js +1 -19
- package/server/routes/gemini.js +0 -30
- package/server/routes/git.js +48 -20
- package/server/routes/messages.js +61 -0
- package/server/routes/plugins.js +5 -1
- package/server/routes/settings.js +99 -1
- package/server/routes/taskmaster.js +2 -2
- package/server/services/notification-orchestrator.js +227 -0
- package/server/services/vapid-keys.js +35 -0
- package/server/utils/plugin-loader.js +53 -4
- package/shared/networkHosts.js +22 -0
- package/dist/assets/index-WNTmA_ug.css +0 -32
package/dist/index.html
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
|
|
26
26
|
<!-- Prevent zoom on iOS -->
|
|
27
27
|
<meta name="format-detection" content="telephone=no" />
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-DnXcHp5q.js"></script>
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-CdSTmIF1.js">
|
|
30
30
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-C8f1vU1x.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/vendor-xterm-CJZjLICi.js">
|
|
32
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
32
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C08k8QbP.css">
|
|
33
33
|
</head>
|
|
34
34
|
<body>
|
|
35
35
|
<div id="root"></div>
|
package/dist/sw.js
CHANGED
|
@@ -19,14 +19,17 @@ self.addEventListener('install', event => {
|
|
|
19
19
|
|
|
20
20
|
// Fetch event
|
|
21
21
|
self.addEventListener('fetch', event => {
|
|
22
|
+
// Never cache API requests or WebSocket upgrades
|
|
23
|
+
if (event.request.url.includes('/api/') || event.request.url.includes('/ws')) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
event.respondWith(
|
|
23
28
|
caches.match(event.request)
|
|
24
29
|
.then(response => {
|
|
25
|
-
// Return cached response if found
|
|
26
30
|
if (response) {
|
|
27
31
|
return response;
|
|
28
32
|
}
|
|
29
|
-
// Otherwise fetch from network
|
|
30
33
|
return fetch(event.request);
|
|
31
34
|
}
|
|
32
35
|
)
|
|
@@ -46,4 +49,57 @@ self.addEventListener('activate', event => {
|
|
|
46
49
|
);
|
|
47
50
|
})
|
|
48
51
|
);
|
|
49
|
-
|
|
52
|
+
self.clients.claim();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Push notification event
|
|
56
|
+
self.addEventListener('push', event => {
|
|
57
|
+
if (!event.data) return;
|
|
58
|
+
|
|
59
|
+
let payload;
|
|
60
|
+
try {
|
|
61
|
+
payload = event.data.json();
|
|
62
|
+
} catch {
|
|
63
|
+
payload = { title: 'Claude Code UI', body: event.data.text() };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const options = {
|
|
67
|
+
body: payload.body || '',
|
|
68
|
+
icon: '/logo-256.png',
|
|
69
|
+
badge: '/logo-128.png',
|
|
70
|
+
data: payload.data || {},
|
|
71
|
+
tag: payload.data?.tag || `${payload.data?.sessionId || 'global'}:${payload.data?.code || 'default'}`,
|
|
72
|
+
renotify: true
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
event.waitUntil(
|
|
76
|
+
self.registration.showNotification(payload.title || 'Claude Code UI', options)
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Notification click event
|
|
81
|
+
self.addEventListener('notificationclick', event => {
|
|
82
|
+
event.notification.close();
|
|
83
|
+
|
|
84
|
+
const sessionId = event.notification.data?.sessionId;
|
|
85
|
+
const provider = event.notification.data?.provider || null;
|
|
86
|
+
const urlPath = sessionId ? `/session/${sessionId}` : '/';
|
|
87
|
+
|
|
88
|
+
event.waitUntil(
|
|
89
|
+
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(async clientList => {
|
|
90
|
+
for (const client of clientList) {
|
|
91
|
+
if (client.url.includes(self.location.origin)) {
|
|
92
|
+
await client.focus();
|
|
93
|
+
client.postMessage({
|
|
94
|
+
type: 'notification:navigate',
|
|
95
|
+
sessionId: sessionId || null,
|
|
96
|
+
provider,
|
|
97
|
+
urlPath
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return self.clients.openWindow(urlPath);
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siteboon/claude-code-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "A web-based UI for Claude Code CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"dev": "concurrently --kill-others \"npm run server\" \"npm run client\"",
|
|
28
28
|
"server": "node server/index.js",
|
|
29
|
-
"client": "vite
|
|
29
|
+
"client": "vite",
|
|
30
30
|
"build": "vite build",
|
|
31
31
|
"preview": "vite preview",
|
|
32
32
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"sqlite": "^5.1.1",
|
|
104
104
|
"sqlite3": "^5.1.7",
|
|
105
105
|
"tailwind-merge": "^3.3.1",
|
|
106
|
+
"web-push": "^3.6.7",
|
|
106
107
|
"ws": "^8.14.2"
|
|
107
108
|
},
|
|
108
109
|
"devDependencies": {
|
package/server/claude-sdk.js
CHANGED
|
@@ -18,6 +18,14 @@ import { promises as fs } from 'fs';
|
|
|
18
18
|
import path from 'path';
|
|
19
19
|
import os from 'os';
|
|
20
20
|
import { CLAUDE_MODELS } from '../shared/modelConstants.js';
|
|
21
|
+
import {
|
|
22
|
+
createNotificationEvent,
|
|
23
|
+
notifyRunFailed,
|
|
24
|
+
notifyRunStopped,
|
|
25
|
+
notifyUserIfEnabled
|
|
26
|
+
} from './services/notification-orchestrator.js';
|
|
27
|
+
import { claudeAdapter } from './providers/claude/adapter.js';
|
|
28
|
+
import { createNormalizedMessage } from './providers/types.js';
|
|
21
29
|
|
|
22
30
|
const activeSessions = new Map();
|
|
23
31
|
const pendingToolApprovals = new Map();
|
|
@@ -136,7 +144,7 @@ function matchesToolPermission(entry, toolName, input) {
|
|
|
136
144
|
* @returns {Object} SDK-compatible options
|
|
137
145
|
*/
|
|
138
146
|
function mapCliOptionsToSDK(options = {}) {
|
|
139
|
-
const { sessionId, cwd, toolsSettings, permissionMode
|
|
147
|
+
const { sessionId, cwd, toolsSettings, permissionMode } = options;
|
|
140
148
|
|
|
141
149
|
const sdkOptions = {};
|
|
142
150
|
|
|
@@ -187,7 +195,7 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
187
195
|
// Map model (default to sonnet)
|
|
188
196
|
// Valid models: sonnet, opus, haiku, opusplan, sonnet[1m]
|
|
189
197
|
sdkOptions.model = options.model || CLAUDE_MODELS.DEFAULT;
|
|
190
|
-
|
|
198
|
+
// Model logged at query start below
|
|
191
199
|
|
|
192
200
|
// Map system prompt configuration
|
|
193
201
|
sdkOptions.systemPrompt = {
|
|
@@ -298,7 +306,7 @@ function extractTokenBudget(resultMessage) {
|
|
|
298
306
|
// This is the user's budget limit, not the model's context window
|
|
299
307
|
const contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 160000;
|
|
300
308
|
|
|
301
|
-
|
|
309
|
+
// Token calc logged via token-budget WS event
|
|
302
310
|
|
|
303
311
|
return {
|
|
304
312
|
used: totalUsed,
|
|
@@ -354,7 +362,7 @@ async function handleImages(command, images, cwd) {
|
|
|
354
362
|
modifiedCommand = command + imageNote;
|
|
355
363
|
}
|
|
356
364
|
|
|
357
|
-
|
|
365
|
+
// Images processed
|
|
358
366
|
return { modifiedCommand, tempImagePaths, tempDir };
|
|
359
367
|
} catch (error) {
|
|
360
368
|
console.error('Error processing images for SDK:', error);
|
|
@@ -387,7 +395,7 @@ async function cleanupTempFiles(tempImagePaths, tempDir) {
|
|
|
387
395
|
);
|
|
388
396
|
}
|
|
389
397
|
|
|
390
|
-
|
|
398
|
+
// Temp files cleaned
|
|
391
399
|
} catch (error) {
|
|
392
400
|
console.error('Error during temp file cleanup:', error);
|
|
393
401
|
}
|
|
@@ -407,7 +415,7 @@ async function loadMcpConfig(cwd) {
|
|
|
407
415
|
await fs.access(claudeConfigPath);
|
|
408
416
|
} catch (error) {
|
|
409
417
|
// File doesn't exist, return null
|
|
410
|
-
|
|
418
|
+
// No config file
|
|
411
419
|
return null;
|
|
412
420
|
}
|
|
413
421
|
|
|
@@ -427,7 +435,7 @@ async function loadMcpConfig(cwd) {
|
|
|
427
435
|
// Add global MCP servers
|
|
428
436
|
if (claudeConfig.mcpServers && typeof claudeConfig.mcpServers === 'object') {
|
|
429
437
|
mcpServers = { ...claudeConfig.mcpServers };
|
|
430
|
-
|
|
438
|
+
// Global MCP servers loaded
|
|
431
439
|
}
|
|
432
440
|
|
|
433
441
|
// Add/override with project-specific MCP servers
|
|
@@ -435,17 +443,14 @@ async function loadMcpConfig(cwd) {
|
|
|
435
443
|
const projectConfig = claudeConfig.claudeProjects[cwd];
|
|
436
444
|
if (projectConfig && projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object') {
|
|
437
445
|
mcpServers = { ...mcpServers, ...projectConfig.mcpServers };
|
|
438
|
-
|
|
446
|
+
// Project MCP servers merged
|
|
439
447
|
}
|
|
440
448
|
}
|
|
441
449
|
|
|
442
450
|
// Return null if no servers found
|
|
443
451
|
if (Object.keys(mcpServers).length === 0) {
|
|
444
|
-
console.log('No MCP servers configured');
|
|
445
452
|
return null;
|
|
446
453
|
}
|
|
447
|
-
|
|
448
|
-
console.log(`Total MCP servers loaded: ${Object.keys(mcpServers).length}`);
|
|
449
454
|
return mcpServers;
|
|
450
455
|
} catch (error) {
|
|
451
456
|
console.error('Error loading MCP config:', error.message);
|
|
@@ -461,12 +466,20 @@ async function loadMcpConfig(cwd) {
|
|
|
461
466
|
* @returns {Promise<void>}
|
|
462
467
|
*/
|
|
463
468
|
async function queryClaudeSDK(command, options = {}, ws) {
|
|
464
|
-
const { sessionId } = options;
|
|
469
|
+
const { sessionId, sessionSummary } = options;
|
|
465
470
|
let capturedSessionId = sessionId;
|
|
466
471
|
let sessionCreatedSent = false;
|
|
467
472
|
let tempImagePaths = [];
|
|
468
473
|
let tempDir = null;
|
|
469
474
|
|
|
475
|
+
const emitNotification = (event) => {
|
|
476
|
+
notifyUserIfEnabled({
|
|
477
|
+
userId: ws?.userId || null,
|
|
478
|
+
writer: ws,
|
|
479
|
+
event
|
|
480
|
+
});
|
|
481
|
+
};
|
|
482
|
+
|
|
470
483
|
try {
|
|
471
484
|
// Map CLI options to SDK format
|
|
472
485
|
const sdkOptions = mapCliOptionsToSDK(options);
|
|
@@ -483,6 +496,26 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
483
496
|
tempImagePaths = imageResult.tempImagePaths;
|
|
484
497
|
tempDir = imageResult.tempDir;
|
|
485
498
|
|
|
499
|
+
sdkOptions.hooks = {
|
|
500
|
+
Notification: [{
|
|
501
|
+
matcher: '',
|
|
502
|
+
hooks: [async (input) => {
|
|
503
|
+
const message = typeof input?.message === 'string' ? input.message : 'Claude requires your attention.';
|
|
504
|
+
emitNotification(createNotificationEvent({
|
|
505
|
+
provider: 'claude',
|
|
506
|
+
sessionId: capturedSessionId || sessionId || null,
|
|
507
|
+
kind: 'action_required',
|
|
508
|
+
code: 'agent.notification',
|
|
509
|
+
meta: { message, sessionName: sessionSummary },
|
|
510
|
+
severity: 'warning',
|
|
511
|
+
requiresUserAction: true,
|
|
512
|
+
dedupeKey: `claude:hook:notification:${capturedSessionId || sessionId || 'none'}:${message}`
|
|
513
|
+
}));
|
|
514
|
+
return {};
|
|
515
|
+
}]
|
|
516
|
+
}]
|
|
517
|
+
};
|
|
518
|
+
|
|
486
519
|
sdkOptions.canUseTool = async (toolName, input, context) => {
|
|
487
520
|
const requiresInteraction = TOOLS_REQUIRING_INTERACTION.has(toolName);
|
|
488
521
|
|
|
@@ -507,13 +540,17 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
507
540
|
}
|
|
508
541
|
|
|
509
542
|
const requestId = createRequestId();
|
|
510
|
-
ws.send({
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
543
|
+
ws.send(createNormalizedMessage({ kind: 'permission_request', requestId, toolName, input, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
544
|
+
emitNotification(createNotificationEvent({
|
|
545
|
+
provider: 'claude',
|
|
546
|
+
sessionId: capturedSessionId || sessionId || null,
|
|
547
|
+
kind: 'action_required',
|
|
548
|
+
code: 'permission.required',
|
|
549
|
+
meta: { toolName, sessionName: sessionSummary },
|
|
550
|
+
severity: 'warning',
|
|
551
|
+
requiresUserAction: true,
|
|
552
|
+
dedupeKey: `claude:permission:${capturedSessionId || sessionId || 'none'}:${requestId}`
|
|
553
|
+
}));
|
|
517
554
|
|
|
518
555
|
const decision = await waitForToolApproval(requestId, {
|
|
519
556
|
timeoutMs: requiresInteraction ? 0 : undefined,
|
|
@@ -525,12 +562,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
525
562
|
_receivedAt: new Date(),
|
|
526
563
|
},
|
|
527
564
|
onCancel: (reason) => {
|
|
528
|
-
ws.send({
|
|
529
|
-
type: 'claude-permission-cancelled',
|
|
530
|
-
requestId,
|
|
531
|
-
reason,
|
|
532
|
-
sessionId: capturedSessionId || sessionId || null
|
|
533
|
-
});
|
|
565
|
+
ws.send(createNormalizedMessage({ kind: 'permission_cancelled', requestId, reason, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
534
566
|
}
|
|
535
567
|
});
|
|
536
568
|
if (!decision) {
|
|
@@ -560,10 +592,22 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
560
592
|
const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
|
|
561
593
|
process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';
|
|
562
594
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
595
|
+
let queryInstance;
|
|
596
|
+
try {
|
|
597
|
+
queryInstance = query({
|
|
598
|
+
prompt: finalCommand,
|
|
599
|
+
options: sdkOptions
|
|
600
|
+
});
|
|
601
|
+
} catch (hookError) {
|
|
602
|
+
// Older/newer SDK versions may not accept hook shapes yet.
|
|
603
|
+
// Keep notification behavior operational via runtime events even if hook registration fails.
|
|
604
|
+
console.warn('Failed to initialize Claude query with hooks, retrying without hooks:', hookError?.message || hookError);
|
|
605
|
+
delete sdkOptions.hooks;
|
|
606
|
+
queryInstance = query({
|
|
607
|
+
prompt: finalCommand,
|
|
608
|
+
options: sdkOptions
|
|
609
|
+
});
|
|
610
|
+
}
|
|
567
611
|
|
|
568
612
|
// Restore immediately — Query constructor already captured the value
|
|
569
613
|
if (prevStreamTimeout !== undefined) {
|
|
@@ -594,39 +638,35 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
594
638
|
// Send session-created event only once for new sessions
|
|
595
639
|
if (!sessionId && !sessionCreatedSent) {
|
|
596
640
|
sessionCreatedSent = true;
|
|
597
|
-
ws.send({
|
|
598
|
-
type: 'session-created',
|
|
599
|
-
sessionId: capturedSessionId
|
|
600
|
-
});
|
|
601
|
-
} else {
|
|
602
|
-
console.log('Not sending session-created. sessionId:', sessionId, 'sessionCreatedSent:', sessionCreatedSent);
|
|
641
|
+
ws.send(createNormalizedMessage({ kind: 'session_created', newSessionId: capturedSessionId, sessionId: capturedSessionId, provider: 'claude' }));
|
|
603
642
|
}
|
|
604
643
|
} else {
|
|
605
|
-
|
|
644
|
+
// session_id already captured
|
|
606
645
|
}
|
|
607
646
|
|
|
608
|
-
// Transform and
|
|
647
|
+
// Transform and normalize message via adapter
|
|
609
648
|
const transformedMessage = transformMessage(message);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
649
|
+
const sid = capturedSessionId || sessionId || null;
|
|
650
|
+
|
|
651
|
+
// Use adapter to normalize SDK events into NormalizedMessage[]
|
|
652
|
+
const normalized = claudeAdapter.normalizeMessage(transformedMessage, sid);
|
|
653
|
+
for (const msg of normalized) {
|
|
654
|
+
// Preserve parentToolUseId from SDK wrapper for subagent tool grouping
|
|
655
|
+
if (transformedMessage.parentToolUseId && !msg.parentToolUseId) {
|
|
656
|
+
msg.parentToolUseId = transformedMessage.parentToolUseId;
|
|
657
|
+
}
|
|
658
|
+
ws.send(msg);
|
|
659
|
+
}
|
|
615
660
|
|
|
616
661
|
// Extract and send token budget updates from result messages
|
|
617
662
|
if (message.type === 'result') {
|
|
618
663
|
const models = Object.keys(message.modelUsage || {});
|
|
619
664
|
if (models.length > 0) {
|
|
620
|
-
|
|
665
|
+
// Model info available in result message
|
|
621
666
|
}
|
|
622
|
-
const
|
|
623
|
-
if (
|
|
624
|
-
|
|
625
|
-
ws.send({
|
|
626
|
-
type: 'token-budget',
|
|
627
|
-
data: tokenBudget,
|
|
628
|
-
sessionId: capturedSessionId || sessionId || null
|
|
629
|
-
});
|
|
667
|
+
const tokenBudgetData = extractTokenBudget(message);
|
|
668
|
+
if (tokenBudgetData) {
|
|
669
|
+
ws.send(createNormalizedMessage({ kind: 'status', text: 'token_budget', tokenBudget: tokenBudgetData, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
630
670
|
}
|
|
631
671
|
}
|
|
632
672
|
}
|
|
@@ -640,14 +680,15 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
640
680
|
await cleanupTempFiles(tempImagePaths, tempDir);
|
|
641
681
|
|
|
642
682
|
// Send completion event
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
683
|
+
ws.send(createNormalizedMessage({ kind: 'complete', exitCode: 0, isNewSession: !sessionId && !!command, sessionId: capturedSessionId, provider: 'claude' }));
|
|
684
|
+
notifyRunStopped({
|
|
685
|
+
userId: ws?.userId || null,
|
|
686
|
+
provider: 'claude',
|
|
687
|
+
sessionId: capturedSessionId || sessionId || null,
|
|
688
|
+
sessionName: sessionSummary,
|
|
689
|
+
stopReason: 'completed'
|
|
649
690
|
});
|
|
650
|
-
|
|
691
|
+
// Complete
|
|
651
692
|
|
|
652
693
|
} catch (error) {
|
|
653
694
|
console.error('SDK query error:', error);
|
|
@@ -661,10 +702,13 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
661
702
|
await cleanupTempFiles(tempImagePaths, tempDir);
|
|
662
703
|
|
|
663
704
|
// Send error to WebSocket
|
|
664
|
-
ws.send({
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
705
|
+
ws.send(createNormalizedMessage({ kind: 'error', content: error.message, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
706
|
+
notifyRunFailed({
|
|
707
|
+
userId: ws?.userId || null,
|
|
708
|
+
provider: 'claude',
|
|
709
|
+
sessionId: capturedSessionId || sessionId || null,
|
|
710
|
+
sessionName: sessionSummary,
|
|
711
|
+
error
|
|
668
712
|
});
|
|
669
713
|
|
|
670
714
|
throw error;
|
package/server/cli.js
CHANGED
|
@@ -110,7 +110,7 @@ function showStatus() {
|
|
|
110
110
|
|
|
111
111
|
// Environment variables
|
|
112
112
|
console.log(`\n${c.info('[INFO]')} Configuration:`);
|
|
113
|
-
console.log(`
|
|
113
|
+
console.log(` SERVER_PORT: ${c.bright(process.env.SERVER_PORT || process.env.PORT || '3001')} ${c.dim(process.env.SERVER_PORT || process.env.PORT ? '' : '(default)')}`);
|
|
114
114
|
console.log(` DATABASE_PATH: ${c.dim(process.env.DATABASE_PATH || '(using default location)')}`);
|
|
115
115
|
console.log(` CLAUDE_CLI_PATH: ${c.dim(process.env.CLAUDE_CLI_PATH || 'claude (default)')}`);
|
|
116
116
|
console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
|
|
@@ -134,7 +134,7 @@ function showStatus() {
|
|
|
134
134
|
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --port 8080')} to run on a custom port`);
|
|
135
135
|
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --database-path /path/to/db')} for custom database`);
|
|
136
136
|
console.log(` ${c.dim('>')} Run ${c.bright('cloudcli help')} for all options`);
|
|
137
|
-
console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.PORT || '3001'}\n`);
|
|
137
|
+
console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.SERVER_PORT || process.env.PORT || '3001'}\n`);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// Show help
|
|
@@ -169,7 +169,8 @@ Examples:
|
|
|
169
169
|
$ cloudcli status # Show configuration
|
|
170
170
|
|
|
171
171
|
Environment Variables:
|
|
172
|
-
|
|
172
|
+
SERVER_PORT Set server port (default: 3001)
|
|
173
|
+
PORT Set server port (default: 3001) (LEGACY)
|
|
173
174
|
DATABASE_PATH Set custom database location
|
|
174
175
|
CLAUDE_CLI_PATH Set custom Claude CLI path
|
|
175
176
|
CONTEXT_WINDOW Set context window size (default: 160000)
|
|
@@ -260,9 +261,9 @@ function parseArgs(args) {
|
|
|
260
261
|
const arg = args[i];
|
|
261
262
|
|
|
262
263
|
if (arg === '--port' || arg === '-p') {
|
|
263
|
-
parsed.options.
|
|
264
|
+
parsed.options.serverPort = args[++i];
|
|
264
265
|
} else if (arg.startsWith('--port=')) {
|
|
265
|
-
parsed.options.
|
|
266
|
+
parsed.options.serverPort = arg.split('=')[1];
|
|
266
267
|
} else if (arg === '--database-path') {
|
|
267
268
|
parsed.options.databasePath = args[++i];
|
|
268
269
|
} else if (arg.startsWith('--database-path=')) {
|
|
@@ -285,8 +286,10 @@ async function main() {
|
|
|
285
286
|
const { command, options } = parseArgs(args);
|
|
286
287
|
|
|
287
288
|
// Apply CLI options to environment variables
|
|
288
|
-
if (options.
|
|
289
|
-
process.env.
|
|
289
|
+
if (options.serverPort) {
|
|
290
|
+
process.env.SERVER_PORT = options.serverPort;
|
|
291
|
+
} else if (!process.env.SERVER_PORT && process.env.PORT) {
|
|
292
|
+
process.env.SERVER_PORT = process.env.PORT;
|
|
290
293
|
}
|
|
291
294
|
if (options.databasePath) {
|
|
292
295
|
process.env.DATABASE_PATH = options.databasePath;
|