@donkeylabs/cli 0.4.0 ā 0.4.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.
- package/package.json +1 -1
- package/src/commands/init.ts +153 -33
- package/src/commands/mcp.ts +305 -0
- package/src/index.ts +6 -0
- package/templates/sveltekit-app/bun.lock +6 -6
- package/templates/sveltekit-app/package.json +3 -3
- package/templates/sveltekit-app/src/lib/api.ts +119 -105
- package/templates/sveltekit-app/src/routes/+page.server.ts +1 -1
- package/templates/sveltekit-app/src/routes/+page.svelte +13 -13
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { mkdir, writeFile, readFile, readdir, copyFile, stat } from "node:fs/pro
|
|
|
8
8
|
import { join, resolve, dirname, basename } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
11
12
|
import pc from "picocolors";
|
|
12
13
|
import prompts from "prompts";
|
|
13
14
|
|
|
@@ -38,21 +39,45 @@ const RENAME_MAP: Record<string, string> = {
|
|
|
38
39
|
|
|
39
40
|
export async function initCommand(args: string[]) {
|
|
40
41
|
// Parse --type flag if provided
|
|
41
|
-
let projectDir =
|
|
42
|
+
let projectDir: string | null = null;
|
|
42
43
|
let typeArg: string | null = null;
|
|
43
44
|
|
|
44
45
|
for (let i = 0; i < args.length; i++) {
|
|
45
46
|
if (args[i] === "--type" && args[i + 1]) {
|
|
46
47
|
typeArg = args[i + 1];
|
|
47
48
|
i++; // skip next arg
|
|
48
|
-
} else if (!args[i]
|
|
49
|
-
projectDir = args[i];
|
|
49
|
+
} else if (!args[i]?.startsWith("-")) {
|
|
50
|
+
projectDir = args[i] ?? null;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
console.log(pc.bold("\nš Create a new @donkeylabs/server project\n"));
|
|
55
|
+
|
|
56
|
+
// If no project directory provided, prompt for it
|
|
57
|
+
if (!projectDir) {
|
|
58
|
+
const { name } = await prompts({
|
|
59
|
+
type: "text",
|
|
60
|
+
name: "name",
|
|
61
|
+
message: "Project name:",
|
|
62
|
+
initial: "my-donkeylabs-app",
|
|
63
|
+
validate: (value) => {
|
|
64
|
+
if (!value) return "Project name is required";
|
|
65
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
|
|
66
|
+
return "Project name can only contain letters, numbers, dashes, and underscores";
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!name) {
|
|
73
|
+
console.log(pc.yellow("Cancelled."));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
54
76
|
|
|
55
|
-
|
|
77
|
+
projectDir = name;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const targetDir = resolve(process.cwd(), projectDir!);
|
|
56
81
|
|
|
57
82
|
let projectType: ProjectType;
|
|
58
83
|
|
|
@@ -130,45 +155,140 @@ export async function initCommand(args: string[]) {
|
|
|
130
155
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
131
156
|
}
|
|
132
157
|
|
|
133
|
-
|
|
134
|
-
if (projectType === "server") {
|
|
135
|
-
console.log(`
|
|
136
|
-
${pc.bold(pc.green("Success!"))} Server project initialized.
|
|
158
|
+
console.log(pc.green("\nā Project files created\n"));
|
|
137
159
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
160
|
+
// Auto-install dependencies
|
|
161
|
+
console.log(pc.cyan("Installing dependencies...\n"));
|
|
162
|
+
const installSuccess = await runCommand("bun", ["install"], targetDir);
|
|
141
163
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
3. Generate types after adding plugins:
|
|
146
|
-
${pc.cyan("bun run gen:types")}
|
|
147
|
-
`);
|
|
164
|
+
if (!installSuccess) {
|
|
165
|
+
console.log(pc.yellow("\nā Dependency installation failed."));
|
|
166
|
+
console.log(pc.dim(" Run 'bun install' manually to install dependencies.\n"));
|
|
148
167
|
} else {
|
|
149
|
-
console.log(
|
|
150
|
-
|
|
168
|
+
console.log(pc.green("\nā Dependencies installed\n"));
|
|
169
|
+
}
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
// Ask about MCP setup
|
|
172
|
+
const { setupMcp } = await prompts({
|
|
173
|
+
type: "confirm",
|
|
174
|
+
name: "setupMcp",
|
|
175
|
+
message: `Setup MCP for AI-assisted development? ${pc.dim("(Highly recommended)")}`,
|
|
176
|
+
initial: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (setupMcp) {
|
|
180
|
+
// Ask which IDE
|
|
181
|
+
const { ide } = await prompts({
|
|
182
|
+
type: "select",
|
|
183
|
+
name: "ide",
|
|
184
|
+
message: "Which AI IDE are you using?",
|
|
185
|
+
choices: [
|
|
186
|
+
{ title: "Claude Code", value: "claude", description: "Anthropic's Claude Code CLI" },
|
|
187
|
+
{ title: "Cursor", value: "cursor", description: "Cursor AI IDE" },
|
|
188
|
+
{ title: "Windsurf", value: "windsurf", description: "Codeium Windsurf" },
|
|
189
|
+
{ title: "Other / Skip instructions", value: "skip" },
|
|
190
|
+
],
|
|
191
|
+
});
|
|
155
192
|
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
// Install @donkeylabs/mcp
|
|
194
|
+
console.log(pc.cyan("\nInstalling @donkeylabs/mcp...\n"));
|
|
195
|
+
const mcpInstallSuccess = await runCommand("bun", ["add", "-d", "@donkeylabs/mcp"], targetDir);
|
|
196
|
+
|
|
197
|
+
if (mcpInstallSuccess) {
|
|
198
|
+
console.log(pc.green("ā Installed @donkeylabs/mcp\n"));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Create .mcp.json
|
|
202
|
+
const mcpConfig = {
|
|
203
|
+
mcpServers: {
|
|
204
|
+
donkeylabs: {
|
|
205
|
+
command: "bunx",
|
|
206
|
+
args: ["@donkeylabs/mcp"],
|
|
207
|
+
cwd: "${workspaceFolder}",
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
await writeFile(join(targetDir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
213
|
+
console.log(pc.green("ā Created .mcp.json\n"));
|
|
214
|
+
|
|
215
|
+
// Show IDE-specific instructions
|
|
216
|
+
if (ide === "claude") {
|
|
217
|
+
console.log(pc.cyan("Claude Code Setup:"));
|
|
218
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
219
|
+
console.log(`
|
|
220
|
+
The .mcp.json file has been created in your project.
|
|
221
|
+
Claude Code will automatically detect and use this configuration.
|
|
222
|
+
|
|
223
|
+
${pc.bold("To verify:")}
|
|
224
|
+
1. Open Claude Code in this project directory
|
|
225
|
+
2. The MCP tools should be available automatically
|
|
226
|
+
3. Try asking Claude to "list plugins" or "get project info"
|
|
227
|
+
`);
|
|
228
|
+
} else if (ide === "cursor") {
|
|
229
|
+
console.log(pc.cyan("Cursor Setup:"));
|
|
230
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
231
|
+
console.log(`
|
|
232
|
+
${pc.bold("To configure Cursor:")}
|
|
233
|
+
1. Open Cursor Settings (Cmd/Ctrl + ,)
|
|
234
|
+
2. Search for "MCP" or "Model Context Protocol"
|
|
235
|
+
3. Add the donkeylabs server from .mcp.json
|
|
236
|
+
4. Restart Cursor to apply changes
|
|
237
|
+
`);
|
|
238
|
+
} else if (ide === "windsurf") {
|
|
239
|
+
console.log(pc.cyan("Windsurf Setup:"));
|
|
240
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
241
|
+
console.log(`
|
|
242
|
+
${pc.bold("To configure Windsurf:")}
|
|
243
|
+
1. Open Windsurf settings
|
|
244
|
+
2. Navigate to AI / MCP configuration
|
|
245
|
+
3. Add the donkeylabs server from .mcp.json
|
|
246
|
+
`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
158
249
|
|
|
159
|
-
|
|
160
|
-
|
|
250
|
+
// Print final success message
|
|
251
|
+
console.log(pc.bold(pc.green("\nš Project ready!\n")));
|
|
161
252
|
|
|
162
|
-
|
|
163
|
-
|
|
253
|
+
if (projectType === "server") {
|
|
254
|
+
console.log(`${pc.bold("Start development:")}
|
|
255
|
+
${pc.cyan("cd " + (projectDir !== "." ? projectDir : ""))}
|
|
256
|
+
${pc.cyan("bun run dev")}
|
|
257
|
+
`);
|
|
258
|
+
} else {
|
|
259
|
+
console.log(`${pc.bold("Start development:")}
|
|
260
|
+
${projectDir !== "." ? pc.cyan("cd " + projectDir) + "\n " : ""}${pc.cyan("bun run dev")}
|
|
164
261
|
|
|
165
262
|
${pc.bold("Project structure:")}
|
|
166
|
-
src/server/
|
|
167
|
-
src/lib/api.ts
|
|
168
|
-
src/routes/
|
|
169
|
-
src/hooks.server.ts - Server hooks for SSR
|
|
263
|
+
src/server/ - @donkeylabs/server API
|
|
264
|
+
src/lib/api.ts - Typed API client
|
|
265
|
+
src/routes/ - SvelteKit pages
|
|
170
266
|
`);
|
|
171
267
|
}
|
|
268
|
+
|
|
269
|
+
if (setupMcp) {
|
|
270
|
+
console.log(pc.dim("MCP is configured. Your AI assistant can now help you build!"));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Run a command and return success status
|
|
276
|
+
*/
|
|
277
|
+
async function runCommand(cmd: string, args: string[], cwd: string): Promise<boolean> {
|
|
278
|
+
return new Promise((resolve) => {
|
|
279
|
+
const child = spawn(cmd, args, {
|
|
280
|
+
stdio: "inherit",
|
|
281
|
+
cwd,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
child.on("close", (code) => {
|
|
285
|
+
resolve(code === 0);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
child.on("error", () => {
|
|
289
|
+
resolve(false);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
172
292
|
}
|
|
173
293
|
|
|
174
294
|
/**
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) setup command
|
|
3
|
+
*
|
|
4
|
+
* Sets up the @donkeylabs/mcp server for AI-assisted development
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import pc from "picocolors";
|
|
12
|
+
import prompts from "prompts";
|
|
13
|
+
|
|
14
|
+
interface McpConfig {
|
|
15
|
+
mcpServers?: Record<string, {
|
|
16
|
+
command: string;
|
|
17
|
+
args: string[];
|
|
18
|
+
cwd?: string;
|
|
19
|
+
env?: Record<string, string>;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function detectPackageManager(): Promise<"bun" | "npm" | "pnpm" | "yarn"> {
|
|
24
|
+
if (existsSync("bun.lockb") || existsSync("bun.lock")) return "bun";
|
|
25
|
+
if (existsSync("pnpm-lock.yaml")) return "pnpm";
|
|
26
|
+
if (existsSync("yarn.lock")) return "yarn";
|
|
27
|
+
return "npm";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function installPackage(pkg: string, dev: boolean = true): Promise<boolean> {
|
|
31
|
+
const pm = await detectPackageManager();
|
|
32
|
+
|
|
33
|
+
const args: string[] = [];
|
|
34
|
+
switch (pm) {
|
|
35
|
+
case "bun":
|
|
36
|
+
args.push("add", dev ? "-d" : "", pkg);
|
|
37
|
+
break;
|
|
38
|
+
case "pnpm":
|
|
39
|
+
args.push("add", dev ? "-D" : "", pkg);
|
|
40
|
+
break;
|
|
41
|
+
case "yarn":
|
|
42
|
+
args.push("add", dev ? "-D" : "", pkg);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
args.push("install", dev ? "--save-dev" : "--save", pkg);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(pc.dim(`$ ${pm} ${args.filter(Boolean).join(" ")}`));
|
|
49
|
+
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const child = spawn(pm, args.filter(Boolean), {
|
|
52
|
+
stdio: "inherit",
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.on("close", (code) => {
|
|
57
|
+
resolve(code === 0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
child.on("error", () => {
|
|
61
|
+
resolve(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function readMcpConfig(): Promise<McpConfig> {
|
|
67
|
+
const configPath = join(process.cwd(), ".mcp.json");
|
|
68
|
+
|
|
69
|
+
if (!existsSync(configPath)) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const content = await readFile(configPath, "utf-8");
|
|
75
|
+
return JSON.parse(content);
|
|
76
|
+
} catch {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function writeMcpConfig(config: McpConfig): Promise<void> {
|
|
82
|
+
const configPath = join(process.cwd(), ".mcp.json");
|
|
83
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function setupClaudeCode(): Promise<void> {
|
|
87
|
+
console.log(pc.cyan("\nClaude Code Setup:"));
|
|
88
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
89
|
+
console.log(`
|
|
90
|
+
The .mcp.json file has been created in your project root.
|
|
91
|
+
Claude Code will automatically detect and use this configuration.
|
|
92
|
+
|
|
93
|
+
${pc.bold("To verify:")}
|
|
94
|
+
1. Open Claude Code in this project
|
|
95
|
+
2. The MCP tools should be available automatically
|
|
96
|
+
3. Try asking Claude to "list plugins" or "get project info"
|
|
97
|
+
|
|
98
|
+
${pc.bold("Manual setup (if needed):")}
|
|
99
|
+
Add to your Claude Code settings:
|
|
100
|
+
${pc.dim(JSON.stringify({
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"donkeylabs": {
|
|
103
|
+
"command": "bunx",
|
|
104
|
+
"args": ["@donkeylabs/mcp"],
|
|
105
|
+
"cwd": "${workspaceFolder}"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, null, 2))}
|
|
109
|
+
`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function setupCursor(): Promise<void> {
|
|
113
|
+
console.log(pc.cyan("\nCursor Setup:"));
|
|
114
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
115
|
+
console.log(`
|
|
116
|
+
${pc.bold("To configure Cursor:")}
|
|
117
|
+
1. Open Cursor Settings (Cmd/Ctrl + ,)
|
|
118
|
+
2. Search for "MCP" or "Model Context Protocol"
|
|
119
|
+
3. Add the donkeylabs server configuration:
|
|
120
|
+
|
|
121
|
+
${pc.dim(JSON.stringify({
|
|
122
|
+
"donkeylabs": {
|
|
123
|
+
"command": "bunx",
|
|
124
|
+
"args": ["@donkeylabs/mcp"],
|
|
125
|
+
"cwd": "${workspaceFolder}"
|
|
126
|
+
}
|
|
127
|
+
}, null, 2))}
|
|
128
|
+
|
|
129
|
+
4. Restart Cursor to apply changes
|
|
130
|
+
`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function setupWindsurf(): Promise<void> {
|
|
134
|
+
console.log(pc.cyan("\nWindsurf Setup:"));
|
|
135
|
+
console.log(pc.dim("ā".repeat(40)));
|
|
136
|
+
console.log(`
|
|
137
|
+
${pc.bold("To configure Windsurf:")}
|
|
138
|
+
1. Open Windsurf settings
|
|
139
|
+
2. Navigate to AI / MCP configuration
|
|
140
|
+
3. Add the donkeylabs server:
|
|
141
|
+
|
|
142
|
+
${pc.dim(JSON.stringify({
|
|
143
|
+
"donkeylabs": {
|
|
144
|
+
"command": "bunx",
|
|
145
|
+
"args": ["@donkeylabs/mcp"],
|
|
146
|
+
"cwd": "${workspaceFolder}"
|
|
147
|
+
}
|
|
148
|
+
}, null, 2))}
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function mcpCommand(args: string[]): Promise<void> {
|
|
153
|
+
const subcommand = args[0];
|
|
154
|
+
|
|
155
|
+
if (!subcommand || subcommand === "setup") {
|
|
156
|
+
await setupMcp(args.slice(1));
|
|
157
|
+
} else if (subcommand === "help" || subcommand === "--help") {
|
|
158
|
+
printMcpHelp();
|
|
159
|
+
} else {
|
|
160
|
+
console.error(pc.red(`Unknown mcp subcommand: ${subcommand}`));
|
|
161
|
+
printMcpHelp();
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function printMcpHelp(): void {
|
|
167
|
+
console.log(`
|
|
168
|
+
${pc.bold("donkeylabs mcp")} - Setup MCP server for AI-assisted development
|
|
169
|
+
|
|
170
|
+
${pc.bold("Usage:")}
|
|
171
|
+
donkeylabs mcp Interactive MCP setup
|
|
172
|
+
donkeylabs mcp setup Setup MCP (interactive)
|
|
173
|
+
donkeylabs mcp setup --claude Setup for Claude Code
|
|
174
|
+
donkeylabs mcp setup --cursor Setup for Cursor
|
|
175
|
+
donkeylabs mcp setup --all Setup for all IDEs
|
|
176
|
+
|
|
177
|
+
${pc.bold("Options:")}
|
|
178
|
+
--claude Configure for Claude Code
|
|
179
|
+
--cursor Configure for Cursor
|
|
180
|
+
--windsurf Configure for Windsurf
|
|
181
|
+
--all Show setup for all IDEs
|
|
182
|
+
--skip-install Skip installing @donkeylabs/mcp package
|
|
183
|
+
|
|
184
|
+
${pc.bold("What this does:")}
|
|
185
|
+
1. Installs @donkeylabs/mcp as a dev dependency
|
|
186
|
+
2. Creates/updates .mcp.json in your project
|
|
187
|
+
3. Provides IDE-specific setup instructions
|
|
188
|
+
|
|
189
|
+
${pc.bold("MCP Tools Available:")}
|
|
190
|
+
- get_project_info - View project structure and routes
|
|
191
|
+
- create_plugin - Create new plugins
|
|
192
|
+
- add_service_method - Add methods to plugin services
|
|
193
|
+
- add_migration - Create database migrations
|
|
194
|
+
- create_router - Create new routers
|
|
195
|
+
- add_route - Add routes to routers
|
|
196
|
+
- add_cron - Schedule cron jobs
|
|
197
|
+
- add_event - Register events
|
|
198
|
+
- add_async_job - Register background jobs
|
|
199
|
+
- generate_types - Regenerate types
|
|
200
|
+
- generate_client - Generate API client
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function setupMcp(args: string[]): Promise<void> {
|
|
205
|
+
console.log(pc.bold("\nš§ Setting up @donkeylabs/mcp\n"));
|
|
206
|
+
|
|
207
|
+
// Check if we're in a donkeylabs project
|
|
208
|
+
const configPath = join(process.cwd(), "donkeylabs.config.ts");
|
|
209
|
+
const hasConfig = existsSync(configPath);
|
|
210
|
+
|
|
211
|
+
if (!hasConfig) {
|
|
212
|
+
console.log(pc.yellow("ā No donkeylabs.config.ts found in current directory."));
|
|
213
|
+
console.log(pc.dim(" The MCP server works best in a @donkeylabs/server project."));
|
|
214
|
+
console.log(pc.dim(" Run 'donkeylabs init' to create a new project first.\n"));
|
|
215
|
+
|
|
216
|
+
const { proceed } = await prompts({
|
|
217
|
+
type: "confirm",
|
|
218
|
+
name: "proceed",
|
|
219
|
+
message: "Continue anyway?",
|
|
220
|
+
initial: false,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (!proceed) {
|
|
224
|
+
console.log(pc.dim("Aborted."));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Parse args for flags
|
|
230
|
+
const skipInstall = args.includes("--skip-install");
|
|
231
|
+
const forClaude = args.includes("--claude");
|
|
232
|
+
const forCursor = args.includes("--cursor");
|
|
233
|
+
const forWindsurf = args.includes("--windsurf");
|
|
234
|
+
const forAll = args.includes("--all");
|
|
235
|
+
|
|
236
|
+
// Install @donkeylabs/mcp if not skipped
|
|
237
|
+
if (!skipInstall) {
|
|
238
|
+
console.log(pc.cyan("Installing @donkeylabs/mcp..."));
|
|
239
|
+
const success = await installPackage("@donkeylabs/mcp");
|
|
240
|
+
|
|
241
|
+
if (!success) {
|
|
242
|
+
console.log(pc.yellow("\nā Package installation failed, but continuing with config setup."));
|
|
243
|
+
console.log(pc.dim(" You can manually install with: bun add -d @donkeylabs/mcp\n"));
|
|
244
|
+
} else {
|
|
245
|
+
console.log(pc.green("ā Installed @donkeylabs/mcp\n"));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Create/update .mcp.json
|
|
250
|
+
console.log(pc.cyan("Configuring .mcp.json..."));
|
|
251
|
+
|
|
252
|
+
const config = await readMcpConfig();
|
|
253
|
+
config.mcpServers = config.mcpServers || {};
|
|
254
|
+
|
|
255
|
+
config.mcpServers.donkeylabs = {
|
|
256
|
+
command: "bunx",
|
|
257
|
+
args: ["@donkeylabs/mcp"],
|
|
258
|
+
cwd: "${workspaceFolder}",
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
await writeMcpConfig(config);
|
|
262
|
+
console.log(pc.green("ā Created .mcp.json\n"));
|
|
263
|
+
|
|
264
|
+
// Show IDE-specific instructions
|
|
265
|
+
if (forAll || (!forClaude && !forCursor && !forWindsurf)) {
|
|
266
|
+
// Interactive mode or --all
|
|
267
|
+
if (!forClaude && !forCursor && !forWindsurf && !forAll) {
|
|
268
|
+
const { ide } = await prompts({
|
|
269
|
+
type: "select",
|
|
270
|
+
name: "ide",
|
|
271
|
+
message: "Which IDE are you using?",
|
|
272
|
+
choices: [
|
|
273
|
+
{ title: "Claude Code", value: "claude" },
|
|
274
|
+
{ title: "Cursor", value: "cursor" },
|
|
275
|
+
{ title: "Windsurf", value: "windsurf" },
|
|
276
|
+
{ title: "Show all", value: "all" },
|
|
277
|
+
],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (ide === "claude") await setupClaudeCode();
|
|
281
|
+
else if (ide === "cursor") await setupCursor();
|
|
282
|
+
else if (ide === "windsurf") await setupWindsurf();
|
|
283
|
+
else {
|
|
284
|
+
await setupClaudeCode();
|
|
285
|
+
await setupCursor();
|
|
286
|
+
await setupWindsurf();
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
await setupClaudeCode();
|
|
290
|
+
await setupCursor();
|
|
291
|
+
await setupWindsurf();
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
if (forClaude) await setupClaudeCode();
|
|
295
|
+
if (forCursor) await setupCursor();
|
|
296
|
+
if (forWindsurf) await setupWindsurf();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
console.log(pc.green("\nā MCP setup complete!\n"));
|
|
300
|
+
console.log(pc.dim("The AI assistant can now help you with:"));
|
|
301
|
+
console.log(pc.dim(" - Creating plugins, routes, and handlers"));
|
|
302
|
+
console.log(pc.dim(" - Adding migrations and service methods"));
|
|
303
|
+
console.log(pc.dim(" - Setting up cron jobs and background tasks"));
|
|
304
|
+
console.log(pc.dim(" - Generating types and API clients\n"));
|
|
305
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ ${pc.bold("Commands:")}
|
|
|
35
35
|
${pc.cyan("init")} Initialize a new project
|
|
36
36
|
${pc.cyan("generate")} Generate types (registry, context, client)
|
|
37
37
|
${pc.cyan("plugin")} Plugin management
|
|
38
|
+
${pc.cyan("mcp")} Setup MCP server for AI-assisted development
|
|
38
39
|
|
|
39
40
|
${pc.bold("Options:")}
|
|
40
41
|
-h, --help Show this help message
|
|
@@ -95,6 +96,11 @@ async function main() {
|
|
|
95
96
|
await pluginCommand(positionals.slice(1));
|
|
96
97
|
break;
|
|
97
98
|
|
|
99
|
+
case "mcp":
|
|
100
|
+
const { mcpCommand } = await import("./commands/mcp");
|
|
101
|
+
await mcpCommand(positionals.slice(1));
|
|
102
|
+
break;
|
|
103
|
+
|
|
98
104
|
default:
|
|
99
105
|
console.error(pc.red(`Unknown command: ${command}`));
|
|
100
106
|
console.log(`Run ${pc.cyan("donkeylabs --help")} for available commands.`);
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"": {
|
|
5
5
|
"name": "my-sveltekit-app",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@donkeylabs/adapter-sveltekit": "0.
|
|
8
|
-
"@donkeylabs/cli": "0.
|
|
9
|
-
"@donkeylabs/server": "0.
|
|
7
|
+
"@donkeylabs/adapter-sveltekit": "^0.4.0",
|
|
8
|
+
"@donkeylabs/cli": "^0.4.0",
|
|
9
|
+
"@donkeylabs/server": "^0.4.0",
|
|
10
10
|
"bits-ui": "^2.15.4",
|
|
11
11
|
"clsx": "^2.1.1",
|
|
12
12
|
"kysely": "^0.27.6",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
"packages": {
|
|
31
|
-
"@donkeylabs/adapter-sveltekit": ["@donkeylabs/adapter-sveltekit@0.
|
|
31
|
+
"@donkeylabs/adapter-sveltekit": ["@donkeylabs/adapter-sveltekit@0.4.0", "", { "peerDependencies": { "@donkeylabs/server": "^0.4.0", "@sveltejs/kit": "^2.0.0" } }, "sha512-J1kcwhp8X0egAhjC/HyxEizR3aEP7ea08rfAh0++C31UldM3ahAjO3Ux0QYp+fAV76lh8mKrDNAWIsKTyPmqNg=="],
|
|
32
32
|
|
|
33
|
-
"@donkeylabs/cli": ["@donkeylabs/cli@0.
|
|
33
|
+
"@donkeylabs/cli": ["@donkeylabs/cli@0.4.0", "", { "dependencies": { "picocolors": "^1.1.1", "prompts": "^2.4.2" }, "peerDependencies": { "@donkeylabs/server": "^0.4.0" }, "bin": { "donkeylabs": "src/index.ts" } }, "sha512-5Y5oXOzFrhdPMAprij5d/D1r9tcjDRuZo1Ud3zSkGTvVxfejMQjbzrB8KmxFM4+7TBJdjuduvhNewL5V6XnUlA=="],
|
|
34
34
|
|
|
35
|
-
"@donkeylabs/server": ["@donkeylabs/server@0.
|
|
35
|
+
"@donkeylabs/server": ["@donkeylabs/server@0.4.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.25.2", "picocolors": "^1.1.1", "prompts": "^2.4.2" }, "peerDependencies": { "kysely": "^0.27.0 || ^0.28.0", "typescript": "^5", "zod": "^3.20.0" } }, "sha512-Nr7kgKtyqM5EDFTmUL2kyEqCW/NeJiQgYQL4Y3DnphM9rgHz07Qi2mgPs8McYFvrWGUVFRMvzXa6CZILY896bw=="],
|
|
36
36
|
|
|
37
37
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
|
|
38
38
|
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"vite": "^7.2.6"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@donkeylabs/cli": "0.
|
|
28
|
-
"@donkeylabs/adapter-sveltekit": "0.
|
|
29
|
-
"@donkeylabs/server": "0.
|
|
27
|
+
"@donkeylabs/cli": "^0.4.2",
|
|
28
|
+
"@donkeylabs/adapter-sveltekit": "^0.4.2",
|
|
29
|
+
"@donkeylabs/server": "^0.4.2",
|
|
30
30
|
"bits-ui": "^2.15.4",
|
|
31
31
|
"clsx": "^2.1.1",
|
|
32
32
|
"kysely": "^0.27.6",
|
|
@@ -27,101 +27,112 @@ export { type ServerContext as AppContext } from "@donkeylabs/server";
|
|
|
27
27
|
// ============================================
|
|
28
28
|
|
|
29
29
|
export namespace Routes {
|
|
30
|
-
export namespace
|
|
31
|
-
export namespace
|
|
32
|
-
export
|
|
33
|
-
|
|
30
|
+
export namespace Api {
|
|
31
|
+
export namespace Counter {
|
|
32
|
+
export namespace Get {
|
|
33
|
+
export type Input = Expand<Record<string, never>>;
|
|
34
|
+
export type Output = Expand<{
|
|
34
35
|
count: number;
|
|
35
36
|
}>;
|
|
36
|
-
|
|
37
|
+
}
|
|
38
|
+
export type Get = { Input: Get.Input; Output: Get.Output };
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
export namespace Increment {
|
|
41
|
+
export type Input = Expand<Record<string, never>>;
|
|
42
|
+
export type Output = Expand<{
|
|
41
43
|
count: number;
|
|
42
44
|
}>;
|
|
43
|
-
|
|
45
|
+
}
|
|
46
|
+
export type Increment = { Input: Increment.Input; Output: Increment.Output };
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
export namespace Decrement {
|
|
49
|
+
export type Input = Expand<Record<string, never>>;
|
|
50
|
+
export type Output = Expand<{
|
|
48
51
|
count: number;
|
|
49
52
|
}>;
|
|
50
|
-
|
|
53
|
+
}
|
|
54
|
+
export type Decrement = { Input: Decrement.Input; Output: Decrement.Output };
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
export namespace Reset {
|
|
57
|
+
export type Input = Expand<Record<string, never>>;
|
|
58
|
+
export type Output = Expand<{
|
|
55
59
|
count: number;
|
|
56
60
|
}>;
|
|
61
|
+
}
|
|
62
|
+
export type Reset = { Input: Reset.Input; Output: Reset.Output };
|
|
57
63
|
}
|
|
58
|
-
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
export namespace Cache {
|
|
66
|
+
export namespace Set {
|
|
67
|
+
export type Input = Expand<{
|
|
63
68
|
key: string;
|
|
64
69
|
value: any;
|
|
65
70
|
ttl?: number;
|
|
66
71
|
}>;
|
|
67
|
-
|
|
72
|
+
export type Output = Expand<{
|
|
68
73
|
success: boolean;
|
|
69
74
|
}>;
|
|
70
|
-
|
|
75
|
+
}
|
|
76
|
+
export type Set = { Input: Set.Input; Output: Set.Output };
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
export namespace Get {
|
|
79
|
+
export type Input = Expand<{
|
|
74
80
|
key: string;
|
|
75
81
|
}>;
|
|
76
|
-
|
|
82
|
+
export type Output = Expand<{
|
|
77
83
|
value?: any;
|
|
78
84
|
exists: boolean;
|
|
79
85
|
}>;
|
|
80
|
-
|
|
86
|
+
}
|
|
87
|
+
export type Get = { Input: Get.Input; Output: Get.Output };
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
export namespace Delete {
|
|
90
|
+
export type Input = Expand<{
|
|
84
91
|
key: string;
|
|
85
92
|
}>;
|
|
86
|
-
|
|
93
|
+
export type Output = Expand<{
|
|
87
94
|
success: boolean;
|
|
88
95
|
}>;
|
|
89
|
-
|
|
96
|
+
}
|
|
97
|
+
export type Delete = { Input: Delete.Input; Output: Delete.Output };
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
export namespace Keys {
|
|
100
|
+
export type Input = Expand<Record<string, never>>;
|
|
101
|
+
export type Output = Expand<{
|
|
94
102
|
keys: string[];
|
|
95
103
|
}>;
|
|
104
|
+
}
|
|
105
|
+
export type Keys = { Input: Keys.Input; Output: Keys.Output };
|
|
96
106
|
}
|
|
97
|
-
}
|
|
98
107
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
export namespace Jobs {
|
|
109
|
+
export namespace Enqueue {
|
|
110
|
+
export type Input = Expand<{
|
|
102
111
|
name: string;
|
|
103
112
|
data: any;
|
|
104
113
|
delay?: number;
|
|
105
114
|
}>;
|
|
106
|
-
|
|
115
|
+
export type Output = Expand<{
|
|
107
116
|
jobId: string;
|
|
108
117
|
}>;
|
|
109
|
-
|
|
118
|
+
}
|
|
119
|
+
export type Enqueue = { Input: Enqueue.Input; Output: Enqueue.Output };
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
export namespace Stats {
|
|
122
|
+
export type Input = Expand<Record<string, never>>;
|
|
123
|
+
export type Output = Expand<{
|
|
114
124
|
pending: number;
|
|
115
125
|
running: number;
|
|
116
126
|
completed: number;
|
|
117
127
|
}>;
|
|
128
|
+
}
|
|
129
|
+
export type Stats = { Input: Stats.Input; Output: Stats.Output };
|
|
118
130
|
}
|
|
119
|
-
}
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
export namespace Cron {
|
|
133
|
+
export namespace List {
|
|
134
|
+
export type Input = Expand<Record<string, never>>;
|
|
135
|
+
export type Output = Expand<{
|
|
125
136
|
tasks: {
|
|
126
137
|
id: string;
|
|
127
138
|
name: string;
|
|
@@ -131,64 +142,71 @@ export namespace Routes {
|
|
|
131
142
|
nextRun?: string;
|
|
132
143
|
}[];
|
|
133
144
|
}>;
|
|
145
|
+
}
|
|
146
|
+
export type List = { Input: List.Input; Output: List.Output };
|
|
134
147
|
}
|
|
135
|
-
}
|
|
136
148
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
149
|
+
export namespace Ratelimit {
|
|
150
|
+
export namespace Check {
|
|
151
|
+
export type Input = Expand<{
|
|
140
152
|
key: string;
|
|
141
153
|
limit: number;
|
|
142
154
|
window: number;
|
|
143
155
|
}>;
|
|
144
|
-
|
|
156
|
+
export type Output = Expand<{
|
|
145
157
|
allowed: boolean;
|
|
146
158
|
remaining: number;
|
|
147
159
|
resetAt: Date;
|
|
148
160
|
}>;
|
|
149
|
-
|
|
161
|
+
}
|
|
162
|
+
export type Check = { Input: Check.Input; Output: Check.Output };
|
|
150
163
|
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
export namespace Reset {
|
|
165
|
+
export type Input = Expand<{
|
|
153
166
|
key: string;
|
|
154
167
|
}>;
|
|
155
|
-
|
|
168
|
+
export type Output = Expand<{
|
|
156
169
|
success: boolean;
|
|
157
170
|
}>;
|
|
171
|
+
}
|
|
172
|
+
export type Reset = { Input: Reset.Input; Output: Reset.Output };
|
|
158
173
|
}
|
|
159
|
-
}
|
|
160
174
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
export namespace Events {
|
|
176
|
+
export namespace Emit {
|
|
177
|
+
export type Input = Expand<{
|
|
164
178
|
event: string;
|
|
165
179
|
data: any;
|
|
166
180
|
}>;
|
|
167
|
-
|
|
181
|
+
export type Output = Expand<{
|
|
168
182
|
success: boolean;
|
|
169
183
|
}>;
|
|
184
|
+
}
|
|
185
|
+
export type Emit = { Input: Emit.Input; Output: Emit.Output };
|
|
170
186
|
}
|
|
171
|
-
}
|
|
172
187
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
188
|
+
export namespace Sse {
|
|
189
|
+
export namespace Broadcast {
|
|
190
|
+
export type Input = Expand<{
|
|
176
191
|
channel: string;
|
|
177
192
|
event: string;
|
|
178
193
|
data: any;
|
|
179
194
|
}>;
|
|
180
|
-
|
|
195
|
+
export type Output = Expand<{
|
|
181
196
|
success: boolean;
|
|
182
197
|
recipients: number;
|
|
183
198
|
}>;
|
|
184
|
-
|
|
199
|
+
}
|
|
200
|
+
export type Broadcast = { Input: Broadcast.Input; Output: Broadcast.Output };
|
|
185
201
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
202
|
+
export namespace Clients {
|
|
203
|
+
export type Input = Expand<Record<string, never>>;
|
|
204
|
+
export type Output = Expand<{
|
|
189
205
|
total: number;
|
|
190
206
|
byChannel: number;
|
|
191
207
|
}>;
|
|
208
|
+
}
|
|
209
|
+
export type Clients = { Input: Clients.Input; Output: Clients.Output };
|
|
192
210
|
}
|
|
193
211
|
}
|
|
194
212
|
}
|
|
@@ -202,41 +220,37 @@ export class ApiClient extends UnifiedApiClientBase {
|
|
|
202
220
|
super(options);
|
|
203
221
|
}
|
|
204
222
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
sse = {
|
|
238
|
-
broadcast: (input: Routes.Sse.broadcast.Input): Promise<Routes.Sse.broadcast.Output> => this.request("api.sse.broadcast", input),
|
|
239
|
-
clients: (input: Routes.Sse.clients.Input): Promise<Routes.Sse.clients.Output> => this.request("api.sse.clients", input)
|
|
223
|
+
api = {
|
|
224
|
+
counter: {
|
|
225
|
+
get: (input: Routes.Api.Counter.Get.Input): Promise<Routes.Api.Counter.Get.Output> => this.request("api.counter.get", input),
|
|
226
|
+
increment: (input: Routes.Api.Counter.Increment.Input): Promise<Routes.Api.Counter.Increment.Output> => this.request("api.counter.increment", input),
|
|
227
|
+
decrement: (input: Routes.Api.Counter.Decrement.Input): Promise<Routes.Api.Counter.Decrement.Output> => this.request("api.counter.decrement", input),
|
|
228
|
+
reset: (input: Routes.Api.Counter.Reset.Input): Promise<Routes.Api.Counter.Reset.Output> => this.request("api.counter.reset", input)
|
|
229
|
+
},
|
|
230
|
+
cache: {
|
|
231
|
+
set: (input: Routes.Api.Cache.Set.Input): Promise<Routes.Api.Cache.Set.Output> => this.request("api.cache.set", input),
|
|
232
|
+
get: (input: Routes.Api.Cache.Get.Input): Promise<Routes.Api.Cache.Get.Output> => this.request("api.cache.get", input),
|
|
233
|
+
delete: (input: Routes.Api.Cache.Delete.Input): Promise<Routes.Api.Cache.Delete.Output> => this.request("api.cache.delete", input),
|
|
234
|
+
keys: (input: Routes.Api.Cache.Keys.Input): Promise<Routes.Api.Cache.Keys.Output> => this.request("api.cache.keys", input)
|
|
235
|
+
},
|
|
236
|
+
jobs: {
|
|
237
|
+
enqueue: (input: Routes.Api.Jobs.Enqueue.Input): Promise<Routes.Api.Jobs.Enqueue.Output> => this.request("api.jobs.enqueue", input),
|
|
238
|
+
stats: (input: Routes.Api.Jobs.Stats.Input): Promise<Routes.Api.Jobs.Stats.Output> => this.request("api.jobs.stats", input)
|
|
239
|
+
},
|
|
240
|
+
cron: {
|
|
241
|
+
list: (input: Routes.Api.Cron.List.Input): Promise<Routes.Api.Cron.List.Output> => this.request("api.cron.list", input)
|
|
242
|
+
},
|
|
243
|
+
ratelimit: {
|
|
244
|
+
check: (input: Routes.Api.Ratelimit.Check.Input): Promise<Routes.Api.Ratelimit.Check.Output> => this.request("api.ratelimit.check", input),
|
|
245
|
+
reset: (input: Routes.Api.Ratelimit.Reset.Input): Promise<Routes.Api.Ratelimit.Reset.Output> => this.request("api.ratelimit.reset", input)
|
|
246
|
+
},
|
|
247
|
+
events: {
|
|
248
|
+
emit: (input: Routes.Api.Events.Emit.Input): Promise<Routes.Api.Events.Emit.Output> => this.request("api.events.emit", input)
|
|
249
|
+
},
|
|
250
|
+
sse: {
|
|
251
|
+
broadcast: (input: Routes.Api.Sse.Broadcast.Input): Promise<Routes.Api.Sse.Broadcast.Output> => this.request("api.sse.broadcast", input),
|
|
252
|
+
clients: (input: Routes.Api.Sse.Clients.Input): Promise<Routes.Api.Sse.Clients.Output> => this.request("api.sse.clients", input)
|
|
253
|
+
}
|
|
240
254
|
};
|
|
241
255
|
}
|
|
242
256
|
|
|
@@ -8,7 +8,7 @@ export const load: PageServerLoad = async ({ locals }) => {
|
|
|
8
8
|
|
|
9
9
|
try {
|
|
10
10
|
// Direct service call through typed client
|
|
11
|
-
const result = await client.counter.get({});
|
|
11
|
+
const result = await client.api.counter.get({});
|
|
12
12
|
return {
|
|
13
13
|
count: result.count,
|
|
14
14
|
loadedAt: new Date().toISOString(),
|
|
@@ -72,37 +72,37 @@
|
|
|
72
72
|
) {
|
|
73
73
|
counterLoading = true;
|
|
74
74
|
|
|
75
|
-
const result = await client.counter[action]({});
|
|
75
|
+
const result = await client.api.counter[action]({});
|
|
76
76
|
count = result.count;
|
|
77
77
|
counterLoading = false;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// Cache actions - using typed client
|
|
81
81
|
async function cacheSet() {
|
|
82
|
-
await client.cache.set({ key: cacheKey, value: cacheValue, ttl: cacheTTL });
|
|
82
|
+
await client.api.cache.set({ key: cacheKey, value: cacheValue, ttl: cacheTTL });
|
|
83
83
|
cacheResult = { action: "set", success: true };
|
|
84
84
|
refreshCacheKeys();
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
async function cacheGet() {
|
|
88
|
-
cacheResult = await client.cache.get({ key: cacheKey });
|
|
88
|
+
cacheResult = await client.api.cache.get({ key: cacheKey });
|
|
89
89
|
refreshCacheKeys();
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
async function cacheDelete() {
|
|
93
|
-
await client.cache.delete({ key: cacheKey });
|
|
93
|
+
await client.api.cache.delete({ key: cacheKey });
|
|
94
94
|
cacheResult = { action: "deleted", success: true };
|
|
95
95
|
refreshCacheKeys();
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
async function refreshCacheKeys() {
|
|
99
|
-
const result = await client.cache.keys({});
|
|
99
|
+
const result = await client.api.cache.keys({});
|
|
100
100
|
cacheKeys = result.keys || [];
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// Jobs actions - using typed client
|
|
104
104
|
async function enqueueJob() {
|
|
105
|
-
const result = (await client.jobs.enqueue({
|
|
105
|
+
const result = (await client.api.jobs.enqueue({
|
|
106
106
|
name: "demo-job",
|
|
107
107
|
data: { message: jobMessage },
|
|
108
108
|
delay: jobDelay > 0 ? jobDelay : undefined,
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
async function refreshJobStats() {
|
|
115
|
-
jobStats = (await client.jobs.stats({})) as {
|
|
115
|
+
jobStats = (await client.api.jobs.stats({})) as {
|
|
116
116
|
pending: number;
|
|
117
117
|
running: number;
|
|
118
118
|
completed: number;
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
|
|
122
122
|
// Rate limiter actions - using typed client
|
|
123
123
|
async function checkRateLimit() {
|
|
124
|
-
rateLimitResult = await client.ratelimit.check({
|
|
124
|
+
rateLimitResult = await client.api.ratelimit.check({
|
|
125
125
|
key: rateLimitKey,
|
|
126
126
|
limit: rateLimitMax,
|
|
127
127
|
window: rateLimitWindow,
|
|
@@ -129,13 +129,13 @@
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
async function resetRateLimit() {
|
|
132
|
-
await client.ratelimit.reset({ key: rateLimitKey });
|
|
132
|
+
await client.api.ratelimit.reset({ key: rateLimitKey });
|
|
133
133
|
rateLimitResult = { reset: true, message: "Rate limit reset" };
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
// Cron actions - using typed client
|
|
137
137
|
async function refreshCronTasks() {
|
|
138
|
-
const result = (await client.cron.list({})) as { tasks: CronTask[] };
|
|
138
|
+
const result = (await client.api.cron.list({})) as { tasks: CronTask[] };
|
|
139
139
|
cronTasks = result.tasks;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
async function emitEvent() {
|
|
144
144
|
try {
|
|
145
145
|
const parsedData = JSON.parse(eventData);
|
|
146
|
-
await client.events.emit({ event: eventName, data: parsedData });
|
|
146
|
+
await client.api.events.emit({ event: eventName, data: parsedData });
|
|
147
147
|
} catch (e) {
|
|
148
148
|
console.error("Invalid JSON:", e);
|
|
149
149
|
}
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
|
|
152
152
|
// SSE actions - using typed client
|
|
153
153
|
async function manualBroadcast() {
|
|
154
|
-
await client.
|
|
154
|
+
await client.api.sse.broadcast({
|
|
155
155
|
channel: "events",
|
|
156
156
|
event: "manual",
|
|
157
157
|
data: {
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
async function refreshSSEClients() {
|
|
167
|
-
sseClients = (await client.
|
|
167
|
+
sseClients = (await client.api.sse.clients({})) as {
|
|
168
168
|
total: number;
|
|
169
169
|
byChannel: number;
|
|
170
170
|
};
|