@diggerhq/anyware 0.7.32 → 0.7.34

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 (56) hide show
  1. package/dist/api/wsClient.d.ts +34 -0
  2. package/dist/api/wsClient.d.ts.map +1 -1
  3. package/dist/api/wsClient.js +214 -0
  4. package/dist/api/wsClient.js.map +1 -1
  5. package/dist/main.d.ts +1 -1
  6. package/dist/main.js +14 -113
  7. package/dist/main.js.map +1 -1
  8. package/dist/server/display.d.ts +5 -0
  9. package/dist/server/display.d.ts.map +1 -0
  10. package/dist/server/display.js +21 -0
  11. package/dist/server/display.js.map +1 -0
  12. package/dist/server/index.d.ts +5 -0
  13. package/dist/server/index.d.ts.map +1 -0
  14. package/dist/server/index.js +94 -0
  15. package/dist/server/index.js.map +1 -0
  16. package/dist/server/lockfile.d.ts +3 -0
  17. package/dist/server/lockfile.d.ts.map +1 -0
  18. package/dist/server/lockfile.js +32 -0
  19. package/dist/server/lockfile.js.map +1 -0
  20. package/dist/server/sessionManager.d.ts +20 -0
  21. package/dist/server/sessionManager.d.ts.map +1 -0
  22. package/dist/server/sessionManager.js +112 -0
  23. package/dist/server/sessionManager.js.map +1 -0
  24. package/package.json +5 -2
  25. package/dist/claude/claudeLocal.d.ts +0 -17
  26. package/dist/claude/claudeLocal.d.ts.map +0 -1
  27. package/dist/claude/claudeLocal.js +0 -171
  28. package/dist/claude/claudeLocal.js.map +0 -1
  29. package/dist/claude/claudeLocalLauncher.d.ts +0 -14
  30. package/dist/claude/claudeLocalLauncher.d.ts.map +0 -1
  31. package/dist/claude/claudeLocalLauncher.js +0 -167
  32. package/dist/claude/claudeLocalLauncher.js.map +0 -1
  33. package/dist/claude/claudeRemote.d.ts +0 -27
  34. package/dist/claude/claudeRemote.d.ts.map +0 -1
  35. package/dist/claude/claudeRemote.js +0 -250
  36. package/dist/claude/claudeRemote.js.map +0 -1
  37. package/dist/claude/claudeRemoteLauncher.d.ts +0 -15
  38. package/dist/claude/claudeRemoteLauncher.d.ts.map +0 -1
  39. package/dist/claude/claudeRemoteLauncher.js +0 -182
  40. package/dist/claude/claudeRemoteLauncher.js.map +0 -1
  41. package/dist/claude/loop.d.ts +0 -24
  42. package/dist/claude/loop.d.ts.map +0 -1
  43. package/dist/claude/loop.js +0 -52
  44. package/dist/claude/loop.js.map +0 -1
  45. package/dist/claude/sessionScanner.d.ts +0 -102
  46. package/dist/claude/sessionScanner.d.ts.map +0 -1
  47. package/dist/claude/sessionScanner.js +0 -241
  48. package/dist/claude/sessionScanner.js.map +0 -1
  49. package/dist/hooks/generateHookSettings.d.ts +0 -20
  50. package/dist/hooks/generateHookSettings.d.ts.map +0 -1
  51. package/dist/hooks/generateHookSettings.js +0 -126
  52. package/dist/hooks/generateHookSettings.js.map +0 -1
  53. package/dist/hooks/hookServer.d.ts +0 -45
  54. package/dist/hooks/hookServer.d.ts.map +0 -1
  55. package/dist/hooks/hookServer.js +0 -83
  56. package/dist/hooks/hookServer.js.map +0 -1
