ragent-cli 1.6.2 → 1.7.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.
Files changed (2) hide show
  1. package/dist/index.js +103 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "ragent-cli",
34
- version: "1.6.2",
34
+ version: "1.7.1",
35
35
  description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
36
36
  main: "dist/index.js",
37
37
  bin: {
@@ -682,6 +682,43 @@ function decodeJwtExp(token) {
682
682
  return null;
683
683
  }
684
684
  }
685
+ async function startSessionWithMachineSecret(params) {
686
+ console.log("[rAgent] Attempting session recovery with machine credential...");
687
+ const response = await fetch(`${params.portal}/api/agent/session/start`, {
688
+ method: "POST",
689
+ headers: { "Content-Type": "application/json" },
690
+ body: JSON.stringify({
691
+ hostId: params.hostId,
692
+ machineSecret: params.machineSecret
693
+ })
694
+ });
695
+ if (response.status === 401 || response.status === 403) {
696
+ throw new AuthError(
697
+ "Machine credential rejected \u2014 agent may be revoked. Re-connect with: ragent connect --token <token>"
698
+ );
699
+ }
700
+ if (!response.ok) {
701
+ const data2 = await response.json().catch(() => ({}));
702
+ throw new Error(
703
+ `Session start failed: ${data2.error || response.status}`
704
+ );
705
+ }
706
+ const data = await response.json();
707
+ if (!data.agentToken) {
708
+ throw new Error("Session start response missing agentToken");
709
+ }
710
+ const patch = {
711
+ agentToken: data.agentToken,
712
+ tokenExpiresAt: data.expiresAt || ""
713
+ };
714
+ if (data.refreshToken) {
715
+ patch.refreshToken = data.refreshToken;
716
+ patch.refreshExpiresAt = data.refreshExpiresAt || "";
717
+ }
718
+ saveConfigPatch(patch);
719
+ console.log("[rAgent] Session recovered via machine credential.");
720
+ return data.agentToken;
721
+ }
685
722
  async function refreshTokenIfNeeded(params) {
686
723
  const config = loadConfig();
687
724
  const refreshToken = config.refreshToken;
@@ -707,10 +744,23 @@ async function refreshTokenIfNeeded(params) {
707
744
  body: JSON.stringify(body)
708
745
  });
709
746
  if (!response.ok) {
710
- const data2 = await response.json().catch(() => ({}));
747
+ const errorData = await response.json().catch(() => ({}));
711
748
  console.warn(
712
- `[rAgent] Token refresh failed: ${data2.error || response.status}`
749
+ `[rAgent] Token refresh failed: ${errorData.error || response.status}`
713
750
  );
751
+ if (config.machineSecret && config.hostId) {
752
+ try {
753
+ return await startSessionWithMachineSecret({
754
+ portal: params.portal,
755
+ hostId: config.hostId,
756
+ machineSecret: config.machineSecret
757
+ });
758
+ } catch (mcError) {
759
+ if (mcError instanceof AuthError) throw mcError;
760
+ const mcMessage = mcError instanceof Error ? mcError.message : String(mcError);
761
+ console.warn(`[rAgent] Machine credential recovery failed: ${mcMessage}`);
762
+ }
763
+ }
714
764
  return params.agentToken;
715
765
  }
716
766
  const data = await response.json();
@@ -723,10 +773,14 @@ async function refreshTokenIfNeeded(params) {
723
773
  patch.refreshToken = data.refreshToken;
724
774
  patch.refreshExpiresAt = data.refreshExpiresAt || "";
725
775
  }
776
+ if (data.machineSecret) {
777
+ patch.machineSecret = data.machineSecret;
778
+ }
726
779
  saveConfigPatch(patch);
727
780
  console.log("[rAgent] Token refreshed successfully.");
728
781
  return data.agentToken;
