loopsy 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 (262) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +425 -0
  3. package/dist/cli/commands/connect.d.ts +2 -0
  4. package/dist/cli/commands/connect.d.ts.map +1 -0
  5. package/dist/cli/commands/connect.js +120 -0
  6. package/dist/cli/commands/connect.js.map +1 -0
  7. package/dist/cli/commands/context.d.ts +2 -0
  8. package/dist/cli/commands/context.d.ts.map +1 -0
  9. package/dist/cli/commands/context.js +39 -0
  10. package/dist/cli/commands/context.js.map +1 -0
  11. package/dist/cli/commands/daemon.d.ts +4 -0
  12. package/dist/cli/commands/daemon.d.ts.map +1 -0
  13. package/dist/cli/commands/daemon.js +55 -0
  14. package/dist/cli/commands/daemon.js.map +1 -0
  15. package/dist/cli/commands/dashboard.d.ts +2 -0
  16. package/dist/cli/commands/dashboard.d.ts.map +1 -0
  17. package/dist/cli/commands/dashboard.js +24 -0
  18. package/dist/cli/commands/dashboard.js.map +1 -0
  19. package/dist/cli/commands/doctor.d.ts +2 -0
  20. package/dist/cli/commands/doctor.d.ts.map +1 -0
  21. package/dist/cli/commands/doctor.js +130 -0
  22. package/dist/cli/commands/doctor.js.map +1 -0
  23. package/dist/cli/commands/exec.d.ts +2 -0
  24. package/dist/cli/commands/exec.d.ts.map +1 -0
  25. package/dist/cli/commands/exec.js +34 -0
  26. package/dist/cli/commands/exec.js.map +1 -0
  27. package/dist/cli/commands/init.d.ts +2 -0
  28. package/dist/cli/commands/init.d.ts.map +1 -0
  29. package/dist/cli/commands/init.js +71 -0
  30. package/dist/cli/commands/init.js.map +1 -0
  31. package/dist/cli/commands/key.d.ts +2 -0
  32. package/dist/cli/commands/key.d.ts.map +1 -0
  33. package/dist/cli/commands/key.js +39 -0
  34. package/dist/cli/commands/key.js.map +1 -0
  35. package/dist/cli/commands/logs.d.ts +2 -0
  36. package/dist/cli/commands/logs.d.ts.map +1 -0
  37. package/dist/cli/commands/logs.js +26 -0
  38. package/dist/cli/commands/logs.js.map +1 -0
  39. package/dist/cli/commands/mcp.d.ts +4 -0
  40. package/dist/cli/commands/mcp.d.ts.map +1 -0
  41. package/dist/cli/commands/mcp.js +70 -0
  42. package/dist/cli/commands/mcp.js.map +1 -0
  43. package/dist/cli/commands/pair.d.ts +6 -0
  44. package/dist/cli/commands/pair.d.ts.map +1 -0
  45. package/dist/cli/commands/pair.js +208 -0
  46. package/dist/cli/commands/pair.js.map +1 -0
  47. package/dist/cli/commands/peers.d.ts +2 -0
  48. package/dist/cli/commands/peers.d.ts.map +1 -0
  49. package/dist/cli/commands/peers.js +29 -0
  50. package/dist/cli/commands/peers.js.map +1 -0
  51. package/dist/cli/commands/service/linux.d.ts +7 -0
  52. package/dist/cli/commands/service/linux.d.ts.map +1 -0
  53. package/dist/cli/commands/service/linux.js +86 -0
  54. package/dist/cli/commands/service/linux.js.map +1 -0
  55. package/dist/cli/commands/service/macos.d.ts +7 -0
  56. package/dist/cli/commands/service/macos.d.ts.map +1 -0
  57. package/dist/cli/commands/service/macos.js +83 -0
  58. package/dist/cli/commands/service/macos.js.map +1 -0
  59. package/dist/cli/commands/service/windows.d.ts +7 -0
  60. package/dist/cli/commands/service/windows.d.ts.map +1 -0
  61. package/dist/cli/commands/service/windows.js +52 -0
  62. package/dist/cli/commands/service/windows.js.map +1 -0
  63. package/dist/cli/commands/service.d.ts +4 -0
  64. package/dist/cli/commands/service.d.ts.map +1 -0
  65. package/dist/cli/commands/service.js +68 -0
  66. package/dist/cli/commands/service.js.map +1 -0
  67. package/dist/cli/commands/session.d.ts +8 -0
  68. package/dist/cli/commands/session.d.ts.map +1 -0
  69. package/dist/cli/commands/session.js +270 -0
  70. package/dist/cli/commands/session.js.map +1 -0
  71. package/dist/cli/commands/transfer.d.ts +3 -0
  72. package/dist/cli/commands/transfer.d.ts.map +1 -0
  73. package/dist/cli/commands/transfer.js +57 -0
  74. package/dist/cli/commands/transfer.js.map +1 -0
  75. package/dist/cli/index.d.ts +3 -0
  76. package/dist/cli/index.d.ts.map +1 -0
  77. package/dist/cli/index.js +89 -0
  78. package/dist/cli/index.js.map +1 -0
  79. package/dist/cli/package-root.d.ts +27 -0
  80. package/dist/cli/package-root.d.ts.map +1 -0
  81. package/dist/cli/package-root.js +54 -0
  82. package/dist/cli/package-root.js.map +1 -0
  83. package/dist/cli/utils.d.ts +11 -0
  84. package/dist/cli/utils.d.ts.map +1 -0
  85. package/dist/cli/utils.js +48 -0
  86. package/dist/cli/utils.js.map +1 -0
  87. package/dist/daemon/config.d.ts +4 -0
  88. package/dist/daemon/config.d.ts.map +1 -0
  89. package/dist/daemon/config.js +58 -0
  90. package/dist/daemon/config.js.map +1 -0
  91. package/dist/daemon/hooks/permission-hook.mjs +108 -0
  92. package/dist/daemon/index.d.ts +3 -0
  93. package/dist/daemon/index.d.ts.map +1 -0
  94. package/dist/daemon/index.js +3 -0
  95. package/dist/daemon/index.js.map +1 -0
  96. package/dist/daemon/main.d.ts +3 -0
  97. package/dist/daemon/main.d.ts.map +1 -0
  98. package/dist/daemon/main.js +28 -0
  99. package/dist/daemon/main.js.map +1 -0
  100. package/dist/daemon/middleware/auth.d.ts +3 -0
  101. package/dist/daemon/middleware/auth.d.ts.map +1 -0
  102. package/dist/daemon/middleware/auth.js +22 -0
  103. package/dist/daemon/middleware/auth.js.map +1 -0
  104. package/dist/daemon/routes/ai-tasks.d.ts +4 -0
  105. package/dist/daemon/routes/ai-tasks.d.ts.map +1 -0
  106. package/dist/daemon/routes/ai-tasks.js +146 -0
  107. package/dist/daemon/routes/ai-tasks.js.map +1 -0
  108. package/dist/daemon/routes/context.d.ts +4 -0
  109. package/dist/daemon/routes/context.d.ts.map +1 -0
  110. package/dist/daemon/routes/context.js +49 -0
  111. package/dist/daemon/routes/context.js.map +1 -0
  112. package/dist/daemon/routes/execute.d.ts +4 -0
  113. package/dist/daemon/routes/execute.d.ts.map +1 -0
  114. package/dist/daemon/routes/execute.js +74 -0
  115. package/dist/daemon/routes/execute.js.map +1 -0
  116. package/dist/daemon/routes/health.d.ts +15 -0
  117. package/dist/daemon/routes/health.d.ts.map +1 -0
  118. package/dist/daemon/routes/health.js +38 -0
  119. package/dist/daemon/routes/health.js.map +1 -0
  120. package/dist/daemon/routes/pair.d.ts +11 -0
  121. package/dist/daemon/routes/pair.d.ts.map +1 -0
  122. package/dist/daemon/routes/pair.js +149 -0
  123. package/dist/daemon/routes/pair.js.map +1 -0
  124. package/dist/daemon/routes/peers.d.ts +5 -0
  125. package/dist/daemon/routes/peers.d.ts.map +1 -0
  126. package/dist/daemon/routes/peers.js +69 -0
  127. package/dist/daemon/routes/peers.js.map +1 -0
  128. package/dist/daemon/routes/transfer.d.ts +4 -0
  129. package/dist/daemon/routes/transfer.d.ts.map +1 -0
  130. package/dist/daemon/routes/transfer.js +135 -0
  131. package/dist/daemon/routes/transfer.js.map +1 -0
  132. package/dist/daemon/server.d.ts +8 -0
  133. package/dist/daemon/server.d.ts.map +1 -0
  134. package/dist/daemon/server.js +170 -0
  135. package/dist/daemon/server.js.map +1 -0
  136. package/dist/daemon/services/ai-task-manager.d.ts +56 -0
  137. package/dist/daemon/services/ai-task-manager.d.ts.map +1 -0
  138. package/dist/daemon/services/ai-task-manager.js +491 -0
  139. package/dist/daemon/services/ai-task-manager.js.map +1 -0
  140. package/dist/daemon/services/audit-logger.d.ts +16 -0
  141. package/dist/daemon/services/audit-logger.d.ts.map +1 -0
  142. package/dist/daemon/services/audit-logger.js +23 -0
  143. package/dist/daemon/services/audit-logger.js.map +1 -0
  144. package/dist/daemon/services/context-store.d.ts +17 -0
  145. package/dist/daemon/services/context-store.d.ts.map +1 -0
  146. package/dist/daemon/services/context-store.js +97 -0
  147. package/dist/daemon/services/context-store.js.map +1 -0
  148. package/dist/daemon/services/job-manager.d.ts +19 -0
  149. package/dist/daemon/services/job-manager.d.ts.map +1 -0
  150. package/dist/daemon/services/job-manager.js +92 -0
  151. package/dist/daemon/services/job-manager.js.map +1 -0
  152. package/dist/daemon/services/tls-manager.d.ts +33 -0
  153. package/dist/daemon/services/tls-manager.d.ts.map +1 -0
  154. package/dist/daemon/services/tls-manager.js +114 -0
  155. package/dist/daemon/services/tls-manager.js.map +1 -0
  156. package/dist/daemon/utils/which.d.ts +2 -0
  157. package/dist/daemon/utils/which.d.ts.map +1 -0
  158. package/dist/daemon/utils/which.js +18 -0
  159. package/dist/daemon/utils/which.js.map +1 -0
  160. package/dist/dashboard/config.d.ts +8 -0
  161. package/dist/dashboard/config.d.ts.map +1 -0
  162. package/dist/dashboard/config.js +22 -0
  163. package/dist/dashboard/config.js.map +1 -0
  164. package/dist/dashboard/public/app.js +120 -0
  165. package/dist/dashboard/public/icon-192.png +0 -0
  166. package/dist/dashboard/public/icon-512.png +0 -0
  167. package/dist/dashboard/public/index.html +85 -0
  168. package/dist/dashboard/public/manifest.json +12 -0
  169. package/dist/dashboard/public/style.css +784 -0
  170. package/dist/dashboard/public/sw.js +31 -0
  171. package/dist/dashboard/public/views/ai-tasks.js +679 -0
  172. package/dist/dashboard/public/views/context.js +167 -0
  173. package/dist/dashboard/public/views/messages.js +263 -0
  174. package/dist/dashboard/public/views/overview.js +228 -0
  175. package/dist/dashboard/public/views/peers.js +136 -0
  176. package/dist/dashboard/public/views/terminal.js +153 -0
  177. package/dist/dashboard/routes/ai-tasks.d.ts +3 -0
  178. package/dist/dashboard/routes/ai-tasks.d.ts.map +1 -0
  179. package/dist/dashboard/routes/ai-tasks.js +193 -0
  180. package/dist/dashboard/routes/ai-tasks.js.map +1 -0
  181. package/dist/dashboard/routes/messages.d.ts +3 -0
  182. package/dist/dashboard/routes/messages.d.ts.map +1 -0
  183. package/dist/dashboard/routes/messages.js +137 -0
  184. package/dist/dashboard/routes/messages.js.map +1 -0
  185. package/dist/dashboard/routes/peer-utils.d.ts +17 -0
  186. package/dist/dashboard/routes/peer-utils.d.ts.map +1 -0
  187. package/dist/dashboard/routes/peer-utils.js +193 -0
  188. package/dist/dashboard/routes/peer-utils.js.map +1 -0
  189. package/dist/dashboard/routes/peers-all.d.ts +3 -0
  190. package/dist/dashboard/routes/peers-all.d.ts.map +1 -0
  191. package/dist/dashboard/routes/peers-all.js +8 -0
  192. package/dist/dashboard/routes/peers-all.js.map +1 -0
  193. package/dist/dashboard/routes/proxy.d.ts +3 -0
  194. package/dist/dashboard/routes/proxy.d.ts.map +1 -0
  195. package/dist/dashboard/routes/proxy.js +59 -0
  196. package/dist/dashboard/routes/proxy.js.map +1 -0
  197. package/dist/dashboard/routes/sessions.d.ts +3 -0
  198. package/dist/dashboard/routes/sessions.d.ts.map +1 -0
  199. package/dist/dashboard/routes/sessions.js +64 -0
  200. package/dist/dashboard/routes/sessions.js.map +1 -0
  201. package/dist/dashboard/routes/sse.d.ts +3 -0
  202. package/dist/dashboard/routes/sse.d.ts.map +1 -0
  203. package/dist/dashboard/routes/sse.js +49 -0
  204. package/dist/dashboard/routes/sse.js.map +1 -0
  205. package/dist/dashboard/routes/status.d.ts +3 -0
  206. package/dist/dashboard/routes/status.d.ts.map +1 -0
  207. package/dist/dashboard/routes/status.js +38 -0
  208. package/dist/dashboard/routes/status.js.map +1 -0
  209. package/dist/dashboard/server.d.ts +3 -0
  210. package/dist/dashboard/server.d.ts.map +1 -0
  211. package/dist/dashboard/server.js +77 -0
  212. package/dist/dashboard/server.js.map +1 -0
  213. package/dist/dashboard/session-manager.d.ts +17 -0
  214. package/dist/dashboard/session-manager.d.ts.map +1 -0
  215. package/dist/dashboard/session-manager.js +225 -0
  216. package/dist/dashboard/session-manager.js.map +1 -0
  217. package/dist/discovery/health-checker.d.ts +15 -0
  218. package/dist/discovery/health-checker.d.ts.map +1 -0
  219. package/dist/discovery/health-checker.js +47 -0
  220. package/dist/discovery/health-checker.js.map +1 -0
  221. package/dist/discovery/index.d.ts +4 -0
  222. package/dist/discovery/index.d.ts.map +1 -0
  223. package/dist/discovery/index.js +4 -0
  224. package/dist/discovery/index.js.map +1 -0
  225. package/dist/discovery/mdns.d.ts +21 -0
  226. package/dist/discovery/mdns.d.ts.map +1 -0
  227. package/dist/discovery/mdns.js +83 -0
  228. package/dist/discovery/mdns.js.map +1 -0
  229. package/dist/discovery/peer-registry.d.ts +18 -0
  230. package/dist/discovery/peer-registry.d.ts.map +1 -0
  231. package/dist/discovery/peer-registry.js +81 -0
  232. package/dist/discovery/peer-registry.js.map +1 -0
  233. package/dist/mcp-server/daemon-client.d.ts +69 -0
  234. package/dist/mcp-server/daemon-client.d.ts.map +1 -0
  235. package/dist/mcp-server/daemon-client.js +281 -0
  236. package/dist/mcp-server/daemon-client.js.map +1 -0
  237. package/dist/mcp-server/index.d.ts +3 -0
  238. package/dist/mcp-server/index.d.ts.map +1 -0
  239. package/dist/mcp-server/index.js +406 -0
  240. package/dist/mcp-server/index.js.map +1 -0
  241. package/dist/protocol/constants.d.ts +66 -0
  242. package/dist/protocol/constants.d.ts.map +1 -0
  243. package/dist/protocol/constants.js +66 -0
  244. package/dist/protocol/constants.js.map +1 -0
  245. package/dist/protocol/errors.d.ts +47 -0
  246. package/dist/protocol/errors.d.ts.map +1 -0
  247. package/dist/protocol/errors.js +62 -0
  248. package/dist/protocol/errors.js.map +1 -0
  249. package/dist/protocol/index.d.ts +5 -0
  250. package/dist/protocol/index.d.ts.map +1 -0
  251. package/dist/protocol/index.js +5 -0
  252. package/dist/protocol/index.js.map +1 -0
  253. package/dist/protocol/schemas.d.ts +209 -0
  254. package/dist/protocol/schemas.d.ts.map +1 -0
  255. package/dist/protocol/schemas.js +115 -0
  256. package/dist/protocol/schemas.js.map +1 -0
  257. package/dist/protocol/types.d.ts +302 -0
  258. package/dist/protocol/types.d.ts.map +1 -0
  259. package/dist/protocol/types.js +2 -0
  260. package/dist/protocol/types.js.map +1 -0
  261. package/package.json +50 -0
  262. package/scripts/postinstall.mjs +42 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAQA,wBAAsB,WAAW,CAAC,IAAI,EAAE,GAAG,iBAgB1C"}
