ai-remote 0.4.18 → 0.5.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.
@@ -3,7 +3,7 @@ import {
3
3
  VERSION,
4
4
  isTitlebarDragPoint,
5
5
  logicalDisplaySize
6
- } from "./cli-chunk-R2ZEHRBG.mjs";
6
+ } from "./cli-chunk-WBOYG4KJ.mjs";
7
7
 
8
8
  // src/cli/window.ts
9
9
  import { spawn } from "node:child_process";
@@ -412,6 +412,29 @@ async function runWindow(argv) {
412
412
  } catch {
413
413
  }
414
414
  const webview = window.createWebview({ url, enableDevtools: false });
415
+ const SETTLE_MS = 400;
416
+ let reportedFullscreen = null;
417
+ let seen = null;
418
+ let seenSince = 0;
419
+ let publishing = !flags.fullscreen;
420
+ function publishFullscreen(force = false) {
421
+ if (!publishing) return;
422
+ const on = window.fullscreen !== null;
423
+ const now = Date.now();
424
+ if (on !== seen) {
425
+ seen = on;
426
+ seenSince = now;
427
+ }
428
+ if (!force && (on === reportedFullscreen || now - seenSince < SETTLE_MS)) return;
429
+ reportedFullscreen = on;
430
+ try {
431
+ webview.evaluateScript(`window.aiRemoteFullscreen && window.aiRemoteFullscreen(${on})`);
432
+ } catch {
433
+ }
434
+ }
435
+ window.on("resize", () => publishFullscreen());
436
+ webview.on("page-load-finished", () => publishFullscreen(true));
437
+ setInterval(() => publishFullscreen(), 150).unref();
415
438
  const debugDrag = process.env.AI_REMOTE_DEBUG_DRAG === "1";
416
439
  let pageDrag = null;
417
440
  let dragPointer = null;
@@ -427,6 +450,14 @@ async function runWindow(argv) {
427
450
  pageDrag = null;
428
451
  return;
429
452
  }
453
+ if (note.t === "fullscreen") {
454
+ try {
455
+ window.setFullscreen(window.fullscreen !== null ? void 0 : FullscreenType.Borderless);
456
+ } catch {
457
+ }
458
+ publishFullscreen();
459
+ return;
460
+ }
430
461
  if (typeof note.x !== "number" || typeof note.y !== "number") return;
431
462
  if (note.t === "drag-start") {
432
463
  if (window.fullscreen !== null) return;
@@ -468,6 +499,7 @@ async function runWindow(argv) {
468
499
  window.setFullscreen(FullscreenType.Borderless);
469
500
  } catch {
470
501
  }
502
+ publishing = true;
471
503
  }, 400);
472
504
  }
473
505
  await application.whenReady({ interval: 33 });
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SshSession,
3
3
  TcpTransport
4
- } from "./cli-chunk-S2TWL5LF.mjs";
4
+ } from "./cli-chunk-X2JMVOWL.mjs";
5
5
 
6
6
  // src/cli/shell.ts
7
7
  function bareUsername(username) {
@@ -1,5 +1,5 @@
1
1
  // src/cli/generated/version.ts
2
- var VERSION = "0.4.18";
2
+ var VERSION = "0.5.1";
3
3
 
4
4
  // src/cli/viewer-layout.ts
5
5
  var TERMINAL_COLUMNS = 80;
@@ -175,7 +175,9 @@ async function createKexClient(algorithmName) {
175
175
  }
176
176
  };
177
177
  }
178
- const pair = await subtle.generateKey({ name: "ECDH", namedCurve: algorithm.curve }, false, ["deriveBits"]);
178
+ if (!algorithm.curve) throw new Error(`SSH: ${algorithmName} names no curve to exchange over`);
179
+ const curve = algorithm.curve;
180
+ const pair = await subtle.generateKey({ name: "ECDH", namedCurve: curve }, false, ["deriveBits"]);
179
181
  const publicKey = new Uint8Array(await subtle.exportKey("raw", pair.publicKey));
