@stacknet/stacks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/dist/billing-BaJlf_S8.d.cts +1154 -0
- package/dist/billing-BaJlf_S8.d.ts +1154 -0
- package/dist/clients/index.cjs +4 -0
- package/dist/clients/index.d.cts +752 -0
- package/dist/clients/index.d.ts +752 -0
- package/dist/clients/index.js +4 -0
- package/dist/index-DVzKiF_0.d.cts +198 -0
- package/dist/index-DVzKiF_0.d.ts +198 -0
- package/dist/index.cjs +17 -0
- package/dist/index.d.cts +309 -0
- package/dist/index.d.ts +309 -0
- package/dist/index.js +17 -0
- package/dist/proxy/index.cjs +2 -0
- package/dist/proxy/index.d.cts +1 -0
- package/dist/proxy/index.d.ts +1 -0
- package/dist/proxy/index.js +2 -0
- package/dist/streaming/index.cjs +13 -0
- package/dist/streaming/index.d.cts +145 -0
- package/dist/streaming/index.d.ts +145 -0
- package/dist/streaming/index.js +13 -0
- package/dist/types/index.cjs +1 -0
- package/dist/types/index.d.cts +335 -0
- package/dist/types/index.d.ts +335 -0
- package/dist/types/index.js +1 -0
- package/package.json +75 -0
- package/src/clients/agents.ts +233 -0
- package/src/clients/billing.ts +157 -0
- package/src/clients/coder.ts +655 -0
- package/src/clients/files.ts +86 -0
- package/src/clients/index.ts +93 -0
- package/src/clients/magma.ts +299 -0
- package/src/clients/mcp.ts +208 -0
- package/src/clients/network.ts +118 -0
- package/src/clients/points.ts +403 -0
- package/src/clients/skills.ts +236 -0
- package/src/clients/social.ts +286 -0
- package/src/clients/stack-management.ts +279 -0
- package/src/clients/task-network.ts +303 -0
- package/src/clients/user.ts +84 -0
- package/src/clients/widgets.ts +171 -0
- package/src/index.ts +387 -0
- package/src/managers/index.ts +10 -0
- package/src/managers/task-manager.ts +310 -0
- package/src/proxy/forwarder.ts +146 -0
- package/src/proxy/index.ts +32 -0
- package/src/proxy/route-handlers.ts +950 -0
- package/src/streaming/component-stream.ts +319 -0
- package/src/streaming/index.ts +21 -0
- package/src/streaming/sse.ts +241 -0
- package/src/types/agent.ts +106 -0
- package/src/types/billing.ts +87 -0
- package/src/types/chat.ts +58 -0
- package/src/types/coder.ts +345 -0
- package/src/types/credential.ts +111 -0
- package/src/types/file.ts +15 -0
- package/src/types/imagination.ts +50 -0
- package/src/types/index.ts +20 -0
- package/src/types/mcp.ts +35 -0
- package/src/types/network.ts +97 -0
- package/src/types/points.ts +250 -0
- package/src/types/skill.ts +107 -0
- package/src/types/social.ts +109 -0
- package/src/types/stack.ts +269 -0
- package/src/types/task.ts +41 -0
- package/src/types/user.ts +29 -0
- package/src/types/widget.ts +57 -0
- package/src/utils/constants.ts +26 -0
- package/src/utils/errors.ts +169 -0
- package/src/utils/helpers.ts +85 -0
- package/src/utils/index.ts +7 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network Client
|
|
3
|
+
* Network status, MPC nodes, consensus, and auth metrics
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DEFAULT_GLAYER_NETWORK_URL, JSON_HEADERS } from '../utils/constants';
|
|
7
|
+
import { StacksSDKError } from '../utils/errors';
|
|
8
|
+
import type {
|
|
9
|
+
NetworkStatus,
|
|
10
|
+
MPCNode,
|
|
11
|
+
ConsensusStatus,
|
|
12
|
+
LeaderStatus,
|
|
13
|
+
AuthMetrics,
|
|
14
|
+
NetworkClientConfig,
|
|
15
|
+
} from '../types/network';
|
|
16
|
+
|
|
17
|
+
export type { NetworkClientConfig } from '../types/network';
|
|
18
|
+
|
|
19
|
+
const API_PATH = '/api/v2';
|
|
20
|
+
|
|
21
|
+
export class NetworkClient {
|
|
22
|
+
private baseUrl: string;
|
|
23
|
+
private authToken: string | null;
|
|
24
|
+
|
|
25
|
+
constructor(config: NetworkClientConfig = {}) {
|
|
26
|
+
this.baseUrl = config.baseUrl || DEFAULT_GLAYER_NETWORK_URL;
|
|
27
|
+
this.authToken = config.authToken || null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setAuthToken(token: string | null) {
|
|
31
|
+
this.authToken = token;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private get headers(): Record<string, string> {
|
|
35
|
+
const h: Record<string, string> = { ...JSON_HEADERS };
|
|
36
|
+
if (this.authToken) {
|
|
37
|
+
h['Authorization'] = `Bearer ${this.authToken}`;
|
|
38
|
+
}
|
|
39
|
+
return h;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private async request<T>(method: string, url: string): Promise<T> {
|
|
43
|
+
const response = await fetch(url, { method, headers: this.headers });
|
|
44
|
+
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const data = await response.json().catch(() => ({}));
|
|
47
|
+
throw new StacksSDKError(
|
|
48
|
+
'bad_request:api',
|
|
49
|
+
data.error?.message || `Request failed: ${response.statusText}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
return (data && typeof data === 'object' && 'data' in data) ? data.data : data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// =========================================================================
|
|
58
|
+
// Health
|
|
59
|
+
// =========================================================================
|
|
60
|
+
|
|
61
|
+
async checkHealth(): Promise<boolean> {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`${this.baseUrl}/health`);
|
|
64
|
+
return response.ok;
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// =========================================================================
|
|
71
|
+
// Network
|
|
72
|
+
// =========================================================================
|
|
73
|
+
|
|
74
|
+
async getNetworkStatus(): Promise<NetworkStatus> {
|
|
75
|
+
return this.request<NetworkStatus>('GET', `${this.baseUrl}${API_PATH}/network/status`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getMPCNodes(): Promise<MPCNode[]> {
|
|
79
|
+
return this.request<MPCNode[]>('GET', `${this.baseUrl}${API_PATH}/network/nodes`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getNodeHealth(nodeId: string): Promise<{ healthy: boolean; latency_ms: number }> {
|
|
83
|
+
return this.request('GET', `${this.baseUrl}${API_PATH}/network/nodes/${nodeId}/health`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =========================================================================
|
|
87
|
+
// Consensus
|
|
88
|
+
// =========================================================================
|
|
89
|
+
|
|
90
|
+
async getConsensusStatus(): Promise<ConsensusStatus> {
|
|
91
|
+
return this.request<ConsensusStatus>('GET', `${this.baseUrl}/consensus/status`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async getLeaderStatus(): Promise<LeaderStatus> {
|
|
95
|
+
return this.request<LeaderStatus>('GET', `${this.baseUrl}/consensus/leader`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// =========================================================================
|
|
99
|
+
// Metrics
|
|
100
|
+
// =========================================================================
|
|
101
|
+
|
|
102
|
+
async getAuthMetrics(stackId?: string): Promise<AuthMetrics> {
|
|
103
|
+
const path = stackId ? `/stacks/${stackId}/metrics` : '/metrics';
|
|
104
|
+
return this.request<AuthMetrics>('GET', `${this.baseUrl}${API_PATH}${path}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a NetworkClient instance
|
|
110
|
+
*/
|
|
111
|
+
export function createNetworkClient(config: NetworkClientConfig = {}): NetworkClient {
|
|
112
|
+
return new NetworkClient(config);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Default network client instance
|
|
117
|
+
*/
|
|
118
|
+
export const networkClient = createNetworkClient();
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Points Client
|
|
3
|
+
*
|
|
4
|
+
* Client for interacting with the Points Coprocessor API.
|
|
5
|
+
* Provides access to the internal reward and reputation framework for StackNet.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { DEFAULT_MAGMA_RPC_URL, JSON_HEADERS } from '../utils/constants';
|
|
9
|
+
import { StacksSDKError } from '../utils/errors';
|
|
10
|
+
import type {
|
|
11
|
+
ContextDomain,
|
|
12
|
+
PointContext,
|
|
13
|
+
Delegation,
|
|
14
|
+
PointBalance,
|
|
15
|
+
PointRecord,
|
|
16
|
+
CreateDomainInput,
|
|
17
|
+
CreateContextInput,
|
|
18
|
+
CreateDelegationInput,
|
|
19
|
+
AddPointsInput,
|
|
20
|
+
SpendPointsInput,
|
|
21
|
+
HistoryFilters,
|
|
22
|
+
DelegationFilters,
|
|
23
|
+
DomainsListResponse,
|
|
24
|
+
DomainResponse,
|
|
25
|
+
ContextsListResponse,
|
|
26
|
+
ContextResponse,
|
|
27
|
+
DelegationsListResponse,
|
|
28
|
+
DelegationResponse,
|
|
29
|
+
PointRecordResponse,
|
|
30
|
+
PointSpendResponse,
|
|
31
|
+
HistoryResponse,
|
|
32
|
+
InitNetworkResponse,
|
|
33
|
+
PaymentRequiredResponse,
|
|
34
|
+
PointsClientConfig,
|
|
35
|
+
} from '../types/points';
|
|
36
|
+
|
|
37
|
+
export type { PointsClientConfig };
|
|
38
|
+
|
|
39
|
+
export class PointsClient {
|
|
40
|
+
private baseUrl: string;
|
|
41
|
+
|
|
42
|
+
constructor(config: PointsClientConfig = {}) {
|
|
43
|
+
this.baseUrl = config.baseUrl || DEFAULT_MAGMA_RPC_URL;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private get pointsUrl(): string {
|
|
47
|
+
return `${this.baseUrl}/cpx/points`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Network Initialization
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the default geoffnet domain and contexts.
|
|
56
|
+
* Should only be called once during network bootstrap.
|
|
57
|
+
*/
|
|
58
|
+
async initNetworkDomain(): Promise<InitNetworkResponse> {
|
|
59
|
+
const response = await fetch(`${this.pointsUrl}/init`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: JSON_HEADERS,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
const data = await response.json().catch(() => ({}));
|
|
66
|
+
throw new StacksSDKError(
|
|
67
|
+
'bad_request:api',
|
|
68
|
+
data.error || `Failed to initialize network domain: ${response.statusText}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response.json();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Domain Operations
|
|
77
|
+
// ============================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* List all context domains
|
|
81
|
+
*/
|
|
82
|
+
async listDomains(): Promise<DomainsListResponse> {
|
|
83
|
+
const response = await fetch(`${this.pointsUrl}/domains`);
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new StacksSDKError(
|
|
87
|
+
'bad_request:api',
|
|
88
|
+
`Failed to list domains: ${response.statusText}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return response.json();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a domain by ID
|
|
97
|
+
*/
|
|
98
|
+
async getDomain(domainId: string): Promise<ContextDomain | null> {
|
|
99
|
+
const response = await fetch(`${this.pointsUrl}/domains/${domainId}`);
|
|
100
|
+
|
|
101
|
+
if (response.status === 404) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new StacksSDKError(
|
|
107
|
+
'bad_request:api',
|
|
108
|
+
`Failed to get domain: ${response.statusText}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return response.json();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a new context domain.
|
|
117
|
+
* Note: Requires 402 payment for non-default domains.
|
|
118
|
+
*/
|
|
119
|
+
async createDomain(input: CreateDomainInput): Promise<DomainResponse> {
|
|
120
|
+
const response = await fetch(`${this.pointsUrl}/domains`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: JSON_HEADERS,
|
|
123
|
+
body: JSON.stringify(input),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Handle 402 Payment Required
|
|
127
|
+
if (response.status === 402) {
|
|
128
|
+
const data: PaymentRequiredResponse = await response.json();
|
|
129
|
+
throw new StacksSDKError(
|
|
130
|
+
'payment_required:api',
|
|
131
|
+
`${data.error}${data.paymentDetails ? ` (Payment: ${JSON.stringify(data.paymentDetails)})` : ''}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
const data = await response.json().catch(() => ({}));
|
|
137
|
+
throw new StacksSDKError(
|
|
138
|
+
'bad_request:api',
|
|
139
|
+
data.error || `Failed to create domain: ${response.statusText}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return response.json();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Context Operations
|
|
148
|
+
// ============================================================================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* List all point contexts, optionally filtered by domain
|
|
152
|
+
*/
|
|
153
|
+
async listContexts(domainId?: string): Promise<ContextsListResponse> {
|
|
154
|
+
const params = domainId ? `?domainId=${domainId}` : '';
|
|
155
|
+
const response = await fetch(`${this.pointsUrl}/contexts${params}`);
|
|
156
|
+
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
throw new StacksSDKError(
|
|
159
|
+
'bad_request:api',
|
|
160
|
+
`Failed to list contexts: ${response.statusText}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return response.json();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get a context by ID
|
|
169
|
+
*/
|
|
170
|
+
async getContext(contextId: string): Promise<PointContext | null> {
|
|
171
|
+
const response = await fetch(`${this.pointsUrl}/contexts/${contextId}`);
|
|
172
|
+
|
|
173
|
+
if (response.status === 404) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
throw new StacksSDKError(
|
|
179
|
+
'bad_request:api',
|
|
180
|
+
`Failed to get context: ${response.statusText}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return response.json();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Create a new point context.
|
|
189
|
+
* Note: Requires 402 payment for non-default contexts.
|
|
190
|
+
*/
|
|
191
|
+
async createContext(input: CreateContextInput): Promise<ContextResponse> {
|
|
192
|
+
const response = await fetch(`${this.pointsUrl}/contexts`, {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: JSON_HEADERS,
|
|
195
|
+
body: JSON.stringify(input),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Handle 402 Payment Required
|
|
199
|
+
if (response.status === 402) {
|
|
200
|
+
const data: PaymentRequiredResponse = await response.json();
|
|
201
|
+
throw new StacksSDKError(
|
|
202
|
+
'payment_required:api',
|
|
203
|
+
`${data.error}${data.paymentDetails ? ` (Payment: ${JSON.stringify(data.paymentDetails)})` : ''}`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const data = await response.json().catch(() => ({}));
|
|
209
|
+
throw new StacksSDKError(
|
|
210
|
+
'bad_request:api',
|
|
211
|
+
data.error || `Failed to create context: ${response.statusText}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return response.json();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Delegation Operations
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* List delegations with optional filters
|
|
224
|
+
*/
|
|
225
|
+
async listDelegations(filters: DelegationFilters = {}): Promise<DelegationsListResponse> {
|
|
226
|
+
const params = new URLSearchParams();
|
|
227
|
+
if (filters.domainId) params.set('domainId', filters.domainId);
|
|
228
|
+
if (filters.delegator) params.set('delegator', filters.delegator);
|
|
229
|
+
if (filters.delegate) params.set('delegate', filters.delegate);
|
|
230
|
+
if (filters.activeOnly) params.set('activeOnly', 'true');
|
|
231
|
+
|
|
232
|
+
const queryString = params.toString();
|
|
233
|
+
const url = `${this.pointsUrl}/delegations${queryString ? `?${queryString}` : ''}`;
|
|
234
|
+
|
|
235
|
+
const response = await fetch(url);
|
|
236
|
+
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
throw new StacksSDKError(
|
|
239
|
+
'bad_request:api',
|
|
240
|
+
`Failed to list delegations: ${response.statusText}`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return response.json();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Create a new delegation
|
|
249
|
+
*/
|
|
250
|
+
async createDelegation(input: CreateDelegationInput): Promise<DelegationResponse> {
|
|
251
|
+
const response = await fetch(`${this.pointsUrl}/delegations`, {
|
|
252
|
+
method: 'POST',
|
|
253
|
+
headers: JSON_HEADERS,
|
|
254
|
+
body: JSON.stringify(input),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (!response.ok) {
|
|
258
|
+
const data = await response.json().catch(() => ({}));
|
|
259
|
+
throw new StacksSDKError(
|
|
260
|
+
'bad_request:api',
|
|
261
|
+
data.error || `Failed to create delegation: ${response.statusText}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return response.json();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Revoke a delegation
|
|
270
|
+
*/
|
|
271
|
+
async revokeDelegation(delegationId: string): Promise<{ success: boolean }> {
|
|
272
|
+
const response = await fetch(`${this.pointsUrl}/delegations/${delegationId}`, {
|
|
273
|
+
method: 'DELETE',
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
const data = await response.json().catch(() => ({}));
|
|
278
|
+
throw new StacksSDKError(
|
|
279
|
+
'bad_request:api',
|
|
280
|
+
data.error || `Failed to revoke delegation: ${response.statusText}`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return response.json();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// Points Operations
|
|
289
|
+
// ============================================================================
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Add points (positive or negative) to an MID
|
|
293
|
+
*/
|
|
294
|
+
async addPoints(input: AddPointsInput): Promise<PointRecordResponse> {
|
|
295
|
+
const response = await fetch(`${this.pointsUrl}/add`, {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: JSON_HEADERS,
|
|
298
|
+
body: JSON.stringify(input),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
const data = await response.json().catch(() => ({}));
|
|
303
|
+
throw new StacksSDKError(
|
|
304
|
+
'bad_request:api',
|
|
305
|
+
data.error || `Failed to add points: ${response.statusText}`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return response.json();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Spend points (settlement via MetaChain)
|
|
314
|
+
*/
|
|
315
|
+
async spendPoints(input: SpendPointsInput): Promise<PointSpendResponse> {
|
|
316
|
+
const response = await fetch(`${this.pointsUrl}/spend`, {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers: JSON_HEADERS,
|
|
319
|
+
body: JSON.stringify(input),
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const data = await response.json().catch(() => ({}));
|
|
324
|
+
throw new StacksSDKError(
|
|
325
|
+
'bad_request:api',
|
|
326
|
+
data.error || `Failed to spend points: ${response.statusText}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return response.json();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get points balance for an MID
|
|
335
|
+
*/
|
|
336
|
+
async getBalance(
|
|
337
|
+
mid: string,
|
|
338
|
+
options: { domainId?: string; contextId?: string } = {}
|
|
339
|
+
): Promise<PointBalance> {
|
|
340
|
+
const params = new URLSearchParams();
|
|
341
|
+
if (options.domainId) params.set('domainId', options.domainId);
|
|
342
|
+
if (options.contextId) params.set('contextId', options.contextId);
|
|
343
|
+
|
|
344
|
+
const queryString = params.toString();
|
|
345
|
+
const url = `${this.pointsUrl}/balance/${mid}${queryString ? `?${queryString}` : ''}`;
|
|
346
|
+
|
|
347
|
+
const response = await fetch(url);
|
|
348
|
+
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
throw new StacksSDKError(
|
|
351
|
+
'bad_request:api',
|
|
352
|
+
`Failed to get balance: ${response.statusText}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return response.json();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get points history with optional filters
|
|
361
|
+
*/
|
|
362
|
+
async getHistory(filters: HistoryFilters = {}): Promise<HistoryResponse> {
|
|
363
|
+
const params = new URLSearchParams();
|
|
364
|
+
if (filters.mid) params.set('mid', filters.mid);
|
|
365
|
+
if (filters.domainId) params.set('domainId', filters.domainId);
|
|
366
|
+
if (filters.contextId) params.set('contextId', filters.contextId);
|
|
367
|
+
if (filters.periodStart) params.set('periodStart', filters.periodStart.toString());
|
|
368
|
+
if (filters.periodEnd) params.set('periodEnd', filters.periodEnd.toString());
|
|
369
|
+
if (filters.taskId) params.set('taskId', filters.taskId);
|
|
370
|
+
if (filters.limit) params.set('limit', filters.limit.toString());
|
|
371
|
+
if (filters.offset) params.set('offset', filters.offset.toString());
|
|
372
|
+
|
|
373
|
+
const queryString = params.toString();
|
|
374
|
+
const url = `${this.pointsUrl}/history${queryString ? `?${queryString}` : ''}`;
|
|
375
|
+
|
|
376
|
+
const response = await fetch(url);
|
|
377
|
+
|
|
378
|
+
if (!response.ok) {
|
|
379
|
+
throw new StacksSDKError(
|
|
380
|
+
'bad_request:api',
|
|
381
|
+
`Failed to get history: ${response.statusText}`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return response.json();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ============================================================================
|
|
390
|
+
// Factory Functions
|
|
391
|
+
// ============================================================================
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Create a PointsClient instance
|
|
395
|
+
*/
|
|
396
|
+
export function createPointsClient(config: PointsClientConfig = {}): PointsClient {
|
|
397
|
+
return new PointsClient(config);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Default points client instance
|
|
402
|
+
*/
|
|
403
|
+
export const pointsClient = createPointsClient();
|