@mdrv/opencode-quota 262.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -0
  3. package/bin/copilot-quota.ts +374 -0
  4. package/bin/glm-quota.ts +467 -0
  5. package/bin/install.js +439 -0
  6. package/bin/kimi-quota.ts +314 -0
  7. package/dist/bin/copilot-quota.d.ts +8 -0
  8. package/dist/bin/copilot-quota.d.ts.map +1 -0
  9. package/dist/bin/copilot-quota.js +298 -0
  10. package/dist/bin/copilot-quota.js.map +1 -0
  11. package/dist/bin/glm-quota.d.ts +8 -0
  12. package/dist/bin/glm-quota.d.ts.map +1 -0
  13. package/dist/bin/glm-quota.js +367 -0
  14. package/dist/bin/glm-quota.js.map +1 -0
  15. package/dist/bin/kimi-quota.d.ts +3 -0
  16. package/dist/bin/kimi-quota.d.ts.map +1 -0
  17. package/dist/bin/kimi-quota.js +241 -0
  18. package/dist/bin/kimi-quota.js.map +1 -0
  19. package/dist/src/api/client.d.ts +76 -0
  20. package/dist/src/api/client.d.ts.map +1 -0
  21. package/dist/src/api/client.js +203 -0
  22. package/dist/src/api/client.js.map +1 -0
  23. package/dist/src/api/endpoints.d.ts +22 -0
  24. package/dist/src/api/endpoints.d.ts.map +1 -0
  25. package/dist/src/api/endpoints.js +41 -0
  26. package/dist/src/api/endpoints.js.map +1 -0
  27. package/dist/src/api/platforms.d.ts +20 -0
  28. package/dist/src/api/platforms.d.ts.map +1 -0
  29. package/dist/src/api/platforms.js +38 -0
  30. package/dist/src/api/platforms.js.map +1 -0
  31. package/dist/src/index.d.ts +10 -0
  32. package/dist/src/index.d.ts.map +1 -0
  33. package/dist/src/index.js +723 -0
  34. package/dist/src/index.js.map +1 -0
  35. package/dist/src/shared/logging.d.ts +7 -0
  36. package/dist/src/shared/logging.d.ts.map +1 -0
  37. package/dist/src/shared/logging.js +29 -0
  38. package/dist/src/shared/logging.js.map +1 -0
  39. package/dist/src/utils/box-constants.d.ts +43 -0
  40. package/dist/src/utils/box-constants.d.ts.map +1 -0
  41. package/dist/src/utils/box-constants.js +43 -0
  42. package/dist/src/utils/box-constants.js.map +1 -0
  43. package/dist/src/utils/date-formatter.d.ts +17 -0
  44. package/dist/src/utils/date-formatter.d.ts.map +1 -0
  45. package/dist/src/utils/date-formatter.js +33 -0
  46. package/dist/src/utils/date-formatter.js.map +1 -0
  47. package/dist/src/utils/error-formatter.d.ts +17 -0
  48. package/dist/src/utils/error-formatter.d.ts.map +1 -0
  49. package/dist/src/utils/error-formatter.js +60 -0
  50. package/dist/src/utils/error-formatter.js.map +1 -0
  51. package/dist/src/utils/progress-bar.d.ts +35 -0
  52. package/dist/src/utils/progress-bar.d.ts.map +1 -0
  53. package/dist/src/utils/progress-bar.js +43 -0
  54. package/dist/src/utils/progress-bar.js.map +1 -0
  55. package/dist/src/utils/reset-timer.d.ts +11 -0
  56. package/dist/src/utils/reset-timer.d.ts.map +1 -0
  57. package/dist/src/utils/reset-timer.js +32 -0
  58. package/dist/src/utils/reset-timer.js.map +1 -0
  59. package/dist/src/utils/time-window.d.ts +30 -0
  60. package/dist/src/utils/time-window.d.ts.map +1 -0
  61. package/dist/src/utils/time-window.js +34 -0
  62. package/dist/src/utils/time-window.js.map +1 -0
  63. package/dist/tests/error-handling/api-errors.test.d.ts +7 -0
  64. package/dist/tests/error-handling/api-errors.test.d.ts.map +1 -0
  65. package/dist/tests/error-handling/api-errors.test.js +110 -0
  66. package/dist/tests/error-handling/api-errors.test.js.map +1 -0
  67. package/dist/tests/error-handling/auth-errors.test.d.ts +7 -0
  68. package/dist/tests/error-handling/auth-errors.test.d.ts.map +1 -0
  69. package/dist/tests/error-handling/auth-errors.test.js +110 -0
  70. package/dist/tests/error-handling/auth-errors.test.js.map +1 -0
  71. package/dist/tests/error-handling/network-errors.test.d.ts +7 -0
  72. package/dist/tests/error-handling/network-errors.test.d.ts.map +1 -0
  73. package/dist/tests/error-handling/network-errors.test.js +94 -0
  74. package/dist/tests/error-handling/network-errors.test.js.map +1 -0
  75. package/dist/tests/error-handling/parse-errors.test.d.ts +7 -0
  76. package/dist/tests/error-handling/parse-errors.test.d.ts.map +1 -0
  77. package/dist/tests/error-handling/parse-errors.test.js +87 -0
  78. package/dist/tests/error-handling/parse-errors.test.js.map +1 -0
  79. package/dist/tests/error-handling/token-sanitization.test.d.ts +2 -0
  80. package/dist/tests/error-handling/token-sanitization.test.d.ts.map +1 -0
  81. package/dist/tests/error-handling/token-sanitization.test.js +59 -0
  82. package/dist/tests/error-handling/token-sanitization.test.js.map +1 -0
  83. package/dist/tests/functional/date-formatter.test.d.ts +5 -0
  84. package/dist/tests/functional/date-formatter.test.d.ts.map +1 -0
  85. package/dist/tests/functional/date-formatter.test.js +46 -0
  86. package/dist/tests/functional/date-formatter.test.js.map +1 -0
  87. package/dist/tests/functional/progress-bar.test.d.ts +5 -0
  88. package/dist/tests/functional/progress-bar.test.d.ts.map +1 -0
  89. package/dist/tests/functional/progress-bar.test.js +82 -0
  90. package/dist/tests/functional/progress-bar.test.js.map +1 -0
  91. package/dist/tests/functional/reset-timer.test.d.ts +6 -0
  92. package/dist/tests/functional/reset-timer.test.d.ts.map +1 -0
  93. package/dist/tests/functional/reset-timer.test.js +67 -0
  94. package/dist/tests/functional/reset-timer.test.js.map +1 -0
  95. package/dist/tests/functional/time-window.test.d.ts +5 -0
  96. package/dist/tests/functional/time-window.test.d.ts.map +1 -0
  97. package/dist/tests/functional/time-window.test.js +46 -0
  98. package/dist/tests/functional/time-window.test.js.map +1 -0
  99. package/dist/tests/integration/box-alignment.test.d.ts +8 -0
  100. package/dist/tests/integration/box-alignment.test.d.ts.map +1 -0
  101. package/dist/tests/integration/box-alignment.test.js +238 -0
  102. package/dist/tests/integration/box-alignment.test.js.map +1 -0
  103. package/dist/tests/integration/error-handling.test.d.ts +2 -0
  104. package/dist/tests/integration/error-handling.test.d.ts.map +1 -0
  105. package/dist/tests/integration/error-handling.test.js +36 -0
  106. package/dist/tests/integration/error-handling.test.js.map +1 -0
  107. package/dist/tests/integration/installer-config.test.d.ts +2 -0
  108. package/dist/tests/integration/installer-config.test.d.ts.map +1 -0
  109. package/dist/tests/integration/installer-config.test.js +65 -0
  110. package/dist/tests/integration/installer-config.test.js.map +1 -0
  111. package/dist/tests/integration/plugin-catch-block.test.d.ts +2 -0
  112. package/dist/tests/integration/plugin-catch-block.test.d.ts.map +1 -0
  113. package/dist/tests/integration/plugin-catch-block.test.js +134 -0
  114. package/dist/tests/integration/plugin-catch-block.test.js.map +1 -0
  115. package/dist/tests/integration/reset-time-display.test.d.ts +6 -0
  116. package/dist/tests/integration/reset-time-display.test.d.ts.map +1 -0
  117. package/dist/tests/integration/reset-time-display.test.js +138 -0
  118. package/dist/tests/integration/reset-time-display.test.js.map +1 -0
  119. package/dist/tests/module/http-client.test.d.ts +2 -0
  120. package/dist/tests/module/http-client.test.d.ts.map +1 -0
  121. package/dist/tests/module/http-client.test.js +49 -0
  122. package/dist/tests/module/http-client.test.js.map +1 -0
  123. package/dist/tests/module/platform-detection.test.d.ts +5 -0
  124. package/dist/tests/module/platform-detection.test.d.ts.map +1 -0
  125. package/dist/tests/module/platform-detection.test.js +48 -0
  126. package/dist/tests/module/platform-detection.test.js.map +1 -0
  127. package/integration/agents/copilot-quota-exec.md +20 -0
  128. package/integration/agents/glm-quota-exec.md +20 -0
  129. package/integration/command/copilot_quota.md +6 -0
  130. package/integration/command/glm_quota.md +6 -0
  131. package/integration/skills/copilot-quota/SKILL.md +11 -0
  132. package/integration/skills/glm-quota/SKILL.md +11 -0
  133. package/package.json +69 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * API Error Handling Tests
