kumo-cli 1.0.5 → 1.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 (197) hide show
  1. package/README.md +131 -127
  2. package/dist/auth/credentialsStore.js +1 -1
  3. package/dist/auth/credentialsStore.js.map +1 -1
  4. package/dist/claude/transcriptParser.js +110 -11
  5. package/dist/claude/transcriptParser.js.map +1 -1
  6. package/dist/core/config.js +1 -4
  7. package/dist/core/config.js.map +1 -1
  8. package/dist/core/logger.js +6 -18
  9. package/dist/core/logger.js.map +1 -1
  10. package/dist/engine/claudeArgs.js +102 -0
  11. package/dist/engine/claudeArgs.js.map +1 -0
  12. package/dist/engine/claudeBinary.js +85 -0
  13. package/dist/engine/claudeBinary.js.map +1 -0
  14. package/dist/engine/claudeConfig.js +113 -0
  15. package/dist/engine/claudeConfig.js.map +1 -0
  16. package/dist/engine/controlChannel.js +63 -0
  17. package/dist/engine/controlChannel.js.map +1 -0
  18. package/dist/engine/liveTranscript.js +101 -0
  19. package/dist/engine/liveTranscript.js.map +1 -0
  20. package/dist/engine/ndjson.js +43 -0
  21. package/dist/engine/ndjson.js.map +1 -0
  22. package/dist/engine/permissions.js +98 -0
  23. package/dist/engine/permissions.js.map +1 -0
  24. package/dist/engine/sessionManager.js +429 -0
  25. package/dist/engine/sessionManager.js.map +1 -0
  26. package/dist/engine/settingsCommands.js +204 -0
  27. package/dist/engine/settingsCommands.js.map +1 -0
  28. package/dist/engine/streamSession.js +509 -0
  29. package/dist/engine/streamSession.js.map +1 -0
  30. package/dist/engine/taskTracker.js +110 -0
  31. package/dist/engine/taskTracker.js.map +1 -0
  32. package/dist/handlers/promptImages.js +8 -39
  33. package/dist/handlers/promptImages.js.map +1 -1
  34. package/dist/handlers/runtimeHandlers.js +195 -27
  35. package/dist/handlers/runtimeHandlers.js.map +1 -1
  36. package/dist/handlers/sessionHandlers.js +41 -70
  37. package/dist/handlers/sessionHandlers.js.map +1 -1
  38. package/dist/index.js +35 -40
  39. package/dist/index.js.map +1 -1
  40. package/dist/services/cliRuntime.js +37 -30
  41. package/dist/services/cliRuntime.js.map +1 -1
  42. package/dist/services/effortService.js +13 -60
  43. package/dist/services/effortService.js.map +1 -1
  44. package/dist/services/forkService.js +120 -0
  45. package/dist/services/forkService.js.map +1 -0
  46. package/dist/services/modelService.js +37 -84
  47. package/dist/services/modelService.js.map +1 -1
  48. package/dist/services/sessionListService.js +7 -9
  49. package/dist/services/sessionListService.js.map +1 -1
  50. package/dist/services/sessionService.js +77 -57
  51. package/dist/services/sessionService.js.map +1 -1
  52. package/dist/services/tunnelService.js +4 -5
  53. package/dist/services/tunnelService.js.map +1 -1
  54. package/dist/transport/wsClient.js +0 -4
  55. package/dist/transport/wsClient.js.map +1 -1
  56. package/dist/utils/ignorePaths.js +3 -3
  57. package/dist/utils/ignorePaths.js.map +1 -1
  58. package/package.json +62 -69
  59. package/dist/ansiCodes.js +0 -107
  60. package/dist/ansiCodes.js.map +0 -1
  61. package/dist/auth/credentialsStore.test.js +0 -60
  62. package/dist/auth/credentialsStore.test.js.map +0 -1
  63. package/dist/claude/fileHistory.js +0 -79
  64. package/dist/claude/fileHistory.js.map +0 -1
  65. package/dist/claude/generateHookSettings.js +0 -23
  66. package/dist/claude/generateHookSettings.js.map +0 -1
  67. package/dist/claude/hookServer.js +0 -33
  68. package/dist/claude/hookServer.js.map +0 -1
  69. package/dist/claude/sessionScanner.js +0 -162
  70. package/dist/claude/sessionScanner.js.map +0 -1
  71. package/dist/claude/sessionScanner.test.js +0 -24
  72. package/dist/claude/sessionScanner.test.js.map +0 -1
  73. package/dist/claude/transcriptParser.test.js +0 -78
  74. package/dist/claude/transcriptParser.test.js.map +0 -1
  75. package/dist/commands/cliArgs.test.js +0 -25
  76. package/dist/commands/cliArgs.test.js.map +0 -1
  77. package/dist/config.js +0 -40
  78. package/dist/config.js.map +0 -1
  79. package/dist/credentialsStore.js +0 -57
  80. package/dist/credentialsStore.js.map +0 -1
  81. package/dist/generateHookSettings.js +0 -30
  82. package/dist/generateHookSettings.js.map +0 -1
  83. package/dist/handlers/fileHandlerPayloads.test.js +0 -55
  84. package/dist/handlers/fileHandlerPayloads.test.js.map +0 -1
  85. package/dist/handlers/promptImages.test.js +0 -52
  86. package/dist/handlers/promptImages.test.js.map +0 -1
  87. package/dist/hookServer.js +0 -32
  88. package/dist/hookServer.js.map +0 -1
  89. package/dist/ignorePaths.js +0 -68
  90. package/dist/ignorePaths.js.map +0 -1
  91. package/dist/loadDotEnv.js +0 -79
  92. package/dist/loadDotEnv.js.map +0 -1
  93. package/dist/logger.js +0 -29
  94. package/dist/logger.js.map +0 -1
  95. package/dist/portForwardManager.js +0 -97
  96. package/dist/portForwardManager.js.map +0 -1
  97. package/dist/projectHistoryReader.js +0 -85
  98. package/dist/projectHistoryReader.js.map +0 -1
  99. package/dist/pty/ansiCodes.js +0 -14
  100. package/dist/pty/ansiCodes.js.map +0 -1
  101. package/dist/pty/notificationDetector.js +0 -29
  102. package/dist/pty/notificationDetector.js.map +0 -1
  103. package/dist/pty/notificationDetector.test.js +0 -24
  104. package/dist/pty/notificationDetector.test.js.map +0 -1
  105. package/dist/pty/ptyMenuParser.js +0 -91
  106. package/dist/pty/ptyMenuParser.js.map +0 -1
  107. package/dist/pty/ptyMenuParser.test.js +0 -26
  108. package/dist/pty/ptyMenuParser.test.js.map +0 -1
  109. package/dist/pty/ptySnapshotRenderer.js +0 -71
  110. package/dist/pty/ptySnapshotRenderer.js.map +0 -1
  111. package/dist/pty/selectionMenuDetector.js +0 -49
  112. package/dist/pty/selectionMenuDetector.js.map +0 -1
  113. package/dist/pty/selectionMenuDetector.test.js +0 -62
  114. package/dist/pty/selectionMenuDetector.test.js.map +0 -1
  115. package/dist/pty/selectionMenuMeta.js +0 -54
  116. package/dist/pty/selectionMenuMeta.js.map +0 -1
  117. package/dist/pty/selectionMenuMeta.test.js +0 -26
  118. package/dist/pty/selectionMenuMeta.test.js.map +0 -1
  119. package/dist/pty/selectionMenuParser.js +0 -83
  120. package/dist/pty/selectionMenuParser.js.map +0 -1
  121. package/dist/pty/selectionMenuParser.test.js +0 -30
  122. package/dist/pty/selectionMenuParser.test.js.map +0 -1
  123. package/dist/pty/spawn.js +0 -103
  124. package/dist/pty/spawn.js.map +0 -1
  125. package/dist/pty/spawn.test.js +0 -16
  126. package/dist/pty/spawn.test.js.map +0 -1
  127. package/dist/pty/statusSessionDetector.js +0 -28
  128. package/dist/pty/statusSessionDetector.js.map +0 -1
  129. package/dist/pty/statusSessionDetector.test.js +0 -23
  130. package/dist/pty/statusSessionDetector.test.js.map +0 -1
  131. package/dist/pty/textInputDetector.js +0 -60
  132. package/dist/pty/textInputDetector.js.map +0 -1
  133. package/dist/pty/textInputDetector.test.js +0 -32
  134. package/dist/pty/textInputDetector.test.js.map +0 -1
  135. package/dist/ptyMenuParser.js +0 -470
  136. package/dist/ptyMenuParser.js.map +0 -1
  137. package/dist/ptySnapshotRenderer.js +0 -78
  138. package/dist/ptySnapshotRenderer.js.map +0 -1
  139. package/dist/services/checkpointService.js +0 -113
  140. package/dist/services/checkpointService.js.map +0 -1
  141. package/dist/services/checkpointService.test.js +0 -61
  142. package/dist/services/checkpointService.test.js.map +0 -1
  143. package/dist/services/claudeMenuBridge.js +0 -116
  144. package/dist/services/claudeMenuBridge.js.map +0 -1
  145. package/dist/services/claudeMenuBridge.test.js +0 -90
  146. package/dist/services/claudeMenuBridge.test.js.map +0 -1
  147. package/dist/services/claudeService.js +0 -146
  148. package/dist/services/claudeService.js.map +0 -1
  149. package/dist/services/claudeService.test.js +0 -39
  150. package/dist/services/claudeService.test.js.map +0 -1
  151. package/dist/services/cliRuntime.test.js +0 -55
  152. package/dist/services/cliRuntime.test.js.map +0 -1
  153. package/dist/services/modelAdapter.test.js +0 -47
  154. package/dist/services/modelAdapter.test.js.map +0 -1
  155. package/dist/services/modelArgs.test.js +0 -64
  156. package/dist/services/modelArgs.test.js.map +0 -1
  157. package/dist/services/modelBaseUrl.test.js +0 -32
  158. package/dist/services/modelBaseUrl.test.js.map +0 -1
  159. package/dist/services/modelService.test.js +0 -136
  160. package/dist/services/modelService.test.js.map +0 -1
  161. package/dist/services/notificationDedupe.js +0 -23
  162. package/dist/services/notificationDedupe.js.map +0 -1
  163. package/dist/services/notificationDedupe.test.js +0 -20
  164. package/dist/services/notificationDedupe.test.js.map +0 -1
  165. package/dist/services/sessionListService.test.js +0 -22
  166. package/dist/services/sessionListService.test.js.map +0 -1
  167. package/dist/services/sessionRollbackService.js +0 -195
  168. package/dist/services/sessionRollbackService.js.map +0 -1
  169. package/dist/services/sessionRollbackService.test.js +0 -142
  170. package/dist/services/sessionRollbackService.test.js.map +0 -1
  171. package/dist/services/tunnelHelpers.test.js +0 -42
  172. package/dist/services/tunnelHelpers.test.js.map +0 -1
  173. package/dist/services/tunnelService.test.js +0 -91
  174. package/dist/services/tunnelService.test.js.map +0 -1
  175. package/dist/sessionScanner.js +0 -271
  176. package/dist/sessionScanner.js.map +0 -1
  177. package/dist/snapshotScanner.js +0 -154
  178. package/dist/snapshotScanner.js.map +0 -1
  179. package/dist/spawn.js +0 -79
  180. package/dist/spawn.js.map +0 -1
  181. package/dist/terminalManager.js +0 -73
  182. package/dist/terminalManager.js.map +0 -1
  183. package/dist/transport/directWsServer.js +0 -135
  184. package/dist/transport/directWsServer.js.map +0 -1
  185. package/dist/transport/webrtcTransport.js +0 -145
  186. package/dist/transport/webrtcTransport.js.map +0 -1
  187. package/dist/transport/webrtcTransport.test.js +0 -104
  188. package/dist/transport/webrtcTransport.test.js.map +0 -1
  189. package/dist/transport/webrtcTypes.js +0 -29
  190. package/dist/transport/webrtcTypes.js.map +0 -1
  191. package/dist/transport/webrtcTypes.test.js +0 -20
  192. package/dist/transport/webrtcTypes.test.js.map +0 -1
  193. package/dist/utils/ptyBuffer.js +0 -46
  194. package/dist/utils/ptyBuffer.js.map +0 -1
  195. package/scripts/claude_launcher.cjs +0 -71
  196. package/scripts/run-tests.mjs +0 -46
  197. package/scripts/session_hook_forwarder.cjs +0 -44
