@ascendkit/cli 0.2.6 → 0.3.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/dist/api/client.d.ts +1 -0
- package/dist/api/client.js +31 -8
- package/dist/cli.js +713 -384
- package/dist/commands/content.js +4 -4
- package/dist/commands/email.d.ts +62 -6
- package/dist/commands/email.js +26 -17
- package/dist/commands/journeys.d.ts +1 -0
- package/dist/commands/journeys.js +9 -6
- package/dist/commands/platform.d.ts +22 -2
- package/dist/commands/platform.js +203 -101
- package/dist/commands/surveys.js +5 -5
- package/dist/mcp.js +31 -3
- package/dist/tools/auth.js +32 -11
- package/dist/tools/content.js +24 -12
- package/dist/tools/email.js +47 -17
- package/dist/tools/import.js +9 -9
- package/dist/tools/journeys.js +21 -14
- package/dist/tools/platform.js +125 -10
- package/dist/tools/surveys.js +9 -9
- package/dist/utils/journey-format.d.ts +6 -0
- package/dist/utils/journey-format.js +6 -4
- package/dist/utils/survey-format.js +2 -2
- package/package.json +5 -5
package/dist/tools/platform.js
CHANGED
|
@@ -15,13 +15,47 @@ export function registerPlatformTools(server, client) {
|
|
|
15
15
|
],
|
|
16
16
|
};
|
|
17
17
|
});
|
|
18
|
-
server.tool("
|
|
18
|
+
server.tool("ascendkit_set_env", "Set the active environment by public key and persist the shared .ascendkit environment context used by both MCP and CLI.", {
|
|
19
|
+
publicKey: z.string().describe("Environment public key (pk_dev_..., pk_beta_..., or pk_prod_...)"),
|
|
20
|
+
}, async (params) => {
|
|
21
|
+
try {
|
|
22
|
+
const data = await platform.mcpSetEnvironmentByPublicKey(client, params.publicKey);
|
|
23
|
+
return {
|
|
24
|
+
content: [{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: `Active environment: ${data.environment.name} (${data.environment.tier})\n` +
|
|
27
|
+
`Project: ${data.project.name}\n` +
|
|
28
|
+
`Public key: ${data.environment.publicKey}\n` +
|
|
29
|
+
`Updated env files: ${Array.isArray(data.updatedFiles) && data.updatedFiles.length > 0 ? data.updatedFiles.join(", ") : "(none)"}`,
|
|
30
|
+
}],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
35
|
+
const jsonMatch = message.match(/\{.*\}/s);
|
|
36
|
+
if (jsonMatch) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
39
|
+
if (parsed.error)
|
|
40
|
+
message = parsed.error;
|
|
41
|
+
else if (parsed.detail)
|
|
42
|
+
message = parsed.detail;
|
|
43
|
+
}
|
|
44
|
+
catch { /* use raw message */ }
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: message }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
server.tool("project_list", "List all projects in your organization", {}, async () => {
|
|
19
53
|
const data = await platform.mcpListProjects(client);
|
|
20
54
|
return {
|
|
21
55
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
22
56
|
};
|
|
23
57
|
});
|
|
24
|
-
server.tool("
|
|
58
|
+
server.tool("project_create", "Create a new project in your organization", {
|
|
25
59
|
name: z.string().describe("Project name"),
|
|
26
60
|
description: z.string().optional().describe("Project description"),
|
|
27
61
|
enabledServices: z
|
|
@@ -54,7 +88,7 @@ export function registerPlatformTools(server, client) {
|
|
|
54
88
|
};
|
|
55
89
|
}
|
|
56
90
|
});
|
|
57
|
-
server.tool("
|
|
91
|
+
server.tool("env_list", "List environments for a project", {
|
|
58
92
|
projectId: z.string().describe("Project ID (prj_ prefixed)"),
|
|
59
93
|
}, async (params) => {
|
|
60
94
|
const data = await platform.mcpListEnvironments(client, params.projectId);
|
|
@@ -62,7 +96,7 @@ export function registerPlatformTools(server, client) {
|
|
|
62
96
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
63
97
|
};
|
|
64
98
|
});
|
|
65
|
-
server.tool("
|
|
99
|
+
server.tool("env_create", "Create a new environment for a project. Returns the public key needed for SDK/CLI configuration.", {
|
|
66
100
|
projectId: z.string().describe("Project ID (prj_ prefixed)"),
|
|
67
101
|
name: z
|
|
68
102
|
.string()
|
|
@@ -101,7 +135,7 @@ export function registerPlatformTools(server, client) {
|
|
|
101
135
|
};
|
|
102
136
|
}
|
|
103
137
|
});
|
|
104
|
-
server.tool("
|
|
138
|
+
server.tool("env_update", "Update an environment's name or description.", {
|
|
105
139
|
projectId: z.string().describe("Project ID (prj_ prefixed)"),
|
|
106
140
|
environmentId: z.string().describe("Environment ID (env_ prefixed)"),
|
|
107
141
|
name: z.string().optional().describe("New environment name"),
|
|
@@ -138,7 +172,7 @@ export function registerPlatformTools(server, client) {
|
|
|
138
172
|
};
|
|
139
173
|
}
|
|
140
174
|
});
|
|
141
|
-
server.tool("
|
|
175
|
+
server.tool("env_promote", "Promote an environment's configuration to a higher tier (dev → beta → prod).", {
|
|
142
176
|
environmentId: z.string().describe("Environment ID to promote"),
|
|
143
177
|
targetTier: z
|
|
144
178
|
.string()
|
|
@@ -172,10 +206,39 @@ export function registerPlatformTools(server, client) {
|
|
|
172
206
|
};
|
|
173
207
|
}
|
|
174
208
|
});
|
|
175
|
-
server.tool("
|
|
176
|
-
projectId: z.string().describe("
|
|
177
|
-
envId: z.string().describe("
|
|
178
|
-
|
|
209
|
+
server.tool("keystore_list", "List keystore values for the active environment. Uses the shared .ascendkit environment context by default.", {
|
|
210
|
+
projectId: z.string().optional().describe("Optional project ID override (defaults to active environment project)"),
|
|
211
|
+
envId: z.string().optional().describe("Optional environment ID override (defaults to active environment)"),
|
|
212
|
+
}, async (params) => {
|
|
213
|
+
try {
|
|
214
|
+
const env = await platform.mcpGetEnvironment(client, params);
|
|
215
|
+
const variables = (env.variables ?? {});
|
|
216
|
+
const entries = Object.entries(variables);
|
|
217
|
+
const systemVariables = Array.isArray(env.systemVariables)
|
|
218
|
+
? env.systemVariables
|
|
219
|
+
: [];
|
|
220
|
+
const sections = [];
|
|
221
|
+
sections.push(entries.length === 0
|
|
222
|
+
? "Custom variables:\n(none)"
|
|
223
|
+
: `Custom variables:\n${entries.map(([key, value]) => `${key}=${value}`).join("\n")}`);
|
|
224
|
+
if (systemVariables.length > 0) {
|
|
225
|
+
sections.push(`System variables:\n${systemVariables
|
|
226
|
+
.map((item) => `${String(item.key)} = ${String(item.valuePreview)} (${String(item.availability)})`)
|
|
227
|
+
.join("\n")}`);
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: "text", text: sections.join("\n\n") }],
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
235
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
server.tool("keystore_replace", "Replace the full keystore for the active environment. Pass the full key-value map.", {
|
|
239
|
+
projectId: z.string().optional().describe("Optional project ID override (defaults to active environment project)"),
|
|
240
|
+
envId: z.string().optional().describe("Optional environment ID override (defaults to active environment)"),
|
|
241
|
+
variables: z.record(z.string()).describe("Key-value map of keystore values"),
|
|
179
242
|
}, async (params) => {
|
|
180
243
|
try {
|
|
181
244
|
const data = await platform.mcpUpdateEnvironmentVariables(client, params);
|
|
@@ -202,4 +265,56 @@ export function registerPlatformTools(server, client) {
|
|
|
202
265
|
};
|
|
203
266
|
}
|
|
204
267
|
});
|
|
268
|
+
server.tool("keystore_set", "Set a single keystore key for the active environment.", {
|
|
269
|
+
projectId: z.string().optional().describe("Optional project ID override (defaults to active environment project)"),
|
|
270
|
+
envId: z.string().optional().describe("Optional environment ID override (defaults to active environment)"),
|
|
271
|
+
key: z.string().describe("Keystore key"),
|
|
272
|
+
value: z.string().describe("Keystore value"),
|
|
273
|
+
}, async (params) => {
|
|
274
|
+
try {
|
|
275
|
+
const env = await platform.mcpGetEnvironment(client, {
|
|
276
|
+
projectId: params.projectId,
|
|
277
|
+
envId: params.envId,
|
|
278
|
+
});
|
|
279
|
+
const variables = { ...(env.variables ?? {}), [params.key]: params.value };
|
|
280
|
+
const data = await platform.mcpUpdateEnvironmentVariables(client, {
|
|
281
|
+
projectId: params.projectId,
|
|
282
|
+
envId: params.envId,
|
|
283
|
+
variables,
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
content: [{ type: "text", text: `Saved ${params.key}=${params.value}\n\n${JSON.stringify(data, null, 2)}` }],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
291
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
server.tool("keystore_remove", "Remove a single keystore key from the active environment.", {
|
|
295
|
+
projectId: z.string().optional().describe("Optional project ID override (defaults to active environment project)"),
|
|
296
|
+
envId: z.string().optional().describe("Optional environment ID override (defaults to active environment)"),
|
|
297
|
+
key: z.string().describe("Keystore key to remove"),
|
|
298
|
+
}, async (params) => {
|
|
299
|
+
try {
|
|
300
|
+
const env = await platform.mcpGetEnvironment(client, {
|
|
301
|
+
projectId: params.projectId,
|
|
302
|
+
envId: params.envId,
|
|
303
|
+
});
|
|
304
|
+
const variables = { ...(env.variables ?? {}) };
|
|
305
|
+
delete variables[params.key];
|
|
306
|
+
const data = await platform.mcpUpdateEnvironmentVariables(client, {
|
|
307
|
+
projectId: params.projectId,
|
|
308
|
+
envId: params.envId,
|
|
309
|
+
variables,
|
|
310
|
+
});
|
|
311
|
+
return {
|
|
312
|
+
content: [{ type: "text", text: `Removed ${params.key}\n\n${JSON.stringify(data, null, 2)}` }],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
317
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
318
|
+
}
|
|
319
|
+
});
|
|
205
320
|
}
|
package/dist/tools/surveys.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import * as surveys from "../commands/surveys.js";
|
|
3
3
|
import { formatDistributionResult, formatQuestionList, formatSingleQuestion, formatSurveyList, formatSurveyWithGuidance, } from "../utils/survey-format.js";
|
|
4
4
|
export function registerSurveyTools(server, client) {
|
|
5
|
-
server.tool("survey_create", "Create a new survey. Use type 'nps' or 'csat' for ready-to-use presets with standard questions, or 'custom' to build from scratch with
|
|
5
|
+
server.tool("survey_create", "Create a new survey. Use type 'nps' or 'csat' for ready-to-use presets with standard questions, or 'custom' to build from scratch with survey_question_add. Survey lifecycle: create → add questions → activate → distribute → collect → analyze.", {
|
|
6
6
|
name: z.string().describe("Survey name, e.g. 'Q1 NPS Survey'"),
|
|
7
7
|
type: z
|
|
8
8
|
.enum(["nps", "csat", "custom"])
|
|
@@ -22,7 +22,7 @@ export function registerSurveyTools(server, client) {
|
|
|
22
22
|
const formatted = formatSurveyList(data);
|
|
23
23
|
return { content: [{ type: "text", text: formatted }] };
|
|
24
24
|
});
|
|
25
|
-
server.tool("
|
|
25
|
+
server.tool("survey_show", "Get a survey by ID with status, question count, response count, and next-step guidance.", {
|
|
26
26
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
27
27
|
}, async (params) => {
|
|
28
28
|
const data = await surveys.getSurvey(client, params.surveyId);
|
|
@@ -46,7 +46,7 @@ export function registerSurveyTools(server, client) {
|
|
|
46
46
|
const formatted = formatSurveyWithGuidance(data);
|
|
47
47
|
return { content: [{ type: "text", text: formatted }] };
|
|
48
48
|
});
|
|
49
|
-
server.tool("
|
|
49
|
+
server.tool("survey_remove", "Delete a survey and all its invitations and responses", {
|
|
50
50
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
51
51
|
}, async (params) => {
|
|
52
52
|
const data = await surveys.deleteSurvey(client, params.surveyId);
|
|
@@ -62,7 +62,7 @@ export function registerSurveyTools(server, client) {
|
|
|
62
62
|
const formatted = formatDistributionResult(data);
|
|
63
63
|
return { content: [{ type: "text", text: formatted }] };
|
|
64
64
|
});
|
|
65
|
-
server.tool("
|
|
65
|
+
server.tool("survey_invitation_list", "List all invitations for a survey with their status (sent/opened/submitted)", {
|
|
66
66
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
67
67
|
}, async (params) => {
|
|
68
68
|
const data = await surveys.listInvitations(client, params.surveyId);
|
|
@@ -75,14 +75,14 @@ export function registerSurveyTools(server, client) {
|
|
|
75
75
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
76
76
|
});
|
|
77
77
|
// --- Question-level management tools ---
|
|
78
|
-
server.tool("
|
|
78
|
+
server.tool("survey_question_list", "List all questions in a survey in human-readable format. Shows question number, type, title, required status, choices, and any conditional logic.", {
|
|
79
79
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
80
80
|
}, async (params) => {
|
|
81
81
|
const data = await surveys.listQuestions(client, params.surveyId);
|
|
82
82
|
const formatted = formatQuestionList(data);
|
|
83
83
|
return { content: [{ type: "text", text: formatted }] };
|
|
84
84
|
});
|
|
85
|
-
server.tool("
|
|
85
|
+
server.tool("survey_question_add", "Add a question to a survey. Supported types: text (short text), comment (long text/multi-line), radiogroup (single choice), checkbox (multiple choice), rating (scale — use rateMin:0 rateMax:10 for NPS), boolean (yes/no), dropdown, ranking. For date input, use type 'text' with inputType 'date'. Question name is auto-generated from title if not provided.", {
|
|
86
86
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
87
87
|
type: z
|
|
88
88
|
.enum([
|
|
@@ -147,7 +147,7 @@ export function registerSurveyTools(server, client) {
|
|
|
147
147
|
const formatted = formatSingleQuestion(data, "Added");
|
|
148
148
|
return { content: [{ type: "text", text: formatted }] };
|
|
149
149
|
});
|
|
150
|
-
server.tool("
|
|
150
|
+
server.tool("survey_question_update", "Edit an existing question in a survey by its name. Only provided fields are updated; others remain unchanged. Pass empty string for visibleIf/requiredIf to clear conditional logic.", {
|
|
151
151
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
152
152
|
questionName: z.string().describe("Name of the question to edit"),
|
|
153
153
|
title: z.string().optional().describe("New question text"),
|
|
@@ -181,7 +181,7 @@ export function registerSurveyTools(server, client) {
|
|
|
181
181
|
const formatted = formatSingleQuestion(data, "Updated");
|
|
182
182
|
return { content: [{ type: "text", text: formatted }] };
|
|
183
183
|
});
|
|
184
|
-
server.tool("
|
|
184
|
+
server.tool("survey_question_remove", "Remove a question from a survey by its name. This is permanent — the question and its configuration are deleted from the survey definition.", {
|
|
185
185
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
186
186
|
questionName: z.string().describe("Name of the question to remove"),
|
|
187
187
|
}, async (params) => {
|
|
@@ -195,7 +195,7 @@ export function registerSurveyTools(server, client) {
|
|
|
195
195
|
],
|
|
196
196
|
};
|
|
197
197
|
});
|
|
198
|
-
server.tool("
|
|
198
|
+
server.tool("survey_question_reorder", "Reorder questions in a survey by providing all question names in the desired order. All existing question names must be included.", {
|
|
199
199
|
surveyId: z.string().describe("Survey ID (srv_ prefixed)"),
|
|
200
200
|
order: z
|
|
201
201
|
.array(z.string())
|
|
@@ -13,6 +13,11 @@ interface JourneyData {
|
|
|
13
13
|
nodes: Record<string, {
|
|
14
14
|
action?: {
|
|
15
15
|
type?: string;
|
|
16
|
+
templateSlug?: string;
|
|
17
|
+
surveySlug?: string;
|
|
18
|
+
tagName?: string;
|
|
19
|
+
stageName?: string;
|
|
20
|
+
fromIdentityEmail?: string;
|
|
16
21
|
};
|
|
17
22
|
terminal?: boolean;
|
|
18
23
|
}>;
|
|
@@ -83,6 +88,7 @@ interface NodeListItem {
|
|
|
83
88
|
surveySlug?: string;
|
|
84
89
|
tagName?: string;
|
|
85
90
|
stageName?: string;
|
|
91
|
+
fromIdentityEmail?: string;
|
|
86
92
|
};
|
|
87
93
|
terminal: boolean;
|
|
88
94
|
isEntryNode: boolean;
|
|
@@ -32,7 +32,7 @@ export function formatJourneyWithGuidance(journey) {
|
|
|
32
32
|
lines.push("");
|
|
33
33
|
lines.push("Nodes:");
|
|
34
34
|
for (const [name, node] of Object.entries(journey.nodes || {})) {
|
|
35
|
-
const action = node.action
|
|
35
|
+
const action = formatActionLabel(node.action || {});
|
|
36
36
|
const terminal = node.terminal ? " (terminal)" : "";
|
|
37
37
|
const isEntry = name === journey.entryNode ? " [entry]" : "";
|
|
38
38
|
lines.push(` ${name}: ${action}${terminal}${isEntry}`);
|
|
@@ -70,7 +70,7 @@ export function formatJourneyWithGuidance(journey) {
|
|
|
70
70
|
hints.push("Journey is active and enrolling users. Use journey_analytics to see user flow.");
|
|
71
71
|
}
|
|
72
72
|
else if (journey.status === "paused") {
|
|
73
|
-
hints.push("Journey is paused. Actions are queued. Use
|
|
73
|
+
hints.push("Journey is paused. Actions are queued. Use journey_resume to continue delivery.");
|
|
74
74
|
}
|
|
75
75
|
else if (journey.status === "archived") {
|
|
76
76
|
hints.push("Journey is archived. No further enrollment or transitions.");
|
|
@@ -113,6 +113,8 @@ function formatActionLabel(action) {
|
|
|
113
113
|
const type = action?.type || "none";
|
|
114
114
|
if (type === "send_email") {
|
|
115
115
|
const parts = [`send_email (${action.templateSlug || "?"})`];
|
|
116
|
+
if (action.fromIdentityEmail)
|
|
117
|
+
parts.push(`from: ${action.fromIdentityEmail}`);
|
|
116
118
|
if (action.surveySlug)
|
|
117
119
|
parts.push(`+ survey: ${action.surveySlug}`);
|
|
118
120
|
return parts.join(" ");
|
|
@@ -131,7 +133,7 @@ function formatTriggerLabel(trigger) {
|
|
|
131
133
|
export function formatNodeList(data) {
|
|
132
134
|
const nodes = data.nodes || [];
|
|
133
135
|
if (nodes.length === 0) {
|
|
134
|
-
return "No nodes. Use
|
|
136
|
+
return "No nodes. Use journey_node_add to add the first node.";
|
|
135
137
|
}
|
|
136
138
|
const lines = [`${nodes.length} node(s):\n`];
|
|
137
139
|
for (const n of nodes) {
|
|
@@ -150,7 +152,7 @@ export function formatSingleNode(data, action, nodeName) {
|
|
|
150
152
|
export function formatTransitionList(data) {
|
|
151
153
|
const transitions = data.transitions || [];
|
|
152
154
|
if (transitions.length === 0) {
|
|
153
|
-
return "No transitions. Use
|
|
155
|
+
return "No transitions. Use journey_transition_add to connect nodes.";
|
|
154
156
|
}
|
|
155
157
|
const lines = [`${transitions.length} transition(s):\n`];
|
|
156
158
|
for (const t of transitions) {
|
|
@@ -84,11 +84,11 @@ export function formatSurveyWithGuidance(survey) {
|
|
|
84
84
|
// Next steps based on current state
|
|
85
85
|
const hints = [];
|
|
86
86
|
if (questionCount === 0) {
|
|
87
|
-
hints.push("This survey has no questions yet. Use
|
|
87
|
+
hints.push("This survey has no questions yet. Use survey_question_add to add questions, or survey_question_list to see supported question types.");
|
|
88
88
|
}
|
|
89
89
|
else if (survey.status === "draft") {
|
|
90
90
|
hints.push(`Survey has ${questionCount} question(s) and is in draft. Use survey_update with status 'active' to activate it for distribution.`);
|
|
91
|
-
hints.push("Use
|
|
91
|
+
hints.push("Use survey_question_list to review the questions before activating.");
|
|
92
92
|
}
|
|
93
93
|
else if (survey.status === "active" && responseCount === 0) {
|
|
94
94
|
hints.push("Survey is active and ready to distribute. Use survey_distribute with a list of user IDs to create personalized tracking links.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ascendkit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "AscendKit CLI and MCP server",
|
|
5
5
|
"author": "ascendkit.dev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"start": "node dist/cli.js"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
37
|
-
"zod": "^3.
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
37
|
+
"zod": "^3.25.76"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"@types/node": "^25.5.0",
|
|
41
|
+
"typescript": "^5.7.0"
|
|
42
42
|
}
|
|
43
43
|
}
|