@jack-kernel/sdk 1.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 (68) hide show
  1. package/README.md +757 -0
  2. package/dist/cjs/agent.js +321 -0
  3. package/dist/cjs/agent.js.map +1 -0
  4. package/dist/cjs/cache.js +58 -0
  5. package/dist/cjs/cache.js.map +1 -0
  6. package/dist/cjs/client.js +196 -0
  7. package/dist/cjs/client.js.map +1 -0
  8. package/dist/cjs/costs.js +104 -0
  9. package/dist/cjs/costs.js.map +1 -0
  10. package/dist/cjs/errors.js +287 -0
  11. package/dist/cjs/errors.js.map +1 -0
  12. package/dist/cjs/execution.js +385 -0
  13. package/dist/cjs/execution.js.map +1 -0
  14. package/dist/cjs/index.js +256 -0
  15. package/dist/cjs/index.js.map +1 -0
  16. package/dist/cjs/intents.js +185 -0
  17. package/dist/cjs/intents.js.map +1 -0
  18. package/dist/cjs/serialization.js +164 -0
  19. package/dist/cjs/serialization.js.map +1 -0
  20. package/dist/cjs/types.js +31 -0
  21. package/dist/cjs/types.js.map +1 -0
  22. package/dist/cjs/validation.js +114 -0
  23. package/dist/cjs/validation.js.map +1 -0
  24. package/dist/esm/agent.js +317 -0
  25. package/dist/esm/agent.js.map +1 -0
  26. package/dist/esm/cache.js +54 -0
  27. package/dist/esm/cache.js.map +1 -0
  28. package/dist/esm/client.js +192 -0
  29. package/dist/esm/client.js.map +1 -0
  30. package/dist/esm/costs.js +100 -0
  31. package/dist/esm/costs.js.map +1 -0
  32. package/dist/esm/errors.js +278 -0
  33. package/dist/esm/errors.js.map +1 -0
  34. package/dist/esm/execution.js +381 -0
  35. package/dist/esm/execution.js.map +1 -0
  36. package/dist/esm/index.js +236 -0
  37. package/dist/esm/index.js.map +1 -0
  38. package/dist/esm/intents.js +181 -0
  39. package/dist/esm/intents.js.map +1 -0
  40. package/dist/esm/serialization.js +159 -0
  41. package/dist/esm/serialization.js.map +1 -0
  42. package/dist/esm/types.js +28 -0
  43. package/dist/esm/types.js.map +1 -0
  44. package/dist/esm/validation.js +111 -0
  45. package/dist/esm/validation.js.map +1 -0
  46. package/dist/types/agent.d.ts +171 -0
  47. package/dist/types/agent.d.ts.map +1 -0
  48. package/dist/types/cache.d.ts +25 -0
  49. package/dist/types/cache.d.ts.map +1 -0
  50. package/dist/types/client.d.ts +46 -0
  51. package/dist/types/client.d.ts.map +1 -0
  52. package/dist/types/costs.d.ts +91 -0
  53. package/dist/types/costs.d.ts.map +1 -0
  54. package/dist/types/errors.d.ts +242 -0
  55. package/dist/types/errors.d.ts.map +1 -0
  56. package/dist/types/execution.d.ts +145 -0
  57. package/dist/types/execution.d.ts.map +1 -0
  58. package/dist/types/index.d.ts +205 -0
  59. package/dist/types/index.d.ts.map +1 -0
  60. package/dist/types/intents.d.ts +158 -0
  61. package/dist/types/intents.d.ts.map +1 -0
  62. package/dist/types/serialization.d.ts +82 -0
  63. package/dist/types/serialization.d.ts.map +1 -0
  64. package/dist/types/types.d.ts +302 -0
  65. package/dist/types/types.d.ts.map +1 -0
  66. package/dist/types/validation.d.ts +40 -0
  67. package/dist/types/validation.d.ts.map +1 -0
  68. package/package.json +56 -0
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Cost tracking module for JACK SDK
4
+ *
5
+ * Provides methods to query execution costs and budgets.
6
+ * Validates Requirements 4.1, 4.2
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CostTracker = void 0;
10
+ /**
11
+ * CostTracker class for querying execution costs and budgets
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const tracker = new CostTracker(client);
16
+ * const costs = await tracker.getCosts();
17
+ * const issueCost = await tracker.getIssueCost('ISSUE-123');
18
+ * const overBudget = await tracker.getOverBudgetIssues();
19
+ * ```
20
+ */
21
+ class CostTracker {
22
+ client;
23
+ constructor(client) {
24
+ this.client = client;
25
+ }
26
+ /**
27
+ * Get all issue costs from the API
28
+ *
29
+ * Makes a GET request to /api/costs and returns the complete response
30
+ * with cost data for all issues.
31
+ *
32
+ * @returns Promise resolving to CostsResponse with all issue costs
33
+ * @throws {APIError} If the API request fails
34
+ * @throws {NetworkError} If network connection fails
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const costs = await tracker.getCosts();
39
+ * console.log(`Found ${costs.issueCosts.length} issues`);
40
+ * ```
41
+ *
42
+ * Validates Requirement 4.1
43
+ */
44
+ async getCosts() {
45
+ return this.client.get('/api/costs');
46
+ }
47
+ /**
48
+ * Get costs for a specific issue
49
+ *
50
+ * Fetches all costs and filters by the specified issue ID.
51
+ * Returns null if the issue is not found.
52
+ *
53
+ * @param issueId - The issue identifier to filter by
54
+ * @returns Promise resolving to IssueCost or null if not found
55
+ * @throws {APIError} If the API request fails
56
+ * @throws {NetworkError} If network connection fails
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const issueCost = await tracker.getIssueCost('ISSUE-123');
61
+ * if (issueCost) {
62
+ * console.log(`Total cost: ${issueCost.totalCost}`);
63
+ * console.log(`Budget: ${issueCost.budget}`);
64
+ * console.log(`Over budget: ${issueCost.overBudget}`);
65
+ * }
66
+ * ```
67
+ *
68
+ * Validates Requirement 4.2
69
+ */
70
+ async getIssueCost(issueId) {
71
+ const response = await this.getCosts();
72
+ const issueCost = response.issueCosts.find((cost) => cost.issueId === issueId);
73
+ return issueCost ?? null;
74
+ }
75
+ /**
76
+ * Get all issues that are over budget
77
+ *
78
+ * Fetches all costs and filters to only those with overBudget flag set to true.
79
+ * Returns an empty array if no issues are over budget.
80
+ *
81
+ * @returns Promise resolving to array of IssueCost objects that are over budget
82
+ * @throws {APIError} If the API request fails
83
+ * @throws {NetworkError} If network connection fails
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const overBudget = await tracker.getOverBudgetIssues();
88
+ * if (overBudget.length > 0) {
89
+ * console.log(`Warning: ${overBudget.length} issues are over budget`);
90
+ * overBudget.forEach(issue => {
91
+ * console.log(`${issue.issueId}: ${issue.totalCost} / ${issue.budget}`);
92
+ * });
93
+ * }
94
+ * ```
95
+ *
96
+ * Validates Requirement 4.2
97
+ */
98
+ async getOverBudgetIssues() {
99
+ const response = await this.getCosts();
100
+ return response.issueCosts.filter((cost) => cost.overBudget);
101
+ }
102
+ }
103
+ exports.CostTracker = CostTracker;
104
+ //# sourceMappingURL=costs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"costs.js","sourceRoot":"","sources":["../../src/costs.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAKH;;;;;;;;;;GAUG;AACH,MAAa,WAAW;IACO;IAA7B,YAA6B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAEnD;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,QAAQ;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAgB,YAAY,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,YAAY,CAAC,OAAe;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,CACnC,CAAC;QACF,OAAO,SAAS,IAAI,IAAI,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,mBAAmB;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvC,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,CAAC;CACF;AAnFD,kCAmFC"}
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ /**
3
+ * Error hierarchy for the JACK SDK
4
+ *
5
+ * This module defines custom error classes for different failure scenarios.
6
+ * All errors extend the base JackError class and include context for debugging.
7
+ * Requirements: 5.2, 5.3
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.RetryError = exports.TimeoutError = exports.ValidationError = exports.APIError = exports.NetworkError = exports.JackError = void 0;
11
+ /**
12
+ * Base error class for all JACK SDK errors
13
+ *
14
+ * Extends the native Error class and adds optional context for debugging.
15
+ * All SDK-specific errors inherit from this class.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * throw new JackError('Something went wrong', { requestId: '123', timestamp: Date.now() });
20
+ * ```
21
+ *
22
+ * Requirement 5.2
23
+ */
24
+ class JackError extends Error {
25
+ /**
26
+ * Additional context information for debugging
27
+ * Can include request details, timestamps, or any relevant metadata
28
+ */
29
+ context;
30
+ /**
31
+ * Creates a new JackError
32
+ *
33
+ * @param message - Human-readable error message
34
+ * @param context - Optional context object with debugging information
35
+ */
36
+ constructor(message, context) {
37
+ super(message);
38
+ this.name = 'JackError';
39
+ this.context = context;
40
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
41
+ if (Error.captureStackTrace) {
42
+ Error.captureStackTrace(this, this.constructor);
43
+ }
44
+ }
45
+ }
46
+ exports.JackError = JackError;
47
+ /**
48
+ * Error thrown when network-level failures occur
49
+ *
50
+ * This includes connection failures, DNS resolution errors, timeouts at the
51
+ * network layer, and other transport-level issues. The original error is
52
+ * preserved for detailed debugging.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * try {
57
+ * await fetch('https://api.jack.example');
58
+ * } catch (err) {
59
+ * throw new NetworkError('Failed to connect to API', err as Error);
60
+ * }
61
+ * ```
62
+ *
63
+ * Requirement 5.3
64
+ */
65
+ class NetworkError extends JackError {
66
+ /**
67
+ * The original error that caused the network failure
68
+ * Preserved for detailed debugging and error analysis
69
+ */
70
+ originalError;
71
+ /**
72
+ * Creates a new NetworkError
73
+ *
74
+ * @param message - Human-readable error message
75
+ * @param originalError - The underlying error that caused the network failure
76
+ * @param context - Optional additional context
77
+ */
78
+ constructor(message, originalError, context) {
79
+ super(message, context);
80
+ this.name = 'NetworkError';
81
+ this.originalError = originalError;
82
+ }
83
+ }
84
+ exports.NetworkError = NetworkError;
85
+ /**
86
+ * Error thrown when the API returns an error response
87
+ *
88
+ * This includes 4xx client errors (bad request, not found, unauthorized) and
89
+ * 5xx server errors (internal server error, service unavailable). The HTTP
90
+ * status code and response body are included for debugging.
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * if (response.status >= 400) {
95
+ * throw new APIError(
96
+ * 'Intent not found',
97
+ * 404,
98
+ * { error: 'Intent with ID JK-ABC123456 does not exist' }
99
+ * );
100
+ * }
101
+ * ```
102
+ *
103
+ * Requirement 5.3
104
+ */
105
+ class APIError extends JackError {
106
+ /**
107
+ * HTTP status code from the API response
108
+ * Used to distinguish between client errors (4xx) and server errors (5xx)
109
+ */
110
+ statusCode;
111
+ /**
112
+ * The parsed response body from the API
113
+ * May contain error details, validation messages, or other diagnostic information
114
+ */
115
+ response;
116
+ /**
117
+ * Creates a new APIError
118
+ *
119
+ * @param message - Human-readable error message
120
+ * @param statusCode - HTTP status code from the response
121
+ * @param response - Optional parsed response body
122
+ * @param context - Optional additional context
123
+ */
124
+ constructor(message, statusCode, response, context) {
125
+ super(message, context);
126
+ this.name = 'APIError';
127
+ this.statusCode = statusCode;
128
+ this.response = response;
129
+ }
130
+ /**
131
+ * Check if this is a client error (4xx status code)
132
+ * @returns true if status code is in the 400-499 range
133
+ */
134
+ isClientError() {
135
+ return this.statusCode >= 400 && this.statusCode < 500;
136
+ }
137
+ /**
138
+ * Check if this is a server error (5xx status code)
139
+ * @returns true if status code is in the 500-599 range
140
+ */
141
+ isServerError() {
142
+ return this.statusCode >= 500 && this.statusCode < 600;
143
+ }
144
+ /**
145
+ * Check if this error is retryable
146
+ * Server errors (5xx) are typically retryable, client errors (4xx) are not
147
+ * @returns true if the error should be retried
148
+ */
149
+ isRetryable() {
150
+ return this.isServerError();
151
+ }
152
+ }
153
+ exports.APIError = APIError;
154
+ /**
155
+ * Error thrown when client-side validation fails
156
+ *
157
+ * This error is thrown before making any network requests when input parameters
158
+ * fail validation checks. It includes an array of specific validation errors
159
+ * to help developers identify and fix issues.
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * const errors = [];
164
+ * if (params.amountIn <= 0) errors.push('amountIn must be positive');
165
+ * if (params.deadline < Date.now()) errors.push('deadline must be in the future');
166
+ * if (errors.length > 0) {
167
+ * throw new ValidationError('Invalid intent parameters', errors);
168
+ * }
169
+ * ```
170
+ *
171
+ * Requirement 5.3
172
+ */
173
+ class ValidationError extends JackError {
174
+ /**
175
+ * Array of specific validation error messages
176
+ * Each message describes a single validation failure
177
+ */
178
+ errors;
179
+ /**
180
+ * Creates a new ValidationError
181
+ *
182
+ * @param message - Human-readable error message
183
+ * @param errors - Array of specific validation error messages
184
+ * @param context - Optional additional context
185
+ */
186
+ constructor(message, errors, context) {
187
+ super(message, context);
188
+ this.name = 'ValidationError';
189
+ this.errors = errors;
190
+ }
191
+ }
192
+ exports.ValidationError = ValidationError;
193
+ /**
194
+ * Error thrown when an operation exceeds its timeout
195
+ *
196
+ * This includes request timeouts, polling timeouts, and any other time-bound
197
+ * operations. The timeout duration is included for debugging.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const timeout = 30000; // 30 seconds
202
+ * const timeoutPromise = new Promise((_, reject) => {
203
+ * setTimeout(() => {
204
+ * reject(new TimeoutError('Request timed out', timeout));
205
+ * }, timeout);
206
+ * });
207
+ * await Promise.race([fetchPromise, timeoutPromise]);
208
+ * ```
209
+ *
210
+ * Requirement 5.3
211
+ */
212
+ class TimeoutError extends JackError {
213
+ /**
214
+ * The timeout duration in milliseconds
215
+ * Indicates how long the operation was allowed to run before timing out
216
+ */
217
+ timeoutMs;
218
+ /**
219
+ * Creates a new TimeoutError
220
+ *
221
+ * @param message - Human-readable error message
222
+ * @param timeoutMs - The timeout duration in milliseconds
223
+ * @param context - Optional additional context
224
+ */
225
+ constructor(message, timeoutMs, context) {
226
+ super(message, context);
227
+ this.name = 'TimeoutError';
228
+ this.timeoutMs = timeoutMs;
229
+ }
230
+ }
231
+ exports.TimeoutError = TimeoutError;
232
+ /**
233
+ * Error thrown when retry attempts are exhausted
234
+ *
235
+ * This error is thrown after all retry attempts have failed. It includes the
236
+ * number of attempts made and the final error that caused the failure.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * let lastError: Error;
241
+ * for (let attempt = 1; attempt <= maxRetries; attempt++) {
242
+ * try {
243
+ * return await makeRequest();
244
+ * } catch (err) {
245
+ * lastError = err as Error;
246
+ * if (attempt === maxRetries) {
247
+ * throw new RetryError(
248
+ * 'All retry attempts exhausted',
249
+ * attempt,
250
+ * lastError
251
+ * );
252
+ * }
253
+ * await delay(retryDelay * Math.pow(retryBackoff, attempt - 1));
254
+ * }
255
+ * }
256
+ * ```
257
+ *
258
+ * Requirement 5.3
259
+ */
260
+ class RetryError extends JackError {
261
+ /**
262
+ * The number of retry attempts that were made
263
+ * Includes the initial attempt plus all retries
264
+ */
265
+ attempts;
266
+ /**
267
+ * The final error that caused the retry loop to fail
268
+ * This is the error from the last retry attempt
269
+ */
270
+ lastError;
271
+ /**
272
+ * Creates a new RetryError
273
+ *
274
+ * @param message - Human-readable error message
275
+ * @param attempts - The number of attempts that were made
276
+ * @param lastError - The final error from the last attempt
277
+ * @param context - Optional additional context
278
+ */
279
+ constructor(message, attempts, lastError, context) {
280
+ super(message, context);
281
+ this.name = 'RetryError';
282
+ this.attempts = attempts;
283
+ this.lastError = lastError;
284
+ }
285
+ }
286
+ exports.RetryError = RetryError;
287
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}