@bowenqt/qiniu-ai-sdk 0.23.0 → 0.23.2
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.
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
* Implements the Checkpointer interface for serverless-compatible
|
|
5
5
|
* durable state management.
|
|
6
6
|
*
|
|
7
|
+
* **⚠️ Node.js Only**: This module uses `node:crypto` for HMAC-SHA1 signing
|
|
8
|
+
* and is NOT compatible with Edge runtimes (CloudFlare Workers, Vercel Edge).
|
|
9
|
+
* For Edge environments, use MemoryCheckpointer or implement a backend proxy.
|
|
10
|
+
*
|
|
11
|
+
* **Download Domain**: If your bucket doesn't have a default Kodo domain bound,
|
|
12
|
+
* you MUST provide `downloadDomain` to avoid 404 errors on load().
|
|
13
|
+
*
|
|
7
14
|
* @example
|
|
8
15
|
* ```typescript
|
|
9
16
|
* import { KodoCheckpointer } from '@bowenqt/qiniu-ai-sdk';
|
|
@@ -13,11 +20,8 @@
|
|
|
13
20
|
* accessKey: process.env.QINIU_ACCESS_KEY!,
|
|
14
21
|
* secretKey: process.env.QINIU_SECRET_KEY!,
|
|
15
22
|
* region: 'z0',
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* const graph = createAgentGraph({
|
|
19
|
-
* checkpointer,
|
|
20
|
-
* // ...
|
|
23
|
+
* // Required if bucket has no default Kodo domain
|
|
24
|
+
* downloadDomain: 'cdn.example.com',
|
|
21
25
|
* });
|
|
22
26
|
* ```
|
|
23
27
|
*/
|
|
@@ -41,6 +45,12 @@ export interface KodoCheckpointerConfig {
|
|
|
41
45
|
tokenExpiry?: number;
|
|
42
46
|
/** Max retries for API calls (default: 3) */
|
|
43
47
|
maxRetries?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Download domain (e.g., 'cdn.example.com').
|
|
50
|
+
* **REQUIRED** unless bucket has a default Kodo domain bound.
|
|
51
|
+
* Without this, load() will return 404 errors.
|
|
52
|
+
*/
|
|
53
|
+
downloadDomain?: string;
|
|
44
54
|
}
|
|
45
55
|
/**
|
|
46
56
|
* Kodo-backed Checkpointer implementation.
|
|
@@ -61,7 +71,8 @@ export declare class KodoCheckpointer implements Checkpointer {
|
|
|
61
71
|
*/
|
|
62
72
|
load(threadId: string): Promise<Checkpoint | null>;
|
|
63
73
|
/**
|
|
64
|
-
* List
|
|
74
|
+
* List checkpoints for a specific thread.
|
|
75
|
+
* Since we store single checkpoint per thread, this returns 0 or 1 item.
|
|
65
76
|
*/
|
|
66
77
|
list(threadId: string): Promise<CheckpointMetadata[]>;
|
|
67
78
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kodo-checkpointer.d.ts","sourceRoot":"","sources":["../../../src/ai/graph/kodo-checkpointer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"kodo-checkpointer.d.ts","sourceRoot":"","sources":["../../../src/ai/graph/kodo-checkpointer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EACR,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EAExB,MAAM,gBAAgB,CAAC;AAOxB,wBAAwB;AACxB,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAoBrF,qCAAqC;AACrC,MAAM,WAAW,sBAAsB;IACnC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAuND;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,sBAAsB;IAK1C;;OAEG;IACG,IAAI,CACN,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,UAAU,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC1D,OAAO,CAAC,kBAAkB,CAAC;IA0C9B;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAKxD;;;OAGG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAM3D;;OAEG;IACG,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMpD;;OAEG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM9C;;;OAGG;IACH,OAAO,CAAC,cAAc;CAkBzB"}
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* Implements the Checkpointer interface for serverless-compatible
|
|
6
6
|
* durable state management.
|
|
7
7
|
*
|
|
8
|
+
* **⚠️ Node.js Only**: This module uses `node:crypto` for HMAC-SHA1 signing
|
|
9
|
+
* and is NOT compatible with Edge runtimes (CloudFlare Workers, Vercel Edge).
|
|
10
|
+
* For Edge environments, use MemoryCheckpointer or implement a backend proxy.
|
|
11
|
+
*
|
|
12
|
+
* **Download Domain**: If your bucket doesn't have a default Kodo domain bound,
|
|
13
|
+
* you MUST provide `downloadDomain` to avoid 404 errors on load().
|
|
14
|
+
*
|
|
8
15
|
* @example
|
|
9
16
|
* ```typescript
|
|
10
17
|
* import { KodoCheckpointer } from '@bowenqt/qiniu-ai-sdk';
|
|
@@ -14,11 +21,8 @@
|
|
|
14
21
|
* accessKey: process.env.QINIU_ACCESS_KEY!,
|
|
15
22
|
* secretKey: process.env.QINIU_SECRET_KEY!,
|
|
16
23
|
* region: 'z0',
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* const graph = createAgentGraph({
|
|
20
|
-
* checkpointer,
|
|
21
|
-
* // ...
|
|
24
|
+
* // Required if bucket has no default Kodo domain
|
|
25
|
+
* downloadDomain: 'cdn.example.com',
|
|
22
26
|
* });
|
|
23
27
|
* ```
|
|
24
28
|
*/
|
|
@@ -49,21 +53,23 @@ class KodoClient {
|
|
|
49
53
|
prefix: config.prefix ?? 'checkpoints/',
|
|
50
54
|
tokenExpiry: config.tokenExpiry ?? 3600,
|
|
51
55
|
maxRetries: config.maxRetries ?? 3,
|
|
56
|
+
downloadDomain: config.downloadDomain,
|
|
52
57
|
};
|
|
53
58
|
this.hosts = REGION_HOSTS[this.config.region];
|
|
54
59
|
}
|
|
55
|
-
/** Generate upload token */
|
|
56
|
-
generateUploadToken(
|
|
60
|
+
/** Generate upload token (bucket-level scope for reuse) */
|
|
61
|
+
generateUploadToken() {
|
|
57
62
|
const now = Math.floor(Date.now() / 1000);
|
|
58
63
|
const deadline = now + this.config.tokenExpiry;
|
|
59
64
|
// Check cache
|
|
60
65
|
if (this.cachedToken && this.cachedToken.expiresAt > Date.now() + 60000) {
|
|
61
66
|
return this.cachedToken.token;
|
|
62
67
|
}
|
|
63
|
-
// PutPolicy
|
|
68
|
+
// PutPolicy with bucket-level scope (allows any key in bucket)
|
|
64
69
|
const policy = {
|
|
65
|
-
scope:
|
|
70
|
+
scope: this.config.bucket,
|
|
66
71
|
deadline,
|
|
72
|
+
insertOnly: 0, // Allow overwrite
|
|
67
73
|
};
|
|
68
74
|
const encodedPolicy = this.base64UrlSafe(JSON.stringify(policy));
|
|
69
75
|
const sign = this.hmacSha1(encodedPolicy);
|
|
@@ -109,7 +115,7 @@ class KodoClient {
|
|
|
109
115
|
}
|
|
110
116
|
/** Upload JSON data */
|
|
111
117
|
async upload(key, data) {
|
|
112
|
-
const token = this.generateUploadToken(
|
|
118
|
+
const token = this.generateUploadToken();
|
|
113
119
|
const body = JSON.stringify(data);
|
|
114
120
|
const formData = new FormData();
|
|
115
121
|
formData.append('token', token);
|
|
@@ -129,12 +135,15 @@ class KodoClient {
|
|
|
129
135
|
/** Download JSON data */
|
|
130
136
|
async download(key) {
|
|
131
137
|
return this.withRetry(async () => {
|
|
138
|
+
// Determine download domain
|
|
139
|
+
const domain = this.config.downloadDomain
|
|
140
|
+
?? `${this.config.bucket}.${this.hosts.io.replace('iovip', 'kodo')}`;
|
|
132
141
|
// Generate signed URL for private bucket
|
|
133
142
|
const deadline = Math.floor(Date.now() / 1000) + 3600;
|
|
134
|
-
const
|
|
135
|
-
const toSign = `${
|
|
143
|
+
const baseUrl = `https://${domain}/${encodeURIComponent(key)}`;
|
|
144
|
+
const toSign = `${baseUrl}?e=${deadline}`;
|
|
136
145
|
const sign = this.hmacSha1(toSign);
|
|
137
|
-
const signedUrl = `${
|
|
146
|
+
const signedUrl = `${baseUrl}?e=${deadline}&token=${this.config.accessKey}:${this.base64UrlSafe(sign)}`;
|
|
138
147
|
const response = await fetch(signedUrl);
|
|
139
148
|
if (response.status === 404) {
|
|
140
149
|
return null;
|
|
@@ -218,14 +227,31 @@ class KodoCheckpointer {
|
|
|
218
227
|
async save(threadId, state, options) {
|
|
219
228
|
const key = this.client.getFullKey(threadId);
|
|
220
229
|
const serialized = this.serializeState(state);
|
|
230
|
+
// Extract options - handle both new CheckpointSaveOptions and legacy custom object
|
|
231
|
+
let status = 'active';
|
|
232
|
+
let pendingApproval;
|
|
233
|
+
let custom;
|
|
234
|
+
if (options) {
|
|
235
|
+
if ('status' in options || 'pendingApproval' in options || 'custom' in options) {
|
|
236
|
+
// New CheckpointSaveOptions format
|
|
237
|
+
const opts = options;
|
|
238
|
+
status = opts.status ?? 'active';
|
|
239
|
+
pendingApproval = opts.pendingApproval;
|
|
240
|
+
custom = opts.custom;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// Legacy: treat entire object as custom metadata
|
|
244
|
+
custom = options;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
221
247
|
const metadata = {
|
|
222
|
-
id:
|
|
248
|
+
id: threadId, // Simplified: single checkpoint per thread
|
|
223
249
|
threadId,
|
|
224
250
|
createdAt: Date.now(),
|
|
225
251
|
stepCount: state.stepCount,
|
|
226
|
-
status
|
|
227
|
-
pendingApproval
|
|
228
|
-
custom
|
|
252
|
+
status,
|
|
253
|
+
pendingApproval,
|
|
254
|
+
custom,
|
|
229
255
|
};
|
|
230
256
|
const checkpoint = {
|
|
231
257
|
metadata,
|
|
@@ -242,31 +268,20 @@ class KodoCheckpointer {
|
|
|
242
268
|
return this.client.download(key);
|
|
243
269
|
}
|
|
244
270
|
/**
|
|
245
|
-
* List
|
|
271
|
+
* List checkpoints for a specific thread.
|
|
272
|
+
* Since we store single checkpoint per thread, this returns 0 or 1 item.
|
|
246
273
|
*/
|
|
247
274
|
async list(threadId) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
? items.filter(item => item.key.includes(threadId))
|
|
252
|
-
: items;
|
|
253
|
-
// Download metadata for each
|
|
254
|
-
const metadataList = [];
|
|
255
|
-
for (const item of filtered) {
|
|
256
|
-
const checkpoint = await this.client.download(item.key);
|
|
257
|
-
if (checkpoint?.metadata) {
|
|
258
|
-
metadataList.push(checkpoint.metadata);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return metadataList.sort((a, b) => b.createdAt - a.createdAt);
|
|
275
|
+
// Direct download (single checkpoint per thread model)
|
|
276
|
+
const checkpoint = await this.load(threadId);
|
|
277
|
+
return checkpoint ? [checkpoint.metadata] : [];
|
|
262
278
|
}
|
|
263
279
|
/**
|
|
264
280
|
* Delete a single checkpoint.
|
|
265
281
|
*/
|
|
266
282
|
async delete(checkpointId) {
|
|
267
|
-
//
|
|
268
|
-
const
|
|
269
|
-
const key = this.client.getFullKey(threadId);
|
|
283
|
+
// For single checkpoint per thread, checkpointId is the threadId
|
|
284
|
+
const key = this.client.getFullKey(checkpointId);
|
|
270
285
|
return this.client.delete(key);
|
|
271
286
|
}
|
|
272
287
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kodo-checkpointer.js","sourceRoot":"","sources":["../../../src/ai/graph/kodo-checkpointer.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"kodo-checkpointer.js","sourceRoot":"","sources":["../../../src/ai/graph/kodo-checkpointer.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
* Implements the Checkpointer interface for serverless-compatible
|
|
5
5
|
* durable state management.
|
|
6
6
|
*
|
|
7
|
+
* **⚠️ Node.js Only**: This module uses `node:crypto` for HMAC-SHA1 signing
|
|
8
|
+
* and is NOT compatible with Edge runtimes (CloudFlare Workers, Vercel Edge).
|
|
9
|
+
* For Edge environments, use MemoryCheckpointer or implement a backend proxy.
|
|
10
|
+
*
|
|
11
|
+
* **Download Domain**: If your bucket doesn't have a default Kodo domain bound,
|
|
12
|
+
* you MUST provide `downloadDomain` to avoid 404 errors on load().
|
|
13
|
+
*
|
|
7
14
|
* @example
|
|
8
15
|
* ```typescript
|
|
9
16
|
* import { KodoCheckpointer } from '@bowenqt/qiniu-ai-sdk';
|
|
@@ -13,11 +20,8 @@
|
|
|
13
20
|
* accessKey: process.env.QINIU_ACCESS_KEY!,
|
|
14
21
|
* secretKey: process.env.QINIU_SECRET_KEY!,
|
|
15
22
|
* region: 'z0',
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* const graph = createAgentGraph({
|
|
19
|
-
* checkpointer,
|
|
20
|
-
* // ...
|
|
23
|
+
* // Required if bucket has no default Kodo domain
|
|
24
|
+
* downloadDomain: 'cdn.example.com',
|
|
21
25
|
* });
|
|
22
26
|
* ```
|
|
23
27
|
*/
|
|
@@ -46,21 +50,23 @@ class KodoClient {
|
|
|
46
50
|
prefix: config.prefix ?? 'checkpoints/',
|
|
47
51
|
tokenExpiry: config.tokenExpiry ?? 3600,
|
|
48
52
|
maxRetries: config.maxRetries ?? 3,
|
|
53
|
+
downloadDomain: config.downloadDomain,
|
|
49
54
|
};
|
|
50
55
|
this.hosts = REGION_HOSTS[this.config.region];
|
|
51
56
|
}
|
|
52
|
-
/** Generate upload token */
|
|
53
|
-
generateUploadToken(
|
|
57
|
+
/** Generate upload token (bucket-level scope for reuse) */
|
|
58
|
+
generateUploadToken() {
|
|
54
59
|
const now = Math.floor(Date.now() / 1000);
|
|
55
60
|
const deadline = now + this.config.tokenExpiry;
|
|
56
61
|
// Check cache
|
|
57
62
|
if (this.cachedToken && this.cachedToken.expiresAt > Date.now() + 60000) {
|
|
58
63
|
return this.cachedToken.token;
|
|
59
64
|
}
|
|
60
|
-
// PutPolicy
|
|
65
|
+
// PutPolicy with bucket-level scope (allows any key in bucket)
|
|
61
66
|
const policy = {
|
|
62
|
-
scope:
|
|
67
|
+
scope: this.config.bucket,
|
|
63
68
|
deadline,
|
|
69
|
+
insertOnly: 0, // Allow overwrite
|
|
64
70
|
};
|
|
65
71
|
const encodedPolicy = this.base64UrlSafe(JSON.stringify(policy));
|
|
66
72
|
const sign = this.hmacSha1(encodedPolicy);
|
|
@@ -106,7 +112,7 @@ class KodoClient {
|
|
|
106
112
|
}
|
|
107
113
|
/** Upload JSON data */
|
|
108
114
|
async upload(key, data) {
|
|
109
|
-
const token = this.generateUploadToken(
|
|
115
|
+
const token = this.generateUploadToken();
|
|
110
116
|
const body = JSON.stringify(data);
|
|
111
117
|
const formData = new FormData();
|
|
112
118
|
formData.append('token', token);
|
|
@@ -126,12 +132,15 @@ class KodoClient {
|
|
|
126
132
|
/** Download JSON data */
|
|
127
133
|
async download(key) {
|
|
128
134
|
return this.withRetry(async () => {
|
|
135
|
+
// Determine download domain
|
|
136
|
+
const domain = this.config.downloadDomain
|
|
137
|
+
?? `${this.config.bucket}.${this.hosts.io.replace('iovip', 'kodo')}`;
|
|
129
138
|
// Generate signed URL for private bucket
|
|
130
139
|
const deadline = Math.floor(Date.now() / 1000) + 3600;
|
|
131
|
-
const
|
|
132
|
-
const toSign = `${
|
|
140
|
+
const baseUrl = `https://${domain}/${encodeURIComponent(key)}`;
|
|
141
|
+
const toSign = `${baseUrl}?e=${deadline}`;
|
|
133
142
|
const sign = this.hmacSha1(toSign);
|
|
134
|
-
const signedUrl = `${
|
|
143
|
+
const signedUrl = `${baseUrl}?e=${deadline}&token=${this.config.accessKey}:${this.base64UrlSafe(sign)}`;
|
|
135
144
|
const response = await fetch(signedUrl);
|
|
136
145
|
if (response.status === 404) {
|
|
137
146
|
return null;
|
|
@@ -215,14 +224,31 @@ export class KodoCheckpointer {
|
|
|
215
224
|
async save(threadId, state, options) {
|
|
216
225
|
const key = this.client.getFullKey(threadId);
|
|
217
226
|
const serialized = this.serializeState(state);
|
|
227
|
+
// Extract options - handle both new CheckpointSaveOptions and legacy custom object
|
|
228
|
+
let status = 'active';
|
|
229
|
+
let pendingApproval;
|
|
230
|
+
let custom;
|
|
231
|
+
if (options) {
|
|
232
|
+
if ('status' in options || 'pendingApproval' in options || 'custom' in options) {
|
|
233
|
+
// New CheckpointSaveOptions format
|
|
234
|
+
const opts = options;
|
|
235
|
+
status = opts.status ?? 'active';
|
|
236
|
+
pendingApproval = opts.pendingApproval;
|
|
237
|
+
custom = opts.custom;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Legacy: treat entire object as custom metadata
|
|
241
|
+
custom = options;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
218
244
|
const metadata = {
|
|
219
|
-
id:
|
|
245
|
+
id: threadId, // Simplified: single checkpoint per thread
|
|
220
246
|
threadId,
|
|
221
247
|
createdAt: Date.now(),
|
|
222
248
|
stepCount: state.stepCount,
|
|
223
|
-
status
|
|
224
|
-
pendingApproval
|
|
225
|
-
custom
|
|
249
|
+
status,
|
|
250
|
+
pendingApproval,
|
|
251
|
+
custom,
|
|
226
252
|
};
|
|
227
253
|
const checkpoint = {
|
|
228
254
|
metadata,
|
|
@@ -239,31 +265,20 @@ export class KodoCheckpointer {
|
|
|
239
265
|
return this.client.download(key);
|
|
240
266
|
}
|
|
241
267
|
/**
|
|
242
|
-
* List
|
|
268
|
+
* List checkpoints for a specific thread.
|
|
269
|
+
* Since we store single checkpoint per thread, this returns 0 or 1 item.
|
|
243
270
|
*/
|
|
244
271
|
async list(threadId) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
? items.filter(item => item.key.includes(threadId))
|
|
249
|
-
: items;
|
|
250
|
-
// Download metadata for each
|
|
251
|
-
const metadataList = [];
|
|
252
|
-
for (const item of filtered) {
|
|
253
|
-
const checkpoint = await this.client.download(item.key);
|
|
254
|
-
if (checkpoint?.metadata) {
|
|
255
|
-
metadataList.push(checkpoint.metadata);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return metadataList.sort((a, b) => b.createdAt - a.createdAt);
|
|
272
|
+
// Direct download (single checkpoint per thread model)
|
|
273
|
+
const checkpoint = await this.load(threadId);
|
|
274
|
+
return checkpoint ? [checkpoint.metadata] : [];
|
|
259
275
|
}
|
|
260
276
|
/**
|
|
261
277
|
* Delete a single checkpoint.
|
|
262
278
|
*/
|
|
263
279
|
async delete(checkpointId) {
|
|
264
|
-
//
|
|
265
|
-
const
|
|
266
|
-
const key = this.client.getFullKey(threadId);
|
|
280
|
+
// For single checkpoint per thread, checkpointId is the threadId
|
|
281
|
+
const key = this.client.getFullKey(checkpointId);
|
|
267
282
|
return this.client.delete(key);
|
|
268
283
|
}
|
|
269
284
|
/**
|
package/package.json
CHANGED