matterbridge-lg-thinq-plugin 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 (210) hide show
  1. package/.claude/CLAUDE.md +43 -0
  2. package/.claude/agents/compiler.md +91 -0
  3. package/.claude/agents/direct-executor.md +98 -0
  4. package/.claude/agents/documenter.md +78 -0
  5. package/.claude/agents/finalizer.md +233 -0
  6. package/.claude/agents/implementer.md +112 -0
  7. package/.claude/agents/investigator.md +100 -0
  8. package/.claude/agents/release-manager.md +203 -0
  9. package/.claude/agents/reviewer.md +131 -0
  10. package/.claude/agents/technical-architect.md +403 -0
  11. package/.claude/agents/test-writer.md +159 -0
  12. package/.claude/agents/wiki-manager.md +147 -0
  13. package/.claude/instructions/agent-prompts.md +90 -0
  14. package/.claude/instructions/shared-rules.md +11 -0
  15. package/.claude/instructions/team-orchestrator-policy.md +165 -0
  16. package/.claude/memory.md +186 -0
  17. package/.claude/output-styles/engineer-manager.md +38 -0
  18. package/.claude/settings.json +40 -0
  19. package/.claude/settings.local.json +30 -0
  20. package/.claude/skills/load-policy/SKILL.md +13 -0
  21. package/.claude/skills/ref-idea/SKILL.md +44 -0
  22. package/.claude/skills/status-of/SKILL.md +38 -0
  23. package/.claude/skills/workspace-console/SKILL.md +49 -0
  24. package/.claude/statusline-command.sh +130 -0
  25. package/.claude/statusline.md +53 -0
  26. package/.claude/templates/approval-packet.template.html +171 -0
  27. package/.claude/templates/reference-workspaces.md +16 -0
  28. package/.claude/tools/cli-runner-server.cjs +220 -0
  29. package/.claude/tools/discord-send-server.cjs +188 -0
  30. package/.claude/tools/github-release-server.cjs +181 -0
  31. package/.claude/tools/glob-grep-server.cjs +267 -0
  32. package/.claude/tools/read-log-server.cjs +175 -0
  33. package/.github/codeql/codeql-config.yml +14 -0
  34. package/.github/workflows/build.yml +54 -0
  35. package/.github/workflows/codeql.yml +59 -0
  36. package/.github/workflows/coverage.yml +70 -0
  37. package/.github/workflows/publish.yml +103 -0
  38. package/.mcp.json +34 -0
  39. package/CHANGELOG.md +195 -0
  40. package/LICENSE +21 -0
  41. package/dist/cli/commands/devices.js +52 -0
  42. package/dist/cli/commands/energy.js +122 -0
  43. package/dist/cli/commands/login.js +54 -0
  44. package/dist/cli/help.js +50 -0
  45. package/dist/cli/main.js +56 -0
  46. package/dist/cli/session.js +13 -0
  47. package/dist/cli/types.js +1 -0
  48. package/dist/cli/utils.js +29 -0
  49. package/dist/cli.js +2 -0
  50. package/dist/constants/index.js +1 -0
  51. package/dist/core/domain/entities/ThinqDevice.js +43 -0
  52. package/dist/core/domain/value-objects/AirConditionerCapabilities.js +22 -0
  53. package/dist/core/domain/value-objects/ThinqFilterState.js +35 -0
  54. package/dist/core/domain/value-objects/ThinqSnapshot.js +125 -0
  55. package/dist/errors/AuthenticationError.js +26 -0
  56. package/dist/errors/BaseError.js +23 -0
  57. package/dist/errors/index.js +2 -0
  58. package/dist/model/LgThinkqPluginPlatformConfig.js +23 -0
  59. package/dist/model/LgThinqPluginPlatformConfig.js +23 -0
  60. package/dist/module.js +212 -0
  61. package/dist/platform/deviceRegistry.js +26 -0
  62. package/dist/platform/platformConfigManager.js +89 -0
  63. package/dist/platform/platformState.js +9 -0
  64. package/dist/platform/thinq/thinqAirConditionerCommandHandlers.js +190 -0
  65. package/dist/platform/thinq/thinqAirConditionerEndpointFactory.js +53 -0
  66. package/dist/platform/thinq/thinqAirConditionerFilterResetCommandHandler.js +54 -0
  67. package/dist/platform/thinq/thinqAirConditionerFilterStateSync.js +19 -0
  68. package/dist/platform/thinq/thinqAirConditionerStateSync.js +205 -0
  69. package/dist/platform/thinq/thinqDeviceConfigReconciler.js +26 -0
  70. package/dist/platform/thinq/thinqDeviceConfigurator.js +124 -0
  71. package/dist/platform/thinq/thinqDeviceDiscovery.js +33 -0
  72. package/dist/platform/thinq/thinqWasherCommandHandlers.js +54 -0
  73. package/dist/platform/thinq/thinqWasherEndpointFactory.js +6 -0
  74. package/dist/platform/thinq/thinqWasherStateSync.js +31 -0
  75. package/dist/platform/thinq/thinqWasherStopCommandResolver.js +38 -0
  76. package/dist/services/authentication/AccountAuthStrategy.js +135 -0
  77. package/dist/services/authentication/AuthContext.js +1 -0
  78. package/dist/services/authentication/AuthenticationCoordinator.js +29 -0
  79. package/dist/services/authentication/IAuthStrategy.js +1 -0
  80. package/dist/services/authentication/TokenAuthStrategy.js +27 -0
  81. package/dist/services/authentication/UserDataRepository.js +32 -0
  82. package/dist/services/thinq/gateway.js +61 -0
  83. package/dist/services/thinq/mqtt/mqttCertificate.js +63 -0
  84. package/dist/services/thinq/mqtt/mqttConnectionEvents.js +37 -0
  85. package/dist/services/thinq/mqtt/mqttKeyRepository.js +33 -0
  86. package/dist/services/thinq/mqtt/mqttRetry.js +25 -0
  87. package/dist/services/thinq/mqtt/thinqMqttListener.js +97 -0
  88. package/dist/services/thinq/serviceContainer.js +73 -0
  89. package/dist/services/thinq/session.js +48 -0
  90. package/dist/services/thinq/thinqApiClient.js +258 -0
  91. package/dist/services/thinq/thinqDeviceService.js +75 -0
  92. package/dist/services/thinq/thinqFilterMonitoringService.js +40 -0
  93. package/dist/settings.js +9 -0
  94. package/dist/tests/helpers/testUtils.js +87 -0
  95. package/dist/utils/mask.js +39 -0
  96. package/docs/README_CLI.md +166 -0
  97. package/eslint.config.js +194 -0
  98. package/matterbridge-lg-thinq-plugin.config.json +27 -0
  99. package/matterbridge-lg-thinq-plugin.schema.json +520 -0
  100. package/package.json +107 -0
  101. package/prettier.config.js +49 -0
  102. package/scripts/parse-junit-failures.mjs +45 -0
  103. package/scripts/parse-tsc-failures.mjs +48 -0
  104. package/scripts/run-build-local-summary.mjs +29 -0
  105. package/scripts/run-format-summary.mjs +36 -0
  106. package/scripts/run-lint-fix-summary.mjs +38 -0
  107. package/scripts/run-precommit-summary.mjs +94 -0
  108. package/src/cli/commands/devices.ts +67 -0
  109. package/src/cli/commands/energy.ts +160 -0
  110. package/src/cli/commands/login.ts +74 -0
  111. package/src/cli/help.ts +53 -0
  112. package/src/cli/main.ts +66 -0
  113. package/src/cli/session.ts +15 -0
  114. package/src/cli/types.ts +10 -0
  115. package/src/cli/utils.ts +30 -0
  116. package/src/cli.ts +3 -0
  117. package/src/constants/index.ts +8 -0
  118. package/src/core/domain/entities/ThinqDevice.test.ts +224 -0
  119. package/src/core/domain/entities/ThinqDevice.ts +74 -0
  120. package/src/core/domain/value-objects/AirConditionerCapabilities.ts +50 -0
  121. package/src/core/domain/value-objects/ThinqFilterState.ts +50 -0
  122. package/src/core/domain/value-objects/ThinqSnapshot.ts +162 -0
  123. package/src/errors/AuthenticationError.ts +37 -0
  124. package/src/errors/BaseError.ts +38 -0
  125. package/src/errors/index.ts +24 -0
  126. package/src/model/LgThinqPluginPlatformConfig.ts +94 -0
  127. package/src/module.ts +298 -0
  128. package/src/platform/deviceRegistry.ts +58 -0
  129. package/src/platform/platformConfigManager.ts +133 -0
  130. package/src/platform/platformState.ts +16 -0
  131. package/src/platform/thinq/thinqAirConditionerCommandHandlers.ts +291 -0
  132. package/src/platform/thinq/thinqAirConditionerEndpointFactory.ts +145 -0
  133. package/src/platform/thinq/thinqAirConditionerFilterResetCommandHandler.ts +96 -0
  134. package/src/platform/thinq/thinqAirConditionerFilterStateSync.ts +42 -0
  135. package/src/platform/thinq/thinqAirConditionerStateSync.ts +343 -0
  136. package/src/platform/thinq/thinqDeviceConfigReconciler.ts +42 -0
  137. package/src/platform/thinq/thinqDeviceConfigurator.test.ts +90 -0
  138. package/src/platform/thinq/thinqDeviceConfigurator.ts +191 -0
  139. package/src/platform/thinq/thinqDeviceDiscovery.test.ts +173 -0
  140. package/src/platform/thinq/thinqDeviceDiscovery.ts +51 -0
  141. package/src/platform/thinq/thinqWasherCommandHandlers.ts +120 -0
  142. package/src/platform/thinq/thinqWasherEndpointFactory.ts +37 -0
  143. package/src/platform/thinq/thinqWasherStateSync.ts +60 -0
  144. package/src/platform/thinq/thinqWasherStopCommandResolver.ts +62 -0
  145. package/src/services/authentication/AccountAuthStrategy.test.ts +358 -0
  146. package/src/services/authentication/AccountAuthStrategy.ts +209 -0
  147. package/src/services/authentication/AuthContext.ts +19 -0
  148. package/src/services/authentication/AuthenticationCoordinator.test.ts +217 -0
  149. package/src/services/authentication/AuthenticationCoordinator.ts +59 -0
  150. package/src/services/authentication/IAuthStrategy.ts +10 -0
  151. package/src/services/authentication/TokenAuthStrategy.test.ts +167 -0
  152. package/src/services/authentication/TokenAuthStrategy.ts +40 -0
  153. package/src/services/authentication/UserDataRepository.test.ts +149 -0
  154. package/src/services/authentication/UserDataRepository.ts +46 -0
  155. package/src/services/thinq/gateway.ts +86 -0
  156. package/src/services/thinq/mqtt/mqttCertificate.test.ts +344 -0
  157. package/src/services/thinq/mqtt/mqttCertificate.ts +102 -0
  158. package/src/services/thinq/mqtt/mqttConnectionEvents.test.ts +276 -0
  159. package/src/services/thinq/mqtt/mqttConnectionEvents.ts +76 -0
  160. package/src/services/thinq/mqtt/mqttKeyRepository.test.ts +164 -0
  161. package/src/services/thinq/mqtt/mqttKeyRepository.ts +43 -0
  162. package/src/services/thinq/mqtt/mqttRetry.test.ts +239 -0
  163. package/src/services/thinq/mqtt/mqttRetry.ts +43 -0
  164. package/src/services/thinq/mqtt/thinqMqttListener.test.ts +349 -0
  165. package/src/services/thinq/mqtt/thinqMqttListener.ts +120 -0
  166. package/src/services/thinq/serviceContainer.test.ts +203 -0
  167. package/src/services/thinq/serviceContainer.ts +105 -0
  168. package/src/services/thinq/session.test.ts +287 -0
  169. package/src/services/thinq/session.ts +69 -0
  170. package/src/services/thinq/thinqApiClient.test.ts +702 -0
  171. package/src/services/thinq/thinqApiClient.ts +418 -0
  172. package/src/services/thinq/thinqDeviceService.test.ts +415 -0
  173. package/src/services/thinq/thinqDeviceService.ts +101 -0
  174. package/src/services/thinq/thinqFilterMonitoringService.test.ts +264 -0
  175. package/src/services/thinq/thinqFilterMonitoringService.ts +53 -0
  176. package/src/settings.ts +18 -0
  177. package/src/tests/cli/commands/devices.test.ts +293 -0
  178. package/src/tests/cli/commands/login.test.ts +240 -0
  179. package/src/tests/cli/help.test.ts +46 -0
  180. package/src/tests/cli/main.test.ts +214 -0
  181. package/src/tests/cli/session.test.ts +198 -0
  182. package/src/tests/cli/utils.test.ts +194 -0
  183. package/src/tests/core/domain/value-objects/AirConditionerCapabilities.test.ts +685 -0
  184. package/src/tests/core/domain/value-objects/ThinqFilterState.test.ts +210 -0
  185. package/src/tests/core/domain/value-objects/ThinqSnapshot.test.ts +1035 -0
  186. package/src/tests/helpers/testUtils.ts +118 -0
  187. package/src/tests/model/LgThinqPluginPlatformConfig.test.ts +81 -0
  188. package/src/tests/platform/deviceRegistry.test.ts +177 -0
  189. package/src/tests/platform/platformConfigManager.test.ts +790 -0
  190. package/src/tests/platform/platformState.test.ts +41 -0
  191. package/src/tests/platform/thinq/thinqAirConditionerCommandHandlers.test.ts +879 -0
  192. package/src/tests/platform/thinq/thinqAirConditionerEndpointFactory.test.ts +993 -0
  193. package/src/tests/platform/thinq/thinqAirConditionerFilterResetCommandHandler.test.ts +508 -0
  194. package/src/tests/platform/thinq/thinqAirConditionerFilterStateSync.test.ts +212 -0
  195. package/src/tests/platform/thinq/thinqAirConditionerStateSync.test.ts +1586 -0
  196. package/src/tests/platform/thinq/thinqDeviceConfigReconciler.test.ts +269 -0
  197. package/src/tests/platform/thinq/thinqDeviceConfigurator.test.ts +492 -0
  198. package/src/tests/platform/thinq/thinqWasherCommandHandlers.test.ts +594 -0
  199. package/src/tests/platform/thinq/thinqWasherEndpointFactory.test.ts +84 -0
  200. package/src/tests/platform/thinq/thinqWasherStateSync.test.ts +373 -0
  201. package/src/tests/platform/thinq/thinqWasherStopCommandResolver.test.ts +392 -0
  202. package/src/tests/settings.test.ts +33 -0
  203. package/src/utils/mask.ts +50 -0
  204. package/tsconfig.base.json +25 -0
  205. package/tsconfig.json +9 -0
  206. package/tsconfig.production.json +13 -0
  207. package/tsconfig.vitest.json +5 -0
  208. package/vite.config.ts +43 -0
  209. package/wiki/memory-archive.md +12 -0
  210. package/wiki/reference-workspaces.md +23 -0
