claude-remote 0.6.0 → 0.6.1
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/bin/claude-remote.js +1 -1
- package/hooks/bridge-session-start.js +32 -32
- package/lib/http-server.js +60 -27
- package/lib/interactive-questions.js +183 -0
- package/lib/logger.js +172 -138
- package/lib/state.js +7 -6
- package/lib/ws-server.js +132 -96
- package/package.json +1 -1
- package/server.js +18 -17
- package/web/index.html +42 -5
- package/web/modules/interactions.js +205 -86
- package/web/modules/settings.js +101 -74
- package/web/modules/state.js +2 -0
- package/web/modules/websocket.js +10 -7
- package/web/styles.css +321 -84
package/web/modules/settings.js
CHANGED
|
@@ -1,74 +1,101 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Settings
|
|
3
|
-
// ============================================================
|
|
4
|
-
import { $ } from './utils.js';
|
|
5
|
-
import { S, approvalMode, setApprovalModeValue } from './state.js';
|
|
6
|
-
import { showConfirm } from './confirm.js';
|
|
7
|
-
import { updateSettingsCwd } from './dir-picker.js';
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
document.querySelectorAll('.settings-opt').forEach(el => {
|
|
11
|
-
el.classList.toggle('active', el.dataset.mode === approvalMode);
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Settings
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { $ } from './utils.js';
|
|
5
|
+
import { S, approvalMode, setApprovalModeValue, themeMode, setThemeModeValue } from './state.js';
|
|
6
|
+
import { showConfirm } from './confirm.js';
|
|
7
|
+
import { updateSettingsCwd } from './dir-picker.js';
|
|
8
|
+
|
|
9
|
+
function updateApprovalActive() {
|
|
10
|
+
document.querySelectorAll('#approval-options .settings-opt').forEach(el => {
|
|
11
|
+
el.classList.toggle('active', el.dataset.mode === approvalMode);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function updateThemeActive() {
|
|
16
|
+
document.querySelectorAll('#theme-options .settings-opt').forEach(el => {
|
|
17
|
+
el.classList.toggle('active', el.dataset.themeMode === themeMode);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setApprovalMode(mode) {
|
|
22
|
+
setApprovalModeValue(mode);
|
|
23
|
+
localStorage.setItem('approvalMode', mode);
|
|
24
|
+
updateApprovalActive();
|
|
25
|
+
if (S.ws && S.ws.readyState === WebSocket.OPEN) {
|
|
26
|
+
S.ws.send(JSON.stringify({ type: 'set_approval_mode', mode }));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function applyTheme(mode) {
|
|
31
|
+
setThemeModeValue(mode);
|
|
32
|
+
localStorage.setItem('theme', mode);
|
|
33
|
+
if (mode === 'light' || mode === 'dark') {
|
|
34
|
+
document.documentElement.setAttribute('data-theme', mode);
|
|
35
|
+
} else {
|
|
36
|
+
document.documentElement.removeAttribute('data-theme');
|
|
37
|
+
}
|
|
38
|
+
updateThemeActive();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function initSettingsValues() {
|
|
42
|
+
const approvalRadio = document.querySelector(`input[name="approval-mode"][value="${approvalMode}"]`);
|
|
43
|
+
if (approvalRadio) approvalRadio.checked = true;
|
|
44
|
+
updateApprovalActive();
|
|
45
|
+
|
|
46
|
+
const themeRadio = document.querySelector(`input[name="theme-mode"][value="${themeMode}"]`);
|
|
47
|
+
if (themeRadio) themeRadio.checked = true;
|
|
48
|
+
updateThemeActive();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function openSettings() {
|
|
52
|
+
initSettingsValues();
|
|
53
|
+
updateSettingsCwd();
|
|
54
|
+
$('settings-overlay').classList.add('visible');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function closeSettings() {
|
|
58
|
+
$('settings-overlay').classList.remove('visible');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { closeSettings };
|
|
62
|
+
|
|
63
|
+
export function initSettings() {
|
|
64
|
+
$('btn-settings').addEventListener('click', openSettings);
|
|
65
|
+
$('settings-close').addEventListener('click', closeSettings);
|
|
66
|
+
$('settings-overlay').addEventListener('click', e => {
|
|
67
|
+
if (e.target === $('settings-overlay')) closeSettings();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
document.querySelectorAll('input[name="approval-mode"]').forEach(radio => {
|
|
71
|
+
radio.addEventListener('change', async (e) => {
|
|
72
|
+
const mode = e.target.value;
|
|
73
|
+
if (mode === 'all') {
|
|
74
|
+
const ok = await showConfirm(
|
|
75
|
+
'全部自动审批将允许所有命令(包括 Bash、系统命令)无需确认直接执行,这可能存在风险。确定要开启吗?'
|
|
76
|
+
);
|
|
77
|
+
if (!ok) {
|
|
78
|
+
const prev = document.querySelector(`input[name="approval-mode"][value="${approvalMode}"]`);
|
|
79
|
+
if (prev) prev.checked = true;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
} else if (mode === 'partial') {
|
|
83
|
+
const ok = await showConfirm(
|
|
84
|
+
'部分自动审批将自动放行 Read、Write、Edit、Glob、Grep 命令,无需手动确认。确定要开启吗?'
|
|
85
|
+
);
|
|
86
|
+
if (!ok) {
|
|
87
|
+
const prev = document.querySelector(`input[name="approval-mode"][value="${approvalMode}"]`);
|
|
88
|
+
if (prev) prev.checked = true;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
setApprovalMode(mode);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
document.querySelectorAll('input[name="theme-mode"]').forEach(radio => {
|
|
97
|
+
radio.addEventListener('change', (e) => {
|
|
98
|
+
applyTheme(e.target.value);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
package/web/modules/state.js
CHANGED
|
@@ -50,6 +50,7 @@ export let serverCacheAddr = '';
|
|
|
50
50
|
export let serverToken = '';
|
|
51
51
|
export let pendingImage = null;
|
|
52
52
|
export let approvalMode = localStorage.getItem('approvalMode') || 'default';
|
|
53
|
+
export let themeMode = localStorage.getItem('theme') || 'system';
|
|
53
54
|
|
|
54
55
|
export function setServerAddr(v) { serverAddr = v; }
|
|
55
56
|
export function setServerWsUrl(v) { serverWsUrl = v; }
|
|
@@ -57,3 +58,4 @@ export function setServerCacheAddr(v) { serverCacheAddr = v; }
|
|
|
57
58
|
export function setServerToken(v) { serverToken = v; }
|
|
58
59
|
export function setPendingImage(v) { pendingImage = v; }
|
|
59
60
|
export function setApprovalModeValue(v) { approvalMode = v; }
|
|
61
|
+
export function setThemeModeValue(v) { themeMode = v; }
|
package/web/modules/websocket.js
CHANGED
|
@@ -21,7 +21,7 @@ import { showPermission, dismissPermissionById, clearPermissions } from './permi
|
|
|
21
21
|
import { handleUploadStatus, updateImagePreviewUi } from './image-upload.js';
|
|
22
22
|
import { renderSessionList } from './sessions.js';
|
|
23
23
|
import { renderDirBrowser, updateSettingsCwd } from './dir-picker.js';
|
|
24
|
-
import { presentNextPendingInteraction } from './interactions.js';
|
|
24
|
+
import { presentNextPendingInteraction, handleQuestionSubmissionError } from './interactions.js';
|
|
25
25
|
|
|
26
26
|
export function isAuthReadyMessage(msg) {
|
|
27
27
|
return !!msg && (
|
|
@@ -370,12 +370,15 @@ export function connect() {
|
|
|
370
370
|
if (m.cwd) { S.cwd = m.cwd; updateHeaderInfo(); }
|
|
371
371
|
if ('sessionId' in m) await syncSessionState(m.sessionId, m.lastSeq);
|
|
372
372
|
}
|
|
373
|
-
else if (m.type === 'turn_state') {
|
|
374
|
-
if (S.replaying) cacheTurnState(m);
|
|
375
|
-
else applyTurnState(m, 'turn_state');
|
|
376
|
-
}
|
|
377
|
-
else if (m.type === '
|
|
378
|
-
|
|
373
|
+
else if (m.type === 'turn_state') {
|
|
374
|
+
if (S.replaying) cacheTurnState(m);
|
|
375
|
+
else applyTurnState(m, 'turn_state');
|
|
376
|
+
}
|
|
377
|
+
else if (m.type === 'question_submission_error') {
|
|
378
|
+
handleQuestionSubmissionError(m.toolUseId, m.error);
|
|
379
|
+
}
|
|
380
|
+
else if (m.type === 'pty_exit') { setStatus('disconnected'); if (S.waiting) setWaiting(false, 'pty_exit'); }
|
|
381
|
+
else if (m.type === 'permission_request') showPermission(m);
|
|
379
382
|
else if (m.type === 'permission_resolved') dismissPermissionById(m.id);
|
|
380
383
|
else if (m.type === 'clear_permissions') clearPermissions();
|
|
381
384
|
else if (m.type === 'sessions') {
|