@@ -1,78 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import xtermHeadless from '@xterm/headless';
3
- const { Terminal } = xtermHeadless;
4
- export class PtySnapshotRenderer {
5
- terminal;
6
- cols;
7
- rows;
8
- dirty = false;
9
- lastSentHash = '';
10
- maxLines;
11
- constructor(cols = 120, rows = 30, maxLines = 400) {
12
- this.cols = cols;
13
- this.rows = rows;
14
- this.maxLines = maxLines;
15
- this.terminal = this.createTerminal();
16
- }
17
- feed(data) {
18
- this.terminal.write(data);
19
- this.dirty = true;
20
- }
21
- resize(cols, rows) {
22
- if (cols === this.cols && rows === this.rows)
23
- return;
24
- this.cols = cols;
25
- this.rows = rows;
26
- this.terminal.resize(cols, rows);
27
- this.dirty = true;
28
- }
29
- reset(cols = this.cols, rows = this.rows) {
30
- this.cols = cols;
31
- this.rows = rows;
32
- this.terminal.dispose();
33
- this.terminal = this.createTerminal();
34
- this.dirty = false;
35
- this.lastSentHash = '';
36
- }
37
- flushSnapshot() {
38
- if (!this.dirty)
39
- return null;
40
- this.dirty = false;
41
- const snapshot = this.buildSnapshot();
42
- if (snapshot.hash === this.lastSentHash)
43
- return null;
44
- this.lastSentHash = snapshot.hash;
45
- return snapshot;
46
- }
47
- createTerminal() {
48
- return new Terminal({
49
- cols: this.cols,
50
- rows: this.rows,
51
- scrollback: Math.max(this.maxLines * 4, 1000),
52
- allowProposedApi: true,
53
- });
54
- }
55
- buildSnapshot() {
56
- const buffer = this.terminal.buffer.active;
57
- const start = Math.max(0, buffer.length - this.maxLines);
58
- const lines = [];
59
- for (let row = start; row < buffer.length; row++) {
60
- const line = buffer.getLine(row);
61
- lines.push(line?.translateToString(true) ?? '');
62
- }
63
- while (lines.length > 0 && lines[lines.length - 1] === '') {
64
- lines.pop();
65
- }
66
- const text = lines.join('\n').replace(/\r\n/g, '\n');
67
- const hash = createHash('sha1').update(text).digest('hex').slice(0, 16);
68
- return {
69
- text,
70
- hash,
71
- rows: this.rows,
72
- cols: this.cols,
73
- cursorRow: buffer.cursorY,
74
- cursorCol: buffer.cursorX,
75
- };
76
- }
77
- }
78
- //# sourceMappingURL=ptySnapshotRenderer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ptySnapshotRenderer.js","sourceRoot":"","sources":["../src/ptySnapshotRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAiD,CAAC;AAYvE,MAAM,OAAO,mBAAmB;IACtB,QAAQ,CAAmB;IAC3B,IAAI,CAAS;IACb,IAAI,CAAS;IACb,KAAK,GAAG,KAAK,CAAC;IACd,YAAY,GAAG,EAAE,CAAC;IACT,QAAQ,CAAS;IAElC,YAAY,IAAI,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,GAAG,GAAG;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACxC,CAAC;IAED,IAAI,CAAC,IAAY;QACf,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO;QACrD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,aAAa;QACX,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACtC,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAErD,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;QAClC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,cAAc;QACpB,OAAO,IAAI,QAAQ,CAAC;YAClB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,IAAI,CAAC;YAC7C,gBAAgB,EAAE,IAAI;SACvB,CAAC,CAAC;IACL,CAAC;IAEO,aAAa;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,IAAI,GAAG,GAAG,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YAC1D,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAExE,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,MAAM,CAAC,OAAO;YACzB,SAAS,EAAE,MAAM,CAAC,OAAO;SAC1B,CAAC;IACJ,CAAC;CACF"}
@@ -1,113 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { readSessionHistory } from './sessionService.js';
4
- import { safePath } from '../utils/safePath.js';
5
- export function buildRollbackResult(sessionId, targetMessageId, history = readSessionHistory(sessionId)) {
6
- const targetIndex = history.findIndex((m) => m.id === targetMessageId);
7
- if (targetIndex < 0)
8
- throw new Error('rollback target message not found');
9
- const target = history[targetIndex];
10
- if (target.role !== 'user')
11
- throw new Error('rollback target must be a user message');
12
- const cutoff = findTurnEndIndex(history, targetIndex);
13
- return {
14
- sessionId,
15
- targetMessageId,
16
- restoredPrompt: target.content,
17
- messages: history.slice(0, cutoff + 1),
18
- };
19
- }
20
- export function rollbackChatTurnOnDisk(sessionId, targetMessageId, history = readSessionHistory(sessionId)) {
21
- const plan = buildRollbackPlan(sessionId, targetMessageId, history);
22
- for (const operation of plan.operations)
23
- operation();
24
- return plan.result;
25
- }
26
- function buildRollbackPlan(sessionId, targetMessageId, history) {
27
- const targetIndex = history.findIndex((m) => m.id === targetMessageId);
28
- if (targetIndex < 0)
29
- throw new Error('rollback target message not found');
30
- const target = history[targetIndex];
31
- if (target.role !== 'user')
32
- throw new Error('rollback target must be a user message');
33
- const cutoff = findTurnEndIndex(history, targetIndex);
34
- const laterMessages = history.slice(cutoff + 1);
35
- const operations = [];
36
- const restoredFiles = new Set();
37
- for (let i = laterMessages.length - 1; i >= 0; i--) {
38
- const message = laterMessages[i];
39
- for (const tool of [...(message.toolUses ?? [])].reverse()) {
40
- addToolRollbackOperation(tool, operations, restoredFiles);
41
- }
42
- }
43
- return {
44
- result: {
45
- sessionId,
46
- targetMessageId,
47
- restoredPrompt: target.content,
48
- messages: history.slice(0, cutoff + 1),
49
- restoredFiles: [...restoredFiles],
50
- },
51
- operations,
52
- };
53
- }
54
- function findTurnEndIndex(history, userIndex) {
55
- let end = history.length - 1;
56
- for (let i = userIndex + 1; i < history.length; i++) {
57
- if (history[i].role === 'user') {
58
- end = i - 1;
59
- break;
60
- }
61
- }
62
- return end;
63
- }
64
- function addToolRollbackOperation(tool, operations, restoredFiles) {
65
- const filePath = getToolPath(tool);
66
- if (!filePath)
67
- return;
68
- switch (tool.toolName) {
69
- case 'Write': {
70
- restoredFiles.add(filePath);
71
- const oldContent = tool.input['old_content'];
72
- if (typeof oldContent === 'string')
73
- operations.push(() => writeFile(filePath, oldContent));
74
- else
75
- operations.push(() => removePath(filePath));
76
- return;
77
- }
78
- case 'Edit':
79
- case 'SearchReplace': {
80
- const oldStr = tool.input['old_str'] ?? tool.input['old_string'];
81
- const newStr = tool.input['new_str'] ?? tool.input['new_string'];
82
- if (typeof oldStr !== 'string' || typeof newStr !== 'string')
83
- return;
84
- restoredFiles.add(filePath);
85
- operations.push(() => revertTextReplacement(filePath, oldStr, newStr));
86
- return;
87
- }
88
- }
89
- }
90
- function getToolPath(tool) {
91
- const value = tool.input['file_path'] ?? tool.input['path'];
92
- return typeof value === 'string' ? value : '';
93
- }
94
- function writeFile(filePath, content) {
95
- const resolved = safePath(filePath);
96
- fs.mkdirSync(path.dirname(resolved), { recursive: true });
97
- fs.writeFileSync(resolved, content, 'utf-8');
98
- }
99
- function removePath(targetPath) {
100
- const resolved = safePath(targetPath);
101
- if (fs.existsSync(resolved))
102
- fs.rmSync(resolved, { recursive: true, force: false });
103
- }
104
- function revertTextReplacement(filePath, oldStr, newStr) {
105
- const resolved = safePath(filePath);
106
- const content = fs.readFileSync(resolved, 'utf-8');
107
- const index = content.indexOf(newStr);
108
- if (index < 0)
109
- throw new Error(`rollback replacement not found for ${filePath}`);
110
- const next = `${content.slice(0, index)}${oldStr}${content.slice(index + newStr.length)}`;
111
- fs.writeFileSync(resolved, next, 'utf-8');
112
- }
113
- //# sourceMappingURL=checkpointService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"checkpointService.js","sourceRoot":"","sources":["../../src/services/checkpointService.ts"],"names":[],"mappings":"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"}
@@ -1,61 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import fs from 'node:fs';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
- import { buildRollbackResult, rollbackChatTurnOnDisk } from './checkpointService.js';
7
- test('buildRollbackResult truncates history to selected user message', () => {
8
- const result = buildRollbackResult('sess-1', 'u1', [
9
- { id: 'u1', role: 'user', content: 'first' },
10
- { id: 'a1', role: 'assistant', content: 'reply' },
11
- { id: 'u2', role: 'user', content: 'second' },
12
- { id: 'a2', role: 'assistant', content: 'reply2' },
13
- ]);
14
- assert.equal(result.sessionId, 'sess-1');
15
- assert.equal(result.restoredPrompt, 'first');
16
- assert.deepEqual(result.messages.map((m) => m.id), ['u1', 'a1']);
17
- });
18
- test('rollbackChatTurnOnDisk restores file changes from later turns', () => {
19
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'kumo-rollback-'));
20
- const prevCwd = process.cwd();
21
- process.chdir(tempRoot);
22
- try {
23
- fs.mkdirSync(path.join(tempRoot, 'lib'), { recursive: true });
24
- fs.writeFileSync(path.join(tempRoot, 'lib', 'demo.txt'), 'after second', 'utf-8');
25
- const result = rollbackChatTurnOnDisk('sess-1', 'u1', [
26
- { id: 'u1', role: 'user', content: 'first' },
27
- {
28
- id: 'a1',
29
- role: 'assistant',
30
- content: 'reply1',
31
- toolUses: [
32
- { toolName: 'Write', input: { file_path: 'lib/demo.txt', content: 'first version' } },
33
- ],
34
- },
35
- { id: 'u2', role: 'user', content: 'second' },
36
- {
37
- id: 'a2',
38
- role: 'assistant',
39
- content: 'reply2',
40
- toolUses: [
41
- {
42
- toolName: 'Write',
43
- input: {
44
- file_path: 'lib/demo.txt',
45
- content: 'after second',
46
- old_content: 'first version',
47
- },
48
- },
49
- ],
50
- },
51
- ]);
52
- assert.equal(fs.readFileSync(path.join(tempRoot, 'lib', 'demo.txt'), 'utf-8'), 'first version');
53
- assert.deepEqual(result.restoredFiles, ['lib/demo.txt']);
54
- assert.deepEqual(result.messages.map((m) => m.id), ['u1', 'a1']);
55
- }
56
- finally {
57
- process.chdir(prevCwd);
58
- fs.rmSync(tempRoot, { recursive: true, force: true });
59
- }
60
- });
61
- //# sourceMappingURL=checkpointService.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"checkpointService.test.js","sourceRoot":"","sources":["../../src/services/checkpointService.test.ts"],"names":[],"mappings":"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"}
@@ -1,116 +0,0 @@
1
- import { log } from '../core/logger.js';
2
- import { NotificationDedupe } from './notificationDedupe.js';
3
- const LOGIN_WARNING_MESSAGE = 'Not logged in · Run /login';
4
- // translate parsed pty elements into ws messages and handle safe auto-accept flows
5
- export class ClaudeMenuBridge {
6
- send;
7
- getEffectiveEffort;
8
- getWorkingDir;
9
- autoAcceptedApiKeyDialog = false;
10
- menuIdCounter = 0;
11
- notificationDedupe = new NotificationDedupe();
12
- constructor(send, getEffectiveEffort, getWorkingDir) {
13
- this.send = send;
14
- this.getEffectiveEffort = getEffectiveEffort;
15
- this.getWorkingDir = getWorkingDir;
16
- }
17
- // reset transient state whenever a fresh claude process is spawned
18
- reset() {
19
- this.autoAcceptedApiKeyDialog = false;
20
- }
21
- // handle a parser event and emit ws payloads or synthetic process input
22
- handleElement(element, process) {
23
- switch (element.type) {
24
- case 'selection':
25
- this.handleSelection(element, process);
26
- break;
27
- case 'input':
28
- log(`[parser] input: "${element.label}" = "${element.value}"`);
29
- this.send({
30
- type: 'text_input',
31
- label: element.label,
32
- value: element.value,
33
- placeholder: element.placeholder,
34
- cursorPosition: element.cursorPosition,
35
- });
36
- break;
37
- case 'notification':
38
- this.handleNotification(element);
39
- break;
40
- case 'status': {
41
- const effort = element.effort ?? this.getEffectiveEffort();
42
- log(`[parser] status: model=${element.model}, effort=${effort}, logged=${element.loggedIn}`);
43
- this.send({ type: 'cli_status', loggedIn: element.loggedIn, effort, workingDir: element.workingDir });
44
- break;
45
- }
46
- case 'session':
47
- log(`[parser] session: ${element.sessionId}`);
48
- this.send({ type: 'session_info', sessionId: element.sessionId, resumeCommand: element.resumeCommand });
49
- break;
50
- }
51
- }
52
- // auto-accept known safe dialogs or forward the menu to the app
53
- handleSelection(element, process) {
54
- if (element.options.length === 0) {
55
- log('[parser] ignoring empty menu');
56
- return;
57
- }
58
- if (element.title.includes('Bypass Permissions')) {
59
- if (this.tryAcceptYes(element, process, 'auto-accepted bypass permissions dialog'))
60
- return;
61
- }
62
- if (!this.autoAcceptedApiKeyDialog &&
63
- (element.title.includes('Detected a custom API key') || element.title.includes('Do you want to use this API key'))) {
64
- if (this.tryAcceptYes(element, process, 'auto-accepted custom api key dialog', true)) {
65
- this.autoAcceptedApiKeyDialog = true;
66
- return;
67
- }
68
- }
69
- this.menuIdCounter += 1;
70
- const id = `menu-${this.menuIdCounter}`;
71
- log(`[parser] menu detected: "${element.title}" (${element.options.length} opts, selected: ${element.selectedIndex})`);
72
- log(`[parser] options: ${JSON.stringify(element.options)}`);
73
- this.send({
74
- type: 'prompt_options',
75
- id,
76
- title: element.title,
77
- description: element.description,
78
- options: element.options,
79
- selectedIndex: element.selectedIndex,
80
- hint: element.hint,
81
- footer: element.footer,
82
- });
83
- }
84
- // forward deduped notifications and update cli status for login warnings
85
- handleNotification(element) {
86
- log(`[parser] notification: [${element.level}] ${element.message}`);
87
- if (this.notificationDedupe.shouldSend(element.level, element.message)) {
88
- this.send({ type: 'notification', level: element.level, message: element.message });
89
- }
90
- if (element.level === 'warning' && element.message.trim().toLowerCase() === LOGIN_WARNING_MESSAGE.toLowerCase()) {
91
- this.send({
92
- type: 'cli_status',
93
- loggedIn: false,
94
- effort: this.getEffectiveEffort(),
95
- workingDir: this.getWorkingDir(),
96
- });
97
- }
98
- }
99
- // try to auto-press "yes" inside a selection dialog
100
- tryAcceptYes(element, process, logMessage, exact = false) {
101
- const yesIndex = element.options.findIndex((option) => (exact
102
- ? option.label.trim().toLowerCase() === 'yes'
103
- : option.label.trim().toLowerCase().includes('yes')));
104
- if (yesIndex < 0 || !process)
105
- return false;
106
- const delta = yesIndex - element.selectedIndex;
107
- const key = delta < 0 ? '\x1b[A' : '\x1b[B';
108
- for (let index = 0; index < Math.abs(delta); index += 1) {
109
- process.write(key);
110
- }
111
- process.write('\r');
112
- log(`[parser] ${logMessage}`);
113
- return true;
114
- }
115
- }
116
- //# sourceMappingURL=claudeMenuBridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeMenuBridge.js","sourceRoot":"","sources":["../../src/services/claudeMenuBridge.ts"],"names":[],"mappings":"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"}
@@ -1,90 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { ClaudeMenuBridge } from './claudeMenuBridge.js';
4
- function createBridge(sent) {
5
- return new ClaudeMenuBridge((data) => sent.push(data), () => 'high', () => 'E:\\Data\\new_journey\\kumo\\cli');
6
- }
7
- function createProcessRecorder() {
8
- const writes = [];
9
- return {
10
- process: {
11
- write(data) {
12
- writes.push(data);
13
- },
14
- },
15
- writes,
16
- };
17
- }
18
- test('auto accepts bypass permissions dialog without sending prompt options', () => {
19
- const sent = [];
20
- const bridge = createBridge(sent);
21
- const recorder = createProcessRecorder();
22
- bridge.handleElement({
23
- type: 'selection',
24
- title: 'Bypass Permissions',
25
- options: [
26
- { index: 0, label: 'Yes', isSelected: false },
27
- { index: 1, label: 'No', isSelected: true },
28
- ],
29
- selectedIndex: 1,
30
- }, recorder.process);
31
- assert.deepEqual(recorder.writes, ['\x1b[A', '\r']);
32
- assert.equal(sent.length, 0);
33
- });
34
- test('auto accepts custom api key dialog only once', () => {
35
- const sent = [];
36
- const bridge = createBridge(sent);
37
- const recorder = createProcessRecorder();
38
- const element = {
39
- type: 'selection',
40
- title: 'Detected a custom API key',
41
- options: [
42
- { index: 0, label: 'Yes', isSelected: true },
43
- { index: 1, label: 'No', isSelected: false },
44
- ],
45
- selectedIndex: 0,
46
- };
47
- bridge.handleElement(element, recorder.process);
48
- bridge.handleElement(element, recorder.process);
49
- assert.deepEqual(recorder.writes, ['\r']);
50
- assert.equal(sent.length, 1);
51
- assert.equal(sent[0]?.type, 'prompt_options');
52
- });
53
- test('login warning sends both notification and logged-out status', () => {
54
- const sent = [];
55
- const bridge = createBridge(sent);
56
- bridge.handleElement({
57
- type: 'notification',
58
- level: 'warning',
59
- message: 'Not logged in · Run /login',
60
- }, null);
61
- assert.deepEqual(sent, [
62
- { type: 'notification', level: 'warning', message: 'Not logged in · Run /login' },
63
- { type: 'cli_status', loggedIn: false, effort: 'high', workingDir: 'E:\\Data\\new_journey\\kumo\\cli' },
64
- ]);
65
- });
66
- test('selection prompts receive stable incremental ids', () => {
67
- const sent = [];
68
- const bridge = createBridge(sent);
69
- bridge.handleElement({
70
- type: 'selection',
71
- title: 'Select model',
72
- options: [
73
- { index: 0, label: 'Sonnet', isSelected: true },
74
- { index: 1, label: 'Opus', isSelected: false },
75
- ],
76
- selectedIndex: 0,
77
- }, null);
78
- bridge.handleElement({
79
- type: 'selection',
80
- title: 'Select effort',
81
- options: [
82
- { index: 0, label: 'High', isSelected: true },
83
- { index: 1, label: 'Max', isSelected: false },
84
- ],
85
- selectedIndex: 0,
86
- }, null);
87
- assert.equal(sent[0]?.id, 'menu-1');
88
- assert.equal(sent[1]?.id, 'menu-2');
89
- });
90
- //# sourceMappingURL=claudeMenuBridge.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeMenuBridge.test.js","sourceRoot":"","sources":["../../src/services/claudeMenuBridge.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD,SAAS,YAAY,CAAC,IAAmB;IACvC,OAAO,IAAI,gBAAgB,CACzB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAmB,CAAC,EACxC,GAAG,EAAE,CAAC,MAAM,EACZ,GAAG,EAAE,CAAC,kCAAkC,CACzC,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB;IAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO;QACL,OAAO,EAAE;YACP,KAAK,CAAC,IAAY;gBAChB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;SACiB;QACpB,MAAM;KACP,CAAC;AACJ,CAAC;AAED,IAAI,CAAC,uEAAuE,EAAE,GAAG,EAAE;IACjF,MAAM,IAAI,GAAkB,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IAEzC,MAAM,CAAC,aAAa,CAAC;QACnB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,oBAAoB;QAC3B,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;YAC7C,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE;SAC5C;QACD,aAAa,EAAE,CAAC;KACjB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAErB,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,IAAI,GAAkB,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,WAAoB;QAC1B,KAAK,EAAE,2BAA2B;QAClC,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE;YAC5C,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE;SAC7C;QACD,aAAa,EAAE,CAAC;KACjB,CAAC;IAEF,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEhD,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,MAAM,IAAI,GAAkB,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,CAAC,aAAa,CAAC;QACnB,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,4BAA4B;KACtC,EAAE,IAAI,CAAC,CAAC;IAET,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE;QACrB,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,4BAA4B,EAAE;QACjF,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,kCAAkC,EAAE;KACxG,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,IAAI,GAAkB,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,CAAC,aAAa,CAAC;QACnB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE;YAC/C,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE;SAC/C;QACD,aAAa,EAAE,CAAC;KACjB,EAAE,IAAI,CAAC,CAAC;IAET,MAAM,CAAC,aAAa,CAAC;QACnB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,eAAe;QACtB,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE;YAC7C,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;SAC9C;QACD,aAAa,EAAE,CAAC;KACjB,EAAE,IAAI,CAAC,CAAC;IAET,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;IACpC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC"}
@@ -1,146 +0,0 @@
1
- import { spawnClaude, writeToProcess } from '../pty/spawn.js';
2
- import { PtyMenuParser } from '../pty/ptyMenuParser.js';
3
- import { PtySnapshotRenderer } from '../pty/ptySnapshotRenderer.js';
4
- import { PtyBuffer } from '../utils/ptyBuffer.js';
5
- import { log, debugWrite } from '../core/logger.js';
6
- import { ClaudeMenuBridge } from './claudeMenuBridge.js';
7
- // manage the lifecycle of a claude pty process plus its snapshot/parser
8
- export class ClaudeService {
9
- send;
10
- model;
11
- effort;
12
- getDeviceToken;
13
- process = null;
14
- cleanup = null;
15
- isRespawning = false;
16
- pendingSelectNudgeTimer = null;
17
- menuParser;
18
- menuBridge;
19
- snapshotRenderer;
20
- buffer;
21
- lastWsSendAt = 0;
22
- args = [];
23
- settingsPath = '';
24
- onExit = null;
25
- constructor(send, model, effort, getDeviceToken) {
26
- this.send = send;
27
- this.model = model;
28
- this.effort = effort;
29
- this.getDeviceToken = getDeviceToken;
30
- this.snapshotRenderer = new PtySnapshotRenderer(process.stdout.columns ?? 120, process.stdout.rows ?? 30);
31
- this.buffer = new PtyBuffer(this.snapshotRenderer);
32
- this.menuBridge = new ClaudeMenuBridge(this.send, () => this.effort.getEffective(), () => process.cwd());
33
- this.menuParser = new PtyMenuParser((el) => this.handleMenuElement(el));
34
- }
35
- // expose a wrapper so wsClient can stamp the last-send timestamp here
36
- notifySent() { this.lastWsSendAt = Date.now(); }
37
- getProcess() { return this.process; }
38
- resetSnapshot() {
39
- this.snapshotRenderer.reset(process.stdout.columns ?? 120, process.stdout.rows ?? 30);
40
- }
41
- resizeSnapshot() {
42
- this.snapshotRenderer.resize(process.stdout.columns ?? 120, process.stdout.rows ?? 30);
43
- }
44
- resetMenu() { this.menuParser.reset(); }
45
- flushBuffer() { this.buffer.flush(); }
46
- // dispatch a typed prompt into the running claude process
47
- dispatchPrompt(text) {
48
- if (!this.process)
49
- return;
50
- if (text.trim() === '/login')
51
- this.menuParser.reset();
52
- writeToProcess(this.process, text);
53
- }
54
- // send a single key/option select (no Enter), then nudge if no echo soon
55
- selectOption(value) {
56
- if (!this.process) {
57
- log(`[select_option] no claude process!`);
58
- return;
59
- }
60
- log(`[select_option] sending: "${value}" (without Enter)`);
61
- this.menuParser.reset();
62
- this.process.write(value);
63
- if (this.pendingSelectNudgeTimer)
64
- clearTimeout(this.pendingSelectNudgeTimer);
65
- const baseline = this.lastWsSendAt;
66
- this.pendingSelectNudgeTimer = setTimeout(() => {
67
- this.pendingSelectNudgeTimer = null;
68
- if (!this.process)
69
- return;
70
- if (this.lastWsSendAt === baseline)
71
- this.nudgeResize();
72
- }, 1000);
73
- }
74
- // submit a textual input by clearing existing chars then writing+enter
75
- submitInput(value) {
76
- if (!this.process)
77
- return;
78
- this.menuParser.reset();
79
- this.process.write('\x15');
80
- this.process.write(value);
81
- this.process.write('\r');
82
- }
83
- // forward a synthetic key press (only esc currently supported)
84
- sendKey(key) {
85
- if (!this.process || key !== 'esc')
86
- return;
87
- this.menuParser.reset();
88
- this.process.write('\x1b');
89
- }
90
- // toggle pty size by 1 col then back to force claude to redraw
91
- nudgeResize() {
92
- if (!this.process)
93
- return;
94
- const cols = process.stdout.columns ?? 120;
95
- const rows = process.stdout.rows ?? 30;
96
- try {
97
- this.process.resize(cols + 1, rows);
98
- setTimeout(() => { try {
99
- this.process?.resize(cols, rows);
100
- }
101
- catch { } }, 5);
102
- }
103
- catch { }
104
- }
105
- // (re)spawn claude with current args; refreshes default model just-in-time
106
- async spawn() {
107
- log(`[spawn] spawning claude, args=${JSON.stringify(this.args)}, cwd=${process.cwd()}`);
108
- if (this.process) {
109
- this.isRespawning = true;
110
- try {
111
- this.process.kill();
112
- }
113
- catch { }
114
- this.process = null;
115
- }
116
- this.cleanup?.();
117
- this.cleanup = null;
118
- this.menuBridge.reset();
119
- this.resetSnapshot();
120
- this.args = await this.model.ensureBeforeSpawn(this.args, this.getDeviceToken());
121
- this.args = this.effort.ensureBeforeSpawn(this.args);
122
- const env = await this.model.buildEnv();
123
- const { pty, cleanup } = spawnClaude([...this.args, '--settings', this.settingsPath], (active) => this.send({ type: 'thinking', active }), (data) => { debugWrite(data); this.menuParser.feed(data); this.buffer.feed(data); }, (cols, rows) => this.menuParser.resize(cols, rows), (p) => { this.process = p; }, undefined, env);
124
- this.cleanup = cleanup;
125
- this.send({
126
- type: 'cli_status',
127
- effort: this.effort.getEffective(),
128
- workingDir: process.cwd(),
129
- });
130
- pty.onExit(({ exitCode }) => {
131
- log(`[spawn] claude exited, exitCode=${exitCode}, isRespawning=${this.isRespawning}`);
132
- this.flushBuffer();
133
- if (this.isRespawning) {
134
- this.isRespawning = false;
135
- return;
136
- }
137
- this.onExit?.(exitCode);
138
- });
139
- process.on('SIGINT', () => { pty.kill(); });
140
- }
141
- // handle a parsed pty menu element and translate into ws message
142
- handleMenuElement(element) {
143
- this.menuBridge.handleElement(element, this.process);
144
- }
145
- }
146
- //# sourceMappingURL=claudeService.js.map