ai-remote 0.4.15 → 0.4.17

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  Shell,
3
3
  bareUsername
4
- } from "./cli-chunk-RBCU3NX4.mjs";
5
- import "./cli-chunk-EYQCDSPT.mjs";
4
+ } from "./cli-chunk-Q33YGIDO.mjs";
5
+ import "./cli-chunk-S2TWL5LF.mjs";
6
6
  import "./cli-chunk-XT2FISR5.mjs";
7
7
  export {
8
8
  Shell,
@@ -4,8 +4,8 @@ import {
4
4
  openWindow,
5
5
  readCurrentDisplaySize,
6
6
  runWindow
7
- } from "./cli-chunk-7UHHVT7Q.mjs";
8
- import "./cli-chunk-K2DYJXC5.mjs";
7
+ } from "./cli-chunk-DGHEB4P5.mjs";
8
+ import "./cli-chunk-ZQ32C4LC.mjs";
9
9
  export {
10
10
  detectCurrentDisplaySize,
11
11
  nativeWindowAvailable,
package/dist/cli.mjs CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  import {
6
6
  RUN_DIR,
7
7
  SIDE_PANEL_WIDTH,
8
+ VERSION,
8
9
  currentPath,
9
10
  ensureRunDir,
10
11
  fullscreenRdpSize,
@@ -12,7 +13,7 @@ import {
12
13
  metaPath,
13
14
  sessionName,
14
15
  socketPath
15
- } from "./cli-chunk-K2DYJXC5.mjs";
16
+ } from "./cli-chunk-ZQ32C4LC.mjs";
16
17
 
17
18
  // src/cli/cli.ts
18
19
  import { basename, resolve as resolvePath } from "node:path";
@@ -169,6 +170,8 @@ The rest drive that session, and take no host:
169
170
  ${NAME} key <Code> press keys, e.g. MetaLeft, ControlLeft+KeyA
170
171
  ${NAME} exec "cmd" run a command in the terminal (SSH)
171
172
  ${NAME} shell an interactive terminal
173
+ ${NAME} reconnect a new terminal: fresh login, fresh PATH
174
+ ${NAME} clipboard [on|off] share this machine's clipboard both ways
172
175
  ${NAME} cp <src> <dst> copy files; a remote path starts with ':'
173
176
  ${NAME} do "steps" several actions in one go
174
177
  ${NAME} vnc give this session a screen over VNC
@@ -182,6 +185,7 @@ The rest drive that session, and take no host:
182
185
  ${NAME} forget [<host[:port]>] throw one away; --all throws all
183
186
  ${NAME} close close it; --all closes every session
184
187
  ${NAME} probe [<host[:port]>] is anything listening?
188
+ ${NAME} --version which version this is
185
189
 
186
190
  Copying files, like scp but with the session standing in for the host:
187
191
  ${NAME} cp ./setup.exe :C:/Users/owner/Desktop/ to the session in hand
@@ -217,6 +221,11 @@ Options for open
217
221
  --fullscreen fill the display; RDP uses its current resolution
218
222
  --side-panel open the SSH terminal docked on the right
219
223
  --audio play the remote computer's sound in the window (RDP)
224
+ --no-clipboard do not share this machine's clipboard (shared by default)
225
+ --keys MODE adapt | literal (default: adapt)
226
+ adapt swaps Control and Command when one end is a Mac,
227
+ so Command-C copies on Windows and Control-C copies on
228
+ a Mac; literal sends every key through as itself
220
229
  --view-port N port for the window (default: 7373)
221
230
  --idle MINUTES close an unused session (default: 0, never)
222
231
  --headless no window (same as --no-view)
@@ -230,6 +239,7 @@ Options for open
230
239
  Options for the rest
231
240
  --session NAME which session to drive (default: the last opened)
232
241
  -r, --recursive for cp: copy a directory
242
+ --reconnect for exec: a new terminal first, then the command
233
243
  -o, --out FILE where a screenshot goes (default: screen.png)
234
244
  --max-edge N shrink a screenshot to fit N
235
245
  --json machine-readable result on stdout
@@ -277,7 +287,8 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
277
287
  "ssh-port",
278
288
  "settle",
279
289
  "vnc-port",
280
- "port"
290
+ "port",
291
+ "keys"
281
292
  ]);