@@ -0,0 +1,194 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ // Mock readline before importing prompt
4
+ vi.mock('node:readline');
5
+
6
+ import readline from 'node:readline';
7
+
8
+ import { maskSecret, parseArgs, prompt } from '../../cli/utils.js';
9
+
10
+ describe('parseArgs', () => {
11
+ it('should parse a single --command argument', () => {
12
+ const result = parseArgs(['--command', 'login']);
13
+ expect(result).toEqual({ command: 'login' });
14
+ });
15
+
16
+ it('should parse multiple arguments', () => {
17
+ const result = parseArgs(['--command', 'login', '--type', 'token']);
18
+ expect(result).toEqual({ command: 'login', type: 'token' });
19
+ });
20
+
21
+ it('should treat a boolean flag with no following value as "true"', () => {
22
+ const result = parseArgs(['--debug']);
23
+ expect(result).toEqual({ debug: 'true' });
24
+ });
25
+
26
+ it('should treat a boolean flag followed by another flag as "true"', () => {
27
+ const result = parseArgs(['--debug', '--help']);
28
+ expect(result).toEqual({ debug: 'true', help: 'true' });
29
+ });
30
+
31
+ it('should return empty object for empty array', () => {
32
+ const result = parseArgs([]);
33
+ expect(result).toEqual({});
34
+ });
35
+
36
+ it('should ignore args not starting with -- at position 0', () => {
37
+ const result = parseArgs(['login', '--command', 'login']);
38
+ expect(result).toEqual({ command: 'login' });
39
+ });
40
+
41
+ it('should handle multiple flags and values together', () => {
42
+ const result = parseArgs(['--command', 'login', '--country', 'US', '--debug']);
43
+ expect(result).toEqual({ command: 'login', country: 'US', debug: 'true' });
44
+ });
45
+
46
+ it('should ignore non-flag arguments that are not following a flag', () => {
47
+ const result = parseArgs(['--type', 'account', 'extra', '--debug']);
48
+ expect(result).toEqual({ type: 'account', debug: 'true' });
49
+ });
50
+ });
51
+
52
+ describe('maskSecret', () => {
53
+ it('should return empty string for undefined', () => {
54
+ const result = maskSecret(undefined);
55
+ expect(result).toBe('');
56
+ });
57
+
58
+ it('should return empty string for empty string', () => {
59
+ const result = maskSecret('');
60
+ expect(result).toBe('');
61
+ });
62
+
63
+ it('should fully mask values shorter than or equal to visibleChars', () => {
64
+ const result = maskSecret('abc');
65
+ expect(result).toBe('***');
66
+ expect(result.length).toBe(3);
67
+ });
68
+
69
+ it('should mask value with default visibleChars=4', () => {
70
+ const result = maskSecret('abcdefgh1234');
71
+ expect(result).toBe('********1234');
72
+ expect(result.length).toBe(12);
73
+ });
74
+
75
+ it('should mask value with custom visibleChars', () => {
76
+ const result = maskSecret('abcdefgh', 2);
77
+ expect(result).toBe('******gh');
78
+ expect(result.length).toBe(8);
79
+ });
80
+
81
+ it('should preserve the last visibleChars characters', () => {
82
+ const secret = 'mytoken123456789';
83
+ const result = maskSecret(secret, 4);
84
+ expect(result).toBe('************6789');
85
+ expect(result.endsWith('6789')).toBe(true);
86
+ });
87
+
88
+ it('should have the same length as the original value', () => {
89
+ const secret = 'verylongsecrettoken1234567890';
90
+ const result = maskSecret(secret, 4);
91
+ expect(result.length).toBe(secret.length);
92
+ });
93
+
94
+ it('should contain only asterisks and the visible chars suffix', () => {
95
+ const secret = 'abcdefghijklmnopqrstuvwxyz1234';
96
+ const result = maskSecret(secret, 4);
97
+ const prefix = result.slice(0, -4);
98
+ expect(prefix).toBe('*'.repeat(secret.length - 4));
99
+ expect(result.slice(-4)).toBe('1234');
100
+ });
101
+
102
+ it('should mask a realistic token', () => {
103
+ const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIxMjM0NTY3ODkwIn0';
104
+ const result = maskSecret(token, 4);
105
+ // Check that the result only contains asterisks and the last 4 chars
106
+ expect(result.slice(0, -4)).toBe('*'.repeat(token.length - 4));
107
+ expect(result.slice(-4)).toBe(token.slice(-4));
108
+ // Ensure no substring of the original (except suffix) is present
109
+ const maskedPart = result.slice(0, -4);
110
+ const originalPrefix = token.slice(0, -4);
111
+ expect(maskedPart).not.toBe(originalPrefix);
112
+ expect(maskedPart).toBe('*'.repeat(maskedPart.length));
113
+ });
114
+
115
+ it('should handle single character correctly', () => {
116
+ const result = maskSecret('a');
117
+ expect(result).toBe('*');
118
+ });
119
+
120
+ it('should handle exactly visibleChars length', () => {
121
+ const result = maskSecret('abcd', 4);
122
+ expect(result).toBe('****');
123
+ });
124
+ });
125
+
126
+ describe('prompt', () => {
127
+ let mockReadline: any;
128
+
129
+ beforeEach(() => {
130
+ vi.clearAllMocks();
131
+ // Setup mock readline interface
132
+ mockReadline = {
133
+ question: vi.fn((question: string, callback: (answer: string) => void) => {
134
+ callback(' typed answer ');
135
+ }),
136
+ close: vi.fn(),
137
+ };
138
+
139
+ // Mock the createInterface function to return our mock readline
140
+ vi.mocked(readline.createInterface).mockReturnValue(mockReadline as any);
141
+ });
142
+
143
+ afterEach(() => {
144
+ vi.clearAllMocks();
145
+ });
146
+
147
+ it('should return trimmed answer from prompt', async () => {
148
+ // Act
149
+ const result = await prompt('Username: ');
150
+
151
+ // Assert
152
+ expect(result).toBe('typed answer');
153
+ });
154
+
155
+ it('should call close on readline interface', async () => {
156
+ // Act
157
+ await prompt('Password: ');
158
+
159
+ // Assert
160
+ expect(mockReadline.close).toHaveBeenCalled();
161
+ });
162
+
163
+ it('should call createInterface with stdin and stdout', async () => {
164
+ // Act
165
+ await prompt('Test question: ');
166
+
167
+ // Assert
168
+ expect(readline.createInterface).toHaveBeenCalledWith({
169
+ input: process.stdin,
170
+ output: process.stdout,
171
+ });
172
+ });
173
+
174
+ it('should pass the question to readline.question', async () => {
175
+ // Act
176
+ await prompt('Custom prompt: ');
177
+
178
+ // Assert
179
+ expect(mockReadline.question).toHaveBeenCalledWith('Custom prompt: ', expect.any(Function));
180
+ });
181
+
182
+ it('should trim whitespace from answer', async () => {
183
+ // Arrange
184
+ mockReadline.question.mockImplementation((question: string, callback: (answer: string) => void) => {
185
+ callback(' spaces around ');
186
+ });
187
+
188
+ // Act
189
+ const result = await prompt('Input: ');
190
+
191
+ // Assert
192
+ expect(result).toBe('spaces around');
193
+ });
194
+ });