htmsh
Version:
Turn your code into a live website instantly from your shell. No setup, no servers, no stress. Just one command and your HTML, CSS, and JavaScript are live for the world to see.
3 lines (2 loc) • 23.8 kB
JavaScript
import{program as e}from"commander";import t from"node:fs";import o from"node:os";import s from"node:path";import n from"node:crypto";import{spawn as a}from"node:child_process";import{Blob as r}from"buffer";import{once as i}from"node:events";import l from"inquirer";import c from"conf";import{yellow as d,dim as u,bold as p,bgLightGreen as h,white as m,magenta as w,lightGreen as f,cyan as g}from"kolorist";function y(){return y=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var o=arguments[t];for(var s in o)({}).hasOwnProperty.call(o,s)&&(e[s]=o[s])}return e},y.apply(null,arguments)}const b=new c({projectName:"htmsh"}),v="0.2.0",k=864e5,j="https://htm.sh",$=process.env.SUGGESTED_PUBLIC_GATEWAY||"https://htm.sh",x=["/api/deploy","/deploy"];function S({quiet:e=!1,json:t=!1}={}){const o=(o="")=>{e||t||process.stdout.write(o+"\n")},s=e=>{t&&process.stdout.write(JSON.stringify(e)+"\n")},n=(e=72)=>"─".repeat(e);return{banner:()=>{if(t)return s({level:"banner",name:"htm.sh"});o(h(p("htm.sh"))),o("From shell to world. Ship static sites from your shell for free, without leaving the command line."),o("")},section:e=>{if(t)return s({level:"section",title:e});o(p(e)),o(u(n()))},subheader:e=>{if(t)return s({level:"subheader",text:e});o(u(e)),o("")},row:(e,n,a="")=>{if(t)return s({level:"row",key:e,value:n});const r=u((e+":").padEnd(18," "));o(` ${r} ${((e,t)=>{switch(t){case"cyan":return g(e);case"green":return f(e);case"yellow":return d(e);case"magenta":return w(e);case"white":return m(e);default:return e}})(String(n),a)}`)},info:e=>t?s({level:"info",msg:e}):o(`info ${e}`),warn:e=>t?s({level:"warn",msg:e}):o(`warn ${e}`),error:e=>t?s({level:"error",msg:e}):o(`error ${e}`),ok:e=>t?s({level:"ok",msg:e}):o(`ok ${e}`),blank:()=>{t||e||o("")},bar:(e,n,a=28)=>{n=Math.max(0,Math.min(100,0|n));const r=Math.round(n/100*a),i=a-r,l="["+"=".repeat(r)+" ".repeat(i)+"]";if(t)return s({level:"bar",label:e,pct:n});const c=u((e+":").padEnd(18," "));o(` ${c} ${l} ${String(n).padStart(3," ")}%`)},block:(e,n)=>{if(t)return s({level:"block",title:e,lines:n});const a=(e="")=>(e.length>70?e.slice(0,67)+"...":e).padEnd(70," ");o("┌"+"─".repeat(72)+"┐"),o(`│ ${p(a(e))} │`),o("├"+"─".repeat(72)+"┤"),(Array.isArray(n)?n:[n]).forEach(e=>{o(`│ ${a(e)} │`)}),o("└"+"─".repeat(72)+"┘")},jout:s,line:n}}function P(e){return String(e).trim().split(".")[0].replace(/[^a-z0-9-]/gi,"-").toLowerCase().replace(/^-+|-+$/g,"")||"site"}function A(e){const t=["B","KB","MB","GB","TB"];let o=0,s=e;for(;s>=1024&&o<t.length-1;)s/=1024,o++;return`${s.toFixed(s<10&&o>0?1:0)} ${t[o]}`}function C(e){return 0===e?"disabled":e<6e4?`${Math.round(e/1e3)}s`:e<36e5?`${Math.round(e/6e4)}m`:e<864e5?`${Math.round(e/36e5)}h`:`${Math.round(e/864e5)}d`}function D(e){try{const t=new URL(e);return/\.up\.railway\.app$/i.test(t.hostname)}catch(e){return!1}}async function q(e){const t=e,o=b.get("gateway"),s=process.env.DEPLOY_GATEWAY,n=t?"flag --url":o?"saved (login)":s?"env DEPLOY_GATEWAY":"default";return{gateway:(t||o||s||j).replace(/\/$/,""),source:n}}async function E(e){try{const t=new AbortController,o=setTimeout(()=>t.abort(),3500),s=await fetch(`${e}/health`,{signal:t.signal});if(clearTimeout(o),!s.ok)return`${s.status}`;const n=await s.json().catch(()=>({}));return null!=n&&n.ok?"ok":"? (no ok=true)"}catch(e){return`unreachable (${"AbortError"===e.name?"timeout":e.message})`}}function T(e){var t,o,s,n,a,r,i,l,c;const d=null!=(t=null!=(o=null==e?void 0:e.status)?o:null==e||null==(s=e.response)?void 0:s.status)?t:null,u=null!=(n=null!=(a=null==e?void 0:e.data)?a:null==e||null==(r=e.response)?void 0:r.data)?n:null;return{status:d,data:u,text:null==u?void 0:u.text,code:null!=(i=null!=(l=null!=(c=null==e?void 0:e.code)?c:null==u?void 0:u.code)?l:null==u?void 0:u.error)?i:null}}function R(e,t){const o=String(e).split(".").map(e=>parseInt(e,10)||0),s=String(t).split(".").map(e=>parseInt(e,10)||0),n=Math.max(o.length,s.length);for(let e=0;e<n;e++){const t=o[e]||0,n=s[e]||0;if(t>n)return!0;if(t<n)return!1}return!1}async function U({endpoint:e,gatewayUrl:o,tarPath:s,project:n,spa:a,email:l,password:c,sitePassword:d,onProgress:u}){const p=t.statSync(s).size,h=t.createReadStream(s,{highWaterMark:65536}),m=[];let w=0;h.on("data",e=>{m.push(e),w+=e.length;const t=Math.min(100,Math.round(w/p*100));null==u||u(t,w,p)}),await i(h,"end");const f=new r(m,{type:"application/gzip"}),g=new FormData;g.append("project",n),g.append("spa",a?"true":"false"),void 0!==d&&g.append("sitePassword",d),g.append("bundle",f,`${n}.tar.gz`);const y="Basic "+Buffer.from(`${l}:${c}`,"utf8").toString("base64"),b=new URL(`${o}${e}`);b.searchParams.set("project",n);const v=await fetch(b.toString(),{method:"POST",headers:{authorization:y,"x-email":l,"x-password":c},body:g});if(!v.ok){const t=await v.text().catch(()=>""),o=new Error(`HTTP ${v.status} on ${e}${t?`: ${t}`:""}`);throw o.status=v.status,o.response={status:v.status,data:{text:t}},o}return await v.json().catch(()=>({}))}async function M({project:e,sourceDir:o,tmpDir:n,gatewayUrl:r,email:i,password:l,sitePassword:c,spa:d=!0,onPackStart:u,onPackDone:p,onUploadProgress:h}){t.existsSync(n)||t.mkdirSync(n,{recursive:!0});const m=s.join(n,`${e}.tar.gz`);var w,f;null==u||u(),await(w=o,f=m,new Promise((e,t)=>{const o=s.resolve(w),n=a("tar",["-czf",f,"-C",o,"."],{stdio:["ignore","ignore","pipe"]});let r="";n.stderr.on("data",e=>r+=e.toString()),n.on("close",o=>{0===o?e():t(new Error(`tar exited with code ${o}${r?`: ${r}`:""}`))})}));const g=t.statSync(m).size;let y;null==p||p(g);for(const t of x)try{return null==h||h(0,0,g),await U({endpoint:t,gatewayUrl:r,tarPath:m,project:e,spa:d,email:i,password:l,sitePassword:c,onProgress:h})}catch(e){y=e}throw y||new Error("Upload failed")}function B(e,t){return`Basic ${Buffer.from(`${e}:${t}`,"utf8").toString("base64")}`}function L(e,t=0){const o=Number(e);return Number.isFinite(o)?o:t}function N(e){const t=["B","KB","MB","GB","TB"];let o=0,s=L(e,0);for(;s>=1024&&o<t.length-1;)s/=1024,o++;return`${s<10&&o>0?s.toFixed(1):Math.round(s).toString()} ${t[o]}`}async function I({interactive:e}){var t;const o=b.get("creds"),s=Date.now(),n=null!=(t=null==o?void 0:o.ttlMs)?t:k;if(o&&o.savedAt&&s-o.savedAt<n)return{email:o.email,password:o.password,fromCache:!0};if(!e)throw new Error("Credentials required");const a=await l.prompt([{type:"input",name:"email",message:" Email:",validate:e=>!!e||"Required"},{type:"password",name:"password",message:" Password:",mask:"*",validate:e=>!!e||"Required"}]);return b.set("creds",{email:a.email,password:a.password,savedAt:s,ttlMs:k}),y({},a,{firstTime:!o})}e.name("htm.sh").version(v).description("Deploy static sites with a minimal, fast CLI").argument("[path]","directory to publish (default: .)",".").argument("[domain]","desired domain (used as project name)").option("--no-spa","disable SPA fallback").option("--yes","skip confirmation prompt",!1).option("--quiet","suppress standard logs (errors still print)",!1).option("--json","machine-readable logs (CI-friendly)",!1).option("--password <password>","password protect the site").option("--remove-password","remove password protection").action(async(e,a,r)=>{const i=S({quiet:r.quiet,json:r.json});try{var c,u,p,h,m,w,f,g;i.blank(),i.banner(),b.get("seenTips")||(i.block("Welcome to htm.sh",["Deploy your static sites in seconds, straight from the shell.","Here are some quick tips to get you started:","","• Run with `npx htmsh@latest ...` to ensure the newest version.","• Or use `npm i -g htmsh@latest` to install global.","• Use `npx htmsh project-name` to choose the project name.","• Add a CNAME file in the folder to lock the subdomain.","• Create AUTH w/ file user:password to protect your site.","• Ensure a .html file exists in the root (e.g. index.html).","• Run `npx htmsh login` once to cache your credentials.","• Use --no-spa to disable routing fallback to index.html.","• Check your projects and quota with `npx htmsh quota`.","• Run `npx htmsh tips` to see these tips again!"]),b.set("seenTips",!0)),i.blank(),await async function(e){try{var t;const o=864e5,s=Date.now(),n=b.get("versionCheck");if((null==n?void 0:n.timestamp)&&s-n.timestamp<o&&(null==n?void 0:n.current)===v)return void(n.latest&&R(n.latest,v)&&(e.warn(`Update available: ${v} → ${n.latest}`),e.info(d("Run with `npx htmsh@latest` for the latest version or `npm i -g htmsh@latest`")),e.blank()));const a=await fetch("https://registry.npmjs.org/htmsh",{signal:AbortSignal.timeout(2e3)});if(!a.ok)return;const r=await a.json(),i=null==r||null==(t=r["dist-tags"])?void 0:t.latest,l=i&&R(i,v);b.set("versionCheck",{timestamp:s,current:v,latest:i,hasUpdate:l}),l&&(e.warn(`Update available: ${v} → ${i}`),e.info(d("Run with `npx htmsh@latest` for the latest version or `npm i -g htmsh@latest`")),e.blank())}catch(e){}}(i),i.section("Initialize"),i.row("Resolving","gateway");let{gateway:S,source:G}=await q(r.url);r.url&&b.set("gateway",S),i.row("Gateway",S,"cyan"),i.row("Source",G,"magenta");const O=await E(S);i.row("Healthy",O,O.includes("ok")?"green":"red"),S=await async function(e,t,o={}){if(!D(e))return e;if(!o.yes){t.block("GATEWAY WARNING",["Using a Railway host as gateway may cause 404 errors.",`Recommended: ${$}`]);const{change:o}=await l.prompt([{type:"confirm",name:"change",default:!0,message:"Switch gateway?"}]);if(!o)return e}return b.set("gateway",$),t.ok(`Gateway updated to ${$}`),$}(S,i,r),i.blank(),i.section("Project");const H=process.cwd(),F=s.resolve(H,e||".");t.existsSync(F)&&t.statSync(F).isDirectory()||(i.error(`Directory not found: ${F}`),process.exit(1)),function(e){try{return t.readdirSync(e).some(e=>e.toLowerCase().endsWith(".html"))}catch(e){return!1}}(F)||(i.blank(),i.error(`No .html file found in project root: ${F}`),i.info(d("Make sure your site has at least one entry point HTML file (e.g. index.html or 200.html).")),i.blank(),process.exit(1));let Y=a;if(!Y)for(const e of["CNAME"]){const o=s.join(F,e);if(t.existsSync(o)){const e=t.readFileSync(o,"utf8").trim();if(e){Y=e;break}}}Y||(Y=s.basename(F));let W=P(Y);const _=null==(c=r.spa)||c;let Q;const J=function(e){const o=s.join(e,"AUTH");return t.existsSync(o)&&t.readFileSync(o,"utf8").trim()||null}(F);J?(Q=J,i.row("Protection","From file (AUTH)","yellow")):r.removePassword?(Q="",i.row("Protection","Remove","green")):r.password&&(Q=r.password,i.row("Protection","Enabled","yellow"));const{filesCount:K,totalSize:V}=function(e){let o=0,n=0;const a=[e],r=new Set([".git","node_modules"]);for(;a.length;){const e=a.pop(),i=t.statSync(e);if(i.isDirectory()){const o=s.basename(e);if(r.has(o))continue;for(const o of t.readdirSync(e))a.push(s.join(e,o))}else i.isFile()&&(o++,n+=i.size)}return{filesCount:o,totalSize:n}}(F);i.row("Path",F,"white"),i.row("Files",K,"white"),i.row("Size",A(V),"white"),i.row("Project",W,"cyan"),i.row("Domain",`${W}.htm.sh`,"cyan"),i.row("SPA Routing",_?"enabled":"disabled","yellow"),i.blank(),i.section("Login");const X=await I({interactive:!0});if(i.row("Email",X.email,"green"),i.blank(),!r.yes){const{ok:e}=await l.prompt([{type:"confirm",name:"ok",message:"Proceed with deployment?",default:!0}]);e||(i.info("Cancelled"),process.exit(0))}i.blank(),i.section("Deploy"),i.subheader("Packaging and uploading...");let Z=0;const ee=s.join(o.tmpdir(),`htmsh-${Date.now()}-${n.randomBytes(4).toString("hex")}`);t.mkdirSync(ee,{recursive:!0}),i.bar("upload",0);let te,oe=null,se=0;for(;se<1;){se++;try{oe=await M({project:W,sourceDir:F,tmpDir:ee,gatewayUrl:S,email:X.email,password:X.password,sitePassword:Q,spa:_,onPackStart:()=>{},onPackDone:()=>{},onUploadProgress:e=>{e!==Z&&(i.bar("upload",e),Z=e)}}),i.bar("propagate",100),i.blank(),i.row("Deployment","completed","green"),i.blank();break}catch(e){var y;te=e;const{status:t}=T(e);i.blank(),i.warn(`Upload failed (attempt ${se}/1)${t?` [HTTP ${t}]`:""}`),i.blank();const o=null==e||null==(y=e.response)?void 0:y.data;if(409===t||"subdomain_conflict"===(null==o?void 0:o.error)){i.block("SUBDOMAIN CONFLICT",[`The subdomain '${W}' is already taken.`,"Choose a different one."]);const e=await l.prompt([{type:"input",name:"newProject",message:"New subdomain:",default:`${W}-1`,validate:e=>!!P(e)||"Use letters, numbers and hyphens."}]);W=P(e.newProject);continue}se>=1&&(i.error(`Deploy failed after 1 attempts: ${(null==e?void 0:e.message)||e}`),process.exit(1))}}var k,j,x,C,U,B,L,N,z;!oe&&te&&(i.error(`Deploy failed: ${(null==(k=te)?void 0:k.message)||te}`),process.exit(1)),i.section("Result"),i.row("Domain",(null==(u=oe)?void 0:u.subdomain)||`${W}.htm.sh`,"green"),i.row("Release",(null==(p=oe)?void 0:p.release)||"-","yellow"),i.row("Files",null!=(h=null==(m=oe)?void 0:m.filesCount)?h:K,"white"),i.row("Size",null!=(null==(w=oe)?void 0:w.sizeBytes)?A(oe.sizeBytes):A(V),"white"),void 0!==(null==(f=oe)?void 0:f.passwordProtected)&&i.row("Password",oe.passwordProtected?"Yes":"No",oe.passwordProtected?"yellow":"green"),i.blank(),i.row("Success! Your project is live at",(null==(g=oe)?void 0:g.subdomain)||`https://${W}.htm.sh`,"green"),i.blank(),r.json&&i.jout({event:"deployed",project:(null==(j=oe)?void 0:j.project)||W,subdomain:(null==(x=oe)?void 0:x.subdomain)||`${W}.htm.sh`,release:(null==(C=oe)?void 0:C.release)||"-",files:null!=(U=null==(B=oe)?void 0:B.filesCount)?U:K,sizeBytes:null!=(L=null==(N=oe)?void 0:N.sizeBytes)?L:V,passwordProtected:null==(z=oe)?void 0:z.passwordProtected,gateway:S}),process.exit(0)}catch(e){console.error(`error ${(null==e?void 0:e.message)||String(e)}`),process.exit(1)}}),e.command("tips").description("Show quick usage tips").option("--json","machine-readable output",!1).action(e=>{const t=S({json:e.json});t.blank(),t.banner(),t.block("Tips",["Deploy your static sites in seconds, straight from the shell.","Here are some quick tips to get you started:","","• Use `npx htmsh project-name` to choose the project name.","• Add a CNAME file in the folder to lock the subdomain.","• Create AUTH w/ file user:password to protect your site.","• Ensure a .html file exists in the root (e.g. index.html).","• Run `npx htmsh login` once to cache your credentials.","• Use --password <password> to protect your site.","• Use --no-spa to disable routing fallback to index.html.","• Check quota anytime with `npx htmsh quota`.","• Run `npx htmsh tips` to see these tips again!"])}),e.command("login").description("Cache gateway and credentials").option("--ttl <duration>","credential cache TTL (e.g. 15m, 1h, 24h)","24h").option("--quiet","suppress standard logs",!1).option("--json","machine-readable logs",!1).action(async e=>{var t;const o=S({quiet:e.quiet,json:e.json});o.blank(),o.banner(),o.section("Login");let s=e.url||b.get("gateway")||process.env.DEPLOY_GATEWAY||j;o.subheader("Please enter your credentials:");const{email:n,password:a}=await l.prompt([{type:"input",name:"email",message:" email:",validate:e=>!!e||"Required"},{type:"password",name:"password",message:" password:",mask:"*",validate:e=>!!e||"Required"}]),r=function(e,t){if(!e)return t;if("0"===e)return 0;const o=String(e).trim().match(/^(\d+)(ms|s|m|h|d)?$/i);if(!o)return t;const s=Number(o[1]),n=(o[2]||"ms").toLowerCase();return s*("ms"===n?1:"s"===n?1e3:"m"===n?6e4:"h"===n?36e5:864e5)}(null!=(t=e.ttl)?t:"24h",k);if(o.blank(),o.section("Session"),o.row("Gateway",s,"cyan"),D(s)){o.block("GATEWAY RECOMMENDATION",["Detected Railway host - this may cause issues.",`Consider using ${$} instead.`]);const{change:e}=await l.prompt([{type:"confirm",name:"change",default:!0,message:"Switch gateway?"}]);e&&(s=$,o.row("Gateway",s,"cyan"))}b.set("gateway",s),b.set("creds",{email:n,password:a,savedAt:Date.now(),ttlMs:r}),o.row("Email",n,"green"),o.row("Credentials","Stored","green"),o.row("Cache",C(r),"yellow"),e.json&&o.jout({event:"login",email:n,gateway:s,ttlMs:r})}),e.command("quota").description("Show storage quota and per-project usage").option("--json","machine-readable output",!1).option("--quiet","suppress standard logs",!1).action(async e=>{const t=S({quiet:e.quiet,json:e.json});t.blank(),t.banner(),t.section("quota");try{var o;const{gateway:s}=await q(),n=await I({interactive:!0}),a=["/quota","/api/quota"];let r=null;for(const e of a)try{const t=await fetch(`${s}${e}`,{method:"GET",headers:{authorization:B(n.email,n.password),"x-email":n.email,"x-password":n.password}});if(!t.ok){const o=await t.text().catch(()=>"");throw new Error(`HTTP ${t.status} on ${e}${o?`: ${o}`:""}`)}r=await t.json();break}catch(o){t.warn(`Failed on ${e}: ${o.message}`)}r||(t.error(`Could not fetch quota from ${s}.`),process.exit(1));const i=L(r.limitBytes,0),l=L(r.usedBytes,0),c=L(null!=(o=r.remainingBytes)?o:r.canUploadBytesLeft,i-l);if(t.row("Limit",N(i),"cyan"),t.row("Used",N(l),"white"),t.row("Remaining",N(c),"green"),t.blank(),Array.isArray(r.projects)&&r.projects.length){t.section("projects");for(const e of r.projects){const o=L(e.usedBytes,0),s=L(e.releases,0),n=e.lastDeployedAt?new Date(e.lastDeployedAt).toISOString().slice(0,10):"-";t.row(`${e.project}`,`${N(o)} • ${s} release(s) • last: ${n}`,"white")}t.blank()}else t.info("No projects found for this account.");e.json&&t.jout(y({event:"quota"},r))}catch(e){t.error((null==e?void 0:e.message)||String(e)),process.exit(1)}}),e.command("logout").description("Clear saved credentials").option("--quiet","suppress standard logs",!1).option("--json","machine-readable logs",!1).action(e=>{const t=S({quiet:e.quiet,json:e.json});b.delete("gateway"),b.delete("creds"),t.ok("Logged out"),t.info("Cached credentials cleared"),e.json&&t.jout({event:"logout"})}),e.command("whoami").description("Show current user and gateway status").option("--quiet","suppress standard logs",!1).option("--json","machine-readable logs",!1).action(async e=>{var t;const o=S({quiet:e.quiet,json:e.json});o.blank(),o.banner(),o.section("whoami");const s=b.get("gateway"),n=s?"saved (login)":"default",a=s||j;o.row("Gateway",a,"cyan"),o.row("Source",n,"magenta");const r=await E(a);o.row("Healthy",r,r.includes("ok")?"green":"red"),D(a)&&o.warn(`Consider switching to ${$}`);const i=b.get("creds");if(!i)return o.blank(),o.warn("No cached credentials"),o.info(d("Run `npx htmsh login` to authenticate")),o.blank(),void(e.json&&o.jout({event:"whoami",authenticated:!1,gateway:a}));const l=null!=(t=i.ttlMs)?t:k,c=Date.now()-(i.savedAt||0),u=Math.max(0,l-c);u>0?(o.row("Email",i.email,"green"),o.row("Expires in",C(u),"yellow"),e.json&&o.jout({event:"whoami",authenticated:!0,email:i.email,gateway:a,expiresInMs:u})):(o.blank(),o.warn(`Credentials expired for ${i.email}`),o.info(d("Run `htmsh login` to refresh")),o.blank(),e.json&&o.jout({event:"whoami",authenticated:!1,email:i.email,gateway:a,expired:!0}))}),e.command("docs").description("Show concise documentation on how to use htmsh").option("--section <name>","Show only one section (e.g. quickstart, install, login, deploy, spa, quota, password, troubleshoot)").option("--json","machine-readable output",!1).action(e=>{const t=S({json:e.json});t.blank(),t.banner();const o={quickstart:["Quickstart Deploy",["1) Just type: `npx htmsh ./dist project-name`.","2) Ensure there is a .html in the project root.","3) Your site: <project-name>.htm.sh and public fallback path."]],install:["Install / Run",["• NPX (recommended): `npx htmsh ...`","• Global: `npm i -g htmsh` → `htmsh ...`","• Node 18+ and `tar` required in PATH."]],login:["Login & Credentials",["• `npx htmsh login` stores email/password locally (Conf).","• TTL configurable: `npx htmsh login --ttl 24h`.","• `npx htmsh whoami` shows gateway health and cache expiry.","• `npx htmsh logout` clears saved credentials and gateway."]],naming:["Project Naming & Domains",["• Project name = subdomain label (sanitized).","• Choose via CLI arg: `npx htmsh ./dist my-site`.","• Or put a CNAME file in the folder (FQDN or label).","• Default domain: `<project>.htm.sh`.","• Custom FQDN requires DNS pointing (CNAME/ALIAS)."]],spa:["SPA Routing",["• Default: SPA routing enabled (unknown paths → index.html).","• Disable with `--no-spa` to return 404 on unknown paths.","• Good for React/Vue/SPA routers; disable for pure MPA."]],deploy:["Deploy",["• `npx htmsh [path] [project]` (default path is `.`).","• CLI packs directory (tar.gz) and uploads with progress.","• On subdomain conflict, CLI asks a new project label.","• Result shows domain, release, files and size."]],password:["Password Protection",["• Run: `npx htmsh ./dist --password mypass`","• Create AUTH w/ file user:password to protect your site.","• Remove: `npx htmsh ./dist --remove-password`.","• Manage later (if server exposes API):"," - `npx htmsh protect <project> --password <pwd>`"," - `npx htmsh password <project> --set <pwd>` or `--remove`","• Visitors will see the browser’s Basic Auth dialog."]],result:["Result & URLs",["• Domain: `<project>.htm.sh`."]],quota:["Quota",["• `npx htmsh quota` shows limit, used and remaining bytes.","• Also lists per-project usage and last deploy date.","• If limit=0, ask admin to set MAX_USER_BYTES on gateway."]],troubleshoot:["Troubleshooting",["• `Directory not found` → check the path argument.","• `No .html file found` → ensure index.html (or any .html) in root.","• 401 → login first or server missing auth support.","• Subdomain conflict → choose a different project name.","• Password issues → verify project ownership and credentials."]],tips:["Tips",["• `htmsh tips` or `npx htmsh tips` to re-read quick tips.","• `htmsh whoami` to validate gateway + credentials.","• `--json` output for CI logs and scripting.","• Create AUTH w/ file user:password to protect your site."]]},s=(e,o)=>t.block(e,o);if(e.section){const n=e.section.toLowerCase();if(!o[n])return t.error(`Unknown section: ${e.section}`),void t.info("Available: quickstart, install, login, naming, spa, deploy, password, result, quota, troubleshoot, tips");const[a,r]=o[n];return void s(a,r)}const n=["quickstart","install","login","naming","spa","deploy","password","result","quota","troubleshoot","tips"];for(const e of n){const[n,a]=o[e];s(n,a),t.blank()}}),e.configureHelp({helpWidth:80,optionTerm:e=>e.flags,commandTerm:e=>e.name(),helpInformation(){const e=[],t=(t="")=>e.push(t),o=u("─".repeat(72));t(p("Usage")),t(o),t(" npx htmsh [options] [command] [path] [domain]"),t(""),t(p("Description")),t(o),t(" Deploy static sites with a minimal, fast CLI"),t(""),t(p("Arguments")),t(o),t(` ${u("path:".padEnd(20))} directory to publish (default: .)`),t(` ${u("domain:".padEnd(20))} desired domain (used as project name)`),t(""),t(p("Options")),t(o);for(const e of this.visibleOptions()){const o=e.description||"";t(` ${u((e.flags+":").padEnd(20))} ${o}`)}t("");const s=this.visibleCommands();if(s.length){t(p("Commands")),t(o);for(const e of s)t(` ${u((e.name()+":").padEnd(20))} ${e.description()}`);t("")}return e.join("\n")}}),e.helpInformation=function(){const e=[],t=u("─".repeat(72));e.push(""),e.push(p("Usage")),e.push(t),e.push(" htmsh [options] [command] [path] [domain]"),e.push(""),e.push(p("Description")),e.push(t),e.push(" Deploy static sites with a minimal, fast CLI"),e.push(""),e.push(p("Arguments")),e.push(t),e.push(` ${u("path:".padEnd(20))} directory to publish (default: .)`),e.push(` ${u("domain:".padEnd(20))} desired domain (used as project name)`),e.push(""),e.push(p("Options")),e.push(t);for(const t of this.options)e.push(` ${u((t.flags+":").padEnd(20))} ${t.description||""}`);if(e.push(""),this.commands.length){e.push(p("Commands")),e.push(t);for(const t of this.commands)e.push(` ${u((t.name()+":").padEnd(20))} ${t.description()}`);e.push("")}return e.join("\n")},e.parse();