fied 0.2.7 → 0.2.8
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 +46 -5
- package/package.json +2 -4
package/dist/bin.js
CHANGED
|
@@ -274,7 +274,15 @@ async function share(options) {
|
|
|
274
274
|
console.log(` Size: ${cols}x${rows}`);
|
|
275
275
|
console.log("");
|
|
276
276
|
}
|
|
277
|
-
const bridge = new RelayBridge(
|
|
277
|
+
const bridge = new RelayBridge(
|
|
278
|
+
relayTarget,
|
|
279
|
+
cryptoKey,
|
|
280
|
+
keyFragment,
|
|
281
|
+
pty,
|
|
282
|
+
options.background,
|
|
283
|
+
options.showReadonlyLink,
|
|
284
|
+
options.sessionId
|
|
285
|
+
);
|
|
278
286
|
const onUrl = (url) => {
|
|
279
287
|
if (options.background) {
|
|
280
288
|
const sessionId = bridge.getSessionId();
|
|
@@ -352,19 +360,27 @@ async function createSession(relayHttpBase) {
|
|
|
352
360
|
throw new Error(`Failed to create session: ${res.status} ${res.statusText}`);
|
|
353
361
|
}
|
|
354
362
|
const data = await res.json();
|
|
355
|
-
|
|
363
|
+
if (typeof data.sessionId !== "string" || typeof data.viewerAccessToken !== "string" || typeof data.readonlyAccessToken !== "string") {
|
|
364
|
+
throw new Error("Invalid session creation response");
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
sessionId: data.sessionId,
|
|
368
|
+
viewerAccessToken: data.viewerAccessToken,
|
|
369
|
+
readonlyAccessToken: data.readonlyAccessToken
|
|
370
|
+
};
|
|
356
371
|
}
|
|
357
372
|
var WS_CONNECT_TIMEOUT_MS = 1e4;
|
|
358
373
|
function typeAAD(type) {
|
|
359
374
|
return new Uint8Array([type & 255]);
|
|
360
375
|
}
|
|
361
376
|
var RelayBridge = class {
|
|
362
|
-
constructor(relayTarget, key, keyFragment, pty, silent = false, sessionId) {
|
|
377
|
+
constructor(relayTarget, key, keyFragment, pty, silent = false, showReadonlyLink = false, sessionId) {
|
|
363
378
|
this.relayTarget = relayTarget;
|
|
364
379
|
this.key = key;
|
|
365
380
|
this.keyFragment = keyFragment;
|
|
366
381
|
this.pty = pty;
|
|
367
382
|
this.silent = silent;
|
|
383
|
+
this.showReadonlyLink = showReadonlyLink;
|
|
368
384
|
this.sessionId = sessionId ?? null;
|
|
369
385
|
this.pty.onData((data) => {
|
|
370
386
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
@@ -380,6 +396,8 @@ var RelayBridge = class {
|
|
|
380
396
|
encoder = new TextEncoder();
|
|
381
397
|
decoder = new TextDecoder();
|
|
382
398
|
sessionId = null;
|
|
399
|
+
viewerAccessToken = null;
|
|
400
|
+
readonlyAccessToken = null;
|
|
383
401
|
onUrl = null;
|
|
384
402
|
invalidResizeFrames = 0;
|
|
385
403
|
invalidInputFrames = 0;
|
|
@@ -395,19 +413,29 @@ var RelayBridge = class {
|
|
|
395
413
|
}
|
|
396
414
|
if (!this.sessionId) {
|
|
397
415
|
try {
|
|
398
|
-
|
|
416
|
+
const created = await createSession(this.relayTarget.httpBase);
|
|
417
|
+
this.sessionId = created.sessionId;
|
|
418
|
+
this.viewerAccessToken = created.viewerAccessToken;
|
|
419
|
+
this.readonlyAccessToken = created.readonlyAccessToken;
|
|
399
420
|
} catch {
|
|
400
421
|
if (!this.silent) console.error(" \x1B[31mRelay unreachable, retrying...\x1B[0m");
|
|
401
422
|
this.scheduleReconnect();
|
|
402
423
|
return;
|
|
403
424
|
}
|
|
404
425
|
const shareUrl = new URL(`s/${this.sessionId}`, this.relayTarget.httpBase);
|
|
426
|
+
if (!this.viewerAccessToken || !this.readonlyAccessToken) {
|
|
427
|
+
throw new Error("missing access tokens");
|
|
428
|
+
}
|
|
429
|
+
shareUrl.searchParams.set("token", this.viewerAccessToken);
|
|
405
430
|
const interactiveUrl = `${shareUrl.toString()}#${this.keyFragment}`;
|
|
406
431
|
const readonlyShareUrl = new URL(`${shareUrl.pathname.replace(/\/$/, "")}/v`, shareUrl);
|
|
432
|
+
readonlyShareUrl.searchParams.set("token", this.readonlyAccessToken);
|
|
407
433
|
const readonlyUrl = `${readonlyShareUrl.toString()}#${this.keyFragment}`;
|
|
408
434
|
if (!this.silent) {
|
|
409
435
|
await printShareLinkWithQr("interactive", interactiveUrl);
|
|
410
|
-
|
|
436
|
+
if (this.showReadonlyLink) {
|
|
437
|
+
await printShareLinkWithQr("view-only", readonlyUrl);
|
|
438
|
+
}
|
|
411
439
|
console.log("");
|
|
412
440
|
console.log(" \x1B[2mThe encryption key is in the URL fragment (#) \u2014 the server never sees it.\x1B[0m");
|
|
413
441
|
console.log(" \x1B[2mPress Ctrl+C to stop sharing.\x1B[0m");
|
|
@@ -661,12 +689,14 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
661
689
|
\x1B[1mOptions:\x1B[0m
|
|
662
690
|
--session, -s <name> tmux session to share (auto-detected if only one)
|
|
663
691
|
--relay <url> relay server URL (default: https://fied.app)
|
|
692
|
+
--view-only print a separate view-only share link
|
|
664
693
|
--allow-insecure-relay allow http://localhost relay (dev only)
|
|
665
694
|
--help, -h show this help
|
|
666
695
|
|
|
667
696
|
\x1B[1mExamples:\x1B[0m
|
|
668
697
|
npx fied share the only tmux session
|
|
669
698
|
npx fied -s mysession share a specific session
|
|
699
|
+
npx fied --view-only include a view-only link
|
|
670
700
|
npx fied --relay http://localhost:8787 use a local relay
|
|
671
701
|
`);
|
|
672
702
|
process.exit(0);
|
|
@@ -680,6 +710,8 @@ if (args.includes("--__daemon")) {
|
|
|
680
710
|
options.relay = args[++i];
|
|
681
711
|
} else if (args[i] === "--allow-insecure-relay") {
|
|
682
712
|
options.allowInsecureRelay = true;
|
|
713
|
+
} else if (args[i] === "--view-only") {
|
|
714
|
+
options.showReadonlyLink = true;
|
|
683
715
|
} else if (args[i] === "--__session-id" && args[i + 1]) {
|
|
684
716
|
options.sessionId = args[++i];
|
|
685
717
|
} else if (args[i] === "--__key" && args[i + 1]) {
|
|
@@ -697,6 +729,8 @@ async function main() {
|
|
|
697
729
|
let relay;
|
|
698
730
|
let session;
|
|
699
731
|
let allowInsecureRelay = false;
|
|
732
|
+
let showReadonlyLink = false;
|
|
733
|
+
let showReadonlyLinkExplicit = false;
|
|
700
734
|
for (let i = 0; i < args.length; i++) {
|
|
701
735
|
if ((args[i] === "--session" || args[i] === "-s") && args[i + 1]) {
|
|
702
736
|
session = args[++i];
|
|
@@ -704,6 +738,9 @@ async function main() {
|
|
|
704
738
|
relay = args[++i];
|
|
705
739
|
} else if (args[i] === "--allow-insecure-relay") {
|
|
706
740
|
allowInsecureRelay = true;
|
|
741
|
+
} else if (args[i] === "--view-only") {
|
|
742
|
+
showReadonlyLink = true;
|
|
743
|
+
showReadonlyLinkExplicit = true;
|
|
707
744
|
} else if (!args[i].startsWith("-")) {
|
|
708
745
|
continue;
|
|
709
746
|
} else {
|
|
@@ -763,10 +800,14 @@ async function main() {
|
|
|
763
800
|
if (!session) {
|
|
764
801
|
throw new Error("No tmux session selected");
|
|
765
802
|
}
|
|
803
|
+
if (!showReadonlyLinkExplicit && process.stdin.isTTY && process.stderr.isTTY) {
|
|
804
|
+
showReadonlyLink = await confirm("Enable view-only share link?");
|
|
805
|
+
}
|
|
766
806
|
await share({
|
|
767
807
|
session,
|
|
768
808
|
relay,
|
|
769
809
|
allowInsecureRelay,
|
|
810
|
+
showReadonlyLink,
|
|
770
811
|
onShareUrl: async (url) => {
|
|
771
812
|
const background = await confirm("Run in background?");
|
|
772
813
|
if (!background) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fied",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Share your tmux session in the browser with end-to-end encryption",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin":
|
|
7
|
-
"fied": "./dist/bin.js"
|
|
8
|
-
},
|
|
6
|
+
"bin": "dist/bin.js",
|
|
9
7
|
"files": [
|
|
10
8
|
"dist/bin.js",
|
|
11
9
|
"README.md"
|