729
782
  } catch (error) {
783
+ if (error instanceof AuthError) throw error;
730
784
  const message = error instanceof Error ? error.message : String(error);
731
785
  console.warn(`[rAgent] Token refresh error: ${message}`);
732
786
  return params.agentToken;
@@ -753,6 +807,9 @@ async function claimHost(params) {
753
807
  if (!data.agentToken) {
754
808
  throw new Error("Missing connector token in claim response");
755
809
  }
810
+ if (data.machineSecret) {
811
+ saveConfigPatch({ machineSecret: data.machineSecret });
812
+ }
756
813
  return data;
757
814
  }
758
815
  async function negotiateAgent(params) {
@@ -1516,8 +1573,18 @@ var SessionStreamer = class {
1516
1573
  if (!pid) return false;
1517
1574
  if (!(0, import_node_fs.existsSync)(`/proc/${pid}`)) {
1518
1575
  console.warn(`[rAgent] Process ${pid} does not exist (no /proc/${pid}).`);
1576
+ this.sendFn(sessionId, "\r\n[rAgent] Process is no longer running. It will be removed on next sync.\r\n");
1519
1577
  return false;
1520
1578
  }
1579
+ try {
1580
+ const stat = (0, import_node_fs.readFileSync)(`/proc/${pid}/stat`, "utf8");
1581
+ if (stat.includes(") Z")) {
1582
+ console.warn(`[rAgent] Process ${pid} is a zombie.`);
1583
+ this.sendFn(sessionId, "\r\n[rAgent] Process has exited (zombie). It will be removed on next sync.\r\n");
1584
+ return false;
1585
+ }
1586
+ } catch {
1587
+ }
1521
1588
  try {
1522
1589
  const straceProc = (0, import_node_child_process2.spawn)(
1523
1590
  "strace",
@@ -1575,7 +1642,11 @@ var SessionStreamer = class {
1575
1642
  const remaining = stream.utf8Decoder.end();
1576
1643
  if (remaining) this.sendFn(sessionId, remaining);
1577
1644
  if (code === 1) {
1578
- this.sendFn(sessionId, "\r\n[rAgent] Permission denied: cannot attach to process.\r\nTo enable process tracing, run:\r\n sudo sysctl kernel.yama.ptrace_scope=0\r\n");
1645
+ if (pid && !(0, import_node_fs.existsSync)(`/proc/${pid}`)) {
1646
+ this.sendFn(sessionId, "\r\n[rAgent] Process has exited. It will be removed on next sync.\r\n");
1647
+ } else {
1648
+ this.sendFn(sessionId, "\r\n[rAgent] Permission denied: cannot attach to process.\r\nTo enable process tracing, run:\r\n sudo sysctl kernel.yama.ptrace_scope=0\r\n");
1649
+ }
1579
1650
  }
1580
1651
  stream.stopped = true;
1581
1652
  this.active.delete(sessionId);
@@ -2666,8 +2737,13 @@ var ControlDispatcher = class {
2666
2737
  process.kill(pid, "SIGTERM");
2667
2738
  console.log(`[rAgent] Sent SIGTERM to process ${pid} (${sessionId}).`);
2668
2739
  } catch (error) {
2669
- const message = error instanceof Error ? error.message : String(error);
2670
- console.warn(`[rAgent] Failed to kill process ${pid}: ${message}`);
2740
+ const code = error.code;
2741
+ if (code === "ESRCH") {
2742
+ console.log(`[rAgent] Process ${pid} already exited, cleaning up.`);
2743
+ } else {
2744
+ const message = error instanceof Error ? error.message : String(error);
2745
+ console.warn(`[rAgent] Failed to kill process ${pid}: ${message}`);
2746
+ }
2671
2747
  }
2672
2748
  await this.syncInventory();
2673
2749
  }
@@ -3667,6 +3743,27 @@ async function runAgent(rawOptions) {
3667
3743
  });
3668
3744
  } catch (error) {
3669
3745
  if (error instanceof AuthError) {
3746
+ const cfg = loadConfig();
3747
+ if (cfg.machineSecret && cfg.hostId) {
3748
+ try {
3749
+ options.agentToken = await startSessionWithMachineSecret({
3750
+ portal: options.portal,
3751
+ hostId: cfg.hostId,
3752
+ machineSecret: cfg.machineSecret
3753
+ });
3754
+ inventory.updateOptions(options);
3755
+ dispatcher.updateOptions(options);
3756
+ console.log("[rAgent] Recovered from auth failure via machine credential. Reconnecting...");
3757
+ continue;
3758
+ } catch (mcError) {
3759
+ if (mcError instanceof AuthError) {
3760
+ console.error(`[rAgent] ${mcError.message}`);
3761
+ } else {
3762
+ const mcMsg = mcError instanceof Error ? mcError.message : String(mcError);
3763
+ console.error(`[rAgent] Machine credential recovery failed: ${mcMsg}`);
3764
+ }
3765
+ }
3766
+ }
3670
3767
  console.error(`[rAgent] ${error.message}`);
3671
3768
  console.error(
3672
3769
  "[rAgent] Connector token is invalid or revoked. Stopping. Re-connect with: ragent connect --token <token>"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragent-cli",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
4
4
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {