foundingbot 0.1.11 → 0.1.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @foundingbot/cli
2
2
 
3
+ ## 0.1.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [#172](https://github.com/phuctm97/foundingbot/pull/172) [`1d84210`](https://github.com/phuctm97/foundingbot/commit/1d8421043c461519ea14e46b65c8c8b9e0e9c434) Thanks [@kittranofficial](https://github.com/kittranofficial)! - Add `sign-in --device-auth`, which signs in by approving a code in your browser instead of entering your email and password. Over SSH, commands that open a browser now only show the link.
8
+
3
9
  ## 0.1.11
4
10
 
5
11
  ### Patch Changes
package/dist/main.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{defineCittyPlugin as e,defineCommand as t,runMain as n}from"citty";import{cancel as r,confirm as i,group as a,isCancel as o,log as s,note as c,outro as l,password as u,select as d,text as f}from"@clack/prompts";import{createAuthClient as p}from"better-auth/client";import{AsyncEntry as m}from"@napi-rs/keyring";import{once as ee}from"node:events";import te from"open";import{z as h}from"zod";import{createORPCClient as g}from"@orpc/client";import{RPCLink as _}from"@orpc/client/fetch";import{contract as v}from"@foundingbot/util";import{OpenAPILink as y}from"@orpc/openapi-client/fetch";var b=`foundingbot`,x=`0.1.11`,S=`Founding Bot CLI client`;const C=new URL(process.env.FOUNDINGBOT_SERVER_URL||`https://server.founding.bot`);function ne(){if(C.origin===`https://server.founding.bot`)return`bot.founding.cli.auth`;let e=process.env.FOUNDINGBOT_NAMESPACE?.trim();if(!e)throw Error(`FOUNDINGBOT_NAMESPACE is required for non-production servers`);return`bot.founding.cli.auth:${e}`}const w=new m(ne(),`token`);async function T(){return await w.getPassword()||void 0}function re(e){return w.setPassword(e)}function E(){return w.deleteCredential()}async function D(){let e=await T();return{authorization:e?`Bearer ${e}`:void 0,"foundingbot-client":b}}async function O(e){throw await E(),Error(`Verify ${e} to continue. Check your inbox or run the resend-verification command to request another verification email.`)}function k(e){let t=e instanceof Error?e.cause:void 0;return typeof t==`object`&&!!t&&`code`in t&&t.code===`EMAIL_NOT_VERIFIED`}var A=p({baseURL:new URL(`/auth`,C).href,fetchOptions:{throw:!0,auth:{token:T,type:`Bearer`},headers:{"foundingbot-client":b},onSuccess:async({response:e})=>{let t=e.headers.get(`set-auth-token`);t&&await re(t)},customFetchImpl:(e,t)=>globalThis.fetch(e,t)}});const j=e({name:`require-signed-in`,setup:async e=>{let t=await A.getSession();if(!t)throw Error(`Not signed in. Run the sign-in command, then try again.`);t.user.emailVerified||await O(t.user.email),e.data=t}});function M(e){return t({...e,run:e.run,plugins:[j]})}const ie=e({name:`require-signed-out`,setup:async()=>{let e=await A.getSession();if(e)throw Error(`Already signed in as ${e.user.email}. Run the sign-out command, then try again.`)}});function N(e){return t({...e,plugins:[ie]})}function P(e,{trim:t=!0}={}){return h.string().refine(e=>(t?e.trim():e).length>0,`${e} is required`)}const F=h.email(`Enter a valid email address`);function I(e){return P(e).pipe(F)}function L(e){return P(e,{trim:!1}).min(8,`Password must be at least 8 characters`).max(128,`Password must be at most 128 characters`)}const R=P(`Confirm password`,{trim:!1});function z(e){return R.refine(t=>t===e,`Passwords do not match`)}const B=P(`Name`),V=h.union([h.literal(``),h.url({error:`Enter a valid HTTP or HTTPS URL`,protocol:/^https?$/})]),H=I(`Email`),U=I(`New email`),W=P(`Token`),G=P(`Activation code`).regex(/^[a-f0-9]{64}$/,`Enter a valid activation code`),K=P(`Password`,{trim:!1}),q=P(`Current password`,{trim:!1}),J=L(`New password`),Y=L(`Password`);function X(e){r(e),process.exit(0)}async function Z(e){try{let t=await te(e);if(process.platform===`darwin`){t.ref();let[e]=await ee(t,`close`);if(e!==0)throw Error(`Browser launch failed`)}}catch{s.warn(`Couldn’t open your browser. Open the link above manually.`)}}const ae=new _({url:new URL(`/rpc`,C),headers:D,fetch:(e,t)=>globalThis.fetch(e,t)});var Q=g(ae),oe=M({meta:{name:`activate-bot`,description:`Activate your bot`},run:async()=>{let{activationCode:e}=await a({activationCode:()=>u({message:`Activation code`,validate:G,clearOnError:!0})},{onCancel:()=>X(`Bot activation canceled`)}),{url:t}=await Q.activateBot({activationCode:e});s.info(`Complete checkout in Stripe:\n${t}`),await Z(t),l(`Complete checkout to activate your bot.`)}}),se=M({meta:{name:`change-email`,description:`Change your email`},run:async()=>{let{newEmail:e}=await a({newEmail:()=>f({message:`New email`,validate:U})},{onCancel:()=>X(`Email change canceled`)});await A.changeEmail({newEmail:e,callbackURL:`/cli/email-change`}),l(`Email change requested for ${e}`)}}),ce=M({meta:{name:`change-password`,description:`Change your password`},run:async()=>{let e=await a({currentPassword:()=>u({message:`Current password`,validate:q}),newPassword:()=>u({message:`New password`,validate:J,clearOnError:!0}),confirmPassword:({results:e})=>u({message:`Confirm password`,validate:z(e.newPassword||``),clearOnError:!0}),revokeOtherSessions:()=>i({message:`Revoke other sessions?`})},{onCancel:()=>X(`Password change canceled`)});await A.changePassword({currentPassword:e.currentPassword,newPassword:e.newPassword,revokeOtherSessions:e.revokeOtherSessions}),l(`Password change succeeded`)}}),le=N({meta:{name:`forgot-password`,description:`Request a password reset`},run:async()=>{let{email:e}=await a({email:()=>f({message:`Email`,validate:H})},{onCancel:()=>X(`Password reset request canceled`)});try{await A.requestPasswordReset({email:e,redirectTo:`/cli/password-reset`})}catch(t){throw Error(`Failed to request password reset for ${e}`,{cause:t})}l(`If an account exists for ${e}, password reset instructions have been sent`)}}),ue=M({meta:{name:`list-bots`,description:`List your bots`},run:async()=>{let e=await Q.listBots();e.length===0?console.log(`No bots`):e.forEach((e,t)=>{c(`Name: ${e.name}\nTelegram bot username: @${e.telegramBotUsername}\nTelegram bot ID: ${e.telegramBotId}`,`Bot ${t+1}`)})}}),de=M({meta:{name:`list-sessions`,description:`List your active sessions`},run:async({data:e})=>{let t=await A.listSessions();t.length===0?console.log(`No active sessions`):t.forEach((t,n)=>{c([`IP address: ${t.ipAddress||`(None)`}`,`User agent: ${t.userAgent||`(None)`}`,`ID: ${t.id}`,`Created: ${t.createdAt.toISOString()}`,`Updated: ${t.updatedAt.toISOString()}`,`Expires: ${t.expiresAt.toISOString()}`].join(`
3
- `),`Session ${n+1}${t.id===e.session.id?` (Current)`:``}`)})}}),fe=M({meta:{name:`manage-billing`,description:`Manage your billing`},run:async()=>{let{url:e}=await Q.manageBilling();s.info(`Manage your billing in Stripe:\n${e}`),await Z(e),l(`Continue in your browser.`)}});const pe=new y(v,{url:new URL(`/api`,C),headers:D,fetch:(e,t)=>globalThis.fetch(e,t)});var me=g(pe),he=t({meta:{name:`ping`,description:`Run health checks`},run:async()=>{let e=await fetch(new URL(`/health`,C));if(!e.ok)throw Error(`Server health check failed`);console.log(`Server health:`,e.statusText||`OK`);let t=await fetch(new URL(`/auth/ok`,C));if(!t.ok)throw Error(`Auth health check failed`);console.log(`Auth health:`,t.statusText||`OK`);let n=await Q.ping();console.log(`RPC ping:`,n);let r=await me.ping();console.log(`API ping:`,r)}}),ge=M({meta:{name:`rebuild-bot`,description:`Rebuild your bot`},run:async()=>{let e=await Q.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>X(`Bot rebuild canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Rebuild this bot and permanently delete its data?`,initialValue:!1})},{onCancel:t});r||t();let o=e[n];if(!o)throw Error(`Bot not found`);await Q.rebuildBot({telegramBotId:o.telegramBotId}),l(`Bot rebuilt`)}}),$=M({meta:{name:`remove-bot`,description:`Remove your bot`},run:async()=>{let e=await Q.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>X(`Bot removal canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Remove this bot and permanently delete its data?`,initialValue:!1})},{onCancel:t});r||t();let c=e[n];if(!c)throw Error(`Bot not found`);await Q.removeBot({telegramBotId:c.telegramBotId}),s.success(`Bot removed`);let u=`${c.name} (@${c.telegramBotUsername})`,f=await i({message:`Also delete ${u} from Telegram?`});if(o(f)||!f){l(`${u} remains on Telegram. To reconnect it to your account, send /start to it and follow the instructions.`);return}let p=new URL(`https://t.me/BotFather`);p.searchParams.set(`start`,`deletebot`),s.info(`Continue deleting ${u} from Telegram:\n${p}`),await Z(p.href),l(`Select @${c.telegramBotUsername} in BotFather and follow the instructions.`)}}),_e=M({meta:{name:`repair-bot`,description:`Check and repair your bot`},run:async()=>{let e=await Q.listBots();if(e.length===0){console.log(`No bots`);return}let{index:t}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))})},{onCancel:()=>X(`Bot repair canceled`)}),n=e[t];if(!n)throw Error(`Bot not found`);await Q.repairBot({telegramBotId:n.telegramBotId}),l(`Bot repaired`)}}),ve=N({meta:{name:`resend-verification`,description:`Request another verification email`},run:async()=>{let{email:e}=await a({email:()=>f({message:`Email`,validate:H})},{onCancel:()=>X(`Verification email request canceled`)});try{await A.sendVerificationEmail({email:e,callbackURL:`/cli/email-verification`})}catch(t){throw Error(`Failed to request verification email for ${e}`,{cause:t})}l(`Verification email requested for ${e}`)}}),ye=N({meta:{name:`reset-password`,description:`Reset your password`},run:async()=>{let e=await a({token:()=>u({message:`Token`,validate:W,clearOnError:!0}),newPassword:()=>u({message:`New password`,validate:J,clearOnError:!0}),confirmPassword:({results:e})=>u({message:`Confirm password`,validate:z(e.newPassword||``),clearOnError:!0})},{onCancel:()=>X(`Password reset canceled`)});await A.resetPassword({token:e.token,newPassword:e.newPassword}),l(`Password reset succeeded. Run the sign-in command to continue.`)}}),be=M({meta:{name:`reset-usage`,description:`Reset your bot’s usage limits`},run:async()=>{let e=await Q.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>X(`Usage reset canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Use an available reset for this bot?`,initialValue:!1})},{onCancel:t});r||t();let o=e[n];if(!o)throw Error(`Bot not found`);await Q.resetUsage({telegramBotId:o.telegramBotId}),l(`Usage limits reset`)}}),xe=M({meta:{name:`revoke-session`,description:`Revoke an active session`},run:async({data:e})=>{let t=await A.listSessions();if(t.length===0){console.log(`No active sessions`);return}let{index:n}=await a({index:()=>d({message:`Session`,options:t.map((t,n)=>({value:n,label:`Session ${n+1}${t.id===e.session.id?` (Current)`:``}`,hint:`Created: ${t.createdAt.toISOString()} · User agent: ${t.userAgent||`(None)`} · IP address: ${t.ipAddress||`(None)`}`}))})},{onCancel:()=>X(`Session revocation canceled`)}),r=t[n];if(!r)throw Error(`Session not found`);await A.revokeSession({token:r.token}),r.id===e.session.id&&await E(),l(`Session revoked`)}}),Se=N({meta:{name:`sign-in`,description:`Sign in`},run:async()=>{let e=await a({email:()=>f({message:`Email`,validate:H}),password:()=>u({message:`Password`,validate:K})},{onCancel:()=>X(`Sign-in canceled`)}),t;try{({user:t}=await A.signIn.email(e))}catch(t){throw k(t)&&await O(e.email),t}t.emailVerified||await O(t.email),l(`Signed in as ${t.email}`)}}),Ce=t({meta:{name:`sign-out`,description:`Sign out`},run:async()=>{await A.signOut(),await E(),l(`Signed out`)}}),we=N({meta:{name:`sign-up`,description:`Create an account`},run:async()=>{let e=await a({name:()=>f({message:`Name`,validate:B}),email:()=>f({message:`Email`,validate:H}),password:()=>u({message:`Password`,validate:Y,clearOnError:!0})},{onCancel:()=>X(`Sign-up canceled`)}),t=await A.signUp.email({...e,callbackURL:`/cli/email-verification`});t.user.emailVerified||await O(t.user.email),l(`Signed up as ${t.user.email}`)}}),Te=M({meta:{name:`update-profile`,description:`Update your profile`},run:async({data:e})=>{let{name:t,avatar:n}=await a({name:()=>f({message:`Name`,initialValue:e.user.name,validate:B}),avatar:()=>f({message:`Avatar`,initialValue:e.user.image||void 0,placeholder:`Optional URL`,validate:V})},{onCancel:()=>X(`Profile update canceled`)});await A.updateUser({name:t,image:n||null}),l(`Profile update succeeded`)}}),Ee=M({meta:{name:`view-usage`,description:`View your bot’s usage details`},run:async()=>{let e=await Q.listBots();if(e.length===0){console.log(`No bots`);return}let{index:t}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))})},{onCancel:()=>X(`Usage check canceled`)}),n=e[t];if(!n)throw Error(`Bot not found`);let r=await Q.viewUsage({telegramBotId:n.telegramBotId}),i=`Subscription: (None)`;if(r.subscription){let{plan:e,price:t,usage:n}=r.subscription,a=new Intl.NumberFormat(`en-US`,{style:`currency`,currency:t.currency}),o=10**(a.resolvedOptions().maximumFractionDigits??2),s=t.duration.count===1?t.duration.unit:`${t.duration.count} ${t.duration.unit}s`;i=`Subscription:\n Plan: ${e}\n Price: ${a.format(t.amount/o)}\n Billing: Every ${s}\n Usage:\n Five-hour used: ${n.fiveHourUsedPercent}%\n Weekly used: ${n.weeklyUsedPercent}%\n Available resets: ${n.availableResets}`}c(`Status: ${r.status||`(None)`}\n${i}`,`${n.name} (@${n.telegramBotUsername})`)}}),De=M({meta:{name:`whoami`,description:`Show the current user`},run:({data:e})=>{c([`Name: ${e.user.name.trim()||`(None)`}`,`Email: ${e.user.email}`,`Avatar: ${e.user.image||`(None)`}`,`ID: ${e.user.id}`,`Created: ${e.user.createdAt.toISOString()}`,`Updated: ${e.user.updatedAt.toISOString()}`].join(`
4
- `),`Current user`)}});const Oe=t({meta:{name:`foundingbot`,version:x,description:S},subCommands:{"sign-up":we,"resend-verification":ve,"sign-in":Se,"sign-out":Ce,"forgot-password":le,"reset-password":ye,whoami:De,"update-profile":Te,"change-email":se,"change-password":ce,"list-sessions":de,"revoke-session":xe,"activate-bot":oe,"list-bots":ue,"repair-bot":_e,"rebuild-bot":ge,"remove-bot":$,"view-usage":Ee,"reset-usage":be,"manage-billing":fe,ping:he}});await n(Oe);export{};
2
+ import{defineCittyPlugin as e,defineCommand as t,runMain as n}from"citty";import{cancel as r,confirm as i,group as a,isCancel as o,log as s,note as c,outro as l,password as u,select as d,spinner as f,text as p}from"@clack/prompts";import{createAuthClient as m}from"better-auth/client";import{deviceAuthorizationClient as h}from"better-auth/client/plugins";import{AsyncEntry as ee}from"@napi-rs/keyring";import{once as te}from"node:events";import{setTimeout as g}from"node:timers/promises";import _ from"open";import{z as v}from"zod";import{createORPCClient as y}from"@orpc/client";import{RPCLink as b}from"@orpc/client/fetch";import{contract as x}from"@foundingbot/util";import{OpenAPILink as S}from"@orpc/openapi-client/fetch";var C=`foundingbot`,ne=`0.1.12`,re=`Founding Bot CLI client`;const w=new URL(process.env.FOUNDINGBOT_SERVER_URL||`https://server.founding.bot`);function ie(){if(w.origin===`https://server.founding.bot`)return`bot.founding.cli.auth`;let e=process.env.FOUNDINGBOT_NAMESPACE?.trim();if(!e)throw Error(`FOUNDINGBOT_NAMESPACE is required for non-production servers`);return`bot.founding.cli.auth:${e}`}const T=new ee(ie(),`token`);async function E(){return await T.getPassword()||void 0}function D(e){return T.setPassword(e)}function O(){return T.deleteCredential()}async function k(){let e=await E();return{authorization:e?`Bearer ${e}`:void 0,"foundingbot-client":C}}async function A(e){throw await O(),Error(`Verify ${e} to continue. Check your inbox or run the resend-verification command to request another verification email.`)}function j(e){let t=e instanceof Error?e.cause:void 0;return typeof t==`object`&&!!t&&`code`in t&&t.code===`EMAIL_NOT_VERIFIED`}var M=m({baseURL:new URL(`/auth`,w).href,fetchOptions:{throw:!0,auth:{token:E,type:`Bearer`},headers:{"foundingbot-client":C},onSuccess:async({response:e})=>{let t=e.headers.get(`set-auth-token`);t&&await D(t)},customFetchImpl:(e,t)=>globalThis.fetch(e,t)},plugins:[h()]});const ae=e({name:`require-signed-in`,setup:async e=>{let t=await M.getSession();if(!t)throw Error(`Not signed in. Run the sign-in command, then try again.`);t.user.emailVerified||await A(t.user.email),e.data=t}});function N(e){return t({...e,run:e.run,plugins:[ae]})}const P=e({name:`require-signed-out`,setup:async()=>{let e=await M.getSession();if(e)throw Error(`Already signed in as ${e.user.email}. Run the sign-out command, then try again.`)}});function F(e){return t({...e,plugins:[P]})}function I(e,{trim:t=!0}={}){return v.string().refine(e=>(t?e.trim():e).length>0,`${e} is required`)}const L=v.email(`Enter a valid email address`);function R(e){return I(e).pipe(L)}function z(e){return I(e,{trim:!1}).min(8,`Password must be at least 8 characters`).max(128,`Password must be at most 128 characters`)}const B=I(`Confirm password`,{trim:!1});function V(e){return B.refine(t=>t===e,`Passwords do not match`)}const H=I(`Name`),U=v.union([v.literal(``),v.url({error:`Enter a valid HTTP or HTTPS URL`,protocol:/^https?$/})]),W=R(`Email`),G=R(`New email`),K=I(`Token`),q=I(`Activation code`).regex(/^[a-f0-9]{64}$/,`Enter a valid activation code`),J=I(`Password`,{trim:!1}),oe=I(`Current password`,{trim:!1}),Y=z(`New password`),X=z(`Password`);function Z(e){r(e),process.exit(0)}async function Q(e){if(!process.env.SSH_CONNECTION)try{let t=await _(e);if(process.platform===`darwin`){t.ref();let[e]=await te(t,`close`);if(e!==0)throw Error(`Browser launch failed`)}}catch{s.warn(`Couldn’t open your browser. Open the link above manually.`)}}function se(e){return g(e)}const ce=new b({url:new URL(`/rpc`,w),headers:k,fetch:(e,t)=>globalThis.fetch(e,t)});var $=y(ce),le=N({meta:{name:`activate-bot`,description:`Activate your bot`},run:async()=>{let{activationCode:e}=await a({activationCode:()=>u({message:`Activation code`,validate:q,clearOnError:!0})},{onCancel:()=>Z(`Bot activation canceled`)}),{url:t}=await $.activateBot({activationCode:e});s.info(`Complete checkout in Stripe:\n${t}`),await Q(t),l(`Complete checkout to activate your bot.`)}}),ue=N({meta:{name:`change-email`,description:`Change your email`},run:async()=>{let{newEmail:e}=await a({newEmail:()=>p({message:`New email`,validate:G})},{onCancel:()=>Z(`Email change canceled`)});await M.changeEmail({newEmail:e,callbackURL:`/cli/email-change`}),l(`Email change requested for ${e}`)}}),de=N({meta:{name:`change-password`,description:`Change your password`},run:async()=>{let e=await a({currentPassword:()=>u({message:`Current password`,validate:oe}),newPassword:()=>u({message:`New password`,validate:Y,clearOnError:!0}),confirmPassword:({results:e})=>u({message:`Confirm password`,validate:V(e.newPassword||``),clearOnError:!0}),revokeOtherSessions:()=>i({message:`Revoke other sessions?`})},{onCancel:()=>Z(`Password change canceled`)});await M.changePassword({currentPassword:e.currentPassword,newPassword:e.newPassword,revokeOtherSessions:e.revokeOtherSessions}),l(`Password change succeeded`)}}),fe=F({meta:{name:`forgot-password`,description:`Request a password reset`},run:async()=>{let{email:e}=await a({email:()=>p({message:`Email`,validate:W})},{onCancel:()=>Z(`Password reset request canceled`)});try{await M.requestPasswordReset({email:e,redirectTo:`/cli/password-reset`})}catch(t){throw Error(`Failed to request password reset for ${e}`,{cause:t})}l(`If an account exists for ${e}, password reset instructions have been sent`)}}),pe=N({meta:{name:`list-bots`,description:`List your bots`},run:async()=>{let e=await $.listBots();e.length===0?console.log(`No bots`):e.forEach((e,t)=>{c(`Name: ${e.name}\nTelegram bot username: @${e.telegramBotUsername}\nTelegram bot ID: ${e.telegramBotId}`,`Bot ${t+1}`)})}}),me=N({meta:{name:`list-sessions`,description:`List your active sessions`},run:async({data:e})=>{let t=await M.listSessions();t.length===0?console.log(`No active sessions`):t.forEach((t,n)=>{c([`IP address: ${t.ipAddress||`(None)`}`,`User agent: ${t.userAgent||`(None)`}`,`ID: ${t.id}`,`Created: ${t.createdAt.toISOString()}`,`Updated: ${t.updatedAt.toISOString()}`,`Expires: ${t.expiresAt.toISOString()}`].join(`
3
+ `),`Session ${n+1}${t.id===e.session.id?` (Current)`:``}`)})}}),he=N({meta:{name:`manage-billing`,description:`Manage your billing`},run:async()=>{let{url:e}=await $.manageBilling();s.info(`Manage your billing in Stripe:\n${e}`),await Q(e),l(`Continue in your browser.`)}});const ge=new S(x,{url:new URL(`/api`,w),headers:k,fetch:(e,t)=>globalThis.fetch(e,t)});var _e=y(ge),ve=t({meta:{name:`ping`,description:`Run health checks`},run:async()=>{let e=await fetch(new URL(`/health`,w));if(!e.ok)throw Error(`Server health check failed`);console.log(`Server health:`,e.statusText||`OK`);let t=await fetch(new URL(`/auth/ok`,w));if(!t.ok)throw Error(`Auth health check failed`);console.log(`Auth health:`,t.statusText||`OK`);let n=await $.ping();console.log(`RPC ping:`,n);let r=await _e.ping();console.log(`API ping:`,r)}}),ye=N({meta:{name:`rebuild-bot`,description:`Rebuild your bot`},run:async()=>{let e=await $.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>Z(`Bot rebuild canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Rebuild this bot and permanently delete its data?`,initialValue:!1})},{onCancel:t});r||t();let o=e[n];if(!o)throw Error(`Bot not found`);await $.rebuildBot({telegramBotId:o.telegramBotId}),l(`Bot rebuilt`)}}),be=N({meta:{name:`remove-bot`,description:`Remove your bot`},run:async()=>{let e=await $.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>Z(`Bot removal canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Remove this bot and permanently delete its data?`,initialValue:!1})},{onCancel:t});r||t();let c=e[n];if(!c)throw Error(`Bot not found`);await $.removeBot({telegramBotId:c.telegramBotId}),s.success(`Bot removed`);let u=`${c.name} (@${c.telegramBotUsername})`,f=await i({message:`Also delete ${u} from Telegram?`});if(o(f)||!f){l(`${u} remains on Telegram. To reconnect it to your account, send /start to it and follow the instructions.`);return}let p=new URL(`https://t.me/BotFather`);p.searchParams.set(`start`,`deletebot`),s.info(`Continue deleting ${u} from Telegram:\n${p}`),await Q(p.href),l(`Select @${c.telegramBotUsername} in BotFather and follow the instructions.`)}}),xe=N({meta:{name:`repair-bot`,description:`Check and repair your bot`},run:async()=>{let e=await $.listBots();if(e.length===0){console.log(`No bots`);return}let{index:t}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))})},{onCancel:()=>Z(`Bot repair canceled`)}),n=e[t];if(!n)throw Error(`Bot not found`);await $.repairBot({telegramBotId:n.telegramBotId}),l(`Bot repaired`)}}),Se=F({meta:{name:`resend-verification`,description:`Request another verification email`},run:async()=>{let{email:e}=await a({email:()=>p({message:`Email`,validate:W})},{onCancel:()=>Z(`Verification email request canceled`)});try{await M.sendVerificationEmail({email:e,callbackURL:`/cli/email-verification`})}catch(t){throw Error(`Failed to request verification email for ${e}`,{cause:t})}l(`Verification email requested for ${e}`)}}),Ce=F({meta:{name:`reset-password`,description:`Reset your password`},run:async()=>{let e=await a({token:()=>u({message:`Token`,validate:K,clearOnError:!0}),newPassword:()=>u({message:`New password`,validate:Y,clearOnError:!0}),confirmPassword:({results:e})=>u({message:`Confirm password`,validate:V(e.newPassword||``),clearOnError:!0})},{onCancel:()=>Z(`Password reset canceled`)});await M.resetPassword({token:e.token,newPassword:e.newPassword}),l(`Password reset succeeded. Run the sign-in command to continue.`)}}),we=N({meta:{name:`reset-usage`,description:`Reset your bot’s usage limits`},run:async()=>{let e=await $.listBots();if(e.length===0){console.log(`No bots`);return}let t=()=>Z(`Usage reset canceled`),{index:n,confirmed:r}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))}),confirmed:()=>i({message:`Use an available reset for this bot?`,initialValue:!1})},{onCancel:t});r||t();let o=e[n];if(!o)throw Error(`Bot not found`);await $.resetUsage({telegramBotId:o.telegramBotId}),l(`Usage limits reset`)}}),Te=N({meta:{name:`revoke-session`,description:`Revoke an active session`},run:async({data:e})=>{let t=await M.listSessions();if(t.length===0){console.log(`No active sessions`);return}let{index:n}=await a({index:()=>d({message:`Session`,options:t.map((t,n)=>({value:n,label:`Session ${n+1}${t.id===e.session.id?` (Current)`:``}`,hint:`Created: ${t.createdAt.toISOString()} · User agent: ${t.userAgent||`(None)`} · IP address: ${t.ipAddress||`(None)`}`}))})},{onCancel:()=>Z(`Session revocation canceled`)}),r=t[n];if(!r)throw Error(`Session not found`);await M.revokeSession({token:r.token}),r.id===e.session.id&&await O(),l(`Session revoked`)}});function Ee(e){let t=e instanceof Error?e.cause:void 0;return typeof t==`object`&&t&&`error`in t?t.error:void 0}async function De(){let e=await a({email:()=>p({message:`Email`,validate:W}),password:()=>u({message:`Password`,validate:J})},{onCancel:()=>Z(`Sign-in canceled`)});try{return(await M.signIn.email(e)).user}catch(t){throw j(t)&&await A(e.email),t}}async function Oe(){let{device_code:e,user_code:t,verification_uri_complete:n,interval:r}=await M.device.code({client_id:C}),i=`${t.slice(0,4)}-${t.slice(4)}`;s.info(`Confirm the code ${i} in your browser:\n${n}`),await Q(n);let a=f({cancelMessage:`Sign-in canceled`,onCancel:()=>process.exit(0)});a.start(`Waiting for approval in your browser`);let o=r,c;for(;!c;){await se(o*1e3);try{({access_token:c}=await M.device.token({grant_type:`urn:ietf:params:oauth:grant-type:device_code`,device_code:e,client_id:C}))}catch(e){let t=Ee(e);if(t===`authorization_pending`)continue;if(t===`slow_down`){o+=5;continue}throw a.error(`Sign-in failed`),t===`access_denied`?Error(`Sign-in was denied in your browser.`):t===`expired_token`?Error(`The code expired. Run the sign-in command with --device-auth to get a new code.`):e}}await D(c),a.stop(`Approved in your browser`);let l=await M.getSession();if(!l)throw await O(),Error(`Couldn’t load your account after sign-in.`);return l.user}var ke=F({meta:{name:`sign-in`,description:`Sign in`},args:{"device-auth":{type:`boolean`,description:`Sign in by approving a code in your browser`}},run:async({args:e})=>{let t=e[`device-auth`]?await Oe():await De();t.emailVerified||await A(t.email),l(`Signed in as ${t.email}`)}}),Ae=t({meta:{name:`sign-out`,description:`Sign out`},run:async()=>{await M.signOut(),await O(),l(`Signed out`)}}),je=F({meta:{name:`sign-up`,description:`Create an account`},run:async()=>{let e=await a({name:()=>p({message:`Name`,validate:H}),email:()=>p({message:`Email`,validate:W}),password:()=>u({message:`Password`,validate:X,clearOnError:!0})},{onCancel:()=>Z(`Sign-up canceled`)}),t=await M.signUp.email({...e,callbackURL:`/cli/email-verification`});t.user.emailVerified||await A(t.user.email),l(`Signed up as ${t.user.email}`)}}),Me=N({meta:{name:`update-profile`,description:`Update your profile`},run:async({data:e})=>{let{name:t,avatar:n}=await a({name:()=>p({message:`Name`,initialValue:e.user.name,validate:H}),avatar:()=>p({message:`Avatar`,initialValue:e.user.image||void 0,placeholder:`Optional URL`,validate:U})},{onCancel:()=>Z(`Profile update canceled`)});await M.updateUser({name:t,image:n||null}),l(`Profile update succeeded`)}}),Ne=N({meta:{name:`view-usage`,description:`View your bot’s usage details`},run:async()=>{let e=await $.listBots();if(e.length===0){console.log(`No bots`);return}let{index:t}=await a({index:()=>d({message:`Bot`,options:e.map((e,t)=>({value:t,label:`${e.name} (@${e.telegramBotUsername})`}))})},{onCancel:()=>Z(`Usage check canceled`)}),n=e[t];if(!n)throw Error(`Bot not found`);let r=await $.viewUsage({telegramBotId:n.telegramBotId}),i=`Subscription: (None)`;if(r.subscription){let{plan:e,price:t,usage:n}=r.subscription,a=new Intl.NumberFormat(`en-US`,{style:`currency`,currency:t.currency}),o=10**(a.resolvedOptions().maximumFractionDigits??2),s=t.duration.count===1?t.duration.unit:`${t.duration.count} ${t.duration.unit}s`;i=`Subscription:\n Plan: ${e}\n Price: ${a.format(t.amount/o)}\n Billing: Every ${s}\n Usage:\n Five-hour used: ${n.fiveHourUsedPercent}%\n Weekly used: ${n.weeklyUsedPercent}%\n Available resets: ${n.availableResets}`}c(`Status: ${r.status||`(None)`}\n${i}`,`${n.name} (@${n.telegramBotUsername})`)}}),Pe=N({meta:{name:`whoami`,description:`Show the current user`},run:({data:e})=>{c([`Name: ${e.user.name.trim()||`(None)`}`,`Email: ${e.user.email}`,`Avatar: ${e.user.image||`(None)`}`,`ID: ${e.user.id}`,`Created: ${e.user.createdAt.toISOString()}`,`Updated: ${e.user.updatedAt.toISOString()}`].join(`
4
+ `),`Current user`)}});const Fe=t({meta:{name:`foundingbot`,version:ne,description:re},subCommands:{"sign-up":je,"resend-verification":Se,"sign-in":ke,"sign-out":Ae,"forgot-password":fe,"reset-password":Ce,whoami:Pe,"update-profile":Me,"change-email":ue,"change-password":de,"list-sessions":me,"revoke-session":Te,"activate-bot":le,"list-bots":pe,"repair-bot":xe,"rebuild-bot":ye,"remove-bot":be,"view-usage":Ne,"reset-usage":we,"manage-billing":he,ping:ve}});await n(Fe);export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foundingbot",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Founding Bot CLI client",
5
5
  "type": "module",
6
6
  "main": "./dist/main.mjs",