ragent-cli 1.6.1 → 1.7.0
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/index.js +121 -19
- 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.
|
|
34
|
+
version: "1.7.0",
|
|
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: {
|
|
@@ -262,7 +262,7 @@ var os9 = __toESM(require("os"));
|
|
|
262
262
|
// src/agent.ts
|
|
263
263
|
var fs4 = __toESM(require("fs"));
|
|
264
264
|
var os8 = __toESM(require("os"));
|
|
265
|
-
var
|
|
265
|
+
var path4 = __toESM(require("path"));
|
|
266
266
|
var import_ws5 = __toESM(require("ws"));
|
|
267
267
|
|
|
268
268
|
// src/auth.ts
|
|
@@ -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
|
|
747
|
+
const errorData = await response.json().catch(() => ({}));
|
|
711
748
|
console.warn(
|
|
712
|
-
`[rAgent] Token refresh failed: ${
|
|
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) {
|
|
@@ -2030,6 +2087,7 @@ var import_ws4 = __toESM(require("ws"));
|
|
|
2030
2087
|
var import_child_process2 = require("child_process");
|
|
2031
2088
|
var fs2 = __toESM(require("fs"));
|
|
2032
2089
|
var os6 = __toESM(require("os"));
|
|
2090
|
+
var path2 = __toESM(require("path"));
|
|
2033
2091
|
function assertConfiguredAgentToken() {
|
|
2034
2092
|
const config = loadConfig();
|
|
2035
2093
|
if (!config.agentToken) {
|
|
@@ -2216,8 +2274,32 @@ async function stopService() {
|
|
|
2216
2274
|
}
|
|
2217
2275
|
await stopPidfileService();
|
|
2218
2276
|
}
|
|
2277
|
+
function killStaleAgentProcesses() {
|
|
2278
|
+
try {
|
|
2279
|
+
const entries = fs2.readdirSync(CONFIG_DIR);
|
|
2280
|
+
for (const entry of entries) {
|
|
2281
|
+
if (!entry.startsWith("agent-") || !entry.endsWith(".pid")) continue;
|
|
2282
|
+
const pidPath = path2.join(CONFIG_DIR, entry);
|
|
2283
|
+
try {
|
|
2284
|
+
const raw = fs2.readFileSync(pidPath, "utf8").trim();
|
|
2285
|
+
const pid = Number.parseInt(raw, 10);
|
|
2286
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
2287
|
+
try {
|
|
2288
|
+
process.kill(pid, 0);
|
|
2289
|
+
process.kill(pid, "SIGTERM");
|
|
2290
|
+
console.log(`[rAgent] Stopped stale agent process (pid ${pid})`);
|
|
2291
|
+
} catch {
|
|
2292
|
+
}
|
|
2293
|
+
fs2.unlinkSync(pidPath);
|
|
2294
|
+
} catch {
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2219
2300
|
async function restartService() {
|
|
2220
2301
|
const backend = getConfiguredServiceBackend();
|
|
2302
|
+
killStaleAgentProcesses();
|
|
2221
2303
|
if (backend === "systemd") {
|
|
2222
2304
|
await runSystemctlUser(["restart", SERVICE_NAME]);
|
|
2223
2305
|
console.log("[rAgent] Restarted service via systemd.");
|
|
@@ -2231,13 +2313,12 @@ async function printServiceStatus() {
|
|
|
2231
2313
|
if (backend === "systemd") {
|
|
2232
2314
|
const status = await execAsync(
|
|
2233
2315
|
`systemctl --user status ${shellQuote(SERVICE_NAME)} --no-pager --lines=20`,
|
|
2234
|
-
{ timeout: 1e4 }
|
|
2235
|
-
)
|
|
2236
|
-
console.log(error.message);
|
|
2237
|
-
return "";
|
|
2238
|
-
});
|
|
2316
|
+
{ timeout: 1e4, allowNonZeroExit: true }
|
|
2317
|
+
);
|
|
2239
2318
|
if (status) {
|
|
2240
2319
|
process.stdout.write(status);
|
|
2320
|
+
} else {
|
|
2321
|
+
console.log("[rAgent] systemd service is not installed or has no status.");
|
|
2241
2322
|
}
|
|
2242
2323
|
return;
|
|
2243
2324
|
}
|
|
@@ -2842,7 +2923,7 @@ var ControlDispatcher = class {
|
|
|
2842
2923
|
|
|
2843
2924
|
// src/transcript-watcher.ts
|
|
2844
2925
|
var fs3 = __toESM(require("fs"));
|
|
2845
|
-
var
|
|
2926
|
+
var path3 = __toESM(require("path"));
|
|
2846
2927
|
var os7 = __toESM(require("os"));
|
|
2847
2928
|
var import_child_process5 = require("child_process");
|
|
2848
2929
|
var ClaudeCodeParser = class {
|
|
@@ -2978,7 +3059,7 @@ function discoverViaProc(panePid) {
|
|
|
2978
3059
|
const fds = fs3.readdirSync(fdDir);
|
|
2979
3060
|
for (const fd of fds) {
|
|
2980
3061
|
try {
|
|
2981
|
-
const target = fs3.readlinkSync(
|
|
3062
|
+
const target = fs3.readlinkSync(path3.join(fdDir, fd));
|
|
2982
3063
|
if (target.endsWith(".jsonl") && target.includes("/.claude/")) {
|
|
2983
3064
|
return target;
|
|
2984
3065
|
}
|
|
@@ -2998,7 +3079,7 @@ function discoverViaProc(panePid) {
|
|
|
2998
3079
|
const fds = fs3.readdirSync(gcFdDir);
|
|
2999
3080
|
for (const fd of fds) {
|
|
3000
3081
|
try {
|
|
3001
|
-
const target = fs3.readlinkSync(
|
|
3082
|
+
const target = fs3.readlinkSync(path3.join(gcFdDir, fd));
|
|
3002
3083
|
if (target.endsWith(".jsonl") && target.includes("/.claude/")) {
|
|
3003
3084
|
return target;
|
|
3004
3085
|
}
|
|
@@ -3016,15 +3097,15 @@ function discoverViaProc(panePid) {
|
|
|
3016
3097
|
return null;
|
|
3017
3098
|
}
|
|
3018
3099
|
function discoverViaCwd(paneCwd) {
|
|
3019
|
-
const claudeProjectsDir =
|
|
3100
|
+
const claudeProjectsDir = path3.join(os7.homedir(), ".claude", "projects");
|
|
3020
3101
|
if (!fs3.existsSync(claudeProjectsDir)) return null;
|
|
3021
3102
|
const resolvedCwd = fs3.realpathSync(paneCwd);
|
|
3022
3103
|
const expectedDirName = resolvedCwd.replace(/\//g, "-");
|
|
3023
|
-
const projectDir =
|
|
3104
|
+
const projectDir = path3.join(claudeProjectsDir, expectedDirName);
|
|
3024
3105
|
if (!fs3.existsSync(projectDir)) return null;
|
|
3025
3106
|
try {
|
|
3026
3107
|
const jsonlFiles = fs3.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
3027
|
-
const fullPath =
|
|
3108
|
+
const fullPath = path3.join(projectDir, f);
|
|
3028
3109
|
const stat = fs3.statSync(fullPath);
|
|
3029
3110
|
return { path: fullPath, mtime: stat.mtimeMs };
|
|
3030
3111
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -3034,14 +3115,14 @@ function discoverViaCwd(paneCwd) {
|
|
|
3034
3115
|
}
|
|
3035
3116
|
}
|
|
3036
3117
|
function discoverCodexTranscript() {
|
|
3037
|
-
const codexSessionsDir =
|
|
3118
|
+
const codexSessionsDir = path3.join(os7.homedir(), ".codex", "sessions");
|
|
3038
3119
|
if (!fs3.existsSync(codexSessionsDir)) return null;
|
|
3039
3120
|
try {
|
|
3040
3121
|
const dateDirs = fs3.readdirSync(codexSessionsDir).filter((d) => /^\d{4}-\d{2}-\d{2}/.test(d)).sort().reverse();
|
|
3041
3122
|
for (const dateDir of dateDirs.slice(0, 3)) {
|
|
3042
|
-
const fullDir =
|
|
3123
|
+
const fullDir = path3.join(codexSessionsDir, dateDir);
|
|
3043
3124
|
const jsonlFiles = fs3.readdirSync(fullDir).filter((f) => f.startsWith("rollout-") && f.endsWith(".jsonl")).map((f) => {
|
|
3044
|
-
const fp =
|
|
3125
|
+
const fp = path3.join(fullDir, f);
|
|
3045
3126
|
const stat = fs3.statSync(fp);
|
|
3046
3127
|
return { path: fp, mtime: stat.mtimeMs };
|
|
3047
3128
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -3377,7 +3458,7 @@ var TranscriptWatcherManager = class {
|
|
|
3377
3458
|
|
|
3378
3459
|
// src/agent.ts
|
|
3379
3460
|
function pidFilePath(hostId) {
|
|
3380
|
-
return
|
|
3461
|
+
return path4.join(CONFIG_DIR, `agent-${hostId}.pid`);
|
|
3381
3462
|
}
|
|
3382
3463
|
function readPidFile(filePath) {
|
|
3383
3464
|
try {
|
|
@@ -3643,6 +3724,27 @@ async function runAgent(rawOptions) {
|
|
|
3643
3724
|
});
|
|
3644
3725
|
} catch (error) {
|
|
3645
3726
|
if (error instanceof AuthError) {
|
|
3727
|
+
const cfg = loadConfig();
|
|
3728
|
+
if (cfg.machineSecret && cfg.hostId) {
|
|
3729
|
+
try {
|
|
3730
|
+
options.agentToken = await startSessionWithMachineSecret({
|
|
3731
|
+
portal: options.portal,
|
|
3732
|
+
hostId: cfg.hostId,
|
|
3733
|
+
machineSecret: cfg.machineSecret
|
|
3734
|
+
});
|
|
3735
|
+
inventory.updateOptions(options);
|
|
3736
|
+
dispatcher.updateOptions(options);
|
|
3737
|
+
console.log("[rAgent] Recovered from auth failure via machine credential. Reconnecting...");
|
|
3738
|
+
continue;
|
|
3739
|
+
} catch (mcError) {
|
|
3740
|
+
if (mcError instanceof AuthError) {
|
|
3741
|
+
console.error(`[rAgent] ${mcError.message}`);
|
|
3742
|
+
} else {
|
|
3743
|
+
const mcMsg = mcError instanceof Error ? mcError.message : String(mcError);
|
|
3744
|
+
console.error(`[rAgent] Machine credential recovery failed: ${mcMsg}`);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3646
3748
|
console.error(`[rAgent] ${error.message}`);
|
|
3647
3749
|
console.error(
|
|
3648
3750
|
"[rAgent] Connector token is invalid or revoked. Stopping. Re-connect with: ragent connect --token <token>"
|