180
182
  return {
181
183
  hash: algorithm.hash,
@@ -184,7 +186,7 @@ async function createKexClient(algorithmName) {
184
186
  const peer = await subtle.importKey(
185
187
  "raw",
186
188
  serverPublicKey,
187
- { name: "ECDH", namedCurve: algorithm.curve },
189
+ { name: "ECDH", namedCurve: curve },
188
190
  false,
189
191
  []
190
192
  );
@@ -244,7 +246,9 @@ function signatureBytes(algorithmName, signatureBlob) {
244
246
  return concatBytes(padStart(r, 32), padStart(s, 32));
245
247
  }
246
248
  async function verifyHostKeySignature({ algorithm, keyBlob, signatureBlob, exchangeHash: exchangeHash2 }) {
247
- const { kind, hash } = HOST_KEY_ALGORITHMS[algorithm];
249
+ const named = HOST_KEY_ALGORITHMS[algorithm];
250
+ if (!named) throw new Error(`SSH: unsupported host key algorithm ${algorithm}`);
251
+ const { kind, hash } = named;
248
252
  const key = await importHostKey(algorithm, keyBlob);
249
253
  const signature = signatureBytes(algorithm, signatureBlob);
250
254
  const parameters = kind === "ecdsa" ? { name: "ECDSA", hash } : kind === "rsa" ? { name: "RSASSA-PKCS1-v1_5" } : { name: "Ed25519" };
@@ -356,6 +360,7 @@ var SshTransport = class extends EventTarget {
356
360
  releaseNewKeys;
357
361
  pendingServerKexInit;
358
362
  established;
363
+ drainChain;
359
364
  /**
360
365
  * @param {string} url gateway WebSocket URL
361
366
  * @param {object} options
@@ -387,10 +392,11 @@ var SshTransport = class extends EventTarget {
387
392
  }
388
393
  // --- connection ----------------------------------------------------------
389
394
  connect() {
390
- this.socket = this.openTransport ? this.openTransport(this.url) : new WebSocket(this.url);
391
- this.socket.binaryType = "arraybuffer";
392
- this.socket.addEventListener("open", () => {
393
- this.socket.send(GATEWAY_READY_SIGNAL);
395
+ const socket = this.openTransport ? this.openTransport(this.url) : new WebSocket(this.url);
396
+ this.socket = socket;
397
+ socket.binaryType = "arraybuffer";
398
+ socket.addEventListener("open", () => {
399
+ socket.send(GATEWAY_READY_SIGNAL);
394
400
  this.#write(encodeUtf8(`${CLIENT_VERSION}\r
395
401
  `));
396
402
  this.keepaliveTimer = setInterval(() => {
@@ -404,18 +410,18 @@ var SshTransport = class extends EventTarget {
404
410
  }
405
411
  }, KEEPALIVE_INTERVAL_MS);
406
412
  });
407
- this.socket.addEventListener("message", (event) => {
413
+ socket.addEventListener("message", (event) => {
408
414
  if (typeof event.data === "string") return;
409
415
  this.#receive(new Uint8Array(event.data));
410
416
  });
411
- this.socket.addEventListener("close", (event) => {
417
+ socket.addEventListener("close", (event) => {
412
418
  this.#finish({
413
419
  clean: event.code === 1e3,
414
420
  code: event.code,
415
421
  message: event.reason || (event.code === 1006 ? "The gateway rejected or could not reach this SSH target. Confirm that SSH is published for this host." : "")
416
422
  });
417
423
  });
418
- this.socket.addEventListener("error", () => {
424
+ socket.addEventListener("error", () => {
419
425
  this.log("The SSH gateway WebSocket reported a transport error; waiting for its close reason.");
420
426
  });
421
427
  }
@@ -792,11 +798,16 @@ var SshTransport = class extends EventTarget {
792
798
  exchangeHash: hash,
793
799
  sessionId: this.sessionId
794
800
  };
801
+ const toServerCipher = CIPHERS[cipherToServer];
802
+ const toClientCipher = CIPHERS[cipherToClient];
803
+ if (!toServerCipher || !toClientCipher) {
804
+ throw new Error(`SSH: negotiated a cipher this client does not implement (${cipherToServer}, ${cipherToClient})`);
805
+ }
795
806
  const [ivToServer, ivToClient, keyToServer, keyToClient] = await Promise.all([
796
807
  deriveKey({ ...parameters, letter: "A", length: GCM_IV_BYTES }),
797
808
  deriveKey({ ...parameters, letter: "B", length: GCM_IV_BYTES }),
798
- deriveKey({ ...parameters, letter: "C", length: CIPHERS[cipherToServer].keyLength }),
799
- deriveKey({ ...parameters, letter: "D", length: CIPHERS[cipherToClient].keyLength })
809
+ deriveKey({ ...parameters, letter: "C", length: toServerCipher.keyLength }),
810
+ deriveKey({ ...parameters, letter: "D", length: toClientCipher.keyLength })
800
811
  ]);
801
812
  this.outgoing = await GcmCipher.create(keyToServer, ivToServer);
802
813
  this.incoming = await GcmCipher.create(keyToClient, ivToClient);
@@ -956,6 +967,52 @@ function releaseDisplayFilter(session) {
956
967
  }));
957
968
  }
958
969
  var SshSession = class extends EventTarget {
970
+ options;
971
+ username;
972
+ password;
973
+ /**
974
+ * Keys to sign in with, if any. A function rather than a list is allowed
975
+ * because reading keys off a disk -- or asking an agent for them -- is work
976
+ * a session that never needs them should not do.
977
+ */
978
+ /** Replaced with the resolved list the first time it is read. */
979
+ identities;
980
+ requestInput;
981
+ /** Whether there is anybody to ask; see the constructor. */
982
+ canAsk;
983
+ columns;
984
+ rows;
985
+ /** A subsystem to run instead of a shell, e.g. 'sftp'. */
986
+ subsystem;
987
+ channelId;
988
+ remoteChannelId;
989
+ remoteWindow;
990
+ remoteMaxPacket;
991
+ localWindow;
992
+ pendingWrites;
993
+ shellOpen;
994
+ commandInFlight;
995
+ /** Set while a tool command runs, to keep its framing off the screen. */
996
+ displayFilter;
997
+ /** True only after the requested interactive shell is ready for commands. */
998
+ readySignaled;
999
+ powerShellAttempted;
1000
+ powerShellTimer;
1001
+ /** '', 'posix', 'cmd' or 'powershell'; learned from the prompt. */
1002
+ shellFamily;
1003
+ promptTail;
1004
+ exitStatus;
1005
+ exitSignal;
1006
+ authMethods;
1007
+ /** Which keys were offered and refused, for the message if nothing works. */
1008
+ triedKeys;
1009
+ /** Set once the host has said no to the stored password. */
1010
+ passwordRejected;
1011
+ closed;
1012
+ waiters;
1013
+ transport;
1014
+ fingerprint;
1015
+ lastMessage;
959
1016
  /**
960
1017
  * @param {string} url gateway WebSocket URL
961
1018
  * @param {object} options
@@ -1033,7 +1090,7 @@ var SshSession = class extends EventTarget {
1033
1090
  this.transport.addEventListener("close", (event) => {
1034
1091
  if (this.closed) return;
1035
1092
  this.closed = true;
1036
- clearTimeout(this.powerShellTimer);
1093
+ clearTimeout(this.powerShellTimer ?? void 0);
1037
1094
  this.#abandonWaiters(event.detail.message || "The SSH connection closed.");
1038
1095
  this.dispatchEvent(new CustomEvent("close", {
1039
1096
  detail: {
@@ -1050,7 +1107,7 @@ var SshSession = class extends EventTarget {
1050
1107
  }
1051
1108
  disconnect() {
1052
1109
  if (this.closed) return;
1053
- clearTimeout(this.powerShellTimer);
1110
+ clearTimeout(this.powerShellTimer ?? void 0);
1054
1111
  if (this.shellOpen && this.remoteChannelId !== null) {
1055
1112
  this.transport.send(new SshWriter(8).u8(MSG.CHANNEL_CLOSE).u32(this.remoteChannelId).take());
1056
1113
  }
@@ -1111,9 +1168,9 @@ var SshSession = class extends EventTarget {
1111
1168
  return new Promise((resolve, reject) => {
1112
1169
  let transcript = "";
1113
1170
  let started = false;
1114
- let startTimer = null;
1115
- let idleTimer = null;
1116
- let maxTimer = null;
1171
+ let startTimer;
1172
+ let idleTimer;
1173
+ let maxTimer;
1117
1174
  const finish = (settle) => {
1118
1175
  clearTimeout(startTimer);
1119
1176
  clearTimeout(idleTimer);
@@ -1149,7 +1206,7 @@ var SshSession = class extends EventTarget {
1149
1206
  timedOut: false,
1150
1207
  started,
1151
1208
  durationMs: Date.now() - startedAt,
1152
- error: error.message
1209
+ error: error instanceof Error ? error.message : String(error)
1153
1210
  }
1154
1211
  }));
1155
1212
  reject(error);
@@ -1174,7 +1231,7 @@ var SshSession = class extends EventTarget {
1174
1231
  if (!started && transcript.indexOf(begin) !== -1) {
1175
1232
  started = true;
1176
1233
  clearTimeout(startTimer);
1177
- startTimer = null;
1234
+ startTimer = void 0;
1178
1235
  }
1179
1236
  if (started) {
1180
1237
  clearTimeout(idleTimer);
@@ -1333,7 +1390,7 @@ var SshSession = class extends EventTarget {
1333
1390
  }, 2e3);
1334
1391
  }
1335
1392
  #markReady(message = "Connected.") {
1336
- clearTimeout(this.powerShellTimer);
1393
+ clearTimeout(this.powerShellTimer ?? void 0);
1337
1394
  this.powerShellTimer = null;
1338
1395
  this.readySignaled = true;
1339
1396
  this.#status("ready", message);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SshSession,
3
3
  TcpTransport
4
- } from "./cli-chunk-S2TWL5LF.mjs";
4
+ } from "./cli-chunk-X2JMVOWL.mjs";
5
5
  import {
6
6
  SshReader,
7
7
  SshWriter