@xtrn/cli 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +9 -9
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as V}from"node:child_process";import*as R from"node:fs";import*as _ from"node:path";import{spawn as Y}from"node:child_process";import*as x from"node:fs";import*as M from"node:os";import*as c from"node:path";function X(o){let n=o;while(!0){let s=c.join(n,"node_modules");if(x.existsSync(s))return s;let t=c.dirname(n);if(t===n)return null;n=t}}function O(o,n,s){let t=c.dirname(o),e=X(t);if(!e)console.warn("[xtrn] Warning: node_modules not found for symlink");let i=c.join(s,"node_modules");if(e&&!x.existsSync(i))x.symlinkSync(e,i,"junction");let a=`export { XTRNState } from "@xtrn/server";
2
+ import{spawn as V}from"node:child_process";import*as R from"node:fs";import*as _ from"node:path";import{spawn as Y}from"node:child_process";import*as x from"node:fs";import*as T from"node:os";import*as c from"node:path";function X(o){let n=o;while(!0){let s=c.join(n,"node_modules");if(x.existsSync(s))return s;let t=c.dirname(n);if(t===n)return null;n=t}}function A(o,n,s){let t=c.dirname(o),e=X(t);if(!e)console.warn("[xtrn] Warning: node_modules not found for symlink");let i=c.join(s,"node_modules");if(e&&!x.existsSync(i))x.symlinkSync(e,i,"junction");let a=`export { XTRNState } from "@xtrn/server";
3
3
  export { default } from "${o}";
4
4
  `;x.writeFileSync(c.join(s,"_cf_entry.ts"),a);let g=`name = "xtrn-dev"
5
5
  main = "_cf_entry.ts"
@@ -17,7 +17,7 @@ class_name = "XTRNState"
17
17
  [[migrations]]
18
18
  tag = "v1"
19
19
  new_sqlite_classes = ["XTRNState"]
20
- `;x.writeFileSync(c.join(s,"wrangler.toml"),g)}async function A(o,n){let s=c.dirname(o),t=c.basename(o),e=c.join(n,t);x.copyFileSync(o,e);let i=X(s);if(!i)console.warn("[xtrn] Warning: node_modules not found for symlink");let a=c.join(n,"node_modules");if(i&&!x.existsSync(a))x.symlinkSync(i,a,"junction");let g=`export { XTRNState } from "@xtrn/server";
20
+ `;x.writeFileSync(c.join(s,"wrangler.toml"),g)}async function O(o,n){let s=c.dirname(o),t=c.basename(o),e=c.join(n,t);x.copyFileSync(o,e);let i=X(s);if(!i)console.warn("[xtrn] Warning: node_modules not found for symlink");let a=c.join(n,"node_modules");if(i&&!x.existsSync(a))x.symlinkSync(i,a,"junction");let g=`export { XTRNState } from "@xtrn/server";
21
21
  export { default } from "${`./${t}`}";
22
22
  `,m=c.join(n,"_cf_entry.ts");x.writeFileSync(m,g);let l=`name = "xtrn-dev"
23
23
  main = "_cf_entry.ts"
@@ -32,14 +32,14 @@ class_name = "XTRNState"
32
32
  tag = "v1"
33
33
  new_sqlite_classes = ["XTRNState"]
34
34
  `;return x.writeFileSync(c.join(n,"wrangler.toml"),l),await Z(n),c.join(n,"_cf_entry.js")}function Z(o){return new Promise((n,s)=>{let t=Y("wrangler",["deploy","--dry-run","--outdir","."],{cwd:o,stdio:["ignore","pipe","pipe"]}),e="";t.stdout?.on("data",()=>{}),t.stderr?.on("data",(i)=>{e+=i.toString()}),t.on("error",(i)=>{s(Error(`Failed to spawn wrangler: ${i.message}`))}),t.on("close",(i)=>{if(i!==0){s(Error(`wrangler deploy --dry-run failed (exit ${i}):
35
- ${e}`));return}n()})})}function $(){return x.mkdtempSync(c.join(M.tmpdir(),"xtrn-dev-"))}function S(o){x.rmSync(o,{recursive:!0,force:!0})}import*as N from"node:fs";import*as F from"node:path";import{Miniflare as G}from"miniflare";function K(o,n,s,t){let e=t||F.dirname(o);return{modules:!0,scriptPath:o,modulesRoot:e,compatibilityDate:"2026-02-14",compatibilityFlags:["nodejs_compat"],durableObjects:{XTRN_STATE:"XTRNState"},durableObjectsPersist:!0,port:n,bindings:s}}async function C(o,n,s,t){let e=K(o,n,s,t),i=new G(e);return await i.ready,i}function q(o){if(!N.existsSync(o))return{};let n=N.readFileSync(o,"utf-8"),s={};for(let t of n.split(`
36
- `)){let e=t.trim();if(!e||e.startsWith("#"))continue;let i=e.indexOf("=");if(i===-1)continue;s[e.slice(0,i)]=e.slice(i+1)}return s}async function W(o){return await(await o.dispatchFetch("http://localhost/details")).json()}var r={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",magenta:"\x1B[35m",white:"\x1B[37m",red:"\x1B[31m",gray:"\x1B[90m"};function P(o){let n="./index.ts",s=1234;for(let t of o)if(t.startsWith("--port="))s=Number.parseInt(t.slice(t.indexOf("=")+1),10);else if(!t.startsWith("--"))n=t;return{entryPoint:_.resolve(n),port:s}}async function k(o){for(let s=0;s<5;s++){try{let t=await fetch(`http://localhost:${o}/details`);if(t.ok)return await t.json()}catch{}await new Promise((t)=>setTimeout(t,200))}throw Error("Failed to fetch /details after server ready")}function D(o,n){let s=`http://localhost:${o}`;if(console.log(""),console.log(` ${r.bold}${r.cyan}${n.name}${r.reset} ${r.dim}${n.version}${r.reset}`),console.log(` ${r.green}➜${r.reset} ${r.bold}${s}${r.reset}`),console.log(""),n.tools.length>0){console.log(` ${r.bold}${r.white}Tools${r.reset}`);for(let t of n.tools){let e=t.tags.length>0?` ${r.dim}${t.tags.map((i)=>`[${i}]`).join(" ")}${r.reset}`:"";console.log(` ${r.cyan}${t.name}${r.reset}${e} ${r.gray}— ${t.description}${r.reset}`)}console.log("")}if(n.config.length>0){console.log(` ${r.bold}${r.white}Config${r.reset}`);for(let t of n.config)console.log(` ${r.yellow}${t.key}${r.reset} ${r.dim}${t.type}${r.reset}`);console.log("")}if(n.oauth)console.log(` ${r.bold}${r.white}OAuth${r.reset} ${r.magenta}${n.oauth.provider}${r.reset}`),console.log(` ${r.dim}authorize${r.reset} ${n.oauth.authorization_url}`),console.log(` ${r.dim}token${r.reset} ${n.oauth.token_url}`),console.log(` ${r.dim}callback${r.reset} ${n.oauth.callback_url}`),console.log(` ${r.dim}scopes${r.reset} ${n.oauth.scopes.join(", ")}`),console.log("")}function nn(o,n){let s=_.join(o,"wrangler.toml"),t=V("npx",["wrangler","dev","--config",s,"--port",String(n)],{cwd:o,stdio:["ignore","pipe","pipe"]}),e="",i=new Promise((a,f)=>{let g=(l)=>{if(l.toString().includes("Ready on"))t.stdout?.off("data",g),a()},m=(l)=>{e+=l.toString()};t.stdout?.on("data",g),t.stderr?.on("data",m),t.on("error",(l)=>f(l)),t.on("close",(l)=>{if(l!==0&&l!==null)f(Error(e.trim()||`wrangler exited with code ${l}`))})});return i.then(()=>{e="",t.stderr?.on("data",(a)=>{let f=a.toString();if(f.includes("ERROR")||f.includes("Error")||f.includes("error:"))process.stderr.write(`${r.red}[xtrn dev]${r.reset} ${f}`)})}),{proc:t,ready:i}}async function I(o){let{entryPoint:n,port:s}=P(o),t=_.dirname(n);if(!R.existsSync(n))console.error(`${r.red}[xtrn dev]${r.reset} Entry point not found: ${n}`),process.exit(1);if(!R.existsSync(_.join(t,"package.json")))console.error(`${r.red}[xtrn dev]${r.reset} No package.json found in ${t}`),process.exit(1);let e=_.join(t,".dev.vars"),i=q(e),a=$();O(n,i,a);let f=null,g=()=>{if(console.log(`
37
- ${r.dim}[xtrn dev] Shutting down...${r.reset}`),f)f.kill(),f=null;S(a),process.exit(0)};process.on("SIGINT",g),process.on("SIGTERM",g);try{console.log(`${r.dim}[xtrn dev] Starting...${r.reset}`);let m=nn(a,s);f=m.proc,f.on("error",(u)=>{console.error(`${r.red}[xtrn dev]${r.reset} Failed to start: ${u.message}`),S(a),process.exit(1)}),f.on("close",(u)=>{if(u!==0&&u!==null)console.error(`${r.red}[xtrn dev]${r.reset} Process exited with code ${u}`);S(a),process.exit(u??1)}),await m.ready;let l=await k(s);D(s,l),await new Promise(()=>{})}catch(m){if(f)f.kill();if(S(a),m instanceof Error)console.error(`${r.red}[xtrn dev]${r.reset} ${m.message}`);process.exit(1)}}import{mkdir as U,readFile as tn,writeFile as sn}from"node:fs/promises";import{dirname as on,join as h}from"node:path";import{fileURLToPath as en}from"node:url";async function H(o){let n=o[0],s=o[1]??"v1.0.0";if(!n)console.error("Error: <name> argument is required"),console.error("Usage: xtrn new <name> [version]"),process.exit(1);let t=s.startsWith("v")?s:`v${s}`,e=t.replace(/^v/,"");if(!/^\d+\.\d+\.\d+$/.test(e))console.error(`Error: Invalid version "${s}". Expected semver (e.g., v1.0.0)`),process.exit(1);let i=en(import.meta.url),a=on(i),f=h(a,"../templates"),g=h(process.cwd(),n);try{await U(g,{recursive:!1})}catch(m){if(m instanceof Error&&"code"in m&&m.code==="EEXIST")console.error(`Error: Directory "${n}" already exists`),process.exit(1);throw m}try{let m=[{src:"index.ts.template",dst:"index.ts"},{src:"package.json.template",dst:"package.json"},{src:"tsconfig.json.template",dst:"tsconfig.json"}];for(let l of m){let u=h(f,l.src),j=h(g,l.dst),w=await tn(u,"utf-8");if(w=w.replace(/\{\{NAME\}\}/g,n),w=w.replace(/\{\{VERSION\}\}/g,t),w=w.replace(/\{\{VERSION_BARE\}\}/g,e),l.dst==="package.json"){let d=JSON.parse(w);d.dependencies["@xtrn/server"]="^1.0.0",d.devDependencies["@xtrn/cli"]="^1.0.0",d.scripts.submit="xtrn submit",w=`${JSON.stringify(d,null,"\t")}
38
- `}await sn(j,w,"utf-8")}console.log(`✓ Created ${n}/`),console.log(""),console.log("Next steps:"),console.log(` cd ${n}`),console.log(" bun install"),console.log(" bun run dev")}catch(m){try{await U(g,{recursive:!0})}catch{}throw m}}import{spawn as rn}from"node:child_process";import*as y from"node:fs";import{cp as J,mkdir as z,readFile as an,readdir as fn,writeFile as mn}from"node:fs/promises";import*as T from"node:os";import*as v from"node:path";var p="xtrnai/servers",ln=new Set(["node_modules","bun.lock",".dev.vars","dist","_cf_entry.ts","wrangler.toml",".wrangler",".git",".github"]);function b(o,n,s){return new Promise((t)=>{let e=rn(o,n,{stdio:"pipe",cwd:s?.cwd}),i="",a="";e.stdout.on("data",(f)=>{i+=f.toString()}),e.stderr.on("data",(f)=>{a+=f.toString()}),e.on("close",(f)=>{t({stdout:i.trim(),stderr:a.trim(),code:f??1})}),e.on("error",()=>{t({stdout:"",stderr:`Failed to execute: ${o}`,code:1})})})}async function xn(){if((await b("gh",["--version"])).code!==0)console.error("[xtrn submit] GitHub CLI (gh) is not installed."),console.error(""),console.error("Install it:"),console.error(" macOS: brew install gh"),console.error(" Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md"),console.error(" Windows: winget install --id GitHub.cli"),console.error(""),console.error("Then authenticate:"),console.error(" gh auth login"),process.exit(1);if((await b("gh",["auth","status"])).code!==0)console.error("[xtrn submit] GitHub CLI is not authenticated."),console.error(""),console.error("Run:"),console.error(" gh auth login"),process.exit(1)}function cn(o){if(!y.existsSync(v.join(o,"index.ts")))console.error("[xtrn submit] No index.ts found in current directory."),console.error("Run this command from your XTRN server project root."),process.exit(1);if(!y.existsSync(v.join(o,"package.json")))console.error("[xtrn submit] No package.json found in current directory."),console.error("Run this command from your XTRN server project root."),process.exit(1)}async function gn(o){let n=v.resolve(o,"index.ts"),s=$(),t=null;try{console.log("[xtrn submit] Bundling server...");let e=await A(n,s);console.log("[xtrn submit] Starting headless Miniflare..."),t=await C(e,0,{});let i=await W(t),a=i.name,f=i.version;if(typeof a!=="string"||!a)throw Error("Server /details missing 'name' field");if(typeof f!=="string"||!f)throw Error("Server /details missing 'version' field");return console.log(`[xtrn submit] Detected: ${a} v${f}`),{name:a,version:f}}finally{if(t)await t.dispose();S(s)}}async function B(o,n){await z(n,{recursive:!0});let s=await fn(o,{withFileTypes:!0});for(let t of s){if(ln.has(t.name))continue;let e=v.join(o,t.name),i=v.join(n,t.name);if(t.isDirectory())await B(e,i);else await J(e,i)}}function un(o){let n=JSON.parse(o),s=(t)=>{if(!t||typeof t!=="object")return;let e={};for(let[i,a]of Object.entries(t))if(typeof a==="string"&&(a.startsWith("file:")||a.startsWith("link:")||a.startsWith("workspace:")))e[i]="^1.0.0";else e[i]=a;return e};if(n.dependencies)n.dependencies=s(n.dependencies);if(n.devDependencies)n.devDependencies=s(n.devDependencies);return`${JSON.stringify(n,null,"\t")}
39
- `}async function vn(o,n){console.log("[xtrn submit] Preparing files..."),await B(o,n);let s=v.join(n,"package.json");if(y.existsSync(s)){let t=await an(s,"utf-8"),e=un(t);await mn(s,e,"utf-8"),console.log("[xtrn submit] Rewrote package.json (local deps → ^1.0.0)")}}async function yn(o,n,s){let t=y.mkdtempSync(v.join(T.tmpdir(),"xtrn-submit-")),e=v.join(t,"xtrn-servers");try{console.log(`[xtrn submit] Forking ${p}...`);let i=await b("gh",["repo","fork",p,"--clone=true","--default-branch-only"],{cwd:t});if(i.code!==0&&!i.stderr.includes("already exists"))throw Error(`Fork failed: ${i.stderr}`);if(!y.existsSync(e))throw Error(`Expected cloned repo at ${e}, not found. Output: ${i.stdout} ${i.stderr}`);let a=`submit/${o}/v${n}`;console.log(`[xtrn submit] Creating branch ${a}...`);let f=await b("git",["checkout","-b",a],{cwd:e});if(f.code!==0)throw Error(`Branch creation failed: ${f.stderr}`);let g=v.join(e,"servers",o,`v${n}`);await z(g,{recursive:!0}),await J(s,g,{recursive:!0}),console.log("[xtrn submit] Committing...");let m=await b("git",["add","."],{cwd:e});if(m.code!==0)throw Error(`git add failed: ${m.stderr}`);let l=await b("git",["commit","-m",`Add ${o} v${n}`],{cwd:e});if(l.code!==0)throw Error(`git commit failed: ${l.stderr}`);console.log("[xtrn submit] Pushing...");let u=await b("git",["push","origin",a],{cwd:e});if(u.code!==0)throw Error(`git push failed: ${u.stderr}`);console.log("[xtrn submit] Creating pull request...");let j=await b("gh",["pr","create","--repo",p,"--title",`Add ${o} v${n}`,"--body",`Automated submission via \`xtrn submit\` CLI.
35
+ ${e}`));return}n()})})}function E(){return x.mkdtempSync(c.join(T.tmpdir(),"xtrn-dev-"))}function S(o){x.rmSync(o,{recursive:!0,force:!0})}import*as $ from"node:fs";import*as F from"node:path";import{Miniflare as G}from"miniflare";function K(o,n,s,t){let e=t||F.dirname(o);return{modules:!0,scriptPath:o,modulesRoot:e,compatibilityDate:"2026-02-14",compatibilityFlags:["nodejs_compat"],durableObjects:{XTRN_STATE:"XTRNState"},durableObjectsPersist:!0,port:n,bindings:s}}async function C(o,n,s,t){let e=K(o,n,s,t),i=new G(e);return await i.ready,i}function q(o){if(!$.existsSync(o))return{};let n=$.readFileSync(o,"utf-8"),s={};for(let t of n.split(`
36
+ `)){let e=t.trim();if(!e||e.startsWith("#"))continue;let i=e.indexOf("=");if(i===-1)continue;s[e.slice(0,i)]=e.slice(i+1)}return s}async function W(o){return await(await o.dispatchFetch("http://localhost/details")).json()}var r={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",magenta:"\x1B[35m",white:"\x1B[37m",red:"\x1B[31m",gray:"\x1B[90m"};function P(o){let n="./index.ts",s=1234;for(let t of o)if(t.startsWith("--port="))s=Number.parseInt(t.slice(t.indexOf("=")+1),10);else if(!t.startsWith("--"))n=t;return{entryPoint:_.resolve(n),port:s}}async function k(o){for(let s=0;s<5;s++){try{let t=await fetch(`http://localhost:${o}/details`);if(t.ok)return await t.json()}catch{}await new Promise((t)=>setTimeout(t,200))}throw Error("Failed to fetch /details after server ready")}function D(o,n){let s=`http://localhost:${o}`;if(console.log(""),console.log(` ${r.bold}${r.cyan}${n.name}${r.reset} ${r.dim}${n.version}${r.reset}`),console.log(` ${r.green}➜${r.reset} ${r.bold}${s}${r.reset}`),console.log(""),n.tools.length>0){console.log(` ${r.bold}${r.white}Tools${r.reset}`);for(let t of n.tools){let e=t.tags.length>0?` ${r.dim}${t.tags.map((i)=>`[${i}]`).join(" ")}${r.reset}`:"";console.log(` ${r.cyan}${t.name}${r.reset}${e} ${r.gray}— ${t.description}${r.reset}`)}console.log("")}if(n.config.length>0){console.log(` ${r.bold}${r.white}Config${r.reset}`);for(let t of n.config)console.log(` ${r.yellow}${t.key}${r.reset} ${r.dim}${t.type}${r.reset}`);console.log("")}if(n.oauth)console.log(` ${r.bold}${r.white}OAuth${r.reset} ${r.magenta}${n.oauth.provider}${r.reset}`),console.log(` ${r.dim}authorize${r.reset} ${n.oauth.authorization_url}`),console.log(` ${r.dim}token${r.reset} ${n.oauth.token_url}`),console.log(` ${r.dim}callback${r.reset} ${n.oauth.callback_url}`),console.log(` ${r.dim}scopes${r.reset} ${n.oauth.scopes.join(", ")}`),console.log("")}function nn(o,n){let s=_.join(o,"wrangler.toml"),t=V("npx",["wrangler","dev","--config",s,"--port",String(n)],{cwd:o,stdio:["ignore","pipe","pipe"]}),e="",i=new Promise((a,f)=>{let g=(l)=>{if(l.toString().includes("Ready on"))t.stdout?.off("data",g),a()},m=(l)=>{e+=l.toString()};t.stdout?.on("data",g),t.stderr?.on("data",m),t.on("error",(l)=>f(l)),t.on("close",(l)=>{if(l!==0&&l!==null)f(Error(e.trim()||`wrangler exited with code ${l}`))})});return i.then(()=>{e="",t.stderr?.on("data",(a)=>{let f=a.toString();if(f.includes("ERROR")||f.includes("Error")||f.includes("error:"))process.stderr.write(`${r.red}[xtrn dev]${r.reset} ${f}`)})}),{proc:t,ready:i}}async function I(o){let{entryPoint:n,port:s}=P(o),t=_.dirname(n);if(!R.existsSync(n))console.error(`${r.red}[xtrn dev]${r.reset} Entry point not found: ${n}`),process.exit(1);if(!R.existsSync(_.join(t,"package.json")))console.error(`${r.red}[xtrn dev]${r.reset} No package.json found in ${t}`),process.exit(1);let e=_.join(t,".dev.vars"),i=q(e),a=E();A(n,i,a);let f=null,g=()=>{if(console.log(`
37
+ ${r.dim}[xtrn dev] Shutting down...${r.reset}`),f)f.kill(),f=null;S(a),process.exit(0)};process.on("SIGINT",g),process.on("SIGTERM",g);try{console.log(`${r.dim}[xtrn dev] Starting...${r.reset}`);let m=nn(a,s);f=m.proc,f.on("error",(u)=>{console.error(`${r.red}[xtrn dev]${r.reset} Failed to start: ${u.message}`),S(a),process.exit(1)}),f.on("close",(u)=>{if(u!==0&&u!==null)console.error(`${r.red}[xtrn dev]${r.reset} Process exited with code ${u}`);S(a),process.exit(u??1)}),await m.ready;let l=await k(s);D(s,l),await new Promise(()=>{})}catch(m){if(f)f.kill();if(S(a),m instanceof Error)console.error(`${r.red}[xtrn dev]${r.reset} ${m.message}`);process.exit(1)}}import{mkdir as U,readFile as tn,writeFile as sn}from"node:fs/promises";import{dirname as on,join as N}from"node:path";import{fileURLToPath as en}from"node:url";async function H(o){let n=o[0],s=o[1]??"v1.0.0";if(!n)console.error("Error: <name> argument is required"),console.error("Usage: xtrn new <name> [version]"),process.exit(1);let t=s.startsWith("v")?s:`v${s}`,e=t.replace(/^v/,"");if(!/^\d+\.\d+\.\d+$/.test(e))console.error(`Error: Invalid version "${s}". Expected semver (e.g., v1.0.0)`),process.exit(1);let i=en(import.meta.url),a=on(i),f=N(a,"../templates"),g=N(process.cwd(),n);try{await U(g,{recursive:!1})}catch(m){if(m instanceof Error&&"code"in m&&m.code==="EEXIST")console.error(`Error: Directory "${n}" already exists`),process.exit(1);throw m}try{let m=[{src:"index.ts.template",dst:"index.ts"},{src:"package.json.template",dst:"package.json"},{src:"tsconfig.json.template",dst:"tsconfig.json"},{src:"README.md.template",dst:"README.md"}];for(let l of m){let u=N(f,l.src),j=N(g,l.dst),w=await tn(u,"utf-8");if(w=w.replace(/\{\{NAME\}\}/g,n),w=w.replace(/\{\{VERSION\}\}/g,t),w=w.replace(/\{\{VERSION_BARE\}\}/g,e),l.dst==="package.json"){let d=JSON.parse(w);d.dependencies["@xtrn/server"]="^1.0.0",d.devDependencies["@xtrn/cli"]="^1.0.0",d.scripts.submit="xtrn submit",w=`${JSON.stringify(d,null,"\t")}
38
+ `}await sn(j,w,"utf-8")}console.log(`✓ Created ${n}/`),console.log(""),console.log("Next steps:"),console.log(` cd ${n}`),console.log(" bun install"),console.log(" bun run dev")}catch(m){try{await U(g,{recursive:!0})}catch{}throw m}}import{spawn as rn}from"node:child_process";import*as y from"node:fs";import{cp as J,mkdir as z,readFile as an,readdir as fn,writeFile as mn}from"node:fs/promises";import*as M from"node:os";import*as v from"node:path";var p="xtrnai/servers",ln=new Set(["node_modules","bun.lock",".dev.vars","dist","_cf_entry.ts","wrangler.toml",".wrangler",".git",".github"]);function b(o,n,s){return new Promise((t)=>{let e=rn(o,n,{stdio:"pipe",cwd:s?.cwd}),i="",a="";e.stdout.on("data",(f)=>{i+=f.toString()}),e.stderr.on("data",(f)=>{a+=f.toString()}),e.on("close",(f)=>{t({stdout:i.trim(),stderr:a.trim(),code:f??1})}),e.on("error",()=>{t({stdout:"",stderr:`Failed to execute: ${o}`,code:1})})})}async function xn(){if((await b("gh",["--version"])).code!==0)console.error("[xtrn submit] GitHub CLI (gh) is not installed."),console.error(""),console.error("Install it:"),console.error(" macOS: brew install gh"),console.error(" Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md"),console.error(" Windows: winget install --id GitHub.cli"),console.error(""),console.error("Then authenticate:"),console.error(" gh auth login"),process.exit(1);if((await b("gh",["auth","status"])).code!==0)console.error("[xtrn submit] GitHub CLI is not authenticated."),console.error(""),console.error("Run:"),console.error(" gh auth login"),process.exit(1)}function cn(o){if(!y.existsSync(v.join(o,"index.ts")))console.error("[xtrn submit] No index.ts found in current directory."),console.error("Run this command from your XTRN server project root."),process.exit(1);if(!y.existsSync(v.join(o,"package.json")))console.error("[xtrn submit] No package.json found in current directory."),console.error("Run this command from your XTRN server project root."),process.exit(1)}async function gn(o){let n=v.resolve(o,"index.ts"),s=E(),t=null;try{console.log("[xtrn submit] Bundling server...");let e=await O(n,s);console.log("[xtrn submit] Starting headless Miniflare..."),t=await C(e,0,{});let i=await W(t),a=i.name,f=i.version;if(typeof a!=="string"||!a)throw Error("Server /details missing 'name' field");if(typeof f!=="string"||!f)throw Error("Server /details missing 'version' field");return console.log(`[xtrn submit] Detected: ${a} v${f}`),{name:a,version:f}}finally{if(t)await t.dispose();S(s)}}async function B(o,n){await z(n,{recursive:!0});let s=await fn(o,{withFileTypes:!0});for(let t of s){if(ln.has(t.name))continue;let e=v.join(o,t.name),i=v.join(n,t.name);if(t.isDirectory())await B(e,i);else await J(e,i)}}function un(o){let n=JSON.parse(o),s=(t)=>{if(!t||typeof t!=="object")return;let e={};for(let[i,a]of Object.entries(t))if(typeof a==="string"&&(a.startsWith("file:")||a.startsWith("link:")||a.startsWith("workspace:")))e[i]="^1.0.0";else e[i]=a;return e};if(n.dependencies)n.dependencies=s(n.dependencies);if(n.devDependencies)n.devDependencies=s(n.devDependencies);return`${JSON.stringify(n,null,"\t")}
39
+ `}async function vn(o,n){console.log("[xtrn submit] Preparing files..."),await B(o,n);let s=v.join(n,"package.json");if(y.existsSync(s)){let t=await an(s,"utf-8"),e=un(t);await mn(s,e,"utf-8"),console.log("[xtrn submit] Rewrote package.json (local deps → ^1.0.0)")}}async function yn(o,n,s){let t=y.mkdtempSync(v.join(M.tmpdir(),"xtrn-submit-")),e=v.join(t,"xtrn-servers");try{console.log(`[xtrn submit] Forking ${p}...`);let i=await b("gh",["repo","fork",p,"--clone=true","--default-branch-only"],{cwd:t});if(i.code!==0&&!i.stderr.includes("already exists"))throw Error(`Fork failed: ${i.stderr}`);if(!y.existsSync(e))throw Error(`Expected cloned repo at ${e}, not found. Output: ${i.stdout} ${i.stderr}`);let a=`submit/${o}/v${n}`;console.log(`[xtrn submit] Creating branch ${a}...`);let f=await b("git",["checkout","-b",a],{cwd:e});if(f.code!==0)throw Error(`Branch creation failed: ${f.stderr}`);let g=v.join(e,"servers",o,`v${n}`);await z(g,{recursive:!0}),await J(s,g,{recursive:!0}),console.log("[xtrn submit] Committing...");let m=await b("git",["add","."],{cwd:e});if(m.code!==0)throw Error(`git add failed: ${m.stderr}`);let l=await b("git",["commit","-m",`Add ${o} v${n}`],{cwd:e});if(l.code!==0)throw Error(`git commit failed: ${l.stderr}`);console.log("[xtrn submit] Pushing...");let u=await b("git",["push","origin",a],{cwd:e});if(u.code!==0)throw Error(`git push failed: ${u.stderr}`);console.log("[xtrn submit] Creating pull request...");let j=await b("gh",["pr","create","--repo",p,"--title",`Add ${o} v${n}`,"--body",`Automated submission via \`xtrn submit\` CLI.
40
40
 
41
41
  **Server:** ${o}
42
- **Version:** v${n}`],{cwd:e});if(j.code!==0)throw Error(`PR creation failed: ${j.stderr}`);return j.stdout}finally{y.rmSync(t,{recursive:!0,force:!0})}}async function L(o){let n=process.cwd();await xn(),cn(n);let{name:s,version:t}=await gn(n),e=y.mkdtempSync(v.join(T.tmpdir(),"xtrn-prepared-"));try{await vn(n,e);let i=await yn(s,t,e);console.log(""),console.log("✓ Pull request created!"),console.log(` ${i}`),console.log(""),console.log("Your server will be reviewed and deployed automatically once merged.")}finally{y.rmSync(e,{recursive:!0,force:!0})}}var Q="1.0.0",E=`
42
+ **Version:** v${n}`],{cwd:e});if(j.code!==0)throw Error(`PR creation failed: ${j.stderr}`);return j.stdout}finally{y.rmSync(t,{recursive:!0,force:!0})}}async function L(o){let n=process.cwd();await xn(),cn(n);let{name:s,version:t}=await gn(n),e=y.mkdtempSync(v.join(M.tmpdir(),"xtrn-prepared-"));try{await vn(n,e);let i=await yn(s,t,e);console.log(""),console.log("✓ Pull request created!"),console.log(` ${i}`),console.log(""),console.log("Your server will be reviewed and deployed automatically once merged.")}finally{y.rmSync(e,{recursive:!0,force:!0})}}var Q="1.0.0",h=`
43
43
  xtrn v${Q} — XTRN server development toolkit
44
44
 
45
45
  Usage:
@@ -63,4 +63,4 @@ Examples:
63
63
  xtrn dev ./server.ts Start dev server with custom entry
64
64
  xtrn dev --port=3000 Start dev server on port 3000
65
65
  xtrn submit Fork xtrn-servers and open PR
66
- `.trim();async function wn(){let o=process.argv.slice(2),n=o[0];if(!n||n==="--help"||n==="-h")console.log(E),process.exit(0);if(n==="--version"||n==="-v")console.log(`xtrn v${Q}`),process.exit(0);if(n==="new"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(E),process.exit(0);await H(s);return}if(n==="dev"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(E),process.exit(0);await I(s);return}if(n==="submit"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(E),process.exit(0);await L(s);return}console.error(`Unknown command: ${n}`),console.error('Run "xtrn --help" for usage.'),process.exit(1)}wn().catch((o)=>{console.error("[xtrn] Fatal:",o),process.exit(1)});
66
+ `.trim();async function wn(){let o=process.argv.slice(2),n=o[0];if(!n||n==="--help"||n==="-h")console.log(h),process.exit(0);if(n==="--version"||n==="-v")console.log(`xtrn v${Q}`),process.exit(0);if(n==="new"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(h),process.exit(0);await H(s);return}if(n==="dev"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(h),process.exit(0);await I(s);return}if(n==="submit"){let s=o.slice(1);if(s.includes("--help")||s.includes("-h"))console.log(h),process.exit(0);await L(s);return}console.error(`Unknown command: ${n}`),console.error('Run "xtrn --help" for usage.'),process.exit(1)}wn().catch((o)=>{console.error("[xtrn] Fatal:",o),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtrn/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "xtrn": "./dist/cli.js"