fied 0.2.1 → 0.2.3
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/bin.js +89 -24
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -52,6 +52,22 @@ function toBase64Url(bytes) {
|
|
|
52
52
|
}
|
|
53
53
|
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
54
54
|
}
|
|
55
|
+
function fromBase64Url(str) {
|
|
56
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
57
|
+
while (base64.length % 4 !== 0) {
|
|
58
|
+
base64 += "=";
|
|
59
|
+
}
|
|
60
|
+
if (typeof atob === "function") {
|
|
61
|
+
const binary = atob(base64);
|
|
62
|
+
const bytes = new Uint8Array(binary.length);
|
|
63
|
+
for (let i = 0; i < binary.length; i++) {
|
|
64
|
+
bytes[i] = binary.charCodeAt(i);
|
|
65
|
+
}
|
|
66
|
+
return bytes;
|
|
67
|
+
} else {
|
|
68
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
55
71
|
|
|
56
72
|
// ../crypto/dist/protocol.js
|
|
57
73
|
var IV_LENGTH2 = 12;
|
|
@@ -166,6 +182,7 @@ var DEFAULT_RELAY = "https://fied.app";
|
|
|
166
182
|
var MSG_TERMINAL_OUTPUT = 1;
|
|
167
183
|
var MSG_TERMINAL_INPUT = 2;
|
|
168
184
|
var MSG_RESIZE = 3;
|
|
185
|
+
var VIEWER_JOINED = "__fied_viewer_joined__";
|
|
169
186
|
var RESIZE_MIN_COLS = 20;
|
|
170
187
|
var RESIZE_MAX_COLS = 1e3;
|
|
171
188
|
var RESIZE_MIN_ROWS = 5;
|
|
@@ -201,9 +218,9 @@ async function share(options) {
|
|
|
201
218
|
}
|
|
202
219
|
const cols = options.cols ?? process.stdout.columns ?? 80;
|
|
203
220
|
const rows = options.rows ?? process.stdout.rows ?? 24;
|
|
204
|
-
const rawKey = await generateKey();
|
|
221
|
+
const rawKey = options.keyBase64Url ? fromBase64Url(options.keyBase64Url) : await generateKey();
|
|
205
222
|
const cryptoKey = await importKey(rawKey);
|
|
206
|
-
const keyFragment = toBase64Url(rawKey);
|
|
223
|
+
const keyFragment = options.keyBase64Url ?? toBase64Url(rawKey);
|
|
207
224
|
const pty = attachSession(targetSession, cols, rows);
|
|
208
225
|
if (!options.background) {
|
|
209
226
|
console.log("");
|
|
@@ -213,7 +230,7 @@ async function share(options) {
|
|
|
213
230
|
console.log(` Size: ${cols}x${rows}`);
|
|
214
231
|
console.log("");
|
|
215
232
|
}
|
|
216
|
-
const bridge = new RelayBridge(relayTarget, cryptoKey, keyFragment, pty, options.background);
|
|
233
|
+
const bridge = new RelayBridge(relayTarget, cryptoKey, keyFragment, pty, options.background, options.sessionId);
|
|
217
234
|
const onUrl = (url) => {
|
|
218
235
|
if (options.background) {
|
|
219
236
|
addSession({
|
|
@@ -224,6 +241,7 @@ async function share(options) {
|
|
|
224
241
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
225
242
|
});
|
|
226
243
|
}
|
|
244
|
+
return options.onShareUrl?.(url);
|
|
227
245
|
};
|
|
228
246
|
await bridge.connect(onUrl);
|
|
229
247
|
let closed = false;
|
|
@@ -290,12 +308,13 @@ async function createSession(relayHttpBase) {
|
|
|
290
308
|
}
|
|
291
309
|
var WS_CONNECT_TIMEOUT_MS = 1e4;
|
|
292
310
|
var RelayBridge = class {
|
|
293
|
-
constructor(relayTarget, key, keyFragment, pty, silent = false) {
|
|
311
|
+
constructor(relayTarget, key, keyFragment, pty, silent = false, sessionId) {
|
|
294
312
|
this.relayTarget = relayTarget;
|
|
295
313
|
this.key = key;
|
|
296
314
|
this.keyFragment = keyFragment;
|
|
297
315
|
this.pty = pty;
|
|
298
316
|
this.silent = silent;
|
|
317
|
+
this.sessionId = sessionId ?? null;
|
|
299
318
|
this.pty.onData((data) => {
|
|
300
319
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
301
320
|
this.sendEncrypted(MSG_TERMINAL_OUTPUT, this.encoder.encode(data));
|
|
@@ -327,7 +346,6 @@ var RelayBridge = class {
|
|
|
327
346
|
}
|
|
328
347
|
const shareUrl = new URL(`s/${this.sessionId}`, this.relayTarget.httpBase);
|
|
329
348
|
const url = `${shareUrl.toString()}#${this.keyFragment}`;
|
|
330
|
-
this.onUrl?.(url);
|
|
331
349
|
if (!this.silent) {
|
|
332
350
|
console.log(` \x1B[1mShare this link:\x1B[0m`);
|
|
333
351
|
console.log(` \x1B[4m\x1B[36m${url}\x1B[0m`);
|
|
@@ -336,6 +354,7 @@ var RelayBridge = class {
|
|
|
336
354
|
console.log(" \x1B[2mPress Ctrl+C to stop sharing.\x1B[0m");
|
|
337
355
|
console.log("");
|
|
338
356
|
}
|
|
357
|
+
void this.onUrl?.(url);
|
|
339
358
|
}
|
|
340
359
|
const wsUrl = new URL(`api/sessions/${this.sessionId}/ws`, this.relayTarget.wsBase);
|
|
341
360
|
wsUrl.searchParams.set("role", "host");
|
|
@@ -359,6 +378,8 @@ var RelayBridge = class {
|
|
|
359
378
|
const text = this.decoder.decode(raw);
|
|
360
379
|
if (text === "__fied_ping__") {
|
|
361
380
|
ws.send("__fied_pong__");
|
|
381
|
+
} else if (text === VIEWER_JOINED) {
|
|
382
|
+
this.pty.write("\f");
|
|
362
383
|
}
|
|
363
384
|
return;
|
|
364
385
|
}
|
|
@@ -543,6 +564,10 @@ if (args.includes("--__daemon")) {
|
|
|
543
564
|
options.relay = args[++i];
|
|
544
565
|
} else if (args[i] === "--allow-insecure-relay") {
|
|
545
566
|
options.allowInsecureRelay = true;
|
|
567
|
+
} else if (args[i] === "--__session-id" && args[i + 1]) {
|
|
568
|
+
options.sessionId = args[++i];
|
|
569
|
+
} else if (args[i] === "--__key" && args[i + 1]) {
|
|
570
|
+
options.keyBase64Url = args[++i];
|
|
546
571
|
}
|
|
547
572
|
}
|
|
548
573
|
share({ ...options, background: true }).catch(() => process.exit(1));
|
|
@@ -618,26 +643,66 @@ async function main() {
|
|
|
618
643
|
session = session.split(" \u2014 ")[0].trim();
|
|
619
644
|
}
|
|
620
645
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const binPath = fileURLToPath(import.meta.url);
|
|
624
|
-
const childArgs = ["--__daemon", "--session", session];
|
|
625
|
-
if (relay) childArgs.push("--relay", relay);
|
|
626
|
-
if (allowInsecureRelay) childArgs.push("--allow-insecure-relay");
|
|
627
|
-
const child = spawnChild(process.execPath, [binPath, ...childArgs], {
|
|
628
|
-
detached: true,
|
|
629
|
-
stdio: "ignore"
|
|
630
|
-
});
|
|
631
|
-
child.unref();
|
|
632
|
-
console.error("");
|
|
633
|
-
console.error(` \x1B[1m\x1B[32mfied\x1B[0m \u2014 started in background (PID ${child.pid})`);
|
|
634
|
-
console.error(` Session: ${session}`);
|
|
635
|
-
console.error(" Run \x1B[1mnpx fied\x1B[0m again to manage.");
|
|
636
|
-
console.error("");
|
|
637
|
-
setTimeout(() => process.exit(0), 500);
|
|
638
|
-
} else {
|
|
639
|
-
await share({ session, relay, allowInsecureRelay });
|
|
646
|
+
if (!session) {
|
|
647
|
+
throw new Error("No tmux session selected");
|
|
640
648
|
}
|
|
649
|
+
await share({
|
|
650
|
+
session,
|
|
651
|
+
relay,
|
|
652
|
+
allowInsecureRelay,
|
|
653
|
+
onShareUrl: async (url) => {
|
|
654
|
+
const background = await confirm("Run in background?");
|
|
655
|
+
if (!background) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const parsed = parseShareUrl(url);
|
|
659
|
+
const child = spawnBackground({
|
|
660
|
+
session,
|
|
661
|
+
relay,
|
|
662
|
+
allowInsecureRelay,
|
|
663
|
+
sessionId: parsed.sessionId,
|
|
664
|
+
keyBase64Url: parsed.keyBase64Url
|
|
665
|
+
});
|
|
666
|
+
console.error("");
|
|
667
|
+
console.error(` \x1B[1m\x1B[32mfied\x1B[0m \u2014 moved to background (PID ${child.pid})`);
|
|
668
|
+
console.error(` Session: ${session}`);
|
|
669
|
+
console.error(" Same share link stays active.");
|
|
670
|
+
console.error(" Run \x1B[1mnpx fied\x1B[0m again to manage.");
|
|
671
|
+
console.error("");
|
|
672
|
+
setTimeout(() => process.exit(0), 300);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
function spawnBackground(options) {
|
|
677
|
+
const binPath = fileURLToPath(import.meta.url);
|
|
678
|
+
const childArgs = [
|
|
679
|
+
"--__daemon",
|
|
680
|
+
"--session",
|
|
681
|
+
options.session,
|
|
682
|
+
"--__session-id",
|
|
683
|
+
options.sessionId,
|
|
684
|
+
"--__key",
|
|
685
|
+
options.keyBase64Url
|
|
686
|
+
];
|
|
687
|
+
if (options.relay) childArgs.push("--relay", options.relay);
|
|
688
|
+
if (options.allowInsecureRelay) childArgs.push("--allow-insecure-relay");
|
|
689
|
+
const child = spawnChild(process.execPath, [binPath, ...childArgs], {
|
|
690
|
+
detached: true,
|
|
691
|
+
stdio: "ignore"
|
|
692
|
+
});
|
|
693
|
+
child.unref();
|
|
694
|
+
return child;
|
|
695
|
+
}
|
|
696
|
+
function parseShareUrl(url) {
|
|
697
|
+
const parsed = new URL(url);
|
|
698
|
+
const match = parsed.pathname.match(/^\/s\/([A-Za-z0-9_-]{8,64})$/);
|
|
699
|
+
if (!match || !parsed.hash) {
|
|
700
|
+
throw new Error("Invalid share URL");
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
sessionId: match[1],
|
|
704
|
+
keyBase64Url: parsed.hash.slice(1)
|
|
705
|
+
};
|
|
641
706
|
}
|
|
642
707
|
function timeSince(date) {
|
|
643
708
|
const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
|