computesdk 1.0.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 (3) hide show
  1. package/README.md +185 -0
  2. package/dist/index.js +1 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # ComputeSDK Documentation
2
+
3
+ The ComputeSDK provides a powerful interface for interacting with remote compute environments, enabling terminal access, file operations, and process execution in a secure containerized environment.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install computesdk
9
+ # or
10
+ yarn add computesdk
11
+ # or
12
+ pnpm add computesdk
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```javascript
18
+ import { ComputeClient } from 'computesdk';
19
+
20
+ async function main() {
21
+ // Create a new compute client
22
+ const client = new ComputeClient();
23
+
24
+ // Create a new compute instance
25
+ const compute = await client.create();
26
+
27
+ // Listen for server ready events
28
+ compute.onServerReady((port, url) => {
29
+ console.log(`Server is ready on port ${port} at ${url}`);
30
+ });
31
+ }
32
+ ```
33
+
34
+ ## Core Components
35
+
36
+ ### ComputeClient
37
+
38
+ The main entry point for creating and managing compute instances.
39
+
40
+ ```javascript
41
+ const client = new ComputeClient({
42
+ baseUrl: 'https://api.computesdk.com' // Optional, defaults to this value
43
+ });
44
+ ```
45
+
46
+ Methods:
47
+ - `create()`: Creates a new compute instance and returns a `Compute` object
48
+ ```javascript
49
+ // Create a new compute instance
50
+ const compute = await client.create();
51
+
52
+ // The returned compute object provides:
53
+ // - WebSocket connection to the compute environment
54
+ // - Terminal operations
55
+ // - File system operations
56
+ // - Process execution capabilities
57
+
58
+ // Returns: Promise<Compute>
59
+ // Throws: Error if compute creation fails
60
+ ```
61
+
62
+ - `delete(computeId)`: Terminates and removes a compute instance
63
+ ```javascript
64
+ // Delete a specific compute instance
65
+ await client.delete(compute.computeId);
66
+
67
+ // Parameters:
68
+ // - computeId: string (required) - The ID of the compute instance to delete
69
+
70
+ // Returns: Promise<void>
71
+ // Throws: Error if deletion fails or computeId is invalid
72
+ ```
73
+
74
+ Note: It's recommended to use the `compute.teardown()` method instead of calling `delete()` directly, as it properly closes WebSocket connections and handles cleanup.
75
+
76
+ ### Compute
77
+
78
+ Represents a compute instance with capabilities for file management, terminal operations, and process execution.
79
+
80
+ #### File Operations
81
+
82
+ ```javascript
83
+ // Watch files for changes
84
+ const watcher = compute.watchFiles({
85
+ path: '/home/project',
86
+ includeContent: false, // Optional: include file contents in change events
87
+ ignored: ['**/node_modules/**'] // Optional: patterns to ignore
88
+ });
89
+
90
+ watcher.onChanged(data => console.log('File changed:', data));
91
+
92
+ // Read file contents
93
+ const content = await compute.readFile('/path/to/file');
94
+
95
+ // Write to a file
96
+ await compute.writeFile('/path/to/file', 'content');
97
+
98
+ // Create directories
99
+ await compute.mkdir('/path/to/dir', {
100
+ recursive: true // Optional: create parent directories if they don't exist
101
+ });
102
+
103
+ // Delete files
104
+ await compute.deleteFile('/path/to/file');
105
+ ```
106
+
107
+ #### Terminal Operations
108
+
109
+ ```javascript
110
+ // Create a new terminal
111
+ const terminal = await compute.createTerminal({
112
+ rows: 24, // Optional
113
+ cols: 80, // Optional
114
+ command: null, // Optional
115
+ args: [] // Optional
116
+ });
117
+
118
+ // Terminal event handlers
119
+ terminal.onData(data => console.log('Received:', data));
120
+ terminal.onExit(({ exitCode, signal }) => console.log('Terminal exited'));
121
+
122
+ // Send input to terminal
123
+ terminal.write('echo "Hello World"\n');
124
+
125
+ // Resize terminal
126
+ terminal.resize(100, 30);
127
+ ```
128
+
129
+ #### Process Execution
130
+
131
+ ```javascript
132
+ // Execute commands
133
+ const result = await compute.exec('npm install');
134
+ ```
135
+
136
+ #### Event Handlers
137
+
138
+ ```javascript
139
+ // Connection events
140
+ compute.onConnected(() => console.log('Connected to compute instance'));
141
+ compute.onError(error => console.error('Error:', error));
142
+
143
+ // Port and server events
144
+ compute.onPortOpened((port, url) => console.log(`Port ${port} opened at ${url}`));
145
+ compute.onServerReady((port, url) => console.log(`Server ready on port ${port} at ${url}`));
146
+ ```
147
+
148
+ ## WebSocket Communication
149
+
150
+ The SDK uses WebSocket connections for real-time communication with compute instances. It automatically handles:
151
+ - Secure WebSocket connections (wss:// for https, ws:// for http)
152
+ - Connection retries (up to 3 attempts with exponential backoff)
153
+ - Automatic reconnection on unexpected disconnections
154
+
155
+ ## Debug Mode
156
+
157
+ Enable debug logging by setting localStorage.DEBUG to 'true' in the browser:
158
+
159
+ ```javascript
160
+ localStorage.setItem('DEBUG', 'true');
161
+ ```
162
+
163
+ ## Cleanup
164
+
165
+ To properly clean up resources:
166
+
167
+ ```javascript
168
+ await compute.teardown(); // Closes WebSocket and deletes compute instance
169
+ ```
170
+
171
+ ## Security Notes
172
+
173
+ - The SDK automatically handles secure WebSocket connections when using HTTPS
174
+ - All file paths should be properly sanitized before use
175
+ - Node modules are automatically ignored in file watching operations
176
+ - The SDK includes automatic cleanup on window unload in browser environments
177
+
178
+ ## Error Handling
179
+
180
+ The SDK provides comprehensive error handling through:
181
+ - Promise rejections for async operations
182
+ - Error events through the onError handler
183
+ - Detailed console logging in debug mode
184
+
185
+ For more information and advanced usage, please refer to the API documentation or contact support.
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var n=(h,...e)=>{window?.localStorage?.getItem("DEBUG")==="true"&&console.log("foo",h,...e)},p=class{constructor(e,t){this.ws=e,this.terminalId=t,this.onDataCallbacks=new Set,this.onExitCallbacks=new Set}onData(e){return this.onDataCallbacks.add(e),()=>this.onDataCallbacks.delete(e)}onExit(e){return this.onExitCallbacks.add(e),()=>this.onExitCallbacks.delete(e)}write(e){this.ws.send(JSON.stringify({type:"terminal:input",data:{terminalId:this.terminalId,input:e}}))}resize(e,t){this.ws.send(JSON.stringify({type:"terminal:resize",data:{terminalId:this.terminalId,cols:e,rows:t}}))}_handleData(e){this.onDataCallbacks.forEach(t=>t(e))}_handleExit(e,t){this.onExitCallbacks.forEach(s=>s({exitCode:e,signal:t}))}},w=class{constructor(e,t,s={}){this.ws=e,this.path=t,this.options=s,this.onChangeCallbacks=new Set}onChanged(e){return this.onChangeCallbacks.add(e),()=>this.onChangeCallbacks.delete(e)}_handleChange(e){this.onChangeCallbacks.forEach(t=>t(e))}stop(){this.ws.send(JSON.stringify({type:"file:watch:stop",data:{path:this.path}}))}},m=class{constructor(e,t){this.computeId=e,this.podUrl=t,this.ws=null,this.terminals=new Map,this.fileWatchers=new Map,this.onConnectedCallbacks=new Set,this.onErrorCallbacks=new Set,this.portOpenedCallbacks=new Set,this.serverReadyCallbacks=new Set,this.terminalId=0,this._connect(),typeof window<"u"&&(this._onBeforeUnload=s=>{try{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),this.computeId&&new d().delete(this.computeId),this.ws=null,this.computeId=null}catch(l){console.error("Error during window unload cleanup:",l)}s.preventDefault(),s.returnValue=""},window.addEventListener("beforeunload",this._onBeforeUnload))}async teardown(){try{typeof window<"u"&&this._onBeforeUnload&&(window.removeEventListener("beforeunload",this._onBeforeUnload),this._onBeforeUnload=null),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),this.computeId&&await new d().delete(this.computeId),this.ws=null,this.computeId=null}catch(e){throw console.error("Error during teardown:",e),e}}_connect(){let e=new URL(this.podUrl),t=e.protocol==="https:"?"wss:":"ws:",s=e.pathname==="/"?"/ws":`${e.pathname}/ws`,l=`${t}//${e.host}${s}`;console.log("\u{1F50C} Attempting WebSocket connection to:",l),console.log("Original pod URL was:",this.podUrl),n("\u{1F50C} Attempting WebSocket connection to:",l);let o=0,i=3,c=()=>{console.log(`Connecting WebSocket (attempt ${o+1}/${i+1})...`),this.ws=new WebSocket(l),this.ws.onopen=()=>{console.log("\u2705 WebSocket connection established"),n("\u2705 WebSocket connection established"),o=0,this.onConnectedCallbacks.forEach(a=>a())},this.ws.onerror=a=>{console.error("\u274C WebSocket error:",a),console.error("WebSocket readyState:",this.ws.readyState),this.onErrorCallbacks.forEach(r=>r(a)),o<i&&(o++,console.log(`\u{1F504} Retrying WebSocket connection (${o}/${i})...`),n(`\u{1F504} Retrying WebSocket connection (${o}/${i})...`),setTimeout(c,1e3*o))},this.ws.onclose=a=>{console.log("\u{1F504} WebSocket closed:",a.code,a.reason),n("\u{1F504} WebSocket closed:",a.code,a.reason),a.code!==1e3&&o<i&&(o++,console.log(`\u{1F504} Attempting to reconnect (${o}/${i})...`),n(`\u{1F504} Attempting to reconnect (${o}/${i})...`),setTimeout(c,1e3*o))},this.ws.onmessage=a=>{let r=JSON.parse(a.data);this._handleMessage(r)}};c()}_handleMessage({type:e,data:t}){switch(n("\u{1F4E8} Received message:",{type:e,data:t}),e){case"connected":n("\u{1F3AF} Got connected event with data:",t),this.onConnectedCallbacks.forEach(r=>r(t.port,"connected",t.url));break;case"error":console.error("\u274C Got error event:",t.message),this.onErrorCallbacks.forEach(r=>r(t.message));break;case"terminal:output":n("\u{1F4DD} Got terminal output:",t);let s=this.terminals.get(t.terminalId);s?s._handleData(t.data):console.warn("\u26A0\uFE0F No terminal found for ID:",t.terminalId);break;case"terminal:exit":n("\u{1F6AB} Got terminal exit event:",t);let l=this.terminals.get(t.terminalId);l&&(l._handleExit(t.exitCode,t.signal),this.terminals.delete(t.terminalId));break;case"file:changed":n("\u{1F4C1} Got file changed event:",t);let o=t.path.startsWith("/")?t.path:`/home/project/${t.path}`,i=t.path.replace(/^\/home\/project\//,""),c="/home/project",a=this.fileWatchers.get(o)||this.fileWatchers.get(i)||this.fileWatchers.get(c);a?(n("\u2705 Found watcher for path:",t.path),a._handleChange({...t,path:o})):(console.warn("\u26A0\uFE0F No watcher found for paths:",{original:t.path,full:o,relative:i,base:c}),n("\u{1F4CB} Active watchers:",Array.from(this.fileWatchers.keys())));break;case"port:opened":n("\u2705 Got port opened event:",t),this.portOpenedCallbacks.forEach(r=>r(t.port,new URL(`https://${t.port}-${new URL(this.podUrl).host}`)));break;case"server:ready":n("\u2705 Got server ready event:",t),this.serverReadyCallbacks.forEach(r=>r(t.port,new URL(`https://${t.port}-${new URL(this.podUrl).host}`)));break}}onConnected(e){return this.onConnectedCallbacks=this.onConnectedCallbacks||new Set,this.onConnectedCallbacks.add(e),()=>this.onConnectedCallbacks.delete(e)}onPortOpened(e){return this.portOpenedCallbacks=this.portOpenedCallbacks||new Set,this.portOpenedCallbacks.add(e),()=>this.portOpenedCallbacks.delete(e)}onServerReady(e){return this.serverReadyCallbacks=this.serverReadyCallbacks||new Set,this.serverReadyCallbacks.add(e),()=>this.serverReadyCallbacks.delete(e)}onError(e){return this.onErrorCallbacks=this.onErrorCallbacks||new Set,this.onErrorCallbacks.add(e),()=>this.onErrorCallbacks.delete(e)}async createTerminal({rows:e=24,cols:t=80,command:s=null,args:l=[]}={}){return new Promise(o=>{let i=`terminal-${this.terminalId++}`,c=a=>{let r=JSON.parse(a.data);if(n("\u{1F3AF} Terminal creation message:",r),r.type==="terminal:output"&&r.data.terminalId===i){n("\u2728 Creating new terminal with ID:",r.data.terminalId);let f=new p(this.ws,r.data.terminalId);this.terminals.set(r.data.terminalId,f),this.ws.removeEventListener("message",c),o(f)}};n("\u{1F4E1} Sending terminal create request..."),this.ws.addEventListener("message",c),this.ws.send(JSON.stringify({type:"terminal:create",data:{rows:e,cols:t,command:s,args:l,terminalId:i}}))})}watchFiles({path:e,includeContent:t=!1,ignored:s=[],...l}={}){n("\u{1F50D} Starting file watch for:",e);let o=["**/node_modules/**","**/node_modules",...(Array.isArray(s)?s:[s]).filter(Boolean)],i=new w(this.ws,e,{ignored:o,...l});return this.fileWatchers.set(e,i),this.ws.send(JSON.stringify({type:"file:watch:start",data:{path:e,includeContent:t,ignoredOptions:o,...l}})),n("\u{1F4E4} Sent watch request:",{path:e,includeContent:t,ignoredOptions:o,options:l}),i}async readFile(e){let t=await fetch(`${this.podUrl}/fs/read`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})});if(!t.ok)throw new Error(`Failed to read file: ${t.statusText}`);return t.text()}async writeFile(e,t){let s=await fetch(`${this.podUrl}/fs/write`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e,content:t})});if(!s.ok)throw new Error(`Failed to write file: ${s.statusText}`)}async mkdir(e,{recursive:t=!0}){let s=await fetch(`${this.podUrl}/fs/write`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e,directory:!0,recursive:t})});if(!s.ok)throw new Error(`Failed to create directory: ${s.statusText}`)}async deleteFile(e){let t=await fetch(`${this.podUrl}/fs/remove`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})});if(!t.ok)throw new Error(`Failed to delete file: ${t.statusText}`)}async exec(e,t={}){let s=await fetch(`${this.podUrl}/process/exec`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({command:e,...t})});if(!s.ok)throw new Error(`Failed to execute command: ${s.statusText}`);return s.json()}},d=class{constructor(e={}){this.baseUrl=e.baseUrl||"https://api.computesdk.com"}async create(){n("\u{1F680} Creating compute instance at:",`${this.baseUrl}/api/v1/compute`);let e=await fetch(`${this.baseUrl}/api/v1/compute`,{method:"POST"});if(!e.ok){let s=await e.text();throw console.error("\u274C Failed to create compute:",s),new Error(`Failed to create compute: ${e.statusText} - ${s}`)}let t=await e.json();return n("\u2705 Compute created successfully:",t),new m(t.computeId,t.podInfo.podUrl)}async delete(e){let t=await fetch(`${this.baseUrl}/api/v1/compute/${e}`,{method:"DELETE"});if(!t.ok)throw new Error(`Failed to delete compute: ${t.statusText}`)}};export{m as Compute,d as ComputeClient};
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "computesdk",
3
+ "version": "1.0.0",
4
+ "description": "SDK for ComputeSDK - secure, containerized code execution service",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "browser": {
8
+ "ws": false
9
+ },
10
+ "exports": {
11
+ ".": "./dist/index.js"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "README.md"
16
+ ],
17
+ "dependencies": {
18
+ "serve": "^14.2.1"
19
+ },
20
+ "devDependencies": {
21
+ "esbuild": "^0.20.2"
22
+ },
23
+ "scripts": {
24
+ "dev": "serve -p 3000",
25
+ "build": "esbuild src/index.js --bundle --minify --outfile=dist/index.js --format=esm",
26
+ "prepublishOnly": "npm run build",
27
+ "test": "echo \"Error: no test specified\" && exit 0"
28
+ },
29
+ "keywords": [
30
+ "compute",
31
+ "sdk"
32
+ ],
33
+ "author": "ComputeSDK Team",
34
+ "license": "MIT",
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }