@devbookhq/sdk
Version:
SDK for managing Devbook sessions from JavaScript/TypeScript
3 lines (2 loc) • 12.1 kB
JavaScript
import s from"normalize-path";import{RpcWebSocketClient as t}from"rpc-websocket-client";import"cross-fetch/polyfill";import{Fetcher as i}from"openapi-typescript-fetch";function e(s,t,i,e){return new(i||(i=Promise))((function(o,n){function r(s){try{c(e.next(s))}catch(s){n(s)}}function l(s){try{c(e.throw(s))}catch(s){n(s)}}function c(s){var t;s.done?o(s.value):(t=s.value,t instanceof i?t:new i((function(s){s(t)}))).then(r,l)}c((e=e.apply(s,t||[])).next())}))}function o(s){let t="";const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e=i.length;for(let o=0;o<s;o++)t+=i.charAt(Math.floor(Math.random()*e));return t}function n(s){return"fulfilled"===s.status}function r(s){if(!s.every((s=>"fulfilled"===s.status)))return s.reduce(((s,t,i)=>"rejected"===t.status?s+"\n"+`[${i}]: `+`${JSON.stringify(t)}`:s),"errors:\n")}function l(){let s,t;return{promise:new Promise(((i,e)=>{s=i,t=e})),reject:t,resolve:s}}var c;!function(s){s.Running="Running",s.Stopped="Stopped"}(c||(c={}));var d;!function(s){s.Create="Create",s.Write="Write",s.Remove="Remove",s.Rename="Rename",s.Chmod="Chmod"}(d||(d={}));class h{constructor(s,t){this.sessConn=s,this.path=t,this.listeners=new Set}start(){return e(this,void 0,void 0,(function*(){this.rpcSubscriptionID||(this.handleFilesystemEvents=this.handleFilesystemEvents.bind(this),this.rpcSubscriptionID=yield this.sessConn.subscribe("filesystem",this.handleFilesystemEvents,"watchDir",this.path))}))}stop(){return e(this,void 0,void 0,(function*(){this.listeners.clear(),this.rpcSubscriptionID&&(yield this.sessConn.unsubscribe(this.rpcSubscriptionID))}))}addEventListener(s){return this.listeners.add(s),()=>this.listeners.delete(s)}handleFilesystemEvents(s){this.listeners.forEach((t=>{t(s)}))}}const a=i.for();a.configure({baseUrl:"https://ondevbook.com"});class u{constructor(s,t=!1){this.logID=s,this.isEnabled=t}error(...s){console.error(`[31m[${this.id()} ERROR][0m`,...s)}log(...s){this.isEnabled&&console.log(`[36m[${this.id()}][0m`,...s)}id(){return"function"==typeof this.logID?this.logID():this.logID}}function p(s){return new Promise((t=>setTimeout(t,s)))}const v=a.path("/sessions").method("post").create({api_key:!0}),f=a.path("/sessions/{sessionID}/refresh").method("post").create({api_key:!0});class g{constructor(s){this.opts=s,this.isOpen=!1,this.rpc=new t,this.subscribers=[],this.logger=new u("Session",s.debug),this.logger.log(`Session for code snippet "${s.id}" initialized`)}getHostname(s){if(this.opts.__debug_hostname)return s&&"remote"===this.opts.__debug_devEnv?`${s}-${this.opts.__debug_hostname}`:s?`${this.opts.__debug_hostname}:${s}`:this.opts.__debug_hostname;if(!this.session)return;const t=`${this.session.sessionID}-${this.session.clientID}.ondevbook.com`;return s?`${s}-${t}`:t}close(){var s,t,i,o,n,r;return e(this,void 0,void 0,(function*(){if(this.isOpen){this.logger.log("Closing",this.session),this.isOpen=!1,this.logger.log("Unsubscribing...");(yield Promise.allSettled(this.subscribers.map((s=>this.unsubscribe(s.subID))))).forEach((s=>{"rejected"===s.status&&this.logger.log(`Failed to unsubscribe: "${s.reason}"`)})),null===(t=null===(s=this.rpc.ws)||void 0===s?void 0:s.terminate)||void 0===t||t.call(s),null===(o=null===(i=this.rpc.ws)||void 0===i?void 0:i.close)||void 0===o||o.call(i),null===(r=null===(n=this.opts)||void 0===n?void 0:n.onClose)||void 0===r||r.call(n),this.logger.log("Disconected from the session")}}))}open(){return e(this,void 0,void 0,(function*(){if(this.isOpen||this.session)throw new Error("Session connect was already called");if(this.isOpen=!0,!this.opts.__debug_hostname)try{const s=yield v({api_key:this.opts.apiKey,codeSnippetID:this.opts.id,editEnabled:this.opts.editEnabled});this.session=s.data,this.logger.log("Aquired session:",this.session),this.refresh(this.session.sessionID)}catch(s){if(s instanceof v.Error){const t=s.getActualType();if(400===t.status)throw new Error(`Error creating session - (${t.status}) bad request: ${t.data.message}`);if(401===t.status)throw new Error(`Error creating session - (${t.status}) unauthenticated (you need to be authenticated to start an session with persistent edits): ${t.data.message}`);if(500===t.status)throw new Error(`Error creating session - (${t.status}) server error: ${t.data.message}`)}throw s}const s=this.getHostname(this.opts.__debug_port||49982);if(!s)throw new Error("Cannot get session's hostname");const t=`${"local"===this.opts.__debug_devEnv?"ws":"wss"}://${s}/ws`;this.rpc.onError((s=>{this.logger.log("Error in WS session:",this.session,s)}));let i,o,n=!1;const r=new Promise(((s,t)=>{i=()=>{n||(n=!0,s())},o=()=>{n||(n=!0,t())}}));this.rpc.onOpen((()=>{this.logger.log("Connected to session:",this.session),null==i||i()})),this.rpc.onClose((s=>e(this,void 0,void 0,(function*(){var i,e,n,r;if(this.logger.log("Closing WS connection to session:",this.session,s),this.isOpen){null===(e=(i=this.opts).onDisconnect)||void 0===e||e.call(i),yield p(600),this.logger.log("Reconnecting to session:",this.session);try{this.subscribers=[],yield this.rpc.connect(t),null===(r=(n=this.opts).onReconnect)||void 0===r||r.call(n),this.logger.log("Reconnected to session:",this.session)}catch(s){this.logger.log("Failed reconnecting to session:",this.session,s)}}else null==o||o()})))),this.rpc.onNotification.push(this.handleNotification.bind(this));try{this.logger.log("Connection to session:",this.session),yield this.rpc.connect(t)}catch(s){this.logger.log("Error connecting to session",this.session,s)}yield r}))}call(s,t,i){return e(this,void 0,void 0,(function*(){return this.rpc.call(`${s}_${t}`,i)}))}handleSubscriptions(...s){return e(this,void 0,void 0,(function*(){const t=yield Promise.allSettled(s);if(t.every((s=>"fulfilled"===s.status)))return t.map((s=>"fulfilled"===s.status?s.value:void 0));throw yield Promise.all(t.filter(n).map((s=>s.value?this.unsubscribe(s.value):void 0))),new Error(r(t))}))}unsubscribe(s){return e(this,void 0,void 0,(function*(){const t=this.subscribers.find((t=>t.subID===s));t&&(yield this.call(t.service,"unsubscribe",[t.subID]),this.subscribers=this.subscribers.filter((s=>s!==t)),this.logger.log(`Unsubscribed '${s}' from '${t.service}'`))}))}subscribe(s,t,i,...o){return e(this,void 0,void 0,(function*(){const e=yield this.call(s,"subscribe",[i,...o]);if("string"!=typeof e)throw new Error(`Cannot subscribe to ${s}_${i}${o.length>0?" with params ["+o.join(", ")+"]":""}. Expected response should have been a subscription ID, instead we got ${JSON.stringify(e)}`);return this.subscribers.push({handler:t,service:s,subID:e}),this.logger.log(`Subscribed to "${s}_${i}"${o.length>0?" with params ["+o.join(", ")+"] and":""} with id "${e}"`),e}))}handleNotification(s){this.subscribers.filter((t=>{var i;return t.subID===(null===(i=s.params)||void 0===i?void 0:i.subscription)})).forEach((t=>{var i;return t.handler(null===(i=s.params)||void 0===i?void 0:i.result)}))}refresh(s){return e(this,void 0,void 0,(function*(){this.logger.log(`Started refreshing session "${s}"`);try{for(;;){if(!this.isOpen)return void this.logger.log("Cannot refresh session - it was closed",this.session);yield p(5e3);try{this.logger.log(`Refreshed session "${s}"`),yield f({api_key:this.opts.apiKey,sessionID:s})}catch(t){if(t instanceof f.Error){const i=t.getActualType();if(404===i.status)return void this.logger.error(`Error refreshing session - (${i.status}): ${i.data.message}`);this.logger.error(`Refreshing session "${s}" failed - (${i.status})`)}}}}finally{this.logger.log(`Stopped refreshing session "${s}"`),this.close()}}))}}class b extends g{constructor(s){super(s),this.codeSnippetOpts=s.codeSnippet}open(){const t=Object.create(null,{open:{get:()=>super.open}});var i,n,c,d;return e(this,void 0,void 0,(function*(){yield t.open.call(this),yield this.handleSubscriptions((null===(i=this.codeSnippetOpts)||void 0===i?void 0:i.onStateChange)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStateChange,"state"):void 0,(null===(n=this.codeSnippetOpts)||void 0===n?void 0:n.onStderr)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStderr,"stderr"):void 0,(null===(c=this.codeSnippetOpts)||void 0===c?void 0:c.onStdout)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStdout,"stdout"):void 0,(null===(d=this.codeSnippetOpts)||void 0===d?void 0:d.onScanPorts)?this.subscribe("codeSnippet",this.codeSnippetOpts.onScanPorts,"scanOpenedPorts"):void 0),this.codeSnippet={run:(s,t={})=>e(this,void 0,void 0,(function*(){var i,e;const o=yield this.call("codeSnippet","run",[s,t]);return null===(e=null===(i=this.codeSnippetOpts)||void 0===i?void 0:i.onStateChange)||void 0===e||e.call(i,o),o})),stop:()=>e(this,void 0,void 0,(function*(){var s,t;const i=yield this.call("codeSnippet","stop");return null===(t=null===(s=this.codeSnippetOpts)||void 0===s?void 0:s.onStateChange)||void 0===t||t.call(s,i),i}))},this.filesystem={list:s=>e(this,void 0,void 0,(function*(){return yield this.call("filesystem","list",[s])})),read:s=>e(this,void 0,void 0,(function*(){return yield this.call("filesystem","read",[s])})),remove:s=>e(this,void 0,void 0,(function*(){yield this.call("filesystem","remove",[s])})),write:(s,t)=>e(this,void 0,void 0,(function*(){yield this.call("filesystem","write",[s,t])})),writeBytes:(s,t)=>e(this,void 0,void 0,(function*(){const i=Buffer.from(t).toString("base64");yield this.call("filesystem","writeBase64",[s,i])})),readBytes:s=>e(this,void 0,void 0,(function*(){const t=yield this.call("filesystem","readBase64",[s]);return Buffer.from(t,"base64")})),makeDir:s=>e(this,void 0,void 0,(function*(){yield this.call("filesystem","makeDir",[s])})),watchDir:t=>{const i=s(t);return new h(this,i)}},this.terminal={createSession:({onData:s,size:t,onExit:i,envVars:n,cmd:c,rootdir:d,terminalID:h=o(12)})=>e(this,void 0,void 0,(function*(){const{promise:o,resolve:a}=l(),[u,p]=yield this.handleSubscriptions(this.subscribe("terminal",s,"onData",h),this.subscribe("terminal",a,"onExit",h)),{promise:v,resolve:f}=l();o.then((()=>e(this,void 0,void 0,(function*(){const s=r(yield Promise.allSettled([this.unsubscribe(p),this.unsubscribe(u)]));s&&this.logger.error(s),null==i||i(),f()}))));try{yield this.call("terminal","start",[h,t.cols,t.rows,...void 0!==c?[n,c,d]:[]])}catch(s){throw a(),yield v,s}return{destroy:()=>e(this,void 0,void 0,(function*(){try{yield this.call("terminal","destroy",[h])}finally{a(),yield v}})),resize:({cols:s,rows:t})=>e(this,void 0,void 0,(function*(){yield this.call("terminal","resize",[h,s,t])})),sendData:s=>e(this,void 0,void 0,(function*(){yield this.call("terminal","data",[h,s])})),terminalID:h}}))},this.process={start:({cmd:s,onStdout:t,onStderr:i,onExit:n,envVars:c={},rootdir:d="/",processID:h=o(12)})=>e(this,void 0,void 0,(function*(){const{promise:o,resolve:a}=l(),[u,p,v]=yield this.handleSubscriptions(this.subscribe("process",a,"onExit",h),t?this.subscribe("process",t,"onStdout",h):void 0,i?this.subscribe("process",i,"onStderr",h):void 0),{promise:f,resolve:g}=l();o.then((()=>e(this,void 0,void 0,(function*(){const s=r(yield Promise.allSettled([this.unsubscribe(u),p?this.unsubscribe(p):void 0,v?this.unsubscribe(v):void 0]));s&&this.logger.error(s),null==n||n(),g()}))));try{yield this.call("process","start",[h,s,c,d])}catch(s){throw a(),yield f,s}return{kill:()=>e(this,void 0,void 0,(function*(){try{yield this.call("process","kill",[h])}finally{a(),yield f}})),processID:h,sendStdin:s=>e(this,void 0,void 0,(function*(){yield this.call("process","stdin",[h,s])}))}}))}}))}}var m;function y({cmd:s,manager:t,onStderr:i,onStdout:o,processID:n,rootdir:r}){return e(this,void 0,void 0,(function*(){if(!t)throw new Error("Cannot create process - process manager is not defined");const{resolve:e,promise:c}=l(),d=yield t.start({cmd:s,onStdout:o,onStderr:i,onExit:()=>{e()},rootdir:r,processID:n});return{exited:c,processID:d.processID,kill:d.kill,sendStdin:d.sendStdin}}))}!function(s){s.Stdout="Stdout",s.Stderr="Stderr"}(m||(m={}));export{c as CodeSnippetExecState,d as FilesystemOperation,h as FilesystemWatcher,m as OutType,b as Session,a as api,y as createSessionProcess};
//# sourceMappingURL=index.js.map