282
293
  function parse(argv) {
283
294
  const flags = {};
@@ -377,7 +388,7 @@ function refuseTarget(command, token) {
377
388
  );
378
389
  }
379
390
  var DESKTOP_ONLY = /* @__PURE__ */ new Set(["shot", "click", "type", "key", "do"]);
380
- var TAKES_NOTHING = /* @__PURE__ */ new Set(["shot", "view", "status", "shell", "close", "vnc"]);
391
+ var TAKES_NOTHING = /* @__PURE__ */ new Set(["shot", "view", "status", "shell", "close", "vnc", "reconnect"]);
381
392
  var COORDINATE = /^-?\d+\s*,\s*-?\d+$/;
382
393
  function refuseLegacyTarget(args) {
383
394
  const { command, rest } = args;
@@ -405,7 +416,17 @@ async function ask(name, op, args = {}, timeoutMs) {
405
416
  return response.result;
406
417
  }
407
418
  async function tell(session, op, args = {}, timeoutMs) {
408
- const result = await ask(session.name, op, args, timeoutMs);
419
+ const result = await ask(session.name, op, args, timeoutMs).catch((error) => {
420
+ const message = error instanceof Error ? error.message : String(error);
421
+ if (/^Unknown operation/.test(message)) {
422
+ throw new CliError(
423
+ `The session ${session.name} does not support \`${op}\`: it was started by an older version of ${NAME} and is still running it.
424
+ ${NAME} close --session ${session.name} then open it again`,
425
+ 12
426
+ );
427
+ }
428
+ throw error;
429
+ });
409
430
  if (result === null) {
410
431
  cleanup(session.name);
411
432
  throw new CliError(`The session ${session.name} is no longer answering. It has been cleaned up.`, 12);
@@ -418,7 +439,7 @@ async function startSession(args, name, host, port, secret) {
418
439
  const explicitHeight = flag(args, "H", "height");
419
440
  let fullscreenSize = null;
420
441
  if (mode === "rdp" && has(args, "fullscreen") && (!explicitWidth || !explicitHeight)) {
421
- const { detectCurrentDisplaySize } = await import("./cli-window-LN32ONC4.mjs");
442
+ const { detectCurrentDisplaySize } = await import("./cli-window-H5VHVL4I.mjs");
422
443
  const display = await detectCurrentDisplaySize();
423
444
  if (display) fullscreenSize = fullscreenRdpSize(display, has(args, "side-panel"));
424
445
  }
@@ -454,12 +475,15 @@ async function startSession(args, name, host, port, secret) {
454
475
  // Zero unless asked for: a session stays open until it is closed. An agent
455
476
  // that pauses to think is not an agent that has finished.
456
477
  "--idle",
457
- flag(args, "idle") ?? "0"
478
+ flag(args, "idle") ?? "0",
479
+ "--keys",
480
+ flag(args, "keys") ?? "adapt"
458
481
  ];
459
482
  if (mode !== "rdp") daemonArgs.push(`--${mode}`);
460
483
  for (const identity of args.identities) daemonArgs.push("-i", identity);
461
484
  if (has(args, "no-view", "headless")) daemonArgs.push("--no-view");
462
485
  if (has(args, "no-open")) daemonArgs.push("--no-open");
486
+ if (has(args, "no-clipboard")) daemonArgs.push("--no-clipboard");
463
487
  if (has(args, "watch-only", "observe")) daemonArgs.push("--watch-only");
464
488
  if (has(args, "tab")) daemonArgs.push("--tab");
465
489
  if (has(args, "fullscreen")) daemonArgs.push("--fullscreen");
@@ -527,13 +551,18 @@ function sessionFailure(name, died, secret, mode) {
527
551
  }
528
552
  async function main() {
529
553
  const args = parse(process.argv.slice(2));
554
+ if (has(args, "v", "version") || args.command === "version") {
555
+ process.stdout.write(`${NAME} ${VERSION}
556
+ `);
557
+ return;
558
+ }
530
559
  if (!args.command || has(args, "h", "help")) {
531
560
  process.stdout.write(USAGE);
532
561
  return;
533
562
  }
534
563
  const json = has(args, "json");
535
564
  if (args.command === "__display-size") {
536
- const { readCurrentDisplaySize } = await import("./cli-window-LN32ONC4.mjs");
565
+ const { readCurrentDisplaySize } = await import("./cli-window-H5VHVL4I.mjs");
537
566
  const size = await readCurrentDisplaySize();
538
567
  if (size) process.stdout.write(`${JSON.stringify(size)}
539
568
  `);
@@ -542,7 +571,7 @@ async function main() {
542
571
  if (args.command === "__session") {
543
572
  const mode = modeFor(args, args.rest[0]);
544
573
  const { host, port } = splitTarget(args.rest[0] ?? "", defaultPort(mode));
545
- const { runDaemon } = await import("./cli-daemon-LU4LHDCH.mjs");
574
+ const { runDaemon } = await import("./cli-daemon-4YPQAC5X.mjs");
546
575
  await runDaemon({
547
576
  name: sessionName(host, port, flag(args, "name", "session")),
548
577
  host,
@@ -568,12 +597,14 @@ async function main() {
568
597
  // RDP is the only protocol here with an audio channel, and a session with
569
598
  // no window has nowhere to play what the channel would carry.
570
599
  audio: has(args, "audio", "sound") && mode === "rdp" && !has(args, "no-view", "headless"),
571
- idleMs: Math.max(0, Number(flag(args, "idle") ?? 0)) * 6e4
600
+ idleMs: Math.max(0, Number(flag(args, "idle") ?? 0)) * 6e4,
601
+ keys: flag(args, "keys") === "literal" ? "literal" : "adapt",
602
+ clipboard: !has(args, "no-clipboard")
572
603
  });
573
604
  return;
574
605
  }
575
606
  if (args.command === "__window") {
576
- const { runWindow } = await import("./cli-window-LN32ONC4.mjs");
607
+ const { runWindow } = await import("./cli-window-H5VHVL4I.mjs");
577
608
  await runWindow(process.argv.slice(3));
578
609
  return;
579
610
  }
@@ -620,6 +651,10 @@ Saved, nothing open (\`${NAME} open <host>\` needs no password):
620
651
  if (args.command === "open") {
621
652
  const target = args.rest[0];
622
653
  if (!target) throw new CliError(`Name the machine to connect to: \`${NAME} open <host[:port]>\``, 12);
654
+ const keys = flag(args, "keys");
655
+ if (keys && keys !== "adapt" && keys !== "literal") {
656
+ throw new CliError(`--keys takes "adapt" or "literal", not "${keys}".`, 12);
657
+ }
623
658
  const mode = modeFor(args, target);
624
659
  const { host, port } = splitTarget(target, defaultPort(mode));
625
660
  const asked = flag(args, "session", "name");
@@ -665,6 +700,11 @@ Saved, nothing open (\`${NAME} open <host>\` needs no password):
665
700
  sidePanel: has(args, "side-panel")
666
701
  }));
667
702
  }
703
+ const shared = await ask(name2, "clipboard", {});
704
+ Object.assign(status, { clipboard: shared });
705
+ if (has(args, "clipboard") && !shared?.available) {
706
+ process.stderr.write("The clipboard cannot be shared with this session (it needs a desktop at the far end and a clipboard tool here).\n");
707
+ }
668
708
  setCurrent(name2);
669
709
  report(json, "open", {
670
710
  host,
@@ -682,6 +722,14 @@ Saved, nothing open (\`${NAME} open <host>\` needs no password):
682
722
  if (prompted && !saved) {
683
723
  process.stderr.write("Add --save next time to keep that password.\n");
684
724
  }
725
+ if (status.keys?.swapped && status.keys?.describe) {
726
+ process.stderr.write(`${status.keys.describe}
727
+ `);
728
+ }
729
+ if (status.clipboard?.on) {
730
+ process.stderr.write(`Clipboard shared both ways (${status.clipboard.tool}). \`${NAME} clipboard off\` stops it; --no-clipboard opens without it.
731
+ `);
732
+ }
685
733
  return;
686
734
  }
687
735
  if (args.command === "probe") {
@@ -759,6 +807,32 @@ Saved, nothing open (\`${NAME} open <host>\` needs no password):
759
807
  }
760
808
  const session = currentSession(args);
761
809
  const { name } = session;
810
+ if (args.command === "reconnect") {
811
+ const before = await ask(name, "status", {}, 5e3);
812
+ const result = await tell(session, "shell-reconnect");
813
+ report(json, "reconnect", {
814
+ name,
815
+ host: session.host,
816
+ port: session.port,
817
+ replaced: result.replaced,
818
+ previous: before?.shell ?? "unknown",
819
+ shell: result.shell
820
+ });
821
+ return;
822
+ }
823
+ if (args.command === "clipboard") {
824
+ const asked = args.rest[0]?.toLowerCase();
825
+ if (asked && !["on", "off"].includes(asked)) {
826
+ throw new CliError(`\`${NAME} clipboard\` takes "on", "off", or nothing to report.`, 12);
827
+ }
828
+ const state = await tell(
829
+ session,
830
+ "clipboard",
831
+ asked ? { on: asked === "on" } : {}
832
+ );
833
+ report(json, "clipboard", { name, ...state });
834
+ return;
835
+ }
762
836
  if (args.command === "shell") {
763
837
  await interactiveShell(args, session);
764
838
  return;
@@ -886,6 +960,7 @@ Open the host again without --ssh for a desktop.`,
886
960
  refuseSessionHost(args, "exec", session);
887
961
  const command = args.rest.join(" ");
888
962
  if (!command) throw new CliError("exec needs a command to run", 12);
963
+ if (has(args, "reconnect", "fresh")) await tell(session, "shell-reconnect");
889
964
  const result = await tell(session, "exec", { command });
890
965
  Object.assign(summary, result);
891
966
  if (!json) {
@@ -911,7 +986,7 @@ function report(json, command, summary) {
911
986
  `);
912
987
  }
913
988
  async function sshLogin(args, session, what) {
914
- const { bareUsername } = await import("./cli-shell-TE6DLBIB.mjs");
989
+ const { bareUsername } = await import("./cli-shell-45XRTE7O.mjs");
915
990
  const username = bareUsername(flag(args, "ssh-user") ?? session.sshUser ?? "");
916
991
  if (!username) {
917
992
  throw new CliError(
@@ -993,7 +1068,7 @@ async function copyFiles(args, json) {
993
1068
  upload,
994
1069
  walkLocal,
995
1070
  walkRemote
996
- } = await import("./cli-copy-NRXMTHID.mjs");
1071
+ } = await import("./cli-copy-BOF44DWQ.mjs");
997
1072
  const from = resolveEndpoint(args, parseEndpoint(args.rest[0]));
998
1073
  const to = resolveEndpoint(args, parseEndpoint(args.rest[1]));
999
1074
  if (!from.endpoint.remote && !to.endpoint.remote) {
@@ -1071,7 +1146,7 @@ async function copyFiles(args, json) {
1071
1146
  }
1072
1147
  }
1073
1148
  async function interactiveShell(args, session) {
1074
- const { Shell } = await import("./cli-shell-TE6DLBIB.mjs");
1149
+ const { Shell } = await import("./cli-shell-45XRTE7O.mjs");
1075
1150
  const login = await sshLogin(args, session, "a terminal");
1076
1151
  const { username, port, identities } = login;
1077
1152
  const prompts = prompting(args);
@@ -1172,6 +1247,15 @@ The password is saved in ${summary.saved}.` : ""}`;
1172
1247
  const what = files === 1 ? "" : `${files} files, `;
1173
1248
  return `Copied ${what}${summary.size} from ${summary.from} to ${summary.to}${summary.rate ? ` at ${summary.rate}` : ""}.`;
1174
1249
  }
1250
+ if (command === "clipboard") {
1251
+ if (!summary.available) {
1252
+ return "The clipboard cannot be shared with this session: it needs a desktop at the far end and a clipboard tool on this machine.";
1253
+ }
1254
+ return summary.on ? `Clipboard sharing is on for ${summary.name} (${summary.tool}). Copy here and paste there, or the other way round.` : `Clipboard sharing is off for ${summary.name}.`;
1255
+ }
1256
+ if (command === "reconnect") {
1257
+ return `${summary.replaced ? "Replaced" : "Opened"} the terminal on ${summary.name}. It is a new login, so it sees whatever the PATH says now -- and has forgotten the working directory and any exported variables.`;
1258
+ }
1175
1259
  if (command === "shot") return `Wrote ${summary.file} (${summary.width}x${summary.height}) from ${summary.name}.`;
1176
1260
  if (command === "click") {
1177
1261
  const { x, y } = summary.clicked;