@@ -0,0 +1,26 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { CONFIG_DIR } from '@loopsy/protocol';
5
+ import { spawn } from 'node:child_process';
6
+ const AUDIT_FILE = join(homedir(), CONFIG_DIR, 'logs', 'audit.jsonl');
7
+ export async function logsCommand(argv) {
8
+ if (argv.follow) {
9
+ const tail = spawn('tail', ['-f', AUDIT_FILE], { stdio: 'inherit' });
10
+ tail.on('error', () => {
11
+ console.error('Could not tail logs. Is the daemon running?');
12
+ });
13
+ }
14
+ else {
15
+ try {
16
+ const stream = createReadStream(AUDIT_FILE, 'utf-8');
17
+ for await (const chunk of stream) {
18
+ process.stdout.write(chunk);
19
+ }
20
+ }
21
+ catch {
22
+ console.log('No logs found. Is the daemon running?');
23
+ }
24
+ }
25
+ }
26
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAEtE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAS;IACzC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACpB,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACrD,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function mcpAddCommand(): Promise<void>;
2
+ export declare function mcpRemoveCommand(): Promise<void>;
3
+ export declare function mcpStatusCommand(): Promise<void>;
4
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAYA,wBAAsB,aAAa,kBAyBlC;AAED,wBAAsB,gBAAgB,kBAerC;AAED,wBAAsB,gBAAgB,kBAgBrC"}
@@ -0,0 +1,70 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { mcpServerPath } from '../package-root.js';
3
+ function claudeAvailable() {
4
+ try {
5
+ execSync('claude --version', { stdio: 'ignore' });
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function mcpAddCommand() {
13
+ const serverPath = mcpServerPath();
14
+ if (!claudeAvailable()) {
15
+ console.log('Claude Code CLI not found on PATH.');
16
+ console.log('');
17
+ console.log('To register manually, run:');
18
+ console.log(` claude mcp add loopsy -- node ${serverPath}`);
19
+ return;
20
+ }
21
+ try {
22
+ // Remove existing registration first (ignore errors)
23
+ try {
24
+ execSync('claude mcp remove loopsy', { stdio: 'ignore' });
25
+ }
26
+ catch { }
27
+ execSync(`claude mcp add loopsy -- node ${serverPath}`, { stdio: 'inherit' });
28
+ console.log('MCP server registered with Claude Code');
29
+ }
30
+ catch (err) {
31
+ console.error('Failed to register MCP server:', err.message);
32
+ console.log('');
33
+ console.log('To register manually, run:');
34
+ console.log(` claude mcp add loopsy -- node ${serverPath}`);
35
+ }
36
+ }
37
+ export async function mcpRemoveCommand() {
38
+ if (!claudeAvailable()) {
39
+ console.log('Claude Code CLI not found on PATH.');
40
+ console.log('');
41
+ console.log('To remove manually, run:');
42
+ console.log(' claude mcp remove loopsy');
43
+ return;
44
+ }
45
+ try {
46
+ execSync('claude mcp remove loopsy', { stdio: 'inherit' });
47
+ console.log('MCP server unregistered from Claude Code');
48
+ }
49
+ catch (err) {
50
+ console.error('Failed to unregister MCP server:', err.message);
51
+ }
52
+ }
53
+ export async function mcpStatusCommand() {
54
+ if (!claudeAvailable()) {
55
+ console.log('Claude Code CLI not found on PATH');
56
+ return;
57
+ }
58
+ try {
59
+ const output = execSync('claude mcp list', { encoding: 'utf-8' });
60
+ const hasLoopsy = output.includes('loopsy');
61
+ console.log(`MCP server: ${hasLoopsy ? 'registered' : 'not registered'}`);
62
+ if (!hasLoopsy) {
63
+ console.log('Run "loopsy mcp add" to register');
64
+ }
65
+ }
66
+ catch {
67
+ console.log('Could not check MCP status');
68
+ }
69
+ }
70
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `loopsy pair` — Start a pairing session (Machine A, the one waiting)
3
+ * `loopsy pair <address>` — Connect to Machine A (Machine B, the one initiating)
4
+ */
5
+ export declare function pairCommand(argv: any): Promise<void>;
6
+ //# sourceMappingURL=pair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.d.ts","sourceRoot":"","sources":["../../src/commands/pair.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,GAAG,iBAQ1C"}
@@ -0,0 +1,208 @@
1
+ import { createECDH, createHash } from 'node:crypto';
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { homedir, hostname as osHostname } from 'node:os';
5
+ import { createInterface } from 'node:readline';
6
+ import { parse as parseYaml, stringify as toYaml } from 'yaml';
7
+ import { CONFIG_DIR, CONFIG_FILE, DEFAULT_PORT } from '@loopsy/protocol';
8
+ import { daemonRequest } from '../utils.js';
9
+ function prompt(question) {
10
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
11
+ return new Promise((resolve) => {
12
+ rl.question(question, (answer) => {
13
+ rl.close();
14
+ resolve(answer.trim());
15
+ });
16
+ });
17
+ }
18
+ /**
19
+ * `loopsy pair` — Start a pairing session (Machine A, the one waiting)
20
+ * `loopsy pair <address>` — Connect to Machine A (Machine B, the one initiating)
21
+ */
22
+ export async function pairCommand(argv) {
23
+ const target = argv.address;
24
+ if (target) {
25
+ await pairAsInitiator(target);
26
+ }
27
+ else {
28
+ await pairAsWaiter();
29
+ }
30
+ }
31
+ /** Machine A: start a pairing session and wait for peer to connect */
32
+ async function pairAsWaiter() {
33
+ console.log('Starting pairing session...');
34
+ console.log('');
35
+ const result = await daemonRequest('/pair/start', { method: 'POST' });
36
+ const expiresIn = Math.round((result.expiresAt - Date.now()) / 1000);
37
+ console.log(`Invite code: ${result.inviteCode}`);
38
+ console.log(`Expires in ${expiresIn} seconds`);
39
+ console.log('');
40
+ console.log('On the other machine, run:');
41
+ console.log(` loopsy pair <this-machine-ip>`);
42
+ console.log('');
43
+ // Poll for status changes
44
+ console.log('Waiting for peer to connect...');
45
+ let lastState = 'waiting';
46
+ while (true) {
47
+ await sleep(2000);
48
+ const status = await daemonRequest('/pair/status');
49
+ if (!status.active) {
50
+ console.log('Pairing session ended');
51
+ return;
52
+ }
53
+ if (status.state === 'key_exchanged' && lastState === 'waiting') {
54
+ console.log('');
55
+ console.log(`Peer connected! Verification code: ${status.sas}`);
56
+ console.log('');
57
+ const answer = await prompt('Does the code match on the other machine? (y/n): ');
58
+ if (answer.toLowerCase() === 'y') {
59
+ const confirmResult = await daemonRequest('/pair/confirm', {
60
+ method: 'POST',
61
+ body: JSON.stringify({ confirmed: true }),
62
+ });
63
+ console.log('');
64
+ console.log(confirmResult.message);
65
+ console.log('Restart the daemon to pick up the new peer: loopsy stop && loopsy start');
66
+ return;
67
+ }
68
+ else {
69
+ await daemonRequest('/pair/confirm', {
70
+ method: 'POST',
71
+ body: JSON.stringify({ confirmed: false }),
72
+ });
73
+ console.log('Pairing cancelled');
74
+ return;
75
+ }
76
+ }
77
+ if (status.state === 'completed') {
78
+ console.log('Pairing completed!');
79
+ return;
80
+ }
81
+ if (status.state === 'expired') {
82
+ console.log('Pairing session expired');
83
+ return;
84
+ }
85
+ lastState = status.state;
86
+ }
87
+ }
88
+ /** Machine B: connect to Machine A's pairing session */
89
+ async function pairAsInitiator(target) {
90
+ // Parse target address
91
+ const parts = target.split(':');
92
+ const address = parts[0];
93
+ const port = parts.length > 1 ? parseInt(parts[1], 10) : DEFAULT_PORT;
94
+ console.log(`Connecting to ${address}:${port}...`);
95
+ // Ask for invite code
96
+ const inviteCode = await prompt('Enter invite code: ');
97
+ if (!inviteCode) {
98
+ console.log('Cancelled');
99
+ return;
100
+ }
101
+ // Generate ECDH keypair
102
+ const ecdh = createECDH('prime256v1');
103
+ ecdh.generateKeys();
104
+ // Load our config to get API key and hostname
105
+ const configPath = join(homedir(), CONFIG_DIR, CONFIG_FILE);
106
+ const configRaw = await readFile(configPath, 'utf-8');
107
+ const config = parseYaml(configRaw);
108
+ const myApiKey = config.auth?.apiKey;
109
+ const myHostname = config.server?.hostname || osHostname();
110
+ if (!myApiKey) {
111
+ console.error('No API key found. Run "loopsy init" first.');
112
+ return;
113
+ }
114
+ // Send our public key and invite code to Machine A
115
+ // Note: pairing endpoints are unauthed (no apiKey required)
116
+ const response = await fetch(`http://${address}:${port}/api/v1/pair/initiate`, {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({
120
+ publicKey: ecdh.getPublicKey('base64'),
121
+ inviteCode,
122
+ hostname: myHostname,
123
+ apiKey: myApiKey,
124
+ }),
125
+ });
126
+ if (!response.ok) {
127
+ const body = await response.json().catch(() => ({}));
128
+ console.error(`Pairing failed: ${body.error || `HTTP ${response.status}`}`);
129
+ return;
130
+ }
131
+ const peerInfo = await response.json();
132
+ // Verify SAS
133
+ const peerPubKey = Buffer.from(peerInfo.publicKey, 'base64');
134
+ const sharedSecret = ecdh.computeSecret(peerPubKey);
135
+ const sasHash = createHash('sha256').update(sharedSecret).update('loopsy-sas').digest();
136
+ const localSas = String(sasHash.readUInt32BE() % 10 ** 6).padStart(6, '0');
137
+ console.log('');
138
+ console.log(`Verification code: ${localSas}`);
139
+ console.log('');
140
+ const answer = await prompt('Does this code match on the other machine? (y/n): ');
141
+ if (answer.toLowerCase() !== 'y') {
142
+ // Notify peer to cancel
143
+ try {
144
+ await fetch(`http://${address}:${port}/api/v1/pair/confirm`, {
145
+ method: 'POST',
146
+ headers: { 'Content-Type': 'application/json' },
147
+ body: JSON.stringify({ confirmed: false }),
148
+ });
149
+ }
150
+ catch { }
151
+ console.log('Pairing cancelled');
152
+ return;
153
+ }
154
+ // Confirm pairing on peer
155
+ const confirmResp = await fetch(`http://${address}:${port}/api/v1/pair/confirm`, {
156
+ method: 'POST',
157
+ headers: { 'Content-Type': 'application/json' },
158
+ body: JSON.stringify({ confirmed: true }),
159
+ });
160
+ if (!confirmResp.ok) {
161
+ console.error('Peer rejected the confirmation');
162
+ return;
163
+ }
164
+ // Save peer info to our config
165
+ await addPeerToConfig(peerInfo.hostname, peerInfo.apiKey, peerInfo.certFingerprint);
166
+ // Also add as a manual peer for discovery
167
+ await addManualPeer(address, port, peerInfo.hostname);
168
+ console.log('');
169
+ console.log(`Paired with ${peerInfo.hostname}!`);
170
+ console.log('Restart the daemon to pick up the new peer: loopsy stop && loopsy start');
171
+ }
172
+ async function addPeerToConfig(peerHostname, peerApiKey, certFingerprint) {
173
+ const configPath = join(homedir(), CONFIG_DIR, CONFIG_FILE);
174
+ const raw = await readFile(configPath, 'utf-8');
175
+ const config = parseYaml(raw);
176
+ if (!config.auth)
177
+ config.auth = {};
178
+ if (!config.auth.allowedKeys)
179
+ config.auth.allowedKeys = {};
180
+ config.auth.allowedKeys[peerHostname] = peerApiKey;
181
+ if (certFingerprint) {
182
+ if (!config.tls)
183
+ config.tls = { enabled: false };
184
+ if (!config.tls.pinnedCerts)
185
+ config.tls.pinnedCerts = {};
186
+ config.tls.pinnedCerts[peerHostname] = certFingerprint;
187
+ }
188
+ await writeFile(configPath, toYaml(config));
189
+ }
190
+ async function addManualPeer(address, port, hostname) {
191
+ const configPath = join(homedir(), CONFIG_DIR, CONFIG_FILE);
192
+ const raw = await readFile(configPath, 'utf-8');
193
+ const config = parseYaml(raw);
194
+ if (!config.discovery)
195
+ config.discovery = { enabled: true, manualPeers: [] };
196
+ if (!config.discovery.manualPeers)
197
+ config.discovery.manualPeers = [];
198
+ // Check if already present
199
+ const exists = config.discovery.manualPeers.some((p) => p.address === address && p.port === port);
200
+ if (!exists) {
201
+ config.discovery.manualPeers.push({ address, port, hostname });
202
+ await writeFile(configPath, toYaml(config));
203
+ }
204
+ }
205
+ function sleep(ms) {
206
+ return new Promise((resolve) => setTimeout(resolve, ms));
207
+ }
208
+ //# sourceMappingURL=pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.js","sourceRoot":"","sources":["../../src/commands/pair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function peersCommand(argv: any): Promise<void>;
2
+ //# sourceMappingURL=peers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../src/commands/peers.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,IAAI,EAAE,GAAG,iBAwB3C"}
@@ -0,0 +1,29 @@
1
+ import { daemonRequest } from '../utils.js';
2
+ export async function peersCommand(argv) {
3
+ const sub = argv._[1];
4
+ if (sub === 'add') {
5
+ const result = await daemonRequest('/peers', {
6
+ method: 'POST',
7
+ body: JSON.stringify({ address: argv.address, port: argv.port ?? 19532 }),
8
+ });
9
+ console.log('Peer added:', JSON.stringify(result, null, 2));
10
+ }
11
+ else if (sub === 'remove') {
12
+ const result = await daemonRequest(`/peers/${encodeURIComponent(argv.nodeId)}`, { method: 'DELETE' });
13
+ console.log('Peer removed:', JSON.stringify(result, null, 2));
14
+ }
15
+ else {
16
+ // List peers
17
+ const result = await daemonRequest('/peers');
18
+ if (result.peers.length === 0) {
19
+ console.log('No peers found');
20
+ }
21
+ else {
22
+ for (const peer of result.peers) {
23
+ const status = peer.status === 'online' ? '\x1b[32monline\x1b[0m' : '\x1b[31moffline\x1b[0m';
24
+ console.log(` ${peer.nodeId.slice(0, 8)} ${peer.address}:${peer.port} ${peer.hostname} ${status}`);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ //# sourceMappingURL=peers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.js","sourceRoot":"","sources":["../../src/commands/peers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAS;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtB,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;YAC3C,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;SAC1E,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;SAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,aAAa;QACb,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,wBAAwB,CAAC;gBAC7F,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC;YACzG,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare function enableLinux(): Promise<void>;
2
+ export declare function disableLinux(): Promise<void>;
3
+ export declare function statusLinux(): Promise<{
4
+ enabled: boolean;
5
+ running: boolean;
6
+ }>;
7
+ //# sourceMappingURL=linux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux.d.ts","sourceRoot":"","sources":["../../../src/commands/service/linux.ts"],"names":[],"mappings":"AA8BA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBjD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAelD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoBnF"}
@@ -0,0 +1,86 @@
1
+ import { writeFile, unlink, mkdir, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { execSync } from 'node:child_process';
5
+ import { daemonMainPath } from '../../package-root.js';
6
+ const SYSTEMD_DIR = join(homedir(), '.config', 'systemd', 'user');
7
+ const SERVICE_NAME = 'loopsy';
8
+ const SERVICE_PATH = join(SYSTEMD_DIR, `${SERVICE_NAME}.service`);
9
+ function buildUnit() {
10
+ const nodePath = process.execPath;
11
+ const daemonPath = daemonMainPath();
12
+ return `[Unit]
13
+ Description=Loopsy daemon — cross-machine communication for Claude Code
14
+ After=network.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ ExecStart=${nodePath} ${daemonPath}
19
+ Restart=always
20
+ RestartSec=5
21
+ Environment=PATH=${process.env.PATH}
22
+
23
+ [Install]
24
+ WantedBy=default.target
25
+ `;
26
+ }
27
+ export async function enableLinux() {
28
+ await mkdir(SYSTEMD_DIR, { recursive: true });
29
+ await writeFile(SERVICE_PATH, buildUnit());
30
+ console.log(`Wrote ${SERVICE_PATH}`);
31
+ execSync('systemctl --user daemon-reload');
32
+ execSync(`systemctl --user enable ${SERVICE_NAME}`);
33
+ execSync(`systemctl --user start ${SERVICE_NAME}`);
34
+ console.log('Loopsy daemon registered with systemd');
35
+ // Check if linger is enabled
36
+ try {
37
+ const output = execSync(`loginctl show-user $(whoami) -p Linger`, { encoding: 'utf-8' });
38
+ if (output.includes('Linger=no')) {
39
+ console.log('');
40
+ console.log('WARNING: User linger is not enabled. The daemon will stop when you log out.');
41
+ console.log('To keep it running after logout, run:');
42
+ console.log(' sudo loginctl enable-linger $(whoami)');
43
+ }
44
+ }
45
+ catch { }
46
+ }
47
+ export async function disableLinux() {
48
+ try {
49
+ execSync(`systemctl --user stop ${SERVICE_NAME}`, { stdio: 'ignore' });
50
+ }
51
+ catch { }
52
+ try {
53
+ execSync(`systemctl --user disable ${SERVICE_NAME}`, { stdio: 'ignore' });
54
+ }
55
+ catch { }
56
+ try {
57
+ await unlink(SERVICE_PATH);
58
+ }
59
+ catch { }
60
+ try {
61
+ execSync('systemctl --user daemon-reload', { stdio: 'ignore' });
62
+ }
63
+ catch { }
64
+ console.log('Loopsy daemon unregistered from systemd');
65
+ }
66
+ export async function statusLinux() {
67
+ let enabled = false;
68
+ let running = false;
69
+ try {
70
+ await readFile(SERVICE_PATH);
71
+ enabled = true;
72
+ }
73
+ catch { }
74
+ if (enabled) {
75
+ try {
76
+ const output = execSync(`systemctl --user is-active ${SERVICE_NAME}`, {
77
+ encoding: 'utf-8',
78
+ stdio: ['pipe', 'pipe', 'pipe'],
79
+ });
80
+ running = output.trim() === 'active';
81
+ }
82
+ catch { }
83
+ }
84
+ return { enabled, running };
85
+ }
86
+ //# sourceMappingURL=linux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux.js","sourceRoot":"","sources":["../../../src/commands/service/linux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function enableMacos(): Promise<void>;
2
+ export declare function disableMacos(): Promise<void>;
3
+ export declare function statusMacos(): Promise<{
4
+ enabled: boolean;
5
+ running: boolean;
6
+ }>;
7
+ //# sourceMappingURL=macos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos.d.ts","sourceRoot":"","sources":["../../../src/commands/service/macos.ts"],"names":[],"mappings":"AA0CA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAajD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAUlD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoBnF"}
@@ -0,0 +1,83 @@
1
+ import { writeFile, unlink, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { execSync } from 'node:child_process';
5
+ import { daemonMainPath } from '../../package-root.js';
6
+ const PLIST_DIR = join(homedir(), 'Library', 'LaunchAgents');
7
+ const PLIST_LABEL = 'com.loopsy.daemon';
8
+ const PLIST_PATH = join(PLIST_DIR, `${PLIST_LABEL}.plist`);
9
+ function buildPlist() {
10
+ const nodePath = process.execPath;
11
+ const daemonPath = daemonMainPath();
12
+ return `<?xml version="1.0" encoding="UTF-8"?>
13
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
14
+ <plist version="1.0">
15
+ <dict>
16
+ <key>Label</key>
17
+ <string>${PLIST_LABEL}</string>
18
+ <key>ProgramArguments</key>
19
+ <array>
20
+ <string>${nodePath}</string>
21
+ <string>${daemonPath}</string>
22
+ </array>
23
+ <key>RunAtLoad</key>
24
+ <true/>
25
+ <key>KeepAlive</key>
26
+ <true/>
27
+ <key>StandardOutPath</key>
28
+ <string>${join(homedir(), '.loopsy', 'logs', 'daemon.stdout.log')}</string>
29
+ <key>StandardErrorPath</key>
30
+ <string>${join(homedir(), '.loopsy', 'logs', 'daemon.stderr.log')}</string>
31
+ <key>EnvironmentVariables</key>
32
+ <dict>
33
+ <key>PATH</key>
34
+ <string>${process.env.PATH}</string>
35
+ </dict>
36
+ </dict>
37
+ </plist>`;
38
+ }
39
+ export async function enableMacos() {
40
+ const plist = buildPlist();
41
+ await writeFile(PLIST_PATH, plist);
42
+ console.log(`Wrote ${PLIST_PATH}`);
43
+ // Unload first in case it's already loaded (ignore errors)
44
+ try {
45
+ execSync(`launchctl bootout gui/${process.getuid()} ${PLIST_PATH}`, { stdio: 'ignore' });
46
+ }
47
+ catch { }
48
+ execSync(`launchctl bootstrap gui/${process.getuid()} ${PLIST_PATH}`);
49
+ console.log('Loopsy daemon registered with launchd');
50
+ console.log('The daemon will start automatically on login');
51
+ }
52
+ export async function disableMacos() {
53
+ try {
54
+ execSync(`launchctl bootout gui/${process.getuid()} ${PLIST_PATH}`, { stdio: 'ignore' });
55
+ }
56
+ catch { }
57
+ try {
58
+ await unlink(PLIST_PATH);
59
+ }
60
+ catch { }
61
+ console.log('Loopsy daemon unregistered from launchd');
62
+ }
63
+ export async function statusMacos() {
64
+ let enabled = false;
65
+ let running = false;
66
+ try {
67
+ await readFile(PLIST_PATH);
68
+ enabled = true;
69
+ }
70
+ catch { }
71
+ if (enabled) {
72
+ try {
73
+ const output = execSync(`launchctl print gui/${process.getuid()}/${PLIST_LABEL}`, {
74
+ encoding: 'utf-8',
75
+ stdio: ['pipe', 'pipe', 'pipe'],
76
+ });
77
+ running = output.includes('state = running');
78
+ }
79
+ catch { }
80
+ }
81
+ return { enabled, running };
82
+ }
83
+ //# sourceMappingURL=macos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos.js","sourceRoot":"","sources":["../../../src/commands/service/macos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function enableWindows(): Promise<void>;
2
+ export declare function disableWindows(): Promise<void>;
3
+ export declare function statusWindows(): Promise<{
4
+ enabled: boolean;
5
+ running: boolean;
6
+ }>;
7
+ //# sourceMappingURL=windows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../../src/commands/service/windows.ts"],"names":[],"mappings":"AAKA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBnD;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAYpD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAcrF"}