@hcu-lab.me/mcp-device 1.0.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 (261) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/bootstrap.d.ts +1 -0
  4. package/dist/bootstrap.js +22 -0
  5. package/dist/command-manager.d.ts +8 -0
  6. package/dist/command-manager.js +241 -0
  7. package/dist/config-field-definitions.d.ts +41 -0
  8. package/dist/config-field-definitions.js +37 -0
  9. package/dist/config-manager.d.ts +102 -0
  10. package/dist/config-manager.js +291 -0
  11. package/dist/config.d.ts +6 -0
  12. package/dist/config.js +10 -0
  13. package/dist/custom-stdio.d.ts +51 -0
  14. package/dist/custom-stdio.js +358 -0
  15. package/dist/data/official-app-ca.pem +12 -0
  16. package/dist/data/onboarding-prompts.json +70 -0
  17. package/dist/device/device-state.d.ts +13 -0
  18. package/dist/device/device-state.js +100 -0
  19. package/dist/device/device-status.d.ts +81 -0
  20. package/dist/device/device-status.js +156 -0
  21. package/dist/device/device.d.ts +16 -0
  22. package/dist/device/device.js +190 -0
  23. package/dist/device/execution-engine.d.ts +144 -0
  24. package/dist/device/execution-engine.js +195 -0
  25. package/dist/device/gateway-channel.d.ts +48 -0
  26. package/dist/device/gateway-channel.js +384 -0
  27. package/dist/device/gateway-config.d.ts +40 -0
  28. package/dist/device/gateway-config.js +207 -0
  29. package/dist/device/gateway-identity.d.ts +35 -0
  30. package/dist/device/gateway-identity.js +178 -0
  31. package/dist/device/gateway-pairing.d.ts +25 -0
  32. package/dist/device/gateway-pairing.js +105 -0
  33. package/dist/device/gateway-secure-transport.d.ts +25 -0
  34. package/dist/device/gateway-secure-transport.js +117 -0
  35. package/dist/device/gateway-tool-adapter.d.ts +17 -0
  36. package/dist/device/gateway-tool-adapter.js +284 -0
  37. package/dist/device/gateway-url-policy.d.ts +3 -0
  38. package/dist/device/gateway-url-policy.js +43 -0
  39. package/dist/device/linux-service.d.ts +70 -0
  40. package/dist/device/linux-service.js +268 -0
  41. package/dist/device/official-trust.d.ts +7 -0
  42. package/dist/device/official-trust.js +49 -0
  43. package/dist/device/project-inspection.d.ts +1 -0
  44. package/dist/device/project-inspection.js +180 -0
  45. package/dist/device/runtime-owner.d.ts +41 -0
  46. package/dist/device/runtime-owner.js +177 -0
  47. package/dist/device/windows-dpapi.d.ts +11 -0
  48. package/dist/device/windows-dpapi.js +67 -0
  49. package/dist/device/windows-service.d.ts +56 -0
  50. package/dist/device/windows-service.js +238 -0
  51. package/dist/error-handlers.d.ts +7 -0
  52. package/dist/error-handlers.js +15 -0
  53. package/dist/handlers/edit-search-handlers.d.ts +6 -0
  54. package/dist/handlers/edit-search-handlers.js +6 -0
  55. package/dist/handlers/filesystem-handlers.d.ts +38 -0
  56. package/dist/handlers/filesystem-handlers.js +440 -0
  57. package/dist/handlers/history-handlers.d.ts +12 -0
  58. package/dist/handlers/history-handlers.js +67 -0
  59. package/dist/handlers/index.d.ts +6 -0
  60. package/dist/handlers/index.js +7 -0
  61. package/dist/handlers/process-handlers.d.ts +9 -0
  62. package/dist/handlers/process-handlers.js +15 -0
  63. package/dist/handlers/search-handlers.d.ts +17 -0
  64. package/dist/handlers/search-handlers.js +224 -0
  65. package/dist/handlers/terminal-handlers.d.ts +21 -0
  66. package/dist/handlers/terminal-handlers.js +35 -0
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.js +138 -0
  69. package/dist/mcp-device.d.ts +2 -0
  70. package/dist/mcp-device.js +7 -0
  71. package/dist/npm-scripts/remote.d.ts +3 -0
  72. package/dist/npm-scripts/remote.js +314 -0
  73. package/dist/search-manager.d.ts +147 -0
  74. package/dist/search-manager.js +828 -0
  75. package/dist/server.d.ts +46 -0
  76. package/dist/server.js +1563 -0
  77. package/dist/terminal-manager.d.ts +104 -0
  78. package/dist/terminal-manager.js +635 -0
  79. package/dist/tools/config.d.ts +96 -0
  80. package/dist/tools/config.js +260 -0
  81. package/dist/tools/edit.d.ts +38 -0
  82. package/dist/tools/edit.js +435 -0
  83. package/dist/tools/feedback.d.ts +8 -0
  84. package/dist/tools/feedback.js +132 -0
  85. package/dist/tools/filesystem.d.ts +79 -0
  86. package/dist/tools/filesystem.js +947 -0
  87. package/dist/tools/fuzzySearch.d.ts +12 -0
  88. package/dist/tools/fuzzySearch.js +74 -0
  89. package/dist/tools/fuzzySearchCore.d.ts +52 -0
  90. package/dist/tools/fuzzySearchCore.js +125 -0
  91. package/dist/tools/improved-process-tools.d.ts +24 -0
  92. package/dist/tools/improved-process-tools.js +627 -0
  93. package/dist/tools/mime-types.d.ts +3 -0
  94. package/dist/tools/mime-types.js +28 -0
  95. package/dist/tools/pdf/extract-images.d.ts +34 -0
  96. package/dist/tools/pdf/extract-images.js +132 -0
  97. package/dist/tools/pdf/index.d.ts +6 -0
  98. package/dist/tools/pdf/index.js +3 -0
  99. package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
  100. package/dist/tools/pdf/lib/pdf2md.js +76 -0
  101. package/dist/tools/pdf/manipulations.d.ts +13 -0
  102. package/dist/tools/pdf/manipulations.js +99 -0
  103. package/dist/tools/pdf/markdown.d.ts +26 -0
  104. package/dist/tools/pdf/markdown.js +275 -0
  105. package/dist/tools/pdf/utils.d.ts +12 -0
  106. package/dist/tools/pdf/utils.js +34 -0
  107. package/dist/tools/process.d.ts +3 -0
  108. package/dist/tools/process.js +56 -0
  109. package/dist/tools/prompts.d.ts +28 -0
  110. package/dist/tools/prompts.js +259 -0
  111. package/dist/tools/schemas.d.ts +454 -0
  112. package/dist/tools/schemas.js +242 -0
  113. package/dist/tools/usage.d.ts +5 -0
  114. package/dist/tools/usage.js +24 -0
  115. package/dist/types.d.ts +83 -0
  116. package/dist/types.js +1 -0
  117. package/dist/ui/config-editor/config-editor-runtime.js +150 -0
  118. package/dist/ui/config-editor/index.html +13 -0
  119. package/dist/ui/config-editor/src/app.d.ts +43 -0
  120. package/dist/ui/config-editor/src/app.js +831 -0
  121. package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
  122. package/dist/ui/config-editor/src/array-modal.js +185 -0
  123. package/dist/ui/config-editor/src/main.d.ts +1 -0
  124. package/dist/ui/config-editor/src/main.js +2 -0
  125. package/dist/ui/config-editor/styles.css +587 -0
  126. package/dist/ui/contracts.d.ts +14 -0
  127. package/dist/ui/contracts.js +21 -0
  128. package/dist/ui/file-preview/index.html +16 -0
  129. package/dist/ui/file-preview/preview-runtime.js +469 -0
  130. package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
  131. package/dist/ui/file-preview/shared/preview-file-types.js +59 -0
  132. package/dist/ui/file-preview/src/app.d.ts +4 -0
  133. package/dist/ui/file-preview/src/app.js +680 -0
  134. package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
  135. package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
  136. package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
  137. package/dist/ui/file-preview/src/components/highlighting.js +54 -0
  138. package/dist/ui/file-preview/src/components/html-renderer.d.ts +5 -0
  139. package/dist/ui/file-preview/src/components/html-renderer.js +47 -0
  140. package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
  141. package/dist/ui/file-preview/src/components/markdown-renderer.js +59 -0
  142. package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
  143. package/dist/ui/file-preview/src/directory-controller.js +240 -0
  144. package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
  145. package/dist/ui/file-preview/src/document-layout.js +109 -0
  146. package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
  147. package/dist/ui/file-preview/src/document-outline.js +97 -0
  148. package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
  149. package/dist/ui/file-preview/src/document-workspace.js +33 -0
  150. package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
  151. package/dist/ui/file-preview/src/file-type-handlers.js +109 -0
  152. package/dist/ui/file-preview/src/host/external-actions.d.ts +8 -0
  153. package/dist/ui/file-preview/src/host/external-actions.js +55 -0
  154. package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
  155. package/dist/ui/file-preview/src/host/selection-context.js +106 -0
  156. package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
  157. package/dist/ui/file-preview/src/image-preview.js +21 -0
  158. package/dist/ui/file-preview/src/main.d.ts +1 -0
  159. package/dist/ui/file-preview/src/main.js +5 -0
  160. package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
  161. package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
  162. package/dist/ui/file-preview/src/markdown/controller.d.ts +44 -0
  163. package/dist/ui/file-preview/src/markdown/controller.js +1043 -0
  164. package/dist/ui/file-preview/src/markdown/editor.d.ts +131 -0
  165. package/dist/ui/file-preview/src/markdown/editor.js +1479 -0
  166. package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
  167. package/dist/ui/file-preview/src/markdown/linking.js +228 -0
  168. package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
  169. package/dist/ui/file-preview/src/markdown/outline.js +16 -0
  170. package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
  171. package/dist/ui/file-preview/src/markdown/parser.js +38 -0
  172. package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
  173. package/dist/ui/file-preview/src/markdown/preview.js +20 -0
  174. package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
  175. package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
  176. package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
  177. package/dist/ui/file-preview/src/markdown/utils.js +15 -0
  178. package/dist/ui/file-preview/src/model.d.ts +35 -0
  179. package/dist/ui/file-preview/src/model.js +1 -0
  180. package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
  181. package/dist/ui/file-preview/src/panel-actions.js +182 -0
  182. package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
  183. package/dist/ui/file-preview/src/path-utils.js +64 -0
  184. package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
  185. package/dist/ui/file-preview/src/payload-utils.js +119 -0
  186. package/dist/ui/file-preview/src/types.d.ts +1 -0
  187. package/dist/ui/file-preview/src/types.js +1 -0
  188. package/dist/ui/file-preview/styles.css +1631 -0
  189. package/dist/ui/resources.d.ts +28 -0
  190. package/dist/ui/resources.js +86 -0
  191. package/dist/ui/shared/compact-row.d.ts +11 -0
  192. package/dist/ui/shared/compact-row.js +18 -0
  193. package/dist/ui/shared/escape-html.d.ts +4 -0
  194. package/dist/ui/shared/escape-html.js +11 -0
  195. package/dist/ui/shared/host-context.d.ts +15 -0
  196. package/dist/ui/shared/host-context.js +51 -0
  197. package/dist/ui/shared/tool-bridge.d.ts +30 -0
  198. package/dist/ui/shared/tool-bridge.js +137 -0
  199. package/dist/ui/shared/tool-shell.d.ts +25 -0
  200. package/dist/ui/shared/tool-shell.js +107 -0
  201. package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
  202. package/dist/ui/shared/ui-event-tracker.js +27 -0
  203. package/dist/ui/shared/widget-state.d.ts +33 -0
  204. package/dist/ui/shared/widget-state.js +158 -0
  205. package/dist/utils/ab-test.d.ts +12 -0
  206. package/dist/utils/ab-test.js +104 -0
  207. package/dist/utils/capture.d.ts +35 -0
  208. package/dist/utils/capture.js +481 -0
  209. package/dist/utils/dockerPrompt.d.ts +17 -0
  210. package/dist/utils/dockerPrompt.js +53 -0
  211. package/dist/utils/feature-flags.d.ts +59 -0
  212. package/dist/utils/feature-flags.js +215 -0
  213. package/dist/utils/files/base.d.ts +173 -0
  214. package/dist/utils/files/base.js +5 -0
  215. package/dist/utils/files/binary.d.ts +21 -0
  216. package/dist/utils/files/binary.js +65 -0
  217. package/dist/utils/files/docx.d.ts +47 -0
  218. package/dist/utils/files/docx.js +679 -0
  219. package/dist/utils/files/excel.d.ts +24 -0
  220. package/dist/utils/files/excel.js +437 -0
  221. package/dist/utils/files/factory.d.ts +41 -0
  222. package/dist/utils/files/factory.js +113 -0
  223. package/dist/utils/files/image.d.ts +21 -0
  224. package/dist/utils/files/image.js +78 -0
  225. package/dist/utils/files/index.d.ts +10 -0
  226. package/dist/utils/files/index.js +13 -0
  227. package/dist/utils/files/pdf.d.ts +32 -0
  228. package/dist/utils/files/pdf.js +142 -0
  229. package/dist/utils/files/text.d.ts +63 -0
  230. package/dist/utils/files/text.js +370 -0
  231. package/dist/utils/fuzzySearchLogger.d.ts +30 -0
  232. package/dist/utils/fuzzySearchLogger.js +126 -0
  233. package/dist/utils/lineEndingHandler.d.ts +21 -0
  234. package/dist/utils/lineEndingHandler.js +77 -0
  235. package/dist/utils/logger.d.ts +32 -0
  236. package/dist/utils/logger.js +72 -0
  237. package/dist/utils/mcp-ui-ab-test.d.ts +13 -0
  238. package/dist/utils/mcp-ui-ab-test.js +62 -0
  239. package/dist/utils/open-browser.d.ts +9 -0
  240. package/dist/utils/open-browser.js +42 -0
  241. package/dist/utils/process-detection.d.ts +23 -0
  242. package/dist/utils/process-detection.js +150 -0
  243. package/dist/utils/ripgrep-resolver.d.ts +9 -0
  244. package/dist/utils/ripgrep-resolver.js +74 -0
  245. package/dist/utils/system-info.d.ts +71 -0
  246. package/dist/utils/system-info.js +712 -0
  247. package/dist/utils/toolHistory.d.ts +86 -0
  248. package/dist/utils/toolHistory.js +257 -0
  249. package/dist/utils/trackTools.d.ts +6 -0
  250. package/dist/utils/trackTools.js +54 -0
  251. package/dist/utils/unsupportedParams.d.ts +24 -0
  252. package/dist/utils/unsupportedParams.js +66 -0
  253. package/dist/utils/usageTracker.d.ts +133 -0
  254. package/dist/utils/usageTracker.js +477 -0
  255. package/dist/utils/welcome-onboarding.d.ts +11 -0
  256. package/dist/utils/welcome-onboarding.js +99 -0
  257. package/dist/utils/withTimeout.d.ts +28 -0
  258. package/dist/utils/withTimeout.js +85 -0
  259. package/dist/version.d.ts +1 -0
  260. package/dist/version.js +1 -0
  261. package/package.json +131 -0