3
+ * Tests for HTTP 429 rate limiting and 500+ server errors
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ import * as assert from 'node:assert';
7
+ import { describe, it } from 'node:test';
8
+ import { formatApiError } from '../../src/api/client.js';
9
+ import { BOX_WIDTH } from '../../src/utils/box-constants.js';
10
+ describe('API Error Handling', () => {
11
+ describe('formatApiError - 429 Rate Limit', () => {
12
+ it('should format 429 error with boxed message', () => {
13
+ // Arrange
14
+ const statusCode = 429;
15
+ const responseBody = 'Too Many Requests';
16
+ const authToken = 'test-token';
17
+ // Act
18
+ const formatted = formatApiError(statusCode, responseBody, authToken);
19
+ // Assert
20
+ assert.ok(formatted.message.includes('Too many requests'));
21
+ assert.ok(formatted.message.includes('╔')); // Has box drawing chars
22
+ assert.ok(formatted.message.includes('║'));
23
+ assert.ok(formatted.message.includes('╚'));
24
+ });
25
+ it('should include helpful message for 429 errors', () => {
26
+ // Arrange
27
+ const statusCode = 429;
28
+ const responseBody = 'Rate limit exceeded';
29
+ const authToken = 'test-token';
30
+ // Act
31
+ const formatted = formatApiError(statusCode, responseBody, authToken);
32
+ // Assert
33
+ assert.ok(formatted.message.includes('Too many requests'));
34
+ assert.ok(formatted.message.includes('Please try again later'));
35
+ });
36
+ it('should sanitize token from 429 error messages', () => {
37
+ // Arrange
38
+ const authToken = 'sk-secret-789';
39
+ const statusCode = 429;
40
+ const responseBody = `Rate limit exceeded for token ${authToken}`;
41
+ // Act
42
+ const formatted = formatApiError(statusCode, responseBody, authToken);
43
+ // Assert
44
+ assert.ok(!formatted.message.includes(authToken));
45
+ assert.ok(formatted.message.includes('***'));
46
+ });
47
+ it('should create 60-character wide boxed error for 429', () => {
48
+ // Arrange
49
+ const statusCode = 429;
50
+ const responseBody = 'Rate limit exceeded';
51
+ const authToken = 'test-token';
52
+ // Act
53
+ const formatted = formatApiError(statusCode, responseBody, authToken);
54
+ // Assert
55
+ const lines = formatted.message.split('\n');
56
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL); // Top border
57
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL); // Bottom border
58
+ });
59
+ });
60
+ describe('formatApiError - 500+ Server Errors', () => {
61
+ it('should format 500 error with boxed message', () => {
62
+ // Arrange
63
+ const statusCode = 500;
64
+ const responseBody = 'Internal Server Error';
65
+ const authToken = 'test-token';
66
+ // Act
67
+ const formatted = formatApiError(statusCode, responseBody, authToken);
68
+ // Assert
69
+ assert.ok(formatted.message.includes('Server error'));
70
+ assert.ok(formatted.message.includes('╔')); // Has box drawing chars
71
+ assert.ok(formatted.message.includes('║'));
72
+ assert.ok(formatted.message.includes('╚'));
73
+ });
74
+ it('should include helpful message for 500+ errors', () => {
75
+ // Arrange
76
+ const statusCode = 503;
77
+ const responseBody = 'Service Unavailable';
78
+ const authToken = 'test-token';
79
+ // Act
80
+ const formatted = formatApiError(statusCode, responseBody, authToken);
81
+ // Assert
82
+ assert.ok(formatted.message.includes('Server error'));
83
+ assert.ok(formatted.message.includes('Please try again later'));
84
+ });
85
+ it('should sanitize token from 500+ error messages', () => {
86
+ // Arrange
87
+ const authToken = 'sk-secret-999';
88
+ const statusCode = 502;
89
+ const responseBody = `Bad Gateway: authentication with token ${authToken} failed`;
90
+ // Act
91
+ const formatted = formatApiError(statusCode, responseBody, authToken);
92
+ // Assert
93
+ assert.ok(!formatted.message.includes(authToken));
94
+ assert.ok(formatted.message.includes('***'));
95
+ });
96
+ it('should create 60-character wide boxed error for 500+', () => {
97
+ // Arrange
98
+ const statusCode = 500;
99
+ const responseBody = 'Internal Server Error';
100
+ const authToken = 'test-token';
101
+ // Act
102
+ const formatted = formatApiError(statusCode, responseBody, authToken);
103
+ // Assert
104
+ const lines = formatted.message.split('\n');
105
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL); // Top border
106
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL); // Bottom border
107
+ });
108
+ });
109
+ });
110
+ //# sourceMappingURL=api-errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-errors.test.js","sourceRoot":"","sources":["../../../tests/error-handling/api-errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Authentication Error Handling Tests
3
+ * Tests for HTTP 401/403 authentication errors
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=auth-errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-errors.test.d.ts","sourceRoot":"","sources":["../../../tests/error-handling/auth-errors.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Authentication Error Handling Tests
3
+ * Tests for HTTP 401/403 authentication errors
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ import * as assert from 'node:assert';
7
+ import { describe, it } from 'node:test';
8
+ import { formatAuthError } from '../../src/api/client.js';
9
+ import { BOX_WIDTH } from '../../src/utils/box-constants.js';
10
+ describe('Authentication Error Handling', () => {
11
+ describe('formatAuthError - 401 Unauthorized', () => {
12
+ it('should format 401 error with boxed message', () => {
13
+ // Arrange
14
+ const statusCode = 401;
15
+ const responseBody = 'Unauthorized';
16
+ const authToken = 'test-token';
17
+ // Act
18
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
19
+ // Assert
20
+ assert.ok(formatted.message.includes('Authentication failed'));
21
+ assert.ok(formatted.message.includes('╔')); // Has box drawing chars
22
+ assert.ok(formatted.message.includes('║'));
23
+ assert.ok(formatted.message.includes('╚'));
24
+ });
25
+ it('should include helpful message for 401 errors', () => {
26
+ // Arrange
27
+ const statusCode = 401;
28
+ const responseBody = 'Unauthorized';
29
+ const authToken = 'test-token';
30
+ // Act
31
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
32
+ // Assert
33
+ assert.ok(formatted.message.includes('Authentication failed'));
34
+ assert.ok(formatted.message.includes('Please check your credentials'));
35
+ });
36
+ it('should sanitize token from 401 error messages', () => {
37
+ // Arrange
38
+ const authToken = 'sk-secret-123';
39
+ const statusCode = 401;
40
+ const responseBody = `Unauthorized: Invalid token ${authToken}`;
41
+ // Act
42
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
43
+ // Assert
44
+ assert.ok(!formatted.message.includes(authToken));
45
+ assert.ok(formatted.message.includes('***'));
46
+ });
47
+ it('should create 60-character wide boxed error for 401', () => {
48
+ // Arrange
49
+ const statusCode = 401;
50
+ const responseBody = 'Unauthorized';
51
+ const authToken = 'test-token';
52
+ // Act
53
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
54
+ // Assert
55
+ const lines = formatted.message.split('\n');
56
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL); // Top border
57
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL); // Bottom border
58
+ });
59
+ });
60
+ describe('formatAuthError - 403 Forbidden', () => {
61
+ it('should format 403 error with boxed message', () => {
62
+ // Arrange
63
+ const statusCode = 403;
64
+ const responseBody = 'Forbidden';
65
+ const authToken = 'test-token';
66
+ // Act
67
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
68
+ // Assert
69
+ assert.ok(formatted.message.includes('Access denied'));
70
+ assert.ok(formatted.message.includes('╔')); // Has box drawing chars
71
+ assert.ok(formatted.message.includes('║'));
72
+ assert.ok(formatted.message.includes('╚'));
73
+ });
74
+ it('should include helpful message for 403 errors', () => {
75
+ // Arrange
76
+ const statusCode = 403;
77
+ const responseBody = 'Forbidden';
78
+ const authToken = 'test-token';
79
+ // Act
80
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
81
+ // Assert
82
+ assert.ok(formatted.message.includes('Access denied'));
83
+ assert.ok(formatted.message.includes("You don't have permission"));
84
+ });
85
+ it('should sanitize token from 403 error messages', () => {
86
+ // Arrange
87
+ const authToken = 'sk-secret-456';
88
+ const statusCode = 403;
89
+ const responseBody = `Forbidden: Token ${authToken} lacks required permissions`;
90
+ // Act
91
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
92
+ // Assert
93
+ assert.ok(!formatted.message.includes(authToken));
94
+ assert.ok(formatted.message.includes('***'));
95
+ });
96
+ it('should create 60-character wide boxed error for 403', () => {
97
+ // Arrange
98
+ const statusCode = 403;
99
+ const responseBody = 'Forbidden';
100
+ const authToken = 'test-token';
101
+ // Act
102
+ const formatted = formatAuthError(statusCode, responseBody, authToken);
103
+ // Assert
104
+ const lines = formatted.message.split('\n');
105
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL); // Top border
106
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL); // Bottom border
107
+ });
108
+ });
109
+ });
110
+ //# sourceMappingURL=auth-errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-errors.test.js","sourceRoot":"","sources":["../../../tests/error-handling/auth-errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Network Error Handling Tests
3
+ * Tests for network-related errors (timeout, connection refused, etc.)
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=network-errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-errors.test.d.ts","sourceRoot":"","sources":["../../../tests/error-handling/network-errors.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Network Error Handling Tests
3
+ * Tests for network-related errors (timeout, connection refused, etc.)
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ import * as assert from 'node:assert';
7
+ import { describe, it } from 'node:test';
8
+ import { createBoxedError, formatNetworkError } from '../../src/api/client.js';
9
+ import { BOX_WIDTH } from '../../src/utils/box-constants.js';
10
+ describe('Network Error Handling', () => {
11
+ describe('formatNetworkError', () => {
12
+ it('should format ETIMEDOUT error with boxed message', () => {
13
+ // Arrange
14
+ const mockError = Object.assign(new Error('Request timeout'), {
15
+ code: 'ETIMEDOUT',
16
+ });
17
+ const authToken = 'test-token';
18
+ // Act
19
+ const formatted = formatNetworkError(mockError, authToken);
20
+ // Assert
21
+ assert.ok(formatted.message.includes('Request timed out'));
22
+ assert.strictEqual(formatted.code, 'ETIMEDOUT');
23
+ });
24
+ it('should format ECONNREFUSED error with boxed message', () => {
25
+ // Arrange
26
+ const mockError = Object.assign(new Error('Connection refused'), {
27
+ code: 'ECONNREFUSED',
28
+ });
29
+ const authToken = 'test-token';
30
+ // Act
31
+ const formatted = formatNetworkError(mockError, authToken);
32
+ // Assert
33
+ assert.ok(formatted.message.includes('Unable to connect to server'));
34
+ assert.strictEqual(formatted.code, 'ECONNREFUSED');
35
+ });
36
+ it('should sanitize token for other error types', () => {
37
+ // Arrange
38
+ const authToken = 'sk-secret-123';
39
+ const mockError = Object.assign(new Error(`Error with token ${authToken}`), {
40
+ code: 'ENOTFOUND',
41
+ });
42
+ // Act
43
+ const formatted = formatNetworkError(mockError, authToken);
44
+ // Assert
45
+ assert.ok(!formatted.message.includes(authToken));
46
+ assert.ok(formatted.message.includes('***'));
47
+ });
48
+ it('should preserve error code in formatted errors', () => {
49
+ // Arrange
50
+ const mockError = Object.assign(new Error('Some error'), {
51
+ code: 'ETIMEDOUT',
52
+ });
53
+ // Act
54
+ const formatted = formatNetworkError(mockError, 'token');
55
+ // Assert
56
+ assert.strictEqual(formatted.code, 'ETIMEDOUT');
57
+ });
58
+ });
59
+ describe('createBoxedError', () => {
60
+ it('should create boxed error with 60-character width', () => {
61
+ // Arrange
62
+ const message = 'Request timed out. Please try again.';
63
+ // Act
64
+ const boxed = createBoxedError(message);
65
+ // Assert
66
+ const lines = boxed.split('\n');
67
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL); // Top border
68
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL); // Bottom border
69
+ });
70
+ it('should include box drawing characters', () => {
71
+ // Arrange
72
+ const message = 'Test message';
73
+ // Act
74
+ const boxed = createBoxedError(message);
75
+ // Assert
76
+ assert.ok(boxed.includes('╔'));
77
+ assert.ok(boxed.includes('║'));
78
+ assert.ok(boxed.includes('╚'));
79
+ });
80
+ it('should wrap long messages across multiple lines', () => {
81
+ // Arrange
82
+ const message = 'This is a very long error message that should wrap across multiple lines in the boxed format';
83
+ // Act
84
+ const boxed = createBoxedError(message);
85
+ // Assert
86
+ const lines = boxed.split('\n');
87
+ assert.ok(lines.length > 3); // Top + content lines + bottom
88
+ lines.forEach(line => {
89
+ assert.strictEqual(line.length, BOX_WIDTH.TOTAL);
90
+ });
91
+ });
92
+ });
93
+ });
94
+ //# sourceMappingURL=network-errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-errors.test.js","sourceRoot":"","sources":["../../../tests/error-handling/network-errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * JSON Parse Error Handling Tests
3
+ * Tests for invalid JSON responses and malformed content
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=parse-errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-errors.test.d.ts","sourceRoot":"","sources":["../../../tests/error-handling/parse-errors.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * JSON Parse Error Handling Tests
3
+ * Tests for invalid JSON responses and malformed content
4
+ * Following TDD methodology with AAA pattern
5
+ */
6
+ import * as assert from 'node:assert';
7
+ import { describe, it } from 'node:test';
8
+ import { BOX_WIDTH } from '../../src/utils/box-constants.js';
9
+ // Import the parse error formatting function (will be created in client.ts)
10
+ import { formatParseError } from '../../src/api/client.js';
11
+ describe('JSON Parse Error Handling', () => {
12
+ describe('formatParseError', () => {
13
+ it('should format parse error with boxed message', () => {
14
+ // Arrange
15
+ const responseBody = 'This is not JSON';
16
+ const authToken = 'test-token';
17
+ // Act
18
+ const formatted = formatParseError(responseBody, authToken);
19
+ // Assert
20
+ assert.ok(formatted.message.includes('Invalid JSON'), 'Should mention invalid JSON');
21
+ assert.ok(formatted.message.includes('╔'), 'Should have top border');
22
+ assert.ok(formatted.message.includes('║'), 'Should have side borders');
23
+ assert.ok(formatted.message.includes('╚'), 'Should have bottom border');
24
+ });
25
+ it('should create 60-character wide boxed error', () => {
26
+ // Arrange
27
+ const responseBody = '<html>Not JSON</html>';
28
+ const authToken = 'test-token';
29
+ // Act
30
+ const formatted = formatParseError(responseBody, authToken);
31
+ // Assert
32
+ const lines = formatted.message.split('\n');
33
+ assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL, 'Top border should be 60 chars');
34
+ assert.strictEqual(lines[lines.length - 1].length, BOX_WIDTH.TOTAL, 'Bottom border should be 60 chars');
35
+ // All lines should be exactly 60 characters
36
+ for (const line of lines) {
37
+ assert.strictEqual(line.length, BOX_WIDTH.TOTAL, 'All lines should be 60 chars');
38
+ }
39
+ });
40
+ it('should sanitize token from parse error response body', () => {
41
+ // Arrange
42
+ const authToken = 'sk-secret-parse-123';
43
+ const responseBody = `Invalid JSON with token ${authToken} exposed`;
44
+ // Act
45
+ const formatted = formatParseError(responseBody, authToken);
46
+ // Assert
47
+ assert.ok(!formatted.message.includes(authToken), 'Should not contain raw token');
48
+ assert.ok(formatted.message.includes('***'), 'Should contain sanitization marker');
49
+ });
50
+ it('should truncate very long invalid response bodies', () => {
51
+ // Arrange
52
+ const longResponse = 'A'.repeat(500) + ' not JSON';
53
+ const authToken = 'test-token';
54
+ // Act
55
+ const formatted = formatParseError(longResponse, authToken);
56
+ // Assert
57
+ const lines = formatted.message.split('\n');
58
+ // All lines should be exactly 60 characters
59
+ for (const line of lines) {
60
+ assert.strictEqual(line.length, BOX_WIDTH.TOTAL, 'All lines should be 60 chars');
61
+ }
62
+ // Should mention invalid JSON
63
+ assert.ok(formatted.message.includes('Invalid JSON'), 'Should mention invalid JSON');
64
+ });
65
+ it('should handle empty response body', () => {
66
+ // Arrange
67
+ const responseBody = '';
68
+ const authToken = 'test-token';
69
+ // Act
70
+ const formatted = formatParseError(responseBody, authToken);
71
+ // Assert
72
+ assert.ok(formatted.message.includes('Invalid JSON'), 'Should mention invalid JSON');
73
+ assert.ok(formatted.message.includes('╔'), 'Should be boxed');
74
+ });
75
+ it('should handle malformed JSON with syntax errors', () => {
76
+ // Arrange
77
+ const responseBody = '{"incomplete": "json"';
78
+ const authToken = 'test-token';
79
+ // Act
80
+ const formatted = formatParseError(responseBody, authToken);
81
+ // Assert
82
+ assert.ok(formatted.message.includes('Invalid JSON'), 'Should mention invalid JSON');
83
+ assert.ok(formatted.message.includes('╔'), 'Should be boxed');
84
+ });
85
+ });
86
+ });
87
+ //# sourceMappingURL=parse-errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-errors.test.js","sourceRoot":"","sources":["../../../tests/error-handling/parse-errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token-sanitization.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-sanitization.test.d.ts","sourceRoot":"","sources":["../../../tests/error-handling/token-sanitization.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import * as assert from 'node:assert';
2
+ import { describe, test } from 'node:test';
3
+ import { sanitizeToken } from '../../src/utils/error-formatter.js';
4
+ describe('sanitizeToken', () => {
5
+ test('replaces token with *** when token appears in message', () => {
6
+ // Arrange
7
+ const message = 'Authentication failed with token abc123def456';
8
+ const token = 'abc123def456';
9
+ // Act
10
+ const result = sanitizeToken(message, token);
11
+ // Assert
12
+ assert.strictEqual(result, 'Authentication failed with token ***');
13
+ });
14
+ test('returns original message when token is undefined', () => {
15
+ // Arrange
16
+ const message = 'Authentication failed';
17
+ // Act
18
+ const result = sanitizeToken(message, undefined);
19
+ // Assert
20
+ assert.strictEqual(result, 'Authentication failed');
21
+ });
22
+ test('returns original message when token is not in message', () => {
23
+ // Arrange
24
+ const message = 'Authentication failed';
25
+ const token = 'abc123def456';
26
+ // Act
27
+ const result = sanitizeToken(message, token);
28
+ // Assert
29
+ assert.strictEqual(result, 'Authentication failed');
30
+ });
31
+ test('replaces all occurrences of token in message', () => {
32
+ // Arrange
33
+ const message = 'Token abc123 used in header. Token abc123 expired.';
34
+ const token = 'abc123';
35
+ // Act
36
+ const result = sanitizeToken(message, token);
37
+ // Assert
38
+ assert.strictEqual(result, 'Token *** used in header. Token *** expired.');
39
+ });
40
+ test('handles empty token gracefully', () => {
41
+ // Arrange
42
+ const message = 'Authentication failed';
43
+ const token = '';
44
+ // Act
45
+ const result = sanitizeToken(message, token);
46
+ // Assert
47
+ assert.strictEqual(result, 'Authentication failed');
48
+ });
49
+ test('is case-sensitive when matching tokens', () => {
50
+ // Arrange
51
+ const message = 'Token ABC123 is valid';
52
+ const token = 'abc123';
53
+ // Act
54
+ const result = sanitizeToken(message, token);
55
+ // Assert
56
+ assert.strictEqual(result, 'Token ABC123 is valid');
57
+ });
58
+ });
59
+ //# sourceMappingURL=token-sanitization.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-sanitization.test.js","sourceRoot":"","sources":["../../../tests/error-handling/token-sanitization.test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAElE,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC9B,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,UAAU;QACV,MAAM,OAAO,GAAG,+CAA+C,CAAA;QAC/D,MAAM,KAAK,GAAG,cAAc,CAAA;QAE5B,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE5C,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC7D,UAAU;QACV,MAAM,OAAO,GAAG,uBAAuB,CAAA;QAEvC,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAEhD,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAClE,UAAU;QACV,MAAM,OAAO,GAAG,uBAAuB,CAAA;QACvC,MAAM,KAAK,GAAG,cAAc,CAAA;QAE5B,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE5C,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACzD,UAAU;QACV,MAAM,OAAO,GAAG,oDAAoD,CAAA;QACpE,MAAM,KAAK,GAAG,QAAQ,CAAA;QAEtB,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE5C,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,8CAA8C,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC3C,UAAU;QACV,MAAM,OAAO,GAAG,uBAAuB,CAAA;QACvC,MAAM,KAAK,GAAG,EAAE,CAAA;QAEhB,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE5C,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACnD,UAAU;QACV,MAAM,OAAO,GAAG,uBAAuB,CAAA;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAA;QAEtB,MAAM;QACN,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE5C,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Functional tests for date formatter module
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=date-formatter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-formatter.test.d.ts","sourceRoot":"","sources":["../../../tests/functional/date-formatter.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Functional tests for date formatter module
3
+ */
4
+ import assert from 'node:assert';
5
+ import { describe, test } from 'node:test';
6
+ import { formatDateTime, parseDateTime } from '../../src/utils/date-formatter.js';
7
+ describe('formatDateTime', () => {
8
+ test('formats date as yyyy-MM-dd HH:mm:ss', () => {
9
+ const date = new Date(2026, 0, 13, 14, 30, 45);
10
+ const result = formatDateTime(date);
11
+ assert.strictEqual(result, '2026-01-13 14:30:45');
12
+ });
13
+ test('pads single digit month with leading zero', () => {
14
+ const date = new Date(2026, 0, 5, 9, 5, 5);
15
+ const result = formatDateTime(date);
16
+ assert.strictEqual(result, '2026-01-05 09:05:05');
17
+ });
18
+ test('handles end of month', () => {
19
+ const date = new Date(2026, 11, 31, 23, 59, 59);
20
+ const result = formatDateTime(date);
21
+ assert.strictEqual(result, '2026-12-31 23:59:59');
22
+ });
23
+ test('handles start of month', () => {
24
+ const date = new Date(2026, 0, 1, 0, 0, 0);
25
+ const result = formatDateTime(date);
26
+ assert.strictEqual(result, '2026-01-01 00:00:00');
27
+ });
28
+ });
29
+ describe('parseDateTime', () => {
30
+ test('parses valid datetime string', () => {
31
+ const result = parseDateTime('2026-01-13 14:30:45');
32
+ assert.strictEqual(result.getFullYear(), 2026);
33
+ assert.strictEqual(result.getMonth(), 0); // January (0-indexed)
34
+ assert.strictEqual(result.getDate(), 13);
35
+ assert.strictEqual(result.getHours(), 14);
36
+ assert.strictEqual(result.getMinutes(), 30);
37
+ assert.strictEqual(result.getSeconds(), 45);
38
+ });
39
+ test('throws error for invalid format', () => {
40
+ assert.throws(() => parseDateTime('invalid'), /Invalid datetime format/);
41
+ });
42
+ test('throws error for empty string', () => {
43
+ assert.throws(() => parseDateTime(''), /Invalid datetime format/);
44
+ });
45
+ });
46
+ //# sourceMappingURL=date-formatter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-formatter.test.js","sourceRoot":"","sources":["../../../tests/functional/date-formatter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Functional tests for progress bar module
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=progress-bar.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-bar.test.d.ts","sourceRoot":"","sources":["../../../tests/functional/progress-bar.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}