wispjs 2.4.0 → 3.0.1
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/dist/wisp_api/index.d.ts +6 -0
- package/dist/wisp_api/index.js +8 -0
- package/dist/wisp_socket/filesystem.d.ts +60 -0
- package/dist/wisp_socket/filesystem.js +97 -0
- package/dist/wisp_socket/git.d.ts +57 -0
- package/dist/wisp_socket/git.js +76 -0
- package/dist/wisp_socket/index.d.ts +37 -18
- package/dist/wisp_socket/index.js +117 -158
- package/dist/wisp_socket/pool.d.ts +192 -48
- package/dist/wisp_socket/pool.js +132 -33
- package/dist/wisp_socket/ws_adapter.d.ts +72 -0
- package/dist/wisp_socket/ws_adapter.js +130 -0
- package/package.json +5 -4
- package/.github/workflows/release.yml +0 -77
- package/tsconfig.json +0 -19
- package/wisp.ts +0 -43
- package/wisp_api/apis/allocations.ts +0 -71
- package/wisp_api/apis/audit_log.ts +0 -81
- package/wisp_api/apis/backups.ts +0 -168
- package/wisp_api/apis/databases.ts +0 -80
- package/wisp_api/apis/fastdl.ts +0 -22
- package/wisp_api/apis/filesystem.ts +0 -291
- package/wisp_api/apis/index.ts +0 -135
- package/wisp_api/apis/mods.ts +0 -53
- package/wisp_api/apis/schedules.ts +0 -270
- package/wisp_api/apis/servers.ts +0 -155
- package/wisp_api/apis/startup.ts +0 -65
- package/wisp_api/apis/subusers.ts +0 -159
- package/wisp_api/index.ts +0 -57
- package/wisp_socket/index.ts +0 -495
- package/wisp_socket/pool.ts +0 -369
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import stripAnsi from 'strip-ansi';
|
|
2
2
|
import { WebsocketPool } from "./pool.js";
|
|
3
|
+
import { GitSocket } from "./git.js";
|
|
4
|
+
import { FilesystemSocket } from "./filesystem.js";
|
|
3
5
|
/**
|
|
4
6
|
* The primary interface to the Websocket API
|
|
5
7
|
*
|
|
@@ -11,6 +13,8 @@ export class WispSocket {
|
|
|
11
13
|
this.api = api;
|
|
12
14
|
this.ghToken = ghToken;
|
|
13
15
|
this.consoleCallbacks = [];
|
|
16
|
+
this.Git = new GitSocket(this);
|
|
17
|
+
this.Filesystem = new FilesystemSocket(this);
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* Sets a callback to run on the Websocket Info before saving the details.
|
|
@@ -43,7 +47,16 @@ export class WispSocket {
|
|
|
43
47
|
if (!this.url || !this.token) {
|
|
44
48
|
throw new Error("Attempted to create a pool without a URL or token");
|
|
45
49
|
}
|
|
46
|
-
|
|
50
|
+
// Provider used by the pool to refresh the (short-lived) token when a
|
|
51
|
+
// worker needs to reconnect.
|
|
52
|
+
const refreshDetails = async () => {
|
|
53
|
+
await this.setDetails();
|
|
54
|
+
return { url: this.url, token: this.token };
|
|
55
|
+
};
|
|
56
|
+
// The Origin header is the panel the connection originates from, which
|
|
57
|
+
// is always the API domain.
|
|
58
|
+
const origin = `https://${this.api.domain}`;
|
|
59
|
+
this.pool = new WebsocketPool(this.url, this.token, origin, refreshDetails);
|
|
47
60
|
}
|
|
48
61
|
/**
|
|
49
62
|
* Requests and saves the Websocket details from the API
|
|
@@ -58,7 +71,7 @@ export class WispSocket {
|
|
|
58
71
|
}
|
|
59
72
|
this.url = websocketInfo.url;
|
|
60
73
|
this.token = websocketInfo.token;
|
|
61
|
-
this.logger.info(
|
|
74
|
+
this.logger.info(`Got Websocket Details. ${this.url} - ${this.token}`);
|
|
62
75
|
}
|
|
63
76
|
catch (e) {
|
|
64
77
|
this.logger.error(`Failed to get websocket details: ${e}`);
|
|
@@ -87,178 +100,112 @@ export class WispSocket {
|
|
|
87
100
|
}
|
|
88
101
|
}
|
|
89
102
|
/**
|
|
90
|
-
*
|
|
103
|
+
* Runs a job on an available pool worker, handing it the worker's socket
|
|
104
|
+
* and logger. Used for event-based flows (e.g. file search) that don't fit
|
|
105
|
+
* the {@link request} request/response protocol.
|
|
91
106
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @param timeout How long to wait (in ms) for results before timing out
|
|
107
|
+
* @param work The job to run; receives the worker and returns a Promise
|
|
94
108
|
*
|
|
95
|
-
* @
|
|
109
|
+
* @internal
|
|
96
110
|
*/
|
|
97
|
-
async
|
|
98
|
-
this.logger.info("Running filesearch with: ", query);
|
|
111
|
+
async runWorker(work) {
|
|
99
112
|
await this.verifyPool();
|
|
100
|
-
return await this.pool.run(
|
|
101
|
-
const socket = worker.socket;
|
|
102
|
-
const logger = worker.logger;
|
|
103
|
-
logger.log("Running filesearch:", query);
|
|
104
|
-
return new Promise((resolve, reject) => {
|
|
105
|
-
const timeoutObj = setTimeout(() => {
|
|
106
|
-
socket.off("filesearch-results");
|
|
107
|
-
logger.error("Rejected filesearch: 'Timeout'");
|
|
108
|
-
reject();
|
|
109
|
-
}, timeout);
|
|
110
|
-
socket.once("filesearch-results", (data) => {
|
|
111
|
-
clearTimeout(timeoutObj);
|
|
112
|
-
resolve(data);
|
|
113
|
-
});
|
|
114
|
-
socket.emit("filesearch-start", query);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
113
|
+
return await this.pool.run(work);
|
|
117
114
|
}
|
|
118
115
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* @param dir The full directory path to perform a pull on
|
|
122
|
-
* @param timeout In milliseconds, how long to wait before timing out
|
|
116
|
+
* Generates a short, unique request id for {@link request} correlation.
|
|
123
117
|
*
|
|
124
|
-
* @
|
|
118
|
+
* @internal
|
|
125
119
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const pullResult = await this.pool.run((worker) => {
|
|
129
|
-
const socket = worker.socket;
|
|
130
|
-
const logger = worker.logger;
|
|
131
|
-
logger.log("Running gitPull:", dir);
|
|
132
|
-
return new Promise((resolve, reject) => {
|
|
133
|
-
let isPrivate = false;
|
|
134
|
-
let finished;
|
|
135
|
-
const timeoutObj = setTimeout(() => {
|
|
136
|
-
logger.error("Rejected gitPull: 'Timeout'");
|
|
137
|
-
finished(false, "Timeout");
|
|
138
|
-
}, timeout);
|
|
139
|
-
finished = (success, output) => {
|
|
140
|
-
socket.removeAllListeners("git-pull");
|
|
141
|
-
socket.removeAllListeners("git-error");
|
|
142
|
-
socket.removeAllListeners("git-success");
|
|
143
|
-
const result = {
|
|
144
|
-
output: output,
|
|
145
|
-
isPrivate: isPrivate
|
|
146
|
-
};
|
|
147
|
-
if (success) {
|
|
148
|
-
resolve(result);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
logger.error("Rejected gitPull:", dir, output);
|
|
152
|
-
reject(output);
|
|
153
|
-
}
|
|
154
|
-
clearTimeout(timeoutObj);
|
|
155
|
-
};
|
|
156
|
-
const sendRequest = (includeAuth = false) => {
|
|
157
|
-
const data = { dir: dir };
|
|
158
|
-
if (includeAuth) {
|
|
159
|
-
if (!this.ghToken) {
|
|
160
|
-
logger.error("No GitHub token set, can't authenticate");
|
|
161
|
-
return finished(false, "Authentication is required, but no GitHub token was set. Can't pull!");
|
|
162
|
-
}
|
|
163
|
-
isPrivate = true;
|
|
164
|
-
data.authkey = this.ghToken;
|
|
165
|
-
}
|
|
166
|
-
socket.emit("git-pull", data);
|
|
167
|
-
};
|
|
168
|
-
socket.once("git-pull", (data) => {
|
|
169
|
-
logger.log(`Updating ${data}`);
|
|
170
|
-
});
|
|
171
|
-
socket.once("git-success", (commit) => {
|
|
172
|
-
logger.log(`Addon updated to ${commit}`);
|
|
173
|
-
if (!commit) {
|
|
174
|
-
logger.log("No commit given!");
|
|
175
|
-
}
|
|
176
|
-
finished(true, commit || "");
|
|
177
|
-
});
|
|
178
|
-
socket.on("git-error", (message) => {
|
|
179
|
-
if (message === "Remote authentication required but no callback set") {
|
|
180
|
-
logger.log(`Remote authentication required, trying again with authkey: ${dir}`);
|
|
181
|
-
sendRequest(true);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
logger.log(`Error updating addon: ${message}`);
|
|
185
|
-
finished(false, message);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
sendRequest(useAuth);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
return pullResult;
|
|
120
|
+
generateReqId() {
|
|
121
|
+
return Math.random().toString(36).slice(2, 14);
|
|
192
122
|
}
|
|
193
123
|
/**
|
|
194
|
-
*
|
|
124
|
+
* Issues a correlated `fs:request` to the backend and awaits its outcome.
|
|
125
|
+
*
|
|
126
|
+
* This is the low-level entry point for the git/filesystem protocol. The
|
|
127
|
+
* client emits `fs:request` with a JSON-string payload (`{ req, op, ...params }`),
|
|
128
|
+
* and the backend replies with one of three events, all correlated by `req`
|
|
129
|
+
* and all carrying a JSON-string `args[0]`:
|
|
130
|
+
*
|
|
131
|
+
* - `fs:result` — success; resolves with the parsed `data`
|
|
132
|
+
* - `fs:error` — failure; rejects with an {@link FsError} (plus collected `output`)
|
|
133
|
+
* - `fs:progress` — streaming stdout/stderr/progress lines (collected for error context)
|
|
195
134
|
*
|
|
196
|
-
*
|
|
197
|
-
* @
|
|
198
|
-
*
|
|
199
|
-
*
|
|
135
|
+
* Typed convenience wrappers live on {@link GitSocket} ({@link WispSocket.Git})
|
|
136
|
+
* and {@link FilesystemSocket} ({@link WispSocket.Filesystem}); call this
|
|
137
|
+
* directly for ops that don't have a typed wrapper yet.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```js
|
|
141
|
+
* const status = await wisp.socket.request("git-status", { directory: "/garrysmod/addons/acf-3" })
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* @param op The operation name (e.g. `git-pull`, `list`)
|
|
145
|
+
* @param params Additional payload fields merged into the request (e.g. `{ directory }`)
|
|
146
|
+
* @param timeout In milliseconds, how long to wait for the result
|
|
200
147
|
*
|
|
201
148
|
* @public
|
|
202
149
|
*/
|
|
203
|
-
async
|
|
150
|
+
async request(op, params = {}, timeout = 10000) {
|
|
204
151
|
await this.verifyPool();
|
|
205
152
|
return await this.pool.run((worker) => {
|
|
206
153
|
const socket = worker.socket;
|
|
207
154
|
const logger = worker.logger;
|
|
208
|
-
|
|
155
|
+
const req = this.generateReqId();
|
|
156
|
+
logger.log(`Running fsRequest: ${op}`, params);
|
|
209
157
|
return new Promise((resolve, reject) => {
|
|
210
|
-
|
|
211
|
-
let
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
socket.removeAllListeners("git-clone");
|
|
218
|
-
socket.removeAllListeners("git-error");
|
|
219
|
-
socket.removeAllListeners("git-success");
|
|
220
|
-
if (success) {
|
|
221
|
-
const result = {
|
|
222
|
-
isPrivate: isPrivate
|
|
223
|
-
};
|
|
224
|
-
resolve(result);
|
|
158
|
+
const progress = [];
|
|
159
|
+
let resultHandler;
|
|
160
|
+
let errorHandler;
|
|
161
|
+
let progressHandler;
|
|
162
|
+
const parse = (raw) => {
|
|
163
|
+
try {
|
|
164
|
+
return JSON.parse(raw);
|
|
225
165
|
}
|
|
226
|
-
|
|
227
|
-
logger.error(
|
|
228
|
-
|
|
166
|
+
catch {
|
|
167
|
+
logger.error(`Failed to parse fs event for ${op}`, raw);
|
|
168
|
+
return undefined;
|
|
229
169
|
}
|
|
170
|
+
};
|
|
171
|
+
const cleanup = () => {
|
|
172
|
+
socket.off("fs:result", resultHandler);
|
|
173
|
+
socket.off("fs:error", errorHandler);
|
|
174
|
+
socket.off("fs:progress", progressHandler);
|
|
230
175
|
clearTimeout(timeoutObj);
|
|
231
176
|
};
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
177
|
+
const timeoutObj = setTimeout(() => {
|
|
178
|
+
cleanup();
|
|
179
|
+
logger.error(`fsRequest timed out: ${op} (${req})`);
|
|
180
|
+
reject(new Error(`fsRequest timed out: ${op}`));
|
|
181
|
+
}, timeout);
|
|
182
|
+
progressHandler = (raw) => {
|
|
183
|
+
const p = parse(raw);
|
|
184
|
+
if (!p || p.req !== req)
|
|
185
|
+
return;
|
|
186
|
+
const line = p.line ?? "";
|
|
187
|
+
progress.push(line);
|
|
188
|
+
logger.debug(`[${op}] ${p.kind}: ${line}`);
|
|
243
189
|
};
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
190
|
+
resultHandler = (raw) => {
|
|
191
|
+
const p = parse(raw);
|
|
192
|
+
if (!p || p.req !== req)
|
|
193
|
+
return;
|
|
194
|
+
cleanup();
|
|
195
|
+
resolve(p.data);
|
|
196
|
+
};
|
|
197
|
+
errorHandler = (raw) => {
|
|
198
|
+
const p = parse(raw);
|
|
199
|
+
if (!p || p.req !== req)
|
|
200
|
+
return;
|
|
201
|
+
cleanup();
|
|
202
|
+
logger.error(`fsRequest error: ${op} - ${p.code}: ${p.message}`);
|
|
203
|
+
reject({ ...p, output: progress.join("\n") });
|
|
204
|
+
};
|
|
205
|
+
socket.on("fs:result", resultHandler);
|
|
206
|
+
socket.on("fs:error", errorHandler);
|
|
207
|
+
socket.on("fs:progress", progressHandler);
|
|
208
|
+
socket.emit("fs:request", JSON.stringify({ req, op, ...params }));
|
|
262
209
|
});
|
|
263
210
|
});
|
|
264
211
|
}
|
|
@@ -273,9 +220,13 @@ export class WispSocket {
|
|
|
273
220
|
const logger = worker.logger;
|
|
274
221
|
logger.log("Running setupConsoleListener");
|
|
275
222
|
return new Promise((resolve) => {
|
|
276
|
-
|
|
277
|
-
|
|
223
|
+
const cleanup = () => {
|
|
224
|
+
worker.socket.off("console output", handleLine);
|
|
225
|
+
worker.socket.off("disconnect", handleDisconnect);
|
|
226
|
+
};
|
|
227
|
+
const handleLine = (line) => {
|
|
278
228
|
if (this.consoleCallbacks.length == 0) {
|
|
229
|
+
cleanup();
|
|
279
230
|
return resolve();
|
|
280
231
|
}
|
|
281
232
|
this.consoleCallbacks.forEach((callback) => {
|
|
@@ -286,7 +237,16 @@ export class WispSocket {
|
|
|
286
237
|
logger.error("Failed to run console callback", e);
|
|
287
238
|
}
|
|
288
239
|
});
|
|
289
|
-
}
|
|
240
|
+
};
|
|
241
|
+
const handleDisconnect = () => {
|
|
242
|
+
cleanup();
|
|
243
|
+
resolve();
|
|
244
|
+
if (this.consoleCallbacks.length > 0) {
|
|
245
|
+
this.setupConsoleListener();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
worker.socket.on("console output", handleLine);
|
|
249
|
+
worker.socket.once("disconnect", handleDisconnect);
|
|
290
250
|
});
|
|
291
251
|
});
|
|
292
252
|
});
|
|
@@ -363,17 +323,16 @@ export class WispSocket {
|
|
|
363
323
|
let callback;
|
|
364
324
|
const timeoutObj = setTimeout(() => {
|
|
365
325
|
logger.error(`Command timed out current output: '${output}'`);
|
|
366
|
-
socket.off("console", callback);
|
|
326
|
+
socket.off("console output", callback);
|
|
367
327
|
logger.log("Rejected sendCommandNonce 'Timeout'", nonce, command);
|
|
368
328
|
reject("Timeout");
|
|
369
329
|
}, timeout);
|
|
370
|
-
callback = (
|
|
371
|
-
const line = data.line;
|
|
330
|
+
callback = (line) => {
|
|
372
331
|
const clean = stripAnsi(line);
|
|
373
332
|
if (clean.startsWith(nonce)) {
|
|
374
333
|
const message = clean.slice(nonce.length);
|
|
375
334
|
if (message === "Done.") {
|
|
376
|
-
socket.off("console", callback);
|
|
335
|
+
socket.off("console output", callback);
|
|
377
336
|
clearTimeout(timeoutObj);
|
|
378
337
|
resolve(output);
|
|
379
338
|
}
|
|
@@ -383,7 +342,7 @@ export class WispSocket {
|
|
|
383
342
|
}
|
|
384
343
|
}
|
|
385
344
|
};
|
|
386
|
-
socket.on("console", callback);
|
|
345
|
+
socket.on("console output", callback);
|
|
387
346
|
socket.emit("send command", command);
|
|
388
347
|
});
|
|
389
348
|
});
|