@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.4.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.
- package/.env.example +1 -1
- package/README.md +24 -2
- package/dist/omadaClient/accessControl.d.ts +290 -0
- package/dist/omadaClient/accessControl.js +509 -0
- package/dist/omadaClient/accessControl.js.map +1 -0
- package/dist/omadaClient/client.d.ts +6 -1
- package/dist/omadaClient/client.js +12 -1
- package/dist/omadaClient/client.js.map +1 -1
- package/dist/omadaClient/diagnose.d.ts +68 -0
- package/dist/omadaClient/diagnose.js +164 -0
- package/dist/omadaClient/diagnose.js.map +1 -0
- package/dist/omadaClient/index.d.ts +9 -0
- package/dist/omadaClient/index.js +19 -0
- package/dist/omadaClient/index.js.map +1 -1
- package/dist/omadaClient/macGroups.d.ts +58 -0
- package/dist/omadaClient/macGroups.js +110 -0
- package/dist/omadaClient/macGroups.js.map +1 -0
- package/dist/omadaClient/serialize.d.ts +13 -0
- package/dist/omadaClient/serialize.js +29 -0
- package/dist/omadaClient/serialize.js.map +1 -0
- package/dist/omadaClient/timeRange.d.ts +41 -0
- package/dist/omadaClient/timeRange.js +109 -0
- package/dist/omadaClient/timeRange.js.map +1 -0
- package/dist/tools/infra/actions.d.ts +1 -1
- package/dist/tools/infra/actions.js +1 -1
- package/dist/tools/omada/accessControl.d.ts +151 -0
- package/dist/tools/omada/accessControl.js +158 -0
- package/dist/tools/omada/accessControl.js.map +1 -0
- package/dist/tools/omada/index.js +9 -0
- package/dist/tools/omada/index.js.map +1 -1
- package/dist/tools/omada/macGroups.d.ts +19 -0
- package/dist/tools/omada/macGroups.js +77 -0
- package/dist/tools/omada/macGroups.js.map +1 -0
- package/dist/tools/omada/namespace.js +16 -0
- package/dist/tools/omada/namespace.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/.codex/review-rules.md +0 -16
- package/.dockerignore +0 -8
- package/.gitattributes +0 -30
- package/.github/workflows/ci.yml +0 -193
- package/AGENTS.md +0 -389
- package/Dockerfile +0 -50
- package/docker-compose.yml +0 -23
- package/docs/CHANGELOG.md +0 -341
- package/docs/COVERAGE.md +0 -219
- package/docs/QUICK_START.md +0 -240
- package/eslint.config.js +0 -33
- package/n8n/ai-agent-safety-monitor.json +0 -355
- package/openapi.json +0 -1259
- package/scripts/check-version-sync.mjs +0 -69
- package/scripts/generate-jwt.ts +0 -121
- package/src/config.ts +0 -440
- package/src/haClient/automations.ts +0 -261
- package/src/haClient/calendars.ts +0 -94
- package/src/haClient/config.ts +0 -52
- package/src/haClient/devices.ts +0 -653
- package/src/haClient/entities.ts +0 -128
- package/src/haClient/events.ts +0 -689
- package/src/haClient/history.ts +0 -73
- package/src/haClient/index.ts +0 -336
- package/src/haClient/request.ts +0 -142
- package/src/haClient/services.ts +0 -45
- package/src/haClient/states.ts +0 -60
- package/src/haClient/updates.ts +0 -58
- package/src/haClient/wsCommand.ts +0 -44
- package/src/index.ts +0 -164
- package/src/localAI/index.ts +0 -88
- package/src/localAI/prompts.ts +0 -50
- package/src/localAI/providers/base.ts +0 -120
- package/src/localAI/providers/ollama.ts +0 -78
- package/src/localAI/providers/openai.ts +0 -86
- package/src/localAI/types.ts +0 -67
- package/src/omadaClient/auth.ts +0 -178
- package/src/omadaClient/client.ts +0 -337
- package/src/omadaClient/device.ts +0 -118
- package/src/omadaClient/index.ts +0 -310
- package/src/omadaClient/network.ts +0 -284
- package/src/omadaClient/request.ts +0 -365
- package/src/omadaClient/security.ts +0 -51
- package/src/omadaClient/site.ts +0 -344
- package/src/ownerQuestions/service.ts +0 -536
- package/src/permissions/index.ts +0 -143
- package/src/resources/index.ts +0 -213
- package/src/server/auth.ts +0 -175
- package/src/server/chatFace.ts +0 -789
- package/src/server/chatSlice.ts +0 -57
- package/src/server/common.ts +0 -170
- package/src/server/eventSubscription.ts +0 -343
- package/src/server/http.ts +0 -626
- package/src/server/instructions.ts +0 -97
- package/src/server/rateLimiter.ts +0 -227
- package/src/server/stdio.ts +0 -41
- package/src/server/stream.ts +0 -205
- package/src/tools/ai/analyzeSensors.ts +0 -33
- package/src/tools/ai/index.ts +0 -20
- package/src/tools/common.ts +0 -442
- package/src/tools/homeassistant/askOwner.ts +0 -133
- package/src/tools/homeassistant/automationControls.ts +0 -66
- package/src/tools/homeassistant/calendar.ts +0 -97
- package/src/tools/homeassistant/callService.ts +0 -25
- package/src/tools/homeassistant/controlClimate.ts +0 -68
- package/src/tools/homeassistant/controlCover.ts +0 -48
- package/src/tools/homeassistant/controlFan.ts +0 -44
- package/src/tools/homeassistant/controlLight.ts +0 -38
- package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
- package/src/tools/homeassistant/createAutomation.ts +0 -35
- package/src/tools/homeassistant/deleteAutomation.ts +0 -24
- package/src/tools/homeassistant/entityAction.ts +0 -35
- package/src/tools/homeassistant/entityGuard.ts +0 -28
- package/src/tools/homeassistant/getAllSensors.ts +0 -46
- package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
- package/src/tools/homeassistant/getDomainSummary.ts +0 -29
- package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
- package/src/tools/homeassistant/getHistory.ts +0 -24
- package/src/tools/homeassistant/getState.ts +0 -24
- package/src/tools/homeassistant/getStates.ts +0 -46
- package/src/tools/homeassistant/getVersion.ts +0 -21
- package/src/tools/homeassistant/index.ts +0 -121
- package/src/tools/homeassistant/listAutomations.ts +0 -35
- package/src/tools/homeassistant/listEntities.ts +0 -44
- package/src/tools/homeassistant/listPersons.ts +0 -46
- package/src/tools/homeassistant/sceneAndScript.ts +0 -53
- package/src/tools/homeassistant/searchEntities.ts +0 -58
- package/src/tools/homeassistant/sendNotification.ts +0 -179
- package/src/tools/homeassistant/systemTools.ts +0 -52
- package/src/tools/homeassistant/triggerAutomation.ts +0 -24
- package/src/tools/index.ts +0 -78
- package/src/tools/infra/actions.ts +0 -95
- package/src/tools/infra/index.ts +0 -138
- package/src/tools/omada/blockClient.ts +0 -28
- package/src/tools/omada/cyclePoePort.ts +0 -32
- package/src/tools/omada/deleteClient.ts +0 -27
- package/src/tools/omada/disableClientRateLimit.ts +0 -23
- package/src/tools/omada/getClient.ts +0 -14
- package/src/tools/omada/getDevice.ts +0 -14
- package/src/tools/omada/getFirewallSetting.ts +0 -14
- package/src/tools/omada/getInternetInfo.ts +0 -14
- package/src/tools/omada/getLanNetworkList.ts +0 -14
- package/src/tools/omada/getLanProfileList.ts +0 -14
- package/src/tools/omada/getPortForwardingStatus.ts +0 -26
- package/src/tools/omada/getRateLimitProfiles.ts +0 -14
- package/src/tools/omada/getSiteNtpStatus.ts +0 -14
- package/src/tools/omada/getSsidDetail.ts +0 -22
- package/src/tools/omada/getSsidList.ts +0 -21
- package/src/tools/omada/getSwitchStackDetail.ts +0 -14
- package/src/tools/omada/getThreatList.ts +0 -42
- package/src/tools/omada/getWlanGroupList.ts +0 -14
- package/src/tools/omada/graph.ts +0 -187
- package/src/tools/omada/index.ts +0 -153
- package/src/tools/omada/listBlockedClients.ts +0 -17
- package/src/tools/omada/listClients.ts +0 -14
- package/src/tools/omada/listClientsActivity.ts +0 -25
- package/src/tools/omada/listClientsPastConnections.ts +0 -44
- package/src/tools/omada/listDevices.ts +0 -14
- package/src/tools/omada/listDevicesStats.ts +0 -41
- package/src/tools/omada/listMostActiveClients.ts +0 -14
- package/src/tools/omada/listSites.ts +0 -18
- package/src/tools/omada/namespace.ts +0 -1164
- package/src/tools/omada/searchDevices.ts +0 -20
- package/src/tools/omada/setClientRateLimit.ts +0 -26
- package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
- package/src/tools/omada/setSiteNtpServers.ts +0 -36
- package/src/tools/omada/setSsidEnabled.ts +0 -38
- package/src/tools/omada/unblockClient.ts +0 -26
- package/src/tools/types.ts +0 -11
- package/src/tools/utils/pagination-schema.ts +0 -27
- package/src/types/ai/index.ts +0 -11
- package/src/types/ai/types.ts +0 -67
- package/src/types/homeassistant/automations.ts +0 -43
- package/src/types/homeassistant/calendars.ts +0 -26
- package/src/types/homeassistant/config.ts +0 -14
- package/src/types/homeassistant/entities.ts +0 -14
- package/src/types/homeassistant/errors.ts +0 -33
- package/src/types/homeassistant/haConfig.ts +0 -7
- package/src/types/homeassistant/history.ts +0 -13
- package/src/types/homeassistant/index.ts +0 -46
- package/src/types/homeassistant/request.ts +0 -6
- package/src/types/homeassistant/serviceCall.ts +0 -18
- package/src/types/homeassistant/states.ts +0 -58
- package/src/types/homeassistant/updates.ts +0 -28
- package/src/types/index.ts +0 -12
- package/src/types/omada/activeClientInfo.ts +0 -17
- package/src/types/omada/clientActivity.ts +0 -32
- package/src/types/omada/clientBlockStatus.ts +0 -11
- package/src/types/omada/clientPastConnection.ts +0 -82
- package/src/types/omada/index.ts +0 -14
- package/src/types/omada/omadaApiResponse.ts +0 -5
- package/src/types/omada/omadaClientInfo.ts +0 -11
- package/src/types/omada/omadaDeviceInfo.ts +0 -11
- package/src/types/omada/omadaDeviceStats.ts +0 -31
- package/src/types/omada/omadaSiteSummary.ts +0 -5
- package/src/types/omada/oswStackDetail.ts +0 -11
- package/src/types/omada/paginatedResult.ts +0 -6
- package/src/types/omada/rateLimitProfile.ts +0 -58
- package/src/types/omada/threatInfo.ts +0 -38
- package/src/types/omada/tokenResult.ts +0 -6
- package/src/types.ts +0 -2
- package/src/utils/config-validations.ts +0 -68
- package/src/utils/jwt.ts +0 -112
- package/src/utils/logger.ts +0 -121
- package/src/utils/plainText.ts +0 -48
- package/src/utils/redact.ts +0 -59
- package/src/utils/sanitizeError.ts +0 -18
- package/src/utils/tlsDispatcher.ts +0 -69
- package/src/version.ts +0 -5
- package/tests/bdd/automation-creation.spec.ts +0 -214
- package/tests/bdd/automation-management.spec.ts +0 -267
- package/tests/bdd/calendar.spec.ts +0 -190
- package/tests/bdd/configuration.spec.ts +0 -283
- package/tests/bdd/device-control.spec.ts +0 -344
- package/tests/bdd/entity-listing.spec.ts +0 -208
- package/tests/bdd/entity-search.spec.ts +0 -183
- package/tests/bdd/history-retrieval.spec.ts +0 -195
- package/tests/bdd/notifications.spec.ts +0 -238
- package/tests/bdd/service-calls.spec.ts +0 -144
- package/tests/bdd/state-retrieval.spec.ts +0 -204
- package/tests/bench/hotpaths.bench.ts +0 -68
- package/tests/config.test.ts +0 -355
- package/tests/features/automation-creation.feature +0 -36
- package/tests/features/automation-management.feature +0 -54
- package/tests/features/calendar.feature +0 -37
- package/tests/features/configuration.feature +0 -70
- package/tests/features/device-control.feature +0 -100
- package/tests/features/entity-listing.feature +0 -51
- package/tests/features/entity-search.feature +0 -44
- package/tests/features/history-retrieval.feature +0 -38
- package/tests/features/notifications.feature +0 -37
- package/tests/features/service-calls.feature +0 -35
- package/tests/features/state-retrieval.feature +0 -37
- package/tests/fixtures/openapi-default-slice.json +0 -290
- package/tests/haClient/automationTrace.test.ts +0 -59
- package/tests/haClient/automations.test.ts +0 -149
- package/tests/haClient/config.test.ts +0 -117
- package/tests/haClient/entities.test.ts +0 -188
- package/tests/haClient/events.test.ts +0 -455
- package/tests/haClient/eventsTls.test.ts +0 -52
- package/tests/haClient/history.test.ts +0 -228
- package/tests/haClient/index.test.ts +0 -243
- package/tests/haClient/request.test.ts +0 -217
- package/tests/haClient/services.test.ts +0 -120
- package/tests/haClient/states.test.ts +0 -119
- package/tests/haClient/updates.test.ts +0 -193
- package/tests/index.test.ts +0 -160
- package/tests/integration/homeassistant.test.ts +0 -144
- package/tests/integration/omada.test.ts +0 -167
- package/tests/integration/server.test.ts +0 -151
- package/tests/localAI/index.test.ts +0 -177
- package/tests/localAI/ollama.test.ts +0 -187
- package/tests/localAI/openai.test.ts +0 -257
- package/tests/omadaClient/actions.test.ts +0 -161
- package/tests/omadaClient/clientBlocks.test.ts +0 -170
- package/tests/omadaClient/ntp.test.ts +0 -381
- package/tests/omadaClient/site.test.ts +0 -37
- package/tests/ownerQuestions/service.test.ts +0 -499
- package/tests/ownerQuestions/tools.test.ts +0 -186
- package/tests/permissions/index.test.ts +0 -217
- package/tests/resources/index.test.ts +0 -288
- package/tests/scripts/generateJwt.test.ts +0 -44
- package/tests/security/accessControl.test.ts +0 -223
- package/tests/security/authentication.test.ts +0 -117
- package/tests/security/configuration.test.ts +0 -170
- package/tests/security/cryptoTransport.test.ts +0 -172
- package/tests/security/dependencies.test.ts +0 -152
- package/tests/security/injection.test.ts +0 -117
- package/tests/security/insecureDesign.test.ts +0 -121
- package/tests/security/logging.test.ts +0 -131
- package/tests/security/resourceConsumption.test.ts +0 -142
- package/tests/server/auth.test.ts +0 -249
- package/tests/server/chatFace.test.ts +0 -320
- package/tests/server/common.test.ts +0 -84
- package/tests/server/http.test.ts +0 -561
- package/tests/server/instructions.test.ts +0 -152
- package/tests/server/memoizeJsonSchema.test.ts +0 -71
- package/tests/server/stdio.test.ts +0 -99
- package/tests/server/stream.test.ts +0 -285
- package/tests/server/toolVisibility.http.test.ts +0 -111
- package/tests/server/toolVisibility.test.ts +0 -129
- package/tests/tools/ai/analyzeSensors.test.ts +0 -227
- package/tests/tools/common.test.ts +0 -123
- package/tests/tools/homeassistant/handlers.test.ts +0 -872
- package/tests/tools/infra/invokeAction.test.ts +0 -280
- package/tests/tools/omada/graph.test.ts +0 -385
- package/tests/tools/omada/handlers.test.ts +0 -897
- package/tests/tools/omada/namespace.test.ts +0 -483
- package/tests/tools/registry.test.ts +0 -168
- package/tests/utils/config-validations.test.ts +0 -352
- package/tests/utils/jwt.test.ts +0 -115
- package/tests/utils/logger.test.ts +0 -327
- package/tests/utils/redact.test.ts +0 -105
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -38
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
6
|
-
import { initLogger, setLevel, logger } from '../../src/utils/logger.js';
|
|
7
|
-
import type { LogLevel, LogFormat } from '../../src/utils/logger.js';
|
|
8
|
-
|
|
9
|
-
describe('Logger', () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
// Reset to default configuration
|
|
12
|
-
initLogger('info', 'plain', false);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('initLogger', () => {
|
|
16
|
-
it('should initialize with default level and format', () => {
|
|
17
|
-
initLogger('info', 'plain', false);
|
|
18
|
-
|
|
19
|
-
// Logger should be initialized without errors
|
|
20
|
-
expect(logger).toBeDefined();
|
|
21
|
-
expect(logger.info).toBeDefined();
|
|
22
|
-
expect(logger.debug).toBeDefined();
|
|
23
|
-
expect(logger.warn).toBeDefined();
|
|
24
|
-
expect(logger.error).toBeDefined();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should support all log levels', () => {
|
|
28
|
-
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'silent'];
|
|
29
|
-
|
|
30
|
-
for (const level of levels) {
|
|
31
|
-
expect(() => initLogger(level, 'plain', false)).not.toThrow();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should support all log formats', () => {
|
|
36
|
-
const formats: LogFormat[] = ['plain', 'json', 'gcp-json'];
|
|
37
|
-
|
|
38
|
-
for (const format of formats) {
|
|
39
|
-
expect(() => initLogger('info', format, false)).not.toThrow();
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should configure stderr output when useStderr is true', () => {
|
|
44
|
-
expect(() => initLogger('info', 'plain', true)).not.toThrow();
|
|
45
|
-
|
|
46
|
-
// Logger should be configured to use stderr
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should configure stdout output when useStderr is false', () => {
|
|
50
|
-
expect(() => initLogger('info', 'plain', false)).not.toThrow();
|
|
51
|
-
|
|
52
|
-
// Logger should be configured to use stdout
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('setLevel', () => {
|
|
57
|
-
it('should change log level dynamically', () => {
|
|
58
|
-
initLogger('info', 'plain', false);
|
|
59
|
-
|
|
60
|
-
expect(() => setLevel('debug')).not.toThrow();
|
|
61
|
-
expect(() => setLevel('warn')).not.toThrow();
|
|
62
|
-
expect(() => setLevel('error')).not.toThrow();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should accept all valid log levels', () => {
|
|
66
|
-
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'silent'];
|
|
67
|
-
|
|
68
|
-
for (const level of levels) {
|
|
69
|
-
expect(() => setLevel(level)).not.toThrow();
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('Log methods', () => {
|
|
75
|
-
it('should have debug method', () => {
|
|
76
|
-
expect(typeof logger.debug).toBe('function');
|
|
77
|
-
expect(() => logger.debug('Test debug message')).not.toThrow();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should have info method', () => {
|
|
81
|
-
expect(typeof logger.info).toBe('function');
|
|
82
|
-
expect(() => logger.info('Test info message')).not.toThrow();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should have warn method', () => {
|
|
86
|
-
expect(typeof logger.warn).toBe('function');
|
|
87
|
-
expect(() => logger.warn('Test warn message')).not.toThrow();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should have error method', () => {
|
|
91
|
-
expect(typeof logger.error).toBe('function');
|
|
92
|
-
expect(() => logger.error('Test error message')).not.toThrow();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('Log with metadata', () => {
|
|
97
|
-
it('should log with metadata object', () => {
|
|
98
|
-
const metadata = {
|
|
99
|
-
userId: 'user-123',
|
|
100
|
-
requestId: 'req-456',
|
|
101
|
-
duration: 123,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
expect(() => logger.info('Test message', metadata)).not.toThrow();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should handle Error objects in metadata', () => {
|
|
108
|
-
const error = new Error('Test error');
|
|
109
|
-
const metadata = {
|
|
110
|
-
error,
|
|
111
|
-
context: 'test',
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
expect(() => logger.error('Error occurred', metadata)).not.toThrow();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should handle nested objects in metadata', () => {
|
|
118
|
-
const metadata = {
|
|
119
|
-
config: {
|
|
120
|
-
baseUrl: 'http://example.com',
|
|
121
|
-
timeout: 30000,
|
|
122
|
-
},
|
|
123
|
-
user: {
|
|
124
|
-
id: 'user-123',
|
|
125
|
-
name: 'Test User',
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
expect(() => logger.info('Complex metadata', metadata)).not.toThrow();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should handle arrays in metadata', () => {
|
|
133
|
-
const metadata = {
|
|
134
|
-
entities: ['light.living_room', 'sensor.temperature'],
|
|
135
|
-
tags: ['home', 'automation'],
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
expect(() => logger.info('Array metadata', metadata)).not.toThrow();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should handle empty metadata', () => {
|
|
142
|
-
expect(() => logger.info('No metadata')).not.toThrow();
|
|
143
|
-
expect(() => logger.info('Empty metadata', {})).not.toThrow();
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('Log formats', () => {
|
|
148
|
-
it('should format plain logs with uppercase level', () => {
|
|
149
|
-
initLogger('info', 'plain', false);
|
|
150
|
-
|
|
151
|
-
// Plain format should use { level: 'INFO' }
|
|
152
|
-
expect(() => logger.info('Plain format test')).not.toThrow();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should format JSON logs with standard structure', () => {
|
|
156
|
-
initLogger('info', 'json', false);
|
|
157
|
-
|
|
158
|
-
// JSON format should use standard pino JSON
|
|
159
|
-
expect(() => logger.info('JSON format test')).not.toThrow();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should format GCP JSON logs with severity field', () => {
|
|
163
|
-
initLogger('info', 'gcp-json', false);
|
|
164
|
-
|
|
165
|
-
// GCP format should use { severity: 'INFO' }
|
|
166
|
-
expect(() => logger.info('GCP format test')).not.toThrow();
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('Severity mapping for GCP format', () => {
|
|
171
|
-
it('should map debug to DEBUG', () => {
|
|
172
|
-
const levelToSeverity = {
|
|
173
|
-
debug: 'DEBUG',
|
|
174
|
-
info: 'INFO',
|
|
175
|
-
warn: 'WARNING',
|
|
176
|
-
error: 'ERROR',
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
expect(levelToSeverity.debug).toBe('DEBUG');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should map info to INFO', () => {
|
|
183
|
-
const levelToSeverity = {
|
|
184
|
-
debug: 'DEBUG',
|
|
185
|
-
info: 'INFO',
|
|
186
|
-
warn: 'WARNING',
|
|
187
|
-
error: 'ERROR',
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
expect(levelToSeverity.info).toBe('INFO');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should map warn to WARNING', () => {
|
|
194
|
-
const levelToSeverity = {
|
|
195
|
-
debug: 'DEBUG',
|
|
196
|
-
info: 'INFO',
|
|
197
|
-
warn: 'WARNING',
|
|
198
|
-
error: 'ERROR',
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
expect(levelToSeverity.warn).toBe('WARNING');
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should map error to ERROR', () => {
|
|
205
|
-
const levelToSeverity = {
|
|
206
|
-
debug: 'DEBUG',
|
|
207
|
-
info: 'INFO',
|
|
208
|
-
warn: 'WARNING',
|
|
209
|
-
error: 'ERROR',
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
expect(levelToSeverity.error).toBe('ERROR');
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('Error metadata normalization', () => {
|
|
217
|
-
it('should normalize Error to object with message and stack', () => {
|
|
218
|
-
const error = new Error('Test error');
|
|
219
|
-
const normalized = {
|
|
220
|
-
message: error.message,
|
|
221
|
-
stack: error.stack,
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
expect(normalized.message).toBe('Test error');
|
|
225
|
-
expect(normalized.stack).toBeDefined();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should preserve non-Error values', () => {
|
|
229
|
-
const metadata = {
|
|
230
|
-
string: 'test',
|
|
231
|
-
number: 123,
|
|
232
|
-
boolean: true,
|
|
233
|
-
null: null,
|
|
234
|
-
array: [1, 2, 3],
|
|
235
|
-
object: { key: 'value' },
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
expect(metadata.string).toBe('test');
|
|
239
|
-
expect(metadata.number).toBe(123);
|
|
240
|
-
expect(metadata.boolean).toBe(true);
|
|
241
|
-
expect(metadata.null).toBeNull();
|
|
242
|
-
expect(metadata.array).toEqual([1, 2, 3]);
|
|
243
|
-
expect(metadata.object).toEqual({ key: 'value' });
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('Timestamp format', () => {
|
|
248
|
-
it('should use ISO time format', () => {
|
|
249
|
-
// Pino stdTimeFunctions.isoTime produces ISO 8601 timestamps
|
|
250
|
-
const now = new Date();
|
|
251
|
-
const isoTime = now.toISOString();
|
|
252
|
-
|
|
253
|
-
expect(isoTime).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe('Message key', () => {
|
|
258
|
-
it('should use "message" as the message key', () => {
|
|
259
|
-
const logEntry = {
|
|
260
|
-
level: 'INFO',
|
|
261
|
-
time: '2026-01-08T12:00:00.000Z',
|
|
262
|
-
message: 'Test message',
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
expect(logEntry.message).toBe('Test message');
|
|
266
|
-
expect('message' in logEntry).toBe(true);
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
describe('Base fields', () => {
|
|
271
|
-
it('should not include base fields like hostname and pid', () => {
|
|
272
|
-
// baseConfig.base = undefined disables these fields
|
|
273
|
-
const logEntry = {
|
|
274
|
-
level: 'INFO',
|
|
275
|
-
time: '2026-01-08T12:00:00.000Z',
|
|
276
|
-
message: 'Test message',
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
expect('hostname' in logEntry).toBe(false);
|
|
280
|
-
expect('pid' in logEntry).toBe(false);
|
|
281
|
-
expect('name' in logEntry).toBe(false);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe('Stdio mode behavior', () => {
|
|
286
|
-
it('should log to stderr in stdio mode to avoid MCP protocol interference', () => {
|
|
287
|
-
const useStderr = true;
|
|
288
|
-
|
|
289
|
-
initLogger('info', 'plain', useStderr);
|
|
290
|
-
|
|
291
|
-
// Logger should be configured for stderr
|
|
292
|
-
expect(() => logger.info('Stdio mode test')).not.toThrow();
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should log to stdout in HTTP mode', () => {
|
|
296
|
-
const useStderr = false;
|
|
297
|
-
|
|
298
|
-
initLogger('info', 'plain', useStderr);
|
|
299
|
-
|
|
300
|
-
// Logger should be configured for stdout
|
|
301
|
-
expect(() => logger.info('HTTP mode test')).not.toThrow();
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
describe('Environment variable initialization', () => {
|
|
306
|
-
it('should use MCP_SERVER_LOG_LEVEL from environment by default', () => {
|
|
307
|
-
// The default instance reads from process.env.MCP_SERVER_LOG_LEVEL
|
|
308
|
-
const defaultLevel = (process.env.MCP_SERVER_LOG_LEVEL as LogLevel | undefined) ?? 'info';
|
|
309
|
-
|
|
310
|
-
expect(['debug', 'info', 'warn', 'error', 'silent']).toContain(defaultLevel);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
describe('Multiple logger instances', () => {
|
|
315
|
-
it('should replace previous instance when reinitializing', () => {
|
|
316
|
-
initLogger('info', 'plain', false);
|
|
317
|
-
const firstInit = { ...logger };
|
|
318
|
-
|
|
319
|
-
initLogger('debug', 'json', true);
|
|
320
|
-
const secondInit = { ...logger };
|
|
321
|
-
|
|
322
|
-
// Second initialization should replace the first
|
|
323
|
-
expect(firstInit).toBeDefined();
|
|
324
|
-
expect(secondInit).toBeDefined();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
});
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Secrets returned by Home Assistant / Omada never reach a tool response.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
6
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
7
|
-
|
|
8
|
-
import { redactSecrets, REDACTED } from '../../src/utils/redact.js';
|
|
9
|
-
import { toToolResult } from '../../src/tools/common.js';
|
|
10
|
-
import { registerOmadaGraphTools } from '../../src/tools/omada/graph.js';
|
|
11
|
-
import type { OmadaClient } from '../../src/omadaClient/index.js';
|
|
12
|
-
|
|
13
|
-
/** Shape of an Omada SSID detail (GET .../wlans/{wlanId}/ssids/{ssidId}). */
|
|
14
|
-
const ssidDetail = {
|
|
15
|
-
ssidId: 'node-s55',
|
|
16
|
-
name: 'node-s55',
|
|
17
|
-
security: 3,
|
|
18
|
-
hidePwd: true,
|
|
19
|
-
pskSetting: { versionPsk: 2, encryptionPsk: 3, gikRekeyPskEnable: false, securityKey: 'HouseWifiPassw0rd!' },
|
|
20
|
-
macFilterEnable: false,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
describe('redactSecrets', () => {
|
|
24
|
-
it('redacts the Wi-Fi password but keeps flags and numbers with similar names', () => {
|
|
25
|
-
const out = redactSecrets(ssidDetail);
|
|
26
|
-
|
|
27
|
-
expect(out.pskSetting.securityKey).toBe(REDACTED);
|
|
28
|
-
expect(out.pskSetting.versionPsk).toBe(2);
|
|
29
|
-
expect(out.pskSetting.encryptionPsk).toBe(3);
|
|
30
|
-
expect(out.hidePwd).toBe(true);
|
|
31
|
-
expect(out.name).toBe('node-s55');
|
|
32
|
-
expect(JSON.stringify(out)).not.toContain('HouseWifiPassw0rd!');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it.each([
|
|
36
|
-
['RADIUS profile', { authServer: [{ ip: '192.168.0.2', port: 1812, secret: 'radius-s3cret' }], accountingServerPwd: 'acct-pw', coaPassword: 'coa-pw', radiusPwd: 'rp' }],
|
|
37
|
-
['PPSK profile', { name: 'kids', ppsk: [{ name: 'tablet', password: 'ppsk-key-1', vlanId: 20 }] }],
|
|
38
|
-
['VPN', { preSharedKey: 'ipsec-psk', presharedKey: 'wg-psk', privateKey: 'wg-private', publicKey: 'wg-public' }],
|
|
39
|
-
['SNMP', { snmpV1V2CEnable: true, communityString: 'snmp-community' }],
|
|
40
|
-
['portal and hotspot', { password: 'portal-pw', authCode: 'auth-code-123', simpleKey: 'sk', md5Key: 'mk' }],
|
|
41
|
-
['SSID variants', { psk: 'override-psk', ssidPassword: 'ssid-pw', bridgeSsidPassword: 'bridge-pw', overrideSsidPassword: 'ov-pw' }],
|
|
42
|
-
['VRRP and certificates', { key: 'vrrp-key', keyPassword: 'ks-pw', trustPassword: 'trust-pw' }],
|
|
43
|
-
['webhooks and tokens', { shardedSecret: 'hook-secret', authToken: 'twilio-token', accessToken: 'at', token: 'user-token' }],
|
|
44
|
-
])('redacts every secret in a %s', (_name, input) => {
|
|
45
|
-
const text = JSON.stringify(redactSecrets(input));
|
|
46
|
-
|
|
47
|
-
for (const secret of ['radius-s3cret', 'acct-pw', 'coa-pw', 'ppsk-key-1', 'ipsec-psk', 'wg-psk', 'wg-private', 'snmp-community', 'portal-pw', 'auth-code-123', 'override-psk', 'ssid-pw', 'bridge-pw', 'ov-pw', 'vrrp-key', 'ks-pw', 'trust-pw', 'hook-secret', 'twilio-token', 'user-token']) {
|
|
48
|
-
expect(text).not.toContain(secret);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('keeps public, non-secret values (public keys, ids, names)', () => {
|
|
53
|
-
const out = redactSecrets({ publicKey: 'wg-public', keyId: 'k1', searchKey: 'abc', ppskProfileId: 'p1' });
|
|
54
|
-
expect(out).toEqual({ publicKey: 'wg-public', keyId: 'k1', searchKey: 'abc', ppskProfileId: 'p1' });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('masks tokens inside URLs (Home Assistant camera image)', () => {
|
|
58
|
-
const out = redactSecrets({
|
|
59
|
-
entity_picture: '/api/camera_proxy/camera.door?token=0123456789abcdef&width=640',
|
|
60
|
-
attributes: { access_token: 'cam-token' },
|
|
61
|
-
});
|
|
62
|
-
expect(out.entity_picture).toBe(`/api/camera_proxy/camera.door?token=${REDACTED}&width=640`);
|
|
63
|
-
expect(out.attributes.access_token).toBe(REDACTED);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('leaves empty secrets empty, so "is it set?" stays answerable', () => {
|
|
67
|
-
expect(redactSecrets({ password: '', securityKey: 'x' })).toEqual({ password: '', securityKey: REDACTED });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('does not modify its input', () => {
|
|
71
|
-
const input = structuredClone(ssidDetail);
|
|
72
|
-
redactSecrets(input);
|
|
73
|
-
expect(input).toEqual(ssidDetail);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('handles arrays, nulls and plain strings', () => {
|
|
77
|
-
expect(redactSecrets([{ password: 'p' }, null, 3])).toEqual([{ password: REDACTED }, null, 3]);
|
|
78
|
-
expect(redactSecrets('see /x?token=abc')).toBe(`see /x?token=${REDACTED}`);
|
|
79
|
-
expect(redactSecrets(null)).toBeNull();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('tool responses are redacted', () => {
|
|
84
|
-
it('toToolResult never carries a secret', () => {
|
|
85
|
-
const result = toToolResult(ssidDetail);
|
|
86
|
-
expect(result.content[0].text).not.toContain('HouseWifiPassw0rd!');
|
|
87
|
-
expect(result.content[0].text).toContain(REDACTED);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('omada_read /wifi/ssids (SSID detail) does not return the Wi-Fi password', async () => {
|
|
91
|
-
const handlers = new Map<string, (args: unknown, extra: unknown) => Promise<{ content: { text: string }[] }>>();
|
|
92
|
-
const server = { registerTool: vi.fn((name, _c, h) => handlers.set(name, h)) } as unknown as McpServer;
|
|
93
|
-
const client = { getSsidDetail: vi.fn().mockResolvedValue(ssidDetail), getSsidList: vi.fn() } as unknown as OmadaClient;
|
|
94
|
-
registerOmadaGraphTools(server, client);
|
|
95
|
-
|
|
96
|
-
const result = await handlers.get('omada_read')!(
|
|
97
|
-
{ path: '/wifi/ssids', params: { wlanId: 'w1', ssidId: 'node-s55' } },
|
|
98
|
-
{ sessionId: 's', http: { authInfo: { extra: { permissions: 0xff } } } }
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
expect(client.getSsidDetail).toHaveBeenCalled();
|
|
102
|
-
expect(result.content[0].text).not.toContain('HouseWifiPassw0rd!');
|
|
103
|
-
expect(JSON.parse(result.content[0].text).pskSetting.securityKey).toBe(REDACTED);
|
|
104
|
-
});
|
|
105
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2023",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"outDir": "dist",
|
|
7
|
-
"rootDir": "src",
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"forceConsistentCasingInFileNames": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"noUnusedLocals": true,
|
|
16
|
-
"noUnusedParameters": true,
|
|
17
|
-
"noImplicitReturns": true,
|
|
18
|
-
"noFallthroughCasesInSwitch": true,
|
|
19
|
-
"allowUnusedLabels": false,
|
|
20
|
-
"allowUnreachableCode": false
|
|
21
|
-
},
|
|
22
|
-
"include": ["src"],
|
|
23
|
-
"exclude": ["node_modules", "dist", "tests", "example"]
|
|
24
|
-
}
|
package/vitest.config.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
globals: true,
|
|
6
|
-
environment: 'node',
|
|
7
|
-
reporters: ['default'],
|
|
8
|
-
exclude: [
|
|
9
|
-
'**/node_modules/**',
|
|
10
|
-
'**/dist/**',
|
|
11
|
-
'**/example/**',
|
|
12
|
-
],
|
|
13
|
-
coverage: {
|
|
14
|
-
provider: 'v8',
|
|
15
|
-
reporter: ['text', 'json', 'html'],
|
|
16
|
-
exclude: [
|
|
17
|
-
'node_modules/',
|
|
18
|
-
'dist/',
|
|
19
|
-
'example/',
|
|
20
|
-
'tests/',
|
|
21
|
-
'**/*.test.ts',
|
|
22
|
-
'**/*.config.ts',
|
|
23
|
-
],
|
|
24
|
-
// Regression FLOOR, not an aspiration. Set just below the measured numbers
|
|
25
|
-
// (stmts 68.7 / branch 58.3 / funcs 75.4 / lines 69.3 as of v1.4.0) so the
|
|
26
|
-
// gate catches a DROP in coverage without forcing busywork tests today.
|
|
27
|
-
// Global (not per-file) by design — per-file 90% gates punish trivial files
|
|
28
|
-
// and breed assertion-free stubs. Ratchet these UP as real coverage grows;
|
|
29
|
-
// never lower them to make a red build pass.
|
|
30
|
-
thresholds: {
|
|
31
|
-
statements: 68,
|
|
32
|
-
branches: 57,
|
|
33
|
-
functions: 74,
|
|
34
|
-
lines: 68,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
});
|