oh-my-codex 0.3.8 → 0.3.10

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 (131) hide show
  1. package/dist/cli/__tests__/doctor-team.test.js +58 -0
  2. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  3. package/dist/cli/__tests__/index.test.js +3 -3
  4. package/dist/cli/__tests__/index.test.js.map +1 -1
  5. package/dist/cli/__tests__/lifecycle-notifications.test.d.ts +2 -0
  6. package/dist/cli/__tests__/lifecycle-notifications.test.d.ts.map +1 -0
  7. package/dist/cli/__tests__/lifecycle-notifications.test.js +48 -0
  8. package/dist/cli/__tests__/lifecycle-notifications.test.js.map +1 -0
  9. package/dist/cli/doctor.js +28 -0
  10. package/dist/cli/doctor.js.map +1 -1
  11. package/dist/cli/index.d.ts.map +1 -1
  12. package/dist/cli/index.js +41 -1
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/config/__tests__/models.test.d.ts +2 -0
  15. package/dist/config/__tests__/models.test.d.ts.map +1 -0
  16. package/dist/config/__tests__/models.test.js +69 -0
  17. package/dist/config/__tests__/models.test.js.map +1 -0
  18. package/dist/config/models.d.ts +24 -0
  19. package/dist/config/models.d.ts.map +1 -0
  20. package/dist/config/models.js +53 -0
  21. package/dist/config/models.js.map +1 -0
  22. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +221 -36
  23. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  24. package/dist/mcp/__tests__/state-paths.test.js +21 -1
  25. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  26. package/dist/mcp/__tests__/state-server-team-tools.test.js +53 -1
  27. package/dist/mcp/__tests__/state-server-team-tools.test.js.map +1 -1
  28. package/dist/mcp/state-paths.d.ts +1 -0
  29. package/dist/mcp/state-paths.d.ts.map +1 -1
  30. package/dist/mcp/state-paths.js +34 -1
  31. package/dist/mcp/state-paths.js.map +1 -1
  32. package/dist/mcp/state-server.d.ts.map +1 -1
  33. package/dist/mcp/state-server.js +46 -11
  34. package/dist/mcp/state-server.js.map +1 -1
  35. package/dist/notifications/__tests__/config.test.d.ts +2 -0
  36. package/dist/notifications/__tests__/config.test.d.ts.map +1 -0
  37. package/dist/notifications/__tests__/config.test.js +186 -0
  38. package/dist/notifications/__tests__/config.test.js.map +1 -0
  39. package/dist/notifications/__tests__/dispatcher.test.d.ts +2 -0
  40. package/dist/notifications/__tests__/dispatcher.test.d.ts.map +1 -0
  41. package/dist/notifications/__tests__/dispatcher.test.js +202 -0
  42. package/dist/notifications/__tests__/dispatcher.test.js.map +1 -0
  43. package/dist/notifications/__tests__/formatter.test.d.ts +2 -0
  44. package/dist/notifications/__tests__/formatter.test.d.ts.map +1 -0
  45. package/dist/notifications/__tests__/formatter.test.js +103 -0
  46. package/dist/notifications/__tests__/formatter.test.js.map +1 -0
  47. package/dist/notifications/__tests__/notifier.test.d.ts +2 -0
  48. package/dist/notifications/__tests__/notifier.test.d.ts.map +1 -0
  49. package/dist/notifications/__tests__/notifier.test.js +104 -0
  50. package/dist/notifications/__tests__/notifier.test.js.map +1 -0
  51. package/dist/notifications/__tests__/profiles.test.d.ts +2 -0
  52. package/dist/notifications/__tests__/profiles.test.d.ts.map +1 -0
  53. package/dist/notifications/__tests__/profiles.test.js +404 -0
  54. package/dist/notifications/__tests__/profiles.test.js.map +1 -0
  55. package/dist/notifications/__tests__/reply-listener.test.d.ts +2 -0
  56. package/dist/notifications/__tests__/reply-listener.test.d.ts.map +1 -0
  57. package/dist/notifications/__tests__/reply-listener.test.js +58 -0
  58. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -0
  59. package/dist/notifications/__tests__/session-registry.test.d.ts +2 -0
  60. package/dist/notifications/__tests__/session-registry.test.d.ts.map +1 -0
  61. package/dist/notifications/__tests__/session-registry.test.js +147 -0
  62. package/dist/notifications/__tests__/session-registry.test.js.map +1 -0
  63. package/dist/notifications/__tests__/tmux-detector.test.d.ts +2 -0
  64. package/dist/notifications/__tests__/tmux-detector.test.d.ts.map +1 -0
  65. package/dist/notifications/__tests__/tmux-detector.test.js +77 -0
  66. package/dist/notifications/__tests__/tmux-detector.test.js.map +1 -0
  67. package/dist/notifications/__tests__/tmux.test.d.ts +2 -0
  68. package/dist/notifications/__tests__/tmux.test.d.ts.map +1 -0
  69. package/dist/notifications/__tests__/tmux.test.js +86 -0
  70. package/dist/notifications/__tests__/tmux.test.js.map +1 -0
  71. package/dist/notifications/config.d.ts +44 -0
  72. package/dist/notifications/config.d.ts.map +1 -0
  73. package/dist/notifications/config.js +407 -0
  74. package/dist/notifications/config.js.map +1 -0
  75. package/dist/notifications/dispatcher.d.ts +15 -0
  76. package/dist/notifications/dispatcher.d.ts.map +1 -0
  77. package/dist/notifications/dispatcher.js +410 -0
  78. package/dist/notifications/dispatcher.js.map +1 -0
  79. package/dist/notifications/formatter.d.ts +14 -0
  80. package/dist/notifications/formatter.d.ts.map +1 -0
  81. package/dist/notifications/formatter.js +134 -0
  82. package/dist/notifications/formatter.js.map +1 -0
  83. package/dist/notifications/index.d.ts +32 -0
  84. package/dist/notifications/index.d.ts.map +1 -0
  85. package/dist/notifications/index.js +93 -0
  86. package/dist/notifications/index.js.map +1 -0
  87. package/dist/notifications/reply-listener.d.ts +47 -0
  88. package/dist/notifications/reply-listener.d.ts.map +1 -0
  89. package/dist/notifications/reply-listener.js +656 -0
  90. package/dist/notifications/reply-listener.js.map +1 -0
  91. package/dist/notifications/session-registry.d.ts +26 -0
  92. package/dist/notifications/session-registry.d.ts.map +1 -0
  93. package/dist/notifications/session-registry.js +275 -0
  94. package/dist/notifications/session-registry.js.map +1 -0
  95. package/dist/notifications/tmux-detector.d.ts +17 -0
  96. package/dist/notifications/tmux-detector.d.ts.map +1 -0
  97. package/dist/notifications/tmux-detector.js +77 -0
  98. package/dist/notifications/tmux-detector.js.map +1 -0
  99. package/dist/notifications/tmux.d.ts +28 -0
  100. package/dist/notifications/tmux.d.ts.map +1 -0
  101. package/dist/notifications/tmux.js +203 -0
  102. package/dist/notifications/tmux.js.map +1 -0
  103. package/dist/notifications/types.d.ts +181 -0
  104. package/dist/notifications/types.d.ts.map +1 -0
  105. package/dist/notifications/types.js +9 -0
  106. package/dist/notifications/types.js.map +1 -0
  107. package/dist/team/__tests__/runtime.test.js +54 -2
  108. package/dist/team/__tests__/runtime.test.js.map +1 -1
  109. package/dist/team/__tests__/state.test.js +30 -0
  110. package/dist/team/__tests__/state.test.js.map +1 -1
  111. package/dist/team/__tests__/worker-bootstrap.test.js +59 -1
  112. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  113. package/dist/team/runtime.d.ts +2 -2
  114. package/dist/team/runtime.d.ts.map +1 -1
  115. package/dist/team/runtime.js +50 -23
  116. package/dist/team/runtime.js.map +1 -1
  117. package/dist/team/state.d.ts +1 -1
  118. package/dist/team/state.d.ts.map +1 -1
  119. package/dist/team/state.js +5 -0
  120. package/dist/team/state.js.map +1 -1
  121. package/dist/team/tmux-session.d.ts.map +1 -1
  122. package/dist/team/tmux-session.js +53 -10
  123. package/dist/team/tmux-session.js.map +1 -1
  124. package/dist/team/worker-bootstrap.d.ts +12 -0
  125. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  126. package/dist/team/worker-bootstrap.js +37 -0
  127. package/dist/team/worker-bootstrap.js.map +1 -1
  128. package/package.json +1 -1
  129. package/prompts/planner.md +3 -3
  130. package/scripts/notify-hook.js +137 -7
  131. package/skills/team/SKILL.md +3 -1
