@donkeylabs/cli 2.0.15 → 2.0.17
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/config.ts +610 -0
- package/src/commands/deploy-enhanced.ts +354 -0
- package/src/commands/deploy.ts +204 -0
- package/src/commands/docs.ts +207 -0
- package/src/commands/init-enhanced.ts +1994 -0
- package/src/deployment/manager.ts +356 -0
- package/src/index.ts +58 -19
- package/templates/starter/.env.example +0 -44
- package/templates/starter/.gitignore.template +0 -4
- package/templates/starter/donkeylabs.config.ts +0 -6
- package/templates/starter/package.json +0 -21
- package/templates/starter/src/index.ts +0 -54
- package/templates/starter/src/plugins/stats/index.ts +0 -105
- package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
- package/templates/starter/src/routes/health/index.ts +0 -19
- package/templates/starter/tsconfig.json +0 -27
- package/templates/sveltekit-app/.env.example +0 -59
- package/templates/sveltekit-app/README.md +0 -103
- package/templates/sveltekit-app/bun.lock +0 -683
- package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
- package/templates/sveltekit-app/package.json +0 -38
- package/templates/sveltekit-app/src/app.css +0 -40
- package/templates/sveltekit-app/src/app.html +0 -12
- package/templates/sveltekit-app/src/hooks.server.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
- package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
- package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
- package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
- package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
- package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
- package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
- package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
- package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
- package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
- package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
- package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
- package/templates/sveltekit-app/src/server/events.ts +0 -28
- package/templates/sveltekit-app/src/server/index.ts +0 -124
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
- package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
- package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
- package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
- package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
- package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
- package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
- package/templates/sveltekit-app/static/robots.txt +0 -3
- package/templates/sveltekit-app/svelte.config.ts +0 -17
- package/templates/sveltekit-app/tsconfig.json +0 -20
- package/templates/sveltekit-app/vite.config.ts +0 -12
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// packages/cli/src/deployment/manager.ts
|
|
2
|
+
/**
|
|
3
|
+
* Deployment Management System
|
|
4
|
+
* Handles deployment history, versioning, rollbacks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
|
|
12
|
+
export interface DeploymentConfig {
|
|
13
|
+
projectName: string;
|
|
14
|
+
platform: "vercel" | "cloudflare" | "aws" | "vps";
|
|
15
|
+
environment: "production" | "staging" | "development";
|
|
16
|
+
versionStrategy: "semver" | "git-sha" | "timestamp";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Deployment {
|
|
20
|
+
id: string;
|
|
21
|
+
version: string;
|
|
22
|
+
platform: string;
|
|
23
|
+
environment: string;
|
|
24
|
+
timestamp: string;
|
|
25
|
+
gitSha: string;
|
|
26
|
+
gitMessage: string;
|
|
27
|
+
status: "success" | "failed" | "rolling-back";
|
|
28
|
+
url?: string;
|
|
29
|
+
logs: string[];
|
|
30
|
+
metadata: Record<string, any>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DeploymentHistory {
|
|
34
|
+
deployments: Deployment[];
|
|
35
|
+
currentVersion: string;
|
|
36
|
+
lastDeployedAt: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class DeploymentManager {
|
|
40
|
+
private projectDir: string;
|
|
41
|
+
private historyFile: string;
|
|
42
|
+
private config: DeploymentConfig;
|
|
43
|
+
|
|
44
|
+
constructor(projectDir: string, config: DeploymentConfig) {
|
|
45
|
+
this.projectDir = projectDir;
|
|
46
|
+
this.config = config;
|
|
47
|
+
this.historyFile = join(projectDir, ".donkeylabs", "deployments.json");
|
|
48
|
+
this.ensureHistoryDir();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get next version based on strategy
|
|
53
|
+
*/
|
|
54
|
+
getNextVersion(bump: "major" | "minor" | "patch" = "patch"): string {
|
|
55
|
+
const { versionStrategy } = this.config;
|
|
56
|
+
|
|
57
|
+
if (versionStrategy === "git-sha") {
|
|
58
|
+
return this.getGitSha();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (versionStrategy === "timestamp") {
|
|
62
|
+
return Date.now().toString();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Semver
|
|
66
|
+
const current = this.getCurrentVersion();
|
|
67
|
+
const [major, minor, patch] = current.split(".").map(Number);
|
|
68
|
+
|
|
69
|
+
switch (bump) {
|
|
70
|
+
case "major":
|
|
71
|
+
return `${major + 1}.0.0`;
|
|
72
|
+
case "minor":
|
|
73
|
+
return `${major}.${minor + 1}.0`;
|
|
74
|
+
case "patch":
|
|
75
|
+
default:
|
|
76
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Record a new deployment
|
|
82
|
+
*/
|
|
83
|
+
async recordDeployment(
|
|
84
|
+
version: string,
|
|
85
|
+
platform: string,
|
|
86
|
+
status: Deployment["status"],
|
|
87
|
+
url?: string,
|
|
88
|
+
metadata?: Record<string, any>
|
|
89
|
+
): Promise<Deployment> {
|
|
90
|
+
const deployment: Deployment = {
|
|
91
|
+
id: randomUUID(),
|
|
92
|
+
version,
|
|
93
|
+
platform,
|
|
94
|
+
environment: this.config.environment,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
gitSha: this.getGitSha(),
|
|
97
|
+
gitMessage: this.getGitMessage(),
|
|
98
|
+
status,
|
|
99
|
+
url,
|
|
100
|
+
logs: [],
|
|
101
|
+
metadata: metadata || {},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const history = this.getHistory();
|
|
105
|
+
history.deployments.unshift(deployment);
|
|
106
|
+
history.currentVersion = version;
|
|
107
|
+
history.lastDeployedAt = deployment.timestamp;
|
|
108
|
+
|
|
109
|
+
// Keep only last 50 deployments
|
|
110
|
+
history.deployments = history.deployments.slice(0, 50);
|
|
111
|
+
|
|
112
|
+
this.saveHistory(history);
|
|
113
|
+
|
|
114
|
+
return deployment;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get deployment history
|
|
119
|
+
*/
|
|
120
|
+
getHistory(): DeploymentHistory {
|
|
121
|
+
if (!existsSync(this.historyFile)) {
|
|
122
|
+
return {
|
|
123
|
+
deployments: [],
|
|
124
|
+
currentVersion: "0.0.0",
|
|
125
|
+
lastDeployedAt: "",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return JSON.parse(readFileSync(this.historyFile, "utf-8"));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get specific deployment
|
|
134
|
+
*/
|
|
135
|
+
getDeployment(deploymentId: string): Deployment | null {
|
|
136
|
+
const history = this.getHistory();
|
|
137
|
+
return history.deployments.find((d) => d.id === deploymentId) || null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get last successful deployment
|
|
142
|
+
*/
|
|
143
|
+
getLastSuccessfulDeployment(): Deployment | null {
|
|
144
|
+
const history = this.getHistory();
|
|
145
|
+
return history.deployments.find((d) => d.status === "success") || null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Rollback to previous version
|
|
150
|
+
*/
|
|
151
|
+
async rollback(toVersion?: string): Promise<Deployment | null> {
|
|
152
|
+
const history = this.getHistory();
|
|
153
|
+
|
|
154
|
+
// Find target deployment
|
|
155
|
+
let targetDeployment: Deployment | undefined;
|
|
156
|
+
|
|
157
|
+
if (toVersion) {
|
|
158
|
+
targetDeployment = history.deployments.find((d) => d.version === toVersion);
|
|
159
|
+
} else {
|
|
160
|
+
// Find last successful deployment before current
|
|
161
|
+
const currentIndex = history.deployments.findIndex(
|
|
162
|
+
(d) => d.version === history.currentVersion
|
|
163
|
+
);
|
|
164
|
+
targetDeployment = history.deployments
|
|
165
|
+
.slice(currentIndex + 1)
|
|
166
|
+
.find((d) => d.status === "success");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!targetDeployment) {
|
|
170
|
+
throw new Error("No previous successful deployment found to rollback to");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Checkout the git sha
|
|
174
|
+
try {
|
|
175
|
+
execSync(`git checkout ${targetDeployment.gitSha}`, {
|
|
176
|
+
cwd: this.projectDir,
|
|
177
|
+
stdio: "pipe",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Mark as rolling back
|
|
181
|
+
targetDeployment.status = "rolling-back";
|
|
182
|
+
this.saveHistory(history);
|
|
183
|
+
|
|
184
|
+
// Redeploy
|
|
185
|
+
await this.redeploy(targetDeployment);
|
|
186
|
+
|
|
187
|
+
// Record the rollback
|
|
188
|
+
const rollbackDeployment = await this.recordDeployment(
|
|
189
|
+
`${targetDeployment.version}-rollback`,
|
|
190
|
+
targetDeployment.platform,
|
|
191
|
+
"success",
|
|
192
|
+
targetDeployment.url,
|
|
193
|
+
{ rollbackFrom: history.currentVersion, originalDeployment: targetDeployment.id }
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return rollbackDeployment;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
// Revert git checkout
|
|
199
|
+
execSync("git checkout -", { cwd: this.projectDir, stdio: "pipe" });
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get deployment statistics
|
|
206
|
+
*/
|
|
207
|
+
getStats(): {
|
|
208
|
+
totalDeployments: number;
|
|
209
|
+
successfulDeployments: number;
|
|
210
|
+
failedDeployments: number;
|
|
211
|
+
rollbackCount: number;
|
|
212
|
+
averageDeployTime: number;
|
|
213
|
+
deploymentsByPlatform: Record<string, number>;
|
|
214
|
+
} {
|
|
215
|
+
const history = this.getHistory();
|
|
216
|
+
const deployments = history.deployments;
|
|
217
|
+
|
|
218
|
+
const successful = deployments.filter((d) => d.status === "success");
|
|
219
|
+
const failed = deployments.filter((d) => d.status === "failed");
|
|
220
|
+
const rollbacks = deployments.filter((d) => d.metadata?.rollbackFrom);
|
|
221
|
+
|
|
222
|
+
const byPlatform: Record<string, number> = {};
|
|
223
|
+
for (const d of deployments) {
|
|
224
|
+
byPlatform[d.platform] = (byPlatform[d.platform] || 0) + 1;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
totalDeployments: deployments.length,
|
|
229
|
+
successfulDeployments: successful.length,
|
|
230
|
+
failedDeployments: failed.length,
|
|
231
|
+
rollbackCount: rollbacks.length,
|
|
232
|
+
averageDeployTime: 0, // Would need to track start/end times
|
|
233
|
+
deploymentsByPlatform: byPlatform,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* List deployments with filtering
|
|
239
|
+
*/
|
|
240
|
+
listDeployments(options?: {
|
|
241
|
+
platform?: string;
|
|
242
|
+
status?: Deployment["status"];
|
|
243
|
+
limit?: number;
|
|
244
|
+
}): Deployment[] {
|
|
245
|
+
let deployments = this.getHistory().deployments;
|
|
246
|
+
|
|
247
|
+
if (options?.platform) {
|
|
248
|
+
deployments = deployments.filter((d) => d.platform === options.platform);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (options?.status) {
|
|
252
|
+
deployments = deployments.filter((d) => d.status === options.status);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (options?.limit) {
|
|
256
|
+
deployments = deployments.slice(0, options.limit);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return deployments;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Compare two deployments
|
|
264
|
+
*/
|
|
265
|
+
compareDeployments(deploymentId1: string, deploymentId2: string): {
|
|
266
|
+
deployment1: Deployment;
|
|
267
|
+
deployment2: Deployment;
|
|
268
|
+
gitDiff: string;
|
|
269
|
+
} {
|
|
270
|
+
const d1 = this.getDeployment(deploymentId1);
|
|
271
|
+
const d2 = this.getDeployment(deploymentId2);
|
|
272
|
+
|
|
273
|
+
if (!d1 || !d2) {
|
|
274
|
+
throw new Error("One or both deployments not found");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const diff = execSync(
|
|
278
|
+
`git log --oneline ${d2.gitSha}..${d1.gitSha}`,
|
|
279
|
+
{ cwd: this.projectDir, encoding: "utf-8" }
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
deployment1: d1,
|
|
284
|
+
deployment2: d2,
|
|
285
|
+
gitDiff: diff,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private ensureHistoryDir(): void {
|
|
290
|
+
const dir = join(this.projectDir, ".donkeylabs");
|
|
291
|
+
if (!existsSync(dir)) {
|
|
292
|
+
mkdirSync(dir, { recursive: true });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private saveHistory(history: DeploymentHistory): void {
|
|
297
|
+
writeFileSync(this.historyFile, JSON.stringify(history, null, 2));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private getCurrentVersion(): string {
|
|
301
|
+
const pkgPath = join(this.projectDir, "package.json");
|
|
302
|
+
if (!existsSync(pkgPath)) {
|
|
303
|
+
return "0.0.0";
|
|
304
|
+
}
|
|
305
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
306
|
+
return pkg.version || "0.0.0";
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private getGitSha(): string {
|
|
310
|
+
try {
|
|
311
|
+
return execSync("git rev-parse --short HEAD", {
|
|
312
|
+
cwd: this.projectDir,
|
|
313
|
+
encoding: "utf-8",
|
|
314
|
+
}).trim();
|
|
315
|
+
} catch {
|
|
316
|
+
return "unknown";
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private getGitMessage(): string {
|
|
321
|
+
try {
|
|
322
|
+
return execSync("git log -1 --pretty=%B", {
|
|
323
|
+
cwd: this.projectDir,
|
|
324
|
+
encoding: "utf-8",
|
|
325
|
+
}).trim();
|
|
326
|
+
} catch {
|
|
327
|
+
return "unknown";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private async redeploy(deployment: Deployment): Promise<void> {
|
|
332
|
+
// Platform-specific redeploy logic
|
|
333
|
+
switch (deployment.platform) {
|
|
334
|
+
case "vercel":
|
|
335
|
+
execSync("vercel --prod", {
|
|
336
|
+
cwd: this.projectDir,
|
|
337
|
+
stdio: "inherit",
|
|
338
|
+
});
|
|
339
|
+
break;
|
|
340
|
+
case "cloudflare":
|
|
341
|
+
execSync("wrangler deploy", {
|
|
342
|
+
cwd: this.projectDir,
|
|
343
|
+
stdio: "inherit",
|
|
344
|
+
});
|
|
345
|
+
break;
|
|
346
|
+
// Add other platforms
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function createDeploymentManager(
|
|
352
|
+
projectDir: string,
|
|
353
|
+
config: DeploymentConfig
|
|
354
|
+
): DeploymentManager {
|
|
355
|
+
return new DeploymentManager(projectDir, config);
|
|
356
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,9 @@ const { positionals, values } = parseArgs({
|
|
|
17
17
|
help: { type: "boolean", short: "h" },
|
|
18
18
|
version: { type: "boolean", short: "v" },
|
|
19
19
|
type: { type: "string", short: "t" },
|
|
20
|
+
local: { type: "boolean", short: "l" },
|
|
21
|
+
list: { type: "boolean" },
|
|
22
|
+
output: { type: "string", short: "o" },
|
|
20
23
|
},
|
|
21
24
|
allowPositionals: true,
|
|
22
25
|
});
|
|
@@ -32,24 +35,37 @@ ${pc.bold("Usage:")}
|
|
|
32
35
|
donkeylabs <command> [options]
|
|
33
36
|
|
|
34
37
|
${pc.bold("Commands:")}
|
|
35
|
-
${pc.cyan("init")}
|
|
36
|
-
${pc.cyan("add")}
|
|
37
|
-
${pc.cyan("generate")}
|
|
38
|
-
${pc.cyan("plugin")}
|
|
39
|
-
${pc.cyan("
|
|
38
|
+
${pc.cyan("init")} Initialize a new project
|
|
39
|
+
${pc.cyan("add")} Add optional plugins (images, auth, etc.)
|
|
40
|
+
${pc.cyan("generate")} Generate types (registry, context, client)
|
|
41
|
+
${pc.cyan("plugin")} Plugin management
|
|
42
|
+
${pc.cyan("docs")} Sync documentation from installed package
|
|
43
|
+
${pc.cyan("deploy")} <platform> Deploy (vercel, cloudflare, aws, vps)
|
|
44
|
+
${pc.cyan("deploy history")} Show deployment history
|
|
45
|
+
${pc.cyan("deploy rollback")} Rollback to version
|
|
46
|
+
${pc.cyan("deploy stats")} Show deployment statistics
|
|
47
|
+
${pc.cyan("config")} Configure plugins, deployment, database
|
|
48
|
+
${pc.cyan("mcp")} Setup MCP server for AI-assisted development
|
|
40
49
|
|
|
41
50
|
${pc.bold("Options:")}
|
|
42
51
|
-h, --help Show this help message
|
|
43
52
|
-v, --version Show version number
|
|
44
53
|
-t, --type <type> Project type for init (server, sveltekit)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
-l, --local Use local workspace packages (for monorepo dev)
|
|
55
|
+
|
|
56
|
+
${pc.bold("Examples:")}
|
|
57
|
+
donkeylabs # Interactive menu
|
|
58
|
+
donkeylabs init # Interactive project setup
|
|
59
|
+
donkeylabs init --type server # Server-only project
|
|
60
|
+
donkeylabs init --type sveltekit # SvelteKit + adapter project
|
|
61
|
+
donkeylabs generate
|
|
62
|
+
donkeylabs plugin create myPlugin
|
|
63
|
+
donkeylabs docs # Sync all docs to ./docs/donkeylabs/
|
|
64
|
+
donkeylabs docs --list # List available docs
|
|
65
|
+
donkeylabs docs workflows # Sync specific doc
|
|
66
|
+
donkeylabs deploy vercel # Deploy to Vercel
|
|
67
|
+
donkeylabs config # Interactive configuration
|
|
68
|
+
donkeylabs config set DATABASE_URL postgresql://...
|
|
53
69
|
`);
|
|
54
70
|
}
|
|
55
71
|
|
|
@@ -78,12 +94,8 @@ async function main() {
|
|
|
78
94
|
|
|
79
95
|
switch (command) {
|
|
80
96
|
case "init":
|
|
81
|
-
const {
|
|
82
|
-
|
|
83
|
-
if (values.type) {
|
|
84
|
-
initArgs.push("--type", values.type);
|
|
85
|
-
}
|
|
86
|
-
await initCommand(initArgs);
|
|
97
|
+
const { initEnhancedCommand } = await import("./commands/init-enhanced");
|
|
98
|
+
await initEnhancedCommand(positionals.slice(1), { useLocalPackages: values.local });
|
|
87
99
|
break;
|
|
88
100
|
|
|
89
101
|
case "add":
|
|
@@ -107,6 +119,33 @@ async function main() {
|
|
|
107
119
|
await mcpCommand(positionals.slice(1));
|
|
108
120
|
break;
|
|
109
121
|
|
|
122
|
+
case "docs":
|
|
123
|
+
const { docsCommand } = await import("./commands/docs");
|
|
124
|
+
await docsCommand(positionals.slice(1), { list: values.list, output: values.output });
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case "deploy":
|
|
128
|
+
const subcommand = positionals[1];
|
|
129
|
+
if (subcommand === "history") {
|
|
130
|
+
const { deployHistoryCommand } = await import("./commands/deploy-enhanced");
|
|
131
|
+
await deployHistoryCommand(positionals.slice(2));
|
|
132
|
+
} else if (subcommand === "rollback") {
|
|
133
|
+
const { deployRollbackCommand } = await import("./commands/deploy-enhanced");
|
|
134
|
+
await deployRollbackCommand(positionals.slice(2));
|
|
135
|
+
} else if (subcommand === "stats") {
|
|
136
|
+
const { deployStatsCommand } = await import("./commands/deploy-enhanced");
|
|
137
|
+
await deployStatsCommand();
|
|
138
|
+
} else {
|
|
139
|
+
const { deployEnhancedCommand } = await import("./commands/deploy-enhanced");
|
|
140
|
+
await deployEnhancedCommand(positionals.slice(1));
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case "config":
|
|
145
|
+
const { configCommand } = await import("./commands/config");
|
|
146
|
+
await configCommand(positionals.slice(1));
|
|
147
|
+
break;
|
|
148
|
+
|
|
110
149
|
default:
|
|
111
150
|
console.error(pc.red(`Unknown command: ${command}`));
|
|
112
151
|
console.log(`Run ${pc.cyan("donkeylabs --help")} for available commands.`);
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# =============================================================================
|
|
2
|
-
# DATABASE
|
|
3
|
-
# =============================================================================
|
|
4
|
-
|
|
5
|
-
# SQLite database path (relative to project root)
|
|
6
|
-
# Use ":memory:" for in-memory database during development
|
|
7
|
-
DATABASE_URL=":memory:"
|
|
8
|
-
|
|
9
|
-
# For production, use a file path:
|
|
10
|
-
# DATABASE_URL="./data/app.db"
|
|
11
|
-
|
|
12
|
-
# =============================================================================
|
|
13
|
-
# SERVER
|
|
14
|
-
# =============================================================================
|
|
15
|
-
|
|
16
|
-
# Port for the API server
|
|
17
|
-
PORT=3000
|
|
18
|
-
|
|
19
|
-
# Node environment
|
|
20
|
-
NODE_ENV=development
|
|
21
|
-
|
|
22
|
-
# =============================================================================
|
|
23
|
-
# AUTHENTICATION (if using auth plugin)
|
|
24
|
-
# =============================================================================
|
|
25
|
-
|
|
26
|
-
# JWT secret for signing tokens (generate with: openssl rand -base64 32)
|
|
27
|
-
# JWT_SECRET=your-secret-key-here
|
|
28
|
-
|
|
29
|
-
# =============================================================================
|
|
30
|
-
# EXTERNAL SERVICES (examples)
|
|
31
|
-
# =============================================================================
|
|
32
|
-
|
|
33
|
-
# Email service
|
|
34
|
-
# RESEND_API_KEY=re_xxxxxxxxxxxx
|
|
35
|
-
|
|
36
|
-
# Payment processing
|
|
37
|
-
# STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx
|
|
38
|
-
|
|
39
|
-
# =============================================================================
|
|
40
|
-
# FEATURE FLAGS
|
|
41
|
-
# =============================================================================
|
|
42
|
-
|
|
43
|
-
# Enable debug logging
|
|
44
|
-
# DEBUG=true
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "my-donkeylabs-app",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "bun --watch src/index.ts",
|
|
7
|
-
"start": "bun src/index.ts",
|
|
8
|
-
"test": "bun test",
|
|
9
|
-
"gen:types": "donkeylabs generate"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"@donkeylabs/server": "^2.0.15",
|
|
13
|
-
"kysely": "^0.27.0",
|
|
14
|
-
"kysely-bun-sqlite": "^0.3.0",
|
|
15
|
-
"zod": "^3.24.0"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@donkeylabs/cli": "^2.0.11",
|
|
19
|
-
"@types/bun": "latest"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { AppServer, createRouter } from "@donkeylabs/server";
|
|
2
|
-
import { Kysely } from "kysely";
|
|
3
|
-
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
4
|
-
import { Database } from "bun:sqlite";
|
|
5
|
-
import { healthRouter } from "./routes/health";
|
|
6
|
-
import { statsPlugin } from "./plugins/stats";
|
|
7
|
-
|
|
8
|
-
// Simple in-memory database
|
|
9
|
-
const db = new Kysely<{}>({
|
|
10
|
-
dialect: new BunSqliteDialect({ database: new Database(":memory:") }),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const server = new AppServer({
|
|
14
|
-
port: Number(process.env.PORT) || 3000,
|
|
15
|
-
db,
|
|
16
|
-
config: { env: process.env.NODE_ENV || "development" },
|
|
17
|
-
generateTypes: {
|
|
18
|
-
output: "./.@donkeylabs/server/api.ts",
|
|
19
|
-
baseImport: 'import { ApiClientBase, type ApiClientOptions } from "@donkeylabs/server/client";',
|
|
20
|
-
baseClass: "ApiClientBase",
|
|
21
|
-
constructorSignature: "baseUrl: string, options?: ApiClientOptions",
|
|
22
|
-
constructorBody: "super(baseUrl, options);",
|
|
23
|
-
factoryFunction: `/**
|
|
24
|
-
* Create an API client instance
|
|
25
|
-
* @param baseUrl - The base URL of the API server
|
|
26
|
-
*/
|
|
27
|
-
export function createApi(baseUrl: string, options?: ApiClientOptions) {
|
|
28
|
-
return new ApiClient(baseUrl, options);
|
|
29
|
-
}`,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Register plugins
|
|
34
|
-
server.registerPlugin(statsPlugin);
|
|
35
|
-
|
|
36
|
-
const api = createRouter("api");
|
|
37
|
-
// Register routes
|
|
38
|
-
api.router(healthRouter);
|
|
39
|
-
|
|
40
|
-
server.use(api);
|
|
41
|
-
|
|
42
|
-
// Handle DONKEYLABS_GENERATE mode for CLI type generation
|
|
43
|
-
if (process.env.DONKEYLABS_GENERATE === "1") {
|
|
44
|
-
const routes = api.getRoutes().map((route) => ({
|
|
45
|
-
name: route.name,
|
|
46
|
-
handler: route.handler || "typed",
|
|
47
|
-
inputType: route.input ? "(generated)" : undefined,
|
|
48
|
-
outputType: route.output ? "(generated)" : undefined,
|
|
49
|
-
}));
|
|
50
|
-
console.log(JSON.stringify({ routes }));
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
await server.start();
|