@getjack/jack 0.1.13 → 0.1.15
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 +2 -6
- package/src/commands/clone.ts +6 -6
- package/src/commands/down.ts +21 -3
- package/src/commands/link.ts +8 -3
- package/src/commands/secrets.ts +11 -14
- package/src/commands/services.ts +148 -5
- package/src/commands/update.ts +53 -0
- package/src/index.ts +31 -0
- package/src/lib/auth/login-flow.ts +11 -3
- package/src/lib/control-plane.ts +47 -0
- package/src/lib/deploy-mode.ts +23 -2
- package/src/lib/deploy-upload.ts +9 -0
- package/src/lib/hooks.ts +22 -45
- package/src/lib/project-operations.ts +401 -313
- package/src/lib/project-resolver.ts +12 -0
- package/src/lib/prompts.ts +23 -21
- package/src/lib/services/db-create.ts +187 -0
- package/src/lib/services/db-list.ts +56 -0
- package/src/lib/version-check.ts +170 -0
- package/src/mcp/resources/index.ts +32 -0
- package/src/mcp/tools/index.ts +131 -1
- package/templates/miniapp/.jack.json +5 -5
- package/templates/miniapp/public/.well-known/farcaster.json +1 -1
- package/templates/miniapp/src/lib/api.ts +71 -3
- package/templates/miniapp/src/worker.ts +44 -0
package/src/mcp/tools/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { z } from "zod";
|
|
|
4
4
|
import { JackError, JackErrorCode } from "../../lib/errors.ts";
|
|
5
5
|
import { createProject, deployProject, getProjectStatus } from "../../lib/project-operations.ts";
|
|
6
6
|
import { listAllProjects } from "../../lib/project-resolver.ts";
|
|
7
|
+
import { createDatabase } from "../../lib/services/db-create.ts";
|
|
8
|
+
import { listDatabases } from "../../lib/services/db-list.ts";
|
|
7
9
|
import { Events, track, withTelemetry } from "../../lib/telemetry.ts";
|
|
8
10
|
import type { DebugLogger, McpServerOptions } from "../types.ts";
|
|
9
11
|
import { formatErrorResponse, formatSuccessResponse } from "../utils.ts";
|
|
@@ -36,6 +38,21 @@ const ListProjectsSchema = z.object({
|
|
|
36
38
|
.describe("Filter projects by status (defaults to 'all')"),
|
|
37
39
|
});
|
|
38
40
|
|
|
41
|
+
const CreateDatabaseSchema = z.object({
|
|
42
|
+
name: z.string().optional().describe("Database name (auto-generated if not provided)"),
|
|
43
|
+
project_path: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const ListDatabasesSchema = z.object({
|
|
50
|
+
project_path: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
54
|
+
});
|
|
55
|
+
|
|
39
56
|
export function registerTools(server: McpServer, _options: McpServerOptions, debug: DebugLogger) {
|
|
40
57
|
// Register tool list handler
|
|
41
58
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -107,6 +124,37 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
107
124
|
},
|
|
108
125
|
},
|
|
109
126
|
},
|
|
127
|
+
{
|
|
128
|
+
name: "create_database",
|
|
129
|
+
description:
|
|
130
|
+
"Create a D1 database for a project. Returns deploy_required=true since binding needs deploy to activate.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
name: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "Database name (auto-generated if not provided)",
|
|
137
|
+
},
|
|
138
|
+
project_path: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Path to project directory (defaults to current directory)",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: "list_databases",
|
|
147
|
+
description: "List all D1 databases for a project.",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
project_path: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Path to project directory (defaults to current directory)",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
110
158
|
],
|
|
111
159
|
};
|
|
112
160
|
});
|
|
@@ -195,7 +243,15 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
195
243
|
const wrappedGetProjectStatus = withTelemetry(
|
|
196
244
|
"get_project_status",
|
|
197
245
|
async (name?: string, projectPath?: string) => {
|
|
198
|
-
|
|
246
|
+
const status = await getProjectStatus(name, projectPath);
|
|
247
|
+
if (status === null) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
// Add available_services to tell agents what services can be created
|
|
251
|
+
return {
|
|
252
|
+
...status,
|
|
253
|
+
available_services: ["d1", "kv", "r2"],
|
|
254
|
+
};
|
|
199
255
|
},
|
|
200
256
|
{ platform: "mcp" },
|
|
201
257
|
);
|
|
@@ -259,6 +315,80 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
259
315
|
};
|
|
260
316
|
}
|
|
261
317
|
|
|
318
|
+
case "create_database": {
|
|
319
|
+
const args = CreateDatabaseSchema.parse(request.params.arguments ?? {});
|
|
320
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
321
|
+
|
|
322
|
+
const wrappedCreateDatabase = withTelemetry(
|
|
323
|
+
"create_database",
|
|
324
|
+
async (projectDir: string, name?: string) => {
|
|
325
|
+
const result = await createDatabase(projectDir, {
|
|
326
|
+
name,
|
|
327
|
+
interactive: false,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Track business event
|
|
331
|
+
track(Events.SERVICE_CREATED, {
|
|
332
|
+
service_type: "d1",
|
|
333
|
+
platform: "mcp",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
database_name: result.databaseName,
|
|
338
|
+
database_id: result.databaseId,
|
|
339
|
+
binding_name: result.bindingName,
|
|
340
|
+
created: result.created,
|
|
341
|
+
deploy_required: true,
|
|
342
|
+
};
|
|
343
|
+
},
|
|
344
|
+
{ platform: "mcp" },
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const result = await wrappedCreateDatabase(projectPath, args.name);
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
content: [
|
|
351
|
+
{
|
|
352
|
+
type: "text",
|
|
353
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
case "list_databases": {
|
|
360
|
+
const args = ListDatabasesSchema.parse(request.params.arguments ?? {});
|
|
361
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
362
|
+
|
|
363
|
+
const wrappedListDatabases = withTelemetry(
|
|
364
|
+
"list_databases",
|
|
365
|
+
async (projectDir: string) => {
|
|
366
|
+
const databases = await listDatabases(projectDir);
|
|
367
|
+
return {
|
|
368
|
+
databases: databases.map((db) => ({
|
|
369
|
+
name: db.name,
|
|
370
|
+
binding: db.binding,
|
|
371
|
+
id: db.id,
|
|
372
|
+
size_bytes: db.sizeBytes,
|
|
373
|
+
num_tables: db.numTables,
|
|
374
|
+
})),
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
{ platform: "mcp" },
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const result = await wrappedListDatabases(projectPath);
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
content: [
|
|
384
|
+
{
|
|
385
|
+
type: "text",
|
|
386
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
262
392
|
default:
|
|
263
393
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
264
394
|
}
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"path": "public/.well-known/farcaster.json",
|
|
51
51
|
"successMessage": "Updated manifest URLs to {{url}}",
|
|
52
52
|
"set": {
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
53
|
+
"frame.name": "{{name}}",
|
|
54
|
+
"frame.homeUrl": "{{url}}",
|
|
55
|
+
"frame.iconUrl": "{{url}}/icon.png",
|
|
56
|
+
"frame.imageUrl": "{{url}}/og.png",
|
|
57
|
+
"frame.splashImageUrl": "{{url}}/icon.png"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
import { hc } from "hono/client";
|
|
2
2
|
import type { AppType } from "../worker";
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// Create Hono RPC client
|
|
5
|
+
// Note: Type inference may show 'unknown' in IDE but works at runtime
|
|
6
|
+
// The explicit response types below ensure type safety for API consumers
|
|
7
|
+
const client = hc<AppType>("/");
|
|
8
|
+
|
|
9
|
+
// Export typed client - if AppType inference fails, these explicit types provide safety
|
|
10
|
+
export const api = client as {
|
|
11
|
+
api: {
|
|
12
|
+
guestbook: {
|
|
13
|
+
$get: () => Promise<
|
|
14
|
+
Response & {
|
|
15
|
+
json: () => Promise<{
|
|
16
|
+
entries: Array<{
|
|
17
|
+
id: number;
|
|
18
|
+
fid: number;
|
|
19
|
+
username: string;
|
|
20
|
+
display_name: string | null;
|
|
21
|
+
pfp_url: string | null;
|
|
22
|
+
message: string;
|
|
23
|
+
created_at: string;
|
|
24
|
+
}>;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
>;
|
|
28
|
+
$post: (options: {
|
|
29
|
+
json: {
|
|
30
|
+
fid: number;
|
|
31
|
+
username: string;
|
|
32
|
+
displayName?: string;
|
|
33
|
+
pfpUrl?: string;
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
}) => Promise<
|
|
37
|
+
Response & {
|
|
38
|
+
json: () => Promise<{
|
|
39
|
+
entry?: {
|
|
40
|
+
id: number;
|
|
41
|
+
fid: number;
|
|
42
|
+
username: string;
|
|
43
|
+
display_name: string | null;
|
|
44
|
+
pfp_url: string | null;
|
|
45
|
+
message: string;
|
|
46
|
+
created_at: string;
|
|
47
|
+
};
|
|
48
|
+
error?: string;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
>;
|
|
52
|
+
};
|
|
53
|
+
ai: {
|
|
54
|
+
generate: {
|
|
55
|
+
$post: (options: {
|
|
56
|
+
json: { prompt: string; schema?: object };
|
|
57
|
+
}) => Promise<
|
|
58
|
+
Response & {
|
|
59
|
+
json: () => Promise<{
|
|
60
|
+
result?: string;
|
|
61
|
+
provider?: "openai" | "workers-ai";
|
|
62
|
+
error?: string;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
>;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
notifications: {
|
|
69
|
+
$get: (options?: {
|
|
70
|
+
query: { fid: string };
|
|
71
|
+
}) => Promise<Response & { json: () => Promise<unknown> }>;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Server-side Worker - handles API routes, keeps secrets secure
|
|
2
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
3
|
|
|
3
4
|
import { Hono } from "hono";
|
|
4
5
|
import { cors } from "hono/cors";
|
|
@@ -624,6 +625,49 @@ app.get("/share", (c) => {
|
|
|
624
625
|
});
|
|
625
626
|
});
|
|
626
627
|
|
|
628
|
+
// GET / - Inject fc:miniapp meta tags for Farcaster embeds on the main page
|
|
629
|
+
// Without this, sharing the app URL in a cast won't render an embed card
|
|
630
|
+
app.get("/", async (c) => {
|
|
631
|
+
// Fetch the static index.html from Vite build
|
|
632
|
+
const response = await c.env.ASSETS.fetch(c.req.raw);
|
|
633
|
+
const html = await response.text();
|
|
634
|
+
|
|
635
|
+
const baseUrl = getBaseUrl(c.env, c);
|
|
636
|
+
|
|
637
|
+
// Local dev - serve without meta tags (they require https URLs)
|
|
638
|
+
if (!baseUrl) {
|
|
639
|
+
return c.html(html);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Build embed JSON (same structure as /share route)
|
|
643
|
+
const embedJson = JSON.stringify({
|
|
644
|
+
version: "1",
|
|
645
|
+
imageUrl: `${baseUrl}/og.png`,
|
|
646
|
+
button: {
|
|
647
|
+
title: "Open App",
|
|
648
|
+
action: {
|
|
649
|
+
type: "launch_miniapp",
|
|
650
|
+
name: "jack-template",
|
|
651
|
+
url: baseUrl,
|
|
652
|
+
splashImageUrl: `${baseUrl}/icon.png`,
|
|
653
|
+
splashBackgroundColor: "#0a0a0a",
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
// Meta tags to inject
|
|
659
|
+
const metaTags = `
|
|
660
|
+
<meta property="og:title" content="jack-template" />
|
|
661
|
+
<meta property="og:image" content="${baseUrl}/og.png" />
|
|
662
|
+
<meta name="fc:miniapp" content='${embedJson}' />
|
|
663
|
+
<meta name="fc:frame" content='${embedJson}' />
|
|
664
|
+
`;
|
|
665
|
+
|
|
666
|
+
// Inject before </head>
|
|
667
|
+
const injectedHtml = html.replace("</head>", `${metaTags}</head>`);
|
|
668
|
+
return c.html(injectedHtml);
|
|
669
|
+
});
|
|
670
|
+
|
|
627
671
|
// Serve React app for all other routes
|
|
628
672
|
app.get("*", (c) => c.env.ASSETS.fetch(c.req.raw));
|
|
629
673
|
|