@aigne/core 1.58.2 → 1.58.3
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/CHANGELOG.md +8 -0
- package/lib/cjs/agents/agent.d.ts +1 -1
- package/lib/cjs/agents/agent.js +2 -1
- package/lib/cjs/agents/chat-model.js +10 -8
- package/lib/cjs/loader/agent-yaml.js +4 -1
- package/lib/cjs/utils/json-schema.d.ts +1 -0
- package/lib/cjs/utils/json-schema.js +6 -0
- package/lib/cjs/utils/type-utils.d.ts +1 -0
- package/lib/cjs/utils/type-utils.js +19 -0
- package/lib/dts/agents/agent.d.ts +1 -1
- package/lib/dts/utils/json-schema.d.ts +1 -0
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/esm/agents/agent.d.ts +1 -1
- package/lib/esm/agents/agent.js +3 -2
- package/lib/esm/agents/chat-model.js +11 -9
- package/lib/esm/loader/agent-yaml.js +4 -1
- package/lib/esm/utils/json-schema.d.ts +1 -0
- package/lib/esm/utils/json-schema.js +5 -0
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/type-utils.js +18 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.58.3](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.58.2...core-v1.58.3) (2025-09-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* handle absolute paths in agent YAML prompt URLs ([#466](https://github.com/AIGNE-io/aigne-framework/issues/466)) ([a07a088](https://github.com/AIGNE-io/aigne-framework/commit/a07a0880728f65fc831578763b62ce5144d1aed8))
|
|
9
|
+
* support optional field sturectured output for gemini ([#468](https://github.com/AIGNE-io/aigne-framework/issues/468)) ([70c6279](https://github.com/AIGNE-io/aigne-framework/commit/70c62795039a2862e3333f26707329489bf938de))
|
|
10
|
+
|
|
3
11
|
## [1.58.2](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.58.1...core-v1.58.2) (2025-09-05)
|
|
4
12
|
|
|
5
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
2
|
import type * as prompts from "@inquirer/prompts";
|
|
3
|
-
import { ZodObject, type ZodType } from "zod";
|
|
3
|
+
import { type ZodObject, type ZodType } from "zod";
|
|
4
4
|
import type { AgentEvent, Context, UserContext } from "../aigne/context.js";
|
|
5
5
|
import type { MessagePayload } from "../aigne/message-queue.js";
|
|
6
6
|
import type { ContextUsage } from "../aigne/usage.js";
|
package/lib/cjs/agents/agent.js
CHANGED
|
@@ -51,6 +51,7 @@ const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
|
51
51
|
const nunjucks_1 = __importDefault(require("nunjucks"));
|
|
52
52
|
const zod_1 = require("zod");
|
|
53
53
|
const agent_utils_js_1 = require("../utils/agent-utils.js");
|
|
54
|
+
const json_schema_js_1 = require("../utils/json-schema.js");
|
|
54
55
|
const logger_js_1 = require("../utils/logger.js");
|
|
55
56
|
const stream_utils_js_1 = require("../utils/stream-utils.js");
|
|
56
57
|
const type_utils_js_1 = require("../utils/type-utils.js");
|
|
@@ -748,7 +749,7 @@ async function agentProcessResultToObject(response) {
|
|
|
748
749
|
: response;
|
|
749
750
|
}
|
|
750
751
|
function checkAgentInputOutputSchema(schema) {
|
|
751
|
-
if (
|
|
752
|
+
if (typeof schema !== "function" && !(0, json_schema_js_1.isZodSchema)(schema)) {
|
|
752
753
|
throw new Error(`schema must be a zod object or function return a zod object, got: ${typeof schema}`);
|
|
753
754
|
}
|
|
754
755
|
}
|
|
@@ -216,14 +216,6 @@ class ChatModel extends agent_js_1.Agent {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
-
if (input.responseFormat?.type === "json_schema" &&
|
|
220
|
-
// NOTE: Should not validate if there are tool calls
|
|
221
|
-
!output.toolCalls?.length) {
|
|
222
|
-
const ajv = new ajv_1.Ajv();
|
|
223
|
-
if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
|
|
224
|
-
throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
219
|
super.postprocess(input, output, options);
|
|
228
220
|
const { usage } = output;
|
|
229
221
|
if (usage) {
|
|
@@ -241,6 +233,16 @@ class ChatModel extends agent_js_1.Agent {
|
|
|
241
233
|
files: await Promise.all(files.map((file) => this.transformFileOutput(input, file, options))),
|
|
242
234
|
};
|
|
243
235
|
}
|
|
236
|
+
// Remove fields with `null` value for validation
|
|
237
|
+
output = (0, type_utils_js_1.omitByDeep)(output, (value) => (0, type_utils_js_1.isNil)(value));
|
|
238
|
+
if (input.responseFormat?.type === "json_schema" &&
|
|
239
|
+
// NOTE: Should not validate if there are tool calls
|
|
240
|
+
!output.toolCalls?.length) {
|
|
241
|
+
const ajv = new ajv_1.Ajv();
|
|
242
|
+
if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
|
|
243
|
+
throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
244
246
|
return super.processAgentOutput(input, output, options);
|
|
245
247
|
}
|
|
246
248
|
async transformFileOutput(input, data, options) {
|
|
@@ -59,7 +59,9 @@ async function parseAgentFile(path, data) {
|
|
|
59
59
|
])
|
|
60
60
|
.transform((v) => typeof v === "string"
|
|
61
61
|
? { content: v, path }
|
|
62
|
-
: Promise.resolve(index_js_1.nodejs.path.
|
|
62
|
+
: Promise.resolve(index_js_1.nodejs.path.isAbsolute(v.url)
|
|
63
|
+
? v.url
|
|
64
|
+
: index_js_1.nodejs.path.join(index_js_1.nodejs.path.dirname(path), v.url)).then((path) => index_js_1.nodejs.fs.readFile(path, "utf8").then((content) => ({ content, path }))));
|
|
63
65
|
return (0, schema_js_1.camelizeSchema)(zod_1.z.discriminatedUnion("type", [
|
|
64
66
|
zod_1.z
|
|
65
67
|
.object({
|
|
@@ -68,6 +70,7 @@ async function parseAgentFile(path, data) {
|
|
|
68
70
|
inputKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
|
|
69
71
|
outputKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
|
|
70
72
|
toolChoice: (0, schema_js_1.optionalize)(zod_1.z.nativeEnum(ai_agent_js_1.AIAgentToolChoice)),
|
|
73
|
+
structuredStreamMode: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
|
|
71
74
|
})
|
|
72
75
|
.extend(baseAgentSchema.shape),
|
|
73
76
|
zod_1.z
|
|
@@ -10,3 +10,4 @@ export declare function parseJSON(json: string): any;
|
|
|
10
10
|
* @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
|
|
11
11
|
*/
|
|
12
12
|
export declare function ensureZodUnionArray<T extends z.ZodType>(union: T[]): [T, T, ...T[]];
|
|
13
|
+
export declare function isZodSchema(schema: ZodType): schema is ZodType;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.outputSchemaToResponseFormatSchema = outputSchemaToResponseFormatSchema;
|
|
4
4
|
exports.parseJSON = parseJSON;
|
|
5
5
|
exports.ensureZodUnionArray = ensureZodUnionArray;
|
|
6
|
+
exports.isZodSchema = isZodSchema;
|
|
6
7
|
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
7
8
|
const logger_js_1 = require("./logger.js");
|
|
8
9
|
function outputSchemaToResponseFormatSchema(agentOutput) {
|
|
@@ -45,3 +46,8 @@ function ensureZodUnionArray(union) {
|
|
|
45
46
|
}
|
|
46
47
|
return union;
|
|
47
48
|
}
|
|
49
|
+
function isZodSchema(schema) {
|
|
50
|
+
if (!schema || typeof schema !== "object")
|
|
51
|
+
return false;
|
|
52
|
+
return typeof schema.parse === "function" && typeof schema.safeParse === "function";
|
|
53
|
+
}
|
|
@@ -19,6 +19,7 @@ export declare function pick<T extends object, K extends keyof T>(obj: T, ...key
|
|
|
19
19
|
export declare function omit<T extends object, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
|
|
20
20
|
export declare function omitDeep<T, K>(obj: T, ...keys: (K | K[])[]): unknown;
|
|
21
21
|
export declare function omitBy<T extends object, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
22
|
+
export declare function omitByDeep(obj: any, predicate: (value: any, key: any) => boolean): any;
|
|
22
23
|
export declare function flat<T>(...value: (T | T[])[]): NonNullable<T>[];
|
|
23
24
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
24
25
|
[key: string]: T;
|
|
@@ -12,6 +12,7 @@ exports.pick = pick;
|
|
|
12
12
|
exports.omit = omit;
|
|
13
13
|
exports.omitDeep = omitDeep;
|
|
14
14
|
exports.omitBy = omitBy;
|
|
15
|
+
exports.omitByDeep = omitByDeep;
|
|
15
16
|
exports.flat = flat;
|
|
16
17
|
exports.createAccessorArray = createAccessorArray;
|
|
17
18
|
exports.checkArguments = checkArguments;
|
|
@@ -102,6 +103,24 @@ function omitBy(obj, predicate) {
|
|
|
102
103
|
return !predicate(value, k);
|
|
103
104
|
}));
|
|
104
105
|
}
|
|
106
|
+
function omitByDeep(obj, predicate) {
|
|
107
|
+
if (obj === null || obj === undefined)
|
|
108
|
+
return obj;
|
|
109
|
+
if (Array.isArray(obj)) {
|
|
110
|
+
return obj.map((item) => omitByDeep(item, predicate));
|
|
111
|
+
}
|
|
112
|
+
if (typeof obj === "object") {
|
|
113
|
+
const result = {};
|
|
114
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
115
|
+
const newValue = omitByDeep(value, predicate);
|
|
116
|
+
if (!predicate(newValue, key)) {
|
|
117
|
+
result[key] = newValue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
return obj;
|
|
123
|
+
}
|
|
105
124
|
function flat(...value) {
|
|
106
125
|
return value.flat().filter(isNonNullable);
|
|
107
126
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
2
|
import type * as prompts from "@inquirer/prompts";
|
|
3
|
-
import { ZodObject, type ZodType } from "zod";
|
|
3
|
+
import { type ZodObject, type ZodType } from "zod";
|
|
4
4
|
import type { AgentEvent, Context, UserContext } from "../aigne/context.js";
|
|
5
5
|
import type { MessagePayload } from "../aigne/message-queue.js";
|
|
6
6
|
import type { ContextUsage } from "../aigne/usage.js";
|
|
@@ -10,3 +10,4 @@ export declare function parseJSON(json: string): any;
|
|
|
10
10
|
* @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
|
|
11
11
|
*/
|
|
12
12
|
export declare function ensureZodUnionArray<T extends z.ZodType>(union: T[]): [T, T, ...T[]];
|
|
13
|
+
export declare function isZodSchema(schema: ZodType): schema is ZodType;
|
|
@@ -19,6 +19,7 @@ export declare function pick<T extends object, K extends keyof T>(obj: T, ...key
|
|
|
19
19
|
export declare function omit<T extends object, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
|
|
20
20
|
export declare function omitDeep<T, K>(obj: T, ...keys: (K | K[])[]): unknown;
|
|
21
21
|
export declare function omitBy<T extends object, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
22
|
+
export declare function omitByDeep(obj: any, predicate: (value: any, key: any) => boolean): any;
|
|
22
23
|
export declare function flat<T>(...value: (T | T[])[]): NonNullable<T>[];
|
|
23
24
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
24
25
|
[key: string]: T;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
2
|
import type * as prompts from "@inquirer/prompts";
|
|
3
|
-
import { ZodObject, type ZodType } from "zod";
|
|
3
|
+
import { type ZodObject, type ZodType } from "zod";
|
|
4
4
|
import type { AgentEvent, Context, UserContext } from "../aigne/context.js";
|
|
5
5
|
import type { MessagePayload } from "../aigne/message-queue.js";
|
|
6
6
|
import type { ContextUsage } from "../aigne/usage.js";
|
package/lib/esm/agents/agent.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
2
|
import equal from "fast-deep-equal";
|
|
3
3
|
import nunjucks from "nunjucks";
|
|
4
|
-
import {
|
|
4
|
+
import { z } from "zod";
|
|
5
5
|
import { sortHooks } from "../utils/agent-utils.js";
|
|
6
|
+
import { isZodSchema } from "../utils/json-schema.js";
|
|
6
7
|
import { logger } from "../utils/logger.js";
|
|
7
8
|
import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, mergeAgentResponseChunk, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
|
|
8
9
|
import { checkArguments, createAccessorArray, flat, isEmpty, isNil, isNonNullable, isRecord, } from "../utils/type-utils.js";
|
|
@@ -699,7 +700,7 @@ export async function agentProcessResultToObject(response) {
|
|
|
699
700
|
: response;
|
|
700
701
|
}
|
|
701
702
|
function checkAgentInputOutputSchema(schema) {
|
|
702
|
-
if (
|
|
703
|
+
if (typeof schema !== "function" && !isZodSchema(schema)) {
|
|
703
704
|
throw new Error(`schema must be a zod object or function return a zod object, got: ${typeof schema}`);
|
|
704
705
|
}
|
|
705
706
|
}
|
|
@@ -4,7 +4,7 @@ import mime from "mime";
|
|
|
4
4
|
import { v7 } from "uuid";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { optionalize } from "../loader/schema.js";
|
|
7
|
-
import { checkArguments, pick } from "../utils/type-utils.js";
|
|
7
|
+
import { checkArguments, isNil, omitByDeep, pick, } from "../utils/type-utils.js";
|
|
8
8
|
import { Agent, agentOptionsSchema, } from "./agent.js";
|
|
9
9
|
const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
|
|
10
10
|
retries: 3,
|
|
@@ -176,14 +176,6 @@ export class ChatModel extends Agent {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
if (input.responseFormat?.type === "json_schema" &&
|
|
180
|
-
// NOTE: Should not validate if there are tool calls
|
|
181
|
-
!output.toolCalls?.length) {
|
|
182
|
-
const ajv = new Ajv();
|
|
183
|
-
if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
|
|
184
|
-
throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
179
|
super.postprocess(input, output, options);
|
|
188
180
|
const { usage } = output;
|
|
189
181
|
if (usage) {
|
|
@@ -201,6 +193,16 @@ export class ChatModel extends Agent {
|
|
|
201
193
|
files: await Promise.all(files.map((file) => this.transformFileOutput(input, file, options))),
|
|
202
194
|
};
|
|
203
195
|
}
|
|
196
|
+
// Remove fields with `null` value for validation
|
|
197
|
+
output = omitByDeep(output, (value) => isNil(value));
|
|
198
|
+
if (input.responseFormat?.type === "json_schema" &&
|
|
199
|
+
// NOTE: Should not validate if there are tool calls
|
|
200
|
+
!output.toolCalls?.length) {
|
|
201
|
+
const ajv = new Ajv();
|
|
202
|
+
if (!ajv.validate(input.responseFormat.jsonSchema.schema, output.json)) {
|
|
203
|
+
throw new StructuredOutputError(`Output JSON does not conform to the provided JSON schema: ${ajv.errorsText()}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
204
206
|
return super.processAgentOutput(input, output, options);
|
|
205
207
|
}
|
|
206
208
|
async transformFileOutput(input, data, options) {
|
|
@@ -55,7 +55,9 @@ export async function parseAgentFile(path, data) {
|
|
|
55
55
|
])
|
|
56
56
|
.transform((v) => typeof v === "string"
|
|
57
57
|
? { content: v, path }
|
|
58
|
-
: Promise.resolve(nodejs.path.
|
|
58
|
+
: Promise.resolve(nodejs.path.isAbsolute(v.url)
|
|
59
|
+
? v.url
|
|
60
|
+
: nodejs.path.join(nodejs.path.dirname(path), v.url)).then((path) => nodejs.fs.readFile(path, "utf8").then((content) => ({ content, path }))));
|
|
59
61
|
return camelizeSchema(z.discriminatedUnion("type", [
|
|
60
62
|
z
|
|
61
63
|
.object({
|
|
@@ -64,6 +66,7 @@ export async function parseAgentFile(path, data) {
|
|
|
64
66
|
inputKey: optionalize(z.string()),
|
|
65
67
|
outputKey: optionalize(z.string()),
|
|
66
68
|
toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
|
|
69
|
+
structuredStreamMode: optionalize(z.boolean()),
|
|
67
70
|
})
|
|
68
71
|
.extend(baseAgentSchema.shape),
|
|
69
72
|
z
|
|
@@ -10,3 +10,4 @@ export declare function parseJSON(json: string): any;
|
|
|
10
10
|
* @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
|
|
11
11
|
*/
|
|
12
12
|
export declare function ensureZodUnionArray<T extends z.ZodType>(union: T[]): [T, T, ...T[]];
|
|
13
|
+
export declare function isZodSchema(schema: ZodType): schema is ZodType;
|
|
@@ -40,3 +40,8 @@ export function ensureZodUnionArray(union) {
|
|
|
40
40
|
}
|
|
41
41
|
return union;
|
|
42
42
|
}
|
|
43
|
+
export function isZodSchema(schema) {
|
|
44
|
+
if (!schema || typeof schema !== "object")
|
|
45
|
+
return false;
|
|
46
|
+
return typeof schema.parse === "function" && typeof schema.safeParse === "function";
|
|
47
|
+
}
|
|
@@ -19,6 +19,7 @@ export declare function pick<T extends object, K extends keyof T>(obj: T, ...key
|
|
|
19
19
|
export declare function omit<T extends object, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
|
|
20
20
|
export declare function omitDeep<T, K>(obj: T, ...keys: (K | K[])[]): unknown;
|
|
21
21
|
export declare function omitBy<T extends object, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
22
|
+
export declare function omitByDeep(obj: any, predicate: (value: any, key: any) => boolean): any;
|
|
22
23
|
export declare function flat<T>(...value: (T | T[])[]): NonNullable<T>[];
|
|
23
24
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
24
25
|
[key: string]: T;
|
|
@@ -84,6 +84,24 @@ export function omitBy(obj, predicate) {
|
|
|
84
84
|
return !predicate(value, k);
|
|
85
85
|
}));
|
|
86
86
|
}
|
|
87
|
+
export function omitByDeep(obj, predicate) {
|
|
88
|
+
if (obj === null || obj === undefined)
|
|
89
|
+
return obj;
|
|
90
|
+
if (Array.isArray(obj)) {
|
|
91
|
+
return obj.map((item) => omitByDeep(item, predicate));
|
|
92
|
+
}
|
|
93
|
+
if (typeof obj === "object") {
|
|
94
|
+
const result = {};
|
|
95
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
96
|
+
const newValue = omitByDeep(value, predicate);
|
|
97
|
+
if (!predicate(newValue, key)) {
|
|
98
|
+
result[key] = newValue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
return obj;
|
|
104
|
+
}
|
|
87
105
|
export function flat(...value) {
|
|
88
106
|
return value.flat().filter(isNonNullable);
|
|
89
107
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/core",
|
|
3
|
-
"version": "1.58.
|
|
3
|
+
"version": "1.58.3",
|
|
4
4
|
"description": "The functional core of agentic AI",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
"yaml": "^2.8.0",
|
|
93
93
|
"zod": "^3.25.67",
|
|
94
94
|
"zod-to-json-schema": "^3.24.6",
|
|
95
|
-
"@aigne/
|
|
96
|
-
"@aigne/
|
|
95
|
+
"@aigne/platform-helpers": "^0.6.2",
|
|
96
|
+
"@aigne/observability-api": "^0.10.2"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@types/bun": "^1.2.18",
|