fied 0.2.8 → 0.2.9

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.
Files changed (2) hide show
  1. package/dist/bin.js +53 -34
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -262,9 +262,13 @@ async function share(options) {
262
262
  }
263
263
  const cols = options.cols ?? process.stdout.columns ?? 80;
264
264
  const rows = options.rows ?? process.stdout.rows ?? 24;
265
- const rawKey = options.keyBase64Url ? fromBase64Url(options.keyBase64Url) : await generateKey();
266
- const cryptoKey = await importKey(rawKey);
267
- const keyFragment = options.keyBase64Url ?? toBase64Url(rawKey);
265
+ const legacyKey = options.keyBase64Url;
266
+ const readKeyRaw = options.readKeyBase64Url ? fromBase64Url(options.readKeyBase64Url) : legacyKey ? fromBase64Url(legacyKey) : await generateKey();
267
+ const writeKeyRaw = options.writeKeyBase64Url ? fromBase64Url(options.writeKeyBase64Url) : legacyKey ? fromBase64Url(legacyKey) : await generateKey();
268
+ const readKey = await importKey(readKeyRaw);
269
+ const writeKey = await importKey(writeKeyRaw);
270
+ const readKeyFragment = options.readKeyBase64Url ?? legacyKey ?? toBase64Url(readKeyRaw);
271
+ const writeKeyFragment = options.writeKeyBase64Url ?? legacyKey ?? toBase64Url(writeKeyRaw);
268
272
  const pty = attachSession(targetSession, cols, rows);
