@devbookhq/sdk
Version:
SDK for managing Devbook sessions from JavaScript/TypeScript
3 lines (2 loc) • 12.4 kB
JavaScript
;Object.defineProperty(exports,"__esModule",{value:!0});var s=require("normalize-path"),t=require("rpc-websocket-client");require("cross-fetch/polyfill");var e=require("openapi-typescript-fetch");function i(s){return s&&"object"==typeof s&&"default"in s?s:{default:s}}var o=i(s);function n(s,t,e,i){return new(e||(e=Promise))((function(o,n){function r(s){try{c(i.next(s))}catch(s){n(s)}}function l(s){try{c(i.throw(s))}catch(s){n(s)}}function c(s){var t;s.done?o(s.value):(t=s.value,t instanceof e?t:new e((function(s){s(t)}))).then(r,l)}c((i=i.apply(s,t||[])).next())}))}function r(s){let t="";const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",i=e.length;for(let o=0;o<s;o++)t+=e.charAt(Math.floor(Math.random()*i));return t}function l(s){return"fulfilled"===s.status}function c(s){if(!s.every((s=>"fulfilled"===s.status)))return s.reduce(((s,t,e)=>"rejected"===t.status?s+"\n"+`[${e}]: `+`${JSON.stringify(t)}`:s),"errors:\n")}function d(){let s,t;return{promise:new Promise(((e,i)=>{s=e,t=i})),reject:t,resolve:s}}var h;exports.CodeSnippetExecState=void 0,(h=exports.CodeSnippetExecState||(exports.CodeSnippetExecState={})).Running="Running",h.Stopped="Stopped";var a;exports.FilesystemOperation=void 0,(a=exports.FilesystemOperation||(exports.FilesystemOperation={})).Create="Create",a.Write="Write",a.Remove="Remove",a.Rename="Rename",a.Chmod="Chmod";class u{constructor(s,t){this.sessConn=s,this.path=t,this.listeners=new Set}start(){return n(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 n(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 p=e.Fetcher.for();p.configure({baseUrl:"https://ondevbook.com"});class v{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 f(s){return new Promise((t=>setTimeout(t,s)))}const g=p.path("/sessions").method("post").create({api_key:!0}),b=p.path("/sessions/{sessionID}/refresh").method("post").create({api_key:!0});class y{constructor(s){this.opts=s,this.isOpen=!1,this.rpc=new t.RpcWebSocketClient,this.subscribers=[],this.logger=new v("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,e,i,o,r;return n(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===(i=null===(e=this.rpc.ws)||void 0===e?void 0:e.close)||void 0===i||i.call(e),null===(r=null===(o=this.opts)||void 0===o?void 0:o.onClose)||void 0===r||r.call(o),this.logger.log("Disconected from the session")}}))}open(){return n(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 g({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 g.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 e,i,o=!1;const r=new Promise(((s,t)=>{e=()=>{o||(o=!0,s())},i=()=>{o||(o=!0,t())}}));this.rpc.onOpen((()=>{this.logger.log("Connected to session:",this.session),null==e||e()})),this.rpc.onClose((s=>n(this,void 0,void 0,(function*(){var e,o,n,r;if(this.logger.log("Closing WS connection to session:",this.session,s),this.isOpen){null===(o=(e=this.opts).onDisconnect)||void 0===o||o.call(e),yield f(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==i||i()})))),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,e){return n(this,void 0,void 0,(function*(){return this.rpc.call(`${s}_${t}`,e)}))}handleSubscriptions(...s){return n(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(l).map((s=>s.value?this.unsubscribe(s.value):void 0))),new Error(c(t))}))}unsubscribe(s){return n(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,e,...i){return n(this,void 0,void 0,(function*(){const o=yield this.call(s,"subscribe",[e,...i]);if("string"!=typeof o)throw new Error(`Cannot subscribe to ${s}_${e}${i.length>0?" with params ["+i.join(", ")+"]":""}. Expected response should have been a subscription ID, instead we got ${JSON.stringify(o)}`);return this.subscribers.push({handler:t,service:s,subID:o}),this.logger.log(`Subscribed to "${s}_${e}"${i.length>0?" with params ["+i.join(", ")+"] and":""} with id "${o}"`),o}))}handleNotification(s){this.subscribers.filter((t=>{var e;return t.subID===(null===(e=s.params)||void 0===e?void 0:e.subscription)})).forEach((t=>{var e;return t.handler(null===(e=s.params)||void 0===e?void 0:e.result)}))}refresh(s){return n(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 f(5e3);try{this.logger.log(`Refreshed session "${s}"`),yield b({api_key:this.opts.apiKey,sessionID:s})}catch(t){if(t instanceof b.Error){const e=t.getActualType();if(404===e.status)return void this.logger.error(`Error refreshing session - (${e.status}): ${e.data.message}`);this.logger.error(`Refreshing session "${s}" failed - (${e.status})`)}}}}finally{this.logger.log(`Stopped refreshing session "${s}"`),this.close()}}))}}var m;exports.OutType=void 0,(m=exports.OutType||(exports.OutType={})).Stdout="Stdout",m.Stderr="Stderr",exports.FilesystemWatcher=u,exports.Session=class extends y{constructor(s){super(s),this.codeSnippetOpts=s.codeSnippet}open(){const s=Object.create(null,{open:{get:()=>super.open}});var t,e,i,l;return n(this,void 0,void 0,(function*(){yield s.open.call(this),yield this.handleSubscriptions((null===(t=this.codeSnippetOpts)||void 0===t?void 0:t.onStateChange)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStateChange,"state"):void 0,(null===(e=this.codeSnippetOpts)||void 0===e?void 0:e.onStderr)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStderr,"stderr"):void 0,(null===(i=this.codeSnippetOpts)||void 0===i?void 0:i.onStdout)?this.subscribe("codeSnippet",this.codeSnippetOpts.onStdout,"stdout"):void 0,(null===(l=this.codeSnippetOpts)||void 0===l?void 0:l.onScanPorts)?this.subscribe("codeSnippet",this.codeSnippetOpts.onScanPorts,"scanOpenedPorts"):void 0),this.codeSnippet={run:(s,t={})=>n(this,void 0,void 0,(function*(){var e,i;const o=yield this.call("codeSnippet","run",[s,t]);return null===(i=null===(e=this.codeSnippetOpts)||void 0===e?void 0:e.onStateChange)||void 0===i||i.call(e,o),o})),stop:()=>n(this,void 0,void 0,(function*(){var s,t;const e=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,e),e}))},this.filesystem={list:s=>n(this,void 0,void 0,(function*(){return yield this.call("filesystem","list",[s])})),read:s=>n(this,void 0,void 0,(function*(){return yield this.call("filesystem","read",[s])})),remove:s=>n(this,void 0,void 0,(function*(){yield this.call("filesystem","remove",[s])})),write:(s,t)=>n(this,void 0,void 0,(function*(){yield this.call("filesystem","write",[s,t])})),writeBytes:(s,t)=>n(this,void 0,void 0,(function*(){const e=Buffer.from(t).toString("base64");yield this.call("filesystem","writeBase64",[s,e])})),readBytes:s=>n(this,void 0,void 0,(function*(){const t=yield this.call("filesystem","readBase64",[s]);return Buffer.from(t,"base64")})),makeDir:s=>n(this,void 0,void 0,(function*(){yield this.call("filesystem","makeDir",[s])})),watchDir:s=>{const t=o.default(s);return new u(this,t)}},this.terminal={createSession:({onData:s,size:t,onExit:e,envVars:i,cmd:o,rootdir:l,terminalID:h=r(12)})=>n(this,void 0,void 0,(function*(){const{promise:r,resolve:a}=d(),[u,p]=yield this.handleSubscriptions(this.subscribe("terminal",s,"onData",h),this.subscribe("terminal",a,"onExit",h)),{promise:v,resolve:f}=d();r.then((()=>n(this,void 0,void 0,(function*(){const s=c(yield Promise.allSettled([this.unsubscribe(p),this.unsubscribe(u)]));s&&this.logger.error(s),null==e||e(),f()}))));try{yield this.call("terminal","start",[h,t.cols,t.rows,...void 0!==o?[i,o,l]:[]])}catch(s){throw a(),yield v,s}return{destroy:()=>n(this,void 0,void 0,(function*(){try{yield this.call("terminal","destroy",[h])}finally{a(),yield v}})),resize:({cols:s,rows:t})=>n(this,void 0,void 0,(function*(){yield this.call("terminal","resize",[h,s,t])})),sendData:s=>n(this,void 0,void 0,(function*(){yield this.call("terminal","data",[h,s])})),terminalID:h}}))},this.process={start:({cmd:s,onStdout:t,onStderr:e,onExit:i,envVars:o={},rootdir:l="/",processID:h=r(12)})=>n(this,void 0,void 0,(function*(){const{promise:r,resolve:a}=d(),[u,p,v]=yield this.handleSubscriptions(this.subscribe("process",a,"onExit",h),t?this.subscribe("process",t,"onStdout",h):void 0,e?this.subscribe("process",e,"onStderr",h):void 0),{promise:f,resolve:g}=d();r.then((()=>n(this,void 0,void 0,(function*(){const s=c(yield Promise.allSettled([this.unsubscribe(u),p?this.unsubscribe(p):void 0,v?this.unsubscribe(v):void 0]));s&&this.logger.error(s),null==i||i(),g()}))));try{yield this.call("process","start",[h,s,o,l])}catch(s){throw a(),yield f,s}return{kill:()=>n(this,void 0,void 0,(function*(){try{yield this.call("process","kill",[h])}finally{a(),yield f}})),processID:h,sendStdin:s=>n(this,void 0,void 0,(function*(){yield this.call("process","stdin",[h,s])}))}}))}}))}},exports.api=p,exports.createSessionProcess=function({cmd:s,manager:t,onStderr:e,onStdout:i,processID:o,rootdir:r}){return n(this,void 0,void 0,(function*(){if(!t)throw new Error("Cannot create process - process manager is not defined");const{resolve:n,promise:l}=d(),c=yield t.start({cmd:s,onStdout:i,onStderr:e,onExit:()=>{n()},rootdir:r,processID:o});return{exited:l,processID:c.processID,kill:c.kill,sendStdin:c.sendStdin}}))};
//# sourceMappingURL=index.js.map