@raintree-technology/perps 0.1.2 → 0.1.4
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 +19 -1
- package/README.md +42 -4
- package/dist/adapters/aevo.d.ts +1 -6
- package/dist/adapters/aevo.js +0 -21
- package/dist/adapters/certification.js +55 -9
- package/dist/adapters/decibel/order-manager.d.ts +41 -0
- package/dist/adapters/decibel/order-manager.js +216 -0
- package/dist/adapters/decibel/rest-client.d.ts +21 -0
- package/dist/adapters/decibel/rest-client.js +15 -8
- package/dist/adapters/decibel/ws-feed.js +19 -4
- package/dist/adapters/decibel.d.ts +15 -10
- package/dist/adapters/decibel.js +371 -50
- package/dist/adapters/hyperliquid.d.ts +1 -6
- package/dist/adapters/hyperliquid.js +0 -18
- package/dist/adapters/interface.d.ts +15 -14
- package/dist/adapters/orderly.d.ts +1 -9
- package/dist/adapters/orderly.js +0 -33
- package/dist/adapters/paradex.d.ts +1 -9
- package/dist/adapters/paradex.js +1 -34
- package/dist/cli/experience.js +0 -1
- package/dist/cli/program.js +28 -5
- package/dist/commands/arb/alert.d.ts +0 -4
- package/dist/commands/arb/alert.js +4 -14
- package/dist/commands/markets/index.js +2 -0
- package/dist/commands/markets/read-simple.d.ts +2 -0
- package/dist/commands/markets/read-simple.js +130 -0
- package/dist/commands/order/advanced-simple.d.ts +2 -0
- package/dist/commands/order/advanced-simple.js +820 -0
- package/dist/commands/order/index.js +2 -0
- package/dist/index.js +35 -1
- package/dist/lib/exit-codes.js +5 -1
- package/dist/lib/prompts.d.ts +0 -18
- package/dist/lib/prompts.js +1 -22
- package/dist/lib/schema.d.ts +8 -8
- package/dist/lib/schema.js +47 -8
- package/package.json +5 -2
|
@@ -2,6 +2,7 @@ import { registerLimitOrderSimpleCommand } from "./limit-simple.js";
|
|
|
2
2
|
import { registerMarketOrderSimpleCommand } from "./market-simple.js";
|
|
3
3
|
import { registerCancelOrderSimpleCommand } from "./cancel-simple.js";
|
|
4
4
|
import { registerTriggerOrderSimpleCommands } from "./trigger-simple.js";
|
|
5
|
+
import { registerAdvancedOrderSimpleCommands } from "./advanced-simple.js";
|
|
5
6
|
export function registerOrderCommands(program) {
|
|
6
7
|
const order = program
|
|
7
8
|
.command("order")
|
|
@@ -10,4 +11,5 @@ export function registerOrderCommands(program) {
|
|
|
10
11
|
registerMarketOrderSimpleCommand(order);
|
|
11
12
|
registerTriggerOrderSimpleCommands(order);
|
|
12
13
|
registerCancelOrderSimpleCommand(order);
|
|
14
|
+
registerAdvancedOrderSimpleCommands(order);
|
|
13
15
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createProgram } from "./cli/program.js";
|
|
3
|
+
import { withJsonContract } from "./lib/contracts.js";
|
|
4
|
+
import { inferExitCode } from "./lib/exit-codes.js";
|
|
5
|
+
const isJsonMode = process.argv.includes("--json");
|
|
3
6
|
const program = createProgram();
|
|
4
|
-
|
|
7
|
+
if (isJsonMode) {
|
|
8
|
+
program.configureOutput({
|
|
9
|
+
writeOut: () => undefined,
|
|
10
|
+
writeErr: () => undefined,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
await program.parseAsync(process.argv);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18
|
+
const commanderExitCode = typeof err === "object" && err !== null && "exitCode" in err
|
|
19
|
+
? Number(err.exitCode)
|
|
20
|
+
: Number.NaN;
|
|
21
|
+
const commanderCode = typeof err === "object" && err !== null && "code" in err
|
|
22
|
+
? String(err.code ?? "")
|
|
23
|
+
: "";
|
|
24
|
+
const exitCode = Number.isFinite(commanderExitCode) && commanderExitCode === 0
|
|
25
|
+
? 0
|
|
26
|
+
: commanderCode.startsWith("commander.")
|
|
27
|
+
? inferExitCode(err)
|
|
28
|
+
: Number.isFinite(commanderExitCode)
|
|
29
|
+
? commanderExitCode
|
|
30
|
+
: inferExitCode(err);
|
|
31
|
+
if (isJsonMode && exitCode !== 0) {
|
|
32
|
+
console.error(JSON.stringify(withJsonContract("perps.error", {
|
|
33
|
+
status: "error",
|
|
34
|
+
error: { message, exitCode },
|
|
35
|
+
}), null, 2));
|
|
36
|
+
}
|
|
37
|
+
process.exit(exitCode);
|
|
38
|
+
}
|
package/dist/lib/exit-codes.js
CHANGED
|
@@ -25,7 +25,11 @@ export function inferExitCode(err) {
|
|
|
25
25
|
const message = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();
|
|
26
26
|
if (message.includes("must be") ||
|
|
27
27
|
message.includes("invalid") ||
|
|
28
|
-
message.includes("use --yes with --json")
|
|
28
|
+
message.includes("use --yes with --json") ||
|
|
29
|
+
message.includes("unknown option") ||
|
|
30
|
+
message.includes("too many arguments") ||
|
|
31
|
+
message.includes("missing required argument") ||
|
|
32
|
+
message.includes("option argument missing")) {
|
|
29
33
|
return EXIT_CODES.VALIDATION_ERROR;
|
|
30
34
|
}
|
|
31
35
|
if (message.includes("not configured") ||
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt for text input
|
|
3
|
-
*/
|
|
4
1
|
export declare function prompt(question: string): Promise<string>;
|
|
5
|
-
/**
|
|
6
|
-
* Prompt for selection from a list of options with arrow key navigation
|
|
7
|
-
*/
|
|
8
2
|
export declare function select<T extends string>(question: string, options: {
|
|
9
3
|
value: T;
|
|
10
4
|
label: string;
|
|
11
5
|
description?: string;
|
|
12
6
|
}[]): Promise<T>;
|
|
13
|
-
/**
|
|
14
|
-
* Prompt for multiple selections with checkboxes
|
|
15
|
-
*/
|
|
16
7
|
export declare function multiSelect<T extends string>(question: string, options: {
|
|
17
8
|
value: T;
|
|
18
9
|
label: string;
|
|
19
10
|
description?: string;
|
|
20
11
|
}[]): Promise<T[]>;
|
|
21
|
-
/**
|
|
22
|
-
* Prompt for yes/no confirmation
|
|
23
|
-
*/
|
|
24
12
|
export declare function confirm(question: string, defaultValue?: boolean): Promise<boolean>;
|
|
25
|
-
/**
|
|
26
|
-
* Wait for user to press Enter
|
|
27
|
-
*/
|
|
28
13
|
export declare function waitForEnter(message?: string): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Wait for user to press Enter (returns true) or Escape (returns false)
|
|
31
|
-
*/
|
|
32
14
|
export declare function pressEnterOrEsc(message: string): Promise<boolean>;
|
package/dist/lib/prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { highlighter, inquirerTheme } from "./ui-tokens.js";
|
|
2
2
|
let promptsModule = null;
|
|
3
3
|
async function loadPrompts() {
|
|
4
4
|
if (promptsModule)
|
|
@@ -6,9 +6,6 @@ async function loadPrompts() {
|
|
|
6
6
|
promptsModule = await import("@inquirer/prompts");
|
|
7
7
|
return promptsModule;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Prompt for text input
|
|
11
|
-
*/
|
|
12
9
|
export async function prompt(question) {
|
|
13
10
|
const { input } = await loadPrompts();
|
|
14
11
|
const answer = await input({
|
|
@@ -17,9 +14,6 @@ export async function prompt(question) {
|
|
|
17
14
|
});
|
|
18
15
|
return answer.trim();
|
|
19
16
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Prompt for selection from a list of options with arrow key navigation
|
|
22
|
-
*/
|
|
23
17
|
export async function select(question, options) {
|
|
24
18
|
const { select: inquirerSelect } = await loadPrompts();
|
|
25
19
|
const result = await inquirerSelect({
|
|
@@ -33,9 +27,6 @@ export async function select(question, options) {
|
|
|
33
27
|
});
|
|
34
28
|
return result;
|
|
35
29
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Prompt for multiple selections with checkboxes
|
|
38
|
-
*/
|
|
39
30
|
export async function multiSelect(question, options) {
|
|
40
31
|
const { checkbox } = await loadPrompts();
|
|
41
32
|
const results = await checkbox({
|
|
@@ -49,9 +40,6 @@ export async function multiSelect(question, options) {
|
|
|
49
40
|
});
|
|
50
41
|
return results;
|
|
51
42
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Prompt for yes/no confirmation
|
|
54
|
-
*/
|
|
55
43
|
export async function confirm(question, defaultValue = false) {
|
|
56
44
|
const { confirm: inquirerConfirm } = await loadPrompts();
|
|
57
45
|
return inquirerConfirm({
|
|
@@ -60,9 +48,6 @@ export async function confirm(question, defaultValue = false) {
|
|
|
60
48
|
theme: inquirerTheme,
|
|
61
49
|
});
|
|
62
50
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Wait for user to press Enter
|
|
65
|
-
*/
|
|
66
51
|
export async function waitForEnter(message = "Press Enter to continue...") {
|
|
67
52
|
const { input } = await loadPrompts();
|
|
68
53
|
await input({
|
|
@@ -76,9 +61,6 @@ export async function waitForEnter(message = "Press Enter to continue...") {
|
|
|
76
61
|
},
|
|
77
62
|
});
|
|
78
63
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Wait for user to press Enter (returns true) or Escape (returns false)
|
|
81
|
-
*/
|
|
82
64
|
export async function pressEnterOrEsc(message) {
|
|
83
65
|
return new Promise((resolve) => {
|
|
84
66
|
console.log(`${highlighter.warn("→")} ${message}`);
|
|
@@ -88,19 +70,16 @@ export async function pressEnterOrEsc(message) {
|
|
|
88
70
|
stdin.resume();
|
|
89
71
|
const onData = (key) => {
|
|
90
72
|
const char = key.toString();
|
|
91
|
-
// Enter key
|
|
92
73
|
if (char === "\r" || char === "\n") {
|
|
93
74
|
cleanup();
|
|
94
75
|
console.log(`${highlighter.success("✔")} Opening browser...`);
|
|
95
76
|
resolve(true);
|
|
96
77
|
}
|
|
97
|
-
// Escape key
|
|
98
78
|
else if (char === "\x1b") {
|
|
99
79
|
cleanup();
|
|
100
80
|
console.log(`${highlighter.dimWhite("✔")} Skipped`);
|
|
101
81
|
resolve(false);
|
|
102
82
|
}
|
|
103
|
-
// Ctrl+C
|
|
104
83
|
else if (char === "\x03") {
|
|
105
84
|
cleanup();
|
|
106
85
|
process.exit(0);
|
package/dist/lib/schema.d.ts
CHANGED
|
@@ -5,15 +5,15 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
5
5
|
HYPERLIQUID_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
|
|
6
6
|
DECIBEL_API_WALLET_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
7
7
|
DECIBEL_API_WALLET_ADDRESS: z.ZodOptional<z.ZodString>;
|
|
8
|
-
DECIBEL_API_BEARER_TOKEN: z.ZodOptional<z.ZodString
|
|
8
|
+
DECIBEL_API_BEARER_TOKEN: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
9
9
|
DECIBEL_SUBACCOUNT_ADDRESS: z.ZodOptional<z.ZodString>;
|
|
10
10
|
DECIBEL_PACKAGE_ADDRESS: z.ZodOptional<z.ZodString>;
|
|
11
11
|
DECIBEL_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
12
12
|
DECIBEL_WS_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
13
13
|
DECIBEL_FULLNODE_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
14
14
|
DECIBEL_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
|
|
15
|
-
AEVO_API_KEY: z.ZodOptional<z.ZodString
|
|
16
|
-
AEVO_API_SECRET: z.ZodOptional<z.ZodString
|
|
15
|
+
AEVO_API_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
16
|
+
AEVO_API_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
17
17
|
AEVO_SIGNING_KEY: z.ZodOptional<z.ZodString>;
|
|
18
18
|
AEVO_ACCOUNT_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
19
19
|
AEVO_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
@@ -21,13 +21,13 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
21
21
|
ORDERLY_BROKER_ID: z.ZodOptional<z.ZodString>;
|
|
22
22
|
ORDERLY_CHAIN_ID: z.ZodOptional<z.ZodString>;
|
|
23
23
|
ORDERLY_ACCOUNT_ID: z.ZodOptional<z.ZodString>;
|
|
24
|
-
ORDERLY_KEY: z.ZodOptional<z.ZodString
|
|
25
|
-
ORDERLY_SECRET: z.ZodOptional<z.ZodString
|
|
26
|
-
ORDERLY_TRADING_KEY: z.ZodOptional<z.ZodString
|
|
27
|
-
ORDERLY_TRADING_SECRET: z.ZodOptional<z.ZodString
|
|
24
|
+
ORDERLY_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
25
|
+
ORDERLY_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
26
|
+
ORDERLY_TRADING_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
27
|
+
ORDERLY_TRADING_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
28
28
|
ORDERLY_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
29
29
|
ORDERLY_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
|
|
30
|
-
PARADEX_API_BEARER_TOKEN: z.ZodOptional<z.ZodString
|
|
30
|
+
PARADEX_API_BEARER_TOKEN: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
31
31
|
PARADEX_ACCOUNT_ADDRESS: z.ZodOptional<z.ZodString>;
|
|
32
32
|
PARADEX_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
33
33
|
PARADEX_ETHEREUM_ACCOUNT: z.ZodOptional<z.ZodString>;
|
package/dist/lib/schema.js
CHANGED
|
@@ -11,6 +11,45 @@ const aptosAddress = z
|
|
|
11
11
|
const starknetFieldElement = z
|
|
12
12
|
.string()
|
|
13
13
|
.regex(/^0x[a-fA-F0-9]{1,64}$/, "must be a 0x-prefixed Starknet field element");
|
|
14
|
+
const PLACEHOLDER_SECRET_VALUES = new Set([
|
|
15
|
+
"...",
|
|
16
|
+
"your-api-key",
|
|
17
|
+
"your_api_key",
|
|
18
|
+
"your-api-secret",
|
|
19
|
+
"your_api_secret",
|
|
20
|
+
"your-token",
|
|
21
|
+
"your_token",
|
|
22
|
+
"your-secret",
|
|
23
|
+
"your_secret",
|
|
24
|
+
"api-key-here",
|
|
25
|
+
"api_key_here",
|
|
26
|
+
"token-here",
|
|
27
|
+
"token_here",
|
|
28
|
+
"secret-here",
|
|
29
|
+
"secret_here",
|
|
30
|
+
"changeme",
|
|
31
|
+
"change-me",
|
|
32
|
+
"change_me",
|
|
33
|
+
"replace-me",
|
|
34
|
+
"replace_me",
|
|
35
|
+
"placeholder",
|
|
36
|
+
]);
|
|
37
|
+
function isPlaceholderSecret(value) {
|
|
38
|
+
const normalized = value.trim().toLowerCase();
|
|
39
|
+
if (!normalized) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const canonical = normalized.startsWith("<") && normalized.endsWith(">")
|
|
43
|
+
? normalized.slice(1, -1)
|
|
44
|
+
: normalized;
|
|
45
|
+
return PLACEHOLDER_SECRET_VALUES.has(canonical);
|
|
46
|
+
}
|
|
47
|
+
const userProvidedSecret = z
|
|
48
|
+
.string()
|
|
49
|
+
.min(1)
|
|
50
|
+
.refine((value) => !isPlaceholderSecret(value), {
|
|
51
|
+
message: "must be user-provided (placeholder values like '...' are not allowed)",
|
|
52
|
+
});
|
|
14
53
|
function isLoopbackHost(hostname) {
|
|
15
54
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
16
55
|
}
|
|
@@ -72,7 +111,7 @@ export const envSchema = z
|
|
|
72
111
|
// Decibel credentials/settings
|
|
73
112
|
DECIBEL_API_WALLET_PRIVATE_KEY: hexPrivateKey.optional(),
|
|
74
113
|
DECIBEL_API_WALLET_ADDRESS: aptosAddress.optional(),
|
|
75
|
-
DECIBEL_API_BEARER_TOKEN:
|
|
114
|
+
DECIBEL_API_BEARER_TOKEN: userProvidedSecret.optional(),
|
|
76
115
|
DECIBEL_SUBACCOUNT_ADDRESS: aptosAddress.optional(),
|
|
77
116
|
DECIBEL_PACKAGE_ADDRESS: aptosAddress.optional(),
|
|
78
117
|
DECIBEL_REST_URL: secureHttpUrl.optional(),
|
|
@@ -80,8 +119,8 @@ export const envSchema = z
|
|
|
80
119
|
DECIBEL_FULLNODE_URL: secureHttpUrl.optional(),
|
|
81
120
|
DECIBEL_NETWORK: z.enum(["testnet", "mainnet"]).optional(),
|
|
82
121
|
// Aevo credentials
|
|
83
|
-
AEVO_API_KEY:
|
|
84
|
-
AEVO_API_SECRET:
|
|
122
|
+
AEVO_API_KEY: userProvidedSecret.optional(),
|
|
123
|
+
AEVO_API_SECRET: userProvidedSecret.optional(),
|
|
85
124
|
AEVO_SIGNING_KEY: hexPrivateKey.optional(),
|
|
86
125
|
AEVO_ACCOUNT_PRIVATE_KEY: hexPrivateKey.optional(),
|
|
87
126
|
AEVO_REST_URL: secureHttpUrl.optional(),
|
|
@@ -90,14 +129,14 @@ export const envSchema = z
|
|
|
90
129
|
ORDERLY_BROKER_ID: z.string().min(1).optional(),
|
|
91
130
|
ORDERLY_CHAIN_ID: z.string().min(1).optional(),
|
|
92
131
|
ORDERLY_ACCOUNT_ID: z.string().min(1).optional(),
|
|
93
|
-
ORDERLY_KEY:
|
|
94
|
-
ORDERLY_SECRET:
|
|
95
|
-
ORDERLY_TRADING_KEY:
|
|
96
|
-
ORDERLY_TRADING_SECRET:
|
|
132
|
+
ORDERLY_KEY: userProvidedSecret.optional(),
|
|
133
|
+
ORDERLY_SECRET: userProvidedSecret.optional(),
|
|
134
|
+
ORDERLY_TRADING_KEY: userProvidedSecret.optional(),
|
|
135
|
+
ORDERLY_TRADING_SECRET: userProvidedSecret.optional(),
|
|
97
136
|
ORDERLY_REST_URL: secureHttpUrl.optional(),
|
|
98
137
|
ORDERLY_NETWORK: z.enum(["testnet", "mainnet"]).optional(),
|
|
99
138
|
// Paradex credentials
|
|
100
|
-
PARADEX_API_BEARER_TOKEN:
|
|
139
|
+
PARADEX_API_BEARER_TOKEN: userProvidedSecret.optional(),
|
|
101
140
|
PARADEX_ACCOUNT_ADDRESS: starknetFieldElement.optional(),
|
|
102
141
|
PARADEX_PRIVATE_KEY: starknetFieldElement.optional(),
|
|
103
142
|
PARADEX_ETHEREUM_ACCOUNT: hexAddress.optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raintree-technology/perps",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Universal CLI for perpetual DEXes",
|
|
5
5
|
"author": "Raintree Technology (https://raintree.technology)",
|
|
6
6
|
"contributors": [
|
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
"sideEffects": false,
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "tsc",
|
|
66
|
+
"prepare": "npm run build",
|
|
67
|
+
"prepack": "npm run build",
|
|
66
68
|
"dev": "tsx src/index.ts",
|
|
67
69
|
"typecheck": "tsc --noEmit",
|
|
68
70
|
"smoke": "node scripts/smoke.mjs",
|
|
@@ -82,7 +84,7 @@
|
|
|
82
84
|
"lint:fix": "biome lint --write src scripts",
|
|
83
85
|
"publint": "publint",
|
|
84
86
|
"attw": "attw --pack . --profile esm-only",
|
|
85
|
-
"pack:check": "npm pack --dry-run",
|
|
87
|
+
"pack:check": "npm run build && npm pack --dry-run && node scripts/verify-pack.mjs",
|
|
86
88
|
"prepublishOnly": "npm run build && npm run publint && npm run attw"
|
|
87
89
|
},
|
|
88
90
|
"dependencies": {
|
|
@@ -105,6 +107,7 @@
|
|
|
105
107
|
"zod": "^3.24.0"
|
|
106
108
|
},
|
|
107
109
|
"devDependencies": {
|
|
110
|
+
"next": "^16.1.6",
|
|
108
111
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
109
112
|
"@biomejs/biome": "^1.9.4",
|
|
110
113
|
"@types/better-sqlite3": "^7.6.13",
|