@@ -1,167 +0,0 @@
1
- import { claudeLocal } from './claudeLocal.js';
2
- import { createSessionScanner } from './sessionScanner.js';
3
- import { startHookServer } from '../hooks/hookServer.js';
4
- import { generateHookSettingsFile, cleanupHookSettingsFile } from '../hooks/generateHookSettings.js';
5
- /**
6
- * Launch Claude in local mode with session scanning
7
- * Returns 'switch' if we should switch to remote mode, 'exit' if we should exit
8
- */
9
- export async function claudeLocalLauncher(opts) {
10
- const { session, onThinkingChange, onIdle } = opts;
11
- // Variable to hold scanner reference (needed in onHookEvent before scanner is created)
12
- let scanner = null;
13
- // Start hook server to receive session notifications from Claude
14
- const hookServer = await startHookServer({
15
- onSessionHook: (sessionId) => {
16
- session.setClaudeSessionId(sessionId);
17
- if (scanner) {
18
- scanner.onNewSession(sessionId);
19
- }
20
- },
21
- onHookEvent: (eventType, _sessionId, data) => {
22
- // Forward hook events to the WebSocket as claude_event
23
- // Transform hook data to the expected format
24
- const claudeEvent = {
25
- type: eventType,
26
- hook_data: {
27
- tool_name: data.tool_name,
28
- tool_input: data.tool_input,
29
- tool_response: data.tool_response,
30
- prompt: data.prompt,
31
- stop_reason: data.stop_reason,
32
- response: data.response,
33
- cwd: data.cwd,
34
- },
35
- session_id: data.session_id,
36
- };
37
- session.sendClaudeEvent(claudeEvent);
38
- },
39
- });
40
- // Generate hook settings file for Claude
41
- const hookSettingsPath = generateHookSettingsFile(hookServer.port);
42
- // Create scanner to watch session file and forward messages to server
43
- scanner = await createSessionScanner({
44
- sessionId: session.claudeSessionId,
45
- workingDirectory: session.path,
46
- onMessage: (message) => {
47
- // Skip summary messages - we generate our own
48
- if (message.type !== 'summary') {
49
- session.sendClaudeEvent(message);
50
- }
51
- },
52
- onIdle,
53
- });
54
- // Register callback for when session ID is discovered (from other sources)
55
- const scannerSessionCallback = (sessionId) => {
56
- scanner.onNewSession(sessionId);
57
- };
58
- session.addSessionFoundCallback(scannerSessionCallback);
59
- let exitReason = null;
60
- const processAbortController = new AbortController();
61
- // Use a deferred pattern for exit promise
62
- let exitResolve = () => { };
63
- const exitPromise = new Promise((resolve) => {
64
- exitResolve = resolve;
65
- });
66
- try {
67
- // Abort function
68
- async function abort() {
69
- if (!processAbortController.signal.aborted) {
70
- processAbortController.abort();
71
- }
72
- await exitPromise;
73
- }
74
- // Handle abort request (switch to remote)
75
- async function doAbort() {
76
- if (!exitReason) {
77
- exitReason = 'switch';
78
- }
79
- session.queue.reset();
80
- await abort();
81
- }
82
- // Handle switch request
83
- async function doSwitch() {
84
- if (!exitReason) {
85
- exitReason = 'switch';
86
- }
87
- await abort();
88
- }
89
- // Register handlers for incoming messages
90
- session.onUserInput(() => {
91
- // Any user input from web triggers switch to remote mode
92
- doSwitch();
93
- });
94
- session.onSwitch(() => {
95
- doSwitch();
96
- });
97
- // When a permission response comes from web while in local mode,
98
- // switch to remote mode to handle it
99
- session.onPermissionResponseTriggerSwitch(() => {
100
- console.log('\n\x1b[36mPermission response received from web, switching to remote mode...\x1b[0m\n');
101
- doSwitch();
102
- });
103
- // If there are already messages in the queue, switch to remote immediately
104
- if (session.queue.size() > 0) {
105
- return 'switch';
106
- }
107
- // Handle session start
108
- const handleSessionStart = (sessionId) => {
109
- session.setClaudeSessionId(sessionId);
110
- scanner.onNewSession(sessionId);
111
- };
112
- // Run local mode loop
113
- while (true) {
114
- if (exitReason) {
115
- return exitReason;
116
- }
117
- try {
118
- await claudeLocal({
119
- path: session.path,
120
- sessionId: session.claudeSessionId,
121
- onSessionFound: handleSessionStart,
122
- onThinkingChange: (thinking) => {
123
- session.sendThinking(thinking);
124
- if (onThinkingChange) {
125
- onThinkingChange(thinking);
126
- }
127
- },
128
- abort: processAbortController.signal,
129
- claudeArgs: session.claudeArgs,
130
- hookSettingsPath,
131
- });
132
- // Consume one-time flags after first spawn
133
- session.consumeOneTimeFlags();
134
- // Normal exit
135
- if (!exitReason) {
136
- exitReason = 'exit';
137
- break;
138
- }
139
- }
140
- catch {
141
- if (!exitReason) {
142
- // Retry on error
143
- continue;
144
- }
145
- else {
146
- break;
147
- }
148
- }
149
- }
150
- }
151
- finally {
152
- // Resolve exit promise
153
- exitResolve();
154
- // Cleanup handlers
155
- session.onUserInput(null);
156
- session.onSwitch(null);
157
- session.onPermissionResponseTriggerSwitch(null);
158
- session.removeSessionFoundCallback(scannerSessionCallback);
159
- // Cleanup scanner
160
- await scanner.cleanup();
161
- // Cleanup hook server and settings file
162
- hookServer.stop();
163
- cleanupHookSettingsFile(hookSettingsPath);
164
- }
165
- return exitReason || 'exit';
166
- }
167
- //# sourceMappingURL=claudeLocalLauncher.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeLocalLauncher.js","sourceRoot":"","sources":["../../src/claude/claudeLocalLauncher.ts"],"names":[],"mappings":"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"}
@@ -1,27 +0,0 @@
1
- /**
2
- * Remote mode - runs Claude via SDK with streaming responses
3
- * Messages come from the web via WebSocket
4
- */
5
- import type { SDKMessage } from './sdk/types.js';
6
- import type { ImageAttachment } from '../utils/messageQueue.js';
7
- export type PermissionResponse = 'yes' | 'no' | 'always';
8
- export interface ClaudeRemoteOptions {
9
- abort: AbortSignal;
10
- sessionId: string | null;
11
- path: string;
12
- onMessage: (message: SDKMessage) => void;
13
- onSessionFound: (id: string) => void;
14
- onThinkingChange?: (thinking: boolean) => void;
15
- nextMessage: () => Promise<{
16
- message: string;
17
- images?: ImageAttachment[];
18
- } | null>;
19
- waitForPermission: (toolName: string) => Promise<PermissionResponse>;
20
- onPermissionRequest?: (toolName: string, toolInput: unknown) => void;
21
- claudeArgs?: string[];
22
- }
23
- /**
24
- * Run Claude in remote mode using the SDK
25
- */
26
- export declare function claudeRemote(opts: ClaudeRemoteOptions): Promise<string | null>;
27
- //# sourceMappingURL=claudeRemote.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeRemote.d.ts","sourceRoot":"","sources":["../../src/claude/claudeRemote.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAmD,MAAM,gBAAgB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AA0DhE,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACzC,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,WAAW,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACnF,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrE,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAuDD;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwKpF"}
@@ -1,250 +0,0 @@
1
- /**
2
- * Remote mode - runs Claude via SDK with streaming responses
3
- * Messages come from the web via WebSocket
4
- */
5
- import { query } from './sdk/query.js';
6
- import { join, resolve } from 'node:path';
7
- import { homedir } from 'node:os';
8
- import { existsSync } from 'node:fs';
9
- /**
10
- * Pushable async iterable for messages
11
- */
12
- class PushableAsyncIterable {
13
- queue = [];
14
- waitResolve = null;
15
- ended = false;
16
- push(value) {
17
- if (this.ended)
18
- return;
19
- if (this.waitResolve) {
20
- const resolve = this.waitResolve;
21
- this.waitResolve = null;
22
- resolve({ done: false, value });
23
- }
24
- else {
25
- this.queue.push(value);
26
- }
27
- }
28
- end() {
29
- this.ended = true;
30
- if (this.waitResolve) {
31
- const resolve = this.waitResolve;
32
- this.waitResolve = null;
33
- resolve({ done: true, value: undefined });
34
- }
35
- }
36
- async *[Symbol.asyncIterator]() {
37
- while (true) {
38
- if (this.queue.length > 0) {
39
- yield this.queue.shift();
40
- continue;
41
- }
42
- if (this.ended) {
43
- return;
44
- }
45
- const result = await new Promise((resolve) => {
46
- this.waitResolve = resolve;
47
- });
48
- if (result.done) {
49
- return;
50
- }
51
- yield result.value;
52
- }
53
- }
54
- }
55
- /**
56
- * Build SDK content from text and optional images
57
- */
58
- function buildContent(text, images) {
59
- if (!images || images.length === 0) {
60
- return text;
61
- }
62
- // Build content array with images first, then text
63
- const content = [];
64
- // Add images
65
- for (const img of images) {
66
- content.push({
67
- type: 'image',
68
- source: {
69
- type: 'base64',
70
- media_type: img.mimeType,
71
- data: img.data,
72
- },
73
- });
74
- }
75
- // Add text if present
76
- if (text) {
77
- content.push({
78
- type: 'text',
79
- text: text,
80
- });
81
- }
82
- return content;
83
- }
84
- /**
85
- * Get project path for Claude sessions
86
- * Claude uses the resolved path with special chars replaced by dashes
87
- */
88
- function getProjectPath(workingDirectory) {
89
- const projectId = resolve(workingDirectory).replace(/[\\\/.:]/g, '-');
90
- const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
91
- return join(claudeConfigDir, 'projects', projectId);
92
- }
93
- /**
94
- * Check if Claude session exists
95
- */
96
- function claudeCheckSession(sessionId, workingDirectory) {
97
- const projectDir = getProjectPath(workingDirectory);
98
- const sessionFile = join(projectDir, `${sessionId}.jsonl`);
99
- return existsSync(sessionFile);
100
- }
101
- /**
102
- * Run Claude in remote mode using the SDK
103
- */
104
- export async function claudeRemote(opts) {
105
- // Determine session to resume
106
- let startFrom = opts.sessionId;
107
- // Extract --resume from claudeArgs if present
108
- if (!startFrom && opts.claudeArgs) {
109
- for (let i = 0; i < opts.claudeArgs.length; i++) {
110
- if (opts.claudeArgs[i] === '--resume') {
111
- if (i + 1 < opts.claudeArgs.length) {
112
- const nextArg = opts.claudeArgs[i + 1];
113
- if (!nextArg.startsWith('-') && nextArg.includes('-')) {
114
- startFrom = nextArg;
115
- }
116
- }
117
- }
118
- }
119
- }
120
- // Validate session exists
121
- if (startFrom && !claudeCheckSession(startFrom, opts.path)) {
122
- startFrom = null;
123
- }
124
- // Wait for first message
125
- const firstMessage = await opts.nextMessage();
126
- if (!firstMessage) {
127
- return startFrom;
128
- }
129
- // Thinking state
130
- let thinking = false;
131
- const updateThinking = (newThinking) => {
132
- if (thinking !== newThinking) {
133
- thinking = newThinking;
134
- if (opts.onThinkingChange) {
135
- opts.onThinkingChange(thinking);
136
- }
137
- }
138
- };
139
- // Build SDK options (resume is passed to runQuery separately)
140
- const sdkOptions = {
141
- cwd: opts.path,
142
- abort: opts.abort,
143
- permissionMode: 'default',
144
- // Permission callback - notify web UI and wait for response
145
- canCallTool: async (toolName, input) => {
146
- // Send permission request to web UI (may be skipped if already always-allowed)
147
- if (opts.onPermissionRequest) {
148
- opts.onPermissionRequest(toolName, input);
149
- }
150
- // Wait for response from web UI (or get auto-approval if always-allowed)
151
- const response = await opts.waitForPermission(toolName);
152
- if (response === 'yes' || response === 'always') {
153
- return {
154
- behavior: 'allow',
155
- updatedInput: input,
156
- };
157
- }
158
- else {
159
- return {
160
- behavior: 'deny',
161
- message: 'User denied permission',
162
- };
163
- }
164
- },
165
- };
166
- // Helper function to run the query loop
167
- const runQuery = async (sessionToResume, initialMessage) => {
168
- // Create fresh pushable stream for messages
169
- const msgStream = new PushableAsyncIterable();
170
- // Push the initial message
171
- const initialUserMsg = {
172
- type: 'user',
173
- message: { role: 'user', content: buildContent(initialMessage.message, initialMessage.images) },
174
- };
175
- msgStream.push(initialUserMsg);
176
- // Also send to callback so it gets rendered
177
- opts.onMessage(initialUserMsg);
178
- const options = {
179
- ...sdkOptions,
180
- resume: sessionToResume,
181
- };
182
- const response = query({
183
- prompt: msgStream,
184
- options,
185
- });
186
- updateThinking(true);
187
- console.error('\x1b[90m[debug] Starting message loop\x1b[0m');
188
- let messageCount = 0;
189
- for await (const message of response) {
190
- messageCount++;
191
- console.error(`\x1b[90m[debug] Message #${messageCount}: type=${message.type}${message.subtype ? `, subtype=${message.subtype}` : ''}\x1b[0m`);
192
- // Forward message to callback
193
- opts.onMessage(message);
194
- if (message.type === 'system' && 'subtype' in message && message.subtype === 'init') {
195
- updateThinking(true);
196
- // Extract session ID
197
- if ('session_id' in message && typeof message.session_id === 'string') {
198
- opts.onSessionFound(message.session_id);
199
- }
200
- }
201
- if (message.type === 'result') {
202
- console.error('\x1b[90m[debug] Got result, waiting for next user message...\x1b[0m');
203
- updateThinking(false);
204
- // Get next message
205
- const next = await opts.nextMessage();
206
- if (!next) {
207
- console.error('\x1b[90m[debug] No next message, ending stream\x1b[0m');
208
- msgStream.end();
209
- return sessionToResume ?? null;
210
- }
211
- console.error(`\x1b[90m[debug] Got next message: "${next.message.slice(0, 50)}..."\x1b[0m`);
212
- // Push next message (with optional images)
213
- const nextUserMsg = {
214
- type: 'user',
215
- message: { role: 'user', content: buildContent(next.message, next.images) },
216
- };
217
- msgStream.push(nextUserMsg);
218
- // Also send to callback so it gets rendered
219
- opts.onMessage(nextUserMsg);
220
- }
221
- }
222
- console.error(`\x1b[90m[debug] Message loop ended after ${messageCount} messages\x1b[0m`);
223
- return sessionToResume ?? null;
224
- };
225
- try {
226
- return await runQuery(startFrom ?? undefined, firstMessage);
227
- }
228
- catch (e) {
229
- // If resume failed and we were trying to resume, try starting fresh
230
- if (startFrom && !opts.abort.aborted) {
231
- console.log(`[remote] Could not resume session ${startFrom}, starting fresh`);
232
- try {
233
- return await runQuery(undefined, firstMessage);
234
- }
235
- catch (e2) {
236
- if (!opts.abort.aborted) {
237
- console.error('[remote] Error starting fresh session:', e2);
238
- }
239
- }
240
- }
241
- else if (!opts.abort.aborted) {
242
- console.error('[remote] Error:', e);
243
- }
244
- }
245
- finally {
246
- updateThinking(false);
247
- }
248
- return startFrom;
249
- }
250
- //# sourceMappingURL=claudeRemote.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeRemote.js","sourceRoot":"","sources":["../../src/claude/claudeRemote.ts"],"names":[],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- /**
2
- * Remote launcher - handles remote mode execution with session management
3
- */
4
- import type { Session } from './session.js';
5
- export type RemoteExitReason = 'switch' | 'exit';
6
- export interface RemoteLauncherOptions {
7
- session: Session;
8
- onThinkingChange?: (thinking: boolean) => void;
9
- }
10
- /**
11
- * Launch Claude in remote mode
12
- * Returns 'switch' if we should switch to local mode, 'exit' if we should exit
13
- */
14
- export declare function claudeRemoteLauncher(opts: RemoteLauncherOptions): Promise<RemoteExitReason>;
15
- //# sourceMappingURL=claudeRemoteLauncher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeRemoteLauncher.d.ts","sourceRoot":"","sources":["../../src/claude/claudeRemoteLauncher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+LjG"}
@@ -1,182 +0,0 @@
1
- /**
2
- * Remote launcher - handles remote mode execution with session management
3
- */
4
- import { claudeRemote } from './claudeRemote.js';
5
- import { createTerminalRenderer } from './terminalRenderer.js';
6
- import * as readline from 'node:readline';
7
- /**
8
- * Launch Claude in remote mode
9
- * Returns 'switch' if we should switch to local mode, 'exit' if we should exit
10
- */
11
- export async function claudeRemoteLauncher(opts) {
12
- const { session, onThinkingChange } = opts;
13
- let exitReason = null;
14
- const processAbortController = new AbortController();
15
- // Setup keyboard listener for switching back to local mode
16
- const rl = readline.createInterface({
17
- input: process.stdin,
18
- output: process.stdout,
19
- });
20
- // Listen for any keypress to switch back to local
21
- readline.emitKeypressEvents(process.stdin);
22
- if (process.stdin.isTTY) {
23
- process.stdin.setRawMode(true);
24
- }
25
- const keypressHandler = (str, key) => {
26
- // Switch to local on Enter, Escape, or 'q'
27
- if (key.name === 'return' || key.name === 'escape' || str === 'q') {
28
- if (!exitReason) {
29
- exitReason = 'switch';
30
- }
31
- session.queue.reset(); // Unblock waitForMessage
32
- processAbortController.abort();
33
- }
34
- // Ctrl+C to exit completely
35
- if (key.ctrl && key.name === 'c') {
36
- if (!exitReason) {
37
- exitReason = 'exit';
38
- }
39
- session.queue.reset(); // Unblock waitForMessage
40
- processAbortController.abort();
41
- }
42
- };
43
- process.stdin.on('keypress', keypressHandler);
44
- // Create terminal renderer for displaying messages
45
- const renderer = createTerminalRenderer();
46
- try {
47
- // Handle switch request from web (user wants to go back to local mode)
48
- session.onSwitch(() => {
49
- if (!exitReason) {
50
- exitReason = 'switch';
51
- }
52
- session.queue.reset(); // Unblock waitForMessage
53
- processAbortController.abort();
54
- });
55
- // If we have a pending permission response (from local mode) and no messages in queue,
56
- // queue a "continue" message to trigger Claude to resume.
57
- // NOTE: Don't do this if there's already a message in queue - that handles AskUserQuestion
58
- // where the user's selection is sent as user_input.
59
- if (session.hasPendingPermissionResponse() && session.queue.size() === 0) {
60
- console.log('\x1b[90m[remote] Pending permission response detected, queueing "continue" to resume Claude\x1b[0m');
61
- session.queue.push('continue');
62
- }
63
- // Run remote mode
64
- console.log('');
65
- console.log('\x1b[36m\x1b[1mRemote Mode Active\x1b[0m');
66
- console.log('\x1b[90mMessages from web will appear here. Press Enter/q to switch to local, Ctrl+C to exit.\x1b[0m');
67
- console.log('');
68
- await claudeRemote({
69
- path: session.path,
70
- sessionId: session.claudeSessionId,
71
- abort: processAbortController.signal,
72
- claudeArgs: session.claudeArgs,
73
- onMessage: (message) => {
74
- // Render message in terminal
75
- renderer.render(message);
76
- // Forward SDK messages to server
77
- session.sendClaudeEvent(message);
78
- },
79
- onSessionFound: (sessionId) => {
80
- session.setClaudeSessionId(sessionId);
81
- },
82
- onThinkingChange: (thinking) => {
83
- // Show/hide thinking indicator in terminal
84
- if (thinking) {
85
- renderer.showThinking();
86
- }
87
- else {
88
- renderer.clearThinking();
89
- }
90
- session.sendThinking(thinking);
91
- if (onThinkingChange) {
92
- onThinkingChange(thinking);
93
- }
94
- },
95
- nextMessage: async () => {
96
- // Wait for next message from queue (includes optional images)
97
- const queueMsg = await session.queue.waitForMessage();
98
- if (!queueMsg) {
99
- // Queue was reset or closed
100
- return null;
101
- }
102
- return { message: queueMsg.message, images: queueMsg.images };
103
- },
104
- waitForPermission: async (toolName) => {
105
- // Check if tool is already "always allowed"
106
- if (session.isToolAlwaysAllowed(toolName)) {
107
- console.log(`\x1b[32m✓ Auto-approved: ${toolName} (always allowed)\x1b[0m`);
108
- return 'always';
109
- }
110
- // Check if there's a pending permission response from local mode switch
111
- const pending = session.consumePendingPermissionResponse();
112
- if (pending) {
113
- console.log(`\x1b[32m✓ Using pending permission response: ${pending}\x1b[0m`);
114
- if (pending === 'always') {
115
- session.markToolAlwaysAllowed(toolName);
116
- }
117
- return pending;
118
- }
119
- // Otherwise wait for new response from web
120
- const response = await session.waitForPermissionResponse();
121
- // If user selected "always", remember it for this session
122
- if (response === 'always') {
123
- session.markToolAlwaysAllowed(toolName);
124
- }
125
- return response;
126
- },
127
- onPermissionRequest: (toolName, toolInput) => {
128
- // If tool is already "always allowed", don't show permission request
129
- if (session.isToolAlwaysAllowed(toolName)) {
130
- return;
131
- }
132
- // IMPORTANT: Clear ALL pending permission responses before requesting new permission
133
- // This prevents an OLD response from being consumed by the NEW request
134
- // (e.g., user approved Bash 20s ago, now Edit is asking - shouldn't auto-approve Edit)
135
- session.clearPendingPermissionResponse(0);
136
- // Extra safety: AskUserQuestion should never be auto-answered by a stale queued message
137
- // (e.g. an old "proceed" sitting in the queue from a previous UI bug).
138
- if (toolName === 'AskUserQuestion') {
139
- session.queue.reset();
140
- }
141
- // Send permission request event to web UI
142
- const permissionEvent = {
143
- type: 'PermissionRequest',
144
- hook_data: {
145
- tool_name: toolName,
146
- tool_input: toolInput,
147
- },
148
- };
149
- session.sendClaudeEvent(permissionEvent);
150
- // Also render in terminal
151
- console.log('');
152
- console.log('\x1b[33m⚠️ Permission Required\x1b[0m');
153
- console.log(`\x1b[90mTool: ${toolName}\x1b[0m`);
154
- console.log('\x1b[90mWaiting for approval from web UI...\x1b[0m');
155
- },
156
- });
157
- // Consume one-time flags after spawn
158
- session.consumeOneTimeFlags();
159
- // Normal exit if no exit reason set
160
- if (!exitReason) {
161
- exitReason = 'exit';
162
- }
163
- }
164
- catch (e) {
165
- console.error('[remote] Error:', e);
166
- if (!exitReason) {
167
- exitReason = 'exit';
168
- }
169
- }
170
- finally {
171
- // Cleanup keyboard listener
172
- process.stdin.removeListener('keypress', keypressHandler);
173
- if (process.stdin.isTTY) {
174
- process.stdin.setRawMode(false);
175
- }
176
- rl.close();
177
- // Cleanup handlers
178
- session.onSwitch(null);
179
- }
180
- return exitReason;
181
- }
182
- //# sourceMappingURL=claudeRemoteLauncher.js.map