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.
- package/README.md +185 -0
- package/dist/index.js +1 -0
- 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
|
+
}
|