modelence 0.26.0 → 0.26.1-nd.dev.1
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/bin/modelence.js +14 -14
- package/dist/bin/modelence.js.map +1 -1
- package/dist/chunk-GW46X34B.js +3 -0
- package/dist/chunk-GW46X34B.js.map +1 -0
- package/dist/{chunk-3KNJR4AV.js → chunk-YJZ22HME.js} +5 -5
- package/dist/{chunk-3KNJR4AV.js.map → chunk-YJZ22HME.js.map} +1 -1
- package/dist/{package-532Q6Y5O.js → package-MS2HPTQM.js} +2 -2
- package/dist/{package-532Q6Y5O.js.map → package-MS2HPTQM.js.map} +1 -1
- package/dist/server-FILJ5I4O.js +2 -0
- package/dist/{server-UJQAC4CW.js.map → server-FILJ5I4O.js.map} +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-TJPIV5YP.js +0 -3
- package/dist/chunk-TJPIV5YP.js.map +0 -1
- package/dist/server-UJQAC4CW.js +0 -2
package/dist/bin/modelence.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {Command}from'commander';import {readFileSync,promises,createWriteStream,openAsBlob}from'fs';import {fileURLToPath}from'url';import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
claude plugin
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`);if(await promises.
|
|
9
|
-
`)
|
|
2
|
+
import {Command}from'commander';import {readFileSync,promises,createWriteStream,openAsBlob}from'fs';import {fileURLToPath}from'url';import se,{join,dirname,relative,isAbsolute,resolve,sep}from'path';import {parse}from'dotenv';import {createInterface}from'readline';import {execFile,execSync,spawnSync}from'child_process';import yt from'open';import Et from'os';import Ie from'fs/promises';import {createJiti}from'jiti';import {z as z$1}from'zod';import {build}from'tsup';import {loadConfigFromFile,build as build$1,mergeConfig}from'vite';import no from'archiver';import {promisify}from'util';import {parse as parse$1,printParseErrorCode}from'jsonc-parser';async function x(e,{pick:t,purpose:o,pickEnvironment:n=false,appId:r}={}){let i=await fetch(`${e}/api/cli/auth`,{method:"POST"});if(!i.ok)throw new Error("Failed to create CLI authentication code");let{code:l,verificationUrl:a}=await i.json(),c=new URL(a),s=t??(n?"environment":void 0);s&&c.searchParams.set("pick",s),o&&c.searchParams.set("purpose",o),r&&c.searchParams.set("appId",r),console.log(`Please visit ${c} to authenticate`),console.log(`Code: ${l}`);try{await yt(c.toString());}catch{console.log("Could not open a browser automatically. Please open the URL above manually.");}return await wt(e,l)}async function wt(e,t){let r=Date.now()+6e5;for(;Date.now()<r;){try{let i=await vt(e,t);if(i)return i}catch(i){console.error("Error polling for CLI token:",i);}await new Promise(i=>setTimeout(i,5e3));}throw new Error("Unable to authenticate CLI - timed out. Please try again.")}async function vt(e,t){let o=await fetch(`${e}/api/cli/token?code=${t}`,{method:"GET"});if(!o.ok)throw new Error(`CLI token polling failed: ${o.statusText}`);let{token:n,expiresAt:r,target:i}=await o.json();return n?{token:n,expiresAt:r,target:i}:null}var St="auth.json",Ct=40*60*1e3;function N(){let e=process.env.MODELENCE_HOME||join(Et.homedir(),".modelence");return join(e,St)}async function O(){try{let e=JSON.parse(await promises.readFile(N(),"utf8"));return e&&typeof e=="object"?e:{}}catch{return {}}}async function X(e){let t=N();await promises.mkdir(dirname(t),{recursive:true,mode:448}),await promises.writeFile(t,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{mode:384});}async function me(e){let{hosts:t={}}=await O(),o=t[e];return !o?.token||!o.expiresAt||new Date(o.expiresAt).getTime()-Ct<=Date.now()?null:o.token}async function fe(e){let{hosts:t={}}=await O();return Object.entries(t).filter(([o,n])=>(!e||o===e)&&!!n?.token).map(([o,n])=>({host:o,token:n.token}))}async function ge(e,t,o){let n=await O();await X({...n,hosts:{...n.hosts,[e]:{token:t,expiresAt:o}}});}async function M(e){if(!e){await X({});return}let t=await O(),{[e]:o,...n}=t.hosts??{};await X({...t,hosts:n});}var A=".modelence",F="project.json";function ye(e=process.cwd()){return join(e,A,F)}async function k(e=process.cwd()){try{let t=await promises.readFile(ye(e),"utf8"),o=JSON.parse(t);return o&&typeof o=="object"?o:{}}catch{return {}}}async function L(e,t=process.cwd()){let o=await k(t);await promises.mkdir(join(t,A),{recursive:true}),await promises.writeFile(ye(t),JSON.stringify({...o,...e},null,2)+`
|
|
4
|
+
`);}var y=class extends Error{constructor(o,n){super(o);this.status=n;this.name="StudioApiError";}};async function f(e,t,{method:o="GET",token:n,body:r,query:i}={}){let l=new URL(t,e);for(let[s,p]of Object.entries(i??{}))l.searchParams.set(s,String(p));let a={};n&&(a.Authorization=`Bearer ${n}`),r!==void 0&&(a["Content-Type"]="application/json");let c=await fetch(l,{method:o,headers:a,body:r===void 0?void 0:JSON.stringify(r)});if(!c.ok){let s=await c.text(),p=s||c.statusText;try{let u=JSON.parse(s);p=u.error||u.message||p;}catch{}throw new y(p,c.status)}return await c.json()}var we="https://cloud.modelence.com";function j(e){return e instanceof y&&e.status===401}async function ve(e){return process.env.MODELENCE_TOKEN?process.env.MODELENCE_TOKEN:await me(e)}async function Q(e,t,o){if(o)try{await ge(e,t,o);}catch(n){console.warn("Could not save the login for next time:",n);}}async function ee(e,t){let o={environmentId:e.environmentId,appAlias:e.appAlias,envAlias:e.envAlias,host:t};try{await L({deploy:o,..."appId"in e?{appId:e.appId}:{}});}catch(n){console.warn("Could not record the deploy target in .modelence/project.json:",n);}}function Ee(e,t){if(!e.deploy||(e.deploy.host??we)===t)return e;let{deploy:o,appId:n,...r}=e;return r}function $(e){let t=e.trim().replace(/\/+$/,""),o=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`https://${t}`;try{return new URL(o).origin}catch{throw new Error(`Invalid Modelence host "${e}"; expected e.g. https://cloud.modelence.com`)}}async function be(e,t){if(e)return $(e);if(process.env.MODELENCE_SERVICE_ENDPOINT)return $(process.env.MODELENCE_SERVICE_ENDPOINT);let o={};try{o=parse(await promises.readFile(join(t,".modelence.env"),"utf8"));}catch{}return o.MODELENCE_SERVICE_ENDPOINT?$(o.MODELENCE_SERVICE_ENDPOINT):we}var oe=".modelence.env";async function It(e,t){let o="setupToken"in t?{"X-Modelence-Setup-Token":t.setupToken}:{Authorization:`Bearer ${t.cliToken}`},n=await fetch(`${e}/api/setup`,{method:"GET",headers:o});if(!n.ok){let r=await n.text();throw new Error(r||`Request failed with status ${n.status}`)}return n.json()}async function Ce(e){let t=createInterface({input:process.stdin,output:process.stdout});return new Promise(o=>{t.question(e,n=>{t.close(),o(n.trim());});})}async function Dt(){return (await Ce(`Warning: ${oe} already exists. Do you want to overwrite it? (y/N) `)).toLowerCase()==="y"}function _t(e){return String(e).replace(/"/g,'\\"')}async function Nt(){let{appId:e}=await k();return typeof e=="string"&&e?e:void 0}async function Ot(e){await L({appId:e});}var Pe=".claude",T=join(Pe,"settings.json"),U="modelence@modelence",ne="modelence/modelence",Mt={modelence:{source:{source:"github",repo:ne}}},Ft="https://docs.modelence.com/ai-coding-agents",Se=`Install the Modelence plugin for Claude Code by hand:
|
|
5
|
+
claude plugin marketplace add ${ne}
|
|
6
|
+
claude plugin install ${U} --scope project
|
|
7
|
+
Guide: ${Ft}`;async function Lt(){let e=join(process.cwd(),T),t;try{t=await promises.readFile(e,"utf8");}catch(n){if(n.code!=="ENOENT"){console.warn(`Could not read ${T}; leaving it unchanged.`);return}}let o={};if(t!==void 0)try{o=JSON.parse(t);}catch{console.warn(`Could not parse ${T}; leaving it unchanged.`);return}o.extraKnownMarketplaces={...o.extraKnownMarketplaces,...Mt},o.enabledPlugins?.[U]===void 0&&(o.enabledPlugins={...o.enabledPlugins,[U]:true});try{await promises.mkdir(join(process.cwd(),Pe),{recursive:!0}),await promises.writeFile(e,JSON.stringify(o,null,2)+`
|
|
8
|
+
`),console.log(`Declared the Modelence Claude Code plugin in ${T}`);}catch(n){console.warn(`Failed to write ${T}:`,n);}}function te(e){return spawnSync("claude",e,{encoding:"utf8",shell:process.platform==="win32"})}async function jt(){let e=te(["--version"]);if(e.error||e.status!==0){console.log(Se);return}let t=(await Ce("Install the Modelence plugin for Claude Code? (Y/n) ")).toLowerCase();if(t&&t!=="y"&&t!=="yes")return;let o=te(["plugin","marketplace","add",ne]),n=te(["plugin","install",U,"--scope","project"]);if(n.status===0)console.log("Installed the Modelence plugin for Claude Code. Sign in once from Claude Code: run /mcp, choose modelence, then Authenticate.");else {let r=o.status!==0?o:n;console.warn(`Could not install the Modelence plugin: ${(r.stderr||r.stdout||"").trim()}`),console.warn(Se);}}async function Rt(e){try{let t=e.replace(".env",".backup.env");await promises.copyFile(e,t),console.log(`Backup created at ${t}`);}catch(t){console.warn("Failed to create backup file:",t);}}async function xe(e){let t={...e,host:$(e.host)};try{let o=join(process.cwd(),oe),n={},r=!1;try{let s=await promises.readFile(o,"utf8");n=parse(s),r=!0,await Rt(o),await Dt()||(console.log("Setup canceled"),process.exit(0));}catch{}let i;if(t.token)i={setupToken:t.token};else {let{token:s}=await x(t.host,{pickEnvironment:!0,appId:await Nt()});i={cliToken:s};}console.log("Fetching service configuration...");let l=await It(t.host,i),a={...n,MODELENCE_TELEMETRY_ENABLED:"false",MODELENCE_ENVIRONMENT_ID:l.environmentId,MODELENCE_SERVICE_ENDPOINT:t.host,MODELENCE_SERVICE_TOKEN:l.serviceToken,MODELENCE_CONTAINER_ID:l.containerId,MODELENCE_RUNTIME:process.env.MODELENCE_RUNTIME||"local"},c=Object.entries(a).map(([s,p])=>`${s}="${_t(p)}"`).join(`
|
|
9
|
+
`);if(await promises.writeFile(o,c.trim()+`
|
|
10
|
+
`),console.log(`Successfully configured ${oe} file`),l.appId)try{await Ot(l.appId),console.log(`Recorded the app ID in ${A}/${F}`);}catch(s){console.warn(`Failed to record the app ID in ${A}/${F}:`,s);}await Lt(),!t.token&&process.stdin.isTTY&&await jt(),r&&console.log("The project now points to a different environment. Restart your dev server, and if you use an AI coding agent, start a new session."),!r&&a.MODELENCE_RUNTIME==="local"&&(console.log(`
|
|
10
11
|
Next step \u2014 run the app on this machine:
|
|
11
12
|
`),console.log(" npm install (if you haven't already)"),console.log(` npm run dev (starts the app)
|
|
12
|
-
`));}catch(
|
|
13
|
-
`,{mode:384});}async function ke(e){let{hosts:t={}}=await j(),o=t[e];return !o?.token||!o.expiresAt||new Date(o.expiresAt).getTime()-Vt<=Date.now()?null:o.token}async function xe(e){let{hosts:t={}}=await j();return Object.entries(t).filter(([o,n])=>(!e||o===e)&&!!n?.token).map(([o,n])=>({host:o,token:n.token}))}async function Ae(e,t,o){let n=await j();await te({...n,hosts:{...n.hosts,[e]:{token:t,expiresAt:o}}});}async function U(e){if(!e){await te({});return}let t=await j(),{[e]:o,...n}=t.hosts??{};await te({...t,hosts:n});}var De=promisify(execFile),$e=200*1024*1024,Xt=[".git","node_modules",".modelence/build",".modelence/tmp",".modelence/cache"],Zt=[/^\.modelence(\.[^/]+)?\.env$/];function _e(e){return /^\.env(?:\.|$)/.test(e)&&!/\.(example|sample|template)$/.test(e)}function Qt(e){return e.split(/\r?\n/).some(t=>{if(/^\s*[#;]/.test(t))return false;let o=/https?:\/\/([^/\s]+)@/.exec(t)?.[1];if(o&&o.replace(/\$\{[A-Za-z_][A-Za-z0-9_]*\}/g,"").replace(/:/g,"")!=="")return true;let n=/^\s*[^=]*?(?:_authToken|_auth|_password|npmAuthToken|npmAuthIdent)["']?\s*[=:]\s*(.*?)\s*$/i.exec(t);if(!n)return false;let r=n[1].replace(/\s+#.*$/,"").replace(/^["']|["']$/g,"").trim();return r!==""&&!/^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(r)})}function B(e){return e.split(sep).join("/")}function eo(e){return ne(e)||_e(B(e).split("/").at(-1)??"")}function ne(e){let t=B(e),o=t.split("/");for(let r of Xt)if(r.split("/").length===1){if(o.slice(0,-1).includes(r))return true}else if(t===r||t.startsWith(`${r}/`))return true;let n=o[o.length-1];return Zt.some(r=>r.test(n))}async function to(e){try{let{stdout:t}=await De("git",["rev-parse","--is-inside-work-tree"],{cwd:e});return t.trim()==="true"}catch{return false}}async function Ie(e,t){let{stdout:o}=await De("git",["ls-files",...t,"-z"],{cwd:e,maxBuffer:67108864});return o.split("\0").filter(n=>n.length>0)}async function Ne(e,t=""){let o=await promises.readdir(join(e,t),{withFileTypes:true}),n=[];for(let r of o){let i=t?`${t}/${r.name}`:r.name;r.isDirectory()?eo(`${i}/x`)||n.push(...await Ne(e,i)):(r.isFile()||r.isSymbolicLink())&&n.push(i);}return n}async function oo(e,t){let o=join(e,t),n=await promises.readlink(o),r=resolve(dirname(o),n),i=relative(e,r);return i.startsWith("..")||isAbsolute(i)?null:{path:t,target:B(relative(dirname(o),r))||"."}}async function no(e=process.cwd()){let t=await to(e),o=new Set(t?await Ie(e,["--cached"]):[]),n=t?[...o,...await Ie(e,["--others","--exclude-standard"])]:await Ne(e),r={files:[],symlinks:[],usedGit:t,excludedFiles:[],skipped:[],committedEnvFiles:[]};for(let i of n){let l=i.split("/").at(-1)??"",c=_e(l);if(ne(i)||c&&!o.has(i)){r.excludedFiles.push(i);continue}let s;try{s=await promises.lstat(join(e,i));}catch{continue}if(s.isSymbolicLink()){if(ne(`${i}/x`)){r.excludedFiles.push(i);continue}let a=await oo(e,i);a?r.symlinks.push(a):r.skipped.push({path:i,reason:"symlink to outside the project"});}else if(s.isDirectory())r.skipped.push({path:i,reason:"git submodule"});else if(s.isFile()){if([".npmrc",".yarnrc.yml",".yarnrc"].includes(l)&&Qt(await promises.readFile(join(e,i),"utf8"))){r.excludedFiles.push(i);continue}r.files.push(i),c&&r.committedEnvFiles.push(i);}}return {...r,files:r.files.sort(),symlinks:r.symlinks.sort((i,l)=>i.path.localeCompare(l.path)),excludedFiles:r.excludedFiles.sort(),committedEnvFiles:r.committedEnvFiles.sort()}}async function Oe(e,t){let{files:o,symlinks:n,...r}=await no(e);if(o.length===0)throw new Error("No files to upload");await promises.mkdir(join(t,".."),{recursive:true}),await promises.rm(t,{force:true});let i=createWriteStream(t),l=Yt("zip",{zlib:{level:6}}),c=new Promise((a,p)=>{i.on("close",a),i.on("error",p),l.on("error",p);});l.pipe(i);for(let a of o)l.file(join(e,a),{name:B(relative("",a))});for(let a of n)l.symlink(a.path,a.target,511);await Promise.all([l.finalize(),c]);let{size:s}=await promises.stat(t);if(s>$e)throw await promises.rm(t,{force:true}),new Error(`Source is ${(s/1024/1024).toFixed(0)} MB compressed; the limit is ${$e/1024/1024} MB. Add build output and large assets to .gitignore.`);return {...r,fileCount:o.length+n.length,sizeBytes:s}}var d="modelence.config.json",ao=1,$="https://docs.modelence.com/deploy/setup",K=`Use ${$}.md to set up Modelence deployment for this project`;function Fe(e){return `${e.replace(/\/$/,"")}/schema/${d}?version=${ao}`}function V(e=process.cwd()){return join(e,d)}async function z(e=process.cwd()){let t;try{t=await promises.readFile(V(e),"utf8");}catch{return null}let o=[],n=parse$1(t,o,{allowTrailingComma:true,disallowComments:false});if(o.length>0)throw new Error(`${d} is not valid JSON: ${co(t,o[0])}`);if(!n||typeof n!="object"||Array.isArray(n))throw new Error(`${d} must contain a JSON object`);return n}function co(e,t){let o=e.slice(0,t.offset),n=o.split(`
|
|
13
|
+
`));}catch(o){console.error(`Setup failed: ${o instanceof Error?o.message:"Unknown error"}`),process.exit(1);}}var Vt=z$1.object({serverDir:z$1.string(),serverEntry:z$1.string(),postBuildCommand:z$1.string().optional()});async function Ae(){let e=join(process.cwd(),"modelence.config.ts"),o=await createJiti(import.meta.url,{interopDefault:true,requireCache:false}).import(e);if(typeof o!="object")throw new Error("modelence.config.ts should export an object");return Vt.parse(o)}var ke=null,re=null;function $e(){if(!re)throw new Error("Configuration not loaded");return re}function v(){let{serverDir:e,serverEntry:t}=$e();return join(e,t).replace(/\\/g,"/")}function Te(){return $e().postBuildCommand}function V(e){let t=ie("build");return e?join(t,e).replace(/\\/g,"/"):t.replace(/\\/g,"/")}function E(e){return join(process.cwd(),e).replace(/\\/g,"/")}function ie(e){let t=E(".modelence");return e?join(t,e).replace(/\\/g,"/"):t.replace(/\\/g,"/")}async function b(){try{re=await Ae();}catch(e){throw console.error(e),new Error("Unable to load modelence.config.ts")}try{let e=await Ie.readFile(join(process.cwd(),".modelence.env"),"utf-8");ke=parse(e);}catch(e){if(e.code==="ENOENT")ke={};else throw e}}async function Ht(){let e=Te();if(e){console.log("Running post-build command..."),execSync(e);return}await Wt(),await qt();}async function Wt(){console.log("Building client with Vite...");let e=await loadConfigFromFile({command:"build",mode:process.env.NODE_ENV||"production"}),t={build:{outDir:se.resolve(process.cwd(),".modelence/build/client").replace(/\\/g,"/"),emptyOutDir:true,ssrManifest:true}};await build$1(mergeConfig(e?.config||{},t,true));}async function qt(){console.log("Building SSR bundle with Vite...");let e=await loadConfigFromFile({command:"build",mode:process.env.NODE_ENV||"production"}),o={build:{ssr:se.resolve(process.cwd(),"src/client/index.tsx").replace(/\\/g,"/"),outDir:se.resolve(process.cwd(),".modelence/build/ssr").replace(/\\/g,"/"),emptyOutDir:true,rollupOptions:{output:{format:"esm",entryFileNames:"index.mjs"}}}};await build$1(mergeConfig(e?.config||{},o,true));}async function Yt(){return console.log("Building server with tsup..."),new Promise(e=>{build({entry:[v()],format:"esm",sourcemap:true,minify:process.env.NODE_ENV==="production",outDir:".modelence/build",clean:true,watch:false,bundle:true,skipNodeModulesBundle:true,treeshake:true,platform:"node",outExtension:()=>({js:".mjs"}),onSuccess:async()=>{e(void 0);}});})}async function K(){console.log("Building Modelence project...");try{let e=V();await Ie.rm(e,{recursive:!0,force:!0}),await Yt(),await Ht(),console.log("Build completed successfully!");}catch(e){throw console.error(e),new Error("Build failed")}try{await Ie.access(ie());}catch{throw new Error("Could not find the .modelence directory. Looks like something went wrong during the build.")}}var Le=promisify(execFile),Me=200*1024*1024,ro=[".git","node_modules",".modelence/build",".modelence/tmp",".modelence/cache"],io=[/^\.modelence(\.[^/]+)?\.env$/];function je(e){return /^\.env(?:\.|$)/.test(e)&&!/\.(example|sample|template)$/.test(e)}function so(e){return e.split(/\r?\n/).some(t=>{if(/^\s*[#;]/.test(t))return false;let o=/https?:\/\/([^/\s]+)@/.exec(t)?.[1];if(o&&o.replace(/\$\{[A-Za-z_][A-Za-z0-9_]*\}/g,"").replace(/:/g,"")!=="")return true;let n=/^\s*[^=]*?(?:_authToken|_auth|_password|npmAuthToken|npmAuthIdent)["']?\s*[=:]\s*(.*?)\s*$/i.exec(t);if(!n)return false;let r=n[1].replace(/\s+#.*$/,"").replace(/^["']|["']$/g,"").trim();return r!==""&&!/^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(r)})}function z(e){return e.split(sep).join("/")}function ao(e){return ce(e)||je(z(e).split("/").at(-1)??"")}function ce(e){let t=z(e),o=t.split("/");for(let r of ro)if(r.split("/").length===1){if(o.slice(0,-1).includes(r))return true}else if(t===r||t.startsWith(`${r}/`))return true;let n=o[o.length-1];return io.some(r=>r.test(n))}async function co(e){try{let{stdout:t}=await Le("git",["rev-parse","--is-inside-work-tree"],{cwd:e});return t.trim()==="true"}catch{return false}}async function Fe(e,t){let{stdout:o}=await Le("git",["ls-files",...t,"-z"],{cwd:e,maxBuffer:67108864});return o.split("\0").filter(n=>n.length>0)}async function Re(e,t=""){let o=await promises.readdir(join(e,t),{withFileTypes:true}),n=[];for(let r of o){let i=t?`${t}/${r.name}`:r.name;r.isDirectory()?ao(`${i}/x`)||n.push(...await Re(e,i)):(r.isFile()||r.isSymbolicLink())&&n.push(i);}return n}async function lo(e,t){let o=join(e,t),n=await promises.readlink(o),r=resolve(dirname(o),n),i=relative(e,r);return i.startsWith("..")||isAbsolute(i)?null:{path:t,target:z(relative(dirname(o),r))||"."}}async function po(e=process.cwd()){let t=await co(e),o=new Set(t?await Fe(e,["--cached"]):[]),n=t?[...o,...await Fe(e,["--others","--exclude-standard"])]:await Re(e),r={files:[],symlinks:[],usedGit:t,excludedFiles:[],skipped:[],committedEnvFiles:[]};for(let i of n){let l=i.split("/").at(-1)??"",a=je(l);if(ce(i)||a&&!o.has(i)){r.excludedFiles.push(i);continue}let c;try{c=await promises.lstat(join(e,i));}catch{continue}if(c.isSymbolicLink()){if(ce(`${i}/x`)){r.excludedFiles.push(i);continue}let s=await lo(e,i);s?r.symlinks.push(s):r.skipped.push({path:i,reason:"symlink to outside the project"});}else if(c.isDirectory())r.skipped.push({path:i,reason:"git submodule"});else if(c.isFile()){if([".npmrc",".yarnrc.yml",".yarnrc"].includes(l)&&so(await promises.readFile(join(e,i),"utf8"))){r.excludedFiles.push(i);continue}r.files.push(i),a&&r.committedEnvFiles.push(i);}}return {...r,files:r.files.sort(),symlinks:r.symlinks.sort((i,l)=>i.path.localeCompare(l.path)),excludedFiles:r.excludedFiles.sort(),committedEnvFiles:r.committedEnvFiles.sort()}}async function Ue(e,t){let{files:o,symlinks:n,...r}=await po(e);if(o.length===0)throw new Error("No files to upload");await promises.mkdir(join(t,".."),{recursive:true}),await promises.rm(t,{force:true});let i=createWriteStream(t),l=no("zip",{zlib:{level:6}}),a=new Promise((s,p)=>{i.on("close",s),i.on("error",p),l.on("error",p);});l.pipe(i);for(let s of o)l.file(join(e,s),{name:z(relative("",s))});for(let s of n)l.symlink(s.path,s.target,511);await Promise.all([l.finalize(),a]);let{size:c}=await promises.stat(t);if(c>Me)throw await promises.rm(t,{force:true}),new Error(`Source is ${(c/1024/1024).toFixed(0)} MB compressed; the limit is ${Me/1024/1024} MB. Add build output and large assets to .gitignore.`);return {...r,fileCount:o.length+n.length,sizeBytes:c}}var d="modelence.config.json",go=1,Ke=`https://raw.githubusercontent.com/modelence/modelence/main/schema/v${go}/modelence.config.json`,D="https://docs.modelence.com/deploy/setup",J=`Use ${D}.md to set up Modelence deployment for this project`;function G(e=process.cwd()){return join(e,d)}async function H(e=process.cwd()){let t;try{t=await promises.readFile(G(e),"utf8");}catch{return null}let o=[],n=parse$1(t,o,{allowTrailingComma:true,disallowComments:false});if(o.length>0)throw new Error(`${d} is not valid JSON: ${ho(t,o[0])}`);if(!n||typeof n!="object"||Array.isArray(n))throw new Error(`${d} must contain a JSON object`);return n}function ho(e,t){let o=e.slice(0,t.offset),n=o.split(`
|
|
14
14
|
`).length,r=t.offset-o.lastIndexOf(`
|
|
15
|
-
`);return `${printParseErrorCode(t.error)} at line ${n}, column ${r}`}async function
|
|
16
|
-
`);}function
|
|
17
|
-
`)}function
|
|
18
|
-
`);if(
|
|
15
|
+
`);return `${printParseErrorCode(t.error)} at line ${n}, column ${r}`}async function ze(e,t=process.cwd()){await promises.writeFile(G(t),JSON.stringify(e,null,2)+`
|
|
16
|
+
`);}function Je(e){let t=[],o=Object.entries(e.resources??{});for(let[n,r]of o){o.length>1&&t.push(` resource: ${n}`),t.push(` image: ${r.image??"default (node-22-slim)"}`),r.root&&r.root!=="."&&t.push(` root: ${r.root}`),t.push(...Be("build",r.build,"default (npm install)")),t.push(...Be("start",r.start,"(none)"));for(let i of r.static??[])t.push(` static: ${i.path} -> ${i.dir}/`);}for(let[n,r]of Object.entries(e.env??{}))t.push(` env: ${n} (${r.type??"text"})`);return t}function Be(e,t,o){let n=` ${`${e}:`.padEnd(8)} `;return t?t.commands.length===0?[`${n}(none)`]:t.commands.map(r=>`${n}${r}`):[`${n}${o}`]}async function We(e,t="."){if(typeof t!="string"||isAbsolute(t))throw new Error("root must be a relative directory inside the project.");let o=await promises.realpath(e),n=resolve(o,t);if(!Ge(o,n))throw new Error("root must stay inside the project.");let r;try{r=await promises.realpath(n);}catch{throw new Error(`root "${t}" does not exist.`)}if(!Ge(o,r)||!(await promises.stat(r)).isDirectory())throw new Error("root must be a directory inside the project.");return r}function Ge(e,t){let o=relative(e,t);return o!==".."&&!o.startsWith(`..${sep}`)&&!isAbsolute(o)}var qe=["$schema","resources","env"];async function Ye(e){let t=await H(e);if(!t)throw new Error(Eo(e));bo(t);for(let o of Object.values(t.resources??{}))o?.root!==void 0&&await We(e,o.root);console.log(`Build plan (${d}):`);for(let o of Je(t))console.log(o);return t}function Eo(e){return [`${d} not found in ${e}.`,"Modelence Cloud builds and runs your app exactly as this file describes (install, build and start commands, Node.js version, static directories).","Ask your coding agent to create it with this prompt:","",` ${J}`,"",`Reference: ${D}`].join(`
|
|
17
|
+
`)}function bo(e){let t=Object.keys(e).filter(o=>!qe.includes(o));if(t.length>0)throw new Error(`${d} has unknown key "${t[0]}" (allowed: ${qe.join(", ")}).`);for(let o of ["resources","env"]){let n=e[o];if(n!==void 0&&(!n||typeof n!="object"||Array.isArray(n)))throw new Error(`${d}: "${o}" must be an object.`)}for(let[o,n]of Object.entries(e.resources??{})){if(!Xe(n))throw new Error(`${d}: "resources.${o}" must be an object.`);for(let r of ["build","start"])So(n[r],`resources.${o}.${r}`);}}function So(e,t){if(e!==void 0){if(!Xe(e)||!Array.isArray(e.commands))throw new Error(`${d}: "${t}" must be an object with a "commands" array, e.g. { "commands": ["npm ci"] }.`);e.commands.forEach((o,n)=>{if(typeof o!="string")throw new Error(`${d}: "${t}.commands.${n}" must be a string.`);if(o.trim()==="")throw new Error(`${d}: "${t}.commands.${n}" must not be empty.`)});}}function Xe(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}async function Ze(e,t){return t.prebuilt?{kind:"bundle",reason:"prebuilt-flag"}:await H(e)!==null?{kind:"source",reason:"spec-file"}:await xo(e)?{kind:"bundle",reason:"modelence-dependency"}:{kind:"source",reason:"default"}}function Qe(e){return e.reason!=="modelence-dependency"?null:`No ${d} found; this is a Modelence app, so it is built locally and uploaded as before. Add a ${d} to build it on Modelence Cloud instead.`}async function xo(e){let t;try{t=await promises.readFile(join(e,"package.json"),"utf8");}catch{return false}let o;try{o=JSON.parse(t);}catch{return false}if(!o||typeof o!="object")return false;let n=o;return ["dependencies","devDependencies"].some(r=>{let i=n[r];return !!(i&&typeof i=="object"&&"modelence"in i)})}function et(e,t){if(e.app&&e.env)return {appAlias:e.app,envAlias:e.env};if(e.env&&t.deploy?.appAlias)return {appAlias:t.deploy.appAlias,envAlias:e.env};if(e.env&&t.appId)return {appId:t.appId,envAlias:e.env};if(e.app||e.env)throw new Error("Pass both --app and --env, or neither to pick the target in the browser.");return t.deploy?.environmentId?{environmentId:t.deploy.environmentId}:null}function tt(e,t){if("environmentId"in e){let o=t.deploy;return `${o?.environmentId===e.environmentId?`${o.appAlias}/${o.envAlias}`:e.environmentId} (saved in .modelence/project.json)`}return "appAlias"in e?`${e.appAlias}/${e.envAlias}`:e.envAlias}function ot(e,t){let o=t.deploy;return !o||"environmentId"in e?false:"appAlias"in e&&e.appAlias!==o.appAlias?true:e.envAlias!==o.envAlias}function W(){return !process.env.CI&&!!process.stdin.isTTY}async function nt(e){let t=createInterface({input:process.stdin,output:process.stdout}),o=await new Promise(n=>t.question(`${e} (y/N) `,n));return t.close(),o.trim().toLowerCase()==="y"}function q(e,t,{deployAgain:o=false}={}){if(!e||e.length===0)return null;let n=e.length===1,r=t?` Set ${n?"it":"them"} at ${t}`:"";return `${e.join(", ")} ${n?"is":"are"} declared in ${d} but ${n?"has":"have"} no value in this environment.`+(r?r+(o?", then deploy again.":""):"")}var rt=3e3,ko=30*60*1e3,$o=10*60*1e3;async function it(e,t,o){let n=Date.now()+$o,r=false;for(;Date.now()<n;){let i;try{({status:i}=await f(e,"/api/environment/status",{token:t,query:{environmentId:o}}));}catch(l){if(l instanceof y&&l.status===404)return false;throw l}if(i==="ready")return r;if(i==="failed")throw new Error("The environment failed to provision; check it in the dashboard.");r||(console.log("Waiting for the environment to finish provisioning..."),r=true),await new Promise(l=>setTimeout(l,rt));}throw new Error("Timed out waiting for the environment to be ready.")}async function st(e,t,o,n){let r=Date.now()+ko,i="",l="",a=null,c=false;for(;Date.now()<r;){let s;try{s=await f(e.host,"/api/deploy/status",{token:e.token,query:{environmentId:o,buildId:n,...a?{logCursor:a}:{}}});}catch(p){if(!j(p)||c)throw p;c=true,await t();continue}for(let p of s.logs)process.stdout.write(` \u2502 ${p.replace(/\n$/,"")}
|
|
18
|
+
`);if(a=s.logCursor??a,s.status!==i){i=s.status;let p=To(s);p&&p!==l&&(l=p,console.log(p));}if(s.status==="deploy-completed"){s.siteUrl&&console.log(`Live at ${s.siteUrl}`);return}if(s.status==="build-failed"||s.status==="deploy-failed"){for(let u of s.errors)console.error(` ${u}`);if(s.containerLogs&&s.containerLogs.length>0){console.error("Container output:");for(let u of s.containerLogs)console.error(` \u2502 ${u.replace(/\n$/,"")}`);}let p=q(s.missingEnvVars,s.configUrl,{deployAgain:true});p&&console.error(p),process.exitCode=1;return}await new Promise(p=>setTimeout(p,rt));}console.error("Timed out waiting for the deployment; check the dashboard for its status."),process.exitCode=1;}function To(e){switch(e.status){case "build-pending":return "Building image...";case "deploy-pending":case "deploying":return "Image built. Deploying containers...";case "rolling-out":return "Rolling out new containers...";case "deploy-completed":return "Deployed successfully.";case "build-failed":return "Build failed.";case "deploy-failed":return "Deployment failed.";default:return null}}async function pe({session:e,target:t,kind:o,archivePath:n,spec:r}){let{host:i,token:l}=e,a=()=>f(i,"/api/upload-bundle",{method:"POST",token:l,body:{...t,kind:o}}),c=await a();if(!c.environmentId)throw new Error("Modelence Cloud is too old for this CLI version (no environment id from /api/upload-bundle). Try again later, or deploy with modelence@0.25.");await it(i,l,c.environmentId)&&(c=await a()),console.log(`Uploading to ${c.appAlias}/${c.envAlias}...`);let s=await openAsBlob(n,{type:"application/zip"}),p=await fetch(c.uploadUrl,{method:"PUT",body:s,headers:{"Content-Type":"application/zip"}});if(!p.ok)throw new Error(`Failed to upload: ${await Do(p)}`);let u=await f(i,"/api/deploy",{method:"POST",token:l,body:{environmentId:c.environmentId,bundleName:c.bundleName,kind:o,spec:r}});console.log(`Deployment started: ${u.deploymentUrl}`);let C=q(u.missingEnvVars,u.configUrl);return C&&console.warn(`Warning: ${C}`),{environmentId:u.environmentId,buildId:u.buildId}}async function Do(e){let t=await e.text().catch(()=>""),o=t.match(/<Code>([^<]+)<\/Code>/)?.[1],n=t.match(/<Message>([^<]+)<\/Message>/)?.[1],r=e.statusText||`HTTP ${e.status}`;return o?`${r} (${o}${n?`: ${n}`:""})`:r}async function pt(e){let t=process.cwd(),o=await be(e.host,t),n=await Ze(t,e),r=n.kind,i=Qe(n);i&&console.log(i);let l=Ee(await k(t),o),a=et(e,l),c=await ve(o);if((!c||!a)&&!W())throw new Error(Oo(!!c,!!a));await Mo(a,l,e.yes);let s,p=join(t,".modelence","tmp",`${r}.zip`);r==="bundle"?(await b(),await K(),await Lo(p)):(s=await Ye(t),Fo(await Ue(t,p)));try{if(!c||!a){let m=await x(o,{pick:a?void 0:"deploy",purpose:"deploy",appId:l.appId});c=m.token,await Q(o,m.token,m.expiresAt),m.target&&(a={environmentId:m.target.environmentId},await ee(m.target,o));}if(!a)throw new Error("No deploy target selected. Run again and pick an environment in the browser, or pass --app and --env.");let u={host:o,token:c},C=async()=>{if(await M(o),!W())throw new Error("Modelence Cloud rejected the token (expired or revoked), and nobody is at the terminal to sign in again. Set a fresh MODELENCE_TOKEN. A deployment that already started keeps running; check the dashboard.");console.log("Your saved login has expired; please sign in again.");let m=await x(o,{pick:a?void 0:"deploy",purpose:"deploy",appId:l.appId});await Q(o,m.token,m.expiresAt),m.target&&(a={environmentId:m.target.environmentId},await ee(m.target,o)),u.token=m.token;},P;try{P=await pe({session:u,target:a,kind:r,archivePath:p,spec:s});}catch(m){if(!j(m))throw m;await C(),P=await pe({session:u,target:a,kind:r,archivePath:p,spec:s});}P.buildId&&await st(u,C,P.environmentId,P.buildId);}finally{await promises.rm(p,{force:true});}}function Oo(e,t){return `Nobody is at the terminal to sign in or pick a target in the browser (CI or a non-interactive shell). To deploy from here, ${[t?null:"pass --app and --env",e?null:"set MODELENCE_TOKEN"].filter(Boolean).join(" and ")}.`}async function Mo(e,t,o){if(!e)return;let n=tt(e,t);if(console.log(`Deploying to ${n}`),o||!ot(e,t)||!W())return;let r=`${t.deploy?.appAlias}/${t.deploy?.envAlias}`;if(!await nt(`This project normally deploys to ${r}. Deploy to ${n} instead?`))throw new Error("Cancelled.")}var at=10;function ct(e){let t=e.slice(0,at).join(", "),o=e.length-at;return o>0?`${t} and ${o} more`:t}function Fo(e){console.log(`Packed ${e.fileCount} files (${dt(e.sizeBytes)})`+(e.usedGit?" from git":"; not a git repository, so only default exclusions applied")),e.excludedFiles.length>0&&console.log(`Not uploaded (local, generated or credentials; store secrets in the target environment): ${ct(e.excludedFiles)}`),e.committedEnvFiles.length>0&&console.log(`Uploaded environment files committed to git: ${ct(e.committedEnvFiles)}`);for(let{path:t,reason:o}of e.skipped)console.warn(`Warning: ${t} is not uploaded (${o}).`);}function dt(e){return `${(e/1024/1024).toFixed(2)} MB`}async function Lo(e){await promises.rm(e,{force:true}),console.log("Creating deployment bundle..."),await promises.mkdir(join(e,".."),{recursive:true});let t=createWriteStream(e),o=no("zip",{zlib:{level:9}}),n=new Promise((a,c)=>{t.on("close",a),t.on("error",c),o.on("error",c);});o.pipe(t);let r=["package.json","package-lock.json","next.config.js","next.config.ts","modelence.config.ts"],i=["public","server","scripts",join(".modelence","build"),".next"];for(let a of r)await lt(E(a))&&o.file(E(a),{name:a});for(let a of i)await lt(E(a))&&o.directory(E(a),a);await o.finalize(),await n;let l=await promises.stat(e);console.log(`Deployment bundle created (${dt(l.size)})`);}async function lt(e){try{return await promises.access(e),!0}catch{return false}}function ut(e={}){console.log("Starting Modelence dev server...");let t=v(),o=se.resolve("./node_modules/.bin/tsx");execSync(`"${o}" watch --ignore "vite.config.ts.timestamp-*" "${t}"`,{stdio:"inherit",cwd:process.cwd(),env:{...process.env,NODE_ENV:"development",...e.takeover?{MODELENCE_TAKEOVER:"1"}:{}}});}function ft(){console.log("Starting Modelence production server...");let e=v(),t=se.basename(e,se.extname(e)),o=V(`${t}.mjs`);execSync(`node "${o}"`,{stdio:"inherit",cwd:process.cwd(),env:{...process.env,NODE_ENV:"production"}});}async function gt(e){let t=await fe(e.host);await Promise.all(t.map(({host:o,token:n})=>Bo(o,n))),await M(e.host),console.log(e.host?`Removed the saved login for ${e.host}.`:`Removed saved logins (${N()}).`);}async function Bo(e,t){try{await f(e,"/api/cli/logout",{method:"POST",token:t});}catch{}}async function ht(e){let t=process.cwd(),o=G(t);if(!e.force&&await Ko(o))throw new Error(`${d} already exists; pass --force to overwrite it.`);await ze({$schema:Ke,resources:{app:{type:"service",image:"node-22-slim",build:{commands:["npm ci"]},static:[]}},env:{}},t),console.log(`Wrote a ${d} template.`),console.log("Ask your coding agent to fill it in with this prompt:"),console.log(""),console.log(` ${J}`),console.log(""),console.log(`Reference: ${D}`),console.log(`Once ${d} describes your app, run \`npx modelence@latest deploy\`.`);}async function Ko(e){try{return await promises.access(e),!0}catch{return false}}var qo=fileURLToPath(import.meta.url),Yo=dirname(qo),Xo=JSON.parse(readFileSync(join(Yo,"../../package.json"),"utf-8")),h=new Command().name("modelence").description("Modelence CLI tool").version(Xo.version);h.command("setup").description("Setup Modelence environment variables").option("-t, --token <token>","Modelence setup token (omit to authorize in the browser)").option("-h, --host <host>","Modelence host","https://cloud.modelence.com").action(async e=>{await xe(e);});h.command("build").description("Build the application").action(async()=>{await b(),await K();});h.command("deploy").description("Deploy the project described by modelence.config.json to Modelence Cloud (picks the target in the browser on first run)").option("-a, --app <app>","Application alias").option("-e, --env <env>","Environment alias").option("-h, --host <host>","Modelence host").option("--prebuilt","Build locally and upload the .modelence/build bundle (Modelence apps only)").option("-y, --yes","Deploy to the given --app/--env without asking, even if it is not the saved target").action(async e=>{try{await pt(e);}catch(t){console.error(`Deploy failed: ${t instanceof Error?t.message:String(t)}`),process.exit(1);}});h.command("init").description("Create a modelence.config.json template for this project").option("--force","Overwrite an existing modelence.config.json").action(async e=>{try{await ht(e);}catch(t){console.error(`Init failed: ${t instanceof Error?t.message:String(t)}`),process.exit(1);}});h.command("logout").description("Forget the saved Modelence Cloud login").option("-h, --host <host>","Only forget the login for this host").action(async e=>{await gt(e);});h.command("dev").description("Start development server").option("--takeover","Disconnect any instance currently holding the environment and connect this one").action(async e=>{await b(),ut(e);});h.command("start").description("Start production server").action(async()=>{await b(),ft();});h.parse(process.argv);//# sourceMappingURL=modelence.js.map
|
|
19
19
|
//# sourceMappingURL=modelence.js.map
|