269
273
  if (!options.background) {
270
274
  console.log("");
@@ -276,8 +280,10 @@ async function share(options) {
276
280
  }
277
281
  const bridge = new RelayBridge(
278
282
  relayTarget,
279
- cryptoKey,
280
- keyFragment,
283
+ readKey,
284
+ writeKey,
285
+ readKeyFragment,
286
+ writeKeyFragment,
281
287
  pty,
282
288
  options.background,
283
289
  options.showReadonlyLink,
@@ -360,24 +366,22 @@ async function createSession(relayHttpBase) {
360
366
  throw new Error(`Failed to create session: ${res.status} ${res.statusText}`);
361
367
  }
362
368
  const data = await res.json();
363
- if (typeof data.sessionId !== "string" || typeof data.viewerAccessToken !== "string" || typeof data.readonlyAccessToken !== "string") {
369
+ if (typeof data.sessionId !== "string") {
364
370
  throw new Error("Invalid session creation response");
365
371
  }
366
- return {
367
- sessionId: data.sessionId,
368
- viewerAccessToken: data.viewerAccessToken,
369
- readonlyAccessToken: data.readonlyAccessToken
370
- };
372
+ return data.sessionId;
371
373
  }
372
374
  var WS_CONNECT_TIMEOUT_MS = 1e4;
373
375
  function typeAAD(type) {
374
376
  return new Uint8Array([type & 255]);
375
377
  }
376
378
  var RelayBridge = class {
377
- constructor(relayTarget, key, keyFragment, pty, silent = false, showReadonlyLink = false, sessionId) {
379
+ constructor(relayTarget, readKey, writeKey, readKeyFragment, writeKeyFragment, pty, silent = false, showReadonlyLink = false, sessionId) {
378
380
  this.relayTarget = relayTarget;
379
- this.key = key;
380
- this.keyFragment = keyFragment;
381
+ this.readKey = readKey;
382
+ this.writeKey = writeKey;
383
+ this.readKeyFragment = readKeyFragment;
384
+ this.writeKeyFragment = writeKeyFragment;
381
385
  this.pty = pty;
382
386
  this.silent = silent;
383
387
  this.showReadonlyLink = showReadonlyLink;
@@ -396,8 +400,6 @@ var RelayBridge = class {
396
400
  encoder = new TextEncoder();
397
401
  decoder = new TextDecoder();
398
402
  sessionId = null;
399
- viewerAccessToken = null;
400
- readonlyAccessToken = null;
401
403
  onUrl = null;
402
404
  invalidResizeFrames = 0;
403
405
  invalidInputFrames = 0;
@@ -413,24 +415,16 @@ var RelayBridge = class {
413
415
  }
414
416
  if (!this.sessionId) {
415
417
  try {
416
- const created = await createSession(this.relayTarget.httpBase);
417
- this.sessionId = created.sessionId;
418
- this.viewerAccessToken = created.viewerAccessToken;
419
- this.readonlyAccessToken = created.readonlyAccessToken;
418
+ this.sessionId = await createSession(this.relayTarget.httpBase);
420
419
  } catch {
421
420
  if (!this.silent) console.error(" \x1B[31mRelay unreachable, retrying...\x1B[0m");
422
421
  this.scheduleReconnect();
423
422
  return;
424
423
  }
425
424
  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);
430
- const interactiveUrl = `${shareUrl.toString()}#${this.keyFragment}`;
425
+ const interactiveUrl = `${shareUrl.toString()}#r=${encodeURIComponent(this.readKeyFragment)}&w=${encodeURIComponent(this.writeKeyFragment)}`;
431
426
  const readonlyShareUrl = new URL(`${shareUrl.pathname.replace(/\/$/, "")}/v`, shareUrl);
432
- readonlyShareUrl.searchParams.set("token", this.readonlyAccessToken);
433
- const readonlyUrl = `${readonlyShareUrl.toString()}#${this.keyFragment}`;
427
+ const readonlyUrl = `${readonlyShareUrl.toString()}#r=${encodeURIComponent(this.readKeyFragment)}`;
434
428
  if (!this.silent) {
435
429
  await printShareLinkWithQr("interactive", interactiveUrl);
436
430
  if (this.showReadonlyLink) {
@@ -474,7 +468,7 @@ var RelayBridge = class {
474
468
  const data = new Uint8Array(raw);
475
469
  const frame = parseFrame(data);
476
470
  if (frame.type === MSG_TERMINAL_INPUT) {
477
- const plaintext = await decrypt(this.key, frame.iv, frame.ciphertext, typeAAD(frame.type));
471
+ const plaintext = await decrypt(this.writeKey, frame.iv, frame.ciphertext, typeAAD(frame.type));
478
472
  const input = parseInputPayload(this.decoder.decode(plaintext));
479
473
  if (!input || this.isReplayNonce(input.nonce)) {
480
474
  this.invalidInputFrames += 1;
@@ -487,7 +481,7 @@ var RelayBridge = class {
487
481
  this.rememberInputNonce(input.nonce);
488
482
  this.pty.write(input.data);
489
483
  } else if (frame.type === MSG_RESIZE) {
490
- const plaintext = await decrypt(this.key, frame.iv, frame.ciphertext, typeAAD(frame.type));
484
+ const plaintext = await decrypt(this.writeKey, frame.iv, frame.ciphertext, typeAAD(frame.type));
491
485
  const resize = parseResizePayload(this.decoder.decode(plaintext));
492
486
  if (!resize) {
493
487
  this.invalidResizeFrames += 1;
@@ -546,7 +540,8 @@ var RelayBridge = class {
546
540
  async sendEncrypted(type, plaintext) {
547
541
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
548
542
  try {
549
- const { iv, ciphertext } = await encrypt(this.key, plaintext, typeAAD(type));
543
+ const key = type === MSG_TERMINAL_OUTPUT ? this.readKey : this.writeKey;
544
+ const { iv, ciphertext } = await encrypt(key, plaintext, typeAAD(type));
550
545
  const frame = frameMessage(type, iv, ciphertext);
551
546
  this.ws.send(frame);
552
547
  } catch (err) {
@@ -714,6 +709,10 @@ if (args.includes("--__daemon")) {
714
709
  options.showReadonlyLink = true;
715
710
  } else if (args[i] === "--__session-id" && args[i + 1]) {
716
711
  options.sessionId = args[++i];
712
+ } else if (args[i] === "--__read-key" && args[i + 1]) {
713
+ options.readKeyBase64Url = args[++i];
714
+ } else if (args[i] === "--__write-key" && args[i + 1]) {
715
+ options.writeKeyBase64Url = args[++i];
717
716
  } else if (args[i] === "--__key" && args[i + 1]) {
718
717
  options.keyBase64Url = args[++i];
719
718
  }
@@ -819,7 +818,8 @@ async function main() {
819
818
  relay,
820
819
  allowInsecureRelay,
821
820
  sessionId: parsed.sessionId,
822
- keyBase64Url: parsed.keyBase64Url
821
+ readKeyBase64Url: parsed.readKeyBase64Url,
822
+ writeKeyBase64Url: parsed.writeKeyBase64Url
823
823
  });
824
824
  console.error("");
825
825
  console.error(` \x1B[1m\x1B[32mfied\x1B[0m \u2014 moved to background (PID ${child.pid})`);
@@ -839,8 +839,10 @@ function spawnBackground(options) {
839
839
  options.session,
840
840
  "--__session-id",
841
841
  options.sessionId,
842
- "--__key",
843
- options.keyBase64Url
842
+ "--__read-key",
843
+ options.readKeyBase64Url,
844
+ "--__write-key",
845
+ options.writeKeyBase64Url
844
846
  ];
845
847
  if (options.relay) childArgs.push("--relay", options.relay);
846
848
  if (options.allowInsecureRelay) childArgs.push("--allow-insecure-relay");
@@ -857,9 +859,26 @@ function parseShareUrl(url) {
857
859
  if (!match || !parsed.hash) {
858
860
  throw new Error("Invalid share URL");
859
861
  }
862
+ const hashRaw = parsed.hash.slice(1);
863
+ let readKeyBase64Url = "";
864
+ let writeKeyBase64Url = "";
865
+ if (hashRaw.includes("=") || hashRaw.includes("&")) {
866
+ const params = new URLSearchParams(hashRaw);
867
+ const read = params.get("r");
868
+ const write = params.get("w");
869
+ if (read && write) {
870
+ readKeyBase64Url = read;
871
+ writeKeyBase64Url = write;
872
+ }
873
+ }
874
+ if (!readKeyBase64Url || !writeKeyBase64Url) {
875
+ readKeyBase64Url = hashRaw;
876
+ writeKeyBase64Url = hashRaw;
877
+ }
860
878
  return {
861
879
  sessionId: match[1],
862
- keyBase64Url: parsed.hash.slice(1)
880
+ readKeyBase64Url,
881
+ writeKeyBase64Url
863
882
  };
864
883
  }
865
884
  function timeSince(date) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fied",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Share your tmux session in the browser with end-to-end encryption",
5
5
  "type": "module",
6
6
  "bin": "dist/bin.js",