@getjack/jack 0.1.32 → 0.1.33
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/deploys.ts +95 -0
- package/src/commands/link.ts +8 -0
- package/src/commands/mcp.ts +179 -4
- package/src/commands/rollback.ts +53 -0
- package/src/commands/services.ts +11 -1
- package/src/commands/ship.ts +3 -1
- package/src/commands/tokens.ts +16 -1
- package/src/commands/whoami.ts +43 -8
- package/src/index.ts +16 -0
- package/src/lib/agent-files.ts +54 -4
- package/src/lib/agent-integration.ts +4 -166
- package/src/lib/claude-hooks-installer.ts +55 -0
- package/src/lib/control-plane.ts +78 -40
- package/src/lib/debug.ts +2 -1
- package/src/lib/deploy-upload.ts +6 -0
- package/src/lib/hooks.ts +3 -1
- package/src/lib/managed-deploy.ts +12 -9
- package/src/lib/project-link.ts +6 -0
- package/src/lib/project-operations.ts +68 -22
- package/src/lib/telemetry.ts +2 -0
- package/src/mcp/README.md +1 -1
- package/src/mcp/resources/index.ts +1 -16
- package/src/mcp/server.ts +23 -0
- package/src/mcp/tools/index.ts +133 -17
- package/src/mcp/types.ts +1 -0
- package/src/mcp/utils.ts +2 -1
- package/src/templates/index.ts +25 -73
- package/templates/CLAUDE.md +41 -0
- package/templates/ai-chat/.jack.json +10 -5
- package/templates/ai-chat/bun.lock +50 -1
- package/templates/ai-chat/package.json +5 -0
- package/templates/ai-chat/public/app.js +73 -0
- package/templates/ai-chat/public/index.html +14 -197
- package/templates/ai-chat/schema.sql +14 -0
- package/templates/ai-chat/src/index.ts +86 -102
- package/templates/ai-chat/wrangler.jsonc +8 -1
- package/templates/cron/.jack.json +66 -0
- package/templates/cron/bun.lock +23 -0
- package/templates/cron/package.json +16 -0
- package/templates/cron/schema.sql +24 -0
- package/templates/cron/src/index.ts +117 -0
- package/templates/cron/src/jobs.ts +139 -0
- package/templates/cron/src/webhooks.ts +95 -0
- package/templates/cron/tsconfig.json +17 -0
- package/templates/cron/wrangler.jsonc +11 -0
- package/templates/miniapp/.jack.json +1 -1
- package/templates/nextjs/.jack.json +1 -1
- package/templates/nextjs-auth/.jack.json +44 -0
- package/templates/nextjs-auth/app/api/auth/[...all]/route.ts +11 -0
- package/templates/nextjs-auth/app/dashboard/loading.tsx +53 -0
- package/templates/nextjs-auth/app/dashboard/page.tsx +73 -0
- package/templates/nextjs-auth/app/error.tsx +44 -0
- package/templates/nextjs-auth/app/globals.css +1 -0
- package/templates/nextjs-auth/app/health/route.ts +3 -0
- package/templates/nextjs-auth/app/layout.tsx +24 -0
- package/templates/nextjs-auth/app/login/page.tsx +10 -0
- package/templates/nextjs-auth/app/page.tsx +86 -0
- package/templates/nextjs-auth/app/signup/page.tsx +10 -0
- package/templates/nextjs-auth/bun.lock +1065 -0
- package/templates/nextjs-auth/cloudflare-env.d.ts +8 -0
- package/templates/nextjs-auth/components/auth-form.tsx +191 -0
- package/templates/nextjs-auth/components/header.tsx +50 -0
- package/templates/nextjs-auth/components/user-menu.tsx +23 -0
- package/templates/nextjs-auth/lib/auth-client.ts +3 -0
- package/templates/nextjs-auth/lib/auth.ts +43 -0
- package/templates/nextjs-auth/lib/utils.ts +6 -0
- package/templates/nextjs-auth/middleware.ts +33 -0
- package/templates/nextjs-auth/next.config.ts +8 -0
- package/templates/nextjs-auth/open-next.config.ts +6 -0
- package/templates/nextjs-auth/package.json +33 -0
- package/templates/nextjs-auth/postcss.config.mjs +8 -0
- package/templates/nextjs-auth/schema.sql +49 -0
- package/templates/nextjs-auth/tsconfig.json +28 -0
- package/templates/nextjs-auth/wrangler.jsonc +23 -0
- package/templates/nextjs-clerk/.jack.json +54 -0
- package/templates/nextjs-clerk/app/dashboard/page.tsx +69 -0
- package/templates/nextjs-clerk/app/globals.css +1 -0
- package/templates/nextjs-clerk/app/health/route.ts +3 -0
- package/templates/nextjs-clerk/app/layout.tsx +26 -0
- package/templates/nextjs-clerk/app/page.tsx +86 -0
- package/templates/nextjs-clerk/app/sign-in/[[...sign-in]]/page.tsx +9 -0
- package/templates/nextjs-clerk/app/sign-up/[[...sign-up]]/page.tsx +9 -0
- package/templates/nextjs-clerk/bun.lock +1055 -0
- package/templates/nextjs-clerk/cloudflare-env.d.ts +3 -0
- package/templates/nextjs-clerk/components/header.tsx +40 -0
- package/templates/nextjs-clerk/lib/utils.ts +6 -0
- package/templates/nextjs-clerk/middleware.ts +18 -0
- package/templates/nextjs-clerk/next.config.ts +8 -0
- package/templates/nextjs-clerk/open-next.config.ts +6 -0
- package/templates/nextjs-clerk/package.json +31 -0
- package/templates/nextjs-clerk/postcss.config.mjs +8 -0
- package/templates/nextjs-clerk/tsconfig.json +28 -0
- package/templates/nextjs-clerk/wrangler.jsonc +17 -0
- package/templates/nextjs-shadcn/.jack.json +34 -0
- package/templates/nextjs-shadcn/app/dashboard/data.json +614 -0
- package/templates/nextjs-shadcn/app/dashboard/page.tsx +55 -0
- package/templates/nextjs-shadcn/app/globals.css +126 -0
- package/templates/nextjs-shadcn/app/health/route.ts +3 -0
- package/templates/nextjs-shadcn/app/layout.tsx +24 -0
- package/templates/nextjs-shadcn/app/login/page.tsx +19 -0
- package/templates/nextjs-shadcn/app/page.tsx +180 -0
- package/templates/nextjs-shadcn/app/showcase.tsx +1262 -0
- package/templates/nextjs-shadcn/bun.lock +1789 -0
- package/templates/nextjs-shadcn/cloudflare-env.d.ts +4 -0
- package/templates/nextjs-shadcn/components/app-sidebar.tsx +175 -0
- package/templates/nextjs-shadcn/components/chart-area-interactive.tsx +291 -0
- package/templates/nextjs-shadcn/components/data-table.tsx +807 -0
- package/templates/nextjs-shadcn/components/login-form.tsx +95 -0
- package/templates/nextjs-shadcn/components/nav-documents.tsx +92 -0
- package/templates/nextjs-shadcn/components/nav-main.tsx +73 -0
- package/templates/nextjs-shadcn/components/nav-projects.tsx +89 -0
- package/templates/nextjs-shadcn/components/nav-secondary.tsx +42 -0
- package/templates/nextjs-shadcn/components/nav-user.tsx +114 -0
- package/templates/nextjs-shadcn/components/section-cards.tsx +102 -0
- package/templates/nextjs-shadcn/components/site-header.tsx +30 -0
- package/templates/nextjs-shadcn/components/team-switcher.tsx +91 -0
- package/templates/nextjs-shadcn/components/ui/accordion.tsx +66 -0
- package/templates/nextjs-shadcn/components/ui/alert-dialog.tsx +196 -0
- package/templates/nextjs-shadcn/components/ui/alert.tsx +66 -0
- package/templates/nextjs-shadcn/components/ui/aspect-ratio.tsx +11 -0
- package/templates/nextjs-shadcn/components/ui/avatar.tsx +109 -0
- package/templates/nextjs-shadcn/components/ui/badge.tsx +48 -0
- package/templates/nextjs-shadcn/components/ui/breadcrumb.tsx +109 -0
- package/templates/nextjs-shadcn/components/ui/button-group.tsx +83 -0
- package/templates/nextjs-shadcn/components/ui/button.tsx +64 -0
- package/templates/nextjs-shadcn/components/ui/calendar.tsx +220 -0
- package/templates/nextjs-shadcn/components/ui/card.tsx +92 -0
- package/templates/nextjs-shadcn/components/ui/carousel.tsx +241 -0
- package/templates/nextjs-shadcn/components/ui/chart.tsx +357 -0
- package/templates/nextjs-shadcn/components/ui/checkbox.tsx +32 -0
- package/templates/nextjs-shadcn/components/ui/collapsible.tsx +33 -0
- package/templates/nextjs-shadcn/components/ui/combobox.tsx +310 -0
- package/templates/nextjs-shadcn/components/ui/command.tsx +184 -0
- package/templates/nextjs-shadcn/components/ui/context-menu.tsx +252 -0
- package/templates/nextjs-shadcn/components/ui/dialog.tsx +158 -0
- package/templates/nextjs-shadcn/components/ui/direction.tsx +22 -0
- package/templates/nextjs-shadcn/components/ui/drawer.tsx +135 -0
- package/templates/nextjs-shadcn/components/ui/dropdown-menu.tsx +257 -0
- package/templates/nextjs-shadcn/components/ui/empty.tsx +104 -0
- package/templates/nextjs-shadcn/components/ui/field.tsx +248 -0
- package/templates/nextjs-shadcn/components/ui/form.tsx +167 -0
- package/templates/nextjs-shadcn/components/ui/hover-card.tsx +44 -0
- package/templates/nextjs-shadcn/components/ui/input-group.tsx +170 -0
- package/templates/nextjs-shadcn/components/ui/input-otp.tsx +77 -0
- package/templates/nextjs-shadcn/components/ui/input.tsx +21 -0
- package/templates/nextjs-shadcn/components/ui/item.tsx +193 -0
- package/templates/nextjs-shadcn/components/ui/kbd.tsx +28 -0
- package/templates/nextjs-shadcn/components/ui/label.tsx +24 -0
- package/templates/nextjs-shadcn/components/ui/menubar.tsx +276 -0
- package/templates/nextjs-shadcn/components/ui/native-select.tsx +53 -0
- package/templates/nextjs-shadcn/components/ui/navigation-menu.tsx +168 -0
- package/templates/nextjs-shadcn/components/ui/pagination.tsx +127 -0
- package/templates/nextjs-shadcn/components/ui/popover.tsx +89 -0
- package/templates/nextjs-shadcn/components/ui/progress.tsx +31 -0
- package/templates/nextjs-shadcn/components/ui/radio-group.tsx +45 -0
- package/templates/nextjs-shadcn/components/ui/resizable.tsx +53 -0
- package/templates/nextjs-shadcn/components/ui/scroll-area.tsx +58 -0
- package/templates/nextjs-shadcn/components/ui/select.tsx +190 -0
- package/templates/nextjs-shadcn/components/ui/separator.tsx +28 -0
- package/templates/nextjs-shadcn/components/ui/sheet.tsx +143 -0
- package/templates/nextjs-shadcn/components/ui/sidebar.tsx +726 -0
- package/templates/nextjs-shadcn/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs-shadcn/components/ui/slider.tsx +63 -0
- package/templates/nextjs-shadcn/components/ui/sonner.tsx +40 -0
- package/templates/nextjs-shadcn/components/ui/spinner.tsx +16 -0
- package/templates/nextjs-shadcn/components/ui/switch.tsx +35 -0
- package/templates/nextjs-shadcn/components/ui/table.tsx +116 -0
- package/templates/nextjs-shadcn/components/ui/tabs.tsx +91 -0
- package/templates/nextjs-shadcn/components/ui/textarea.tsx +18 -0
- package/templates/nextjs-shadcn/components/ui/toggle-group.tsx +83 -0
- package/templates/nextjs-shadcn/components/ui/toggle.tsx +47 -0
- package/templates/nextjs-shadcn/components/ui/tooltip.tsx +57 -0
- package/templates/nextjs-shadcn/components.json +23 -0
- package/templates/nextjs-shadcn/hooks/use-mobile.ts +19 -0
- package/templates/nextjs-shadcn/lib/utils.ts +6 -0
- package/templates/nextjs-shadcn/next-env.d.ts +6 -0
- package/templates/nextjs-shadcn/next.config.ts +8 -0
- package/templates/nextjs-shadcn/open-next.config.ts +6 -0
- package/templates/nextjs-shadcn/package.json +55 -0
- package/templates/nextjs-shadcn/postcss.config.mjs +8 -0
- package/templates/nextjs-shadcn/tsconfig.json +28 -0
- package/templates/nextjs-shadcn/wrangler.jsonc +23 -0
- package/templates/resend/.jack.json +64 -0
- package/templates/resend/bun.lock +23 -0
- package/templates/resend/package.json +16 -0
- package/templates/resend/schema.sql +13 -0
- package/templates/resend/src/email.ts +165 -0
- package/templates/resend/src/index.ts +108 -0
- package/templates/resend/tsconfig.json +17 -0
- package/templates/resend/wrangler.jsonc +11 -0
- package/templates/saas/.jack.json +1 -1
- package/templates/ai-chat/public/chat.js +0 -149
|
@@ -102,6 +102,7 @@ export interface DeployOptions {
|
|
|
102
102
|
managed?: boolean; // Force managed deploy mode
|
|
103
103
|
byo?: boolean; // Force BYO deploy mode
|
|
104
104
|
dryRun?: boolean; // Stop before actual deployment
|
|
105
|
+
message?: string; // Deploy message describing what changed
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
export interface DeployResult {
|
|
@@ -109,6 +110,9 @@ export interface DeployResult {
|
|
|
109
110
|
projectName: string;
|
|
110
111
|
deployOutput?: string;
|
|
111
112
|
deployMode: DeployMode; // The deploy mode used
|
|
113
|
+
deploymentId?: string;
|
|
114
|
+
deployStatus?: string;
|
|
115
|
+
errorMessage?: string | null;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
export interface ProjectStatus {
|
|
@@ -126,6 +130,12 @@ export interface ProjectStatus {
|
|
|
126
130
|
missing: boolean;
|
|
127
131
|
backupFiles: number | null;
|
|
128
132
|
backupLastSync: string | null;
|
|
133
|
+
// Deploy tracking (managed projects only)
|
|
134
|
+
lastDeployAt: string | null;
|
|
135
|
+
deployCount: number;
|
|
136
|
+
lastDeployStatus: string | null;
|
|
137
|
+
lastDeploySource: string | null;
|
|
138
|
+
lastDeployMessage: string | null;
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
export interface StaleProject {
|
|
@@ -361,6 +371,7 @@ async function runParallelSetup(
|
|
|
361
371
|
options: {
|
|
362
372
|
template?: string;
|
|
363
373
|
usePrebuilt?: boolean;
|
|
374
|
+
forkedFrom?: string;
|
|
364
375
|
onRemoteReady?: (result: ManagedCreateResult) => void;
|
|
365
376
|
},
|
|
366
377
|
): Promise<{
|
|
@@ -391,6 +402,7 @@ async function runParallelSetup(
|
|
|
391
402
|
const remotePromise = createManagedProjectRemote(projectName, undefined, {
|
|
392
403
|
template: options.template || "hello",
|
|
393
404
|
usePrebuilt: options.usePrebuilt ?? true,
|
|
405
|
+
forkedFrom: options.forkedFrom,
|
|
394
406
|
}).then((result) => {
|
|
395
407
|
const duration = ((Date.now() - remoteStart) / 1000).toFixed(1);
|
|
396
408
|
debug(`Remote project created in ${duration}s (status: ${result.status})`);
|
|
@@ -793,11 +805,25 @@ export async function createProject(
|
|
|
793
805
|
}
|
|
794
806
|
}
|
|
795
807
|
|
|
796
|
-
//
|
|
808
|
+
// Auto-initialize on first run (replaces "Run: jack init" error)
|
|
797
809
|
const { isInitialized } = await import("../commands/init.ts");
|
|
798
810
|
const initialized = await isInitialized();
|
|
799
811
|
if (!initialized) {
|
|
800
|
-
|
|
812
|
+
const { writeConfig, readConfig } = await import("./config.ts");
|
|
813
|
+
const existingConfig = await readConfig();
|
|
814
|
+
await writeConfig({
|
|
815
|
+
version: 1,
|
|
816
|
+
initialized: true,
|
|
817
|
+
initializedAt: existingConfig?.initializedAt || new Date().toISOString(),
|
|
818
|
+
...existingConfig,
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
// Best-effort MCP config install (non-blocking)
|
|
822
|
+
try {
|
|
823
|
+
const { installMcpConfigsToAllApps, saveMcpConfig } = await import("./mcp-config.ts");
|
|
824
|
+
await installMcpConfigsToAllApps();
|
|
825
|
+
await saveMcpConfig();
|
|
826
|
+
} catch {}
|
|
801
827
|
}
|
|
802
828
|
|
|
803
829
|
// Auth gate - check/prompt for authentication before any work
|
|
@@ -1199,9 +1225,11 @@ export async function createProject(
|
|
|
1199
1225
|
reporter.start("Setting up project...");
|
|
1200
1226
|
|
|
1201
1227
|
try {
|
|
1228
|
+
const forkedFrom = templateOrigin.type !== "builtin" ? templateOrigin.name : undefined;
|
|
1202
1229
|
const result = await runParallelSetup(targetDir, projectName, {
|
|
1203
1230
|
template: resolvedTemplate || "hello",
|
|
1204
1231
|
usePrebuilt: templateOrigin.type === "builtin", // Only builtin templates have prebuilt bundles
|
|
1232
|
+
forkedFrom,
|
|
1205
1233
|
onRemoteReady: (remote) => {
|
|
1206
1234
|
// Show URL immediately when prebuilt succeeds
|
|
1207
1235
|
reporter.stop();
|
|
@@ -1355,22 +1383,8 @@ export async function createProject(
|
|
|
1355
1383
|
reporter.success(`Deployed: ${workerUrl}`);
|
|
1356
1384
|
}
|
|
1357
1385
|
|
|
1358
|
-
//
|
|
1359
|
-
|
|
1360
|
-
const { createSourceZip } = await import("./zip-packager.ts");
|
|
1361
|
-
const { uploadSourceSnapshot } = await import("./control-plane.ts");
|
|
1362
|
-
const { rm } = await import("node:fs/promises");
|
|
1363
|
-
|
|
1364
|
-
const sourceZipPath = await createSourceZip(targetDir);
|
|
1365
|
-
await uploadSourceSnapshot(remoteResult.projectId, sourceZipPath);
|
|
1366
|
-
await rm(sourceZipPath, { force: true });
|
|
1367
|
-
debug("Source snapshot uploaded for prebuilt project");
|
|
1368
|
-
} catch (err) {
|
|
1369
|
-
debug(
|
|
1370
|
-
"Source snapshot upload failed (prebuilt):",
|
|
1371
|
-
err instanceof Error ? err.message : String(err),
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1386
|
+
// source.zip is now stored in deployment artifacts by the control plane
|
|
1387
|
+
// during prebuilt deploy — no client-side upload needed
|
|
1374
1388
|
} else {
|
|
1375
1389
|
// Prebuilt not available - fall back to fresh build
|
|
1376
1390
|
if (remoteResult.prebuiltFailed) {
|
|
@@ -1688,11 +1702,10 @@ export async function deployProject(options: DeployOptions = {}): Promise<Deploy
|
|
|
1688
1702
|
deployMode = link?.deploy_mode ?? "byo";
|
|
1689
1703
|
}
|
|
1690
1704
|
|
|
1691
|
-
// Ensure agent integration is set up (
|
|
1705
|
+
// Ensure agent integration is set up (MCP config)
|
|
1692
1706
|
// This is idempotent and runs silently
|
|
1693
1707
|
try {
|
|
1694
1708
|
await ensureAgentIntegration(projectPath, {
|
|
1695
|
-
projectName,
|
|
1696
1709
|
silent: true,
|
|
1697
1710
|
});
|
|
1698
1711
|
} catch (err) {
|
|
@@ -1723,6 +1736,7 @@ export async function deployProject(options: DeployOptions = {}): Promise<Deploy
|
|
|
1723
1736
|
|
|
1724
1737
|
let workerUrl: string | null = null;
|
|
1725
1738
|
let deployOutput: string | undefined;
|
|
1739
|
+
let managedDeployResult: { deploymentId: string; status: string; errorMessage: string | null } | undefined;
|
|
1726
1740
|
|
|
1727
1741
|
// Deploy based on mode
|
|
1728
1742
|
if (deployMode === "managed") {
|
|
@@ -1781,7 +1795,7 @@ export async function deployProject(options: DeployOptions = {}): Promise<Deploy
|
|
|
1781
1795
|
}
|
|
1782
1796
|
|
|
1783
1797
|
// deployToManagedProject now handles both template and code deploy
|
|
1784
|
-
await deployToManagedProject(managedProjectId as string, projectPath, reporter);
|
|
1798
|
+
managedDeployResult = await deployToManagedProject(managedProjectId as string, projectPath, reporter, options.message);
|
|
1785
1799
|
|
|
1786
1800
|
// Construct URL with username if available
|
|
1787
1801
|
workerUrl = link?.owner_username
|
|
@@ -1936,6 +1950,9 @@ export async function deployProject(options: DeployOptions = {}): Promise<Deploy
|
|
|
1936
1950
|
projectName,
|
|
1937
1951
|
deployOutput: workerUrl ? undefined : deployOutput,
|
|
1938
1952
|
deployMode,
|
|
1953
|
+
deploymentId: managedDeployResult?.deploymentId,
|
|
1954
|
+
deployStatus: managedDeployResult?.status,
|
|
1955
|
+
errorMessage: managedDeployResult?.errorMessage,
|
|
1939
1956
|
};
|
|
1940
1957
|
}
|
|
1941
1958
|
|
|
@@ -2025,11 +2042,35 @@ export async function getProjectStatus(
|
|
|
2025
2042
|
}
|
|
2026
2043
|
}
|
|
2027
2044
|
|
|
2045
|
+
// Fetch real deployment data for managed projects
|
|
2046
|
+
let lastDeployAt: string | null = null;
|
|
2047
|
+
let deployCount = 0;
|
|
2048
|
+
let lastDeployStatus: string | null = null;
|
|
2049
|
+
let lastDeploySource: string | null = null;
|
|
2050
|
+
let lastDeployMessage: string | null = null;
|
|
2051
|
+
|
|
2052
|
+
if (link?.deploy_mode === "managed") {
|
|
2053
|
+
try {
|
|
2054
|
+
const { fetchDeployments } = await import("./control-plane.ts");
|
|
2055
|
+
const result = await fetchDeployments(link.project_id);
|
|
2056
|
+
deployCount = result.total;
|
|
2057
|
+
const latest = result.deployments[0];
|
|
2058
|
+
if (latest) {
|
|
2059
|
+
lastDeployAt = latest.created_at;
|
|
2060
|
+
lastDeployStatus = latest.status;
|
|
2061
|
+
lastDeploySource = latest.source;
|
|
2062
|
+
lastDeployMessage = latest.message;
|
|
2063
|
+
}
|
|
2064
|
+
} catch {
|
|
2065
|
+
// Silent fail — deploy tracking is supplementary
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2028
2069
|
return {
|
|
2029
2070
|
name: projectName,
|
|
2030
2071
|
localPath,
|
|
2031
2072
|
workerUrl,
|
|
2032
|
-
lastDeployed: link?.linked_at ?? null,
|
|
2073
|
+
lastDeployed: lastDeployAt ?? link?.linked_at ?? null,
|
|
2033
2074
|
createdAt: link?.linked_at ?? null,
|
|
2034
2075
|
accountId: null, // No longer stored in registry
|
|
2035
2076
|
workerId: projectName,
|
|
@@ -2040,6 +2081,11 @@ export async function getProjectStatus(
|
|
|
2040
2081
|
missing: false,
|
|
2041
2082
|
backupFiles,
|
|
2042
2083
|
backupLastSync,
|
|
2084
|
+
lastDeployAt,
|
|
2085
|
+
deployCount,
|
|
2086
|
+
lastDeployStatus,
|
|
2087
|
+
lastDeploySource,
|
|
2088
|
+
lastDeployMessage,
|
|
2043
2089
|
};
|
|
2044
2090
|
}
|
|
2045
2091
|
|
package/src/lib/telemetry.ts
CHANGED
|
@@ -47,6 +47,8 @@ export const Events = {
|
|
|
47
47
|
// Token management events
|
|
48
48
|
TOKEN_CREATED: "token_created",
|
|
49
49
|
TOKEN_REVOKED: "token_revoked",
|
|
50
|
+
// Hook events
|
|
51
|
+
HOOK_SESSION_CONTEXT: "hook_session_context",
|
|
50
52
|
} as const;
|
|
51
53
|
|
|
52
54
|
type EventName = (typeof Events)[keyof typeof Events];
|
package/src/mcp/README.md
CHANGED
|
@@ -25,7 +25,7 @@ src/mcp/
|
|
|
25
25
|
### `tools/index.ts`
|
|
26
26
|
- Centralized tool registration and dispatch
|
|
27
27
|
- Implements 4 core tools:
|
|
28
|
-
- `create_project` - Create new
|
|
28
|
+
- `create_project` - Create new project (supports forking via template)
|
|
29
29
|
- `deploy_project` - Deploy existing project
|
|
30
30
|
- `get_project_status` - Get project status info
|
|
31
31
|
- `list_projects` - List all projects with filters
|
|
@@ -74,30 +74,15 @@ export function registerResources(
|
|
|
74
74
|
|
|
75
75
|
if (uri === "agents://context") {
|
|
76
76
|
const projectPath = options.projectPath ?? process.cwd();
|
|
77
|
-
const jackPath = join(projectPath, "JACK.md");
|
|
78
77
|
const agentsPath = join(projectPath, "AGENTS.md");
|
|
79
78
|
const claudePath = join(projectPath, "CLAUDE.md");
|
|
80
79
|
|
|
81
80
|
const contents: string[] = [];
|
|
82
81
|
|
|
83
|
-
// Try to read JACK.md first (jack-specific context)
|
|
84
|
-
if (existsSync(jackPath)) {
|
|
85
|
-
try {
|
|
86
|
-
const jackContent = await Bun.file(jackPath).text();
|
|
87
|
-
contents.push("# JACK.md\n\n");
|
|
88
|
-
contents.push(jackContent);
|
|
89
|
-
} catch {
|
|
90
|
-
// Ignore read errors
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
82
|
// Try to read AGENTS.md
|
|
95
83
|
if (existsSync(agentsPath)) {
|
|
96
84
|
try {
|
|
97
85
|
const agentsContent = await Bun.file(agentsPath).text();
|
|
98
|
-
if (contents.length > 0) {
|
|
99
|
-
contents.push("\n\n---\n\n");
|
|
100
|
-
}
|
|
101
86
|
contents.push("# AGENTS.md\n\n");
|
|
102
87
|
contents.push(agentsContent);
|
|
103
88
|
} catch {
|
|
@@ -143,7 +128,7 @@ If connected, prefer \`mcp__jack__*\` tools over CLI:
|
|
|
143
128
|
|
|
144
129
|
Full docs: https://docs.getjack.org/llms-full.txt
|
|
145
130
|
|
|
146
|
-
Check
|
|
131
|
+
Check AGENTS.md in the project root for project-specific instructions.
|
|
147
132
|
`;
|
|
148
133
|
return {
|
|
149
134
|
contents: [
|
package/src/mcp/server.ts
CHANGED
|
@@ -43,8 +43,31 @@ export async function createMcpServer(options: McpServerOptions = {}) {
|
|
|
43
43
|
return { server, debug };
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Install runtime guards that prevent accidental stdout writes from corrupting
|
|
48
|
+
* the MCP JSON-RPC stdio transport. This catches console.log() and
|
|
49
|
+
* process.stdout.write() but NOT Bun.spawn({ stdout: "inherit" }) which writes
|
|
50
|
+
* directly to fd 1 — that case is handled by the `interactive` flag in hooks.
|
|
51
|
+
*/
|
|
52
|
+
function installStdoutGuards() {
|
|
53
|
+
// Redirect console.log to stderr so accidental calls don't corrupt the stream.
|
|
54
|
+
// We don't wrap process.stdout.write because the MCP SDK writes JSON-RPC
|
|
55
|
+
// messages through it — intercepting those risks breaking the protocol if
|
|
56
|
+
// messages are chunked or newlines are written separately.
|
|
57
|
+
console.log = (...args: unknown[]) => {
|
|
58
|
+
console.error(
|
|
59
|
+
"[jack-mcp] WARNING: console.log intercepted (would corrupt MCP protocol):",
|
|
60
|
+
...args,
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
46
65
|
export async function startMcpServer(options: McpServerOptions = {}) {
|
|
47
66
|
const { server, debug } = await createMcpServer(options);
|
|
67
|
+
|
|
68
|
+
// Install stdout guards BEFORE connecting transport to prevent corruption
|
|
69
|
+
installStdoutGuards();
|
|
70
|
+
|
|
48
71
|
const transport = new StdioServerTransport();
|
|
49
72
|
|
|
50
73
|
debug("Starting MCP server on stdio transport");
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -7,6 +7,10 @@ import { JackError, JackErrorCode } from "../../lib/errors.ts";
|
|
|
7
7
|
import { getDeployMode, getProjectId } from "../../lib/project-link.ts";
|
|
8
8
|
import { createProject, deployProject, getProjectStatus } from "../../lib/project-operations.ts";
|
|
9
9
|
import { listAllProjects } from "../../lib/project-resolver.ts";
|
|
10
|
+
import { createCronSchedule } from "../../lib/services/cron-create.ts";
|
|
11
|
+
import { deleteCronSchedule } from "../../lib/services/cron-delete.ts";
|
|
12
|
+
import { listCronSchedules } from "../../lib/services/cron-list.ts";
|
|
13
|
+
import { testCronExpression } from "../../lib/services/cron-test.ts";
|
|
10
14
|
import { createDatabase } from "../../lib/services/db-create.ts";
|
|
11
15
|
import {
|
|
12
16
|
DestructiveOperationError,
|
|
@@ -15,10 +19,6 @@ import {
|
|
|
15
19
|
wrapResultsForMcp,
|
|
16
20
|
} from "../../lib/services/db-execute.ts";
|
|
17
21
|
import { listDatabases } from "../../lib/services/db-list.ts";
|
|
18
|
-
import { createCronSchedule } from "../../lib/services/cron-create.ts";
|
|
19
|
-
import { deleteCronSchedule } from "../../lib/services/cron-delete.ts";
|
|
20
|
-
import { listCronSchedules } from "../../lib/services/cron-list.ts";
|
|
21
|
-
import { testCronExpression } from "../../lib/services/cron-test.ts";
|
|
22
22
|
import {
|
|
23
23
|
assignDomain,
|
|
24
24
|
connectDomain,
|
|
@@ -41,7 +41,12 @@ import { formatErrorResponse, formatSuccessResponse } from "../utils.ts";
|
|
|
41
41
|
// Tool schemas
|
|
42
42
|
const CreateProjectSchema = z.object({
|
|
43
43
|
name: z.string().optional().describe("Project name (auto-generated if not provided)"),
|
|
44
|
-
template: z
|
|
44
|
+
template: z
|
|
45
|
+
.string()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe(
|
|
48
|
+
"Template to use (e.g., 'miniapp', 'api'). Also supports forking: use 'username/slug' for published projects or 'my-project' to fork your own.",
|
|
49
|
+
),
|
|
45
50
|
});
|
|
46
51
|
|
|
47
52
|
const DeployProjectSchema = z.object({
|
|
@@ -49,6 +54,12 @@ const DeployProjectSchema = z.object({
|
|
|
49
54
|
.string()
|
|
50
55
|
.optional()
|
|
51
56
|
.describe("Path to project directory (defaults to current directory)"),
|
|
57
|
+
message: z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe(
|
|
61
|
+
"Deploy message describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug')",
|
|
62
|
+
),
|
|
52
63
|
});
|
|
53
64
|
|
|
54
65
|
const GetProjectStatusSchema = z.object({
|
|
@@ -200,6 +211,19 @@ const TailLogsSchema = z.object({
|
|
|
200
211
|
.describe("How long to listen before returning (default: 2000ms, max: 10000ms)"),
|
|
201
212
|
});
|
|
202
213
|
|
|
214
|
+
const RollbackProjectSchema = z.object({
|
|
215
|
+
deployment_id: z
|
|
216
|
+
.string()
|
|
217
|
+
.optional()
|
|
218
|
+
.describe(
|
|
219
|
+
"Specific deployment ID to roll back to (defaults to previous successful deployment)",
|
|
220
|
+
),
|
|
221
|
+
project_path: z
|
|
222
|
+
.string()
|
|
223
|
+
.optional()
|
|
224
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
225
|
+
});
|
|
226
|
+
|
|
203
227
|
const ListDomainsSchema = z.object({});
|
|
204
228
|
|
|
205
229
|
const ConnectDomainSchema = z.object({
|
|
@@ -220,7 +244,9 @@ const DisconnectDomainSchema = z.object({
|
|
|
220
244
|
});
|
|
221
245
|
|
|
222
246
|
const CreateCronSchema = z.object({
|
|
223
|
-
expression: z
|
|
247
|
+
expression: z
|
|
248
|
+
.string()
|
|
249
|
+
.describe("Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)"),
|
|
224
250
|
project_path: z
|
|
225
251
|
.string()
|
|
226
252
|
.optional()
|
|
@@ -264,7 +290,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
264
290
|
{
|
|
265
291
|
name: "create_project",
|
|
266
292
|
description:
|
|
267
|
-
"Create a new
|
|
293
|
+
"Create a new project from a template. Automatically installs dependencies, deploys, and registers the project. Also supports forking: pass a 'username/slug' template to fork a published project, or a project slug to fork your own.",
|
|
268
294
|
inputSchema: {
|
|
269
295
|
type: "object",
|
|
270
296
|
properties: {
|
|
@@ -274,7 +300,8 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
274
300
|
},
|
|
275
301
|
template: {
|
|
276
302
|
type: "string",
|
|
277
|
-
description:
|
|
303
|
+
description:
|
|
304
|
+
"Template to use (e.g., 'miniapp', 'api'). Also supports forking: use 'username/slug' for published projects or 'my-project' to fork your own.",
|
|
278
305
|
},
|
|
279
306
|
},
|
|
280
307
|
},
|
|
@@ -282,7 +309,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
282
309
|
{
|
|
283
310
|
name: "deploy_project",
|
|
284
311
|
description:
|
|
285
|
-
"Deploy an existing project
|
|
312
|
+
"Deploy an existing project. Builds if needed and pushes to production. Always provide a 'message' describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug').",
|
|
286
313
|
inputSchema: {
|
|
287
314
|
type: "object",
|
|
288
315
|
properties: {
|
|
@@ -290,13 +317,18 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
290
317
|
type: "string",
|
|
291
318
|
description: "Path to project directory (defaults to current directory)",
|
|
292
319
|
},
|
|
320
|
+
message: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description:
|
|
323
|
+
"Deploy message describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug')",
|
|
324
|
+
},
|
|
293
325
|
},
|
|
294
326
|
},
|
|
295
327
|
},
|
|
296
328
|
{
|
|
297
329
|
name: "get_project_status",
|
|
298
330
|
description:
|
|
299
|
-
"Get
|
|
331
|
+
"Get live deployment state: URL, last deploy time, deploy count, status (live/failed), and deploy source. Call this first to understand what's currently deployed before making changes.",
|
|
300
332
|
inputSchema: {
|
|
301
333
|
type: "object",
|
|
302
334
|
properties: {
|
|
@@ -347,7 +379,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
347
379
|
{
|
|
348
380
|
name: "tail_logs",
|
|
349
381
|
description:
|
|
350
|
-
"Collect
|
|
382
|
+
"Collect live log events from production. Use after deploying to verify changes work, or to debug errors. Returns JSON log entries with timestamps and messages.",
|
|
351
383
|
inputSchema: {
|
|
352
384
|
type: "object",
|
|
353
385
|
properties: {
|
|
@@ -370,6 +402,27 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
370
402
|
},
|
|
371
403
|
},
|
|
372
404
|
},
|
|
405
|
+
{
|
|
406
|
+
name: "rollback_project",
|
|
407
|
+
description:
|
|
408
|
+
"Roll back a managed (jack cloud) project to a previous deployment. " +
|
|
409
|
+
"Defaults to the previous successful deployment if no deployment_id is specified. " +
|
|
410
|
+
"Only rolls back code — database state and secrets are unchanged.",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: "object",
|
|
413
|
+
properties: {
|
|
414
|
+
deployment_id: {
|
|
415
|
+
type: "string",
|
|
416
|
+
description:
|
|
417
|
+
"Specific deployment ID to roll back to (defaults to previous successful deployment)",
|
|
418
|
+
},
|
|
419
|
+
project_path: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "Path to project directory (defaults to current directory)",
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
373
426
|
{
|
|
374
427
|
name: "create_database",
|
|
375
428
|
description:
|
|
@@ -654,13 +707,14 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
654
707
|
{
|
|
655
708
|
name: "create_cron",
|
|
656
709
|
description:
|
|
657
|
-
"Create a cron schedule for a managed (Jack Cloud) project. Minimum interval is 15 minutes. The worker must
|
|
710
|
+
"Create a cron schedule for a managed (Jack Cloud) project. Minimum interval is 15 minutes. The worker must handle POST /__scheduled requests — Cloudflare's native scheduled() export does not work with Jack Cloud crons.",
|
|
658
711
|
inputSchema: {
|
|
659
712
|
type: "object",
|
|
660
713
|
properties: {
|
|
661
714
|
expression: {
|
|
662
715
|
type: "string",
|
|
663
|
-
description:
|
|
716
|
+
description:
|
|
717
|
+
"Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)",
|
|
664
718
|
},
|
|
665
719
|
project_path: {
|
|
666
720
|
type: "string",
|
|
@@ -719,7 +773,8 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
719
773
|
trigger_production: {
|
|
720
774
|
type: "boolean",
|
|
721
775
|
default: false,
|
|
722
|
-
description:
|
|
776
|
+
description:
|
|
777
|
+
"Whether to trigger the cron handler on production (requires managed project)",
|
|
723
778
|
},
|
|
724
779
|
},
|
|
725
780
|
required: ["expression"],
|
|
@@ -777,12 +832,13 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
777
832
|
|
|
778
833
|
const wrappedDeployProject = withTelemetry(
|
|
779
834
|
"deploy_project",
|
|
780
|
-
async (projectPath?: string) => {
|
|
835
|
+
async (projectPath?: string, message?: string) => {
|
|
781
836
|
const result = await deployProject({
|
|
782
837
|
projectPath,
|
|
783
838
|
interactive: false,
|
|
784
839
|
includeSecrets: false,
|
|
785
840
|
includeSync: false,
|
|
841
|
+
message,
|
|
786
842
|
});
|
|
787
843
|
|
|
788
844
|
// Track business event
|
|
@@ -795,7 +851,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
795
851
|
{ platform: "mcp" },
|
|
796
852
|
);
|
|
797
853
|
|
|
798
|
-
const result = await wrappedDeployProject(args.project_path);
|
|
854
|
+
const result = await wrappedDeployProject(args.project_path, args.message);
|
|
799
855
|
|
|
800
856
|
return {
|
|
801
857
|
content: [
|
|
@@ -1046,6 +1102,60 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
1046
1102
|
};
|
|
1047
1103
|
}
|
|
1048
1104
|
|
|
1105
|
+
case "rollback_project": {
|
|
1106
|
+
const args = RollbackProjectSchema.parse(request.params.arguments ?? {});
|
|
1107
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
1108
|
+
|
|
1109
|
+
const deployMode = await getDeployMode(projectPath);
|
|
1110
|
+
if (deployMode !== "managed") {
|
|
1111
|
+
throw new JackError(
|
|
1112
|
+
JackErrorCode.VALIDATION_ERROR,
|
|
1113
|
+
"Rollback is only available for managed (jack cloud) projects",
|
|
1114
|
+
"For BYOC projects, use wrangler to manage deployments.",
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
const projectId = await getProjectId(projectPath);
|
|
1119
|
+
if (!projectId) {
|
|
1120
|
+
throw new JackError(
|
|
1121
|
+
JackErrorCode.PROJECT_NOT_FOUND,
|
|
1122
|
+
"Project not found",
|
|
1123
|
+
"Run this from a linked jack cloud project directory (has .jack/project.json).",
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Import rollbackDeployment from control-plane
|
|
1128
|
+
const { rollbackDeployment } = await import("../../lib/control-plane.ts");
|
|
1129
|
+
|
|
1130
|
+
const wrappedRollback = withTelemetry(
|
|
1131
|
+
"rollback_project",
|
|
1132
|
+
async (id: string, deploymentId?: string) => rollbackDeployment(id, deploymentId),
|
|
1133
|
+
{ platform: "mcp" },
|
|
1134
|
+
);
|
|
1135
|
+
|
|
1136
|
+
const result = await wrappedRollback(projectId, args.deployment_id);
|
|
1137
|
+
|
|
1138
|
+
return {
|
|
1139
|
+
content: [
|
|
1140
|
+
{
|
|
1141
|
+
type: "text",
|
|
1142
|
+
text: JSON.stringify(
|
|
1143
|
+
formatSuccessResponse(
|
|
1144
|
+
{
|
|
1145
|
+
...result.deployment,
|
|
1146
|
+
message:
|
|
1147
|
+
"Code rolled back successfully. Database state and secrets are unchanged.",
|
|
1148
|
+
},
|
|
1149
|
+
startTime,
|
|
1150
|
+
),
|
|
1151
|
+
null,
|
|
1152
|
+
2,
|
|
1153
|
+
),
|
|
1154
|
+
},
|
|
1155
|
+
],
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1049
1159
|
case "create_database": {
|
|
1050
1160
|
const args = CreateDatabaseSchema.parse(request.params.arguments ?? {});
|
|
1051
1161
|
const projectPath = args.project_path ?? process.cwd();
|
|
@@ -1268,7 +1378,13 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
1268
1378
|
content: [
|
|
1269
1379
|
{
|
|
1270
1380
|
type: "text",
|
|
1271
|
-
text: JSON.stringify(
|
|
1381
|
+
text: JSON.stringify(
|
|
1382
|
+
formatSuccessResponse(result, startTime, [
|
|
1383
|
+
"Vectorize indexes have eventual consistency. Newly inserted vectors typically take 2-3 minutes to become queryable.",
|
|
1384
|
+
]),
|
|
1385
|
+
null,
|
|
1386
|
+
2,
|
|
1387
|
+
),
|
|
1272
1388
|
},
|
|
1273
1389
|
],
|
|
1274
1390
|
};
|
package/src/mcp/types.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
export interface McpToolResponse<T = unknown> {
|
|
6
6
|
success: boolean;
|
|
7
7
|
data?: T;
|
|
8
|
+
notes?: string[]; // Situational context for AI agents (e.g. eventual consistency caveats)
|
|
8
9
|
error?: {
|
|
9
10
|
code: string; // Machine-readable: 'AUTH_FAILED', 'PROJECT_NOT_FOUND'
|
|
10
11
|
message: string; // Human-readable description
|
package/src/mcp/utils.ts
CHANGED
|
@@ -5,10 +5,11 @@ import { McpErrorCode, type McpToolResponse } from "./types.ts";
|
|
|
5
5
|
/**
|
|
6
6
|
* Format a successful MCP tool response
|
|
7
7
|
*/
|
|
8
|
-
export function formatSuccessResponse<T>(data: T, startTime: number): McpToolResponse<T> {
|
|
8
|
+
export function formatSuccessResponse<T>(data: T, startTime: number, notes?: string[]): McpToolResponse<T> {
|
|
9
9
|
return {
|
|
10
10
|
success: true,
|
|
11
11
|
data,
|
|
12
|
+
...(notes?.length && { notes }),
|
|
12
13
|
meta: {
|
|
13
14
|
duration_ms: Date.now() - startTime,
|
|
14
15
|
jack_version: packageJson.version,
|