signup 0.2.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +36 -0
  2. package/package.json +20 -0
package/dist/cli.mjs ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import{basename as ht}from"path";var it=process.env.NO_COLOR==null&&process.stdout.isTTY;function y(t,n){return it?`\x1B[${t}m${n}\x1B[0m`:n}function $(t){return y(32,t)}function w(t){return y(31,t)}function s(t){return y(2,t)}function g(t){return y(1,t)}function b(t){return y(36,t)}function p(t){console.error(w("✗")+" "+t)}function O(){let t="",n=0;return{start(o){if(n++,t=o,process.stdout.isTTY)process.stdout.write(` ${s(`${n}.`)} ${o}...\r`)},complete(o){let r=o||t;process.stdout.write(`\x1B[K ${$("✓")} ${r}
3
+ `)},fail(o){let r=o||t;process.stdout.write(`\x1B[K ${w("✗")} ${r}
4
+ `)}}}function R(t){return t.replace(/\x1b\[[0-9;]*m/g,"").length}function T(t,n){let r=Math.max(t.length+2,...n.map((c)=>R(c)))+4,e=` ┌─ ${t} ${"─".repeat(Math.max(0,r-t.length-5))}┐`,i=` │${" ".repeat(r)}│`,a=` └${"─".repeat(r)}┘`;console.log(s(e)),console.log(s(i));for(let c of n){let l=r-R(c)-2;console.log(s(" │")+` ${c}${" ".repeat(Math.max(0,l))}`+s("│"))}console.log(s(i)),console.log(s(a))}var z="https://api.tripwirejs.com/gate/registry";async function P(t,n){let o=`${n||z}/${encodeURIComponent(t.toLowerCase())}`,r=await fetch(o,{signal:AbortSignal.timeout(5000)});if(r.status===404)throw Error(`Unknown service ${g(t)}. Run ${s("npx signup --list")} to see available services.`);if(!r.ok)throw Error(`Failed to look up service: ${r.status}`);let{data:e}=await r.json();return e}async function J(t){let o=await fetch(t||z,{signal:AbortSignal.timeout(5000)});if(!o.ok)throw Error(`Failed to fetch service registry: ${o.status}`);let{data:r}=await o.json();return r}async function I(t,n){let o=await fetch(`${t}/gate/sessions`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)});if(!o.ok){let e=(await o.json().catch(()=>null))?.error?.message||`Server returned ${o.status}`;throw Error(e)}return o.json()}async function M(t,n){let o=await fetch(`${t}/gate/magic-link`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}});if(!o.ok){let e=(await o.json().catch(()=>null))?.error?.message||`Server returned ${o.status}`;throw Error(e)}return o.json()}async function G(t,n,o){let r=await fetch(`${t}/gate/sessions/${n}`,{headers:{Authorization:`Bearer ${o}`}});if(!r.ok){let i=(await r.json().catch(()=>null))?.error?.message||`Server returned ${r.status}`;throw Error(i)}return r.json()}import{createServer as st}from"http";var at=`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Done</title><style>
5
+ *{box-sizing:border-box;margin:0;padding:0}
6
+ :root{--bg:#ffffff;--text:#30313d;--text-secondary:#6d6e78;--success:#1a7f37;--success-bg:rgba(26,127,55,0.08);--btn-bg:#f5f6f8;--btn-text:#30313d}
7
+ @media(prefers-color-scheme:dark){:root{--bg:#1E1F2C;--text:#e4e4e8;--text-secondary:#9a9ba3;--success:#3fb950;--success-bg:rgba(63,185,80,0.1);--btn-bg:#262736;--btn-text:#e4e4e8}}
8
+ body{font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased}
9
+ .wrap{text-align:center;max-width:380px;padding:28px}
10
+ .icon{width:48px;height:48px;margin:0 auto 14px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:var(--success);background:var(--success-bg)}
11
+ h2{font-size:17px;font-weight:600;margin-bottom:6px}
12
+ p{font-size:14px;color:var(--text-secondary);line-height:1.4}
13
+ </style></head><body><div class="wrap"><div class="icon"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></div><h2>Approved</h2><p>You can now close this window.</p></div></body></html>`;function E(){return new Promise((t)=>{let n,o=new Promise((e)=>{n=e}),r=st((e,i)=>{if(!e.url?.startsWith("/callback")){i.writeHead(404),i.end();return}let c=new URL(e.url,"http://localhost").searchParams.get("status")||"error";i.writeHead(200,{"Content-Type":"text/html"}),i.end(at),n(c)});r.listen(0,"127.0.0.1",()=>{let e=r.address(),i=typeof e==="object"&&e?e.port:0;t({port:i,waitForCallback:()=>o,close:()=>r.close()})})})}import{exec as u,execSync as L}from"child_process";var h=420,m=620;function ct(){if(process.platform==="darwin")try{let t=L("system_profiler SPDisplaysDataType 2>/dev/null | grep Resolution | head -1",{encoding:"utf8",timeout:3000}),n=t.match(/(\d+)\s*x\s*(\d+)/);if(n){let o=parseInt(n[1],10),r=parseInt(n[2],10);if(t.includes("Retina"))o=Math.round(o/1.68),r=Math.round(r/1.68);return{w:o,h:r}}}catch{}return{w:1440,h:900}}function gt(){if(process.platform!=="darwin")return!1;try{return L('test -d "/Applications/Google Chrome.app"',{stdio:"pipe",timeout:1000}),!0}catch{return!1}}function pt(){try{return L(`osascript -e 'tell application "System Events" to return true'`,{stdio:"pipe",timeout:2000}),!0}catch{return!1}}function B(t){let n=process.platform==="darwin"?"open":process.platform==="win32"?'start ""':"xdg-open";u(`${n} ${JSON.stringify(t)}`,(o)=>{if(o)console.log(`
14
+ Open this URL in your browser:
15
+ ${t}
16
+ `)})}function D(t){let{w:n,h:o}=ct(),r=Math.round((n-h)/2),e=Math.round((o-m)/2);if(process.platform==="darwin"){let i=pt();if(gt())u(`open -na "Google Chrome" --args --app=${JSON.stringify(t)} --window-size=${h},${m} --window-position=-9999,-9999`,(a)=>{if(a){u(`open ${JSON.stringify(t)}`);return}if(i)setTimeout(()=>{u(`osascript -e 'tell application "Google Chrome" to set bounds of front window to {${r}, ${e}, ${r+h}, ${e+m}}' -e 'tell application "Google Chrome" to activate'`)},300)});else u(`open -a Safari ${JSON.stringify(t)}`,()=>{if(i)setTimeout(()=>{u(`osascript -e 'tell application "Safari" to set bounds of front window to {${r}, ${e}, ${r+h}, ${e+m}}' -e 'tell application "Safari" to activate'`)},500)})}else if(process.platform==="win32")u(`start "" "chrome" --app=${JSON.stringify(t)} --window-size=${h},${m}`,(i)=>{if(i)u(`start "" ${JSON.stringify(t)}`)});else u(`google-chrome --app=${JSON.stringify(t)} --window-size=${h},${m} 2>/dev/null || chromium --app=${JSON.stringify(t)} --window-size=${h},${m} 2>/dev/null`,(i)=>{if(i)u(`xdg-open ${JSON.stringify(t)}`)})}import{readFileSync as ft,writeFileSync as ut,existsSync as dt}from"fs";import{join as lt}from"path";function j(t,n){let o=lt(t,".env"),r=new Set(Object.keys(n)),e=[];if(dt(o)){let i=ft(o,"utf8");for(let a of i.split(`
17
+ `)){let c=a.indexOf("="),l=c>0?a.slice(0,c).trim():"";if(l&&r.has(l))continue;e.push(a)}if(e.length>0&&e[e.length-1].trim()!=="")e.push("")}for(let[i,a]of Object.entries(n))e.push(`${i}=${a}`);return e.push(""),ut(o,e.join(`
18
+ `),"utf8"),o}var F=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];function Y(t){if(!process.stdout.isTTY)return console.log(t),{update(e){console.log(e)},stop(e){if(e)console.log(e)}};let n=0,o=t,r=setInterval(()=>{let e=F[n%F.length];process.stdout.write(`\r\x1B[K${e} ${o}`),n++},80);return{update(e){o=e},stop(e){if(clearInterval(r),process.stdout.write("\r\x1B[K"),e)console.log(e)}}}async function A(t){let n=O();console.log(),n.start(`Looking up ${g(t.service)}`);let o;try{o=await P(t.service,t.registryUrl),n.complete(`Found ${g(o.name)}`)}catch(f){n.fail("Service not found"),p(f instanceof Error?f.message:String(f)),process.exit(1)}if(o.branding?.ascii_art){console.log();for(let f of o.branding.ascii_art.split(`
19
+ `))console.log(` ${s(f)}`);console.log()}let{port:r,waitForCallback:e,close:i}=await E(),a=`http://localhost:${r}/callback`;n.start("Creating signup session");let c=ht(process.cwd()),l;try{l=await I(o.gate_api_url,{account_name:c}),n.complete("Signup session created")}catch(f){n.fail("Failed to create session"),i(),p(f instanceof Error?f.message:String(f)),process.exit(1)}let{id:X,poll_token:Z,consent_url:V,expires_at:q}=l.data,U=`${V}&callback=${encodeURIComponent(a)}`;n.start("Opening browser for approval"),D(U),n.complete("Browser opened"),console.log();let S=Y("Waiting for approval..."),tt=setTimeout(()=>{S.stop(` ${w("✗")} Session expired`),console.log(),p("The signup session expired. Run the command again to retry."),i(),process.exit(1)},Math.max(0,new Date(q).getTime()-Date.now())),ot=await e();if(clearTimeout(tt),i(),ot!=="approved")S.stop(` ${w("✗")} Cancelled`),console.log(),p("The signup was cancelled. Run the command again to retry."),process.exit(1);S.update("Fetching API keys...");let d,v;for(let f=0;f<10;f++){try{if(v=await G(o.gate_api_url,X,Z),d=v.data.api_keys,d?.publishable_key&&d?.secret_key)break}catch{}await new Promise((et)=>setTimeout(et,300))}if(!d?.publishable_key||!d?.secret_key)S.stop(` ${w("✗")} No credentials received`),console.log(),p("The server did not return API keys. Try again."),process.exit(1);S.stop(` ${$("✓")} Approved`),console.log();let nt={[o.env_vars.publishable_key]:d.publishable_key,[o.env_vars.secret_key]:d.secret_key},rt=j(process.cwd(),nt);T(o.name,[`${s(o.env_vars.publishable_key)} ${d.publishable_key}`,`${s(o.env_vars.secret_key)} ${d.secret_key.slice(0,24)}...`,"",`${s("Written to")} ${rt}`]);let C=v.data.docs_url||o.docs_url,_=v.data.quickstart_url||o.quickstart_url;if(_||C){if(console.log(),console.log(` ${g("Next steps")}`),_)console.log(` ${s("Quickstart")} ${b(_)}`);if(C&&C!==_)console.log(` ${s("Docs")} ${b(C)}`)}console.log(),process.exit(0)}import{readFileSync as mt,existsSync as wt}from"fs";import{join as St}from"path";function W(t,n){let o=St(t,".env");if(!wt(o))return null;let r=mt(o,"utf8");for(let e of r.split(`
20
+ `)){let i=e.indexOf("=");if(i<=0)continue;if(e.slice(0,i).trim()===n)return e.slice(i+1).trim()}return null}async function H(t){let n=O();console.log(),n.start(`Looking up ${g(t.service)}`);let o;try{o=await P(t.service,t.registryUrl),n.complete(`Found ${g(o.name)}`)}catch(i){n.fail("Service not found"),p(i instanceof Error?i.message:String(i)),process.exit(1)}n.start("Reading API key from .env");let r=W(process.cwd(),o.env_vars.secret_key);if(!r)n.fail("No API key found"),console.log(),console.log(` Run ${b(`npx signup ${o.id}`)} first to create an account.`),console.log(),process.exit(1);n.complete(`Authenticating with ${s(r.slice(0,16))}...`),n.start("Creating login session");let e;try{e=await M(o.gate_api_url,r),n.complete("Login session created")}catch(i){n.fail("Failed to authenticate"),console.log(),p(i instanceof Error?i.message:String(i)),process.exit(1)}n.start("Opening dashboard"),B(e.data.url),n.complete("Dashboard opened"),console.log()}var N="signup",K="0.2.0";async function Q(){try{let t=new AbortController,n=setTimeout(()=>t.abort(),3000),o=await fetch(`https://registry.npmjs.org/${N}/latest`,{signal:t.signal});if(clearTimeout(n),!o.ok)return;let{version:r}=await o.json();if(r&&r!==K)console.log(),console.log(` ${s("Update available:")} ${s(K)} ${s("→")} ${$(r)}`),console.log(` ${s("Run")} npx ${N}@latest ${s("to update")}`),console.log()}catch{}}Q();function yt(t){let n=t.slice(2),o=[],r={};for(let e=0;e<n.length;e++){let i=n[e];if(i.startsWith("--")&&i.includes("=")){let[a,...c]=i.slice(2).split("=");r[a]=c.join("=")}else if(i.startsWith("--")){let a=i.slice(2),c=n[e+1];if(c&&!c.startsWith("--"))r[a]=c,e++;else r[a]="true"}else o.push(i)}return{service:o[0]||"",subcommand:o[1]||"",flags:r}}function k(){console.log(`
21
+ ${g("signup")} — Agentic signup from your terminal
22
+
23
+ ${g("Usage:")}
24
+ npx signup <service> Sign up for a service
25
+ npx signup <service> login Log into the dashboard
26
+
27
+ ${g("Options:")}
28
+ --list List available services
29
+ --registry-url Registry URL override (for development)
30
+ --help Show this help message
31
+
32
+ ${g("Examples:")}
33
+ npx signup tripwire
34
+ npx signup tripwire login
35
+ npx signup --list
36
+ `)}async function $t(){let{service:t,subcommand:n,flags:o}=yt(process.argv);if(o.help)k(),process.exit(0);if(o.list){try{let r=await J(o["registry-url"]);console.log(),console.log(g("Available services:")),console.log();for(let e of r)console.log(` ${g(e.id)} ${s("—")} ${e.description}`);console.log()}catch(r){p(r instanceof Error?r.message:String(r)),process.exit(1)}process.exit(0)}if(!t)k(),process.exit(1);if(n==="login"){await H({service:t,registryUrl:o["registry-url"]});return}if(n&&n!=="login")p(`Unknown command: ${n}`),k(),process.exit(1);await A({service:t,registryUrl:o["registry-url"]})}$t().catch((t)=>{p(t instanceof Error?t.message:String(t)),process.exit(1)});
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "signup",
3
+ "version": "0.2.0",
4
+ "description": "Agentic signup — get API keys from your terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "signup": "dist/cli.mjs"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "bun build src/cli.ts --outfile dist/cli.mjs --target node --format esm --minify",
14
+ "dev": "bun run src/cli.ts"
15
+ },
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "license": "MIT"
20
+ }