@@ -0,0 +1,314 @@
1
+ import os from 'os';
2
+ import readline from 'readline/promises';
3
+ import { MCPDevice } from '../device/device.js';
4
+ import { GatewayDeviceChannel } from '../device/gateway-channel.js';
5
+ import { GatewayDeviceConfigStore, captureGatewayConfigFromEnvironment, createGatewayProxyAgent } from '../device/gateway-config.js';
6
+ import { GatewayDeviceIdentity, deviceIdentityNeedsPairing } from '../device/gateway-identity.js';
7
+ import { pairGatewayDevice } from '../device/gateway-pairing.js';
8
+ import { bootstrapOfficialGatewayTrust } from '../device/official-trust.js';
9
+ import { GatewayDeviceStatusStore, effectiveGatewayStatus, formatGatewayStatus } from '../device/device-status.js';
10
+ import { chooseLinuxManager, discoverLinuxManagers, Pm2DeviceService, SystemdUserDeviceService } from '../device/linux-service.js';
11
+ import { RuntimeOwner, acquireRuntimeOwner, probeRuntimeOwner, stopRuntimeOwner } from '../device/runtime-owner.js';
12
+ import { WindowsDeviceService } from '../device/windows-service.js';
13
+ function commandName() {
14
+ const value = String(process.argv[3] || '').trim().toLowerCase();
15
+ if (['help', '-h', '--help'].includes(value))
16
+ return 'help';
17
+ if (!value || value.startsWith('--'))
18
+ return 'start';
19
+ return value;
20
+ }
21
+ function printRemoteUsage() {
22
+ console.log([
23
+ 'Usage: mcp-device [command]',
24
+ '',
25
+ 'Commands: start, login, logout, status, install, stop, uninstall'
26
+ ].join('\n'));
27
+ }
28
+ async function persistGatewayPayload(store, gatewayUrl, identity, payload) {
29
+ const record = await identity.loadOrCreate();
30
+ const current = await store.load();
31
+ return await store.update({
32
+ gatewayUrl,
33
+ deviceId: record.deviceId,
34
+ deviceName: payload?.device?.name || current.deviceName || os.hostname(),
35
+ identityPresent: true,
36
+ account: payload?.account || current.account,
37
+ connection: {
38
+ online: payload?.device?.online !== false,
39
+ connectionEpoch: payload?.device?.connectionEpoch ?? current.connection.connectionEpoch,
40
+ lastConnectedAt: payload?.device?.connectedAt || Date.now()
41
+ },
42
+ usage: payload?.usage ?? current.usage,
43
+ schema: payload?.schema ?? current.schema,
44
+ usageFreshAt: Date.now()
45
+ });
46
+ }
47
+ function controlAdapter() {
48
+ return { async call() { throw new Error('Tool calls are disabled for this control-only connection.'); } };
49
+ }
50
+ async function lifecycleSnapshot(status) {
51
+ const owner = await probeRuntimeOwner();
52
+ if (process.platform === 'win32' && status.deviceId) {
53
+ const service = await new WindowsDeviceService().status(status.deviceId);
54
+ const managers = service.installed ? [{
55
+ manager: service.registration === 'task' ? 'windows-task' : service.registration === 'run' ? 'windows-run' : 'windows-manual',
56
+ installed: service.installed,
57
+ running: service.running,
58
+ autostart: service.autostart
59
+ }] : [];
60
+ return { owner, service, managers };
61
+ }
62
+ if (process.platform === 'linux') {
63
+ const managers = await discoverLinuxManagers();
64
+ const installed = managers.some(manager => manager.installed);
65
+ const running = managers.some(manager => manager.running);
66
+ const autostart = managers.some(manager => manager.autostart);
67
+ return { owner, service: { installed, running, autostart }, managers };
68
+ }
69
+ return { owner, service: { installed: false, running: false, autostart: false }, managers: [] };
70
+ }
71
+ async function confirmRuntimeTakeover(existing) {
72
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
73
+ return false;
74
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
75
+ try {
76
+ const answer = await rl.question(`MCP Device is running in ${existing.mode} mode (PID ${existing.pid}). Stop it and take over? [Y/n] `);
77
+ return !['n', 'no'].includes(String(answer || '').trim().toLowerCase());
78
+ }
79
+ finally {
80
+ rl.close();
81
+ }
82
+ }
83
+ export async function applyServiceCommandStatus(store, command) {
84
+ if (command === 'stop' || command === 'uninstall')
85
+ await store.markOffline();
86
+ }
87
+ async function showStatus(json) {
88
+ const store = new GatewayDeviceStatusStore();
89
+ const status = await store.load();
90
+ const config = await new GatewayDeviceConfigStore().load();
91
+ const lifecycle = await lifecycleSnapshot(status);
92
+ const effectiveStatus = effectiveGatewayStatus(status, { runtimeOwned: Boolean(lifecycle.owner), serviceRunning: lifecycle.service.running });
93
+ console.log(formatGatewayStatus(effectiveStatus, {
94
+ json,
95
+ service: lifecycle.service,
96
+ runtime: lifecycle.owner ? { mode: lifecycle.owner.mode, pid: lifecycle.owner.pid } : null,
97
+ managers: lifecycle.managers,
98
+ security: { protocolFloor: config.securityProtocolFloor, appCaProvisioned: Boolean(config.appCaPem) },
99
+ proxy: { mode: config.proxy.mode }
100
+ }));
101
+ }
102
+ async function withBootstrapOwner(action, allowTakeover = true) {
103
+ const owner = new RuntimeOwner({ mode: 'bootstrap' });
104
+ await acquireRuntimeOwner(owner, { confirmTakeover: allowTakeover ? confirmRuntimeTakeover : undefined });
105
+ try {
106
+ await owner.bootstrap();
107
+ await bootstrapOfficialGatewayTrust(new GatewayDeviceConfigStore());
108
+ return await action();
109
+ }
110
+ finally {
111
+ await owner.release();
112
+ }
113
+ }
114
+ async function ensureLinkedDeviceUnderOwner(forcePair = false) {
115
+ const store = new GatewayDeviceStatusStore();
116
+ const current = await store.load();
117
+ const config = await captureGatewayConfigFromEnvironment(new GatewayDeviceConfigStore(), { fallbackGatewayUrl: current.gatewayUrl });
118
+ const gatewayUrl = String(config.gatewayUrl || '').trim();
119
+ const identity = new GatewayDeviceIdentity();
120
+ const record = await identity.loadOrCreate();
121
+ await store.update({
122
+ gatewayUrl,
123
+ deviceId: record.deviceId,
124
+ deviceName: current.deviceName || os.hostname(),
125
+ identityPresent: true
126
+ });
127
+ if (!forcePair && !deviceIdentityNeedsPairing(record)) {
128
+ return { deviceId: record.deviceId, accountLabel: current.account.label };
129
+ }
130
+ const proxy = createGatewayProxyAgent(config);
131
+ let channel;
132
+ try {
133
+ const pairing = await pairGatewayDevice({ gatewayUrl, identity, proxyAgent: proxy.agent, deviceName: os.hostname() });
134
+ await store.update({
135
+ gatewayUrl,
136
+ deviceId: record.deviceId,
137
+ deviceName: os.hostname(),
138
+ identityPresent: true,
139
+ account: { connected: false, label: pairing.account.label },
140
+ connection: { ...current.connection, online: false }
141
+ });
142
+ channel = new GatewayDeviceChannel({
143
+ gatewayUrl,
144
+ pairingGrant: pairing.enrollmentGrant,
145
+ identity,
146
+ proxyAgent: proxy.agent,
147
+ securityProtocolFloor: config.securityProtocolFloor,
148
+ appCaPem: config.appCaPem || undefined,
149
+ adapter: controlAdapter(),
150
+ agentVersion: process.env.npm_package_version,
151
+ onStatus: async (payload) => { await persistGatewayPayload(store, gatewayUrl, identity, payload); }
152
+ });
153
+ await channel.start();
154
+ const linked = await store.load();
155
+ return { deviceId: record.deviceId, accountLabel: linked.account.label };
156
+ }
157
+ finally {
158
+ await channel?.stop().catch(() => { });
159
+ proxy.agent.destroy();
160
+ await store.markOffline().catch(() => { });
161
+ }
162
+ }
163
+ async function login() {
164
+ return await withBootstrapOwner(async () => {
165
+ const linked = await ensureLinkedDeviceUnderOwner(true);
166
+ console.log(`Account connected: ${linked.accountLabel || 'gateway account'}`);
167
+ });
168
+ }
169
+ async function logout() {
170
+ return await withBootstrapOwner(async () => {
171
+ const store = new GatewayDeviceStatusStore();
172
+ const current = await store.load();
173
+ const config = await new GatewayDeviceConfigStore().load();
174
+ const gatewayUrl = String(config.gatewayUrl || current.gatewayUrl || '').trim();
175
+ if (!gatewayUrl || !current.identityPresent)
176
+ throw new Error('No connected gateway identity is available to log out.');
177
+ const proxy = createGatewayProxyAgent(config);
178
+ const identity = new GatewayDeviceIdentity();
179
+ const channel = new GatewayDeviceChannel({
180
+ gatewayUrl,
181
+ identity,
182
+ proxyAgent: proxy.agent,
183
+ securityProtocolFloor: config.securityProtocolFloor,
184
+ appCaPem: config.appCaPem || undefined,
185
+ adapter: controlAdapter(),
186
+ agentVersion: process.env.npm_package_version,
187
+ onStatus: async (payload) => { await persistGatewayPayload(store, gatewayUrl, identity, payload); }
188
+ });
189
+ try {
190
+ await channel.start();
191
+ await channel.logoutAccount();
192
+ console.log('Account disconnected from this device.');
193
+ }
194
+ finally {
195
+ await channel.stop().catch(() => { });
196
+ proxy.agent.destroy();
197
+ await store.markOffline().catch(() => { });
198
+ }
199
+ });
200
+ }
201
+ async function waitForOwnerRelease(timeoutMs = 5000) {
202
+ const deadline = Date.now() + timeoutMs;
203
+ while (Date.now() < deadline) {
204
+ if (!await probeRuntimeOwner())
205
+ return;
206
+ await new Promise(resolve => setTimeout(resolve, 50));
207
+ }
208
+ throw new Error('MCP Device runtime did not release ownership after authenticated stop.');
209
+ }
210
+ async function serviceCommand(command) {
211
+ const store = new GatewayDeviceStatusStore();
212
+ const owner = await probeRuntimeOwner();
213
+ if ((command === 'stop' || command === 'uninstall') && owner) {
214
+ if (!await stopRuntimeOwner())
215
+ throw new Error('MCP Device runtime did not accept authenticated stop.');
216
+ await waitForOwnerRelease();
217
+ await store.markOffline();
218
+ if (command === 'stop') {
219
+ console.log('MCP Device stop requested.');
220
+ return;
221
+ }
222
+ }
223
+ if (!['win32', 'linux'].includes(process.platform)) {
224
+ throw new Error('Background lifecycle is currently supported on Windows and Linux only.');
225
+ }
226
+ if (command === 'install') {
227
+ const linuxManager = process.platform === 'linux' ? await chooseLinuxManager() : null;
228
+ const deviceId = await withBootstrapOwner(async () => {
229
+ const linked = await ensureLinkedDeviceUnderOwner(false);
230
+ if (process.platform === 'win32')
231
+ await new WindowsDeviceService().install({ deviceId: linked.deviceId });
232
+ else if (linuxManager === 'pm2')
233
+ await new Pm2DeviceService().install();
234
+ else
235
+ await new SystemdUserDeviceService().install();
236
+ return linked.deviceId;
237
+ });
238
+ if (process.platform === 'win32')
239
+ await new WindowsDeviceService().start(deviceId);
240
+ console.log(`Background device installed and started: ${deviceId}`);
241
+ return;
242
+ }
243
+ if (command === 'stop') {
244
+ const status = await store.load();
245
+ if (process.platform === 'win32') {
246
+ if (!status.deviceId || !status.identityPresent)
247
+ throw new Error('No installed MCP Device identity was found.');
248
+ await new WindowsDeviceService().stop(status.deviceId);
249
+ }
250
+ else {
251
+ const systemd = new SystemdUserDeviceService();
252
+ const pm2 = new Pm2DeviceService();
253
+ if ((await systemd.status()).installed)
254
+ await systemd.stop();
255
+ if ((await pm2.status()).installed)
256
+ await pm2.stop();
257
+ }
258
+ await applyServiceCommandStatus(store, command);
259
+ console.log('MCP Device background runtime stop requested.');
260
+ return;
261
+ }
262
+ await withBootstrapOwner(async () => {
263
+ const status = await store.load();
264
+ if (process.platform === 'win32') {
265
+ if (!status.deviceId || !status.identityPresent)
266
+ throw new Error('No installed MCP Device identity was found.');
267
+ await new WindowsDeviceService().uninstall(status.deviceId);
268
+ }
269
+ else {
270
+ await new SystemdUserDeviceService().uninstall();
271
+ await new Pm2DeviceService().uninstall();
272
+ }
273
+ await applyServiceCommandStatus(store, command);
274
+ }, false);
275
+ console.log('MCP Device background registration removed; identity and account state were preserved.');
276
+ }
277
+ export async function runRemote() {
278
+ // MCP Device owns its account-scoped usage telemetry at the gateway.
279
+ // Never send inherited Desktop Commander analytics from the remote execution path.
280
+ process.env.DESKTOP_COMMANDER_DISABLE_TELEMETRY = 'true';
281
+ const disableNoSleep = process.argv.includes('--disable-no-sleep');
282
+ const verbose = process.argv.includes('--debug');
283
+ if (!verbose)
284
+ console.debug = () => { };
285
+ else
286
+ console.debug('[DEBUG] Verbose mode:', verbose);
287
+ const command = commandName();
288
+ if (command === 'help')
289
+ return printRemoteUsage();
290
+ if (command === 'status')
291
+ return await showStatus(process.argv.includes('--json'));
292
+ if (command === 'login')
293
+ return await login();
294
+ if (command === 'logout')
295
+ return await logout();
296
+ if (command === 'install' || command === 'stop' || command === 'uninstall') {
297
+ return await serviceCommand(command);
298
+ }
299
+ if (command !== 'start') {
300
+ throw new Error('Unknown MCP Device command. Use start, status, login, logout, install, stop, or uninstall.');
301
+ }
302
+ if (!disableNoSleep && os.platform() === 'darwin') {
303
+ try {
304
+ const { default: caffeinate } = await import('caffeinate');
305
+ caffeinate({ pid: process.pid });
306
+ console.log('No sleep mode enabled');
307
+ }
308
+ catch (error) {
309
+ console.warn('Failed to start caffeinate:', error);
310
+ }
311
+ }
312
+ const device = new MCPDevice();
313
+ await device.start({ confirmTakeover: process.argv.includes('--service') ? undefined : confirmRuntimeTakeover });
314
+ }
@@ -0,0 +1,147 @@
1
+ import { ChildProcess } from 'child_process';
2
+ export interface SearchResult {
3
+ file: string;
4
+ line?: number;
5
+ match?: string;
6
+ type: 'file' | 'content';
7
+ }
8
+ export interface SearchSession {
9
+ id: string;
10
+ process: ChildProcess;
11
+ results: SearchResult[];
12
+ isComplete: boolean;
13
+ isError: boolean;
14
+ error?: string;
15
+ startTime: number;
16
+ lastReadTime: number;
17
+ options: SearchSessionOptions;
18
+ buffer: string;
19
+ totalMatches: number;
20
+ totalContextLines: number;
21
+ wasIncomplete?: boolean;
22
+ }
23
+ export interface SearchSessionOptions {
24
+ rootPath: string;
25
+ pattern: string;
26
+ searchType: 'files' | 'content';
27
+ filePattern?: string;
28
+ ignoreCase?: boolean;
29
+ maxResults?: number;
30
+ includeHidden?: boolean;
31
+ contextLines?: number;
32
+ timeout?: number;
33
+ earlyTermination?: boolean;
34
+ literalSearch?: boolean;
35
+ }
36
+ /**
37
+ * Search Session Manager - handles ripgrep processes like terminal sessions
38
+ * Supports both file search and content search with progressive results
39
+ */ export declare class SearchManager {
40
+ private sessions;
41
+ private sessionCounter;
42
+ /**
43
+ * Start a new search session (like start_process)
44
+ * Returns immediately with initial state and results
45
+ */
46
+ startSearch(options: SearchSessionOptions): Promise<{
47
+ sessionId: string;
48
+ isComplete: boolean;
49
+ isError: boolean;
50
+ results: SearchResult[];
51
+ totalResults: number;
52
+ runtime: number;
53
+ }>;
54
+ /**
55
+ * Read search results with offset-based pagination (like read_file)
56
+ * Supports both range reading and tail behavior
57
+ */
58
+ readSearchResults(sessionId: string, offset?: number, length?: number): {
59
+ results: SearchResult[];
60
+ returnedCount: number;
61
+ totalResults: number;
62
+ totalMatches: number;
63
+ isComplete: boolean;
64
+ isError: boolean;
65
+ error?: string;
66
+ hasMoreResults: boolean;
67
+ runtime: number;
68
+ wasIncomplete?: boolean;
69
+ };
70
+ /**
71
+ * Terminate a search session (like force_terminate)
72
+ */
73
+ terminateSearch(sessionId: string): boolean;
74
+ /**
75
+ * Get list of active search sessions (like list_sessions)
76
+ */
77
+ listSearchSessions(): Array<{
78
+ id: string;
79
+ searchType: string;
80
+ pattern: string;
81
+ isComplete: boolean;
82
+ isError: boolean;
83
+ runtime: number;
84
+ totalResults: number;
85
+ }>;
86
+ /**
87
+ * Search Excel files for content matches
88
+ * Called during content search to include Excel files alongside text files
89
+ * Searches ALL sheets in each Excel file (row-wise for cross-column matching)
90
+ *
91
+ * TODO: Refactor - Extract Excel search logic to separate module (src/utils/search/excel-search.ts)
92
+ * and inject into SearchManager, similar to how file handlers are structured in src/utils/files/
93
+ * This would allow adding other file type searches (PDF, etc.) without bloating search-manager.ts
94
+ */
95
+ private searchExcelFiles;
96
+ /**
97
+ * Find all Excel files in a directory recursively
98
+ */
99
+ private findExcelFiles;
100
+ /**
101
+ * Determine if DOCX search should be included based on context
102
+ */
103
+ private shouldIncludeDocxSearch;
104
+ /**
105
+ * Search DOCX files for content matches
106
+ * Extracts <w:t> text from document.xml and searches it
107
+ */
108
+ private searchDocxFiles;
109
+ /**
110
+ * Find all DOCX files in a directory recursively
111
+ */
112
+ private findDocxFiles;
113
+ /**
114
+ * Extract context around a match for display (show surrounding text)
115
+ */
116
+ private getMatchContext;
117
+ /**
118
+ * Clean up completed sessions older than specified time
119
+ * Called automatically by cleanup interval
120
+ */
121
+ cleanupSessions(maxAge?: number): void;
122
+ /**
123
+ * Get total number of active sessions (excluding completed ones)
124
+ */
125
+ getActiveSessionCount(): number;
126
+ /**
127
+ * Detect if pattern looks like an exact filename
128
+ * (has file extension and no glob wildcards)
129
+ */
130
+ private isExactFilename;
131
+ /**
132
+ * Detect if pattern contains glob wildcards
133
+ */
134
+ private isGlobPattern;
135
+ /**
136
+ * Determine if Excel search should be included based on context
137
+ * Only searches Excel files when:
138
+ * - filePattern explicitly targets Excel files (*.xlsx, *.xls, *.xlsm, *.xlsb)
139
+ * - or the rootPath itself is an Excel file
140
+ */
141
+ private shouldIncludeExcelSearch;
142
+ private buildRipgrepArgs;
143
+ private setupProcessHandlers;
144
+ private processBufferedOutput;
145
+ private parseLine;
146
+ }
147
+ export declare const searchManager: SearchManager;