@mistflow-ai/mcp 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PXD4CR4I.js → chunk-SXNAPWCC.js} +1 -1
- package/dist/chunk-VQPRSDSC.js +23 -0
- package/dist/index.js +18 -333
- package/dist/self-heal-QQ53MP7L.js +1 -0
- package/dist/state-manager-26IE6Y5D.js +1 -0
- package/package.json +1 -1
- package/dist/api-client-7L7LNS2X.js +0 -1
- package/dist/chunk-EIOY6EQ2.js +0 -23
- package/dist/self-heal-J7EGYRN4.js +0 -1
- package/dist/state-manager-QMSYKJ37.js +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as o,b as s,
|
|
1
|
+
import{a as o,b as s,g as i,i as a}from"./chunk-VQPRSDSC.js";import{existsSync as u,readFileSync as f,writeFileSync as d,mkdirSync as m}from"fs";import{join as l}from"path";function p(t){return l(t,".mistflow","state.json")}function P(t){let e=p(t);if(!u(e))return null;try{return JSON.parse(f(e,"utf-8"))}catch{return null}}function w(t,e){let r=l(t,".mistflow");u(r)||m(r,{recursive:!0}),d(p(t),JSON.stringify(e,null,2)+`
|
|
2
2
|
`)}function C(t,e){return{projectId:t,name:e,template:"",features:[],dbSchema:[],deployCount:0,decisions:[],provenance:[]}}async function x(t){let e;try{e=a()}catch{return null}try{let r=await fetch(`${i()}/api/projects/${encodeURIComponent(t)}/state`,{headers:{...e,"Content-Type":"application/json","X-Mistflow-MCP-Version":o()}});try{s(r.headers)}catch{}return r.ok?await r.json():null}catch{return null}}async function R(t,e){let r;try{r=a()}catch{return!1}try{let n=await fetch(`${i()}/api/projects/${encodeURIComponent(t)}/state`,{method:"PUT",headers:{...r,"Content-Type":"application/json","X-Mistflow-MCP-Version":o()},body:JSON.stringify(e)});try{s(n.headers)}catch{}return n.ok}catch{return!1}}function L(t,e){let r=t.toLowerCase(),n=e.toLowerCase();return n.includes(r)||r.includes(n)?!0:r.split(/\s+/).some(c=>c.length>=3&&n.includes(c))}export{p as a,P as b,w as c,C as d,x as e,R as f,L as g};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import{existsSync as w,readFileSync as C,writeFileSync as E,mkdirSync as A}from"fs";import{join as y,dirname as h}from"path";import{homedir as O}from"os";import{fileURLToPath as F}from"url";var d=null;function f(){if(d)return d;try{let e=F(import.meta.url),t=h(e);for(let n=0;n<6;n++){let o=y(t,"package.json");if(w(o)){let s=JSON.parse(C(o,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return d=s.version,s.version}let r=h(t);if(r===t)break;t=r}}catch{}return d="0.0.0","0.0.0"}function g(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?[parseInt(t[1],10),parseInt(t[2],10),parseInt(t[3],10)]:null}function x(e,t){let n=g(e),o=g(t);if(!n||!o)return 0;for(let r=0;r<3;r++){if(n[r]<o[r])return-1;if(n[r]>o[r])return 1}return 0}function b(e,t,n){if(n&&x(e,n)<0)return"unsupported";if(!t||x(e,t)>=0)return"none";let r=g(e),s=g(t);return!r||!s?"none":r[0]<s[0]?"major":r[1]<s[1]?"minor":"patch"}var a=null;function P(e){let t=e.get("x-mistflow-mcp-latest")??"",n=e.get("x-mistflow-mcp-min-supported")??"",o=e.get("x-mistflow-mcp-changelog-url")??"";!t&&!n||(a={latest:t,minSupported:n,changelogUrl:o})}function v(){let e=process.env.MISTFLOW_STATE_DIR||y(O(),".mistflow");return y(e,"upgrade-state.json")}function V(){try{let e=v();return w(e)?JSON.parse(C(e,"utf-8")):{}}catch{return{}}}function L(e){try{let t=v(),n=h(t);w(n)||A(n,{recursive:!0}),E(t,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,{mode:384})}catch{}}var S=!1;function N(e){return e==="patch"?7*864e5:e==="minor"?1*864e5:0}function ie(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!a)return null;let e=f();if(e==="0.0.0")return null;let t=b(e,a.latest,a.minSupported);if(t==="none")return null;if(t==="unsupported")return k(t,e,a);if(S)return null;let n=V(),o=Date.now(),r=N(t);return n.dismissedForVersion===a.latest&&t!=="major"||n.lastLatestSeen===a.latest&&n.lastShownMs&&o-n.lastShownMs<r?null:(S=!0,L({...n,lastShownMs:o,lastLatestSeen:a.latest}),k(t,e,a))}function k(e,t,n){let o="npx -y mistflow-ai install",r=n.changelogUrl?`
|
|
3
|
+
What's new: ${n.changelogUrl}`:"";return e==="unsupported"?`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
Mistflow ${t} is no longer supported.
|
|
7
|
+
You must upgrade to ${n.latest} or newer to continue:
|
|
8
|
+
|
|
9
|
+
${o}
|
|
10
|
+
|
|
11
|
+
After upgrading, restart your AI editor.${r}
|
|
12
|
+
---`:e==="major"?`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
Mistflow ${n.latest} is available (you have ${t}).
|
|
16
|
+
This is a major update. Run \`${o}\` and restart your editor.${r}
|
|
17
|
+
---`:e==="minor"?`
|
|
18
|
+
|
|
19
|
+
--- Mistflow update available: ${t} -> ${n.latest} ---
|
|
20
|
+
Run \`${o}\` to upgrade, then restart your editor.${r}`:`
|
|
21
|
+
|
|
22
|
+
(Mistflow ${n.latest} is out, you have ${t}. Run \`${o}\` when convenient.)`}function ae(){let e=f(),t=a??{latest:"",minSupported:"",changelogUrl:""},n=b(e,t.latest,t.minSupported);return{current:e,latest:t.latest,minSupported:t.minSupported,severity:n,upgradeCmd:"npx -y mistflow-ai install",changelogUrl:t.changelogUrl,backendSignalReceived:a!==null}}import{readFileSync as B,existsSync as D,writeFileSync as q,mkdirSync as K,renameSync as J,unlinkSync as H}from"fs";import{join as U,dirname as W}from"path";import{homedir as Q}from"os";import{randomBytes as z}from"crypto";function I(){return U(Q(),".mistflow","credentials.json")}function m(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}var G="https://api.mistflow.ai";function T(){let e=I();if(!D(e))return null;try{let t=JSON.parse(B(e,"utf-8"));return typeof t.apiKey=="string"?{[G]:t}:t}catch{return null}}function _(){let e=process.env.MISTFLOW_API_KEY;if(e)return{ok:!0,creds:{apiKey:e,orgId:"",orgSlug:"env"}};let t=T();if(!t)return{ok:!1,reason:"missing"};let n=m(),o=t[n];return o&&typeof o.apiKey=="string"&&o.apiKey&&typeof o.orgId=="string"?{ok:!0,creds:o}:{ok:!1,reason:"missing"}}function ge(e){let t=I(),n=W(t);D(n)||K(n,{recursive:!0});let o=T()??{},r=m();o[r]=e;let s=U(n,`.credentials.tmp.${z(8).toString("hex")}`);try{q(s,JSON.stringify(o,null,2)+`
|
|
23
|
+
`,{mode:384}),J(s,t)}catch(c){try{H(s)}catch{}throw c}}function X(){return _().ok}function $(){return m()}function he(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9102")}return"https://mistflow.ai"}var i=class extends Error{constructor(n,o,r,s){super(o);this.code=n;this.statusCode=r;this.details=s;this.name="MistflowApiError"}get isAuth(){return this.code.startsWith("auth_")}get isTransient(){return this.code==="server_error"||this.code==="upstream_error"||this.code==="network_error"||this.code==="rate_limited"}};function Y(){let e=_();if(!e.ok)throw new i("auth_missing","No Mistflow credentials found.",401);return Z(e.creds)}function Z(e){return{Authorization:`ApiKey ${e.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":f()}}function M(e){try{P(e.headers)}catch{}}async function we(){try{let e=await fetch(`${$()}/health`,{method:"GET",signal:AbortSignal.timeout(5e3)});M(e)}catch{}}async function ee(e,t,n,o){for(let r=0;r<2;r++)try{return await fetch(e,{...t,signal:AbortSignal.timeout(n)})}catch(s){let c=s instanceof Error&&s.name==="TimeoutError",p=s instanceof TypeError;if(o&&r===0&&(c||p)){console.error(`[api] Retrying ${e} after ${c?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function te(e){let t=null;try{t=await e.json()}catch{t=null}let n=t&&typeof t.code=="string"?t.code:void 0,o=t&&typeof t.message=="string"?t.message:t&&typeof t.detail=="string"?t.detail:e.statusText||"Request failed";if(n)return new i(n,o,e.status,t?.details);let r=e.status;return r===401?new i("auth_invalid",o,r):r===403?new i("permission_denied",o,r):r===404?new i("not_found",o,r):r===409?new i("conflict",o,r):r===422?new i("validation_error",o,r):r===429?new i("rate_limited",o,r):r>=500?new i("server_error",e.statusText||"Internal server error",r):new i("client_error",o,r)}async function l(e,t={}){let n=Y(),{timeoutMs:o,idempotent:r,...s}=t,c=o??3e4,p=(s.method??"GET").toUpperCase(),j=r??(p==="GET"||p==="HEAD"),u;try{u=await ee(`${$()}${e}`,{...s,headers:{...n,...s.headers}},c,j)}catch(R){throw R instanceof Error&&R.name==="TimeoutError"?new i("network_error","Request timed out. Try again in a moment."):new i("network_error","Cannot reach Mistflow servers. Check your network.")}if(M(u),!u.ok)throw await te(u);return u.json()}var _e=300*1e3;async function Re(e,t,n="neon",o){return l("/api/projects",{method:"POST",body:JSON.stringify({name:e,template:t,db_provider:n,requested_subdomain:o})})}async function xe(e){return l(`/api/deploy/${encodeURIComponent(e)}/status`)}async function Se(e){return l(`/api/deploy/${encodeURIComponent(e)}/logs`)}async function ke(e,t="7d"){return l(`/api/projects/${encodeURIComponent(e)}/errors?period=${encodeURIComponent(t)}`)}async function Ce(e){return l(`/api/projects/${encodeURIComponent(e)}/deployments`)}async function be(e,t){return l(`/api/projects/${encodeURIComponent(e)}/share`,{method:"POST",body:JSON.stringify({is_template:t?.isTemplate??!1,template_description:t?.description})})}export{f as a,P as b,ie as c,ae as d,ge as e,X as f,$ as g,he as h,Y as i,we as j,Re as k,xe as l,Se as m,ke as n,Ce as o,be as p};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as
|
|
1
|
+
import{a as V,d as x,e as I}from"./chunk-UNFTM4B3.js";import{c as F,d as A,e as U,f as H,g as R,h as W,j as B,l as K,m as G,n as Y,o as O,p as z}from"./chunk-VQPRSDSC.js";import{Server as Ue}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Le,ListToolsRequestSchema as Me}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as De}from"zod-to-json-schema";function i(a,e=!1){let t=a;try{let r=F();r&&(t=a+r)}catch{}return{content:[{type:"text",text:t}],isError:e}}function y(a){return i(`This is not a Mistflow project (no mistflow.json found at ${a}).
|
|
2
2
|
|
|
3
3
|
Mistflow creates new projects from scratch \u2014 it doesn't work inside existing codebases.
|
|
4
4
|
|
|
@@ -7,318 +7,17 @@ To get started:
|
|
|
7
7
|
2. Run mist_build (action: 'init') to create a new project in a subdirectory
|
|
8
8
|
3. Run mist_build (action: 'implement') to build each step
|
|
9
9
|
|
|
10
|
-
If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}
|
|
11
|
-
Sign in at: ${
|
|
12
|
-
Your code: ${
|
|
13
|
-
`);try{await
|
|
14
|
-
`)
|
|
15
|
-
`)}
|
|
16
|
-
`)}
|
|
17
|
-
|
|
18
|
-
`+t}function It(n,t){let s=xs(n),e=t??Jn(n);return['import { createSelectSchema, createInsertSchema } from "drizzle-zod";','import { z } from "zod";',`import { ${e} } from "@/db/schema";`,"","// Select schema \u2014 shape of a row read from the DB. Use this on both","// sides of the wire: backend validates responses, frontend gets types.",`export const ${s}Schema = createSelectSchema(${e});`,`export type ${s} = z.infer<typeof ${s}Schema>;`,"","// Insert schema \u2014 shape accepted when creating a new row. id +","// createdAt are generated server-side, so we strip them from the","// input contract. Adjust if your schema uses different column names.",`export const Create${s}Input = createInsertSchema(${e}).omit({`," id: true,"," createdAt: true,","});",`export type Create${s}Input = z.infer<typeof Create${s}Input>;`,""].join(`
|
|
19
|
-
`)}function Ct(n){return`contracts/${je(n)}.ts`}function Xn(n){let t=Fe(At(n)),s=10,e=0;for(;e<s&&t!==Fe(t);){if(he(j(t,"pnpm-workspace.yaml"))||he(j(t,"lerna.json")))return t;let i=j(t,"package.json");if(he(i))try{if(JSON.parse(ot(i,"utf-8")).workspaces)return t}catch{}t=Fe(t),e++}return null}function Tt(n,t,s,e,i,l){return new Promise(o=>{let r=Yn(n,t,{cwd:s,stdio:["pipe","pipe","pipe"],timeout:e,env:l?{...process.env,...l}:process.env}),a="",c="";r.stdout?.on("data",u=>{let m=u.toString();if(c+=m,i)for(let d of m.split(`
|
|
20
|
-
`).filter(Boolean))i(d)}),r.stderr?.on("data",u=>{let m=u.toString();if(a+=m,i)for(let d of m.split(`
|
|
21
|
-
`).filter(Boolean))i(d)}),r.on("close",u=>{if(u===0)o({success:!0});else{let m=a.split(`
|
|
22
|
-
`).find(d=>d.startsWith("npm error"))??a.slice(0,300);o({success:!1,error:m})}}),r.on("error",u=>{o({success:!1,error:u.message})})})}var zi=st.object({name:st.string().min(1),plan:st.any(),path:st.string().optional()});function x(n,t,s){let e=j(n,t);rt(Fe(e),{recursive:!0}),ge(e,s)}function As(n,t){if(!t||typeof t!="object")return;let s=t.acceptanceCriteria;if(!Array.isArray(s)||s.length===0)return;let e=s.map(i=>{let l=i;return{id:String(l.id??""),feature:String(l.feature??""),description:String(l.description??""),priority:l.priority??"p1",status:l.status??"planned",...l.implementedAt?{implementedAt:l.implementedAt}:{},...l.verifiedAt?{verifiedAt:l.verifiedAt}:{},...l.failedAt?{failedAt:l.failedAt}:{},...l.lastError?{lastError:l.lastError}:{}}});Ss(n,{version:1,criteria:e})}function Ns(n){if(!he(n))return!0;let t;try{t=Cs(n)}catch{return!1}return t.filter(e=>e!==".mistflow").length===0}var Zn={sharp:"0.125rem",subtle:"0.375rem",rounded:"0.75rem",pill:"9999px"},er=[["--color-background","#ffffff"],["--color-foreground","#0a0a0a"],["--color-card","#ffffff"],["--color-card-foreground","#0a0a0a"],["--color-popover","#ffffff"],["--color-popover-foreground","#0a0a0a"],["--color-muted","#f5f5f5"],["--color-muted-foreground","#525252"],["--color-border","#e5e5e5"],["--color-input","#e5e5e5"],["--color-primary","#0a0a0a"],["--color-primary-foreground","#ffffff"],["--color-ring","#0a0a0a"],["--color-secondary","#f5f5f5"],["--color-secondary-foreground","#0a0a0a"],["--color-accent","#f5f5f5"],["--color-accent-foreground","#0a0a0a"],["--color-destructive","#b91c1c"],["--color-destructive-foreground","#ffffff"],["--color-success","#15803d"],["--color-success-foreground","#ffffff"],["--color-warning","#a16207"],["--color-warning-foreground","#ffffff"],["--color-info","#0369a1"],["--color-info-foreground","#ffffff"]];function tr(n){let t=n.match(/^---\n([\s\S]*?)\n---/);if(!t)return null;let s=t[1],e={theme:"light",colors:{},typography:{},rounded:{},spacing:{}},i=s.split(`
|
|
23
|
-
`),l=null,o=null,r=a=>a.replace(/^["']|["']$/g,"").trim();for(let a of i){let c=a.replace(/\r$/,"");if(!c.trim()||c.trim().startsWith("#"))continue;if(c.startsWith("theme:")){let d=r(c.slice(6).trim());(d==="dark"||d==="light")&&(e.theme=d);continue}let u=c.match(/^(colors|typography|rounded|spacing):\s*$/);if(u){l=u[1],o=null;continue}if(l==="typography"){let d=c.match(/^ {2}([a-zA-Z0-9_-]+):\s*$/);if(d){o=d[1].replace(/-/g,"_"),e.typography[o]={};continue}let b=c.match(/^ {4}([a-zA-Z0-9_-]+):\s*(.+)$/);if(b&&o){e.typography[o][b[1]]=r(b[2]);continue}}let m=c.match(/^ {2}([a-zA-Z0-9_-]+):\s*(.+)$/);if(m&&l){let d=m[1].replace(/-/g,"_"),b=r(m[2]);l==="colors"?e.colors[d]=b:l==="rounded"?e.rounded[d]=b:l==="spacing"&&(e.spacing[d]=b)}}return e}function sr(n,t){let s=n.colors,i=[["--color-background",s.background],["--color-foreground",s.on_background??s.on_surface],["--color-card",s.surface??s.background],["--color-card-foreground",s.on_surface??s.on_background],["--color-popover",s.surface??s.background],["--color-popover-foreground",s.on_surface??s.on_background],["--color-muted",s.surface_variant??s.outline_variant??s.outline],["--color-muted-foreground",s.on_surface_variant??s.outline],["--color-border",s.outline_variant??s.outline],["--color-input",s.outline_variant??s.outline],["--color-primary",s.primary],["--color-primary-foreground",s.on_primary],["--color-ring",s.primary],["--color-secondary",s.secondary],["--color-secondary-foreground",s.on_secondary],["--color-accent",s.secondary],["--color-accent-foreground",s.on_secondary],["--color-destructive",s.error],["--color-destructive-foreground",s.on_error],["--color-success",s.success],["--color-success-foreground",s.on_success],["--color-warning",s.warning],["--color-warning-foreground",s.on_warning],["--color-info",s.info??s.primary],["--color-info-foreground",s.on_info??s.on_primary]].filter(r=>typeof r[1]=="string").map(([r,a])=>` ${r}: ${a};`).join(`
|
|
24
|
-
`),l=[` --radius-sm: ${n.rounded.sm??"0.25rem"};`,` --radius-md: ${n.rounded.md??t};`,` --radius-lg: ${n.rounded.lg??"0.5rem"};`,` --radius-xl: ${n.rounded.xl??"0.75rem"};`].join(`
|
|
25
|
-
`);return`@import "tailwindcss";
|
|
26
|
-
@import "tw-animate-css";
|
|
27
|
-
|
|
28
|
-
@theme {
|
|
29
|
-
${i}
|
|
30
|
-
${l}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@layer base {
|
|
35
|
-
* { border-color: var(--color-border); }
|
|
36
|
-
body { background-color: var(--color-background); color: var(--color-foreground); }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
:root {
|
|
40
|
-
--ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);
|
|
41
|
-
--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
42
|
-
--ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
43
|
-
--duration-micro: 80ms;
|
|
44
|
-
--duration-short: 150ms;
|
|
45
|
-
--duration-medium: 250ms;
|
|
46
|
-
--duration-long: 400ms;
|
|
47
|
-
--z-dropdown: 10;
|
|
48
|
-
--z-sticky: 20;
|
|
49
|
-
--z-modal-backdrop: 30;
|
|
50
|
-
--z-modal: 40;
|
|
51
|
-
--z-toast: 50;
|
|
52
|
-
--z-tooltip: 60;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
|
|
56
|
-
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
57
|
-
.animate-fade-up { animation: fade-up 0.5s var(--ease-expo-out) both; }
|
|
58
|
-
.animate-fade-in { animation: fade-in 0.3s var(--ease-quart-out) both; }
|
|
59
|
-
|
|
60
|
-
.stagger > :nth-child(1) { animation-delay: 0ms; }
|
|
61
|
-
.stagger > :nth-child(2) { animation-delay: 50ms; }
|
|
62
|
-
.stagger > :nth-child(3) { animation-delay: 100ms; }
|
|
63
|
-
.stagger > :nth-child(4) { animation-delay: 150ms; }
|
|
64
|
-
.stagger > :nth-child(5) { animation-delay: 200ms; }
|
|
65
|
-
.stagger > :nth-child(6) { animation-delay: 250ms; }
|
|
66
|
-
|
|
67
|
-
@media (prefers-reduced-motion: reduce) {
|
|
68
|
-
*, *::before, *::after {
|
|
69
|
-
animation-duration: 0.01ms !important;
|
|
70
|
-
animation-iteration-count: 1 !important;
|
|
71
|
-
transition-duration: 0.01ms !important;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
|
|
76
|
-
:focus:not(:focus-visible) { outline: none; }
|
|
77
|
-
|
|
78
|
-
button, [role="button"] { transition: transform 100ms var(--ease-quart-out); }
|
|
79
|
-
button:active:not(:disabled), [role="button"]:active:not(:disabled) { transform: scale(0.97); }
|
|
80
|
-
`}function nr(n,t,s){let e=n?.borderRadius??"subtle",i=Zn[e]??"0.375rem";if(s){let o=tr(s);if(o)return sr(o,i)}return`@import "tailwindcss";
|
|
81
|
-
@import "tw-animate-css";
|
|
82
|
-
|
|
83
|
-
@theme {
|
|
84
|
-
${[...er.map(([o,r])=>` ${o}: ${r};`)," --radius-sm: 0.25rem;",` --radius-md: ${i};`," --radius-lg: 0.5rem;"," --radius-xl: 0.75rem;"].join(`
|
|
85
|
-
`)}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
@layer base {
|
|
89
|
-
* { border-color: var(--color-border); }
|
|
90
|
-
body { background-color: var(--color-background); color: var(--color-foreground); }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
:root {
|
|
94
|
-
--ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);
|
|
95
|
-
--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
96
|
-
--ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
97
|
-
--duration-micro: 80ms;
|
|
98
|
-
--duration-short: 150ms;
|
|
99
|
-
--duration-medium: 250ms;
|
|
100
|
-
--duration-long: 400ms;
|
|
101
|
-
--z-dropdown: 10;
|
|
102
|
-
--z-sticky: 20;
|
|
103
|
-
--z-modal-backdrop: 30;
|
|
104
|
-
--z-modal: 40;
|
|
105
|
-
--z-toast: 50;
|
|
106
|
-
--z-tooltip: 60;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
|
|
110
|
-
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
111
|
-
.animate-fade-up { animation: fade-up 0.5s var(--ease-expo-out) both; }
|
|
112
|
-
.animate-fade-in { animation: fade-in 0.3s var(--ease-quart-out) both; }
|
|
113
|
-
|
|
114
|
-
.stagger > :nth-child(1) { animation-delay: 0ms; }
|
|
115
|
-
.stagger > :nth-child(2) { animation-delay: 50ms; }
|
|
116
|
-
.stagger > :nth-child(3) { animation-delay: 100ms; }
|
|
117
|
-
.stagger > :nth-child(4) { animation-delay: 150ms; }
|
|
118
|
-
.stagger > :nth-child(5) { animation-delay: 200ms; }
|
|
119
|
-
.stagger > :nth-child(6) { animation-delay: 250ms; }
|
|
120
|
-
|
|
121
|
-
@media (prefers-reduced-motion: reduce) {
|
|
122
|
-
*, *::before, *::after {
|
|
123
|
-
animation-duration: 0.01ms !important;
|
|
124
|
-
animation-iteration-count: 1 !important;
|
|
125
|
-
transition-duration: 0.01ms !important;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
|
|
130
|
-
:focus:not(:focus-visible) { outline: none; }
|
|
131
|
-
|
|
132
|
-
button, [role="button"] { transition: transform 100ms var(--ease-quart-out); }
|
|
133
|
-
button:active:not(:disabled), [role="button"]:active:not(:disabled) { transform: scale(0.97); }
|
|
134
|
-
`}var Rs={"Fredoka One":"Fredoka","Source Sans Pro":"Source_Sans_3","Source Serif Pro":"Source_Serif_4","Open Sans Condensed":"Open_Sans","Baloo 2":"Baloo_2","DM Serif Display":"DM_Serif_Display","DM Serif Text":"DM_Serif_Text","IBM Plex Mono":"IBM_Plex_Mono","IBM Plex Sans":"IBM_Plex_Sans","IBM Plex Serif":"IBM_Plex_Serif","Fira Code":"Fira_Code","Fira Sans":"Fira_Sans","Noto Sans JP":"Noto_Sans_JP","PT Sans":"PT_Sans","PT Serif":"PT_Serif","Work Sans":"Work_Sans","Space Mono":"Space_Mono","Space Grotesk":"Space_Grotesk","Plus Jakarta Sans":"Plus_Jakarta_Sans"};function Is(n){let t=n.replace(/[^A-Za-z0-9_ -]/g,"");return Rs[t]?Rs[t]:t.replace(/\s+/g,"_")}function rr(n){return n?{english:"en",spanish:"es",french:"fr",german:"de",italian:"it",portuguese:"pt",dutch:"nl",russian:"ru",japanese:"ja",chinese:"zh",korean:"ko",arabic:"ar",hebrew:"he",hindi:"hi",turkish:"tr",polish:"pl",swedish:"sv",norwegian:"no",danish:"da",finnish:"fi",thai:"th",vietnamese:"vi",indonesian:"id",malay:"ms",farsi:"fa",persian:"fa",czech:"cs",greek:"el",romanian:"ro",hungarian:"hu",ukrainian:"uk",bengali:"bn",tamil:"ta",telugu:"te",urdu:"ur"}[n.toLowerCase()]??"en":"en"}var or=new Set(["ar","he","fa","ur"]);function ir(n,t,s){let e=n.replace(/[\\"`$]/g,""),i=rr(s),o=or.has(i)?`lang="${i}" dir="rtl"`:`lang="${i}"`,r=t?.fonts?.heading,a=t?.fonts?.body;if(!r&&!a)return`import type { Metadata } from "next";
|
|
135
|
-
import { DM_Sans } from "next/font/google";
|
|
136
|
-
import { Toaster } from "sonner";
|
|
137
|
-
import "./globals.css";
|
|
138
|
-
|
|
139
|
-
const body = DM_Sans({ subsets: ["latin"], variable: "--font-body" });
|
|
140
|
-
|
|
141
|
-
export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
|
|
142
|
-
|
|
143
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
144
|
-
return (
|
|
145
|
-
<html ${o}>
|
|
146
|
-
<body className={body.variable}>{children}<Toaster richColors /></body>
|
|
147
|
-
</html>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
`;let c=Is(r??a),u=Is(a??r);return c===u?`import type { Metadata } from "next";
|
|
151
|
-
import { ${c} } from "next/font/google";
|
|
152
|
-
import { Toaster } from "sonner";
|
|
153
|
-
import "./globals.css";
|
|
154
|
-
|
|
155
|
-
const font = ${c}({ subsets: ["latin"], variable: "--font-body" });
|
|
156
|
-
|
|
157
|
-
export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
|
|
158
|
-
|
|
159
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
160
|
-
return (
|
|
161
|
-
<html ${o}>
|
|
162
|
-
<body className={font.variable}>{children}<Toaster richColors /></body>
|
|
163
|
-
</html>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
`:`import type { Metadata } from "next";
|
|
167
|
-
import { ${c}, ${u} } from "next/font/google";
|
|
168
|
-
import { Toaster } from "sonner";
|
|
169
|
-
import "./globals.css";
|
|
170
|
-
|
|
171
|
-
const heading = ${c}({ subsets: ["latin"], variable: "--font-heading" });
|
|
172
|
-
const body = ${u}({ subsets: ["latin"], variable: "--font-body" });
|
|
173
|
-
|
|
174
|
-
export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
|
|
175
|
-
|
|
176
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
177
|
-
return (
|
|
178
|
-
<html ${o}>
|
|
179
|
-
<body className={\`\${heading.variable} \${body.variable}\`}>{children}<Toaster richColors /></body>
|
|
180
|
-
</html>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
`}function nt(n,...t){let s=JSON.stringify(n).toLowerCase();return t.some(e=>s.includes(e.toLowerCase()))}var ar={dashboard:"Home",home:"Home",overview:"Home",patient:"Users",member:"Users",user:"Users",people:"Users",team:"Users",client:"Users",contact:"Users",customer:"Users",appointment:"Calendar",schedule:"Calendar",booking:"Calendar",event:"Calendar",billing:"CreditCard",invoice:"CreditCard",payment:"CreditCard",pricing:"CreditCard",treatment:"ClipboardList",plan:"ClipboardList",task:"CheckSquare",exercise:"Dumbbell",workout:"Dumbbell",report:"BarChart3",analytics:"BarChart3",stats:"BarChart3",setting:"Settings",config:"Settings",profile:"User",account:"User",message:"MessageSquare",chat:"MessageSquare",inbox:"MessageSquare",product:"Package",item:"Package",catalog:"Package",order:"ShoppingCart",cart:"ShoppingCart",file:"FileText",document:"FileText",upload:"Upload",notification:"Bell",alert:"Bell",project:"FolderKanban",board:"FolderKanban",post:"PenSquare",blog:"PenSquare",article:"PenSquare",course:"GraduationCap",lesson:"GraduationCap",class:"GraduationCap",habit:"Target",goal:"Target",streak:"Flame",progress:"TrendingUp",feature:"Sparkles",subscription:"CreditCard",price:"CreditCard",recipe:"ChefHat",food:"UtensilsCrossed",meal:"UtensilsCrossed",pet:"PawPrint",animal:"PawPrint",music:"Music",playlist:"ListMusic",song:"Music",photo:"Image",image:"Image",gallery:"Images",video:"Video",movie:"Film",map:"MapPin",location:"MapPin",place:"MapPin",search:"Search",explore:"Compass",inventory:"Boxes",stock:"Boxes",warehouse:"Warehouse",review:"Star",rating:"Star",feedback:"Star",log:"ScrollText",history:"Clock",activity:"Activity"};function Et(n){let t=n.toLowerCase().replace(/[^a-z]/g,"");for(let[s,e]of Object.entries(ar))if(t.includes(s))return e;return"Circle"}function Be(n){return n.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function lr(n){if(n.authModel==="none")return null;let t=["/login","/register","/forgot-password","/reset-password","/api/auth","/api/health","/api/webhooks","/api/admin/seed"];if(n.publicPages&&Array.isArray(n.publicPages))for(let l of n.publicPages){if(typeof l!="string"||l.length<1)continue;let o=l.replace(/[\u201C\u201D\u201E\u201F\u2018\u2019\u2033\u2036]/g,"").trim();if(!o)continue;let r=o.startsWith("/")?o:"/"+o;t.includes(r)||t.push(r)}let s=t.filter(l=>l==="/"),e=t.filter(l=>l!=="/"),i=[];i.push('import { NextRequest, NextResponse } from "next/server";'),i.push(""),i.push("const PUBLIC_PREFIXES = [");for(let l of e)i.push(' "'+l+'",');return i.push("];"),i.push(""),s.length>0&&(i.push('const PUBLIC_EXACT = ["'+s.join('", "')+'"];'),i.push("")),i.push("export function middleware(req: NextRequest) {"),i.push(" const { pathname, search } = req.nextUrl;"),i.push(""),s.length>0&&i.push(" if (PUBLIC_EXACT.includes(pathname)) return NextResponse.next();"),i.push(" if (PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))) return NextResponse.next();"),i.push(""),i.push(' const token = req.cookies.get("better-auth.session_token")?.value || req.cookies.get("__Secure-better-auth.session_token")?.value;'),i.push(" if (!token) {"),i.push(' const loginUrl = new URL("/login", req.url);'),i.push(" const params = new URLSearchParams(search);"),i.push(' for (const key of ["verified", "error"]) {'),i.push(" const v = params.get(key);"),i.push(" if (v) loginUrl.searchParams.set(key, v);"),i.push(" }"),i.push(" return NextResponse.redirect(loginUrl);"),i.push(" }"),i.push(""),i.push(" return NextResponse.next();"),i.push("}"),i.push(""),i.push("export const config = {"),i.push(' matcher: ["/((?!_next|static|favicon\\\\.ico).*)"],'),i.push("};"),i.push(""),i.join(`
|
|
184
|
-
`)}function cr(n){if(n.navStyle==="none")return null;let t=["/api","/login","/register","/sign-in","/sign-up","/admin","/pricing","/about","/contact","/terms","/privacy","/onboarding","/join","/forgot-password","/reset-password"],e=(n.pages??[]).filter(a=>{let c=a.path??a.route??"";return c==="/"||c===""||c.includes("[")||c.replace(/^\//,"").split("/").length>1?!1:!t.some(m=>c.startsWith(m))}).map(a=>{let c=a.path??a.route??"",u=c.startsWith("/")?c:"/"+c,m=a.name??Be(c.replace(/^\//,"")),d=Et(m);return{label:m,href:u,icon:d}});e.some(a=>a.href==="/dashboard")||e.unshift({label:"Dashboard",href:"/dashboard",icon:"Home"});let i=n.authModel==="none",l=[...new Set(e.map(a=>a.icon))];i||l.push("LogOut");let o=Be(n.name);if(n.navStyle==="topbar"){let a=[];a.push('"use client";'),a.push(""),a.push('import Link from "next/link";'),a.push('import { usePathname } from "next/navigation";'),i||a.push('import { authClient } from "@/lib/auth-client";'),a.push('import { Button } from "@/components/ui/button";'),a.push('import { cn } from "@/lib/utils";'),a.push("import { "+l.join(", ")+' } from "lucide-react";'),a.push(""),a.push("interface TopNavProps {"),a.push(" user: { name: string | null; email: string; role?: string | undefined };"),a.push("}"),a.push(""),a.push("const NAV_ITEMS = [");for(let c of e)a.push(' { label: "'+c.label+'", href: "'+c.href+'", icon: '+c.icon+" },");return a.push("];"),a.push(""),a.push("export default function TopNav({ user }: TopNavProps) {"),a.push(" const pathname = usePathname();"),a.push(""),a.push(" return ("),a.push(' <nav className="border-b bg-card">'),a.push(' <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4">'),a.push(' <div className="flex items-center gap-6">'),a.push(' <span className="text-lg font-semibold">'+o+"</span>"),a.push(' <div className="flex items-center gap-1">'),a.push(" {NAV_ITEMS.map((item) => ("),a.push(" <Link"),a.push(" key={item.href}"),a.push(" href={item.href}"),a.push(' aria-current={pathname === item.href ? "page" : undefined}'),a.push(" className={cn("),a.push(' "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors",'),a.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground"'),a.push(" )}"),a.push(" >"),a.push(' <item.icon className="h-4 w-4" />'),a.push(" {item.label}"),a.push(" </Link>"),a.push(" ))}"),a.push(" </div>"),a.push(" </div>"),i?a.push(' <span className="text-sm text-muted-foreground">{user.name}</span>'):(a.push(' <div className="flex items-center gap-2">'),a.push(' <span className="text-sm text-muted-foreground">{user.email}</span>'),a.push(" <Button"),a.push(' variant="ghost"'),a.push(' size="sm"'),a.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),a.push(" >"),a.push(' <LogOut className="h-4 w-4" />'),a.push(" </Button>"),a.push(" </div>")),a.push(" </div>"),a.push(" </nav>"),a.push(" );"),a.push("}"),a.push(""),{path:"components/topnav.tsx",content:a.join(`
|
|
185
|
-
`)}}let r=[];r.push('"use client";'),r.push(""),r.push('import { useState } from "react";'),r.push('import Link from "next/link";'),r.push('import { usePathname } from "next/navigation";'),i||r.push('import { authClient } from "@/lib/auth-client";'),r.push('import { Button } from "@/components/ui/button";'),r.push('import { Sheet, SheetContent, SheetTrigger, SheetTitle } from "@/components/ui/sheet";'),r.push('import { cn } from "@/lib/utils";'),r.push("import { Menu, "+l.join(", ")+' } from "lucide-react";'),r.push(""),r.push("interface SidebarProps {"),r.push(" user: { name: string | null; email: string; role?: string | undefined };"),r.push("}"),r.push(""),r.push("const NAV_ITEMS = [");for(let a of e)r.push(' { label: "'+a.label+'", href: "'+a.href+'", icon: '+a.icon+" },");return r.push("];"),r.push(""),r.push('function NavContent({ pathname, user, onNavigate }: { pathname: string; user: SidebarProps["user"]; onNavigate?: () => void }) {'),r.push(" return ("),r.push(" <>"),r.push(' <div className="flex h-14 items-center border-b px-4">'),r.push(' <span className="text-lg font-semibold">'+o+"</span>"),r.push(" </div>"),r.push(' <nav className="flex-1 space-y-1 p-2">'),r.push(" {NAV_ITEMS.map((item) => ("),r.push(" <Link"),r.push(" key={item.href}"),r.push(" href={item.href}"),r.push(" onClick={onNavigate}"),r.push(' aria-current={pathname === item.href ? "page" : undefined}'),r.push(" className={cn("),r.push(' "flex items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium transition-colors",'),r.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"'),r.push(" )}"),r.push(" >"),r.push(' <item.icon className="h-4 w-4" />'),r.push(" {item.label}"),r.push(" </Link>"),r.push(" ))}"),r.push(" </nav>"),r.push(' <div className="border-t p-4">'),r.push(' <div className="flex items-center gap-3">'),r.push(' <div className="flex-1 truncate">'),r.push(' <p className="truncate text-sm font-medium">{user.name ?? "User"}</p>'),r.push(' <p className="truncate text-xs text-muted-foreground">{user.email}</p>'),r.push(" </div>"),i||(r.push(" <Button"),r.push(' variant="ghost"'),r.push(' size="icon"'),r.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),r.push(" >"),r.push(' <LogOut className="h-4 w-4" />'),r.push(" </Button>")),r.push(" </div>"),r.push(" </div>"),r.push(" </>"),r.push(" );"),r.push("}"),r.push(""),r.push("export default function Sidebar({ user }: SidebarProps) {"),r.push(" const pathname = usePathname();"),r.push(" const [open, setOpen] = useState(false);"),r.push(""),r.push(" return ("),r.push(" <>"),r.push(' <aside className="hidden md:flex h-screen w-64 flex-col border-r bg-card">'),r.push(" <NavContent pathname={pathname} user={user} />"),r.push(" </aside>"),r.push(' <div className="sticky top-0 z-[var(--z-sticky)] flex h-14 items-center gap-3 border-b bg-card px-4 md:hidden">'),r.push(" <Sheet open={open} onOpenChange={setOpen}>"),r.push(" <SheetTrigger asChild>"),r.push(' <Button variant="ghost" size="icon" className="-ml-2">'),r.push(' <Menu className="h-5 w-5" />'),r.push(" </Button>"),r.push(" </SheetTrigger>"),r.push(' <SheetContent side="left" className="w-64 p-0">'),r.push(' <SheetTitle className="sr-only">Navigation</SheetTitle>'),r.push(' <div className="flex h-full flex-col">'),r.push(" <NavContent pathname={pathname} user={user} onNavigate={() => setOpen(false)} />"),r.push(" </div>"),r.push(" </SheetContent>"),r.push(" </Sheet>"),r.push(' <span className="text-lg font-semibold">'+o+"</span>"),r.push(" </div>"),r.push(" </>"),r.push(" );"),r.push("}"),r.push(""),{path:"components/sidebar.tsx",content:r.join(`
|
|
186
|
-
`)}}function pr(n){if(!n.roles||n.roles.length===0)return null;let t=n.roles,s=n.defaultRole??t[0],e=[];e.push("export type Role = "+t.map(i=>'"'+i+'"').join(" | ")+";"),e.push(""),e.push("export const ROLES = ["+t.map(i=>'"'+i+'"').join(", ")+"] as const;"),e.push(""),e.push('export const DEFAULT_ROLE: Role = "'+s+'";'),e.push(""),e.push("export const ROLE_LABELS: Record<Role, string> = {");for(let i of t){let l=i.charAt(0).toUpperCase()+i.slice(1);e.push(' "'+i+'": "'+l+'",')}return e.push("};"),e.push(""),e.push("export function getUserRole(user: Record<string, unknown>): Role {"),e.push(" const role = (user.role as string) ?? DEFAULT_ROLE;"),e.push(" if (ROLES.includes(role as Role)) return role as Role;"),e.push(" return DEFAULT_ROLE;"),e.push("}"),e.push(""),e.push("export function hasRole(userRole: string | undefined, required: Role | Role[]): boolean {"),e.push(" if (!userRole) return false;"),e.push(" const allowed = Array.isArray(required) ? required : [required];"),e.push(" return allowed.includes(userRole as Role);"),e.push("}"),e.push(""),e.join(`
|
|
187
|
-
`)}function ur(n){let t=Be(n.name);if(n.authModel==="none"){let l=[];return l.push("export default function HomePage() {"),l.push(" return ("),l.push(' <main className="flex min-h-screen flex-col items-center justify-center p-8">'),l.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),n.summary&&l.push(' <p className="mt-4 text-lg text-muted-foreground">'+n.summary+"</p>"),l.push(" </main>"),l.push(" );"),l.push("}"),l.push(""),l.join(`
|
|
188
|
-
`)}let s=n.publicPages?.includes("/"),e=n.design?.landingTone;if(s&&e){let l=[];return l.push('import Link from "next/link";'),l.push(""),l.push("export default function HomePage() {"),l.push(" return ("),l.push(' <main className="flex min-h-screen flex-col">'),l.push(' <section className="flex flex-1 flex-col items-center justify-center gap-6 px-4 py-24 text-center">'),l.push(' <h1 className="text-5xl font-bold tracking-tight">'+t+"</h1>"),n.summary&&l.push(' <p className="max-w-2xl text-xl text-muted-foreground">'+n.summary+"</p>"),l.push(' <div className="flex gap-4">'),l.push(' <Link href="/register" className="inline-flex h-11 items-center rounded-md bg-primary px-8 text-sm font-medium text-primary-foreground hover:bg-primary/90">'),l.push(" Get Started"),l.push(" </Link>"),l.push(' <Link href="/login" className="inline-flex h-11 items-center rounded-md border px-8 text-sm font-medium hover:bg-muted">'),l.push(" Sign In"),l.push(" </Link>"),l.push(" </div>"),l.push(" </section>"),l.push(" </main>"),l.push(" );"),l.push("}"),l.push(""),l.join(`
|
|
189
|
-
`)}if(s){let l=[];return l.push('import Link from "next/link";'),l.push(""),l.push("export default function HomePage() {"),l.push(" return ("),l.push(' <main className="flex min-h-screen flex-col items-center justify-center gap-6 p-8 text-center">'),l.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),n.summary&&l.push(' <p className="text-lg text-muted-foreground">'+n.summary+"</p>"),l.push(' <Link href="/login" className="inline-flex h-10 items-center rounded-md bg-primary px-6 text-sm font-medium text-primary-foreground hover:bg-primary/90">'),l.push(" Sign In"),l.push(" </Link>"),l.push(" </main>"),l.push(" );"),l.push("}"),l.push(""),l.join(`
|
|
190
|
-
`)}let i=[];return i.push('import { headers } from "next/headers";'),i.push('import { redirect } from "next/navigation";'),i.push('import { auth } from "@/lib/auth";'),i.push(""),i.push("export default async function HomePage() {"),i.push(" const session = await auth.api.getSession({ headers: await headers() });"),i.push(' if (session) redirect("/dashboard");'),i.push(' redirect("/login");'),i.push("}"),i.push(""),i.join(`
|
|
191
|
-
`)}function dr(n,t){let s=n.authModel==="none",e=[];s||(e.push('import { Suspense } from "react";'),e.push('import { headers } from "next/headers";'),e.push('import { redirect } from "next/navigation";'),e.push('import { auth } from "@/lib/auth";'),e.push('import { VerifiedBanner } from "@/components/auth/verified-banner";')),n.navStyle==="topbar"?e.push('import TopNav from "@/components/topnav";'):n.navStyle!=="none"&&e.push('import Sidebar from "@/components/sidebar";'),!s&&n.roles&&n.roles.length>0&&e.push('import { getUserRole } from "@/lib/roles";'),e.push(""),e.push("export default async function DashboardLayout({ children }: { children: React.ReactNode }) {"),s?e.push(' const user = { name: "Guest", email: "" };'):(e.push(" const session = await auth.api.getSession({ headers: await headers() });"),e.push(' if (!session) redirect("/login");'),e.push(""),n.roles&&n.roles.length>0?(e.push(" const role = getUserRole(session.user as Record<string, unknown>);"),e.push(" const user = { name: session.user.name, email: session.user.email, role };")):(e.push(" const user = {"),e.push(" name: session.user.name,"),e.push(" email: session.user.email,"),e.push(" role: (session.user as Record<string, unknown>).role as string | undefined,"),e.push(" };"))),e.push("");let i=s?"":"<Suspense fallback={null}><VerifiedBanner /></Suspense>";return n.navStyle==="topbar"?(e.push(" return ("),e.push(' <div className="min-h-screen">'),e.push(" <TopNav user={user} />"),e.push(' <main className="mx-auto max-w-7xl p-6">'+i+"{children}</main>"),e.push(" </div>"),e.push(" );")):n.navStyle==="none"?(e.push(" return ("),e.push(' <div className="min-h-screen">'),e.push(' <main className="mx-auto max-w-5xl p-6">'+i+"{children}</main>"),e.push(" </div>"),e.push(" );")):(e.push(" return ("),e.push(' <div className="flex flex-col md:flex-row min-h-screen">'),e.push(" <Sidebar user={user} />"),e.push(' <main className="flex-1 overflow-x-hidden p-4 md:p-6">'+i+"{children}</main>"),e.push(" </div>"),e.push(" );")),e.push("}"),e.push(""),e.join(`
|
|
192
|
-
`)}function mr(n){let t=Be(n.name),s=n.dataModel??[],e=[];if(s.length>0){let i=s.map(o=>Et(o.entity??o.name??"item")),l=[...new Set(i)];e.push('import { Card, CardContent } from "@/components/ui/card";'),e.push("import { "+l.join(", ")+' } from "lucide-react";'),e.push("")}if(e.push("export default function DashboardPage() {"),e.push(" return ("),e.push(' <div className="space-y-6">'),e.push(" <div>"),e.push(' <h1 className="text-3xl font-bold">'+t+"</h1>"),n.summary&&e.push(' <p className="mt-1 text-muted-foreground">'+n.summary+"</p>"),e.push(" </div>"),s.length>0){e.push(' <div className="rounded-lg border p-8 text-center">'),e.push(' <h2 className="text-lg font-semibold">Get started</h2>'),e.push(` <p className="mt-1 text-sm text-muted-foreground">Here's what you can do</p>`),e.push(' <div className="mt-6 grid gap-3 sm:grid-cols-2 text-left">');for(let i of s){let l=i.entity??i.name??"Item",o=Et(l),r=Be(l.replace(/_/g,"-"));e.push(' <div className="flex items-center gap-3 rounded-md border p-3">'),e.push(" <"+o+' className="h-5 w-5 text-muted-foreground" />'),e.push(' <span className="text-sm font-medium">Add your first '+r+"</span>"),e.push(" </div>")}e.push(" </div>"),e.push(" </div>")}return e.push(" </div>"),e.push(" );"),e.push("}"),e.push(""),e.join(`
|
|
193
|
-
`)}function fr(n,t=!1){if(!n.multiTenant)return null;let s=[];return t?(s.push('import { pgTable, text, timestamp, index } from "drizzle-orm/pg-core";'),s.push('import { user } from "./auth";'),s.push(""),s.push('export const organization = pgTable("organization", {'),s.push(' id: text("id").primaryKey(),'),s.push(' name: text("name").notNull(),'),s.push(' slug: text("slug").unique().notNull(),'),s.push(' createdAt: timestamp("created_at").defaultNow().notNull(),'),s.push(' updatedAt: timestamp("updated_at").defaultNow().notNull(),'),s.push("});"),s.push(""),s.push("export const orgMember = pgTable(")):(s.push('import { sqliteTable, text, index } from "drizzle-orm/sqlite-core";'),s.push('import { sql } from "drizzle-orm";'),s.push('import { user } from "./auth";'),s.push(""),s.push('export const organization = sqliteTable("organization", {'),s.push(' id: text("id").primaryKey(),'),s.push(' name: text("name").notNull(),'),s.push(' slug: text("slug").unique().notNull(),'),s.push(' createdAt: text("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push(' updatedAt: text("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push("});"),s.push(""),s.push("export const orgMember = sqliteTable(")),s.push(' "org_member",'),s.push(" {"),s.push(' id: text("id").primaryKey(),'),s.push(' orgId: text("org_id").notNull().references(() => organization.id),'),s.push(' userId: text("user_id").notNull().references(() => user.id),'),s.push(' role: text("role").notNull(),'),t?s.push(' joinedAt: timestamp("joined_at").defaultNow().notNull(),'):s.push(' joinedAt: text("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push(" },"),s.push(" (table) => ({"),s.push(' orgIdx: index("org_member_org_idx").on(table.orgId),'),s.push(' userIdx: index("org_member_user_idx").on(table.userId),'),s.push(" }),"),s.push(");"),s.push(""),s.join(`
|
|
194
|
-
`)}function gr(n){if(!n.multiTenant)return null;let t=[];return t.push('import { db } from "./db";'),t.push('import { organization, orgMember } from "@/db/schema/organization";'),t.push('import { eq } from "drizzle-orm";'),t.push(""),t.push("export async function getCurrentOrg(userId: string) {"),t.push(" const membership = await db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.userId, userId))"),t.push(" .limit(1);"),t.push(" if (membership.length === 0) return null;"),t.push(" const org = await db"),t.push(" .select()"),t.push(" .from(organization)"),t.push(" .where(eq(organization.id, membership[0].orgId))"),t.push(" .limit(1);"),t.push(" return org[0] ?? null;"),t.push("}"),t.push(""),t.push("export async function getOrgMembers(orgId: string) {"),t.push(" return db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.orgId, orgId));"),t.push("}"),t.push(""),t.push("export async function inviteToOrg(orgId: string, email: string, role: string) {"),t.push(" const id = crypto.randomUUID();"),t.push(" await db.insert(orgMember).values({"),t.push(" id,"),t.push(" orgId,"),t.push(" userId: email,"),t.push(" role,"),t.push(" });"),t.push(" return { id, orgId, email, role };"),t.push("}"),t.push(""),t.join(`
|
|
195
|
-
`)}function hr(n){if(!n.multiTenant)return null;let t=[];return t.push('"use client";'),t.push(""),t.push("import {"),t.push(" DropdownMenu,"),t.push(" DropdownMenuContent,"),t.push(" DropdownMenuItem,"),t.push(" DropdownMenuTrigger,"),t.push('} from "@/components/ui/dropdown-menu";'),t.push('import { Button } from "@/components/ui/button";'),t.push('import { ChevronsUpDown } from "lucide-react";'),t.push(""),t.push("interface OrgSwitcherProps {"),t.push(" orgs: Array<{ id: string; name: string }>;"),t.push(" currentOrgId: string;"),t.push("}"),t.push(""),t.push("export default function OrgSwitcher({ orgs, currentOrgId }: OrgSwitcherProps) {"),t.push(" const currentOrg = orgs.find((o) => o.id === currentOrgId);"),t.push(""),t.push(" return ("),t.push(" <DropdownMenu>"),t.push(" <DropdownMenuTrigger asChild>"),t.push(' <Button variant="outline" className="w-full justify-between">'),t.push(' <span className="truncate">{currentOrg?.name ?? "Select org"}</span>'),t.push(' <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />'),t.push(" </Button>"),t.push(" </DropdownMenuTrigger>"),t.push(' <DropdownMenuContent className="w-56">'),t.push(" {orgs.map((org) => ("),t.push(" <DropdownMenuItem key={org.id}>"),t.push(" {org.name}"),t.push(" </DropdownMenuItem>"),t.push(" ))}"),t.push(" </DropdownMenuContent>"),t.push(" </DropdownMenu>"),t.push(" );"),t.push("}"),t.push(""),t.join(`
|
|
196
|
-
`)}function yr(n,t,s){let e=[],i=n.split("-").map(a=>a.charAt(0).toUpperCase()+a.slice(1)).join(" ");e.push(`# ${i}`),e.push(""),t?.summary&&(e.push(t.summary),e.push(""));let l=t?.features??[];if(l.length>0){e.push("## Features"),e.push("");for(let a of l){let c=a.description?` \u2014 ${a.description}`:"";e.push(`- **${a.name}**${c}`)}e.push("")}e.push("## Tech Stack"),e.push(""),e.push("| Layer | Technology |"),e.push("|-------|------------|"),e.push("| Framework | Next.js 15 (App Router) |"),e.push("| Database | Mistflow Cloud (Postgres) + Drizzle ORM |"),e.push("| Auth | Better Auth (email/password, social login) |"),e.push("| Styling | Tailwind CSS + shadcn/ui |"),e.push("| Deployment | Mistflow Cloud |"),s.hasStripe&&e.push("| Payments | Stripe |"),s.hasResend&&e.push("| Email | Resend + React Email |"),s.hasStorage&&e.push("| File Storage | Mistflow Cloud (managed blob storage) |"),s.hasAdmin&&e.push("| Admin | Better Auth admin plugin |"),s.hasAI&&e.push("| AI | Vercel AI SDK + OpenAI |"),e.push("");let o=t?.pages??[];if(o.length>0){e.push("## Pages"),e.push(""),e.push("| Route | Description |"),e.push("|-------|-------------|");for(let a of o){let c=a.path??a.route??a.name??"",u=a.description??"";e.push(`| \`${c.startsWith("/")?c:"/"+c}\` | ${u} |`)}e.push("")}let r=t?.dataModel??[];if(r.length>0){e.push("## Data Model"),e.push("");for(let a of r){let c=a.entity??a.name??"Unknown";if(e.push(`### ${c}`),e.push(""),a.fields.length>0){if(typeof a.fields[0]=="string")e.push(`Fields: ${a.fields.join(", ")}`);else{e.push("| Field | Type |"),e.push("|-------|------|");for(let u of a.fields)e.push(`| ${u.name} | ${u.type} |`)}e.push("")}}}return e.push("## Getting Started"),e.push(""),e.push("### Prerequisites"),e.push(""),e.push("- Node.js 20+"),e.push("- npm"),e.push(""),e.push("### Install"),e.push(""),e.push("Use the Mistflow CLI (recommended \u2014 streams output live and has no 60s tool-call timeout):"),e.push(""),e.push("```bash"),e.push("npx -y @mistflow-ai/cli install"),e.push("```"),e.push(""),e.push("Or plain npm if you prefer:"),e.push(""),e.push("```bash"),e.push("npm install"),e.push("```"),e.push(""),e.push("### Set up environment"),e.push(""),e.push("Copy `.env.example` to `.env.local` and fill in the values:"),e.push(""),e.push("```bash"),e.push("cp .env.example .env.local"),e.push("```"),e.push(""),e.push("| Variable | Description | Required |"),e.push("|----------|-------------|----------|"),s.isNeon?e.push("| `DATABASE_URL` | Postgres connection URL | Yes |"):(e.push("| `TURSO_URL` | Database connection URL | Yes |"),e.push("| `TURSO_AUTH_TOKEN` | Database auth token | Yes |")),e.push("| `AUTH_SECRET` | Auth encryption secret (auto-generated) | Yes |"),s.hasStripe&&(e.push("| `STRIPE_SECRET_KEY` | Stripe secret key | Yes |"),e.push("| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | Yes |"),e.push("| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | Yes |")),s.hasResend&&(e.push("| `RESEND_API_KEY` | Resend API key | Yes |"),e.push("| `EMAIL_FROM` | Sender email address | Yes (production) |")),s.hasStorage&&(e.push("| `MISTFLOW_API_KEY` | Mistflow API key for file storage | Yes |"),e.push("| `MISTFLOW_PROJECT_ID` | Mistflow project ID | Yes |")),s.hasAI&&e.push("| `OPENAI_API_KEY` | OpenAI API key | Yes |"),e.push(""),e.push("### Local database"),e.push(""),s.isNeon?(e.push("For local development, start a local Postgres server:"),e.push(""),e.push("```bash"),e.push("# Using Docker:"),e.push("docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:17"),e.push("# Or install via Homebrew: brew install postgresql@17 && brew services start postgresql@17"),e.push("```")):(e.push("For local development, start a local Turso server:"),e.push(""),e.push("```bash"),e.push("npx turso dev"),e.push("```")),e.push(""),e.push("Then set up the database:"),e.push(""),e.push("```bash"),e.push("npm run db:push"),e.push("```"),e.push(""),e.push("### Run"),e.push(""),e.push("```bash"),e.push("npm run dev"),e.push("```"),e.push(""),e.push("Open [http://localhost:3000](http://localhost:3000)."),e.push(""),e.push("## Project Structure"),e.push(""),e.push("```"),e.push("app/"),e.push(" (auth)/ Login and registration pages"),e.push(" (dashboard)/ Authenticated app pages"),s.hasAdmin&&e.push(" (admin)/ Admin panel pages"),e.push(" api/ API routes (auth, health, webhooks)"),e.push(" layout.tsx Root layout with fonts and providers"),e.push(" globals.css Design tokens and Tailwind config"),e.push("components/ Reusable UI components"),e.push("db/"),e.push(" schema/ Database table definitions"),e.push(" index.ts Schema exports"),e.push("lib/"),e.push(" auth.ts Better Auth server config"),e.push(" auth-client.ts Better Auth client config"),e.push(` db.ts ${s.isNeon?"Postgres":"SQLite"} database connection`),s.hasStripe&&e.push(" stripe.ts Stripe client"),s.hasResend&&(e.push(" resend.ts Resend client"),e.push(" email.ts Email send helpers")),s.hasStorage&&e.push(" storage.ts File upload/download helpers"),s.hasAI&&e.push(" ai.ts AI client (Vercel AI SDK + OpenAI)"),s.hasResend&&e.push("emails/ React Email templates"),e.push("```"),e.push(""),e.push("## Deploy"),e.push(""),e.push("Deploy to production with Mistflow:"),e.push(""),e.push("```"),e.push("# In your AI editor (Claude Code, Cursor, etc.):"),e.push("mist_deploy action='deploy'"),e.push("```"),e.push(""),e.push("Your app will be live at `https://<app-name>.mistflow.app`."),e.push(""),t?.design&&(e.push("## Design"),e.push(""),t.design.tone&&e.push(`- **Tone**: ${t.design.tone}`),t.design.fonts&&(e.push(`- **Heading font**: ${t.design.fonts.heading}`),e.push(`- **Body font**: ${t.design.fonts.body}`)),t.design.accentColor&&e.push(`- **Accent color**: ${t.design.accentColor}`),t.design.borderRadius&&e.push(`- **Border radius**: ${t.design.borderRadius}`),e.push("")),e.push("---"),e.push(""),e.push("Built with [Mistflow](https://mistflow.ai)"),e.push(""),e.join(`
|
|
197
|
-
`)}async function js(n,t){let{name:s,plan:e,path:i,planId:l}=n;if(!i)return p("mist_build init requires an explicit 'path' \u2014 the absolute directory where the project should be scaffolded. Pass the user's project directory (e.g. /Users/alice/projects/my-app). Do not rely on a default.",!0);if(!Ts(i))return p(`mist_build init 'path' must be an absolute path \u2014 received '${i}'. Pass the full absolute path to the target directory.`,!0);let o=At(i),r=e?.design,a=e?.appStyle,c=e?nt(e,"stripe","payment","billing","subscription","checkout","pricing"):!1,u=!0,m=e?nt(e,"upload","file storage","image upload","profile picture","attachment","gallery","media","blob"):!1,d=e?nt(e,"admin panel","admin dashboard","admin management"):!1,b=e?nt(e,"ai integration","openai","llm","ai chat","chatbot","gpt"):!1,P=e,g=!0;if(!Ns(o))return p(`A project already exists at this location (${o}). Choose a different name, or delete the existing folder first. (A .mistflow/ folder left over from planning is fine \u2014 init will preserve it.)`,!0);rt(o,{recursive:!0});try{let h=j(Fe(o),".mistflow","mockups");if(he(h)){let k=Cs(h).filter(C=>C.endsWith(".html"));if(k.length>0){let C=j(o,".mistflow","mockups");rt(C,{recursive:!0});for(let I of k)Gn(j(h,I),j(C,I));console.error(`Copied ${k.length} mockup file(s) into project`)}}}catch(h){console.error("Could not copy mockup files:",h instanceof Error?h.message:h)}let f=null;try{f=await et("nextjs")}catch(h){console.error("Could not fetch scaffold from API, using minimal scaffold:",h instanceof Error?h.message:h)}if(f){let h=s.toLowerCase().replace(/[^a-z0-9-]/g,"-");for(let w of f.files){if(w.path==="package.json"||w.path==="middleware.ts"||w.path==="components/sidebar.tsx"||w.path==="components/topnav.tsx"||w.path==="app/(dashboard)/layout.tsx"||w.path==="app/(dashboard)/page.tsx"||w.path==="app/(dashboard)/dashboard/page.tsx"||!c&&(w.path.includes("stripe")||w.path.includes("webhook/stripe"))||!u&&(w.path.includes("resend")||w.path.includes("emails/"))||!d&&(w.path.includes("(admin)")||w.path.includes("admin-sidebar"))||g&&(w.path==="lib/db.ts"||w.path==="lib/auth.ts"||w.path==="drizzle.config.ts"||w.path==="db/schema/auth.ts"))continue;let E=w.content.replace(/\{\{APP_NAME\}\}/g,s).replace(/\{\{WORKER_NAME\}\}/g,h);if(g&&w.path==="next.config.ts"&&(E=E.replace(/serverExternalPackages:\s*\[[^\]]*\],?/g,'serverExternalPackages: ["@electric-sql/pglite"],')),w.path==="next.config.ts"){let L=Xn(o);L&&(console.error(`[init] Project is inside monorepo at ${L} \u2014 adding outputFileTracingRoot`),E.includes("outputFileTracingRoot")||(E=E.replace('import type { NextConfig } from "next";',`import type { NextConfig } from "next";
|
|
198
|
-
import { dirname } from "path";
|
|
199
|
-
import { fileURLToPath } from "url";
|
|
200
|
-
|
|
201
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));`),E=E.replace("images: {",`outputFileTracingRoot: __dirname,
|
|
202
|
-
images: {`)))}!d&&w.path.includes("sidebar")&&(E=E.replace(/\{user\.role === "admin"[\s\S]*?<\/Link>\s*\)\}/m,""),E=E.replace(/, Shield/g,"")),x(o,w.path,E)}let k={...f.dependencies},C={...f.devDependencies};if(k["drizzle-zod"]||(k["drizzle-zod"]="^0.5.1"),g&&(delete k["@libsql/client"],k["@neondatabase/serverless"]="^0.10.0",C["@electric-sql/pglite"]="^0.2.0"),c&&(k.stripe="^17.0.0"),u&&(k.resend="^4.0.0",k["@react-email/components"]="^0.0.31"),b&&(k.ai="^4.0.0",k["@ai-sdk/openai"]="^1.0.0",k.openai="^4.0.0"),x(o,"package.json",JSON.stringify({name:s,version:"0.1.0",private:!0,scripts:{dev:"next dev",build:"next build",start:"next start",lint:"next lint","db:push":"drizzle-kit push","db:studio":"drizzle-kit studio"},dependencies:k,devDependencies:C,optionalDependencies:{"@noble/ciphers":"^1.3.0"},overrides:{react:"19.1.0","react-dom":"19.1.0",punycode:"^2.3.1"}},null,2)),f.methodology){let w=f.methodology;g&&(w=w.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres")),w=Rt(w),x(o,"AGENTS.md",w),x(o,"CLAUDE.md",w)}let $=e?.designMd;$&&x(o,"DESIGN.md",$),g&&(x(o,"lib/db.ts",['import { neon } from "@neondatabase/serverless";','import { drizzle as drizzleNeon } from "drizzle-orm/neon-http";',"","// eslint-disable-next-line @typescript-eslint/no-explicit-any","let _db: any = null;","","function getDb() {"," if (!_db) {",' if (process.env.DATABASE_URL && process.env.DATABASE_URL !== "pglite") {'," // Production / remote Postgres"," const sql = neon(process.env.DATABASE_URL);"," _db = drizzleNeon(sql);"," } else {"," // Local dev \u2014 PGlite (zero-install embedded Postgres). Lives in"," // lib/db-local.ts and is loaded through a runtime-only require"," // whose path is built from a variable, so esbuild's static"," // analysis can't follow it. Keeps pglite + its 30MB WASM out of"," // the Worker bundle.",' const localPath: string = "./" + "db-local";'," // eslint-disable-next-line @typescript-eslint/no-require-imports"," const { createLocalDb } = require(localPath);"," _db = createLocalDb();"," }"," }"," return _db;","}","","// Lazy proxy \u2014 DB isn't initialized at import/build time","// eslint-disable-next-line @typescript-eslint/no-explicit-any","export const db: any = new Proxy({} as any, {"," get(_target, prop, receiver) {"," const realDb = getDb();"," const value = Reflect.get(realDb, prop, receiver);",' if (typeof value === "function") {'," return value.bind(realDb);"," }"," return value;"," },","});",""].join(`
|
|
203
|
-
`)),x(o,"lib/db-local.ts",["// Local-dev-only DB factory. Isolated from lib/db.ts so the production","// Cloudflare Worker bundle never loads drizzle-orm/pglite or its","// transitive 30MB WASM binary. Loaded via runtime-only require() from","// db.ts, where the path is built from a variable to defeat static analysis.","",'const pglitePkg: string = ["@electric-sql", "pglite"].join("/");',"// eslint-disable-next-line @typescript-eslint/no-require-imports","const { PGlite } = require(pglitePkg);","",'const drizzlePath: string = "drizzle-orm/" + "pglite";',"// eslint-disable-next-line @typescript-eslint/no-require-imports","const { drizzle } = require(drizzlePath);","","// eslint-disable-next-line @typescript-eslint/no-explicit-any","export function createLocalDb(): any {",' const client = new PGlite("./local.pg");'," return drizzle(client);","}",""].join(`
|
|
204
|
-
`)),x(o,"drizzle.config.ts",['import { defineConfig } from "drizzle-kit";',"","// PGlite for local dev (no Postgres install needed), Mistflow Cloud for production",'const isPglite = !process.env.DATABASE_URL || process.env.DATABASE_URL === "pglite";',"","export default defineConfig({",' schema: "./db/schema",',' out: "./db/migrations",',' dialect: "postgresql",',' ...(isPglite ? { driver: "pglite", dbCredentials: { url: "./local.pg" } } : { dbCredentials: { url: process.env.DATABASE_URL! } }),',"});",""].join(`
|
|
205
|
-
`)),x(o,"db/schema/auth.ts",['import { pgTable, text, boolean, timestamp } from "drizzle-orm/pg-core";',"",'export const user = pgTable("user", {',' id: text("id").primaryKey(),',' name: text("name").notNull(),',' email: text("email").notNull().unique(),',' emailVerified: boolean("email_verified").notNull().default(false),',' image: text("image"),',' role: text("role").default("user"),',' banned: boolean("banned").default(false),',' banReason: text("ban_reason"),',' banExpires: timestamp("ban_expires"),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',"});","",'export const session = pgTable("session", {',' id: text("id").primaryKey(),',' expiresAt: timestamp("expires_at").notNull(),',' token: text("token").notNull().unique(),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',' ipAddress: text("ip_address"),',' userAgent: text("user_agent"),',' userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),',' impersonatedBy: text("impersonated_by"),',"});","",'export const account = pgTable("account", {',' id: text("id").primaryKey(),',' accountId: text("account_id").notNull(),',' providerId: text("provider_id").notNull(),',' userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),',' accessToken: text("access_token"),',' refreshToken: text("refresh_token"),',' idToken: text("id_token"),',' accessTokenExpiresAt: timestamp("access_token_expires_at"),',' refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),',' scope: text("scope"),',' password: text("password"),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',"});","",'export const verification = pgTable("verification", {',' id: text("id").primaryKey(),',' identifier: text("identifier").notNull(),',' value: text("value").notNull(),',' expiresAt: timestamp("expires_at").notNull(),',' createdAt: timestamp("created_at"),',' updatedAt: timestamp("updated_at"),',"});",""].join(`
|
|
206
|
-
`)),x(o,"lib/auth.ts",['import { betterAuth } from "better-auth";','import { drizzleAdapter } from "better-auth/adapters/drizzle";','import { admin } from "better-auth/plugins/admin";','import { nextCookies } from "better-auth/next-js";','import { db } from "./db";','import * as schema from "@/db";',"","async function sendEmail({ to, subject, html, fallbackUrl }: { to: string; subject: string; html: string; fallbackUrl?: string }) {"," const apiKey = process.env.RESEND_API_KEY;"," if (!apiKey) {"," if (fallbackUrl) {"," console.error(`\\n[auth] No RESEND_API_KEY set. Email to ${to} was not sent.`);"," console.error(`[auth] Dev fallback \u2014 use this link directly:\\n ${fallbackUrl}\\n`);"," } else {"," console.error(`[auth] RESEND_API_KEY not set \u2014 skipping email send to ${to}`);"," }"," return;"," }",' const from = process.env.EMAIL_FROM || "noreply@mail.mistflow.app";',' const res = await fetch("https://api.resend.com/emails", {',' method: "POST",',' headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },'," body: JSON.stringify({ from, to, subject, html }),"," });"," if (!res.ok) {",' const body = await res.text().catch(() => "unknown");'," console.error(`[auth] Email send failed (${res.status}): ${body}`);"," throw new Error(`Email send failed: ${res.status}`);"," }","}","","function createAuth() {",' const baseURL = process.env.BETTER_AUTH_URL || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";',' const isLocal = baseURL.includes("localhost") || baseURL.includes("127.0.0.1");'," const canSendEmail = Boolean(process.env.RESEND_API_KEY);"," // Refuse to boot a production app with email auth but no mail sender. Mistflow's"," // deploy pipeline injects a managed Resend key automatically; if it's missing,"," // that's a real misconfig and silent fallbacks let unverified users sign up."," if (!isLocal && !canSendEmail) {",` throw new Error("[auth] RESEND_API_KEY is required in production. The Mistflow deploy pipeline injects one automatically \u2014 if you're seeing this, check the project's env vars in the dashboard, or set your own RESEND_API_KEY.");`," }"," return betterAuth({"," baseURL,"," trustedOrigins: [baseURL],",' database: drizzleAdapter(db, { provider: "pg", schema }),'," emailAndPassword: {"," enabled: true,"," requireEmailVerification: !isLocal && canSendEmail,"," sendResetPassword: async ({ user, token }: { user: { email: string; name: string }; url: string; token: string }) => {"," // Better Auth's default reset URL points at /reset-password/:token (path),"," // which our frontend doesn't route. We build our own pointing at our"," // /reset-password?token=xxx page which reads the token from the query."," const resetUrl = `${baseURL}/reset-password?token=${token}`;"," await sendEmail({"," to: user.email,",' subject: "Reset your password",',' html: `<p>Hi ${user.name},</p><p>Click the link below to reset your password:</p><p><a href="${resetUrl}">${resetUrl}</a></p>`,'," fallbackUrl: isLocal ? resetUrl : undefined,"," });"," },"," },"," emailVerification: {"," sendOnSignUp: canSendEmail,"," autoSignInAfterVerification: true,"," sendVerificationEmail: async ({ user, url }: { user: { email: string; name: string }; url: string }) => {"," await sendEmail({"," to: user.email,",' subject: "Verify your email address",',' html: `<p>Hi ${user.name},</p><p>Click the link below to verify your email:</p><p><a href="${url}">${url}</a></p>`,'," fallbackUrl: isLocal ? url : undefined,"," });"," },"," },"," secret: process.env.AUTH_SECRET,",' plugins: [admin({ defaultRole: "user" }), nextCookies()],'," databaseHooks: {"," user: {"," create: {"," // Auto-promote the app owner to admin on first signup. ADMIN_EMAIL"," // is injected by the Mistflow deploy pipeline (the email of the"," // account that ran mist_deploy). Email verification still gates"," // login when Resend is configured, so a collision attempt can't"," // actually sign in without clicking a link delivered to the"," // owner's inbox."," before: async (user: { email?: string; [k: string]: unknown }) => {"," const adminEmail = process.env.ADMIN_EMAIL;"," if (adminEmail && user.email?.toLowerCase() === adminEmail.toLowerCase()) {",' return { data: { ...user, role: "admin" } };'," }"," return { data: user };"," },"," },"," },"," },"," socialProviders: {"," ...(process.env.GOOGLE_CLIENT_ID ? {"," google: {"," clientId: process.env.GOOGLE_CLIENT_ID,"," clientSecret: process.env.GOOGLE_CLIENT_SECRET!,"," },"," } : {}),"," ...(process.env.GITHUB_CLIENT_ID ? {"," github: {"," clientId: process.env.GITHUB_CLIENT_ID,"," clientSecret: process.env.GITHUB_CLIENT_SECRET!,"," },"," } : {}),"," },"," });","}","","// Lazy init \u2014 process.env isn't populated at module scope on Cloudflare Workers.","// The `has` trap is required: better-auth's toNextJsHandler does",'// `"handler" in auth ? auth.handler(request) : auth(request)` \u2014 without a `has`',"// trap the default forwards to the empty target object, returns false, and the","// handler tries to call the Proxy as a function, which throws TypeError and","// returns 500 on every /api/auth/* request.","let _auth: ReturnType<typeof createAuth> | null = null;","export const auth = new Proxy({} as ReturnType<typeof createAuth>, {"," get(_target, prop, receiver) {"," if (!_auth) _auth = createAuth();"," const value = Reflect.get(_auth, prop, receiver);",' if (typeof value === "function") return value.bind(_auth);'," return value;"," },"," has(_target, prop) {"," if (!_auth) _auth = createAuth();"," return prop in _auth;"," },","});",""].join(`
|
|
207
|
-
`)))}else x(o,"package.json",JSON.stringify({name:s,version:"0.1.0",private:!0},null,2));let S=e?.designMd;x(o,"app/globals.css",nr(r,a,S)),x(o,"app/layout.tsx",ir(s,r,P?.language)),x(o,"README.md",yr(s,e,{hasStripe:c,hasResend:u,hasStorage:m,hasAdmin:d,hasAI:b,isNeon:g})),x(o,"contracts/README.md",Pt());let B=e?.dataModel??[],W=new Set,K=0;for(let h of B){let k=h.entity??h.name;if(!k||typeof k!="string")continue;let C=Ct(k);W.has(C)||(W.add(C),x(o,C,It(k)),K++)}K===0&&x(o,"contracts/.gitkeep","");let M=[],Q=e?.publicPages;if(Array.isArray(Q))M=Q;else if(typeof Q=="string"){try{M=JSON.parse(Q)}catch{M=[]}Array.isArray(M)||(M=[])}if(!M.includes("/")){let h=e?.steps?.some(C=>{let I=((C.name??"")+" "+(C.description??"")).toLowerCase();return I.includes("landing")||I.includes("marketing")||I.includes("homepage")}),k=e?.pages?.some(C=>C.path==="/");(h||k)&&(M=["/",...M])}let N={name:s,summary:e?.summary,authModel:e?.authModel,roles:e?.roles,defaultRole:e?.defaultRole,publicPages:M,navStyle:e?.navStyle,multiTenant:e?.multiTenant,pages:e?.pages,dataModel:e?.dataModel,design:e?.design},Te=lr(N);Te&&x(o,"middleware.ts",Te);let ne=cr(N);ne&&x(o,ne.path,ne.content);let re=pr(N);if(re&&x(o,"lib/roles.ts",re),x(o,"app/page.tsx",ur(N)),x(o,"app/(dashboard)/layout.tsx",dr(N,d)),x(o,"app/(dashboard)/dashboard/page.tsx",mr(N)),N.multiTenant){let h=fr(N,g);h&&x(o,"db/schema/organization.ts",h);let k=gr(N);k&&x(o,"lib/org.ts",k);let C=hr(N);C&&x(o,"components/org-switcher.tsx",C)}x(o,"tsconfig.json",JSON.stringify({compilerOptions:{target:"ES2017",lib:["dom","dom.iterable","esnext"],allowJs:!0,skipLibCheck:!0,strict:!1,noEmit:!0,esModuleInterop:!0,module:"esnext",moduleResolution:"bundler",resolveJsonModule:!0,isolatedModules:!0,jsx:"preserve",incremental:!0,plugins:[{name:"next"}],paths:{"@/*":["./*"]}},include:["next-env.d.ts","**/*.ts","**/*.tsx",".next/types/**/*.ts"],exclude:["node_modules"]},null,2)),c&&x(o,"lib/stripe.ts",['import Stripe from "stripe";',"","let _stripe: Stripe | null = null;","","function getStripe(): Stripe {"," if (!_stripe) {"," if (!process.env.STRIPE_SECRET_KEY) {",' throw new Error("STRIPE_SECRET_KEY is not set");'," }"," _stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {"," typescript: true,"," });"," }"," return _stripe;","}","","// Lazy proxy \u2014 Stripe isn't initialized at import/build time","export const stripe = new Proxy({} as Stripe, {"," get(_target, prop) {"," return (getStripe() as unknown as Record<string | symbol, unknown>)[prop];"," },","});",""].join(`
|
|
208
|
-
`)),u&&(x(o,"lib/resend.ts",['import { Resend } from "resend";',"","let _resend: Resend | null = null;","","function getResend(): Resend {"," if (!_resend) {"," if (!process.env.RESEND_API_KEY) {",' throw new Error("RESEND_API_KEY is not set");'," }"," _resend = new Resend(process.env.RESEND_API_KEY);"," }"," return _resend;","}","","// Lazy proxy \u2014 Resend isn't initialized at import/build time","export const resend = new Proxy({} as Resend, {"," get(_target, prop) {"," return (getResend() as unknown as Record<string | symbol, unknown>)[prop];"," },","});",""].join(`
|
|
209
|
-
`)),x(o,"lib/email.ts",['import { resend } from "./resend";',"",'const FROM = process.env.EMAIL_FROM ?? "onboarding@resend.dev";',"","export async function sendEmail({"," to,"," subject,"," react,","}: {"," to: string;"," subject: string;"," react: React.ReactElement;","}) {"," return resend.emails.send({ from: FROM, to, subject, react });","}",""].join(`
|
|
210
|
-
`))),m&&(x(o,"lib/storage.ts",['const MISTFLOW_API = process.env.MISTFLOW_API_URL ?? "https://api.mistflow.ai";',"const MISTFLOW_API_KEY = process.env.MISTFLOW_API_KEY;","const PROJECT_ID = process.env.MISTFLOW_PROJECT_ID;","","interface UploadResult {"," upload_url: string;"," download_url: string;"," key: string;","}","","function authHeaders(): Record<string, string> {"," return {",' "Content-Type": "application/json",',' "Authorization": `ApiKey ${MISTFLOW_API_KEY}`,'," };","}","",'export async function getUploadUrl(filename: string, contentType: string = "application/octet-stream"): Promise<UploadResult> {'," const res = await fetch(`${MISTFLOW_API}/api/storage/upload-url`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename, content_type: contentType }),"," });"," if (!res.ok) throw new Error(`Storage error: ${res.status}`);"," return res.json();","}","","export async function getDownloadUrl(filename: string): Promise<string> {"," const res = await fetch(`${MISTFLOW_API}/api/storage/download-url`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename }),"," });"," if (!res.ok) throw new Error(`Storage error: ${res.status}`);"," const data = await res.json();"," return data.download_url;","}","","export async function deleteFile(filename: string): Promise<void> {"," await fetch(`${MISTFLOW_API}/api/storage/delete`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename }),"," });","}","","export async function uploadFile(file: File): Promise<string> {"," const { upload_url, download_url } = await getUploadUrl(file.name, file.type);",' await fetch(upload_url, { method: "PUT", body: file, headers: { "Content-Type": file.type } });'," return download_url;","}",""].join(`
|
|
211
|
-
`)),x(o,"app/api/upload/route.ts",['import { NextRequest, NextResponse } from "next/server";','import { getUploadUrl } from "@/lib/storage";',"","export async function POST(req: NextRequest) {"," const { filename, contentType } = await req.json();"," if (!filename) {",' return NextResponse.json({ error: "filename is required" }, { status: 400 });'," }"," try {",' const result = await getUploadUrl(filename, contentType ?? "application/octet-stream");'," return NextResponse.json(result);"," } catch {",' return NextResponse.json({ error: "Failed to get upload URL" }, { status: 500 });'," }","}",""].join(`
|
|
212
|
-
`))),b&&(x(o,"lib/ai.ts",['import { createOpenAI } from "@ai-sdk/openai";',"","export const openai = createOpenAI({"," apiKey: process.env.OPENAI_API_KEY,","});",""].join(`
|
|
213
|
-
`)),x(o,"app/api/chat/route.ts",['import { openai } from "@/lib/ai";','import { streamText } from "ai";',"","export async function POST(req: Request) {"," const { messages } = await req.json();",""," const result = streamText({",' model: openai("gpt-4o"),'," messages,"," });",""," return result.toDataStreamResponse();","}",""].join(`
|
|
214
|
-
`)));let Je={name:s,methodologyVersion:f?.version??"1.0",createdAt:new Date().toISOString(),...l?{planId:l}:{},plan:Array.isArray(e?.steps)?{...e,steps:e.steps.map(h=>({number:h.number,name:h.name??h.title,description:h.description,entities:h.entities,pages:h.pages,features:h.features,status:"pending"}))}:e,dbProvider:"neon",env:{managed:{DATABASE_URL:{description:"Postgres connection URL",scope:"production"},AUTH_SECRET:{description:"Auth encryption secret",scope:"production"},...c?{STRIPE_SECRET_KEY:{description:"Stripe secret key",scope:"production"},STRIPE_WEBHOOK_SECRET:{description:"Stripe webhook signing secret",scope:"production"},NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:{description:"Stripe publishable key",scope:"production"}}:{},...u?{RESEND_API_KEY:{description:"Resend API key \u2014 managed by Mistflow by default, override with your own key from resend.com",scope:"production"},EMAIL_FROM:{description:"Sender email address \u2014 managed by Mistflow by default",scope:"production"}}:{},...m?{MISTFLOW_API_KEY:{description:"Mistflow API key for file storage",scope:"production"},MISTFLOW_PROJECT_ID:{description:"Mistflow project ID",scope:"production"}}:{}},...b?{required:{OPENAI_API_KEY:{description:"OpenAI API key",setupUrl:"https://platform.openai.com/api-keys"}}}:{}},authModel:e?.authModel??"email",roles:e?.roles??null,navStyle:e?.navStyle??"sidebar",multiTenant:e?.multiTenant??!1,hasAdmin:d,hasResend:u,hasStorage:m,hasAI:b,deploy:null};x(o,"mistflow.json",JSON.stringify(Je,null,2)),As(o,e);let ue=Qn(32).toString("hex"),de=c?`
|
|
215
|
-
# Stripe
|
|
216
|
-
STRIPE_SECRET_KEY=
|
|
217
|
-
STRIPE_WEBHOOK_SECRET=
|
|
218
|
-
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
|
|
219
|
-
`:"",q=u?`
|
|
220
|
-
# Email (Resend)
|
|
221
|
-
RESEND_API_KEY=
|
|
222
|
-
EMAIL_FROM=onboarding@resend.dev
|
|
223
|
-
`:"",X=m?`
|
|
224
|
-
# File Storage (Mistflow managed)
|
|
225
|
-
MISTFLOW_API_KEY=
|
|
226
|
-
MISTFLOW_PROJECT_ID=
|
|
227
|
-
`:"",ke=b?`
|
|
228
|
-
# AI (get your key at https://platform.openai.com/api-keys)
|
|
229
|
-
OPENAI_API_KEY=
|
|
230
|
-
`:"",Ee=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
|
|
231
|
-
# Set DATABASE_URL only for production or to use a remote Postgres
|
|
232
|
-
# DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`,Se=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
|
|
233
|
-
# Set DATABASE_URL only for production or to use a remote Postgres
|
|
234
|
-
# DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`;x(o,".env.local",`${Ee}
|
|
235
|
-
AUTH_SECRET=${ue}
|
|
236
|
-
${de}${q}${X}${ke}`),x(o,".env.example",`${Se}
|
|
237
|
-
AUTH_SECRET=your-secret-here
|
|
238
|
-
${de}${q}${X}${ke}`);let D=[],oe=(h,k)=>{D.push({phase:h,message:k})},y=(h,k)=>{let C=D.find(I=>I.phase===h&&!I.durationMs);C&&(C.durationMs=k)};if(t){let h=tt(t.server,t.progressToken,()=>D[D.length-1]?.message??"Setting up project...");t.cleanup=()=>h.stop()}let R=P?.requestedSubdomain||void 0,_,v;oe("register","Registering project on Mistflow...");let U=Date.now();try{let h=await es(s,void 0,"neon",R);_=h.id;let k=j(o,"mistflow.json"),C=JSON.parse(ot(k,"utf-8"));if(C.projectId=_,ge(k,JSON.stringify(C,null,2)),kt(o,St(_,s)),h.managed_env&&Object.keys(h.managed_env).length>0){let I=j(o,".env.local"),le=he(I)?ot(I,"utf-8"):"";for(let[$,w]of Object.entries(h.managed_env)){let E=new RegExp(`^${$}=.*$`,"m");E.test(le)?le=le.replace(E,`${$}=${w}`):le+=`
|
|
239
|
-
${$}=${w}`}ge(I,le)}try{let{getBaseUrl:I,getAuthHeaders:le}=await import("./api-client-7L7LNS2X.js"),$=le(),w=e?.features,E=e?.steps,L={};Array.isArray(w)&&w.length>0&&(L.features=w.map(Ae=>Ae.name)),e&&(L.plan=e),Array.isArray(E)&&E.length>0&&(L.provenance=E.map(Ae=>({feature:Ae.name??Ae.title??`Step ${Ae.number??"?"}`,user_intent:(Ae.description??"").slice(0,500),decisions:"Seeded from plan at init",tradeoffs:"",files_affected:[]}))),Object.keys(L).length>0&&await fetch(`${I()}/api/projects/${encodeURIComponent(_)}/state`,{method:"PUT",headers:{...$,"Content-Type":"application/json"},body:JSON.stringify(L)})}catch{}D[D.length-1].message=`Registered as ${_.slice(0,8)}`}catch(h){let k=h instanceof Error?h.message:String(h);console.error("Could not register project on backend:",k),v=`Project created locally but NOT registered on Mistflow servers (${k}). Deploy will auto-register it.`,D[D.length-1].message="Registration skipped (offline \u2014 deploy will retry)"}y("register",Date.now()-U),oe("git","Initializing git repository...");let ie=Date.now();try{let h=Es(o);await h.init(),await h.add("."),await h.commit("Initial Mistflow project setup"),D[D.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),D[D.length-1].message="Git init skipped"}y("git",Date.now()-ie);let Ge=D.reduce((h,k)=>h+(k.durationMs??0),0),te={projectPath:o,projectId:_,status:"awaiting_install"},Ye=D.map(h=>{let k=h.durationMs?` (${(h.durationMs/1e3).toFixed(1)}s)`:"";return`${h.message}${k}`});te.progress=Ye,te.totalSetupTime=`${(Ge/1e3).toFixed(1)}s`;let ae=[];_||ae.push("Project was not registered with Mistflow (not signed in). Run mist_setup to sign in BEFORE deploying \u2014 deploy will fail without it."),v&&(te.registrationWarning=v),ae.length>0&&(te.warnings=ae);let xe=`NEXT: Run \`npx -y @mistflow-ai/cli install ${o}\` via your shell/bash tool \u2014 NOT via the mist_build MCP tool. The CLI streams npm output live and has no MCP 60s timeout; the MCP install action can hit -32001 timeout on slow installs. Do NOT ask the user for permission \u2014 install is a required follow-up to init, not a decision point. If your environment can't run the CLI, fall back to mist_build action='install' projectPath='${o}'. After install finishes, call mist_build action='implement' to build the first plan step \u2014 also without asking.`;return te.nextAction=_?xe:`${xe} IMPORTANT: You MUST also run mist_setup to sign in before deploying \u2014 the project could not be registered because auth is missing.`,p(JSON.stringify(te))}async function Ds(n){let{name:t,plan:s,projectId:e,sourceDeploymentId:i,forkToken:l,requiredEnvVars:o,dbProvider:r,planId:a}=n;if(!n.path)return p("mist_build init requires an explicit 'path' \u2014 the absolute directory where the forked project should be scaffolded. Pass the user's project directory (e.g. /Users/alice/projects/my-app). Do not rely on a default.",!0);if(!Ts(n.path))return p(`mist_build init 'path' must be an absolute path \u2014 received '${n.path}'. Pass the full absolute path to the target directory.`,!0);let c=At(n.path);if(!Ns(c))return p(`A project already exists at this location (${c}). Choose a different name, or delete the existing folder first. (A .mistflow/ folder left over from planning is fine \u2014 init will preserve it.)`,!0);let{mkdtempSync:u,renameSync:m,rmSync:d,cpSync:b}=await import("fs"),{tmpdir:P}=await import("os"),g=u(j(P(),"mistflow-fork-")),f=j(g,"project");rt(f,{recursive:!0});let S=[],B=(K,M)=>S.push({phase:K,message:M}),W=(K,M)=>{let Q=S.find(N=>N.phase===K);Q&&(Q.durationMs=M)};try{B("download","Downloading source code from template...");let K=Date.now(),M=j(g,"source.tar.gz");try{await fs(i,l,M)}catch(v){d(g,{recursive:!0,force:!0});let U=v instanceof Error?v.message:"Source download failed";return p(`Source code download failed: ${U}. You can still build from the plan \u2014 run mist_build init without the source (omit planId and pass the plan directly).`,!0)}W("download",Date.now()-K),S[S.length-1].message="Source code downloaded",B("extract","Extracting source code...");let Q=Date.now(),N=await Tt("tar",["-xzf",M,"-C",f,"--exclude","node_modules","--exclude",".git"],f,6e4);if(!N.success)return d(g,{recursive:!0,force:!0}),p(`Failed to extract source archive: ${N.error}`,!0);if(W("extract",Date.now()-Q),!he(j(f,"package.json")))return d(g,{recursive:!0,force:!0}),p("Source archive does not contain a package.json. The template may be corrupted.",!0);let Te=[".mistflow",".env.local",".env.example","local.db","local.pg"];for(let v of Te){let U=j(f,v);he(U)&&d(U,{recursive:!0,force:!0})}let ne={name:t,projectId:e,template:"nextjs",createdAt:new Date().toISOString(),planId:a??void 0,plan:s,dbProvider:r};ge(j(f,"mistflow.json"),JSON.stringify(ne,null,2)),As(f,s);let re=o.map(v=>{let U=v.description?`# ${v.description}`:`# ${v.key}`,ie=v.setup_url?` (${v.setup_url})`:"";return`${U}${ie}
|
|
240
|
-
${v.key}=`});ge(j(f,".env.local"),re.join(`
|
|
241
|
-
|
|
242
|
-
`)+`
|
|
243
|
-
`);let Je=o.map(v=>`${v.key}=`);ge(j(f,".env.example"),Je.join(`
|
|
244
|
-
`)+`
|
|
245
|
-
`),ge(j(f,"README.md"),`# ${t}
|
|
246
|
-
|
|
247
|
-
Forked from a Mistflow template. Built with Next.js, Drizzle ORM, and Better Auth.
|
|
248
|
-
|
|
249
|
-
## Getting Started
|
|
250
|
-
|
|
251
|
-
1. Copy \`.env.example\` to \`.env.local\` and fill in your values
|
|
252
|
-
2. Run \`npm run dev\`
|
|
253
|
-
3. Deploy with \`mist_deploy\`
|
|
254
|
-
`),B("setup","Setting up project directory...");let ue=Date.now();try{m(f,c)}catch(v){if(v.code==="EXDEV")b(f,c,{recursive:!0}),d(f,{recursive:!0,force:!0});else throw v}W("setup",Date.now()-ue),B("install","Installing packages...");let de=Date.now(),q=0,X=await Tt("npm",["install"],c,12e4,v=>{let U=v.match(/added (\d+) packages/);U&&(q=parseInt(U[1],10))});if(X.success||(console.error("[initFromSource] npm install failed, retrying..."),X=await Tt("npm",["install"],c,12e4,v=>{let U=v.match(/added (\d+) packages/);U&&(q=parseInt(U[1],10))})),!X.success)return p(`Source code was restored at ${c} but package installation failed: ${X.error}. Run "npm install" manually.`,!0);W("install",Date.now()-de),S[S.length-1].message=`Installed ${q||"all"} packages`,B("git","Initializing git repository...");let ke=Date.now();try{let v=Es(c);await v.init(),await v.add("."),await v.commit("Forked from Mistflow template"),S[S.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),S[S.length-1].message="Git init skipped"}W("git",Date.now()-ke);try{let{markLocalSetupDone:v}=await import("./api-client-7L7LNS2X.js");await v(e)}catch{console.error("[initFromSource] Could not mark local_setup_done on backend, continuing.")}let Ee=JSON.parse(ot(j(c,"mistflow.json"),"utf-8"));Ee.projectId=e,ge(j(c,"mistflow.json"),JSON.stringify(Ee,null,2)),kt(c,St(e,t));let Se=s.steps,D=Se?.filter(v=>v.status==="completed").length??0,oe=Se?.filter(v=>v.status==="pending").length??0,y=Se?.length??0,R=o.length>0?`
|
|
255
|
-
|
|
256
|
-
Environment variables needed:
|
|
257
|
-
`+o.map(v=>` \u2022 ${v.key}${v.description?` \u2014 ${v.description}`:""}`).join(`
|
|
258
|
-
`):"",_=oe>0?`Source code restored with ${D}/${y} steps complete. ${oe} steps need implementation \u2014 call mist_build with action='implement' to apply your changes.`:`Source code fully restored (${y} steps complete). Configure your .env.local, then deploy with mist_deploy.`;return p(JSON.stringify({status:"success",projectPath:c,projectId:e,planStepsCompleted:D,planStepsTotal:y,pendingSteps:oe,progress:S,nextAction:_+R}))}finally{try{d(g,{recursive:!0,force:!0})}catch{}}}var Nt={},De=[];function it(n){let t=De.find(e=>e.id===n);if(t)return t;let s=n.toLowerCase().replace(/[^a-z0-9]/g,"");return De.find(e=>{let i=e.title.toLowerCase().replace(/[^a-z0-9]/g,"");return i===s||i.includes(s)||s.includes(i)})}function Ms(n){return Nt[n]}function jt(n){return n?De.filter(t=>t.category.toLowerCase()===n.toLowerCase()):De}function $s(n){let t=n??De;if(t.length===0)return"Landing page presets have been replaced by the tone-based system. The landing page tone is now auto-selected based on your app's description during planning.";let s={};for(let i of t){s[i.category]||(s[i.category]=[]);let l=Nt[i.id],o=l?` \u2014 ${l.description}`:"";s[i.category].push(`${i.id} \u2014 "${i.title}"${o}`)}let e=[];for(let[i,l]of Object.entries(s))e.push(`**${i}**:
|
|
10
|
+
If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}import{z as L}from"zod";import{platform as ye}from"os";import{execFile as J}from"child_process";var we=L.object({apiKey:L.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:L.string().optional().describe("Resume polling for a pending device code. Returned by a previous mist_setup call with status 'pending'. Call mist_setup again with this value after ~15 seconds to check if the user approved.")});function ve(a){return"error"in a}function be(a){return new Promise(e=>setTimeout(e,a))}function ke(a){return new Promise(e=>{let t=ye();t==="win32"?J("cmd.exe",["/c","start","",a],r=>{r&&console.error("Could not open browser:",r.message),e(!r)}):J(t==="darwin"?"open":"xdg-open",[a],s=>{s&&console.error("Could not open browser:",s.message),e(!s)}),setTimeout(()=>e(!1),5e3)})}var Se={fetch:globalThis.fetch,openBrowser:ke};async function X(a,e,t,r){let s=t;for(let l=0;l<e;l++){await be(s);let o;try{let c=await r.fetch(`${R()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a})});if(!c.ok)continue;o=await c.json()}catch{continue}if(ve(o))switch(o.error){case"authorization_pending":continue;case"slow_down":s+=5e3;continue;case"expired_token":return i("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return i("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return i("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}let n=o.email||o.org_name||o.org_slug;return U({apiKey:o.api_key,apiKeyId:o.api_key_id,apiKeyName:o.api_key_name,orgId:o.org_id,orgSlug:o.org_slug,email:o.email}),i(`Connected to Mistflow as ${n}. You are ready to build and deploy.`)}return null}async function Pe(a,e=Se){let t=a;if(t?.apiKey)try{let o=await e.fetch(`${R()}/api/org`,{headers:{Authorization:`ApiKey ${t.apiKey}`}});if(!o.ok)return i("Invalid API key. Check the key and try again.",!0);let n=await o.json();return U({apiKey:t.apiKey,orgId:n.id,orgSlug:n.slug}),i(`Connected to Mistflow as ${n.slug} via API key. You are ready to build and deploy.`)}catch{return i("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(t?.deviceCode){let o=await X(t.deviceCode,6,5e3,e);return o||i(JSON.stringify({status:"pending",deviceCode:t.deviceCode,instruction:"The user hasn't approved yet. Wait ~15 seconds and call mist_setup again with the same deviceCode."}))}let r;try{let o=await e.fetch(`${R()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!o.ok)return i("Cannot reach Mistflow servers. Check your internet connection.",!0);r=await o.json()}catch{return i("Cannot reach Mistflow servers. Check your internet connection.",!0)}let s=`${r.verification_uri}?code=${r.user_code}`;console.error(`
|
|
11
|
+
Sign in at: ${s}
|
|
12
|
+
Your code: ${r.user_code}
|
|
13
|
+
`);try{await e.openBrowser(s)}catch{}let l=await X(r.device_code,6,5e3,e);return l||i(JSON.stringify({status:"pending",deviceCode:r.device_code,signInUrl:s,userCode:r.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+r.device_code+"' to check if they approved."}))}var Q={name:"mist_setup",description:"Connect the user's Mistflow account. Call this ONLY when: (a) the user has literally never signed in, or (b) a previous tool call returned error code 'auth_missing' or 'auth_revoked'. DO NOT call this tool in response to 500 errors, 404 errors, network errors, or any generic failure \u2014 those are backend issues, not auth issues. Running mist_setup when not needed wastes the user's time and creates login fatigue. Once signed in, the MCP persists a long-lived API key and never asks again. Two-phase device code flow: (1) Call without deviceCode \u2014 opens browser, returns status 'pending' with deviceCode and userCode. Tell the user the verification code and that they need to approve in the browser. (2) Call again with deviceCode after ~15 seconds \u2014 polls for approval. Also accepts an apiKey for headless auth (skips device code entirely).",inputSchema:we,handler:a=>Pe(a)};import{z as m}from"zod";import{resolve as _}from"path";import{existsSync as C,readFileSync as T}from"fs";import{join as E}from"path";import{z as f}from"zod";import{resolve as Re,join as ee}from"path";import{existsSync as _e,readFileSync as te,writeFileSync as Ce}from"fs";import{existsSync as xe,readFileSync as Ie}from"fs";function Z(a){let e=new Set;if(!xe(a))return e;let t=Ie(a,"utf-8");for(let r of t.split(`
|
|
14
|
+
`)){let s=r.trim();if(!s||s.startsWith("#"))continue;let l=s.indexOf("=");if(l>0){let o=s.slice(0,l).trim(),n=s.slice(l+1).trim();n&&n!=='""'&&n!=="''"&&e.add(o)}}return e}var Te=f.object({action:f.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:f.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:f.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:f.object({key:f.string(),description:f.string().optional(),setupUrl:f.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),oe={name:"mist_state",description:"Read or update project state in mistflow.json. Use action='get' to load plan progress, env var status, and deploy info. Use action='update' to mark plan steps complete or add required env vars. Use when the user says 'mist status', 'mist state', or 'mist update state'.",inputSchema:Te,handler:async a=>{let e=a,t=Re(e.projectPath??process.cwd()),r=ee(t,"mistflow.json");if(!_e(r))return y(t);let s;try{s=JSON.parse(te(r,"utf-8"))}catch{return i("Failed to parse mistflow.json.",!0)}if(e.action==="get"){if(!s.projectId)try{let{ensureBackendRegistered:d}=await import("./self-heal-QQ53MP7L.js");await d(t)&&(s=JSON.parse(te(r,"utf-8")))}catch{}let n=s.plan,c=n?.steps?.filter(d=>d.status==="completed").length??0,p=n?.steps?.length??0,g=Z(ee(t,".env.local")),h=s.env?.required?Object.entries(s.env.required).map(([d,P])=>({name:d,description:P?.description,configured:g.has(d)})):[];s.projectId&&import("./state-manager-26IE6Y5D.js").then(({fetchRemoteState:d})=>d(s.projectId)).catch(()=>{});let u=[`Project: ${s.name}`];if(n){u.push(`Plan: ${n.summary??n.name??"unnamed"} \u2014 ${c}/${p} steps complete`);for(let d of n.steps){let P=d.status==="completed"?"\u2713":d.status==="in_progress"?"\u2192":" ";u.push(` [${P}] ${d.number}. ${d.name}`)}}let k=h.filter(d=>!d.configured);k.length>0&&u.push(`Missing env vars: ${k.map(d=>d.name).join(", ")}`),s.deploy?.url?u.push(`Deployed: ${s.deploy.url} (${s.deploy.count??0} deploys)`):u.push("Not deployed yet");let S=[],N=n?.steps?.find(d=>d.status!=="completed");return N?S.push(`NEXT: Call mist_build with action='implement' to work on step ${N.number} (${N.name}).`):n&&c===p&&(s.deploy?.url||S.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT ask the user \u2014 just deploy.")),k.length>0&&S.push(`Missing env vars in .env.local: ${k.map(d=>d.name).join(", ")}`),i(JSON.stringify({name:s.name,projectId:s.projectId,planProgress:n?{name:n.name,summary:n.summary,totalSteps:p,completedSteps:c,steps:n.steps}:null,envStatus:h,deploy:s.deploy??null,contextMessage:u.join(`
|
|
15
|
+
`),nextSteps:S}))}let l=[];if(e.completedStep!==void 0){let n=s.plan;if(n?.steps){let c=n.steps.findIndex(p=>p.number===e.completedStep);if(c===-1)return i(`Step ${e.completedStep} not found in the plan.`,!0);n.steps[c].status="completed",l.push(`Step ${e.completedStep} marked as completed`)}}e.addEnvVar&&(s.env||(s.env={required:{}}),s.env.required||(s.env.required={}),s.env.required[e.addEnvVar.key]={description:e.addEnvVar.description,setupUrl:e.addEnvVar.setupUrl},l.push(`Added required env var: ${e.addEnvVar.key}`)),Ce(r,JSON.stringify(s,null,2)+`
|
|
16
|
+
`),s.projectId&&import("./state-manager-26IE6Y5D.js").then(async({readLocalState:n,syncRemoteState:c})=>{let p=n(t);p&&await c(s.projectId,p)}).catch(()=>{});let o=[];if(e.completedStep!==void 0){let c=s.plan?.steps?.find(p=>p.status!=="completed");c?o.push(`NEXT: Call mist_build with action='implement' to work on step ${c.number} (${c.name}). Do this now.`):o.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT suggest localhost.")}return e.addEnvVar&&(o.push(`Add ${e.addEnvVar.key} to your .env.local file`),e.addEnvVar.setupUrl&&o.push(`Get the value from: ${e.addEnvVar.setupUrl}`)),i(JSON.stringify({updated:!0,changes:l,message:l.length>0?`Project state saved. ${l.join(". ")}.`:"No changes made.",nextSteps:o.length>0?o:void 0}))}};var M={},v=[];function se(a){let e=v.find(r=>r.id===a);if(e)return e;let t=a.toLowerCase().replace(/[^a-z0-9]/g,"");return v.find(r=>{let s=r.title.toLowerCase().replace(/[^a-z0-9]/g,"");return s===t||s.includes(t)||t.includes(s)})}function re(a){return M[a]}function D(a){return a?v.filter(e=>e.category.toLowerCase()===a.toLowerCase()):v}function ne(a){let e=a??v;if(e.length===0)return"Landing page presets have been replaced by the tone-based system. The landing page tone is now auto-selected based on your app's description during planning.";let t={};for(let s of e){t[s.category]||(t[s.category]=[]);let l=M[s.id],o=l?` \u2014 ${l.description}`:"";t[s.category].push(`${s.id} \u2014 "${s.title}"${o}`)}let r=[];for(let[s,l]of Object.entries(t))r.push(`**${s}**:
|
|
259
17
|
${l.map(o=>` \u2022 ${o}`).join(`
|
|
260
|
-
`)}`);return
|
|
261
|
-
|
|
262
|
-
`)}function br(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function Us(n){return(n?jt(n):De).map(s=>{let e=Nt[s.id];return{id:s.id,slug:br(s.title),title:s.title,category:s.category,description:e?.description??"",tags:e?.tags??[],theme:e?.theme??"dark",colors:e?.colors??[],style:e?.style??""}})}function xr(n){let t=kr(Sr(),".mistflow","plans",`${n}.json`);if(!wr(t))return null;try{let s=JSON.parse(vr(t,"utf-8"));return s.plan?s:null}catch{return null}}var _r=se.object({action:se.enum(["init"]).describe("'init' creates and sets up a new project from your plan \u2014 writes files, registers the project, initializes git. Returns fast; does NOT run npm install."),name:se.string().optional().describe("(init) Project name"),planId:se.string().optional().describe("(init) Plan ID from mist_plan"),plan:se.any().optional().describe("(init) Full plan object \u2014 use planId instead when available"),path:se.string().optional().describe("(init) REQUIRED for action='init'. Absolute path to the target directory where the project should be scaffolded (e.g. /Users/alice/projects/my-app). Do not omit \u2014 there is no default. If unsure, ask the user for their working directory."),landingDesign:se.string().optional().describe("(init) Landing design ID to apply to the landing page. Can be set here if not set during mist_plan. Use mist_project action='landing-designs' to browse."),appStyle:se.string().optional().describe("(init) Optional aesthetic-direction hint passed to the backend LLM. DESIGN.md is generated per-product; this string is a hint, not a catalog ID."),confirmDarkTheme:se.boolean().optional().describe("(init) Set to true only after confirming with the user that a dark-themed app style is intentional for a consumer (b2c) app. Skips the dark-on-consumer-app warning."),heroPhoto:se.boolean().optional().describe("(init) Whether the landing page hero uses a lifestyle photo background. true = Unsplash photo + overlaid glass card (HabitFlow, Airbnb). false = pure CSS gradients + glassmorphism (Stripe, Linear). Pass the user's answer to the heroPhotoQuestion from mist_plan. If omitted, defaults based on the plan's audienceType (b2c \u2192 photo, else \u2192 CSS).")}),Os={name:"mist_build",description:"STEP 2 of the Mistflow workflow \u2014 scaffolds a new Mistflow project from a plan. 'init' writes files, registers the project, and initializes git. Returns fast (~10s); does NOT run npm install. Pass the planId returned by mist_plan \u2014 do NOT pass the full plan object. All other build steps (install, implement, build, debug, qa, mockup) moved to the `mist` CLI \u2014 invoke via your shell/bash tool: `npx -y @mistflow-ai/cli <command>`. Call `mist_help` for the full CLI reference.",inputSchema:_r,handler:async(n,t)=>{let s=n;switch(s.action){case"init":{if(!s.name)return p("Project name is required for init.",!0);let e=s.plan,i=null;if(s.planId){if(i=xr(s.planId),!i)return p(`Plan not found for planId '${s.planId}'. The plan may have expired. Call mist_plan again to generate a new plan.`,!0);e=i.plan}if(!e)return p("No plan provided. Pass the planId returned by mist_plan, or call mist_plan first to generate a plan.",!0);if(!Array.isArray(e?.steps)||e.steps.length===0)return p("The plan is missing a 'steps' array. This usually means the plan generation was incomplete. Call mist_plan again with the same description to get a complete plan with implementation steps.",!0);if(i?.sourceDeploymentId&&i?.forkToken&&i?.projectId)return Ds({name:s.name,plan:e,path:s.path,projectId:i.projectId,sourceDeploymentId:i.sourceDeploymentId,forkToken:i.forkToken,requiredEnvVars:i.requiredEnvVars??[],dbProvider:i.dbProvider??"neon",planId:s.planId});if(s.landingDesign){let c=it(s.landingDesign);c?e.landingDesign=c.id:console.error(`Landing design '${s.landingDesign}' not found \u2014 ignoring.`)}s.appStyle&&(e.appStyle=s.appStyle);let l=e.design,r=e.audienceType==="b2c",a=s.heroPhoto??r;return e.design={...l??{},heroPhoto:a},js({name:s.name,plan:e,path:s.path,planId:s.planId},t)}default:return p(`Unknown action: ${s.action}. Only 'init' is supported on mist_build. All other build steps (install, implement, build, debug, qa, mockup) moved to the \`mist\` CLI \u2014 invoke via your shell/bash tool: \`npx -y @mistflow-ai/cli <command>\`. Call \`mist_help\` for the full CLI reference.`,!0)}}};import{z as Ie}from"zod";import{z as ct}from"zod";import{resolve as Gs,join as A,dirname as pt,basename as Br}from"path";import{existsSync as O,readFileSync as Ys,writeFileSync as Hr,unlinkSync as Bs,mkdirSync as Vr,cpSync as Ot,rmSync as Lt,readdirSync as Wr}from"fs";import{execFileSync as Kr}from"child_process";import{spawn as Jr}from"child_process";import{tmpdir as Gr}from"os";function Ls(n,t){if(n instanceof H)switch(n.code){case"auth_missing":return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);case"auth_revoked":return p("Your Mistflow credentials were revoked. Run mist_setup to reconnect.",!0);case"auth_expired":case"auth_invalid":case"auth_org_not_found":return p("Your Mistflow session needs to be refreshed. Run mist_setup to reconnect.",!0);case"permission_denied":return p(n.message||"You don't have permission for that action.",!0);case"rate_limited":return p("You're hitting rate limits. Wait 30 seconds and try again.",!0);case"quota_exceeded":return p(`${n.message} Upgrade at https://app.mistflow.ai/pricing to lift limits.`,!0);case"validation_error":case"conflict":return p(n.message,!0);case"not_found":{let e=Ze(),i=e?.orgSlug?.replace(/^user_user_/,"")||void 0,l=e?.email||i;return l&&(n.message.toLowerCase().includes("project")||t.toLowerCase().includes("project"))?p(`Project not found. You are signed in as ${l}. This project may belong to a different account. Run mist_setup to sign in with the account that owns this project.`,!0):p(n.message,!0)}case"server_error":case"upstream_error":return p(`${t} failed: the Mistflow backend returned an error. Try again in 30 seconds.`,!0);case"network_error":return p(`${t} failed: cannot reach Mistflow servers. Check your network and try again.`,!0);default:return p(`${t} failed: ${n.message}`,!0)}let s=n instanceof Error?n.message:String(n);return p(`${t} failed: ${s}`,!0)}import{existsSync as ye,readFileSync as He,readdirSync as Dt,statSync as Mt}from"fs";import{join as G,relative as at}from"path";import{execFileSync as ia}from"child_process";function Pr(n){let t=G(n,".env.local"),s=[];return ye(t)?(He(t,"utf-8").match(/^AUTH_SECRET=(.*)$/m)?.[1]?.trim()||s.push({check:"env",message:"AUTH_SECRET is missing from .env.local. Add a random secret for authentication.",file:".env.local"}),s):(s.push({check:"env",message:"Missing .env.local file. Create one with your local development environment variables (AUTH_SECRET, etc.).",file:".env.local"}),s)}function Rr(n){let t=G(n,"app","api","auth","[...all]","route.ts");return ye(t)?[]:[{check:"auth-routes",message:"Missing Better Auth catch-all route at app/api/auth/[...all]/route.ts. Create this file to handle authentication.",file:"app/api/auth/[...all]/route.ts"}]}function Ir(n){let t=G(n,"app","api","health","route.ts");return ye(t)?[]:[{check:"health-endpoint",message:"Missing health check endpoint at app/api/health/route.ts. Create this file so Mistflow can verify your deployment.",file:"app/api/health/route.ts"}]}function Cr(n){let t=G(n,"mistflow.json");if(!ye(t))return[];let s;try{s=JSON.parse(He(t,"utf-8"))}catch{return[]}let e=s.env;if(!e?.required||typeof e.required!="object")return[];let i=[],l=G(n,".env"),o="";try{ye(l)&&(o=He(l,"utf-8"))}catch{}for(let[r,a]of Object.entries(e.required)){let c=!!process.env[r],u=o.includes(`${r}=`);if(c||u)continue;let m=a?.description?` for '${a.description}'`:"";i.push({check:"required-env",message:`${r} is required${m}. Run mist_config to set it if you haven't already.`})}return i}function Tr(n){let t=G(n,"app");if(!ye(t))return[];let s=[];function e(o){let r;try{r=Dt(o)}catch{return}for(let a of r){if(a==="node_modules"||a===".next"||a===".open-next"||a==="api")continue;let c=G(o,a),u;try{u=Mt(c)}catch{continue}if(u.isDirectory())e(c);else if(a==="page.tsx"||a==="page.ts"||a==="page.jsx"||a==="page.js"){let d="/"+at(t,c).replace(/\\/g,"/").replace(/\([^)]+\)\//g,"").replace(/\/page\.(tsx?|jsx?)$/,"").replace(/^page\.(tsx?|jsx?)$/,"");s.push({file:at(n,c),resolvedRoute:d||"/"})}}}e(t);let i=new Map;for(let o of s){let r=i.get(o.resolvedRoute)??[];r.push(o.file),i.set(o.resolvedRoute,r)}let l=[];for(let[o,r]of i)r.length>1&&l.push({check:"route-collision",message:`Route collision: ${r.join(" and ")} both resolve to "${o}". Next.js will fail with "two parallel pages that resolve to the same path". Move one into a real path segment (e.g. rename (admin)/page.tsx to admin/page.tsx).`,file:r[1]});return l}function Er(n){let t=G(n,"app");if(!ye(t))return[];let s=[];function e(i){let l;try{l=Dt(i)}catch{return}for(let o of l){if(o==="node_modules"||o===".next"||o===".open-next")continue;let r=G(i,o),a;try{a=Mt(r)}catch{continue}if(a.isDirectory())e(r);else if(o==="actions.ts"||o==="actions.tsx"){let c=He(r,"utf-8");c.includes("use server")&&c.includes("cookies()")&&c.includes(".set(")&&s.push(at(n,r))}}}return e(t),s.length>0?[{check:"cookies-in-actions",message:`${s.length} server action(s) use cookies().set() which crashes on Mistflow Cloud's edge runtime: ${s.join(", ")}. Use a database field or form parameter instead.`,file:s[0]}]:[]}function Ar(n){let t=/\b(useState|useEffect|useRef|useCallback|useMemo|useReducer|useContext|useLayoutEffect|useTransition)\s*\(/,s=[];function e(i){if(!ye(i))return;let l;try{l=Dt(i)}catch{return}for(let o of l){if(o==="node_modules"||o===".next"||o===".open-next"||o==="ui")continue;let r=G(i,o),a;try{a=Mt(r)}catch{continue}if(a.isDirectory())e(r);else if(o.endsWith(".tsx")||o.endsWith(".jsx")){if(o==="layout.tsx"||o==="loading.tsx"||o==="error.tsx"||o==="not-found.tsx")continue;let c=He(r,"utf-8");if(c.trimStart().startsWith('"use client"')||c.trimStart().startsWith("'use client'")||c.trimStart().startsWith('"use server"')||c.trimStart().startsWith("'use server'"))continue;if(t.test(c)){let u=at(n,r);s.push({check:"missing-use-client",message:`${u} uses React hooks but is missing "use client" directive. Add "use client"; as the first line of the file.`,file:u})}}}}return e(G(n,"app")),e(G(n,"components")),s}function qs(n){let t=[...Pr(n),...Rr(n),...Ir(n),...Tr(n),...Er(n),...Ar(n)],s=[...Cr(n)];return{passed:t.length===0,errors:t,warnings:s}}import{existsSync as V,readFileSync as Re,readdirSync as lt,statSync as Ve}from"fs";import{join as T}from"path";async function zs(n,t){let s=[];s.push(Nr(n)),s.push(jr(n)),s.push(Dr(n)),s.push(Mr(n)),s.push($r(n)),s.push(qr(n)),t?.plan&&s.push(...Ur(n,t)),s.push(Or(n)),s.push(Lr(n)),s.push(...Fr(n)),s.push(zr(n));let e=s.filter(c=>c.status==="fail"),i=s.filter(c=>c.status==="warn"),l=s.filter(c=>c.status==="pass"),o=e.length>0,r=!o,a;return r&&i.length===0?a=`All ${s.length} checks passed`:r?a=`${l.length}/${s.length} passed, ${i.length} warning(s)`:a=`${e.length} issue(s) found:
|
|
263
|
-
${e.map(c=>` - ${c.message}`).join(`
|
|
264
|
-
`)}`,{passed:r,blocking:o,checksRun:s.length,checks:s,summary:a}}function Nr(n){let t=T(n,".open-next","worker.js");if(!V(t))return{name:"worker-exists",status:"fail",message:"worker.js not found in build output. The build may have failed silently."};let s=Re(t,"utf-8");return s.length<1e3?{name:"worker-exists",status:"warn",message:`worker.js is unusually small (${s.length} bytes). The build output may be incomplete.`}:{name:"worker-exists",status:"pass",message:"Worker bundle exists"}}function jr(n){let t=T(n,".open-next","assets");if(!V(t))return{name:"assets-exist",status:"warn",message:"No static assets directory in build output. CSS and images may not load."};let s=0;function e(i){try{for(let l of lt(i)){let o=T(i,l);try{Ve(o).isDirectory()?e(o):s++}catch{}}}catch{}}return e(t),s===0?{name:"assets-exist",status:"warn",message:"Assets directory is empty. Your app may not have styles or images."}:{name:"assets-exist",status:"pass",message:`${s} static assets ready`}}function Dr(n){let t=T(n,"app","api","health","route.ts");return V(t)?{name:"health-route",status:"pass",message:"Health endpoint found"}:{name:"health-route",status:"warn",message:"No health endpoint found. Deployment verification may fail."}}function Mr(n){let t=T(n,"app","api","auth","[...all]","route.ts");return V(t)?{name:"auth-route",status:"pass",message:"Auth routes found"}:{name:"auth-route",status:"warn",message:"Auth catch-all route not found. Login/register may not work."}}function $r(n){let t=T(n,".open-next","worker.js");if(!V(t))return{name:"worker-size",status:"pass",message:"Worker size check skipped"};let e=Ve(t).size/(1024*1024);return e>60?{name:"worker-size",status:"fail",message:`Worker bundle is ${e.toFixed(1)}MB \u2014 exceeds Mistflow Cloud's 64MB worker bundle limit.`}:e>30?{name:"worker-size",status:"warn",message:`Worker bundle is ${e.toFixed(1)}MB \u2014 large but within Mistflow Cloud limits. May be slow to upload.`}:{name:"worker-size",status:"pass",message:`Worker bundle: ${e.toFixed(1)}MB`}}function Ur(n,t){let s=[],e=new Set;if(t.plan?.pages)for(let o of t.plan.pages){let r=o.path??o.route??o.name;r&&e.add(r.replace(/^\//,""))}if(e.size===0)return[];let i=0,l=[];for(let o of e){if(o.startsWith("api/")||o==="login"||o==="register"||o==="sign-in"||o==="sign-up"){i++;continue}[T(n,"app","(dashboard)",o,"page.tsx"),T(n,"app","(dashboard)",o,"page.ts"),T(n,"app",o,"page.tsx"),T(n,"app",o,"page.ts"),T(n,"app","(admin)",o,"page.tsx")].some(a=>V(a))?i++:l.push(`/${o}`)}return l.length===0?s.push({name:"plan-routes",status:"pass",message:`All ${e.size} planned pages found`}):l.length<=2?s.push({name:"plan-routes",status:"warn",message:`${i}/${e.size} planned pages found. Missing: ${l.join(", ")}`}):s.push({name:"plan-routes",status:"warn",message:`Only ${i}/${e.size} planned pages found. ${l.length} pages missing.`}),s}function Or(n){let t=T(n,"app");if(!V(t))return{name:"empty-pages",status:"pass",message:"App directory check skipped"};let s=[];function e(i){try{for(let l of lt(i)){let o=T(i,l);try{if(Ve(o).isDirectory())e(o);else if(l==="page.tsx"||l==="page.ts"){let a=Re(o,"utf-8").trim();if(a.length<50||a.includes("export default function")&&a.includes("TODO")){let c=o.replace(n+"/","");s.push(c)}}}catch{}}}catch{}}return e(t),s.length>0?{name:"empty-pages",status:"warn",message:`${s.length} page(s) appear to be empty/placeholder: ${s.slice(0,3).join(", ")}`}:{name:"empty-pages",status:"pass",message:"No empty pages detected"}}function Lr(n){let t=T(n,"app","page.tsx");if(!V(t))return{name:"landing-page",status:"warn",message:"No root page.tsx found"};let s=Re(t,"utf-8");if(s.includes('redirect("/login")')||s.includes('redirect("/register")')){let e=T(n,"middleware.ts"),i=V(e)?Re(e,"utf-8"):"";if(!(i.includes('"/"')||i.includes("'/'")))return{name:"landing-page",status:"fail",message:"Landing page (app/page.tsx) is just a redirect to /login, and middleware does not allow '/' as public. Users will never see a landing page."}}return s.length<200?{name:"landing-page",status:"warn",message:`Landing page is very small (${s.length} chars) \u2014 may be a placeholder.`}:{name:"landing-page",status:"pass",message:"Landing page has content"}}function qr(n){let t=T(n,"app");if(!V(t))return{name:"cookies-in-actions",status:"pass",message:"No app directory"};let s=[];function e(i){try{for(let l of lt(i)){if(l==="node_modules"||l===".next"||l===".open-next")continue;let o=T(i,l);try{if(Ve(o).isDirectory())e(o);else if(l==="actions.ts"||l==="actions.tsx"){let a=Re(o,"utf-8");a.includes("use server")&&a.includes("cookies()")&&a.includes(".set(")&&s.push(o.replace(n+"/",""))}}catch{}}}catch{}}return e(t),s.length>0?{name:"cookies-in-actions",status:"fail",message:`${s.length} server action(s) use cookies().set() which crashes on Mistflow Cloud's edge runtime: ${s.join(", ")}. Use a database field or form parameter instead.`}:{name:"cookies-in-actions",status:"pass",message:"No cookies().set() in server actions"}}function zr(n){let t=T(n,"app");if(!V(t))return{name:"fake-forms",status:"pass",message:"No app directory"};let s=[];function e(i){try{for(let l of lt(i)){if(l==="node_modules"||l===".next"||l===".open-next")continue;let o=T(i,l);try{if(Ve(o).isDirectory())e(o);else if(l.endsWith(".tsx")||l.endsWith(".ts")){let a=Re(o,"utf-8");if((a.includes("setTimeout")||a.includes("new Promise"))&&a.includes("Simulate")||a.includes("simulate")||a.includes("// TODO")&&a.includes("API")){let c=o.replace(n+"/","");s.push(c)}}}catch{}}}catch{}}return e(t),s.length>0?{name:"fake-forms",status:"fail",message:`${s.length} file(s) have fake/simulated API calls instead of real server actions: ${s.slice(0,3).join(", ")}. Forms must use 'use server' actions that write to the database.`}:{name:"fake-forms",status:"pass",message:"No fake form handlers detected"}}function Fr(n){let s=[T(n,"components","sidebar.tsx"),T(n,"components","topnav.tsx"),T(n,"components","nav.tsx")].find(r=>V(r));if(!s)return[];let l=(Re(s,"utf-8").match(/href:\s*"([^"]+)"/g)??[]).map(r=>r.replace(/href:\s*"/,"").replace(/"$/,"")).filter(r=>!r.startsWith("/api")&&!r.includes("["));if(l.length===0)return[];let o=[];for(let r of l){let a=r.replace(/^\//,"");if(!a||a==="")continue;[T(n,"app","(dashboard)",a,"page.tsx"),T(n,"app","(dashboard)",a,"page.ts"),T(n,"app","(app)",a,"page.tsx"),T(n,"app",a,"page.tsx"),T(n,"app",a,"page.ts")].some(u=>V(u))||o.push(r)}return o.length>0?[{name:"nav-links",status:"fail",message:`Sidebar/nav links to pages that don't exist: ${o.join(", ")}. These will 404.`}]:[{name:"nav-links",status:"pass",message:`All ${l.length} nav links have matching pages`}]}import{z as $t}from"zod";import{resolve as ma}from"path";import{execFileSync as ga}from"child_process";var ya=$t.object({projectPath:$t.string().optional().describe("Path to the project directory (default: current working directory)"),buildOutput:$t.string().optional().describe("Build output to parse (if not provided, runs npm run build)")});function Fs(n){let t=[],s=/([^\s(]+)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)/g,e;for(;(e=s.exec(n))!==null;){let[,a,c,u,m,d]=e;t.push({file:a,line:parseInt(c,10),column:parseInt(u,10),message:`${m}: ${d}`,humanMessage:`There is a type error in ${a} on line ${c}: ${d}`,suggestion:`Check line ${c} in ${a}. ${m==="TS2345"?"The types of the arguments do not match.":`Fix the ${m} error.`}`})}let i=/(?:Error:\s*)?\.\/([^\s:]+):(\d+):(\d+)\s*\n\s*(.+)/g;for(;(e=i.exec(n))!==null;){let[,a,c,u,m]=e;t.some(d=>d.file===a&&d.line===parseInt(c,10))||t.push({file:a,line:parseInt(c,10),column:parseInt(u,10),message:m,humanMessage:`There is an error in ${a} on line ${c}: ${m.trim()}`,suggestion:`Check line ${c} in ${a} and fix the issue.`})}let l=/Module not found:\s*(?:Error:\s*)?Can't resolve ['"]([^'"]+)['"]\s*(?:in\s*['"]?([^'"]+)['"]?)?/g;for(;(e=l.exec(n))!==null;){let[,a,c]=e;t.push({file:c,message:`Module not found: ${a}`,humanMessage:`The file ${c??"your project"} is trying to import '${a}' which is not installed.`,suggestion:`Run npm install ${a}`})}let o=/Package subpath ['"]([^'"]+)['"] is not defined by "exports" in .*?node_modules\/([^/]+(?:\/[^/]+)?)\//g;for(;(e=o.exec(n))!==null;){let[,a,c]=e;t.push({message:`ERR_PACKAGE_PATH_NOT_EXPORTED: ${c}${a}`,humanMessage:`The package '${c}' does not export the subpath '${a}'. This is usually caused by a version conflict between packages that depend on different major versions of '${c}'.`,suggestion:`Add '${c}' at the version that exports '${a}' as an optionalDependency in package.json (this pins it at root level and lets the other version nest). Then delete node_modules and package-lock.json, and run npm install.`})}let r=/SyntaxError:\s*([^\s:]+):\s*(.+?)\s*\((\d+):(\d+)\)/g;for(;(e=r.exec(n))!==null;){let[,a,c,u,m]=e;t.some(d=>d.file===a&&d.line===parseInt(u,10))||t.push({file:a,line:parseInt(u,10),column:parseInt(m,10),message:`SyntaxError: ${c}`,humanMessage:`There is a syntax error in ${a} on line ${u}.`,suggestion:`Check line ${u} in ${a} for a missing closing bracket or unexpected token.`})}return t}function pe(n,t){return Kr("git",n,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function Ut(n){try{return pe(["rev-parse","--is-inside-work-tree"],n),!0}catch{return!1}}function Hs(n){try{return pe(["status","--porcelain"],n).length>0}catch{return!1}}function Vs(n,t){return pe(["add","-A"],n),pe(["commit","-m",t,"--allow-empty-message"],n),pe(["rev-parse","HEAD"],n)}function Yr(n){return pe(["rev-parse","HEAD"],n)}function Ws(n){try{return pe(["remote","get-url","origin"],n),!0}catch{return!1}}function Qr(n){try{let t=pe(["rev-parse","--abbrev-ref","HEAD"],n);return pe(["push","origin",t],n),{success:!0}}catch(t){return{success:!1,error:t instanceof Error?t.message:"push failed"}}}function Xr(n){let t=pt(Gs(n)),s=pt(t)===t?t:"/";for(;t!==s&&t!==pt(t);){if(O(A(t,"pnpm-workspace.yaml"))||O(A(t,"lerna.json")))return t;let e=A(t,"package.json");if(O(e))try{if(JSON.parse(Ys(e,"utf-8")).workspaces)return t}catch{}t=pt(t)}return null}function Zr(n){let t=A(Gr(),"mistflow-build");Vr(t,{recursive:!0});let s=A(t,`${Br(n)}-${Date.now()}`);return Ot(n,s,{recursive:!0,filter:e=>{let i=e.slice(n.length);return!(i.startsWith("/.git")||i.startsWith("\\.git")||i==="/node_modules"||i.startsWith("/node_modules/")||i==="/.open-next"||i.startsWith("/.open-next/")||i==="/.next"||i.startsWith("/.next/"))}}),s}function eo(n,t){let s=A(n,".open-next"),e=A(n,".next");if(O(s)){let i=A(t,".open-next");O(i)&&Lt(i,{recursive:!0}),Ot(s,i,{recursive:!0})}if(O(e)){let i=A(t,".next");O(i)&&Lt(i,{recursive:!0}),Ot(e,i,{recursive:!0})}}function to(n){try{Lt(n,{recursive:!0,force:!0})}catch{console.error(`[deploy] Failed to clean up isolated build dir: ${n}`)}}function Me(n,t,s,e,i,l){return new Promise(o=>{let r=Jr(n,t,{cwd:s,stdio:["pipe","pipe","pipe"],timeout:e,...l?{env:l}:{}}),a="",c="";r.stdout?.on("data",u=>{let m=u.toString();if(c+=m,i)for(let d of m.split(`
|
|
265
|
-
`).filter(Boolean))i(d)}),r.stderr?.on("data",u=>{let m=u.toString();if(a+=m,i)for(let d of m.split(`
|
|
266
|
-
`).filter(Boolean))i(d)}),r.on("close",(u,m)=>{o({success:u===0,stdout:c,stderr:a,signal:m})}),r.on("error",u=>{o({success:!1,stdout:c,stderr:a+u.message})})})}var ja=ct.object({projectPath:ct.string().optional().describe("Path to the project directory (default: current working directory)"),message:ct.string().optional().describe("Deploy message"),environment:ct.enum(["production","preview"]).optional().default("production").describe("Target environment: 'production' (default) or 'preview' for a shareable URL")});function so(n){return new Promise(t=>setTimeout(t,n))}function Ks(n){switch(n){case"pending":return"Provisioning database...";case"building":return"Building your app...";case"deploying":return"Deploying to Mistflow Cloud...";case"verifying":return"Verifying deployment...";default:return`Status: ${n}`}}function qt(n){let t=A(n,"mistflow.json");if(O(t))try{return JSON.parse(Ys(t,"utf-8"))}catch{}return{}}function no(n,t){let s=A(n,"mistflow.json"),e=qt(n),i=e.deploy?.count??e.deployCount??0;e.deploy={url:t,count:i+1,lastDeployedAt:new Date().toISOString()};let l=e.plan?.steps;if(Array.isArray(l))for(let o of l)o.status==="in_progress"&&(o.status="completed");delete e.deployUrl,delete e.deployCount,Hr(s,JSON.stringify(e,null,2)+`
|
|
267
|
-
`)}async function Qs(n,t){let e=process.platform==="win32"?"npx.cmd":"npx",i=0,l=A(n,"node_modules",".bin","opennextjs-cloudflare"),o=O(l),r={...process.env,NODE_ENV:"production"},a=await Me(o?l:e,o?["build"]:["@opennextjs/cloudflare","build"],n,3e5,d=>{d.includes("Compiling")?t?.("Compiling your app..."):d.includes("Collecting page data")?t?.("Collecting page data..."):d.includes("Generating static pages")?t?.("Generating static pages..."):d.match(/^[○●◐λƒ]\s/)?i++:d.includes("Creating Cloudflare worker")?t?.("Packaging for Mistflow Cloud..."):d.includes("Build completed")&&t?.("Build completed!")},r);if(a.success)return{success:!0,buildStats:i>0?`${i} routes compiled`:"build complete"};let c=a.stderr+`
|
|
268
|
-
`+a.stdout;if(a.signal==="SIGKILL"||c.includes("SIGKILL")||c.includes("exit code: 137"))return{success:!1,signal:"SIGKILL",error:"Your app ran out of memory while building. This sometimes happens with complex projects. Try deploying again \u2014 if it keeps failing, we'll help you figure it out."};let u=Fs(c);if(u.length>0){let d=u.map((b,P)=>{let g=`${P+1}. ${b.humanMessage}`;return b.suggestion&&(g+=`
|
|
269
|
-
Fix: ${b.suggestion}`),g}).join(`
|
|
270
|
-
|
|
271
|
-
`);return{success:!1,error:`Build failed with ${u.length} error${u.length===1?"":"s"}:
|
|
272
|
-
|
|
273
|
-
${d}`}}let m=c.split(`
|
|
274
|
-
`).filter(d=>d.includes("error")||d.includes("Error")).slice(0,10);return{success:!1,error:m.length>0?`OpenNext build failed:
|
|
275
|
-
${m.join(`
|
|
276
|
-
`)}`:`OpenNext build failed:
|
|
277
|
-
${c.slice(-500)}`}}async function ro(n,t){let s=Xr(n);if(s)return console.error(`[deploy] Project is inside monorepo at ${s} \u2014 building in isolated temp directory`),t?.("Detected parent project folder \u2014 building in isolated directory..."),Js(n,t);let e=await Qs(n,t);return!e.success&&e.signal==="SIGKILL"?(console.error("[deploy] Build was OOM-killed \u2014 retrying in isolated temp directory"),t?.("Build ran out of memory \u2014 retrying with a fresh setup..."),Js(n,t)):e}async function Js(n,t){let s;try{t?.("Copying project to isolated build directory..."),s=Zr(n),t?.("Installing dependencies...");let i=process.platform==="win32"?"npm.cmd":"npm",l=await Me(i,["install","--prefer-offline"],s,12e4);if(!l.success)return{success:!1,error:`Failed to install dependencies in isolated build:
|
|
278
|
-
${l.stderr.slice(-300)}`};await Me(i,["dedupe"],s,6e4);let o=await Qs(s,t);return o.success&&(t?.("Copying build artifacts..."),eo(s,n)),{...o,builtInIsolation:s}}finally{s&&to(s)}}async function oo(n,t,s){if(!O(A(n,"db","schema")))return{success:!0,skipped:!0,skipReason:"No database structure found"};let e=A(n,"db","schema");try{if(Wr(e).filter(b=>b.endsWith(".ts")).length===0)return{success:!0,skipped:!0,skipReason:"No database files found in db/schema/"}}catch{return{success:!0,skipped:!0,skipReason:"Could not read db/schema/ directory"}}if(!O(A(n,"drizzle.config.ts")))return{success:!0,skipped:!0,skipReason:"No drizzle.config.ts found"};let i;try{i=await rs(t)}catch{return console.error("[deploy] Could not fetch DB credentials \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"Could not fetch database credentials \u2014 backend will handle the database update"}}let{db_provider:l,credentials:o}=i;if(l==="neon"){if(!o.DATABASE_URL)return console.error("[deploy] No DATABASE_URL found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No DATABASE_URL configured \u2014 set it with mist_config"}}else if(!o.TURSO_URL||!o.TURSO_AUTH_TOKEN)return console.error("[deploy] No Turso credentials found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No Turso credentials configured"};let r={PATH:process.env.PATH??"",HOME:process.env.HOME??"",NODE_PATH:process.env.NODE_PATH??"",TMPDIR:process.env.TMPDIR??"",...o},c=process.platform==="win32"?"npx.cmd":"npx",m=await Me(c,s?["drizzle-kit","push","--force"]:["drizzle-kit","push"],n,6e4,d=>{let b=d.replace(/(?:TURSO_AUTH_TOKEN|DATABASE_URL|AUTH_SECRET)=[^\s&]*/gi,P=>P.split("=")[0]+"=REDACTED").replace(/postgresql:\/\/[^@]*@/g,"postgresql://REDACTED@").replace(/libsql:\/\/[^\s]*/g,"libsql://REDACTED");console.error(`[drizzle-kit] ${b}`)},r);if(!m.success){let d=(m.stderr+`
|
|
279
|
-
`+m.stdout).trim(),b=d.toLowerCase();if(b.includes("data-loss")||b.includes("cannot be reverted"))return{success:!1,error:"Database update blocked: your database has existing data that would be deleted. The update includes changes that would remove existing data. If you're OK with losing the data, deploy again with forceSchema: true."};let P=d.split(`
|
|
280
|
-
`).filter(f=>f.includes("error")||f.includes("Error")||f.includes("ERR")).slice(0,5);return{success:!1,error:`Database update failed:
|
|
281
|
-
${P.length>0?P.join(`
|
|
282
|
-
`):d.slice(-500)}`}}return{success:!0}}async function Xs(n,t){let{projectPath:s,message:e,environment:i="production",forceSchema:l}=n,o=i,r=Gs(s??process.cwd());if(!J())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let a=qs(r);if(!a.passed){let y=a.errors.map(R=>{let _=`[${R.check}] ${R.message}`;return R.file&&(_+=` (${R.file}${R.line?`:${R.line}`:""})`),_}).join(`
|
|
283
|
-
`);return p(`Deploy blocked \u2014 fix these issues first:
|
|
284
|
-
|
|
285
|
-
${y}`,!0)}let c=a.warnings.map(y=>`[${y.check}] ${y.message}`),u;if(Ut(r))if(Hs(r))try{let y=qt(r).deploy?.count??0,R=e?`Deploy v${y+1}: ${e}`:`Deploy v${y+1}`;u=Vs(r,R)}catch{console.error("[deploy] Pre-deploy git commit failed, continuing")}else try{u=Yr(r)}catch{}let m=A(r,"mistflow.json");if(!O(m))return p("No mistflow.json found. Run mist_build (action: 'init') first to set up your project.",!0);let d;try{let{ensureBackendRegistered:y}=await import("./self-heal-J7EGYRN4.js");d=await y(r,{forceSync:!0})}catch(y){let R=y instanceof H?y.message:"Check your internet connection.";return p(`Could not register project with Mistflow: ${R}
|
|
286
|
-
|
|
287
|
-
Try deploying again in a moment.`,!0)}if(d||(d=Z(r)?.projectId),!d)return p("Could not register project with Mistflow \u2014 you may not be signed in. Run mist_setup to authenticate, then redeploy.",!0);if(d&&o==="production")try{let y=await Zt(d),R=y,_=R.deploy_count??y.deployCount??0;(R.deploy_strategy??y.deploy_strategy)==="staging"&&_>0&&(o="preview",console.error("[deploy] Staging mode enabled \u2014 auto-redirecting to preview"))}catch{}let b=[],P=Date.now(),g=(y,R)=>{let _={phase:y,message:R};return b.push(_),_},f=(y,R)=>{y.durationMs=Date.now()-P,R&&(y.message=R)};if(t){let y=tt(t.server,t.progressToken,()=>b[b.length-1]?.message??"Deploying...");t.cleanup=()=>y.stop()}let S=!1;if(d&&o!=="preview"){let y=g("schema","Updating database structure..."),R=await oo(r,d,l);if(R.skipped)f(y,R.skipReason??"Database update skipped");else if(R.success)S=!0,f(y,"Database updated successfully");else return f(y,"Database update failed"),p(`Deploy blocked \u2014 database update failed:
|
|
288
|
-
|
|
289
|
-
${R.error}
|
|
18
|
+
`)}`);return r.join(`
|
|
290
19
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
${K.error}`,!0);f(B,`Build complete \u2014 ${K.buildStats??"ready"} (${((Date.now()-W)/1e3).toFixed(0)}s)`);let M=g("qa","Running smoke tests..."),Q=Z(r),N=await zs(r,Q);if(N.passed)f(M,`Smoke test passed \u2014 ${N.checksRun} checks OK`);else if(f(M,`Smoke test: ${N.summary}`),N.blocking)return p(`Deploy blocked \u2014 smoke test failed:
|
|
294
|
-
|
|
295
|
-
${N.summary}
|
|
296
|
-
|
|
297
|
-
Fix these issues and try again.`,!0);let Te=A(r,".open-next");if(!O(Te))return p("Build succeeded but .open-next/ directory not found. Check your OpenNext configuration.",!0);g("package","Packaging build artifacts...");let ne=A(r,".open-next-build.tar.gz"),re=[".open-next"];if(O(A(r,"db"))&&re.push("db"),O(A(r,"drizzle.config.ts"))&&re.push("drizzle.config.ts"),O(A(r,"package.json"))&&re.push("package.json"),!(await Me("tar",["-czf",ne,"-C",r,...re],r,6e4)).success)return p("Failed to create build archive. Check disk space and permissions.",!0);let ue;{let y=A(r,".mistflow-source.tar.gz");(await Me("tar",["-czf",y,"-C",r,"--exclude",".open-next","--exclude","node_modules","--exclude",".git","--exclude",".next","--exclude",".open-next-build.tar.gz","--exclude",".mistflow-source.tar.gz","."],r,6e4)).success?ue=y:console.error("[deploy] Source archive creation failed, continuing without it")}g("upload","Uploading to Mistflow...");let de=Ze()?.email,q,X;try{let y=await ts(d,ne,o,de,S,ue,u);if(q=y.deployment_id??y.id,!q)return p("Upload succeeded but no deployment ID was returned. Check the Mistflow dashboard.",!0);X=y.status}catch(y){return Ls(y,"Deploy")}finally{try{Bs(ne)}catch{}if(ue)try{Bs(ue)}catch{}}g("deploying","Deploying to the edge...");let ke=Date.now(),Ee=24e4,Se=3e3,D=[Ks(X)],oe=X;for(;Date.now()-ke<Ee;){await so(Se);let y;try{y=await fe(q)}catch{continue}if(y.status!==oe&&(D.push(Ks(y.status)),oe=y.status),y.status==="live"){let R=((Date.now()-ke)/1e3).toFixed(0),_=y.url??"";if(!_)return p("Deployment marked as live but no URL was returned. Check the Mistflow dashboard.",!0);let v=o==="preview";if(!v){no(r,_);try{let{readLocalState:w,syncRemoteState:E}=await import("./state-manager-QMSYKJ37.js"),L=w(r);L&&d&&(L.deployCount=(L.deployCount??0)+1,E(d,L).catch(()=>{}))}catch{}if(Ut(r)&&Ws(r))try{Hs(r)&&Vs(r,"Update deploy metadata"),Qr(r)}catch{}}let U=v?"Preview":"App",ie=qt(r),Ge=ie.name,te=typeof Ge=="string"?Ge:"my app",Ye=ie.features,ae=Array.isArray(Ye)?Ye.length:0,xe=ie.plan,h=xe&&typeof xe=="object"&&"steps"in xe?xe.steps:void 0,k=Array.isArray(h)?h.length:0,C=b.map(w=>w.message),I={url:_,time:`${R}s`,deploymentId:q,environment:o,statusLog:D,deployTimeline:C,message:v?`${U} is live at ${_} \u2014 preview deployed in ${R}s. This URL expires in 72 hours.`:`Your app is live at ${_} \u2014 deployed in ${R}s.`};if(v)I.instruction=["STAGING FLOW: The preview is deployed with an isolated database. Verify it before going live.","","1. Call mist_build with action='qa' and url='"+_+"' to test the preview.","2. If QA passes \u2014 tell the user the preview looks good and ask: 'Ready to go live?'","3. If QA finds issues \u2014 fix them, then call mist_deploy action='deploy' environment='preview' to redeploy the preview.","4. When user confirms \u2014 call mist_deploy action='promote' to push to production (~10s).","","Show the preview URL to the user so they can check it themselves while QA runs."].join(`
|
|
298
|
-
`),I.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${_}' to verify the preview. After QA passes, ask user to confirm, then call mist_deploy action='promote'.`,I.qaRequired=!0,I.qaUrl=_,I.previewDeploymentId=q,I.promoteAction=`mist_deploy action='promote' deploymentId='${q}'`;else{let w=encodeURIComponent(`Just built "${te}" with AI and deployed it live in ${R}s.
|
|
299
|
-
|
|
300
|
-
${k} steps, ${ae} features, zero config.
|
|
301
|
-
|
|
302
|
-
${_}
|
|
303
|
-
|
|
304
|
-
Built with @mistflow`),E=encodeURIComponent(`I just described an app idea and got a live, working URL in ${R} seconds.
|
|
305
|
-
|
|
306
|
-
"${te}" \u2014 ${k} build steps, ${ae} features, fully deployed.
|
|
307
|
-
|
|
308
|
-
${_}
|
|
309
|
-
|
|
310
|
-
Built with Mistflow (mistflow.ai)`);I.share={tweetUrl:`https://twitter.com/intent/tweet?text=${w}`,linkedInUrl:`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(_)}`,celebration:["","================================================",` ${te} is LIVE!`,"================================================","",` ${_}`,"",` Deployed in ${R}s`,` ${ae>0?`${ae} features`:""}${ae>0&&k>0?" \xB7 ":""}${k>0?`${k} build steps`:""}`,""," Share it:",` Twitter \u2192 https://twitter.com/intent/tweet?text=${w}`,` LinkedIn \u2192 https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(_)}`,"","================================================"].join(`
|
|
311
|
-
`)},I.instruction=["IMPORTANT: The app is deployed but NOT verified yet. Before celebrating:","","1. Call mist_build with action='qa' to test the live app.","2. If QA passes \u2014 present the URL prominently and show the celebration block above.","3. If QA finds issues \u2014 fix them, redeploy (mist_deploy action='deploy'), and run qa again.","4. Keep looping until QA passes. Only celebrate when all checks are green.","","Do NOT show the URL to the user until QA passes. A broken app is worse than no app."].join(`
|
|
312
|
-
`),I.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${_}' to verify the app works. Do NOT tell the user the app is ready until QA passes.`,I.qaRequired=!0,I.qaUrl=_}ie.hasAdmin&&!v&&de&&(I.adminSetup={message:`Your app is live. You're the admin \u2014 sign up at ${_}/signup with ${de} to create your admin account. You'll pick your own password; the /admin panel unlocks as soon as you sign up.`}),c.length>0&&(I.warnings=c);let $=[];if(!v){let w=JSON.stringify(ie).toLowerCase();$.push({action:"Add a custom domain",command:"mist_config resource='domain' action='add' domain='yourdomain.com'",reason:"Give your app a professional URL instead of the default .mistflow.app domain.",priority:"medium"}),Ut(r)&&!Ws(r)&&$.push({action:"Back up to GitHub",command:"Create a repo at github.com/new, then tell me the URL and I'll push your code there",reason:"Your source code is saved with every deploy, but GitHub gives you an extra backup and lets you collaborate.",priority:"medium"});let E=w.includes("user")||w.includes("account")||w.includes("member"),L=w.includes("stripe")||w.includes("payment")||w.includes("billing");E&&!L&&$.push({action:"Add payments",command:"Tell the AI: 'Add Stripe payments to my app'",reason:"Your app has user accounts \u2014 you could monetize with subscriptions or one-time payments.",priority:"low"}),$.push({action:"Share your app",command:"mist_project action='share'",reason:"Generate a shareable link so others can fork and customize your app.",priority:"low"}),$.push({action:"Make changes",command:"Describe what you want to change, then run mist_deploy action='deploy'",reason:"Edit your code and redeploy in seconds. Your app is yours to evolve.",priority:"low"}),$.push({action:"See analytics",command:`Visit ${Yt()} to see pageviews and visitors`,reason:"Track how people are using your app.",priority:"low"})}return $.length>0&&(I.nextSteps=$),Ne(_,JSON.stringify(I))}if(y.status==="failed")return p(y.error??"Deployment failed. Check the Mistflow dashboard for details.",!0)}return p(`Deploy is taking longer than expected. Your deploy ID is ${q} \u2014 check status later.`,!0)}import{z as ut}from"zod";import{resolve as io,join as ao}from"path";import{execFileSync as lo}from"child_process";var za=ut.object({projectPath:ut.string().optional().describe("Path to the project directory (default: cwd)"),action:ut.enum(["redeploy","rollback"]).describe("Action to perform: 'redeploy' re-deploys the latest build, 'rollback' reverts to a specific previous deployment"),deploymentId:ut.string().optional().describe("Deployment ID to rollback to (required for 'rollback')")});function be(n,t){return lo("git",n,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function Zs(n){try{return be(["rev-parse","--is-inside-work-tree"],n),!0}catch{return!1}}async function co(n,t,s,e){if(t&&Zs(n))try{be(["cat-file","-t",t],n);let i=!1;try{be(["status","--porcelain"],n).length>0&&(be(["stash","push","-m","mistflow-rollback-auto-stash"],n),i=!0)}catch{}return be(["reset","--hard",t],n),be(["commit","--allow-empty","-m","Rollback to previous deploy"],n),{method:"git",success:!0,...i?{warning:"Your uncommitted changes were saved to git stash. Run `git stash pop` to restore them."}:{}}}catch{}if(s&&e)try{let{existsSync:i,mkdirSync:l,rmSync:o}=await import("fs"),r=ao(n,".mistflow-rollback-source.tar.gz");await ms(e,r);let{spawn:a}=await import("child_process");await new Promise((c,u)=>{let m=a("tar",["-xzf",r,"-C",n,"--exclude","node_modules","--exclude",".git"],{cwd:n,stdio:"pipe"});m.on("close",d=>d===0?c():u(new Error(`tar exit ${d}`))),m.on("error",u)});try{o(r)}catch{}if(Zs(n))try{be(["add","-A"],n),be(["commit","-m","Rollback to previous deploy (from source snapshot)"],n)}catch{}return{method:"r2",success:!0}}catch(i){return{method:"r2",success:!1,error:i instanceof Error?i.message:"Source download failed"}}return{method:"none",success:!1,error:"No source snapshot available for this deployment"}}function po(n){return new Promise(t=>setTimeout(t,n))}function uo(n){switch(n){case"pending":return"Provisioning...";case"building":return"Building...";case"deploying":return"Deploying to Mistflow Cloud...";case"verifying":return"Verifying deployment...";default:return`Status: ${n}`}}async function en(n,t,s){let e=Date.now(),i=12e4,l=3e3,o=[],r="";for(;Date.now()-e<i;){await po(l);let a;try{a=await fe(n)}catch{continue}if(a.status!==r&&(o.push(uo(a.status)),r=a.status),a.status==="live"){let c=((Date.now()-e)/1e3).toFixed(0),u=a.url??"";if(s)try{await s()}catch{}return p(JSON.stringify({url:u,time:`${c}s`,deploymentId:n,statusLog:o,message:u?`${t} complete \u2014 live at ${u} in ${c}s.`:`${t} complete in ${c}s.`}))}if(a.status==="failed")return p(a.error??`${t} failed. Check the Mistflow dashboard for details.`,!0)}return p(`${t} is taking longer than expected. Deployment ID: ${n} \u2014 check status later.`,!0)}async function zt(n){let{projectPath:t,action:s,deploymentId:e}=n,i=io(t??process.cwd());if(!J())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let o=Z(i)?.projectId;if(!o)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(s){case"redeploy":{let r=await ps(o);return en(r.deployment_id,"Redeploy")}case"rollback":{if(!e)return p("Deployment ID is required for rollback. Use mist_project (action: 'deployments') to see previous deployment IDs.",!0);let r=await ds(e);return en(r.deployment_id,"Rollback",async()=>{let a=await co(i,r.git_commit_sha,r.source_artifact_key,e);a.success||console.error(`[rollback] Local source restore failed: ${a.error}`),a.warning&&console.error(`[rollback] ${a.warning}`)})}default:return p(`Unknown action: ${s}. Use redeploy or rollback.`,!0)}}catch(r){if(r instanceof H)return p(r.message,!0);let a=r instanceof Error?r.message:"An unexpected error occurred";return p(a,!0)}}import{resolve as mo}from"path";function fo(n){return new Promise(t=>setTimeout(t,n))}async function tn(n){let t=mo(n.projectPath??process.cwd());if(!J())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let e=Z(t)?.projectId;if(!e)return p("No project ID found. Deploy the project first.",!0);let i=n.deploymentId;if(!i)try{let c=(await ze(e)).find(u=>u.environment==="preview"&&u.status==="live");if(!c)return p("No live preview found to promote. Deploy with staging mode first, then call promote after QA passes.",!0);i=c.id}catch{return p("Could not list deployments. Check your connection.",!0)}console.error(`[promote] Promoting preview ${i} to production...`);let l;try{l=(await us(e,i)).deployment_id}catch(a){let c=a instanceof Error?a.message:"Promote failed";return p(`Promote failed: ${c}`,!0)}let o=Date.now(),r=12e4;for(;Date.now()-o<r;){await fo(3e3);try{let a=await fe(l);if(a.status==="live"){let c=((Date.now()-o)/1e3).toFixed(0),u=a.url??"",m={url:u,time:`${c}s`,deploymentId:l,environment:"production",promoted:!0,message:`Promoted to production at ${u} in ${c}s. Same build that passed QA on preview.`};return m.instruction=["IMPORTANT: The app has been promoted to production. Run a quick QA check to verify.","","1. Call mist_build with action='qa' to test the live production app.","2. If QA passes \u2014 celebrate! Present the URL to the user.","3. If QA finds issues \u2014 these are likely environment differences (auth, DNS). Report them."].join(`
|
|
313
|
-
`),m.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${u}' to verify production.`,m.qaRequired=!0,m.qaUrl=u,u?Ne(u,JSON.stringify(m)):p(JSON.stringify(m))}if(a.status==="failed")return p(`Promote failed: ${a.error??"Check the Mistflow dashboard for details."}`,!0)}catch{}}return p(`Promote is taking longer than expected. Deployment ID: ${l}`,!0)}import{z as sn}from"zod";import{spawn as yo,execFileSync as nn}from"child_process";import{existsSync as mt,readFileSync as rn,writeFileSync as bo,mkdirSync as wo}from"fs";import{join as $e,resolve as vo}from"path";import{homedir as on}from"os";import{createConnection as ko}from"net";import{existsSync as go,readFileSync as ho}from"fs";function dt(n){let t=new Set;if(!go(n))return t;let s=ho(n,"utf-8");for(let e of s.split(`
|
|
314
|
-
`)){let i=e.trim();if(!i||i.startsWith("#"))continue;let l=i.indexOf("=");if(l>0){let o=i.slice(0,l).trim(),r=i.slice(l+1).trim();r&&r!=='""'&&r!=="''"&&t.add(o)}}return t}var So=sn.object({projectPath:sn.string().optional().describe("Path to the project directory (default: current working directory)")});function an(){return $e(on(),".mistflow","processes.json")}function ln(){let n=an();if(!mt(n))return[];try{return JSON.parse(rn(n,"utf-8"))}catch{return[]}}function cn(n){let t=$e(on(),".mistflow");mt(t)||wo(t,{recursive:!0}),bo(an(),JSON.stringify(n,null,2))}function xo(){let n=ln(),t=[];for(let s of n)try{process.kill(s.pid,0),t.push(s)}catch{}cn(t)}function pn(n){return new Promise(t=>{let s=ko({port:n,host:"127.0.0.1"});s.on("connect",()=>{s.destroy(),t(!0)}),s.on("error",()=>{t(!1)})})}async function _o(n,t,s){let e=Date.now();for(;Date.now()-e<t;){if(await pn(n))return!0;await new Promise(i=>setTimeout(i,s))}return!1}function Po(n){return mt($e(n,"mistflow.json"))}function Ro(n){let t=$e(n,"mistflow.json");if(!mt(t))return[];let s;try{s=JSON.parse(rn(t,"utf-8"))}catch{return[]}let e=s.env?.required;if(!e||typeof e!="object")return[];let i=Object.keys(e);if(i.length===0)return[];let l=dt($e(n,".env.local"));return i.filter(o=>!l.has(o))}var un={name:"mist_preview",description:"Build the app and start a local production server on localhost:3000 for previewing. Use when the user says 'mist preview'.",inputSchema:So,handler:async n=>{let s=vo(n.projectPath??process.cwd()),e=3e3;if(!Po(s))return ce(s);let i=Ro(s);if(i.length>0)return p(`Missing required environment variables in .env.local: ${i.join(", ")}. Add them to ${$e(s,".env.local")} before previewing.`,!0);xo();let l=await pn(e),o=ln(),r=o.some(m=>m.type==="dev-server"&&m.port===e);if(l&&!r)return p(`Port ${e} is in use. Stop the other process or I will use a different port.`,!0);let a=[...o];if(!l){try{nn("npx",["drizzle-kit","push"],{cwd:s,stdio:["pipe","pipe","pipe"],timeout:3e4})}catch{console.error("[preview] drizzle-kit push failed, continuing...")}try{nn("npm",["run","build"],{cwd:s,stdio:["pipe","pipe","pipe"],timeout:18e4})}catch(P){let g=P instanceof Error&&"stderr"in P?String(P.stderr).slice(-1500):"",f=P instanceof Error&&"stdout"in P?String(P.stdout).slice(-1500):"",S=(g+`
|
|
315
|
-
`+f).trim();return p(`Build failed. Fix the errors before previewing:
|
|
316
|
-
|
|
317
|
-
${S}`,!0)}let m=yo("npx",["next","start","-p",String(e)],{cwd:s,detached:!0,stdio:["ignore","pipe","pipe"]}),d="";if(m.stderr?.on("data",P=>{d+=P.toString()}),m.pid&&(m.unref(),a.push({pid:m.pid,type:"dev-server",port:e,startedAt:new Date().toISOString()}),cn(a)),!await _o(e,15e3,500)){let P=d?`Server failed to start:
|
|
318
|
-
|
|
319
|
-
${d.slice(-1e3)}`:"Server failed to start within 15s. Check for runtime errors.";return p(P,!0)}}let c=`http://localhost:${e}`,u=JSON.stringify({localUrl:c,message:`Preview is live at ${c}. Run mist_deploy to get your permanent URL on mistflow.app.`});return Ne(c,u)}};import{resolve as Io,join as Co}from"path";import{existsSync as To,readFileSync as Eo}from"fs";var Ft="test@mistflow.dev",dn="MistflowTest123!",Ao="Test User";function mn(n){let t=Co(n,"mistflow.json");if(!To(t))return null;try{return JSON.parse(Eo(t,"utf-8"))}catch{return null}}function No(n){let t=JSON.stringify(n).toLowerCase();return["auth","login","sign-in","sign-up","signin","signup"].some(s=>t.includes(s))}function jo(n){let t=n.plan;if(!t?.steps)return[];let s=new Set;for(let i of t.steps)if(i.pages)for(let l of i.pages)s.add(l);let e=new Set(["login","signup","sign-in","sign-up","signin","signout","sign-out","logout"]);return[...s].filter(i=>!e.has(i.toLowerCase())).slice(0,3)}function Do(n){let t=n.deploy;return t?.url?t.url:typeof n.deployUrl=="string"?n.deployUrl:null}async function fn(n){let t=Io(n.projectPath??process.cwd()),s=n.verifyUrl,e=null;if(s)e=mn(t);else{if(e=mn(t),!e)return p("No mistflow.json found. Run mist_deploy first, or pass verifyUrl explicitly.",!0);if(s=Do(e)??void 0,!s)return p("No deploy URL found in mistflow.json. Deploy your app first with mist_deploy.",!0)}s.startsWith("http")||(s=`https://${s}`);let i=e?No(e):!1,l=e?jo(e):[],o;try{o=await Qe()}catch{return p("Browser not available. Install Playwright to use verify: npx playwright install chromium",!0)}let r={verified:!0,url:s,authTested:!1,testUser:null,pages:[],message:""},a=[];try{await o.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),await o.waitForLoadState("networkidle").catch(()=>{})}catch{return r.verified=!1,r.message=`Could not load ${s}. The app may not be deployed yet or the URL may be incorrect.`,p(JSON.stringify(r),!0)}let c=await o.title(),u=c.toLowerCase().includes("error")||c.toLowerCase().includes("404")?"error":"ok";r.pages.push({path:"/",status:u,title:c});try{let g=await me(o,!1);a.push({data:g.toString("base64"),mimeType:"image/png"})}catch{}if(u==="error"&&(r.verified=!1),i)try{(await o.evaluate(async f=>{let S=await fetch("/api/auth/sign-up/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(f)});return{status:S.status,ok:S.ok}},{email:Ft,password:dn,name:Ao})).ok||await o.evaluate(async f=>{let S=await fetch("/api/auth/sign-in/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(f)});return{status:S.status,ok:S.ok}},{email:Ft,password:dn}),await o.reload({waitUntil:"domcontentloaded",timeout:15e3}),await o.waitForLoadState("networkidle").catch(()=>{}),r.authTested=!0,r.testUser={email:Ft};try{let f=await me(o,!1);a.push({data:f.toString("base64"),mimeType:"image/png"})}catch{}}catch{console.error("[verify] Auth test failed, continuing with public pages")}for(let g of l){let f=g.startsWith("/")?g:`/${g}`;try{await o.goto(`${s}${f}`,{waitUntil:"domcontentloaded",timeout:15e3}),await o.waitForLoadState("networkidle").catch(()=>{});let S=await o.title(),B=S.toLowerCase().includes("404")||S.toLowerCase().includes("not found")?"error":"ok";r.pages.push({path:f,status:B,title:S}),B==="error"&&(r.verified=!1);try{let W=await me(o,!1);a.push({data:W.toString("base64"),mimeType:"image/png"})}catch{}}catch{r.pages.push({path:f,status:"error",title:"Timeout"}),r.verified=!1}}let m=r.pages.filter(g=>g.status==="ok").length,d=r.pages.length,b=r.authTested?" Auth login successful.":"";r.message=r.verified?`Verified ${m}/${d} pages.${b} App looks good.`:`Verified ${m}/${d} pages \u2014 some issues found.${b} Check screenshots.`;let P=[{type:"text",text:JSON.stringify(r)}];for(let g of a)P.push({type:"image",data:g.data,mimeType:g.mimeType});return{content:P}}var Mo=Ie.object({action:Ie.enum(["deploy","promote","preview","redeploy","rollback","verify"]).describe("'deploy' builds and deploys to Mistflow Cloud (auto-redirects to preview if staging mode is enabled). 'promote' promotes a verified preview to production (~10s, reuses the preview build). 'preview' builds the app and starts a local production server on localhost:3000. 'redeploy' re-deploys the latest build without rebuilding. 'rollback' reverts to a specific previous deployment. 'verify' navigates the deployed app, tests auth, and screenshots pages to confirm it works."),projectPath:Ie.string().optional().describe("Path to the project directory (default: cwd)"),environment:Ie.enum(["production","preview"]).optional().describe("(deploy) Target environment. Defaults to 'production'. Projects with staging mode auto-redirect to preview."),forceSchema:Ie.boolean().optional().describe("(deploy) Reset the database structure. Warning: this deletes existing data. Only use when the user confirms this is OK."),deploymentId:Ie.string().optional().describe("(rollback/promote) Deployment ID to rollback to, or preview deployment ID to promote"),verifyUrl:Ie.string().optional().describe("(verify) URL to verify. Defaults to deploy URL from mistflow.json")}),gn={name:"mist_deploy",description:"STEP 4 (final) of the Mistflow workflow. Deploy a Mistflow project. Call this AFTER all mist_build implement steps are done. If the project has staging mode enabled (configurable in dashboard), the tool automatically deploys to preview first with an isolated database. After QA passes, call action='promote' to go live (~10s, reuses the same build artifact). Actions: 'deploy' (default), 'promote' (promote preview to production), 'preview' (local dev server), 'redeploy', 'rollback', 'verify'. The workflow is: mist_plan \u2192 mist_build init \u2192 implement (repeat) \u2192 mist_build build \u2192 mist_deploy \u2192 mist_build qa.",inputSchema:Mo,handler:async(n,t)=>{let s=n;switch(s.action){case"deploy":return Xs({projectPath:s.projectPath,environment:s.environment??"production",forceSchema:s.forceSchema},t);case"promote":return tn({projectPath:s.projectPath,deploymentId:s.deploymentId});case"preview":return un.handler({projectPath:s.projectPath});case"redeploy":return zt({projectPath:s.projectPath,action:"redeploy"});case"rollback":return zt({projectPath:s.projectPath,action:"rollback",deploymentId:s.deploymentId});case"verify":return fn({projectPath:s.projectPath,verifyUrl:s.verifyUrl});default:return p(`Unknown action: ${s.action}. Use deploy, promote, preview, redeploy, rollback, or verify.`,!0)}}};import{z}from"zod";import{resolve as ft}from"path";import{existsSync as gt,readFileSync as ht}from"fs";import{join as yt}from"path";import{z as we}from"zod";import{resolve as $o,join as hn}from"path";import{existsSync as Uo,readFileSync as yn,writeFileSync as Oo}from"fs";var Lo=we.object({action:we.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:we.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:we.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:we.object({key:we.string(),description:we.string().optional(),setupUrl:we.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),bn={name:"mist_state",description:"Read or update project state in mistflow.json. Use action='get' to load plan progress, env var status, and deploy info. Use action='update' to mark plan steps complete or add required env vars. Use when the user says 'mist status', 'mist state', or 'mist update state'.",inputSchema:Lo,handler:async n=>{let t=n,s=$o(t.projectPath??process.cwd()),e=hn(s,"mistflow.json");if(!Uo(e))return ce(s);let i;try{i=JSON.parse(yn(e,"utf-8"))}catch{return p("Failed to parse mistflow.json.",!0)}if(t.action==="get"){if(!i.projectId)try{let{ensureBackendRegistered:f}=await import("./self-heal-J7EGYRN4.js");await f(s)&&(i=JSON.parse(yn(e,"utf-8")))}catch{}let r=i.plan,a=r?.steps?.filter(f=>f.status==="completed").length??0,c=r?.steps?.length??0,u=dt(hn(s,".env.local")),m=i.env?.required?Object.entries(i.env.required).map(([f,S])=>({name:f,description:S?.description,configured:u.has(f)})):[];i.projectId&&import("./state-manager-QMSYKJ37.js").then(({fetchRemoteState:f})=>f(i.projectId)).catch(()=>{});let d=[`Project: ${i.name}`];if(r){d.push(`Plan: ${r.summary??r.name??"unnamed"} \u2014 ${a}/${c} steps complete`);for(let f of r.steps){let S=f.status==="completed"?"\u2713":f.status==="in_progress"?"\u2192":" ";d.push(` [${S}] ${f.number}. ${f.name}`)}}let b=m.filter(f=>!f.configured);b.length>0&&d.push(`Missing env vars: ${b.map(f=>f.name).join(", ")}`),i.deploy?.url?d.push(`Deployed: ${i.deploy.url} (${i.deploy.count??0} deploys)`):d.push("Not deployed yet");let P=[],g=r?.steps?.find(f=>f.status!=="completed");return g?P.push(`NEXT: Call mist_build with action='implement' to work on step ${g.number} (${g.name}).`):r&&a===c&&(i.deploy?.url||P.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT ask the user \u2014 just deploy.")),b.length>0&&P.push(`Missing env vars in .env.local: ${b.map(f=>f.name).join(", ")}`),p(JSON.stringify({name:i.name,projectId:i.projectId,planProgress:r?{name:r.name,summary:r.summary,totalSteps:c,completedSteps:a,steps:r.steps}:null,envStatus:m,deploy:i.deploy??null,contextMessage:d.join(`
|
|
320
|
-
`),nextSteps:P}))}let l=[];if(t.completedStep!==void 0){let r=i.plan;if(r?.steps){let a=r.steps.findIndex(c=>c.number===t.completedStep);if(a===-1)return p(`Step ${t.completedStep} not found in the plan.`,!0);r.steps[a].status="completed",l.push(`Step ${t.completedStep} marked as completed`)}}t.addEnvVar&&(i.env||(i.env={required:{}}),i.env.required||(i.env.required={}),i.env.required[t.addEnvVar.key]={description:t.addEnvVar.description,setupUrl:t.addEnvVar.setupUrl},l.push(`Added required env var: ${t.addEnvVar.key}`)),Oo(e,JSON.stringify(i,null,2)+`
|
|
321
|
-
`),i.projectId&&import("./state-manager-QMSYKJ37.js").then(async({readLocalState:r,syncRemoteState:a})=>{let c=r(s);c&&await a(i.projectId,c)}).catch(()=>{});let o=[];if(t.completedStep!==void 0){let a=i.plan?.steps?.find(c=>c.status!=="completed");a?o.push(`NEXT: Call mist_build with action='implement' to work on step ${a.number} (${a.name}). Do this now.`):o.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT suggest localhost.")}return t.addEnvVar&&(o.push(`Add ${t.addEnvVar.key} to your .env.local file`),t.addEnvVar.setupUrl&&o.push(`Get the value from: ${t.addEnvVar.setupUrl}`)),p(JSON.stringify({updated:!0,changes:l,message:l.length>0?`Project state saved. ${l.join(". ")}.`:"No changes made.",nextSteps:o.length>0?o:void 0}))}};var Bt={"resend-email":{description:"Transactional email with React Email templates and webhook handling.",tags:["email","transactional","welcome","notification","invite","alert"],envVars:[{key:"RESEND_API_KEY",description:"Resend API key for sending emails",setupUrl:"https://resend.com/api-keys"}],docsUrl:"https://resend.com/docs/send-with-nextjs",packages:["resend","@react-email/components"],difficulty:"easy"},"r2-storage":{description:"File uploads with drag-and-drop UI, stored in Mistflow Cloud.",tags:["storage","upload","file","image","media","attachment","avatar"],envVars:[],docsUrl:"https://developers.cloudflare.com/r2/",packages:[],difficulty:"easy"},"openai-ai":{description:"AI-powered features with OpenAI SDK, streaming chat, and content generation.",tags:["ai","openai","chatbot","gpt","llm","assistant","generation"],envVars:[{key:"OPENAI_API_KEY",description:"OpenAI API key for AI features",setupUrl:"https://platform.openai.com/api-keys"}],docsUrl:"https://platform.openai.com/docs/guides/text-generation",packages:["openai","ai"],difficulty:"medium"},"anthropic-ai":{description:"AI features with the Anthropic SDK, streaming Claude chat, and content generation.",tags:["ai","anthropic","claude","llm","assistant","generation"],envVars:[{key:"ANTHROPIC_API_KEY",description:"Anthropic API key for Claude",setupUrl:"https://console.anthropic.com/settings/keys"}],docsUrl:"https://docs.anthropic.com/en/docs/initial-setup",packages:["@anthropic-ai/sdk","ai"],difficulty:"medium"},"openrouter-ai":{description:"AI model router with access to 200+ models (GPT, Claude, Llama, Mistral, etc.) through one API.",tags:["ai","openrouter","llm","multi-model","claude","gpt","llama","mistral"],envVars:[{key:"OPENROUTER_API_KEY",description:"OpenRouter API key",setupUrl:"https://openrouter.ai/keys"}],docsUrl:"https://openrouter.ai/docs/quickstart",packages:["@openrouter/sdk"],difficulty:"easy"},"stripe-payments":{description:"Payment processing with Stripe Checkout, webhooks, and billing portal.",tags:["payments","stripe","billing","subscription","checkout","invoice"],envVars:[{key:"STRIPE_SECRET_KEY",description:"Stripe secret key",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_PUBLISHABLE_KEY",description:"Stripe publishable key (client-side)",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_WEBHOOK_SECRET",description:"Stripe webhook signing secret",setupUrl:"https://dashboard.stripe.com/webhooks"}],docsUrl:"https://docs.stripe.com/checkout/quickstart",packages:["stripe","@stripe/stripe-js"],difficulty:"advanced"},"elevenlabs-voice":{description:"Text-to-speech and voice generation with ElevenLabs API.",tags:["voice","tts","speech","audio","elevenlabs","narration","podcast"],envVars:[{key:"ELEVENLABS_API_KEY",description:"ElevenLabs API key for voice generation",setupUrl:"https://elevenlabs.io/app/settings/api-keys"}],docsUrl:"https://elevenlabs.io/docs/api-reference/text-to-speech",packages:["elevenlabs"],difficulty:"medium"},"google-maps":{description:"Google Maps embed, Places autocomplete, and geolocation features.",tags:["maps","location","google","places","geocoding","directions","nearby"],envVars:[{key:"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY",description:"Google Maps API key (client-side)",setupUrl:"https://console.cloud.google.com/apis/credentials"}],docsUrl:"https://developers.google.com/maps/documentation/javascript",packages:["@googlemaps/js-api-loader"],difficulty:"medium"},"twilio-sms":{description:"SMS notifications, OTP verification, and phone number validation.",tags:["sms","twilio","otp","phone","verification","text-message"],envVars:[{key:"TWILIO_ACCOUNT_SID",description:"Twilio account SID",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_AUTH_TOKEN",description:"Twilio auth token",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_PHONE_NUMBER",description:"Twilio phone number for sending SMS",setupUrl:"https://console.twilio.com/us1/develop/phone-numbers/manage/incoming"}],docsUrl:"https://www.twilio.com/docs/messaging/quickstart/node",packages:["twilio"],difficulty:"medium"},"posthog-analytics":{description:"Product analytics with event tracking, feature flags, and session replay.",tags:["analytics","posthog","tracking","funnel","event","feature-flag"],envVars:[{key:"NEXT_PUBLIC_POSTHOG_KEY",description:"PostHog project API key",setupUrl:"https://app.posthog.com/project/settings"},{key:"NEXT_PUBLIC_POSTHOG_HOST",description:"PostHog instance URL (default: https://us.i.posthog.com)",setupUrl:"https://app.posthog.com/project/settings"}],docsUrl:"https://posthog.com/docs/libraries/next-js",packages:["posthog-js","posthog-node"],difficulty:"easy"},"firecrawl-scraping":{description:"Web scraping and crawling with markdown output, structured extraction, and async crawls.",tags:["scraping","crawl","firecrawl","web","extract","rag","markdown"],envVars:[{key:"FIRECRAWL_API_KEY",description:"Firecrawl API key",setupUrl:"https://firecrawl.dev"}],docsUrl:"https://docs.firecrawl.dev/sdks/node",packages:["@mendable/firecrawl-js"],difficulty:"easy"},"replicate-media":{description:"Image and video generation with 200+ AI models (Flux, Wan Video, Runway, SDXL, etc.) through one API.",tags:["image","video","replicate","flux","sdxl","generation","media","avatar","thumbnail"],envVars:[{key:"REPLICATE_API_TOKEN",description:"Replicate API token",setupUrl:"https://replicate.com/account/api-tokens"}],docsUrl:"https://replicate.com/docs/get-started/nodejs",packages:["replicate"],difficulty:"medium"}},Ue=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
|
|
20
|
+
`)}function Ee(a){return a.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ae(a){return(a?D(a):v).map(t=>{let r=M[t.id];return{id:t.id,slug:Ee(t.title),title:t.title,category:t.category,description:r?.description??"",tags:r?.tags??[],theme:r?.theme??"dark",colors:r?.colors??[],style:r?.style??""}})}var q={"resend-email":{description:"Transactional email with React Email templates and webhook handling.",tags:["email","transactional","welcome","notification","invite","alert"],envVars:[{key:"RESEND_API_KEY",description:"Resend API key for sending emails",setupUrl:"https://resend.com/api-keys"}],docsUrl:"https://resend.com/docs/send-with-nextjs",packages:["resend","@react-email/components"],difficulty:"easy"},"r2-storage":{description:"File uploads with drag-and-drop UI, stored in Mistflow Cloud.",tags:["storage","upload","file","image","media","attachment","avatar"],envVars:[],docsUrl:"https://developers.cloudflare.com/r2/",packages:[],difficulty:"easy"},"openai-ai":{description:"AI-powered features with OpenAI SDK, streaming chat, and content generation.",tags:["ai","openai","chatbot","gpt","llm","assistant","generation"],envVars:[{key:"OPENAI_API_KEY",description:"OpenAI API key for AI features",setupUrl:"https://platform.openai.com/api-keys"}],docsUrl:"https://platform.openai.com/docs/guides/text-generation",packages:["openai","ai"],difficulty:"medium"},"anthropic-ai":{description:"AI features with the Anthropic SDK, streaming Claude chat, and content generation.",tags:["ai","anthropic","claude","llm","assistant","generation"],envVars:[{key:"ANTHROPIC_API_KEY",description:"Anthropic API key for Claude",setupUrl:"https://console.anthropic.com/settings/keys"}],docsUrl:"https://docs.anthropic.com/en/docs/initial-setup",packages:["@anthropic-ai/sdk","ai"],difficulty:"medium"},"openrouter-ai":{description:"AI model router with access to 200+ models (GPT, Claude, Llama, Mistral, etc.) through one API.",tags:["ai","openrouter","llm","multi-model","claude","gpt","llama","mistral"],envVars:[{key:"OPENROUTER_API_KEY",description:"OpenRouter API key",setupUrl:"https://openrouter.ai/keys"}],docsUrl:"https://openrouter.ai/docs/quickstart",packages:["@openrouter/sdk"],difficulty:"easy"},"stripe-payments":{description:"Payment processing with Stripe Checkout, webhooks, and billing portal.",tags:["payments","stripe","billing","subscription","checkout","invoice"],envVars:[{key:"STRIPE_SECRET_KEY",description:"Stripe secret key",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_PUBLISHABLE_KEY",description:"Stripe publishable key (client-side)",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_WEBHOOK_SECRET",description:"Stripe webhook signing secret",setupUrl:"https://dashboard.stripe.com/webhooks"}],docsUrl:"https://docs.stripe.com/checkout/quickstart",packages:["stripe","@stripe/stripe-js"],difficulty:"advanced"},"elevenlabs-voice":{description:"Text-to-speech and voice generation with ElevenLabs API.",tags:["voice","tts","speech","audio","elevenlabs","narration","podcast"],envVars:[{key:"ELEVENLABS_API_KEY",description:"ElevenLabs API key for voice generation",setupUrl:"https://elevenlabs.io/app/settings/api-keys"}],docsUrl:"https://elevenlabs.io/docs/api-reference/text-to-speech",packages:["elevenlabs"],difficulty:"medium"},"google-maps":{description:"Google Maps embed, Places autocomplete, and geolocation features.",tags:["maps","location","google","places","geocoding","directions","nearby"],envVars:[{key:"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY",description:"Google Maps API key (client-side)",setupUrl:"https://console.cloud.google.com/apis/credentials"}],docsUrl:"https://developers.google.com/maps/documentation/javascript",packages:["@googlemaps/js-api-loader"],difficulty:"medium"},"twilio-sms":{description:"SMS notifications, OTP verification, and phone number validation.",tags:["sms","twilio","otp","phone","verification","text-message"],envVars:[{key:"TWILIO_ACCOUNT_SID",description:"Twilio account SID",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_AUTH_TOKEN",description:"Twilio auth token",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_PHONE_NUMBER",description:"Twilio phone number for sending SMS",setupUrl:"https://console.twilio.com/us1/develop/phone-numbers/manage/incoming"}],docsUrl:"https://www.twilio.com/docs/messaging/quickstart/node",packages:["twilio"],difficulty:"medium"},"posthog-analytics":{description:"Product analytics with event tracking, feature flags, and session replay.",tags:["analytics","posthog","tracking","funnel","event","feature-flag"],envVars:[{key:"NEXT_PUBLIC_POSTHOG_KEY",description:"PostHog project API key",setupUrl:"https://app.posthog.com/project/settings"},{key:"NEXT_PUBLIC_POSTHOG_HOST",description:"PostHog instance URL (default: https://us.i.posthog.com)",setupUrl:"https://app.posthog.com/project/settings"}],docsUrl:"https://posthog.com/docs/libraries/next-js",packages:["posthog-js","posthog-node"],difficulty:"easy"},"firecrawl-scraping":{description:"Web scraping and crawling with markdown output, structured extraction, and async crawls.",tags:["scraping","crawl","firecrawl","web","extract","rag","markdown"],envVars:[{key:"FIRECRAWL_API_KEY",description:"Firecrawl API key",setupUrl:"https://firecrawl.dev"}],docsUrl:"https://docs.firecrawl.dev/sdks/node",packages:["@mendable/firecrawl-js"],difficulty:"easy"},"replicate-media":{description:"Image and video generation with 200+ AI models (Flux, Wan Video, Runway, SDXL, etc.) through one API.",tags:["image","video","replicate","flux","sdxl","generation","media","avatar","thumbnail"],envVars:[{key:"REPLICATE_API_TOKEN",description:"Replicate API token",setupUrl:"https://replicate.com/account/api-tokens"}],docsUrl:"https://replicate.com/docs/get-started/nodejs",packages:["replicate"],difficulty:"medium"}},b=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
|
|
322
21
|
|
|
323
22
|
### File Structure
|
|
324
23
|
\`\`\`
|
|
@@ -1738,30 +1437,16 @@ export function ImageGenerator() {
|
|
|
1738
1437
|
5. **Replicate charges per prediction.** Flux Schnell is ~$0.003/image. Flux Pro is ~$0.05/image. Video models are $0.05-$0.50/generation. Show generation cost to users.
|
|
1739
1438
|
6. **Cache generated images.** Store output URLs in your database. Replicate URLs expire after a few hours. Download and re-host on R2 for permanent storage.
|
|
1740
1439
|
7. **Network I/O does NOT count as CPU time on Workers.** Image generation wait time is all network I/O.
|
|
1741
|
-
8. **Never ask the user to paste REPLICATE_API_TOKEN in chat.** Direct them to set it in the Mistflow dashboard (Project Settings > Environment Variables).`}];function
|
|
1440
|
+
8. **Never ask the user to paste REPLICATE_API_TOKEN in chat.** Direct them to set it in the Mistflow dashboard (Project Settings > Environment Variables).`}];function je(a){return a.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ie(a){let e=b.find(r=>r.id===a);if(e)return e;let t=a.toLowerCase().replace(/[^a-z0-9]/g,"");return b.find(r=>{let s=r.name.toLowerCase().replace(/[^a-z0-9]/g,"");return s===t||s.includes(t)||t.includes(s)})}function le(a){return q[a]}function $(a){return a?b.filter(e=>e.category.toLowerCase()===a.toLowerCase()):b}function ce(a){let e=a??b,t={};for(let s of e){t[s.category]||(t[s.category]=[]);let l=q[s.id],o=l?` \u2014 ${l.description}`:"",n=l?.packages.length?` (${l.packages.join(", ")})`:"";t[s.category].push(`${s.id} \u2014 "${s.name}"${o}${n}`)}let r=[];for(let[s,l]of Object.entries(t))r.push(`**${s}**:
|
|
1742
1441
|
${l.map(o=>` - ${o}`).join(`
|
|
1743
|
-
`)}`);return
|
|
1442
|
+
`)}`);return r.join(`
|
|
1744
1443
|
|
|
1745
|
-
`)}function
|
|
1444
|
+
`)}function pe(a){return(a?$(a):b).map(t=>{let r=q[t.id];return{id:t.id,slug:je(t.name),name:t.name,category:t.category,description:r?.description??"",tags:r?.tags??[],envVars:r?.envVars??[],docsUrl:r?.docsUrl??"",packages:r?.packages??[],difficulty:r?.difficulty??"medium"}})}var Ne=m.object({action:m.enum(["get","update"]).default("get").describe("'get' reads current project state (context oracle \u2014 call before making decisions in an existing project). 'update' marks steps complete or adds env vars. All other project queries moved to the CLI in MCP 0.6.0: `mist projects share`, `mist projects errors`, `mist projects logs`, `mist projects deployments`, `mist projects version`, `mist projects designs`, `mist projects app-styles`, `mist projects integrations`."),projectPath:m.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:m.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:m.object({key:m.string(),description:m.string().optional(),setupUrl:m.string().optional()}).optional().describe("(update) Add a required env var to the project manifest"),templateDescription:m.string().optional().describe("(share) Short description of what this template builds"),category:m.string().optional().describe("(landing-designs) Filter by category"),presetId:m.string().optional().describe("(landing-designs) Get full details for a specific landing design by ID"),integrationId:m.string().optional().describe("(integrations) Get full details for a specific integration preset by ID (e.g. 'stripe-payments', 'resend-email', 'elevenlabs-voice')"),period:m.string().optional().describe("(errors) Time period for errors: '1h', '24h', '7d' (default: '7d')"),deploymentId:m.string().optional().describe("(logs) Deployment ID to fetch logs for. If omitted, fetches logs for the latest deployment.")}),de={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info. 'update' marks plan steps complete or adds env vars (note: mist_build implement auto-marks the previous step, so manual updates are rarely needed). 'share' makes the project a forkable template with a shareable URL. 'landing-designs' lists curated landing page hero designs \u2014 pass an ID to mist_plan's landingDesign field to apply it. 'integrations' lists third-party service integration blueprints (Stripe, Resend, ElevenLabs, OpenAI, Twilio, etc.) \u2014 these are auto-injected during implementation when the plan includes matching integration steps. 'errors' fetches runtime errors from the live deployed app (same data shown on the dashboard). 'logs' fetches deploy logs for a deployment (phase-by-phase progress with error details). 'deployments' lists deployment history with status and error messages.",inputSchema:Ne,handler:async a=>{let e=a;if(["share","errors","logs","deployments"].includes(e.action)&&!H())return i("You need to sign in first. Run mist_setup to connect your account.",!0);switch(e.action){case"get":case"update":return oe.handler({action:e.action,projectPath:e.projectPath,completedStep:e.completedStep,addEnvVar:e.addEnvVar});case"share":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first to register it.",!0);try{let n=await z(o,{isTemplate:!0,description:e.templateDescription});return i(JSON.stringify({shareUrl:n.share_url,shareToken:n.share_token,message:`Your project is now a shareable template!
|
|
1746
1445
|
|
|
1747
|
-
Anyone can fork it: ${
|
|
1446
|
+
Anyone can fork it: ${n.share_url}
|
|
1748
1447
|
|
|
1749
1448
|
Others can use it in their AI editor:
|
|
1750
|
-
"build me something like ${r.share_url}"`}))}catch(r){let a=r instanceof Error?r.message:"Failed to share project";return p(a,!0)}}case"landing-designs":{if(t.presetId){let o=it(t.presetId);if(!o)return p(`Preset '${t.presetId}' not found. Use mist_project action='presets' without presetId to list all available presets.`,!0);let r=Ms(t.presetId);return p(JSON.stringify({preset:{id:o.id,title:o.title,category:o.category,description:r?.description??"",style:r?.style??"",theme:r?.theme??"dark",colors:r?.colors??[],tags:r?.tags??[],promptLength:o.prompt.length},message:`Landing design "${o.title}" (${o.category}) \u2014 ${r?.description??""}. To use it, pass landingDesign="${o.id}" when calling mist_plan.`}))}let e=Us(t.category??void 0),i=jt(t.category),l=$s(i);return p(JSON.stringify({count:e.length,presets:e.map(o=>({id:o.id,title:o.title,category:o.category,description:o.description,style:o.style,theme:o.theme,colors:o.colors})),formatted:l,message:`${e.length} landing designs available.${t.category?` Filtered by: ${t.category}.`:""} To use one, pass landingDesign="<id>" when calling mist_plan. The design blueprint will be injected during the landing page implementation step. Browse them at ${Gt()}/designs?tab=landing-designs.`}))}case"integrations":{if(t.integrationId){let o=wn(t.integrationId);if(!o)return p(`Integration '${t.integrationId}' not found. Use mist_project action='integrations' without integrationId to list all available integrations.`,!0);let r=vn(o.id);return p(JSON.stringify({integration:{id:o.id,name:o.name,category:o.category,description:r?.description??"",packages:r?.packages??[],envVars:r?.envVars??[],docsUrl:r?.docsUrl??"",difficulty:r?.difficulty??"medium"},message:`Integration "${o.name}" (${o.category}) \u2014 ${r?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${r?.envVars?.map(a=>a.key).join(", ")||"none"}. Docs: ${r?.docsUrl??"n/a"}.`}))}let e=Sn(t.category??void 0),i=Ht(t.category??void 0),l=kn(i);return p(JSON.stringify({count:e.length,integrations:e.map(o=>({id:o.id,name:o.name,category:o.category,description:o.description,packages:o.packages,difficulty:o.difficulty,envVars:o.envVars.map(r=>r.key)})),formatted:l,message:`${e.length} integration blueprints available.${t.category?` Filtered by: ${t.category}.`:""} Integration blueprints are auto-injected during implementation when your plan includes a matching integration step. Use integrationId to see full details including env vars and setup URLs.`}))}case"errors":{let e=ft(t.projectPath??process.cwd()),i=yt(e,"mistflow.json");if(!gt(i))return ce(e);let l;try{l=JSON.parse(ht(i,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return p("No project ID found. Deploy the project first.",!0);try{let r=await cs(o,t.period??"7d");return r.total===0?p(JSON.stringify({total:0,period:r.period,message:`No runtime errors in the last ${r.period}. The app is running clean.`})):p(JSON.stringify({total:r.total,period:r.period,errors:r.errors,message:`${r.total} runtime error(s) in the last ${r.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(r){let a=r instanceof Error?r.message:"Failed to fetch errors";return p(a,!0)}}case"logs":{let e=ft(t.projectPath??process.cwd()),i=yt(e,"mistflow.json");if(!gt(i))return ce(e);let l;try{l=JSON.parse(ht(i,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return p("No project ID found. Deploy the project first.",!0);let r=t.deploymentId;if(!r)try{let a=await ze(o);if(a.length===0)return p("No deployments found for this project.",!0);r=a[0].id}catch(a){let c=a instanceof Error?a.message:"Failed to fetch deployments";return p(c,!0)}try{let[a,c]=await Promise.all([ls(r),fe(r)]),u=a.filter(d=>d.level==="error"),m=a.filter(d=>d.level==="warn");return p(JSON.stringify({deploymentId:r,status:c.status,errorMessage:c.error??null,totalLogs:a.length,errorCount:u.length,warnCount:m.length,logs:a.map(d=>({time:d.timestamp,level:d.level,phase:d.phase,message:d.message})),message:c.status==="failed"?`Deployment failed. ${u.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${c.status}. ${a.length} log entries (${u.length} errors, ${m.length} warnings).`}))}catch(a){let c=a instanceof Error?a.message:"Failed to fetch deploy logs";return p(c,!0)}}case"deployments":{let e=ft(t.projectPath??process.cwd()),i=yt(e,"mistflow.json");if(!gt(i))return ce(e);let l;try{l=JSON.parse(ht(i,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return p("No project ID found. Deploy the project first.",!0);try{let r=await ze(o);return p(JSON.stringify({total:r.length,deployments:r.map(a=>({id:a.id,status:a.status,errorMessage:a.error_message,durationSeconds:a.duration_seconds,isRollback:!!a.rollback_from_id,createdAt:a.created_at})),message:`${r.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(r){let a=r instanceof Error?r.message:"Failed to fetch deployments";return p(a,!0)}}case"version":{qe().backendSignalReceived||await Xt();let e=qe(),i=e.severity==="none",l={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"UNSUPPORTED \u2014 upgrade required"};return p(JSON.stringify({current:e.current,latest:e.latest||"unknown",minSupported:e.minSupported||"unknown",severity:e.severity,upToDate:i,upgradeCmd:e.upgradeCmd,changelogUrl:e.changelogUrl,backendSignalReceived:e.backendSignalReceived,message:e.backendSignalReceived?`Mistflow MCP ${e.current} (${l[e.severity]??e.severity}). Latest: ${e.latest}.${i?"":` Run \`${e.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${e.current}. The backend hasn't replied yet \u2014 make one other API call (e.g. mist_project action='get') then retry to see the latest version.`}))}default:return p(`Unknown action: ${t.action}. Use get, update, share, landing-designs, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as ee}from"zod";import{z as ve}from"zod";import{resolve as Fo}from"path";var Fl=ve.object({projectPath:ve.string().optional().describe("Path to the project directory (default: cwd)"),action:ve.enum(["set","list","delete"]).describe("Action to perform"),key:ve.string().optional().describe("Environment variable name (required for 'set' and 'delete')"),value:ve.string().optional().describe("Environment variable value (required for 'set')"),category:ve.string().optional().describe("Category for the env var (default: 'custom')"),description:ve.string().optional().describe("Description of what this env var is for"),setupUrl:ve.string().optional().describe("URL where the user can obtain this value (e.g. Stripe dashboard)")});async function _n(n){let{projectPath:t,action:s,key:e,value:i,category:l,description:o,setupUrl:r}=n,a=Fo(t??process.cwd());if(!J())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let u=Z(a)?.projectId;if(!u)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(s){case"set":return e?i?(await is(u,e,i,{category:l,description:o,setupUrl:r}),p(JSON.stringify({set:!0,key:e,message:`Environment variable '${e}' has been set. It will be available on your next deployment.`}))):p("Value is required. Provide the env var value.",!0):p("Key is required. Provide the env var name like 'STRIPE_SECRET_KEY'.",!0);case"list":{let m=await os(u);return m.length===0?p(JSON.stringify({envVars:[],message:"No environment variables configured. Use action 'set' to add one."})):p(JSON.stringify({envVars:m.map(d=>({key:d.key,category:d.category,description:d.description,hasValue:d.has_value})),message:`${m.length} environment variable(s) configured.`}))}case"delete":return e?(await as(u,e),p(JSON.stringify({deleted:!0,key:e,message:`Environment variable '${e}' has been removed.`}))):p("Key is required. Provide the env var name to delete.",!0);default:return p(`Unknown action: ${s}. Use set, list, or delete.`,!0)}}catch(m){if(m instanceof H)return p(m.message,!0);let d=m instanceof Error?m.message:"An unexpected error occurred";return p(d,!0)}}import{z as We}from"zod";import{resolve as Bo,join as Ho}from"path";import{existsSync as Vo,readFileSync as Wo,writeFileSync as Ko}from"fs";var Yl=We.object({projectPath:We.string().optional().describe("Path to the project directory (default: cwd)"),action:We.enum(["add","list","verify","remove"]).describe("Action to perform"),domain:We.string().optional().describe("Domain name (required for 'add' and 'remove')"),domainId:We.string().optional().describe("Domain ID (required for 'verify' and 'remove')")});function Vt(n,t){let s=Ho(n,"mistflow.json");if(!Vo(s))return;let e;try{e=JSON.parse(Wo(s,"utf-8"))}catch{return}e.domains=t,Ko(s,JSON.stringify(e,null,2)+`
|
|
1751
|
-
`)}async function Pn(n){let{projectPath:t,action:s,domain:e,domainId:i}=n,l=Bo(t??process.cwd());if(!J())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let r=Z(l)?.projectId;if(!r)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(s){case"add":{if(!e)return p("Domain name is required. Provide the domain like 'myapp.com' or 'app.mycompany.com'.",!0);let a=await ss(r,e),c=await Pe(r);return Vt(l,c.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({added:!0,domain:a.domain,status:a.status,instructions:a.instructions,message:`Domain '${a.domain}' added. Set up DNS records as described, then use action 'verify' to check status.`}))}case"list":{let a=await Pe(r);return a.length===0?p(JSON.stringify({domains:[],message:"No custom domains configured. Use action 'add' to add one."})):p(JSON.stringify({domains:a.map(c=>({id:c.id,domain:c.domain,status:c.status,ssl:c.ssl_status,error:c.error_message}))}))}case"verify":{if(!i){if(e){let m=(await Pe(r)).find(d=>d.domain===e);if(m){let d=await vt(r,m.id);return p(JSON.stringify({domain:d.domain,status:d.status,ssl:d.ssl_status,error:d.error_message,message:d.status==="active"?`Domain '${d.domain}' is active and serving traffic.`:`Domain '${d.domain}' is ${d.status}. Make sure DNS records are configured correctly.`}))}return p(`Domain '${e}' not found. Use action 'list' to see configured domains.`,!0)}return p("Provide either domainId or domain name to verify.",!0)}let a=await vt(r,i),c=await Pe(r);return Vt(l,c.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({domain:a.domain,status:a.status,ssl:a.ssl_status,error:a.error_message,message:a.status==="active"?`Domain '${a.domain}' is active and serving traffic.`:`Domain '${a.domain}' is ${a.status}. Make sure DNS records are configured correctly.`}))}case"remove":{if(!i&&!e)return p("Provide either domainId or domain name to remove.",!0);let a=i;if(!a&&e){let m=(await Pe(r)).find(d=>d.domain===e);if(!m)return p(`Domain '${e}' not found.`,!0);a=m.id}await ns(r,a);let c=await Pe(r);return Vt(l,c.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({removed:!0,message:"Domain removed. It may take a few minutes for DNS changes to propagate."}))}default:return p(`Unknown action: ${s}. Use add, list, verify, or remove.`,!0)}}catch(a){if(a instanceof H)return p(a.message,!0);let c=a instanceof Error?a.message:"An unexpected error occurred";return p(c,!0)}}var Jo=ee.object({resource:ee.enum(["env","domain"]).describe("'env' manages app secrets and configuration values. 'domain' manages custom domains."),action:ee.string().describe("Action to perform. env: 'set', 'list', 'delete'. domain: 'add', 'list', 'verify', 'remove'."),projectPath:ee.string().optional().describe("Path to the project directory (default: cwd)"),key:ee.string().optional().describe("(env) Variable name"),value:ee.string().optional().describe("(env set) Variable value"),category:ee.string().optional().describe("(env set) Category"),description:ee.string().optional().describe("(env set) Description"),setupUrl:ee.string().optional().describe("(env set) URL to obtain the value"),domain:ee.string().optional().describe("(domain) Domain name"),domainId:ee.string().optional().describe("(domain) Domain ID")}),Rn={name:"mist_config",description:"Manage project configuration: app secrets and custom domains. Set resource='env' to manage encrypted app secrets (set, list, delete). Set resource='domain' to manage custom domains (add, list, verify, remove). Use when the user says 'mist env' or 'mist domain'.",inputSchema:Jo,handler:async n=>{let t=n;switch(t.resource){case"env":return _n({projectPath:t.projectPath,action:t.action,key:t.key,value:t.value,category:t.category,description:t.description,setupUrl:t.setupUrl});case"domain":return Pn({projectPath:t.projectPath,action:t.action,domain:t.domain,domainId:t.domainId});default:return p(`Unknown resource: ${t.resource}. Use env or domain.`,!0)}}};import{z as Ce}from"zod";var Go=Ce.object({action:Ce.enum(["navigate","go_back","go_forward","click","type","fill","select_option","press_key","hover","screenshot","snapshot"]).describe("Action to perform. Navigation: navigate|go_back|go_forward. Interaction: click|type|fill|select_option|press_key|hover. Visual: screenshot (returns image) | snapshot (returns accessibility tree)."),url:Ce.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:Ce.string().optional().describe("CSS selector of the target element. Required for: click, type, fill, select_option, hover. Optional for screenshot (captures just that element)."),value:Ce.string().optional().describe("Text to type/fill, option to select, or key to press (e.g. 'Enter', 'Tab'). Required for: type, fill, select_option, press_key."),fullPage:Ce.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:Ce.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),In={name:"mist_browser",description:"Unified browser tool for navigating, interacting with, and capturing the app. Use 'navigate' to open a URL, interaction actions (click/type/fill/etc.) to test flows, 'snapshot' to inspect the accessibility tree, and 'screenshot' for a visual capture. Use after mist_preview to verify UI, test flows, and iterate on design.",inputSchema:Go,handler:async n=>{let t=n,s=await Qe();if(t.action==="navigate"){if(!t.url)return p("URL is required for 'navigate'.",!0);let l=[],o=c=>{c.type()==="error"&&l.push(c.text())};s.on("console",o),await s.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await s.waitForLoadState("networkidle").catch(()=>{});let r=[],a=c=>r.push(c.message);if(s.on("pageerror",a),await s.waitForTimeout(500),s.removeListener("console",o),s.removeListener("pageerror",a),l.length>0||r.length>0){let c=await Xe(s),u=[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:c,consoleErrors:l,pageErrors:r,hasErrors:!0})}];if(t.includeScreenshot){let m=await me(s);u.push({type:"image",data:m.toString("base64"),mimeType:"image/png"})}return{content:u}}}else if(t.action==="go_back")await s.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="go_forward")await s.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="click"){if(!t.selector)return p("Selector is required for 'click'.",!0);await s.click(t.selector,{timeout:1e4}),await s.waitForLoadState("domcontentloaded").catch(()=>{}),await s.waitForTimeout(500)}else if(t.action==="type"){if(!t.selector)return p("Selector is required for 'type'.",!0);if(!t.value)return p("Value is required for 'type'.",!0);await s.type(t.selector,t.value,{delay:50})}else if(t.action==="fill"){if(!t.selector)return p("Selector is required for 'fill'.",!0);if(!t.value)return p("Value is required for 'fill'.",!0);await s.fill(t.selector,t.value)}else if(t.action==="select_option"){if(!t.selector)return p("Selector is required for 'select_option'.",!0);if(!t.value)return p("Value is required for 'select_option'.",!0);await s.selectOption(t.selector,t.value)}else if(t.action==="hover"){if(!t.selector)return p("Selector is required for 'hover'.",!0);await s.hover(t.selector,{timeout:1e4})}else if(t.action==="press_key"){if(!t.value)return p("Value is required for 'press_key' (e.g. 'Enter').",!0);await s.keyboard.press(t.value),await s.waitForLoadState("domcontentloaded").catch(()=>{}),await s.waitForTimeout(500)}else if(t.action==="screenshot"){t.url&&(await s.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await s.waitForLoadState("networkidle").catch(()=>{}));let l;if(t.selector){let o=await s.$(t.selector);if(!o)return p(`Element not found: ${t.selector}`,!0);l=await o.screenshot({type:"png"})}else l=await me(s,t.fullPage);return{content:[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),message:`Screenshot captured (${t.fullPage?"full page":"viewport"})`})},{type:"image",data:l.toString("base64"),mimeType:"image/png"}]}}else if(t.action==="snapshot"){let l=await Xe(s);return{content:[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:l})}]}}let e=await Xe(s),i=[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:e})}];if(t.includeScreenshot){let l=await me(s);i.push({type:"image",data:l.toString("base64"),mimeType:"image/png"})}return{content:i}}};import{existsSync as Y,readFileSync as Le,readdirSync as Qo,writeFileSync as Oe}from"fs";import{join as F}from"path";import{z as Ke}from"zod";function Yo(n){let t=[],s=0,e=n.length,i=!1,l=!1,o=!1;for(;s<e;){let r=n[s],a=n[s+1];if(l){r===`
|
|
1752
|
-
`&&(l=!1,t.push(r)),s++;continue}if(o){r==="*"&&a==="/"?(o=!1,s+=2):s++;continue}if(i){if(t.push(r),r==="\\"&&a!==void 0){t.push(a),s+=2;continue}r===i&&(i=!1),s++;continue}if(r==="/"&&a==="/"){l=!0,s+=2;continue}if(r==="/"&&a==="*"){o=!0,s+=2;continue}if(r==='"'||r==="'"||r==="`"){i=r,t.push(r),s++;continue}t.push(r),s++}return t.join("")}function Cn(n){if(!n||typeof n!="string")return[];let t;try{t=Yo(n)}catch{return[]}let s=/export\s+const\s+(\w+)\s*=\s*(?:pg|sqlite|mysql)Table\s*\(\s*(["'])([^"'`]+)\2/g,e=[],i=new Set,l;for(;(l=s.exec(t))!==null;){let o=l[1],r=l[3]??o;i.has(o)||(i.add(o),e.push({varName:o,tableName:r}))}return e}function Tn(n){let t=F(n,"mistflow.json");if(!Y(t))return{result:{name:"Project",status:"fail",message:"No mistflow.json found. This is not a Mistflow project.",fix:"Run mist_plan to design your app, then mist_build init to scaffold it."},config:null};let s;try{s=JSON.parse(Le(t,"utf-8"))}catch{return{result:{name:"Project",status:"fail",message:"mistflow.json exists but contains invalid JSON.",fix:"Check mistflow.json for syntax errors. If corrupted beyond repair, delete it and re-run mist_build init."},config:null}}if(!s.name||typeof s.name!="string")return{result:{name:"Project",status:"warn",message:"mistflow.json is missing the 'name' field."},config:s};let e=s.projectId,i=e?`, id: ${e}`:", no projectId";return{result:{name:"Project",status:"pass",message:`${s.name}${i}`},config:s}}async function Xo(){let n=Jt();if(!n.ok)return{result:{name:"Auth",status:"fail",message:n.reason==="missing"?"No credentials found.":"Credentials file is malformed.",fix:"Run mist_setup to log in."},creds:null,authValid:!1};try{let t=Qt(),s=await fetch(`${_e()}/api/org`,{headers:t,signal:AbortSignal.timeout(1e4)});try{Wt(s.headers)}catch{}if(!s.ok)return s.status===401?{result:{name:"Auth",status:"fail",message:"API key is invalid or revoked.",fix:"Run mist_setup to re-authenticate."},creds:n.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:`Server returned ${s.status}. Credentials may still be valid.`},creds:n.creds,authValid:!1};let e=await s.json(),i=e.slug??n.creds.orgSlug??"unknown",l=e.plan?`, plan: ${e.plan}`:"";return{result:{name:"Auth",status:"pass",message:`org: ${i}${l}`},creds:n.creds,authValid:!0}}catch(t){return t instanceof H?{result:{name:"Auth",status:"fail",message:`Auth check failed: ${t.message}`,fix:"Run mist_setup to re-authenticate."},creds:n.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:"Could not reach API to validate credentials. Network issue?"},creds:n.creds,authValid:!1}}}async function Zo(){let n=Date.now();try{let t=await et("nextjs"),s=Date.now()-n;return{result:{name:"API",status:"pass",message:`${_e()} reachable (${s}ms)`},scaffold:t}}catch(t){let s=Date.now()-n;return{result:{name:"API",status:"fail",message:t instanceof H?t.message:`Timeout or network error after ${s}ms`,fix:"Check your network connection. If using --api-url, verify the backend is running."},scaffold:null}}}function ei(){let n=qe();if(!n.backendSignalReceived)return{name:"MCP version",status:"warn",message:`v${n.current} installed, but no backend signal received to compare against.`};let t={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"no longer supported"};return n.severity==="none"?{name:"MCP version",status:"pass",message:`v${n.current} (latest)`}:n.severity==="unsupported"?{name:"MCP version",status:"fail",message:`v${n.current} is ${t[n.severity]}. Minimum: v${n.minSupported}.`,fix:`Run \`${n.upgradeCmd}\` then restart your editor.`}:{name:"MCP version",status:"warn",message:`v${n.current} installed, v${n.latest} available (${t[n.severity]}).`,fix:`Run \`${n.upgradeCmd}\` then restart your editor.`}}function ti(n,t,s,e){let i=F(n,"AGENTS.md"),l=F(n,"CLAUDE.md"),o=Y(i),r=t.methodologyVersion??"",a=s?.version??"";if(!o){if(!e||!s?.methodology)return{name:"AGENTS.md",status:"fail",message:"Missing. The host AI has no methodology context for this project.",fix:s?"Run mist_doctor without reportOnly to auto-restore it.":"Cannot restore: API unreachable. Fix connectivity first."};let c=En(s.methodology,t);return Oe(i,c),Oe(l,c),{name:"AGENTS.md",status:"fix",message:`Restored from methodology v${a}.`}}if(e&&!Y(l)){let c=Le(i,"utf-8");Oe(l,c)}if(a&&r&&a!==r){if(!e||!s?.methodology)return{name:"AGENTS.md",status:"warn",message:`Methodology v${r} installed, v${a} available.`,fix:"Run mist_doctor to update it."};let c=En(s.methodology,t);Oe(i,c),Oe(l,c);try{let u=F(n,"mistflow.json"),m=JSON.parse(Le(u,"utf-8"));m.methodologyVersion=a,Oe(u,JSON.stringify(m,null,2)+`
|
|
1753
|
-
`)}catch{}return{name:"AGENTS.md",status:"fix",message:`Updated methodology v${r} -> v${a}.`}}return{name:"AGENTS.md",status:"pass",message:r?`v${r}`:"present"}}async function si(n,t,s,e){let i=t.projectId;if(!i)return{name:"State sync",status:"skip",message:"No projectId. State sync not applicable without a linked project."};if(!s)return{name:"State sync",status:"skip",message:"Skipped (not authenticated)."};let l=hs(n),o=await ys(i);if(!l&&!o)return{name:"State sync",status:"warn",message:"No local or remote state found."};if(!l&&o){if(e){let{writeLocalState:r}=await import("./state-manager-QMSYKJ37.js");return r(n,o),{name:"State sync",status:"fix",message:"Local state was missing. Restored from remote."}}return{name:"State sync",status:"warn",message:"Local .mistflow/state.json is missing but remote state exists.",fix:"Run mist_doctor to restore it from the server."}}return l&&!o?{name:"State sync",status:"warn",message:"Local state exists but no remote state found. Remote sync may have failed."}:l&&l.projectId!==i?{name:"State sync",status:"warn",message:`state.json projectId (${l.projectId}) doesn't match mistflow.json projectId (${i}).`}:{name:"State sync",status:"pass",message:`deployCount: ${l.deployCount}, features: ${l.features.length}`}}function ni(n){let t=[],s=[];if(Y(F(n,"package.json"))?Y(F(n,"node_modules"))||t.push("node_modules not installed (run `npx -y @mistflow-ai/cli install`)"):t.push("missing package.json"),!Y(F(n,".env.local")))t.push("missing .env.local");else try{Le(F(n,".env.local"),"utf-8").match(/^AUTH_SECRET=(.+)$/m)||t.push("AUTH_SECRET not set in .env.local")}catch{t.push(".env.local exists but is not readable")}let e=F(n,"app","api","auth","[...all]","route.ts");Y(e)||s.push("missing app/api/auth/[...all]/route.ts");let i=F(n,"app","api","health","route.ts");return Y(i)||s.push("missing app/api/health/route.ts (needed for deploy verification)"),t.length>0?{name:"Structure",status:"fail",message:t.join("; "),fix:t.map(l=>l.includes("node_modules")?"Run `npx -y @mistflow-ai/cli install` in the project directory (streams output, no MCP 60s timeout). Plain `npm install` also works.":l.includes(".env.local")?"Create .env.local with AUTH_SECRET=<random-secret>.":l.includes("AUTH_SECRET")?"Add AUTH_SECRET=<random-secret> to .env.local.":l.includes("package.json")?"This project is missing package.json. Was it scaffolded correctly?":"").filter(Boolean).join(" ")}:s.length>0?{name:"Structure",status:"warn",message:s.join("; ")}:{name:"Structure",status:"pass",message:"all required files present"}}function ri(n,t){let s=t.env;if(!s?.required||typeof s.required!="object"||Object.keys(s.required).length===0)return{name:"Env vars",status:"pass",message:"no required env vars declared"};let e=[],i="";for(let l of[".env.local",".env"]){let o=F(n,l);try{Y(o)&&(i+=`
|
|
1754
|
-
`+Le(o,"utf-8"))}catch{}}for(let[l,o]of Object.entries(s.required)){let r=i.includes(`${l}=`),a=!!process.env[l];if(!r&&!a){let c=o?.description?` (${o.description})`:"";e.push(`${l}${c}`)}}return e.length>0?{name:"Env vars",status:"warn",message:`${e.length} required but not set: ${e.join(", ")}`,fix:"Set them via mist_config or add to .env.local."}:{name:"Env vars",status:"pass",message:`${Object.keys(s.required).length} required, all set`}}function oi(n){let t=n.plan;if(!t?.steps||t.steps.length===0)return{name:"Plan",status:"skip",message:"No plan found in mistflow.json."};let s=t.steps.length,e=t.steps.filter(o=>o.status==="completed").length,i=t.steps.filter(o=>o.status==="in_progress").length,l=s-e-i;if(e===s)return{name:"Plan",status:"pass",message:`${e}/${s} steps completed`};if(i>0){let o=t.steps.find(r=>r.status==="in_progress");return{name:"Plan",status:"warn",message:`${e}/${s} completed, step ${o?.number} "${o?.name}" in progress`,fix:"Run mist_build implement to continue."}}return e===0?{name:"Plan",status:"warn",message:`${s} steps planned, none started.`,fix:"Run mist_build implement to start building."}:{name:"Plan",status:"warn",message:`${e}/${s} completed, ${l} remaining.`,fix:"Run mist_build implement to continue."}}function ii(n,t){let s=F(n,"contracts");if(!Y(s))return{name:"Contracts",status:"skip",message:"No contracts/ directory. Scaffolded before integration contracts landed, or template app."};let e=t.plan,i=[];for(let g of e?.dataModel??[]){let f=g.entity??g.name;f&&typeof f=="string"&&i.push(f)}let l=F(n,"db","schema.ts"),o=[];if(Y(l))try{let g=Le(l,"utf-8"),f=Cn(g);for(let S of f)o.push(S.varName)}catch{}let r=new Map;for(let g of o)r.set(je(g),g);for(let g of i){let f=je(g);r.has(f)||r.set(f,g)}if(r.size===0)return{name:"Contracts",status:"pass",message:"contracts/ present, no entities in plan"};let a=new Set(i.map(g=>je(g))),c=new Set(o.map(g=>je(g))),u=[],m=[];for(let[g,f]of r){let S=F(s,`${g}.ts`);Y(S)||u.push(f),c.has(g)&&!a.has(g)&&m.push(f)}let d=[];try{d=Qo(s).filter(f=>f.endsWith(".ts")).map(f=>f.slice(0,-3)).filter(f=>!r.has(f))}catch{}let b=[];if(u.length>0&&b.push(`missing contract for ${u.length} entit${u.length===1?"y":"ies"}: ${u.join(", ")}`),d.length>0&&b.push(`orphan contract file${d.length===1?"":"s"} not in schema or plan: ${d.join(", ")}`),m.length>0&&b.push(`schema entit${m.length===1?"y":"ies"} missing from plan: ${m.join(", ")}`),u.length>0||d.length>0){let g=[];return u.length>0&&g.push("Create contracts/<entity>.ts for each missing entity. Every route and server action should import its types from contracts/, never inline them."),d.length>0&&g.push("Delete or rename the orphan contract file(s) \u2014 they don't match any table in db/schema.ts or entity in the plan."),{name:"Contracts",status:"warn",message:b.join("; "),fix:g.join(" ")}}return m.length>0?{name:"Contracts",status:"pass",message:`${r.size} entit${r.size===1?"y has":"ies have"} contracts; ${b[b.length-1]}`}:{name:"Contracts",status:"pass",message:`${r.size} entit${r.size===1?"y has":"ies have"} contracts`}}function En(n,t){let s=t.dbProvider;return s==="neon"||!s?n.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres"):n}function ai(n){let t={pass:"PASS",warn:"WARN",fail:"FAIL",fix:"FIXED",skip:"SKIP"},s=Math.max(...n.map(u=>u.name.length)),e=n.map(u=>{let m=".".repeat(s-u.name.length+4),d=t[u.status].padEnd(5);return` ${u.name} ${m} ${d} ${u.message}`}),i=n.filter(u=>u.status==="fix").length,l=n.filter(u=>u.status==="fail").length,o=n.filter(u=>u.status==="warn").length,r=[];i>0&&r.push(`${i} fixed`),l>0&&r.push(`${l} failure${l>1?"s":""}`),o>0&&r.push(`${o} warning${o>1?"s":""}`);let a=`mist_doctor results:
|
|
1755
|
-
|
|
1756
|
-
`+e.join(`
|
|
1757
|
-
`);r.length>0?a+=`
|
|
1758
|
-
|
|
1759
|
-
${r.join(", ")}`:a+=`
|
|
1760
|
-
|
|
1761
|
-
All checks passed.`;let c=n.filter(u=>(u.status==="fail"||u.status==="warn")&&u.fix);if(c.length>0){a+=`
|
|
1762
|
-
|
|
1763
|
-
To fix:`;for(let u of c)a+=`
|
|
1764
|
-
- ${u.fix}`}return a}var An=Ke.object({projectPath:Ke.string().optional().describe("Path to the project directory. Defaults to the current working directory."),checks:Ke.array(Ke.enum(["project","auth","api","version","methodology","state","structure","env","plan","contracts"])).optional().describe("Run only specific checks. Omit to run all."),reportOnly:Ke.boolean().optional().describe("If true, report issues without auto-fixing them.")}),Nn={name:"mist_doctor",description:"Diagnose project health: checks auth, API connectivity, AGENTS.md methodology, project state, version alignment, structure, and integration contracts. Auto-fixes safe issues (missing AGENTS.md, stale state). Run when something feels off.",inputSchema:An,handler:async n=>{let t=An.parse(n),s=t.projectPath||process.cwd(),e=!t.reportOnly,i=!t.checks,l=t.checks??[],o=g=>i||l.includes(g),r=[],a=null;if(o("project")){let g=Tn(s);r.push(g.result),a=g.config}else a=Tn(s).config;let c=!1;if(o("auth")||o("state")){let g=await Xo();o("auth")&&r.push(g.result),c=g.authValid}let m=null;if(o("api")||o("methodology")){let g=await Zo();o("api")&&r.push(g.result),m=g.scaffold}o("version")&&r.push(ei()),a?(o("methodology")&&r.push(ti(s,a,m,e)),o("state")&&r.push(await si(s,a,c,e)),o("structure")&&r.push(ni(s)),o("env")&&r.push(ri(s,a)),o("plan")&&r.push(oi(a)),o("contracts")&&r.push(ii(s,a))):i&&(r.push({name:"AGENTS.md",status:"skip",message:"Skipped (not a Mistflow project)."}),r.push({name:"State sync",status:"skip",message:"Skipped (not a Mistflow project)."}),r.push({name:"Structure",status:"skip",message:"Skipped (not a Mistflow project)."}),r.push({name:"Env vars",status:"skip",message:"Skipped (not a Mistflow project)."}),r.push({name:"Plan",status:"skip",message:"Skipped (not a Mistflow project)."}),r.push({name:"Contracts",status:"skip",message:"Skipped (not a Mistflow project)."}));let b=ai(r),P=r.some(g=>g.status==="fail");return p(b,P)}};import{z as jn}from"zod";var Dn=`# Mistflow CLI reference
|
|
1449
|
+
"build me something like ${n.share_url}"`}))}catch(n){let c=n instanceof Error?n.message:"Failed to share project";return i(c,!0)}}case"landing-designs":{if(e.presetId){let o=se(e.presetId);if(!o)return i(`Preset '${e.presetId}' not found. Use mist_project action='presets' without presetId to list all available presets.`,!0);let n=re(e.presetId);return i(JSON.stringify({preset:{id:o.id,title:o.title,category:o.category,description:n?.description??"",style:n?.style??"",theme:n?.theme??"dark",colors:n?.colors??[],tags:n?.tags??[],promptLength:o.prompt.length},message:`Landing design "${o.title}" (${o.category}) \u2014 ${n?.description??""}. To use it, pass landingDesign="${o.id}" when calling mist_plan.`}))}let r=ae(e.category??void 0),s=D(e.category),l=ne(s);return i(JSON.stringify({count:r.length,presets:r.map(o=>({id:o.id,title:o.title,category:o.category,description:o.description,style:o.style,theme:o.theme,colors:o.colors})),formatted:l,message:`${r.length} landing designs available.${e.category?` Filtered by: ${e.category}.`:""} To use one, pass landingDesign="<id>" when calling mist_plan. The design blueprint will be injected during the landing page implementation step. Browse them at ${W()}/designs?tab=landing-designs.`}))}case"integrations":{if(e.integrationId){let o=ie(e.integrationId);if(!o)return i(`Integration '${e.integrationId}' not found. Use mist_project action='integrations' without integrationId to list all available integrations.`,!0);let n=le(o.id);return i(JSON.stringify({integration:{id:o.id,name:o.name,category:o.category,description:n?.description??"",packages:n?.packages??[],envVars:n?.envVars??[],docsUrl:n?.docsUrl??"",difficulty:n?.difficulty??"medium"},message:`Integration "${o.name}" (${o.category}) \u2014 ${n?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${n?.envVars?.map(c=>c.key).join(", ")||"none"}. Docs: ${n?.docsUrl??"n/a"}.`}))}let r=pe(e.category??void 0),s=$(e.category??void 0),l=ce(s);return i(JSON.stringify({count:r.length,integrations:r.map(o=>({id:o.id,name:o.name,category:o.category,description:o.description,packages:o.packages,difficulty:o.difficulty,envVars:o.envVars.map(n=>n.key)})),formatted:l,message:`${r.length} integration blueprints available.${e.category?` Filtered by: ${e.category}.`:""} Integration blueprints are auto-injected during implementation when your plan includes a matching integration step. Use integrationId to see full details including env vars and setup URLs.`}))}case"errors":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);try{let n=await Y(o,e.period??"7d");return n.total===0?i(JSON.stringify({total:0,period:n.period,message:`No runtime errors in the last ${n.period}. The app is running clean.`})):i(JSON.stringify({total:n.total,period:n.period,errors:n.errors,message:`${n.total} runtime error(s) in the last ${n.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(n){let c=n instanceof Error?n.message:"Failed to fetch errors";return i(c,!0)}}case"logs":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);let n=e.deploymentId;if(!n)try{let c=await O(o);if(c.length===0)return i("No deployments found for this project.",!0);n=c[0].id}catch(c){let p=c instanceof Error?c.message:"Failed to fetch deployments";return i(p,!0)}try{let[c,p]=await Promise.all([G(n),K(n)]),g=c.filter(u=>u.level==="error"),h=c.filter(u=>u.level==="warn");return i(JSON.stringify({deploymentId:n,status:p.status,errorMessage:p.error??null,totalLogs:c.length,errorCount:g.length,warnCount:h.length,logs:c.map(u=>({time:u.timestamp,level:u.level,phase:u.phase,message:u.message})),message:p.status==="failed"?`Deployment failed. ${g.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${p.status}. ${c.length} log entries (${g.length} errors, ${h.length} warnings).`}))}catch(c){let p=c instanceof Error?c.message:"Failed to fetch deploy logs";return i(p,!0)}}case"deployments":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);try{let n=await O(o);return i(JSON.stringify({total:n.length,deployments:n.map(c=>({id:c.id,status:c.status,errorMessage:c.error_message,durationSeconds:c.duration_seconds,isRollback:!!c.rollback_from_id,createdAt:c.created_at})),message:`${n.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(n){let c=n instanceof Error?n.message:"Failed to fetch deployments";return i(c,!0)}}case"version":{A().backendSignalReceived||await B();let r=A(),s=r.severity==="none",l={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"UNSUPPORTED \u2014 upgrade required"};return i(JSON.stringify({current:r.current,latest:r.latest||"unknown",minSupported:r.minSupported||"unknown",severity:r.severity,upToDate:s,upgradeCmd:r.upgradeCmd,changelogUrl:r.changelogUrl,backendSignalReceived:r.backendSignalReceived,message:r.backendSignalReceived?`Mistflow MCP ${r.current} (${l[r.severity]??r.severity}). Latest: ${r.latest}.${s?"":` Run \`${r.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${r.current}. The backend hasn't replied yet \u2014 make one other API call (e.g. mist_project action='get') then retry to see the latest version.`}))}default:return i(`Unknown action: ${e.action}. Use get, update, share, landing-designs, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as w}from"zod";var Ae=w.object({action:w.enum(["navigate","go_back","go_forward","click","type","fill","select_option","press_key","hover","screenshot","snapshot"]).describe("Action to perform. Navigation: navigate|go_back|go_forward. Interaction: click|type|fill|select_option|press_key|hover. Visual: screenshot (returns image) | snapshot (returns accessibility tree)."),url:w.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:w.string().optional().describe("CSS selector of the target element. Required for: click, type, fill, select_option, hover. Optional for screenshot (captures just that element)."),value:w.string().optional().describe("Text to type/fill, option to select, or key to press (e.g. 'Enter', 'Tab'). Required for: type, fill, select_option, press_key."),fullPage:w.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:w.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),ue={name:"mist_browser",description:"Unified browser tool for navigating, interacting with, and capturing the app. Use 'navigate' to open a URL, interaction actions (click/type/fill/etc.) to test flows, 'snapshot' to inspect the accessibility tree, and 'screenshot' for a visual capture. Use after mist_preview to verify UI, test flows, and iterate on design.",inputSchema:Ae,handler:async a=>{let e=a,t=await V();if(e.action==="navigate"){if(!e.url)return i("URL is required for 'navigate'.",!0);let l=[],o=p=>{p.type()==="error"&&l.push(p.text())};t.on("console",o),await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{});let n=[],c=p=>n.push(p.message);if(t.on("pageerror",c),await t.waitForTimeout(500),t.removeListener("console",o),t.removeListener("pageerror",c),l.length>0||n.length>0){let p=await x(t),g=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:p,consoleErrors:l,pageErrors:n,hasErrors:!0})}];if(e.includeScreenshot){let h=await I(t);g.push({type:"image",data:h.toString("base64"),mimeType:"image/png"})}return{content:g}}}else if(e.action==="go_back")await t.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(e.action==="go_forward")await t.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(e.action==="click"){if(!e.selector)return i("Selector is required for 'click'.",!0);await t.click(e.selector,{timeout:1e4}),await t.waitForLoadState("domcontentloaded").catch(()=>{}),await t.waitForTimeout(500)}else if(e.action==="type"){if(!e.selector)return i("Selector is required for 'type'.",!0);if(!e.value)return i("Value is required for 'type'.",!0);await t.type(e.selector,e.value,{delay:50})}else if(e.action==="fill"){if(!e.selector)return i("Selector is required for 'fill'.",!0);if(!e.value)return i("Value is required for 'fill'.",!0);await t.fill(e.selector,e.value)}else if(e.action==="select_option"){if(!e.selector)return i("Selector is required for 'select_option'.",!0);if(!e.value)return i("Value is required for 'select_option'.",!0);await t.selectOption(e.selector,e.value)}else if(e.action==="hover"){if(!e.selector)return i("Selector is required for 'hover'.",!0);await t.hover(e.selector,{timeout:1e4})}else if(e.action==="press_key"){if(!e.value)return i("Value is required for 'press_key' (e.g. 'Enter').",!0);await t.keyboard.press(e.value),await t.waitForLoadState("domcontentloaded").catch(()=>{}),await t.waitForTimeout(500)}else if(e.action==="screenshot"){e.url&&(await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{}));let l;if(e.selector){let o=await t.$(e.selector);if(!o)return i(`Element not found: ${e.selector}`,!0);l=await o.screenshot({type:"png"})}else l=await I(t,e.fullPage);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),message:`Screenshot captured (${e.fullPage?"full page":"viewport"})`})},{type:"image",data:l.toString("base64"),mimeType:"image/png"}]}}else if(e.action==="snapshot"){let l=await x(t);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:l})}]}}let r=await x(t),s=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:r})}];if(e.includeScreenshot){let l=await I(t);s.push({type:"image",data:l.toString("base64"),mimeType:"image/png"})}return{content:s}}};import{z as me}from"zod";var ge=`# Mistflow CLI reference
|
|
1765
1450
|
|
|
1766
1451
|
The Mistflow CLI handles local execution and long-running operations that
|
|
1767
1452
|
would hit the MCP 60s tool-call ceiling. Every command below is invokable
|
|
@@ -1843,6 +1528,6 @@ and decides the next command. Example end-to-end chain:
|
|
|
1843
1528
|
echo '{...}' | mist plan --cid ... --pick-stdin --json
|
|
1844
1529
|
# \u2192 {"status":"ready","plan":{...}}
|
|
1845
1530
|
# Then: mist_build init (MCP), mist install (CLI), mist_build implement (MCP), etc.
|
|
1846
|
-
`,
|
|
1847
|
-
`),
|
|
1848
|
-
`).trim())}};var
|
|
1531
|
+
`,fe={name:"mist_help",description:"Returns the Mistflow CLI command reference. Call this ONCE at session start (or whenever you're unsure which tool to use) to learn every `mist` CLI command, when to prefer it over an MCP tool, and how to chain calls. Results are static \u2014 no backend round-trip, safe to call frequently.",inputSchema:me.object({command:me.string().optional().describe("Optional: name of a specific command to get focused reference for. Omit to get the full catalog.")}),handler:async a=>{let{command:e}=a;if(!e)return i(ge);let t=ge.split(`
|
|
1532
|
+
`),r=new RegExp(`^### \`mist ${e}`),s=-1,l=t.length;for(let o=0;o<t.length;o++)if(r.test(t[o]))s=o;else if(s>=0&&t[o].startsWith("### ")){l=o;break}else if(s>=0&&t[o].startsWith("## ")&&o>s){l=o;break}return s<0?i(`No command named '${e}' found. Call mist_help with no args to see the full catalog.`,!0):i(t.slice(s,l).join(`
|
|
1533
|
+
`).trim())}};var j=new Ue({name:"mistflow",version:"0.3.0"},{capabilities:{tools:{}},instructions:"Mistflow is a full-stack app builder that creates and deploys web apps from natural language descriptions. When a user asks to build, create, or make a web app, website, landing page, dashboard, internal tool, marketplace, content site, or browser game, use Mistflow tools. Mistflow creates NEW apps from scratch. It does NOT modify existing non-Mistflow codebases.\n\nCall `mist_help` at any point for the full CLI command reference. The 4 MCP tools handle short, structured, AI-native flows (auth, project state, browser automation, CLI discovery). Everything else is the `mist` CLI \u2014 invoke via your shell/bash tool.\n\nNew app workflow (entirely CLI-driven):\n1. Plan: run `mist plan --describe \"<user's description>\" --json` via your shell/bash tool. Pass the user's description EXACTLY as written \u2014 do NOT expand, rephrase, or add features. Relay any returned questions to the user, then submit answers via `echo '<answers-json>' | mist plan --token <id> --answers-stdin --json`. When status becomes \"design_clarify_pending\", poll with `mist plan-directions --cid <id> --wait --json`. Present the directions picker, then finalize with `echo '<pick-json>' | mist plan --cid <id> --pick-stdin --json`. If the CLI returns status \"confirm_new_project\" (safety gate when inside an existing codebase), ask the user whether to scaffold a new Mistflow app or edit the existing code.\n2. Mockup (optional): run `mist mockup --plan-id <id>` via your shell/bash tool \u2014 generates a visual HTML wireframe for user preview. Iterative: pass --feedback to refine, --approved to lock in the design.\n3. Scaffold: run `mist init --plan-id <id> --path <absolute-path>` via your shell/bash tool. Writes the Next.js scaffold, contracts/, AGENTS.md, and registers the project with Mistflow. Returns fast; does NOT run npm install.\n4. Install dependencies: run `mist install <projectPath>` via your shell/bash tool (streams output).\n5. Implement: run `mist implement --project-path <path>` via your shell/bash tool \u2014 executes plan steps one at a time. Call repeatedly until all steps are done; it auto-marks the previous step as completed on each call.\n6. Deploy: run `mist deploy [path]` via your shell/bash tool \u2014 tars, uploads, polls status with live streaming. Subcommands: `mist deploy promote` (staging\u2192prod), `mist deploy preview` (local tunnel), `mist deploy rollback <id>`, `mist deploy verify <url>`, `mist deploy redeploy`.\n7. QA: run `mist qa --project-path <path>` via your shell/bash tool. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n\nCompanion CLI (`@mistflow-ai/cli`, invoke as `mist` or via `npx -y @mistflow-ai/cli`) is the primary path for EVERYTHING except the 4 MCP tools below:\n- `mist plan` / `mist plan-directions` \u2014 plan an app.\n- `mist init` \u2014 scaffold a new project from a plan (fast, ~10s).\n- `mist install` / `mist build` / `mist mockup` / `mist implement` / `mist debug` / `mist qa` \u2014 local project lifecycle.\n- `mist deploy` (+ promote/preview/rollback/verify/redeploy subcommands) \u2014 deploy orchestration.\n- `mist status` / `mist fix` \u2014 feature manifest viewer + iteration loop.\n- `mist contracts` \u2014 integration-contract layer management.\n- `mist doctor` \u2014 project health diagnostics.\n- `mist login` / `mist projects` / `mist logs` / `mist env` / `mist domains` / `mist rollback` \u2014 cloud-coordination commands.\n- Call mist_help for the full reference.\n\nIMPORTANT \u2014 chaining discipline: once the user approves the plan, the init \u2192 install \u2192 implement (repeat) \u2192 build \u2192 deploy \u2192 qa chain is expected. Do NOT pause between these calls to ask the user \"should I continue?\" or offer options like \"full build vs step-by-step.\" The tool itself will return a status requiring user input when it actually needs one (e.g. confirm_new_project, confirm_dark_theme, awaiting promotion). Otherwise, chain calls continuously. Brief one-line status updates are fine and encouraged; permission requests are not.\n\nDesign presets (optional, between steps 2 and 3): run `mist projects designs`, `mist projects app-styles`, `mist projects integrations` via your shell/bash tool to browse catalogs. After `mist plan` generates a plan, it may recommend designs and styles \u2014 present these to the user before calling `mist init`.\n\nUpdating an existing Mistflow app:\n- Cosmetic or single-file changes: edit files directly, then `mist deploy` to publish.\n- New page or feature (no new data model): mist_project action=get for context, build it, then `mist deploy`.\n- Feature needing new data model or integration: run `mist plan --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Bug fix: run `mist debug --project-path <path>` to analyze, fix the code, then `mist deploy`.\n\nTemplate forking: use the Mistflow dashboard UI (app.mistflow.ai) to fork templates for now. CLI-side template-fork plumbing is in place but the API-client bridge lands in a follow-up release.\n\nThe 4 MCP tools:\n- mist_setup: authentication. Only call when user has never signed in or a tool returned 'auth_missing'/'auth_revoked'. Do NOT call for 500s, 404s, or network errors. Also accepts an apiKey parameter for headless auth.\n- mist_project: read/write project state (actions: 'get' / 'update'). Other project queries (errors, logs, deployments, share, version, catalog browsing) moved to `mist projects <subcommand>` in the CLI.\n- mist_browser: navigate, interact with, and screenshot the app during preview or after deploy. Returns screenshots inline in tool results \u2014 the one tool that genuinely needs the MCP transport.\n- mist_help: returns the full CLI command reference. Call once per session to learn the available `mist` commands."}),he=[Q,de,ue,fe];j.setRequestHandler(Me,async()=>({tools:he.map(a=>({name:a.name,description:a.description,inputSchema:De(a.inputSchema)}))}));j.setRequestHandler(Le,async a=>{let e=he.find(t=>t.name===a.params.name);if(!e)return i(`Unknown tool: ${a.params.name}`,!0);try{let t=e.inputSchema.safeParse(a.params.arguments);if(!t.success){let l=t.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join(", ");return i(`Invalid input: ${l}`,!0)}let r=a.params._meta?.progressToken,s={server:j,progressToken:r};try{return await e.handler(t.data,s)}finally{s.cleanup?.()}}catch(t){let r=t instanceof Error?t.message:"An unexpected error occurred";return console.error("Tool error:",t),i(r,!0)}});async function qe(){let a=process.argv.indexOf("--api-url");a!==-1&&process.argv[a+1]&&(process.env.MISTFLOW_API_URL=process.argv[a+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let e=new Oe;await j.connect(e),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}qe().catch(a=>{console.error("Fatal error:",a),process.exit(1)});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as m,d as y}from"./chunk-SXNAPWCC.js";import{f,g as u,i as p,k as g}from"./chunk-VQPRSDSC.js";import{existsSync as h,readFileSync as w,writeFileSync as b}from"fs";import{join as l}from"path";import{spawn as j}from"child_process";function C(r,t,e,s,o){return new Promise(n=>{let a=j(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:s,...o?{env:{...process.env,...o}}:{}}),c="",d="";a.stdout?.on("data",i=>{c+=i.toString()}),a.stderr?.on("data",i=>{d+=i.toString()}),a.on("close",i=>n({success:i===0,stdout:c,stderr:d})),a.on("error",i=>n({success:!1,stdout:c,stderr:d+i.message}))})}function P(r){let t=l(r,"mistflow.json");if(!h(t))return null;try{return JSON.parse(w(t,"utf-8"))}catch{return null}}function k(r,t){b(l(r,"mistflow.json"),JSON.stringify(t,null,2))}async function S(r,t){try{let e=t.features,s=t.steps,o={plan:t};Array.isArray(e)&&e.length>0&&(o.features=e.map(n=>n.name)),Array.isArray(s)&&s.length>0&&(o.provenance=s.map(n=>({feature:n.name??n.title??`Step ${n.number??"?"}`,user_intent:(n.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${u()}/api/projects/${encodeURIComponent(r)}/state`,{method:"PUT",headers:{...p(),"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(e){console.error("[self-heal] state sync failed:",e instanceof Error?e.message:String(e))}}async function x(r,t={}){let e=P(r);if(e){if(!f())return e.projectId;if(!e.projectId)try{let o=e.plan?.requestedSubdomain,n=await g(e.name,void 0,e.dbProvider??"neon",o);return e.projectId=n.id,k(r,e),m(r,y(n.id,e.name)),e.plan&&await S(n.id,e.plan),console.error(`[self-heal] registered project ${n.id.slice(0,8)} with backend`),n.id}catch(s){console.error("[self-heal] createProject failed:",s instanceof Error?s.message:String(s));return}return t.forceSync&&e.plan&&e.projectId&&await S(e.projectId,e.plan),e.projectId}}async function N(r,t){let e=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],s=t&&t.length>0?t:e,o=l(r,"components","ui"),n=[],a=[];for(let i of s)h(l(o,`${i}.tsx`))?n.push(i):a.push(i);if(a.length===0)return{installed:[],alreadyPresent:n};let c={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},d=await C("npx",["--yes","shadcn@latest","add","-y","-o",...a],r,18e4,c);return d.success?{installed:a,alreadyPresent:n}:{installed:[],alreadyPresent:n,failed:`shadcn add failed for: ${a.join(", ")}. ${d.stderr.slice(-300)}`.trim()}}export{x as ensureBackendRegistered,N as ensureShadcnComponents};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a,b,c,d,e,f,g}from"./chunk-SXNAPWCC.js";import"./chunk-VQPRSDSC.js";export{d as emptyState,e as fetchRemoteState,g as fuzzyMatch,a as getLocalStatePath,b as readLocalState,f as syncRemoteState,c as writeLocalState};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mistflow-ai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Mistflow MCP server for AI coding editors. Installed into Claude Code, Cursor, Codex CLI, and VS Code Copilot by the mistflow-ai installer.",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as s,B as t,C as u,D as v,E as w,F as x,G as y,H as z,I as A,J as B,K as C,L as D,M as E,N as F,O as G,P as H,Q as I,R as J,S as K,T as L,U as M,V as N,W as O,X as P,Y as Q,Z as R,_ as S,h as a,j as b,k as c,l as d,m as e,n as f,o as g,p as h,q as i,r as j,s as k,t as l,u as m,v as n,w as o,x as p,y as q,z as r}from"./chunk-EIOY6EQ2.js";export{e as MistflowApiError,u as addDomain,i as checkAuth,j as checkAuthDetailed,l as checkSubdomain,n as createDeployment,m as createProject,D as deleteEnvVar,t as discoverDecisions,K as downloadSource,Q as downloadSourceWithToken,q as fetchDesignDirections,N as fetchModule,L as fetchScaffold,M as fetchStepContext,P as forkTemplate,r as generatePlan,f as getAuthHeaders,b as getBaseUrl,d as getDashboardUrl,y as getDbCredentials,E as getDeployLogs,p as getDeploymentStatus,k as getProject,F as getProjectErrors,z as getSeedInfo,c as getSiteUrl,O as getTemplate,a as hasCredentialsOnDisk,h as hasLocalCredentials,G as listDeployments,v as listDomains,B as listEnvVars,R as markLocalSetupDone,s as modifyPlan,g as pingBackend,I as promotePreview,H as redeployProject,x as removeDomain,J as rollbackDeployment,S as shareProject,o as uploadAndDeploy,A as uploadQAResults,C as upsertEnvVar,w as verifyDomain};
|
package/dist/chunk-EIOY6EQ2.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import{readFileSync as $,existsSync as P,writeFileSync as z,mkdirSync as G,renameSync as X,unlinkSync as Y}from"fs";import{join as v,dirname as Z}from"path";import{homedir as ee}from"os";import{randomBytes as te}from"crypto";function M(){return v(ee(),".mistflow","credentials.json")}function x(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}var ne="https://api.mistflow.ai";function j(){let e=M();if(!P(e))return null;try{let t=JSON.parse($(e,"utf-8"));return typeof t.apiKey=="string"?{[ne]:t}:t}catch{return null}}function m(){let e=process.env.MISTFLOW_API_KEY;if(e)return{ok:!0,creds:{apiKey:e,orgId:"",orgSlug:"env"}};let t=j();if(!t)return{ok:!1,reason:"missing"};let n=x(),r=t[n];return r&&typeof r.apiKey=="string"&&r.apiKey&&typeof r.orgId=="string"?{ok:!0,creds:r}:{ok:!1,reason:"missing"}}function Re(e){let t=M(),n=Z(t);P(n)||G(n,{recursive:!0});let r=j()??{},o=x();r[o]=e;let s=v(n,`.credentials.tmp.${te(8).toString("hex")}`);try{z(s,JSON.stringify(r,null,2)+`
|
|
2
|
-
`,{mode:384}),X(s,t)}catch(c){try{Y(s)}catch{}throw c}}function xe(){let e=m();return e.ok?e.creds:null}function re(){return m().ok}function Se(e){let t=v(e,"mistflow.json");if(!P(t))return null;try{return JSON.parse($(t,"utf-8"))}catch{return null}}import{existsSync as I,readFileSync as F,writeFileSync as oe,mkdirSync as se}from"fs";import{join as D,dirname as U}from"path";import{homedir as ie}from"os";import{fileURLToPath as ae}from"url";var S=null;function f(){if(S)return S;try{let e=ae(import.meta.url),t=U(e);for(let n=0;n<6;n++){let r=D(t,"package.json");if(I(r)){let s=JSON.parse(F(r,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return S=s.version,s.version}let o=U(t);if(o===t)break;t=o}}catch{}return S="0.0.0","0.0.0"}function k(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?[parseInt(t[1],10),parseInt(t[2],10),parseInt(t[3],10)]:null}function E(e,t){let n=k(e),r=k(t);if(!n||!r)return 0;for(let o=0;o<3;o++){if(n[o]<r[o])return-1;if(n[o]>r[o])return 1}return 0}function V(e,t,n){if(n&&E(e,n)<0)return"unsupported";if(!t||E(e,t)>=0)return"none";let o=k(e),s=k(t);return!o||!s?"none":o[0]<s[0]?"major":o[1]<s[1]?"minor":"patch"}var u=null;function L(e){let t=e.get("x-mistflow-mcp-latest")??"",n=e.get("x-mistflow-mcp-min-supported")??"",r=e.get("x-mistflow-mcp-changelog-url")??"";!t&&!n||(u={latest:t,minSupported:n,changelogUrl:r})}function N(){let e=process.env.MISTFLOW_STATE_DIR||D(ie(),".mistflow");return D(e,"upgrade-state.json")}function ce(){try{let e=N();return I(e)?JSON.parse(F(e,"utf-8")):{}}catch{return{}}}function le(e){try{let t=N(),n=U(t);I(n)||se(n,{recursive:!0}),oe(t,JSON.stringify(e,null,2)+`
|
|
3
|
-
`,{mode:384})}catch{}}var A=!1;function ue(e){return e==="patch"?7*864e5:e==="minor"?1*864e5:0}function De(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!u)return null;let e=f();if(e==="0.0.0")return null;let t=V(e,u.latest,u.minSupported);if(t==="none")return null;if(t==="unsupported")return O(t,e,u);if(A)return null;let n=ce(),r=Date.now(),o=ue(t);return n.dismissedForVersion===u.latest&&t!=="major"||n.lastLatestSeen===u.latest&&n.lastShownMs&&r-n.lastShownMs<o?null:(A=!0,le({...n,lastShownMs:r,lastLatestSeen:u.latest}),O(t,e,u))}function O(e,t,n){let r="npx -y mistflow-ai install",o=n.changelogUrl?`
|
|
4
|
-
What's new: ${n.changelogUrl}`:"";return e==="unsupported"?`
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
Mistflow ${t} is no longer supported.
|
|
8
|
-
You must upgrade to ${n.latest} or newer to continue:
|
|
9
|
-
|
|
10
|
-
${r}
|
|
11
|
-
|
|
12
|
-
After upgrading, restart your AI editor.${o}
|
|
13
|
-
---`:e==="major"?`
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
Mistflow ${n.latest} is available (you have ${t}).
|
|
17
|
-
This is a major update. Run \`${r}\` and restart your editor.${o}
|
|
18
|
-
---`:e==="minor"?`
|
|
19
|
-
|
|
20
|
-
--- Mistflow update available: ${t} -> ${n.latest} ---
|
|
21
|
-
Run \`${r}\` to upgrade, then restart your editor.${o}`:`
|
|
22
|
-
|
|
23
|
-
(Mistflow ${n.latest} is out, you have ${t}. Run \`${r}\` when convenient.)`}function Ue(){let e=f(),t=u??{latest:"",minSupported:"",changelogUrl:""},n=V(e,t.latest,t.minSupported);return{current:e,latest:t.latest,minSupported:t.minSupported,severity:n,upgradeCmd:"npx -y mistflow-ai install",changelogUrl:t.changelogUrl,backendSignalReceived:u!==null}}function y(){return x()}function Me(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9102")}return"https://mistflow.ai"}function je(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","app.mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9101")}return"https://app.mistflow.ai"}var a=class extends Error{constructor(n,r,o,s){super(r);this.code=n;this.statusCode=o;this.details=s;this.name="MistflowApiError"}get isAuth(){return this.code.startsWith("auth_")}get isTransient(){return this.code==="server_error"||this.code==="upstream_error"||this.code==="network_error"||this.code==="rate_limited"}};function pe(){let e=m();if(!e.ok)throw new a("auth_missing","No Mistflow credentials found.",401);return de(e.creds)}function de(e){return{Authorization:`ApiKey ${e.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":f()}}function h(e){try{L(e.headers)}catch{}}async function Ee(){try{let e=await fetch(`${y()}/health`,{method:"GET",signal:AbortSignal.timeout(5e3)});h(e)}catch{}}async function q(e,t,n,r){for(let o=0;o<2;o++)try{return await fetch(e,{...t,signal:AbortSignal.timeout(n)})}catch(s){let c=s instanceof Error&&s.name==="TimeoutError",p=s instanceof TypeError;if(r&&o===0&&(c||p)){console.error(`[api] Retrying ${e} after ${c?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function _(e){let t=null;try{t=await e.json()}catch{t=null}let n=t&&typeof t.code=="string"?t.code:void 0,r=t&&typeof t.message=="string"?t.message:t&&typeof t.detail=="string"?t.detail:e.statusText||"Request failed";if(n)return new a(n,r,e.status,t?.details);let o=e.status;return o===401?new a("auth_invalid",r,o):o===403?new a("permission_denied",r,o):o===404?new a("not_found",r,o):o===409?new a("conflict",r,o):o===422?new a("validation_error",r,o):o===429?new a("rate_limited",r,o):o>=500?new a("server_error",e.statusText||"Internal server error",o):new a("client_error",r,o)}async function i(e,t={}){let n=pe(),{timeoutMs:r,idempotent:o,...s}=t,c=r??3e4,p=(s.method??"GET").toUpperCase(),d=o??(p==="GET"||p==="HEAD"),l;try{l=await q(`${y()}${e}`,{...s,headers:{...n,...s.headers}},c,d)}catch(R){throw R instanceof Error&&R.name==="TimeoutError"?new a("network_error","Request timed out. Try again in a moment."):new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(l),!l.ok)throw await _(l);return l.json()}function ge(){return m().ok}var C=null,B=0,fe=300*1e3;async function Ae(){return(await me()).ok}async function me(){if(C!==null&&Date.now()<B)return C?{ok:!0}:{ok:!1,reason:"no_credentials"};if(!ge())return{ok:!1,reason:"no_credentials"};try{return await i("/api/org"),C=!0,B=Date.now()+fe,{ok:!0}}catch(e){if(C=null,!(e instanceof a))return{ok:!1,reason:"network_error"};switch(e.code){case"auth_missing":case"auth_revoked":return{ok:!1,reason:"no_credentials"};case"auth_expired":case"auth_invalid":case"auth_org_not_found":return{ok:!1,reason:"expired"};case"network_error":return{ok:!1,reason:"network_error"};case"rate_limited":case"server_error":case"upstream_error":return{ok:!1,reason:"server_error"};default:return{ok:!1,reason:"server_error"}}}}async function Oe(e){return i(`/api/projects/${encodeURIComponent(e)}`)}async function Fe(e){return i(`/api/projects/check-subdomain?name=${encodeURIComponent(e)}`)}async function Ve(e,t,n="neon",r){return i("/api/projects",{method:"POST",body:JSON.stringify({name:e,template:t,db_provider:n,requested_subdomain:r})})}async function Le(e,t){return i("/api/deploy",{method:"POST",body:JSON.stringify({project_id:e,build_output_url:t})})}async function Ne(e,t,n="production",r,o,s,c){let{readFileSync:p}=await import("fs"),{basename:d}=await import("path"),l=m();if(!l.ok)throw new a("auth_missing","No Mistflow credentials found.",401);let R=l.creds,K=p(t),J=new Blob([K],{type:"application/gzip"}),g=new FormData;if(g.append("project_id",e),g.append("build",J,d(t)),n!=="production"&&g.append("environment",n),r&&g.append("admin_email",r),o&&g.append("schema_pushed","true"),s){let{existsSync:H}=await import("fs");if(H(s)){let W=p(s),Q=new Blob([W],{type:"application/gzip"});g.append("source",Q,"source.tar.gz")}}c&&g.append("git_commit_sha",c);let w;try{w=await fetch(`${y()}/api/deploy/upload`,{method:"POST",headers:{Authorization:`ApiKey ${R.apiKey}`,"X-Mistflow-MCP-Version":f()},body:g,signal:AbortSignal.timeout(3e5)})}catch{throw new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(w),!w.ok)throw await _(w);let T=await w.json();return{...T,id:T.deployment_id}}async function Be(e){return i(`/api/deploy/${encodeURIComponent(e)}/status`)}async function qe(e){return i(`/api/plan/design-directions/${encodeURIComponent(e)}`,{timeoutMs:15e3})}async function Ke(e,t){let n={description:e,conversation_id:t?.conversationId,answers:t?.answers};t?.autonomous&&(n.autonomous=!0),t?.language&&t.language.toLowerCase()!=="english"&&(n.language=t.language),t?.designConversationId&&(n.design_conversation_id=t.designConversationId),t?.designDirection&&(n.design_direction=t.designDirection);let r=t?.conversationId||t?.answers||t?.designConversationId?18e4:6e4;return i("/api/plan",{method:"POST",body:JSON.stringify(n),timeoutMs:r,idempotent:!1})}async function Je(e,t){return i("/api/plan/modify",{method:"POST",body:JSON.stringify({existing_plan:e,modification:t})})}async function He(e){return i("/api/plan/discover",{method:"POST",body:JSON.stringify({description:e})})}async function We(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains`,{method:"POST",body:JSON.stringify({domain:t})})}async function Qe(e){return i(`/api/projects/${encodeURIComponent(e)}/domains`)}async function ze(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains/${encodeURIComponent(t)}/verify`)}async function Ge(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains/${encodeURIComponent(t)}`,{method:"DELETE"})}async function Xe(e){return i(`/api/projects/${encodeURIComponent(e)}/db-credentials`)}async function Ye(e){try{return await i(`/api/projects/${encodeURIComponent(e)}/test-accounts`)}catch(t){return t instanceof a&&(t.statusCode===404||t.code==="not_found")||console.error("[api] Failed to fetch seed info:",t instanceof Error?t.message:t),null}}async function Ze(e,t){try{return await i(`/api/deploy/${encodeURIComponent(e)}/qa-results`,{method:"POST",body:JSON.stringify(t)})}catch(n){return console.error("[api] Failed to upload QA results:",n instanceof Error?n.message:n),null}}async function et(e){return i(`/api/projects/${encodeURIComponent(e)}/env`)}async function tt(e,t,n,r){return i(`/api/projects/${encodeURIComponent(e)}/env`,{method:"PUT",body:JSON.stringify({key:t,value:n,category:r?.category??"custom",description:r?.description,setup_url:r?.setupUrl})})}async function nt(e,t){return i(`/api/projects/${encodeURIComponent(e)}/env/${encodeURIComponent(t)}`,{method:"DELETE"})}async function rt(e){return i(`/api/deploy/${encodeURIComponent(e)}/logs`)}async function ot(e,t="7d"){return i(`/api/projects/${encodeURIComponent(e)}/errors?period=${encodeURIComponent(t)}`)}async function st(e){return i(`/api/projects/${encodeURIComponent(e)}/deployments`)}async function it(e){return i(`/api/deploy/${encodeURIComponent(e)}/redeploy`,{method:"POST"})}async function at(e,t){let n=new URLSearchParams({preview_deployment_id:t});return i(`/api/deploy/${encodeURIComponent(e)}/promote`,{method:"POST",body:n.toString(),headers:{"Content-Type":"application/x-www-form-urlencoded"}})}async function ct(e){return i(`/api/deploy/${encodeURIComponent(e)}/rollback`,{method:"POST"})}async function lt(e,t){let n=m();if(!n.ok)throw new a("auth_missing","Not authenticated.",401);let r=n.creds,o=await fetch(`${y()}/api/deploy/${encodeURIComponent(e)}/source`,{headers:{Authorization:`ApiKey ${r.apiKey}`,"X-Mistflow-MCP-Version":f()},signal:AbortSignal.timeout(12e4)});if(h(o),!o.ok)throw await _(o);let{writeFileSync:s}=await import("fs"),c=Buffer.from(await o.arrayBuffer());s(t,c)}async function b(e,t){let{timeoutMs:n,idempotent:r,...o}=t??{},s=n??3e4,c=(o.method??"GET").toUpperCase(),p=r??(c==="GET"||c==="HEAD"),d;try{d=await q(`${y()}${e}`,{headers:{"Content-Type":"application/json","X-Mistflow-MCP-Version":f()},...o},s,p)}catch(l){throw l instanceof Error&&l.name==="TimeoutError"?new a("network_error","Request timed out. Try again in a moment."):new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(d),!d.ok)throw await _(d);return d.json()}async function ut(e="nextjs"){return b(`/api/scaffold/${encodeURIComponent(e)}`)}async function pt(e,t){return b(`/api/scaffold/${encodeURIComponent(e)}/context?step_type=${encodeURIComponent(t)}`)}async function dt(e,t,n,r){return b(`/api/scaffold/${encodeURIComponent(e)}/module`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"crud",entity:t,fields:n,entity_plural:r})})}async function gt(e){return b(`/api/templates/${encodeURIComponent(e)}`)}async function ft(e){return i(`/api/templates/${encodeURIComponent(e)}/fork`,{method:"POST"})}async function mt(e,t,n){let r;try{r=await fetch(`${y()}/api/deploy/${encodeURIComponent(e)}/fork-source?fork_token=${encodeURIComponent(t)}`,{headers:{"X-Mistflow-MCP-Version":f()},signal:AbortSignal.timeout(12e4)})}catch{throw new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(r),!r.ok)throw r.status===401?new a("validation_error","Fork token expired or invalid. Re-open the project in the dashboard to get a fresh token.",r.status):await _(r);let{writeFileSync:o}=await import("fs"),s=Buffer.from(await r.arrayBuffer());o(n,s)}async function yt(e){await i(`/api/projects/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({local_setup_done:!0})})}async function ht(e,t){return i(`/api/projects/${encodeURIComponent(e)}/share`,{method:"POST",body:JSON.stringify({is_template:t?.isTemplate??!1,template_description:t?.description})})}export{f as a,L as b,De as c,Ue as d,m as e,Re as f,xe as g,re as h,Se as i,y as j,Me as k,je as l,a as m,pe as n,Ee as o,ge as p,Ae as q,me as r,Oe as s,Fe as t,Ve as u,Le as v,Ne as w,Be as x,qe as y,Ke as z,Je as A,He as B,We as C,Qe as D,ze as E,Ge as F,Xe as G,Ye as H,Ze as I,et as J,tt as K,nt as L,rt as M,ot as N,st as O,it as P,at as Q,ct as R,lt as S,ut as T,pt as U,dt as V,gt as W,ft as X,mt as Y,yt as Z,ht as _};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as m,d as y}from"./chunk-PXD4CR4I.js";import{h as f,j as u,n as p,u as g}from"./chunk-EIOY6EQ2.js";import{existsSync as h,readFileSync as w,writeFileSync as b}from"fs";import{join as l}from"path";import{spawn as j}from"child_process";function C(r,t,e,s,o){return new Promise(n=>{let a=j(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:s,...o?{env:{...process.env,...o}}:{}}),c="",d="";a.stdout?.on("data",i=>{c+=i.toString()}),a.stderr?.on("data",i=>{d+=i.toString()}),a.on("close",i=>n({success:i===0,stdout:c,stderr:d})),a.on("error",i=>n({success:!1,stdout:c,stderr:d+i.message}))})}function P(r){let t=l(r,"mistflow.json");if(!h(t))return null;try{return JSON.parse(w(t,"utf-8"))}catch{return null}}function k(r,t){b(l(r,"mistflow.json"),JSON.stringify(t,null,2))}async function S(r,t){try{let e=t.features,s=t.steps,o={plan:t};Array.isArray(e)&&e.length>0&&(o.features=e.map(n=>n.name)),Array.isArray(s)&&s.length>0&&(o.provenance=s.map(n=>({feature:n.name??n.title??`Step ${n.number??"?"}`,user_intent:(n.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${u()}/api/projects/${encodeURIComponent(r)}/state`,{method:"PUT",headers:{...p(),"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(e){console.error("[self-heal] state sync failed:",e instanceof Error?e.message:String(e))}}async function x(r,t={}){let e=P(r);if(e){if(!f())return e.projectId;if(!e.projectId)try{let o=e.plan?.requestedSubdomain,n=await g(e.name,void 0,e.dbProvider??"neon",o);return e.projectId=n.id,k(r,e),m(r,y(n.id,e.name)),e.plan&&await S(n.id,e.plan),console.error(`[self-heal] registered project ${n.id.slice(0,8)} with backend`),n.id}catch(s){console.error("[self-heal] createProject failed:",s instanceof Error?s.message:String(s));return}return t.forceSync&&e.plan&&e.projectId&&await S(e.projectId,e.plan),e.projectId}}async function N(r,t){let e=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],s=t&&t.length>0?t:e,o=l(r,"components","ui"),n=[],a=[];for(let i of s)h(l(o,`${i}.tsx`))?n.push(i):a.push(i);if(a.length===0)return{installed:[],alreadyPresent:n};let c={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},d=await C("npx",["--yes","shadcn@latest","add","-y","-o",...a],r,18e4,c);return d.success?{installed:a,alreadyPresent:n}:{installed:[],alreadyPresent:n,failed:`shadcn add failed for: ${a.join(", ")}. ${d.stderr.slice(-300)}`.trim()}}export{x as ensureBackendRegistered,N as ensureShadcnComponents};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a,b,c,d,e,f,g}from"./chunk-PXD4CR4I.js";import"./chunk-EIOY6EQ2.js";export{d as emptyState,e as fetchRemoteState,g as fuzzyMatch,a as getLocalStatePath,b as readLocalState,f as syncRemoteState,c as writeLocalState};
|