@@ -0,0 +1,202 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { sendDiscord, sendDiscordBot, sendSlack, sendWebhook, dispatchNotifications, } from '../dispatcher.js';
4
+ const basePayload = {
5
+ event: 'session-idle',
6
+ sessionId: 'test-session-123',
7
+ message: 'Test notification message',
8
+ timestamp: new Date('2025-01-15T12:00:00Z').toISOString(),
9
+ projectPath: '/home/user/project',
10
+ projectName: 'project',
11
+ };
12
+ // ---------------------------------------------------------------------------
13
+ // sendDiscord
14
+ // ---------------------------------------------------------------------------
15
+ describe('sendDiscord', () => {
16
+ it('returns error when not enabled', async () => {
17
+ const config = { enabled: false, webhookUrl: '' };
18
+ const result = await sendDiscord(config, basePayload);
19
+ assert.equal(result.success, false);
20
+ assert.equal(result.platform, 'discord');
21
+ assert.ok(result.error?.includes('Not configured'));
22
+ });
23
+ it('returns error when webhookUrl is empty', async () => {
24
+ const config = { enabled: true, webhookUrl: '' };
25
+ const result = await sendDiscord(config, basePayload);
26
+ assert.equal(result.success, false);
27
+ });
28
+ it('rejects invalid webhook URL (non-discord host)', async () => {
29
+ const config = {
30
+ enabled: true,
31
+ webhookUrl: 'https://evil.com/webhook',
32
+ };
33
+ const result = await sendDiscord(config, basePayload);
34
+ assert.equal(result.success, false);
35
+ assert.equal(result.error, 'Invalid webhook URL');
36
+ });
37
+ it('rejects http:// webhook URL', async () => {
38
+ const config = {
39
+ enabled: true,
40
+ webhookUrl: 'http://discord.com/api/webhooks/123/abc',
41
+ };
42
+ const result = await sendDiscord(config, basePayload);
43
+ assert.equal(result.success, false);
44
+ assert.equal(result.error, 'Invalid webhook URL');
45
+ });
46
+ it('rejects malformed URL', async () => {
47
+ const config = {
48
+ enabled: true,
49
+ webhookUrl: 'not-a-url',
50
+ };
51
+ const result = await sendDiscord(config, basePayload);
52
+ assert.equal(result.success, false);
53
+ assert.equal(result.error, 'Invalid webhook URL');
54
+ });
55
+ });
56
+ // ---------------------------------------------------------------------------
57
+ // sendDiscordBot
58
+ // ---------------------------------------------------------------------------
59
+ describe('sendDiscordBot', () => {
60
+ it('returns error when not enabled', async () => {
61
+ const config = { enabled: false };
62
+ const result = await sendDiscordBot(config, basePayload);
63
+ assert.equal(result.success, false);
64
+ assert.equal(result.platform, 'discord-bot');
65
+ assert.ok(result.error?.includes('Not enabled'));
66
+ });
67
+ it('returns error when missing botToken', async () => {
68
+ const config = {
69
+ enabled: true,
70
+ channelId: '123456',
71
+ };
72
+ const result = await sendDiscordBot(config, basePayload);
73
+ assert.equal(result.success, false);
74
+ assert.ok(result.error?.includes('Missing botToken or channelId'));
75
+ });
76
+ it('returns error when missing channelId', async () => {
77
+ const config = {
78
+ enabled: true,
79
+ botToken: 'token',
80
+ };
81
+ const result = await sendDiscordBot(config, basePayload);
82
+ assert.equal(result.success, false);
83
+ assert.ok(result.error?.includes('Missing botToken or channelId'));
84
+ });
85
+ });
86
+ // ---------------------------------------------------------------------------
87
+ // sendSlack
88
+ // ---------------------------------------------------------------------------
89
+ describe('sendSlack', () => {
90
+ it('returns error when not enabled', async () => {
91
+ const config = { enabled: false, webhookUrl: '' };
92
+ const result = await sendSlack(config, basePayload);
93
+ assert.equal(result.success, false);
94
+ assert.equal(result.platform, 'slack');
95
+ });
96
+ it('rejects invalid slack webhook URL', async () => {
97
+ const config = {
98
+ enabled: true,
99
+ webhookUrl: 'https://evil.com/services/hook',
100
+ };
101
+ const result = await sendSlack(config, basePayload);
102
+ assert.equal(result.success, false);
103
+ assert.equal(result.error, 'Invalid webhook URL');
104
+ });
105
+ it('rejects http:// slack webhook URL', async () => {
106
+ const config = {
107
+ enabled: true,
108
+ webhookUrl: 'http://hooks.slack.com/services/test',
109
+ };
110
+ const result = await sendSlack(config, basePayload);
111
+ assert.equal(result.success, false);
112
+ assert.equal(result.error, 'Invalid webhook URL');
113
+ });
114
+ });
115
+ // ---------------------------------------------------------------------------
116
+ // sendWebhook
117
+ // ---------------------------------------------------------------------------
118
+ describe('sendWebhook', () => {
119
+ it('returns error when not enabled', async () => {
120
+ const config = { enabled: false, url: '' };
121
+ const result = await sendWebhook(config, basePayload);
122
+ assert.equal(result.success, false);
123
+ assert.equal(result.platform, 'webhook');
124
+ });
125
+ it('rejects http:// URL (requires HTTPS)', async () => {
126
+ const config = {
127
+ enabled: true,
128
+ url: 'http://example.com/hook',
129
+ };
130
+ const result = await sendWebhook(config, basePayload);
131
+ assert.equal(result.success, false);
132
+ assert.equal(result.error, 'Invalid URL (HTTPS required)');
133
+ });
134
+ it('rejects malformed URL', async () => {
135
+ const config = {
136
+ enabled: true,
137
+ url: 'not-a-url',
138
+ };
139
+ const result = await sendWebhook(config, basePayload);
140
+ assert.equal(result.success, false);
141
+ assert.equal(result.error, 'Invalid URL (HTTPS required)');
142
+ });
143
+ });
144
+ // ---------------------------------------------------------------------------
145
+ // dispatchNotifications
146
+ // ---------------------------------------------------------------------------
147
+ describe('dispatchNotifications', () => {
148
+ it('returns empty results when no platforms enabled', async () => {
149
+ const config = { enabled: true };
150
+ const result = await dispatchNotifications(config, 'session-idle', basePayload);
151
+ assert.equal(result.event, 'session-idle');
152
+ assert.equal(result.results.length, 0);
153
+ assert.equal(result.anySuccess, false);
154
+ });
155
+ it('returns empty when config disabled', async () => {
156
+ const config = {
157
+ enabled: true,
158
+ discord: { enabled: false, webhookUrl: '' },
159
+ };
160
+ const result = await dispatchNotifications(config, 'session-end', basePayload);
161
+ assert.equal(result.results.length, 0);
162
+ assert.equal(result.anySuccess, false);
163
+ });
164
+ it('dispatches to enabled platforms and collects results', async () => {
165
+ const config = {
166
+ enabled: true,
167
+ discord: { enabled: true, webhookUrl: 'not-valid' },
168
+ slack: { enabled: true, webhookUrl: 'not-valid' },
169
+ };
170
+ const result = await dispatchNotifications(config, 'session-end', basePayload);
171
+ assert.ok(result.results.length > 0);
172
+ // Both should fail (invalid URLs)
173
+ assert.equal(result.anySuccess, false);
174
+ });
175
+ it('uses event-level platform config when present', async () => {
176
+ const config = {
177
+ enabled: true,
178
+ events: {
179
+ 'session-end': {
180
+ enabled: true,
181
+ discord: { enabled: true, webhookUrl: 'invalid-url' },
182
+ },
183
+ },
184
+ };
185
+ const result = await dispatchNotifications(config, 'session-end', basePayload);
186
+ assert.ok(result.results.length > 0);
187
+ assert.equal(result.results[0].platform, 'discord');
188
+ });
189
+ it('falls back to top-level config when event has no platform override', async () => {
190
+ const config = {
191
+ enabled: true,
192
+ discord: { enabled: true, webhookUrl: 'invalid-url' },
193
+ events: {
194
+ 'session-start': { enabled: true },
195
+ },
196
+ };
197
+ const result = await dispatchNotifications(config, 'session-start', basePayload);
198
+ assert.ok(result.results.length > 0);
199
+ assert.equal(result.results[0].platform, 'discord');
200
+ });
201
+ });
202
+ //# sourceMappingURL=dispatcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.test.js","sourceRoot":"","sources":["../../../src/notifications/__tests__/dispatcher.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=formatter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.test.d.ts","sourceRoot":"","sources":["../../../src/notifications/__tests__/formatter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { formatSessionIdle, formatSessionStart, formatSessionEnd, formatSessionStop, formatAskUserQuestion, formatNotification, } from '../formatter.js';
4
+ const basePayload = {
5
+ event: 'session-idle',
6
+ sessionId: 'test-session-123',
7
+ message: '',
8
+ timestamp: new Date('2025-01-15T12:00:00Z').toISOString(),
9
+ projectPath: '/home/user/my-project',
10
+ projectName: 'my-project',
11
+ };
12
+ describe('formatSessionIdle', () => {
13
+ it('should include idle header and waiting message', () => {
14
+ const result = formatSessionIdle(basePayload);
15
+ assert.ok(result.includes('# Session Idle'));
16
+ assert.ok(result.includes('Codex has finished and is waiting for input.'));
17
+ });
18
+ it('should include project info in footer', () => {
19
+ const result = formatSessionIdle(basePayload);
20
+ assert.ok(result.includes('`my-project`'));
21
+ });
22
+ it('should include reason when provided', () => {
23
+ const result = formatSessionIdle({ ...basePayload, reason: 'task_complete' });
24
+ assert.ok(result.includes('**Reason:** task_complete'));
25
+ });
26
+ it('should include modes when provided', () => {
27
+ const result = formatSessionIdle({ ...basePayload, modesUsed: ['ultrawork', 'ralph'] });
28
+ assert.ok(result.includes('**Modes:** ultrawork, ralph'));
29
+ });
30
+ it('should include tmux session in footer when available', () => {
31
+ const result = formatSessionIdle({ ...basePayload, tmuxSession: 'dev-session' });
32
+ assert.ok(result.includes('`dev-session`'));
33
+ });
34
+ });
35
+ describe('formatSessionStart', () => {
36
+ it('should include start header and session info', () => {
37
+ const result = formatSessionStart({ ...basePayload, event: 'session-start' });
38
+ assert.ok(result.includes('# Session Started'));
39
+ assert.ok(result.includes('`test-session-123`'));
40
+ assert.ok(result.includes('`my-project`'));
41
+ });
42
+ it('should include tmux session when available', () => {
43
+ const result = formatSessionStart({ ...basePayload, event: 'session-start', tmuxSession: 'main' });
44
+ assert.ok(result.includes('`main`'));
45
+ });
46
+ });
47
+ describe('formatSessionEnd', () => {
48
+ it('should include end header and duration', () => {
49
+ const result = formatSessionEnd({ ...basePayload, event: 'session-end', durationMs: 125000 });
50
+ assert.ok(result.includes('# Session Ended'));
51
+ assert.ok(result.includes('2m 5s'));
52
+ });
53
+ it('should include agents count', () => {
54
+ const result = formatSessionEnd({ ...basePayload, event: 'session-end', agentsSpawned: 5, agentsCompleted: 3 });
55
+ assert.ok(result.includes('3/5 completed'));
56
+ });
57
+ it('should include modes and summary', () => {
58
+ const result = formatSessionEnd({
59
+ ...basePayload,
60
+ event: 'session-end',
61
+ modesUsed: ['ralph'],
62
+ contextSummary: 'Fixed auth bug',
63
+ });
64
+ assert.ok(result.includes('**Modes:** ralph'));
65
+ assert.ok(result.includes('**Summary:** Fixed auth bug'));
66
+ });
67
+ });
68
+ describe('formatSessionStop', () => {
69
+ it('should include continuing header and mode info', () => {
70
+ const result = formatSessionStop({
71
+ ...basePayload,
72
+ event: 'session-stop',
73
+ activeMode: 'ralph',
74
+ iteration: 3,
75
+ maxIterations: 10,
76
+ });
77
+ assert.ok(result.includes('# Session Continuing'));
78
+ assert.ok(result.includes('**Mode:** ralph'));
79
+ assert.ok(result.includes('3/10'));
80
+ });
81
+ });
82
+ describe('formatAskUserQuestion', () => {
83
+ it('should include question text', () => {
84
+ const result = formatAskUserQuestion({
85
+ ...basePayload,
86
+ event: 'ask-user-question',
87
+ question: 'Which approach should I use?',
88
+ });
89
+ assert.ok(result.includes('# Input Needed'));
90
+ assert.ok(result.includes('Which approach should I use?'));
91
+ assert.ok(result.includes('Codex is waiting for your response.'));
92
+ });
93
+ });
94
+ describe('formatNotification routing', () => {
95
+ it('should route each event type correctly', () => {
96
+ assert.ok(formatNotification({ ...basePayload, event: 'session-idle' }).includes('# Session Idle'));
97
+ assert.ok(formatNotification({ ...basePayload, event: 'session-start' }).includes('# Session Started'));
98
+ assert.ok(formatNotification({ ...basePayload, event: 'session-end' }).includes('# Session Ended'));
99
+ assert.ok(formatNotification({ ...basePayload, event: 'session-stop' }).includes('# Session Continuing'));
100
+ assert.ok(formatNotification({ ...basePayload, event: 'ask-user-question' }).includes('# Input Needed'));
101
+ });
102
+ });
103
+ //# sourceMappingURL=formatter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.test.js","sourceRoot":"","sources":["../../../src/notifications/__tests__/formatter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notifier.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.test.d.ts","sourceRoot":"","sources":["../../../src/notifications/__tests__/notifier.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdirSync, writeFileSync, rmSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { tmpdir } from 'os';
6
+ import { randomUUID } from 'crypto';
7
+ import { loadNotificationConfig, notify } from '../notifier.js';
8
+ describe('loadNotificationConfig', () => {
9
+ it('returns null when config file does not exist', async () => {
10
+ const fakePath = join(tmpdir(), `omx-test-${randomUUID()}`);
11
+ const config = await loadNotificationConfig(fakePath);
12
+ assert.equal(config, null);
13
+ });
14
+ it('returns parsed config when file exists', async () => {
15
+ const tmpDir = join(tmpdir(), `omx-test-${randomUUID()}`);
16
+ const omxDir = join(tmpDir, '.omx');
17
+ mkdirSync(omxDir, { recursive: true });
18
+ const configData = {
19
+ desktop: true,
20
+ discord: { webhookUrl: 'https://discord.com/api/webhooks/test' },
21
+ telegram: { botToken: '123:abc', chatId: '456' },
22
+ };
23
+ writeFileSync(join(omxDir, 'notifications.json'), JSON.stringify(configData));
24
+ try {
25
+ const config = await loadNotificationConfig(tmpDir);
26
+ assert.ok(config);
27
+ assert.equal(config.desktop, true);
28
+ assert.equal(config.discord?.webhookUrl, 'https://discord.com/api/webhooks/test');
29
+ assert.equal(config.telegram?.botToken, '123:abc');
30
+ assert.equal(config.telegram?.chatId, '456');
31
+ }
32
+ finally {
33
+ rmSync(tmpDir, { recursive: true, force: true });
34
+ }
35
+ });
36
+ it('returns null for invalid JSON', async () => {
37
+ const tmpDir = join(tmpdir(), `omx-test-${randomUUID()}`);
38
+ const omxDir = join(tmpDir, '.omx');
39
+ mkdirSync(omxDir, { recursive: true });
40
+ writeFileSync(join(omxDir, 'notifications.json'), 'not-json');
41
+ try {
42
+ const config = await loadNotificationConfig(tmpDir);
43
+ assert.equal(config, null);
44
+ }
45
+ finally {
46
+ rmSync(tmpDir, { recursive: true, force: true });
47
+ }
48
+ });
49
+ });
50
+ describe('notify', () => {
51
+ const basePayload = {
52
+ title: 'Test',
53
+ message: 'Test message',
54
+ type: 'info',
55
+ mode: 'test',
56
+ };
57
+ it('does nothing when config is null', async () => {
58
+ // Should not throw
59
+ await notify(basePayload, null);
60
+ });
61
+ it('does nothing when all channels are disabled', async () => {
62
+ const config = {};
63
+ // Should not throw
64
+ await notify(basePayload, config);
65
+ });
66
+ it('accepts all notification types', async () => {
67
+ const types = ['info', 'success', 'warning', 'error'];
68
+ for (const type of types) {
69
+ // Should not throw
70
+ await notify({ ...basePayload, type }, {});
71
+ }
72
+ });
73
+ });
74
+ describe('NotificationPayload type', () => {
75
+ it('requires title, message, and type', () => {
76
+ const payload = {
77
+ title: 'Test Title',
78
+ message: 'Test message body',
79
+ type: 'success',
80
+ };
81
+ assert.equal(payload.title, 'Test Title');
82
+ assert.equal(payload.message, 'Test message body');
83
+ assert.equal(payload.type, 'success');
84
+ });
85
+ it('supports optional mode', () => {
86
+ const payload = {
87
+ title: 'Test',
88
+ message: 'msg',
89
+ type: 'info',
90
+ mode: 'ralph',
91
+ };
92
+ assert.equal(payload.mode, 'ralph');
93
+ });
94
+ it('supports optional projectPath', () => {
95
+ const payload = {
96
+ title: 'Test',
97
+ message: 'msg',
98
+ type: 'warning',
99
+ projectPath: '/home/user/project',
100
+ };
101
+ assert.equal(payload.projectPath, '/home/user/project');
102
+ });
103
+ });
104
+ //# sourceMappingURL=notifier.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.test.js","sourceRoot":"","sources":["../../../src/notifications/__tests__/notifier.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profiles.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.test.d.ts","sourceRoot":"","sources":["../../../src/notifications/__tests__/profiles.test.ts"],"names":[],"mappings":""}