@cmdctrl/claude-code 0.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 (83) hide show
  1. package/dist/adapter/claude-cli.d.ts +41 -0
  2. package/dist/adapter/claude-cli.d.ts.map +1 -0
  3. package/dist/adapter/claude-cli.js +525 -0
  4. package/dist/adapter/claude-cli.js.map +1 -0
  5. package/dist/adapter/events.d.ts +52 -0
  6. package/dist/adapter/events.d.ts.map +1 -0
  7. package/dist/adapter/events.js +134 -0
  8. package/dist/adapter/events.js.map +1 -0
  9. package/dist/client/messages.d.ts +140 -0
  10. package/dist/client/messages.d.ts.map +1 -0
  11. package/dist/client/messages.js +6 -0
  12. package/dist/client/messages.js.map +1 -0
  13. package/dist/client/websocket.d.ts +115 -0
  14. package/dist/client/websocket.d.ts.map +1 -0
  15. package/dist/client/websocket.js +434 -0
  16. package/dist/client/websocket.js.map +1 -0
  17. package/dist/commands/register.d.ts +10 -0
  18. package/dist/commands/register.d.ts.map +1 -0
  19. package/dist/commands/register.js +175 -0
  20. package/dist/commands/register.js.map +1 -0
  21. package/dist/commands/start.d.ts +9 -0
  22. package/dist/commands/start.d.ts.map +1 -0
  23. package/dist/commands/start.js +54 -0
  24. package/dist/commands/start.js.map +1 -0
  25. package/dist/commands/status.d.ts +5 -0
  26. package/dist/commands/status.d.ts.map +1 -0
  27. package/dist/commands/status.js +38 -0
  28. package/dist/commands/status.js.map +1 -0
  29. package/dist/commands/stop.d.ts +5 -0
  30. package/dist/commands/stop.d.ts.map +1 -0
  31. package/dist/commands/stop.js +59 -0
  32. package/dist/commands/stop.js.map +1 -0
  33. package/dist/commands/unregister.d.ts +5 -0
  34. package/dist/commands/unregister.d.ts.map +1 -0
  35. package/dist/commands/unregister.js +28 -0
  36. package/dist/commands/unregister.js.map +1 -0
  37. package/dist/config/config.d.ts +68 -0
  38. package/dist/config/config.d.ts.map +1 -0
  39. package/dist/config/config.js +193 -0
  40. package/dist/config/config.js.map +1 -0
  41. package/dist/handlers/context-handler.d.ts +37 -0
  42. package/dist/handlers/context-handler.d.ts.map +1 -0
  43. package/dist/handlers/context-handler.js +303 -0
  44. package/dist/handlers/context-handler.js.map +1 -0
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +39 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/message-reader.d.ts +25 -0
  50. package/dist/message-reader.d.ts.map +1 -0
  51. package/dist/message-reader.js +454 -0
  52. package/dist/message-reader.js.map +1 -0
  53. package/dist/session-discovery.d.ts +48 -0
  54. package/dist/session-discovery.d.ts.map +1 -0
  55. package/dist/session-discovery.js +496 -0
  56. package/dist/session-discovery.js.map +1 -0
  57. package/dist/session-watcher.d.ts +92 -0
  58. package/dist/session-watcher.d.ts.map +1 -0
  59. package/dist/session-watcher.js +494 -0
  60. package/dist/session-watcher.js.map +1 -0
  61. package/dist/session-watcher.test.d.ts +9 -0
  62. package/dist/session-watcher.test.d.ts.map +1 -0
  63. package/dist/session-watcher.test.js +149 -0
  64. package/dist/session-watcher.test.js.map +1 -0
  65. package/jest.config.js +8 -0
  66. package/package.json +42 -0
  67. package/src/adapter/claude-cli.ts +591 -0
  68. package/src/adapter/events.ts +186 -0
  69. package/src/client/messages.ts +193 -0
  70. package/src/client/websocket.ts +509 -0
  71. package/src/commands/register.ts +201 -0
  72. package/src/commands/start.ts +70 -0
  73. package/src/commands/status.ts +47 -0
  74. package/src/commands/stop.ts +58 -0
  75. package/src/commands/unregister.ts +30 -0
  76. package/src/config/config.ts +163 -0
  77. package/src/handlers/context-handler.ts +337 -0
  78. package/src/index.ts +45 -0
  79. package/src/message-reader.ts +485 -0
  80. package/src/session-discovery.ts +557 -0
  81. package/src/session-watcher.test.ts +141 -0
  82. package/src/session-watcher.ts +560 -0
  83. package/tsconfig.json +19 -0
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.start = start;
4
+ const config_1 = require("../config/config");
5
+ const websocket_1 = require("../client/websocket");
6
+ /**
7
+ * Start command - launch the daemon and connect to server
8
+ */
9
+ async function start(options) {
10
+ // Check registration
11
+ if (!(0, config_1.isRegistered)()) {
12
+ console.error('Device not registered. Run "cmdctrl-claude-code-daemon register" first.');
13
+ process.exit(1);
14
+ }
15
+ // Check if already running
16
+ if ((0, config_1.isDaemonRunning)()) {
17
+ console.error('Daemon is already running. Run "cmdctrl-claude-code-daemon stop" first.');
18
+ process.exit(1);
19
+ }
20
+ const config = (0, config_1.readConfig)();
21
+ const credentials = (0, config_1.readCredentials)();
22
+ console.log(`Starting CmdCtrl daemon...`);
23
+ console.log(`Server: ${config.serverUrl}`);
24
+ console.log(`Device: ${config.deviceName} (${config.deviceId})`);
25
+ // Write PID file
26
+ (0, config_1.writePidFile)(process.pid);
27
+ // Create and start client
28
+ const client = new websocket_1.DaemonClient(config, credentials);
29
+ // Handle shutdown signals
30
+ const shutdown = async () => {
31
+ console.log('\nShutting down...');
32
+ await client.disconnect();
33
+ process.exit(0);
34
+ };
35
+ process.on('SIGINT', shutdown);
36
+ process.on('SIGTERM', shutdown);
37
+ // Connect and run
38
+ try {
39
+ await client.connect();
40
+ console.log('Connected to server.');
41
+ if (options.foreground) {
42
+ console.log('Running in foreground. Press Ctrl+C to stop.\n');
43
+ }
44
+ // Keep process alive - the WebSocket client handles events
45
+ await new Promise(() => {
46
+ // Never resolves - daemon runs until killed
47
+ });
48
+ }
49
+ catch (err) {
50
+ console.error('Failed to start daemon:', err);
51
+ process.exit(1);
52
+ }
53
+ }
54
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":";;AAgBA,sBAqDC;AArED,6CAM0B;AAC1B,mDAAmD;AAMnD;;GAEG;AACI,KAAK,UAAU,KAAK,CAAC,OAAqB;IAC/C,qBAAqB;IACrB,IAAI,CAAC,IAAA,qBAAY,GAAE,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,2BAA2B;IAC3B,IAAI,IAAA,wBAAe,GAAE,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAG,CAAC;IAC7B,MAAM,WAAW,GAAG,IAAA,wBAAe,GAAG,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAEjE,iBAAiB;IACjB,IAAA,qBAAY,EAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAE1B,0BAA0B;IAC1B,MAAM,MAAM,GAAG,IAAI,wBAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAErD,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,kBAAkB;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QAEpC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;QAChE,CAAC;QAED,2DAA2D;QAC3D,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE;YACrB,4CAA4C;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Status command - check daemon and connection status
3
+ */
4
+ export declare function status(): Promise<void>;
5
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAmC5C"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.status = status;
4
+ const config_1 = require("../config/config");
5
+ /**
6
+ * Status command - check daemon and connection status
7
+ */
8
+ async function status() {
9
+ console.log('CmdCtrl Daemon Status\n');
10
+ // Registration status
11
+ if (!(0, config_1.isRegistered)()) {
12
+ console.log('Registration: Not registered');
13
+ console.log('\nRun "cmdctrl-claude-code-daemon register" to register this device.');
14
+ return;
15
+ }
16
+ const config = (0, config_1.readConfig)();
17
+ const credentials = (0, config_1.readCredentials)();
18
+ console.log('Registration: Registered');
19
+ console.log(` Server: ${config.serverUrl}`);
20
+ console.log(` Device: ${config.deviceName}`);
21
+ console.log(` Device ID: ${config.deviceId}`);
22
+ // Token status
23
+ const tokenExpired = credentials.expiresAt < Date.now();
24
+ console.log(`\nToken: ${tokenExpired ? 'Expired' : 'Valid'}`);
25
+ if (!tokenExpired) {
26
+ const expiresIn = Math.round((credentials.expiresAt - Date.now()) / 1000 / 60);
27
+ console.log(` Expires in: ${expiresIn} minutes`);
28
+ }
29
+ // Daemon status
30
+ const running = (0, config_1.isDaemonRunning)();
31
+ console.log(`\nDaemon: ${running ? 'Running' : 'Stopped'}`);
32
+ if (running) {
33
+ const pid = (0, config_1.readPidFile)();
34
+ console.log(` PID: ${pid}`);
35
+ }
36
+ // TODO: Query daemon for actual connection status via local socket or HTTP
37
+ }
38
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":";;AAWA,wBAmCC;AA9CD,6CAM0B;AAE1B;;GAEG;AACI,KAAK,UAAU,MAAM;IAC1B,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAEvC,sBAAsB;IACtB,IAAI,CAAC,IAAA,qBAAY,GAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;QACpF,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAG,CAAC;IAC7B,MAAM,WAAW,GAAG,IAAA,wBAAe,GAAG,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE/C,eAAe;IACf,MAAM,YAAY,GAAG,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,iBAAiB,SAAS,UAAU,CAAC,CAAC;IACpD,CAAC;IAED,gBAAgB;IAChB,MAAM,OAAO,GAAG,IAAA,wBAAe,GAAE,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAC5D,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,IAAA,oBAAW,GAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,2EAA2E;AAC7E,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stop command - stop the running daemon
3
+ */
4
+ export declare function stop(): Promise<void>;
5
+ //# sourceMappingURL=stop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAoD1C"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stop = stop;
4
+ const config_1 = require("../config/config");
5
+ /**
6
+ * Stop command - stop the running daemon
7
+ */
8
+ async function stop() {
9
+ if (!(0, config_1.isDaemonRunning)()) {
10
+ console.log('Daemon is not running.');
11
+ return;
12
+ }
13
+ const pid = (0, config_1.readPidFile)();
14
+ if (pid === null) {
15
+ console.log('No PID file found.');
16
+ return;
17
+ }
18
+ console.log(`Stopping daemon (PID ${pid})...`);
19
+ try {
20
+ // Send SIGTERM for graceful shutdown
21
+ process.kill(pid, 'SIGTERM');
22
+ // Wait for process to exit (up to 5 seconds)
23
+ let attempts = 0;
24
+ while (attempts < 50) {
25
+ await new Promise((resolve) => setTimeout(resolve, 100));
26
+ try {
27
+ process.kill(pid, 0); // Check if still running
28
+ attempts++;
29
+ }
30
+ catch {
31
+ // Process has exited
32
+ break;
33
+ }
34
+ }
35
+ // If still running after 5 seconds, force kill
36
+ try {
37
+ process.kill(pid, 0);
38
+ console.log('Daemon did not stop gracefully, sending SIGKILL...');
39
+ process.kill(pid, 'SIGKILL');
40
+ }
41
+ catch {
42
+ // Already dead, good
43
+ }
44
+ (0, config_1.deletePidFile)();
45
+ console.log('Daemon stopped.');
46
+ }
47
+ catch (err) {
48
+ if (err.code === 'ESRCH') {
49
+ // Process doesn't exist
50
+ (0, config_1.deletePidFile)();
51
+ console.log('Daemon was not running (stale PID file removed).');
52
+ }
53
+ else {
54
+ console.error('Failed to stop daemon:', err);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":";;AAKA,oBAoDC;AAzDD,6CAA+E;AAE/E;;GAEG;AACI,KAAK,UAAU,IAAI;IACxB,IAAI,CAAC,IAAA,wBAAe,GAAE,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,oBAAW,GAAE,CAAC;IAC1B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,qCAAqC;QACrC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAE7B,6CAA6C;QAC7C,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,OAAO,QAAQ,GAAG,EAAE,EAAE,CAAC;YACrB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YACzD,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,yBAAyB;gBAC/C,QAAQ,EAAE,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;gBACrB,MAAM;YACR,CAAC;QACH,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;QAED,IAAA,sBAAa,GAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACpD,wBAAwB;YACxB,IAAA,sBAAa,GAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unregister command - removes local registration data
3
+ */
4
+ export declare function unregister(): Promise<void>;
5
+ //# sourceMappingURL=unregister.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unregister.d.ts","sourceRoot":"","sources":["../../src/commands/unregister.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAwBhD"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unregister = unregister;
4
+ const config_1 = require("../config/config");
5
+ /**
6
+ * Unregister command - removes local registration data
7
+ */
8
+ async function unregister() {
9
+ const config = (0, config_1.readConfig)();
10
+ if (!config) {
11
+ console.log('Not registered.');
12
+ return;
13
+ }
14
+ // Check if daemon is running
15
+ if ((0, config_1.isDaemonRunning)()) {
16
+ console.error('Error: Daemon is currently running.');
17
+ console.error('Please stop the daemon first with: cmdctrl-claude-code stop');
18
+ process.exit(1);
19
+ }
20
+ console.log(`Unregistering device "${config.deviceName}" (${config.deviceId})...`);
21
+ console.log(`Server: ${config.serverUrl}`);
22
+ // Clear local registration data
23
+ (0, config_1.clearRegistration)();
24
+ console.log('\nLocal registration data cleared.');
25
+ console.log('Note: The device entry may still exist on the server.');
26
+ console.log('You can now register again with: cmdctrl-claude-code register -s <server-url>');
27
+ }
28
+ //# sourceMappingURL=unregister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unregister.js","sourceRoot":"","sources":["../../src/commands/unregister.ts"],"names":[],"mappings":";;AAKA,gCAwBC;AA7BD,6CAAkF;AAElF;;GAEG;AACI,KAAK,UAAU,UAAU;IAC9B,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,6BAA6B;IAC7B,IAAI,IAAA,wBAAe,GAAE,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,yBAAyB,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAE3C,gCAAgC;IAChC,IAAA,0BAAiB,GAAE,CAAC;IAEpB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,+EAA+E,CAAC,CAAC;AAC/F,CAAC"}
@@ -0,0 +1,68 @@
1
+ export interface CmdCtrlConfig {
2
+ serverUrl: string;
3
+ deviceId: string;
4
+ deviceName: string;
5
+ }
6
+ export interface Credentials {
7
+ accessToken: string;
8
+ refreshToken: string;
9
+ expiresAt: number;
10
+ }
11
+ declare const CONFIG_DIR: string;
12
+ declare const CONFIG_FILE: string;
13
+ declare const CREDENTIALS_FILE: string;
14
+ declare const PID_FILE: string;
15
+ /**
16
+ * Ensure the config directory exists with proper permissions
17
+ */
18
+ export declare function ensureConfigDir(): void;
19
+ /**
20
+ * Read the config file
21
+ */
22
+ export declare function readConfig(): CmdCtrlConfig | null;
23
+ /**
24
+ * Write the config file
25
+ */
26
+ export declare function writeConfig(config: CmdCtrlConfig): void;
27
+ /**
28
+ * Read credentials (access/refresh tokens)
29
+ */
30
+ export declare function readCredentials(): Credentials | null;
31
+ /**
32
+ * Write credentials with restrictive permissions (600)
33
+ */
34
+ export declare function writeCredentials(creds: Credentials): void;
35
+ /**
36
+ * Delete credentials (for logout/revoke)
37
+ */
38
+ export declare function deleteCredentials(): void;
39
+ /**
40
+ * Delete config file
41
+ */
42
+ export declare function deleteConfig(): void;
43
+ /**
44
+ * Clear all registration data (config and credentials)
45
+ */
46
+ export declare function clearRegistration(): void;
47
+ /**
48
+ * Check if device is registered
49
+ */
50
+ export declare function isRegistered(): boolean;
51
+ /**
52
+ * Write daemon PID file
53
+ */
54
+ export declare function writePidFile(pid: number): void;
55
+ /**
56
+ * Read daemon PID
57
+ */
58
+ export declare function readPidFile(): number | null;
59
+ /**
60
+ * Delete PID file
61
+ */
62
+ export declare function deletePidFile(): void;
63
+ /**
64
+ * Check if daemon is running
65
+ */
66
+ export declare function isDaemonRunning(): boolean;
67
+ export { CONFIG_DIR, CONFIG_FILE, CREDENTIALS_FILE, PID_FILE };
68
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,QAAA,MAAM,UAAU,QAAkD,CAAC;AACnE,QAAA,MAAM,WAAW,QAAuC,CAAC;AACzD,QAAA,MAAM,gBAAgB,QAAuC,CAAC;AAC9D,QAAA,MAAM,QAAQ,QAAsC,CAAC;AAErD;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,aAAa,GAAG,IAAI,CAUjD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAGvD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAUpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAGzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAInC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAItC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAU3C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAIpC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAczC;AAED,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PID_FILE = exports.CREDENTIALS_FILE = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
37
+ exports.ensureConfigDir = ensureConfigDir;
38
+ exports.readConfig = readConfig;
39
+ exports.writeConfig = writeConfig;
40
+ exports.readCredentials = readCredentials;
41
+ exports.writeCredentials = writeCredentials;
42
+ exports.deleteCredentials = deleteCredentials;
43
+ exports.deleteConfig = deleteConfig;
44
+ exports.clearRegistration = clearRegistration;
45
+ exports.isRegistered = isRegistered;
46
+ exports.writePidFile = writePidFile;
47
+ exports.readPidFile = readPidFile;
48
+ exports.deletePidFile = deletePidFile;
49
+ exports.isDaemonRunning = isDaemonRunning;
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const os = __importStar(require("os"));
53
+ const CONFIG_DIR = path.join(os.homedir(), '.cmdctrl-claude-code');
54
+ exports.CONFIG_DIR = CONFIG_DIR;
55
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
56
+ exports.CONFIG_FILE = CONFIG_FILE;
57
+ const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials');
58
+ exports.CREDENTIALS_FILE = CREDENTIALS_FILE;
59
+ const PID_FILE = path.join(CONFIG_DIR, 'daemon.pid');
60
+ exports.PID_FILE = PID_FILE;
61
+ /**
62
+ * Ensure the config directory exists with proper permissions
63
+ */
64
+ function ensureConfigDir() {
65
+ if (!fs.existsSync(CONFIG_DIR)) {
66
+ fs.mkdirSync(CONFIG_DIR, { mode: 0o700 });
67
+ }
68
+ }
69
+ /**
70
+ * Read the config file
71
+ */
72
+ function readConfig() {
73
+ try {
74
+ if (!fs.existsSync(CONFIG_FILE)) {
75
+ return null;
76
+ }
77
+ const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
78
+ return JSON.parse(content);
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * Write the config file
86
+ */
87
+ function writeConfig(config) {
88
+ ensureConfigDir();
89
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
90
+ }
91
+ /**
92
+ * Read credentials (access/refresh tokens)
93
+ */
94
+ function readCredentials() {
95
+ try {
96
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
97
+ return null;
98
+ }
99
+ const content = fs.readFileSync(CREDENTIALS_FILE, 'utf-8');
100
+ return JSON.parse(content);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Write credentials with restrictive permissions (600)
108
+ */
109
+ function writeCredentials(creds) {
110
+ ensureConfigDir();
111
+ fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), { mode: 0o600 });
112
+ }
113
+ /**
114
+ * Delete credentials (for logout/revoke)
115
+ */
116
+ function deleteCredentials() {
117
+ if (fs.existsSync(CREDENTIALS_FILE)) {
118
+ fs.unlinkSync(CREDENTIALS_FILE);
119
+ }
120
+ }
121
+ /**
122
+ * Delete config file
123
+ */
124
+ function deleteConfig() {
125
+ if (fs.existsSync(CONFIG_FILE)) {
126
+ fs.unlinkSync(CONFIG_FILE);
127
+ }
128
+ }
129
+ /**
130
+ * Clear all registration data (config and credentials)
131
+ */
132
+ function clearRegistration() {
133
+ deleteConfig();
134
+ deleteCredentials();
135
+ }
136
+ /**
137
+ * Check if device is registered
138
+ */
139
+ function isRegistered() {
140
+ const config = readConfig();
141
+ const creds = readCredentials();
142
+ return config !== null && creds !== null && config.deviceId !== '';
143
+ }
144
+ /**
145
+ * Write daemon PID file
146
+ */
147
+ function writePidFile(pid) {
148
+ ensureConfigDir();
149
+ fs.writeFileSync(PID_FILE, pid.toString(), { mode: 0o600 });
150
+ }
151
+ /**
152
+ * Read daemon PID
153
+ */
154
+ function readPidFile() {
155
+ try {
156
+ if (!fs.existsSync(PID_FILE)) {
157
+ return null;
158
+ }
159
+ const content = fs.readFileSync(PID_FILE, 'utf-8');
160
+ return parseInt(content, 10);
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ }
166
+ /**
167
+ * Delete PID file
168
+ */
169
+ function deletePidFile() {
170
+ if (fs.existsSync(PID_FILE)) {
171
+ fs.unlinkSync(PID_FILE);
172
+ }
173
+ }
174
+ /**
175
+ * Check if daemon is running
176
+ */
177
+ function isDaemonRunning() {
178
+ const pid = readPidFile();
179
+ if (pid === null) {
180
+ return false;
181
+ }
182
+ try {
183
+ // Signal 0 doesn't kill, just checks if process exists
184
+ process.kill(pid, 0);
185
+ return true;
186
+ }
187
+ catch {
188
+ // Process doesn't exist, clean up stale PID file
189
+ deletePidFile();
190
+ return false;
191
+ }
192
+ }
193
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,0CAIC;AAKD,gCAUC;AAKD,kCAGC;AAKD,0CAUC;AAKD,4CAGC;AAKD,8CAIC;AAKD,oCAIC;AAKD,8CAGC;AAKD,oCAIC;AAKD,oCAGC;AAKD,kCAUC;AAKD,sCAIC;AAKD,0CAcC;AAhKD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAczB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,CAAC;AAkJ1D,gCAAU;AAjJnB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAiJpC,kCAAW;AAhJhC,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAgJ5B,4CAAgB;AA/IlD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AA+ID,4BAAQ;AA7I5D;;GAEG;AACH,SAAgB,eAAe;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,UAAU;IACxB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CAAC,MAAqB;IAC/C,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClF,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,KAAkB;IACjD,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACtF,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,IAAI,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpC,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY;IAC1B,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,YAAY,EAAE,CAAC;IACf,iBAAiB,EAAE,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,OAAO,MAAM,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAAC,GAAW;IACtC,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW;IACzB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,uDAAuD;QACvD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;QACjD,aAAa,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Context handler for extracting session context for dashboard summaries
3
+ */
4
+ import { SessionStatus, ContextResponseMessage } from '../client/messages';
5
+ interface SessionContext {
6
+ title: string;
7
+ projectPath: string;
8
+ initialPrompt?: string;
9
+ recentMessages: Array<{
10
+ role: 'USER' | 'AGENT';
11
+ content: string;
12
+ }>;
13
+ lastToolUse?: string;
14
+ messageCount: number;
15
+ startedAt?: string;
16
+ lastActivityAt: string;
17
+ status: SessionStatus;
18
+ statusDetail?: string;
19
+ }
20
+ /**
21
+ * Extract context from a session JSONL file
22
+ */
23
+ export declare function extractSessionContext(sessionId: string, options?: {
24
+ includeInitialPrompt?: boolean;
25
+ recentMessagesCount?: number;
26
+ includeLastToolUse?: boolean;
27
+ }): SessionContext | null;
28
+ /**
29
+ * Build a context response message
30
+ */
31
+ export declare function buildContextResponse(requestId: string, sessionId: string, options?: {
32
+ includeInitialPrompt?: boolean;
33
+ recentMessagesCount?: number;
34
+ includeLastToolUse?: boolean;
35
+ }): ContextResponseMessage;
36
+ export {};
37
+ //# sourceMappingURL=context-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/context-handler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAkB3E,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACzB,GACL,cAAc,GAAG,IAAI,CA+KvB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACzB,GACL,sBAAsB,CAoCxB"}