@ricsam/r5dctl 0.0.21 → 0.0.23

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.
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var shell_exports = {};
30
+ __export(shell_exports, {
31
+ createShellWebSocketUrl: () => createShellWebSocketUrl,
32
+ runR5dctlShell: () => runR5dctlShell
33
+ });
34
+ module.exports = __toCommonJS(shell_exports);
35
+ var import_ws = __toESM(require("ws"), 1);
36
+ function clampTerminalSize(value, fallback) {
37
+ if (!Number.isFinite(value) || (value ?? 0) <= 0) {
38
+ return fallback;
39
+ }
40
+ return Math.max(1, Math.min(Math.floor(value), 500));
41
+ }
42
+ function createShellWebSocketUrl(baseUrl) {
43
+ const url = new URL("/ws", baseUrl);
44
+ if (url.protocol === "https:") {
45
+ url.protocol = "wss:";
46
+ } else if (url.protocol === "http:") {
47
+ url.protocol = "ws:";
48
+ } else {
49
+ throw new Error(`Unsupported r5d base URL protocol: ${url.protocol}`);
50
+ }
51
+ return url.toString();
52
+ }
53
+ function decodeMessage(data) {
54
+ if (typeof data === "string") {
55
+ return data;
56
+ }
57
+ if (Buffer.isBuffer(data)) {
58
+ return data.toString("utf8");
59
+ }
60
+ if (data instanceof ArrayBuffer) {
61
+ return Buffer.from(data).toString("utf8");
62
+ }
63
+ if (ArrayBuffer.isView(data)) {
64
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
65
+ }
66
+ if (Array.isArray(data)) {
67
+ return Buffer.concat(data).toString("utf8");
68
+ }
69
+ return String(data);
70
+ }
71
+ async function runR5dctlShell(options) {
72
+ const stdin = options.stdin ?? process.stdin;
73
+ const stdout = options.stdout ?? process.stdout;
74
+ const signalTarget = options.signalTarget ?? process;
75
+ const interactive = options.command === void 0;
76
+ if (interactive && !stdin.isTTY) {
77
+ throw new Error("Interactive shell requires a TTY. Use `shell -c <command>` for non-interactive use.");
78
+ }
79
+ if (!options.credential) {
80
+ throw new Error("Authentication required. Run `r5dctl auth login` or provide a token or API key.");
81
+ }
82
+ const createWebSocket = options.createWebSocket ?? ((url, clientOptions) => new import_ws.default(url, clientOptions));
83
+ const socket = createWebSocket(createShellWebSocketUrl(options.baseUrl), {
84
+ headers: {
85
+ Authorization: `Bearer ${options.credential}`
86
+ }
87
+ });
88
+ return await new Promise((resolve, reject) => {
89
+ let ptyId;
90
+ let openRequested = false;
91
+ let settled = false;
92
+ let inputAttached = false;
93
+ const priorRawMode = stdin.isRaw ?? false;
94
+ const send = (message) => {
95
+ if (socket.readyState !== import_ws.default.OPEN) {
96
+ return;
97
+ }
98
+ socket.send(JSON.stringify(message));
99
+ };
100
+ const sendResize = () => {
101
+ if (!ptyId) {
102
+ return;
103
+ }
104
+ send({
105
+ type: "shell_pty_resize",
106
+ ptyId,
107
+ cols: clampTerminalSize(stdout.columns, 80),
108
+ rows: clampTerminalSize(stdout.rows, 24)
109
+ });
110
+ };
111
+ const onInput = (chunk) => {
112
+ if (ptyId) {
113
+ send({ type: "shell_pty_input", ptyId, data: Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk });
114
+ }
115
+ };
116
+ const onInputEnd = () => {
117
+ if (!interactive && ptyId) {
118
+ send({ type: "shell_pty_input", ptyId, data: "" });
119
+ }
120
+ };
121
+ const onSigint = () => {
122
+ if (ptyId) {
123
+ send({ type: "shell_pty_input", ptyId, data: "" });
124
+ }
125
+ };
126
+ const onSigterm = () => {
127
+ if (ptyId) {
128
+ send({ type: "shell_pty_close", ptyId });
129
+ ptyId = void 0;
130
+ }
131
+ finish({ exitCode: 143 });
132
+ };
133
+ const cleanup = () => {
134
+ socket.off("message", onMessage);
135
+ socket.off("error", onSocketError);
136
+ socket.off("close", onSocketClose);
137
+ if (inputAttached) {
138
+ stdin.off("data", onInput);
139
+ stdin.off("end", onInputEnd);
140
+ }
141
+ signalTarget.off("SIGWINCH", sendResize);
142
+ signalTarget.off("SIGINT", onSigint);
143
+ signalTarget.off("SIGTERM", onSigterm);
144
+ if (stdin.isTTY && stdin.setRawMode) {
145
+ stdin.setRawMode(priorRawMode);
146
+ }
147
+ };
148
+ const finish = (result) => {
149
+ if (settled) {
150
+ return;
151
+ }
152
+ settled = true;
153
+ cleanup();
154
+ if (socket.readyState === import_ws.default.OPEN || socket.readyState === import_ws.default.CONNECTING) {
155
+ socket.close(1e3, "r5dctl shell finished");
156
+ }
157
+ if ("error" in result) {
158
+ reject(result.error);
159
+ } else {
160
+ resolve(result.exitCode);
161
+ }
162
+ };
163
+ function attachInput() {
164
+ if (inputAttached) {
165
+ return;
166
+ }
167
+ inputAttached = true;
168
+ if (stdin.isTTY && stdin.setRawMode) {
169
+ stdin.setRawMode(true);
170
+ }
171
+ stdin.on("data", onInput);
172
+ stdin.on("end", onInputEnd);
173
+ signalTarget.on("SIGWINCH", sendResize);
174
+ signalTarget.on("SIGTERM", onSigterm);
175
+ if (!stdin.isTTY) {
176
+ signalTarget.on("SIGINT", onSigint);
177
+ }
178
+ }
179
+ function onMessage(data) {
180
+ let message;
181
+ try {
182
+ const parsed = JSON.parse(decodeMessage(data));
183
+ if (!parsed || typeof parsed !== "object" || !("type" in parsed)) {
184
+ throw new Error("Invalid message");
185
+ }
186
+ message = parsed;
187
+ } catch {
188
+ finish({ error: new Error("Received an invalid response from r5d.dev") });
189
+ return;
190
+ }
191
+ if (message.type === "connected") {
192
+ if (!openRequested) {
193
+ openRequested = true;
194
+ send({
195
+ type: "shell_pty_open",
196
+ projectId: options.projectId,
197
+ branchName: options.branchName,
198
+ cols: clampTerminalSize(stdout.columns, 80),
199
+ rows: clampTerminalSize(stdout.rows, 24),
200
+ ...options.command === void 0 ? {} : { command: options.command }
201
+ });
202
+ }
203
+ return;
204
+ }
205
+ if (message.type === "shell_pty_opened") {
206
+ ptyId = message.ptyId;
207
+ attachInput();
208
+ return;
209
+ }
210
+ if (message.type === "shell_pty_output") {
211
+ if (message.ptyId === ptyId) {
212
+ stdout.write(message.data);
213
+ }
214
+ return;
215
+ }
216
+ if (message.type === "shell_pty_exit") {
217
+ if (message.ptyId === ptyId) {
218
+ ptyId = void 0;
219
+ finish({ exitCode: message.exitCode });
220
+ }
221
+ return;
222
+ }
223
+ if (message.type === "shell_pty_error" && (!message.ptyId || message.ptyId === ptyId)) {
224
+ finish({ error: new Error(message.message) });
225
+ }
226
+ }
227
+ function onSocketError(error) {
228
+ finish({ error: new Error(`r5d.dev shell connection failed: ${error.message}`) });
229
+ }
230
+ function onSocketClose(code, reason) {
231
+ if (!settled) {
232
+ const suffix = reason.length > 0 ? `: ${reason.toString("utf8")}` : "";
233
+ finish({ error: new Error(`r5d.dev shell connection closed (${code})${suffix}`) });
234
+ }
235
+ }
236
+ socket.on("message", onMessage);
237
+ socket.on("error", onSocketError);
238
+ socket.on("close", onSocketClose);
239
+ });
240
+ }
241
+ // Annotate the CommonJS export names for ESM import in node:
242
+ 0 && (module.exports = {
243
+ createShellWebSocketUrl,
244
+ runR5dctlShell
245
+ });