@groupchatai/claude-runner 0.4.12 → 0.4.13
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 +122 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { spawn, execFileSync } from "child_process";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
readFileSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
existsSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
mkdirSync,
|
|
13
|
+
unlinkSync
|
|
14
|
+
} from "fs";
|
|
6
15
|
import path from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
7
17
|
import { fileURLToPath } from "url";
|
|
8
18
|
var API_URL = "https://groupchat.ai";
|
|
9
19
|
var CONVEX_URL = "https://fantastic-jay-464.convex.cloud";
|
|
@@ -707,12 +717,22 @@ function createWorktree(repoDir, taskId, existingBranch) {
|
|
|
707
717
|
return worktreePath;
|
|
708
718
|
}
|
|
709
719
|
const baseBranch = getDefaultBranch(repoDir);
|
|
710
|
-
const branchName = `agent/${name}
|
|
720
|
+
const branchName = `agent/${name}`;
|
|
711
721
|
try {
|
|
712
722
|
execGit(["fetch", "origin", baseBranch, "--quiet"], repoDir);
|
|
713
723
|
} catch {
|
|
714
724
|
}
|
|
715
|
-
|
|
725
|
+
let branchExists = false;
|
|
726
|
+
try {
|
|
727
|
+
execGit(["rev-parse", "--verify", branchName], repoDir);
|
|
728
|
+
branchExists = true;
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
if (branchExists) {
|
|
732
|
+
execGit(["worktree", "add", worktreePath, branchName], repoDir);
|
|
733
|
+
} else {
|
|
734
|
+
execGit(["worktree", "add", "-b", branchName, worktreePath, `origin/${baseBranch}`], repoDir);
|
|
735
|
+
}
|
|
716
736
|
writeFileSync(path.join(worktreePath, ".agent-branch"), branchName, "utf-8");
|
|
717
737
|
return worktreePath;
|
|
718
738
|
}
|
|
@@ -823,11 +843,6 @@ async function removeWorktree(workDir, info) {
|
|
|
823
843
|
}
|
|
824
844
|
}
|
|
825
845
|
function removeWorktreeSimple(repoDir, worktreePath) {
|
|
826
|
-
let branchName;
|
|
827
|
-
try {
|
|
828
|
-
branchName = readFileSync(path.join(worktreePath, ".agent-branch"), "utf-8").trim();
|
|
829
|
-
} catch {
|
|
830
|
-
}
|
|
831
846
|
try {
|
|
832
847
|
execGit(["worktree", "remove", worktreePath, "--force"], repoDir);
|
|
833
848
|
} catch {
|
|
@@ -837,12 +852,6 @@ function removeWorktreeSimple(repoDir, worktreePath) {
|
|
|
837
852
|
} catch {
|
|
838
853
|
}
|
|
839
854
|
}
|
|
840
|
-
if (branchName) {
|
|
841
|
-
try {
|
|
842
|
-
execGit(["branch", "-D", branchName], repoDir);
|
|
843
|
-
} catch {
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
855
|
}
|
|
847
856
|
async function startupSweep(workDir) {
|
|
848
857
|
const worktrees = await listOurWorktrees(workDir);
|
|
@@ -1217,6 +1226,76 @@ ${message.slice(0, 2e3)}
|
|
|
1217
1226
|
log(`${C.red}\u274C Error: ${message}${C.reset}`);
|
|
1218
1227
|
}
|
|
1219
1228
|
}
|
|
1229
|
+
function globalConfigDir() {
|
|
1230
|
+
return path.join(homedir(), ".config", "groupchat");
|
|
1231
|
+
}
|
|
1232
|
+
function globalConfigPath() {
|
|
1233
|
+
return path.join(globalConfigDir(), "config");
|
|
1234
|
+
}
|
|
1235
|
+
function readGlobalConfig() {
|
|
1236
|
+
try {
|
|
1237
|
+
const contents = readFileSync(globalConfigPath(), "utf-8");
|
|
1238
|
+
const config = {};
|
|
1239
|
+
for (const line of contents.split("\n")) {
|
|
1240
|
+
const trimmed = line.trim();
|
|
1241
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1242
|
+
const eqIdx = trimmed.indexOf("=");
|
|
1243
|
+
if (eqIdx === -1) continue;
|
|
1244
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
1245
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
1246
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1247
|
+
value = value.slice(1, -1);
|
|
1248
|
+
}
|
|
1249
|
+
config[key] = value;
|
|
1250
|
+
}
|
|
1251
|
+
return config;
|
|
1252
|
+
} catch {
|
|
1253
|
+
return {};
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
function writeGlobalConfig(config) {
|
|
1257
|
+
const dir = globalConfigDir();
|
|
1258
|
+
mkdirSync(dir, { recursive: true });
|
|
1259
|
+
const lines = Object.entries(config).map(([k, v]) => `${k}=${v}`);
|
|
1260
|
+
writeFileSync(globalConfigPath(), lines.join("\n") + "\n", "utf-8");
|
|
1261
|
+
}
|
|
1262
|
+
function handleLogin(token) {
|
|
1263
|
+
if (!token) {
|
|
1264
|
+
console.error("Usage: npx @groupchatai/claude-runner login <gca_token>");
|
|
1265
|
+
process.exit(1);
|
|
1266
|
+
}
|
|
1267
|
+
if (!token.startsWith("gca_")) {
|
|
1268
|
+
console.error(`Error: Token must start with gca_ (got "${token.slice(0, 8)}\u2026")`);
|
|
1269
|
+
process.exit(1);
|
|
1270
|
+
}
|
|
1271
|
+
const config = readGlobalConfig();
|
|
1272
|
+
config.GCA_TOKEN = token;
|
|
1273
|
+
writeGlobalConfig(config);
|
|
1274
|
+
console.log(`\u2705 Token saved to ${globalConfigPath()}`);
|
|
1275
|
+
console.log(` You can now run the agent from any directory.`);
|
|
1276
|
+
}
|
|
1277
|
+
function handleLogout() {
|
|
1278
|
+
const configFile = globalConfigPath();
|
|
1279
|
+
if (!existsSync(configFile)) {
|
|
1280
|
+
console.log("No saved token found.");
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
const config = readGlobalConfig();
|
|
1284
|
+
if (!config.GCA_TOKEN) {
|
|
1285
|
+
console.log("No saved token found.");
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
delete config.GCA_TOKEN;
|
|
1289
|
+
if (Object.keys(config).length === 0) {
|
|
1290
|
+
try {
|
|
1291
|
+
unlinkSync(configFile);
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
} else {
|
|
1295
|
+
writeGlobalConfig(config);
|
|
1296
|
+
}
|
|
1297
|
+
console.log("\u2705 Token removed.");
|
|
1298
|
+
}
|
|
1220
1299
|
function loadEnvFile() {
|
|
1221
1300
|
const candidates = [".env.local", ".env"];
|
|
1222
1301
|
for (const file of candidates) {
|
|
@@ -1325,8 +1404,10 @@ function showHelp() {
|
|
|
1325
1404
|
Usage: npx @groupchatai/claude-runner [command] [options]
|
|
1326
1405
|
|
|
1327
1406
|
Commands:
|
|
1328
|
-
(default)
|
|
1329
|
-
|
|
1407
|
+
(default) Start the agent runner
|
|
1408
|
+
login <gca_token> Save your agent token for use from any directory
|
|
1409
|
+
logout Remove saved agent token
|
|
1410
|
+
cleanup Interactively review and remove stale worktrees
|
|
1330
1411
|
|
|
1331
1412
|
Options:
|
|
1332
1413
|
--work-dir <path> Repo directory for Claude Code to work in (default: cwd)
|
|
@@ -1342,6 +1423,12 @@ Options:
|
|
|
1342
1423
|
-h, --help Show this help message
|
|
1343
1424
|
-v, -version, --version Print version and exit
|
|
1344
1425
|
|
|
1426
|
+
Token resolution order:
|
|
1427
|
+
1. --token flag
|
|
1428
|
+
2. GCA_TOKEN env var
|
|
1429
|
+
3. .env.local / .env in current directory
|
|
1430
|
+
4. ~/.config/groupchat/config (saved via 'login' command)
|
|
1431
|
+
|
|
1345
1432
|
Environment variables:
|
|
1346
1433
|
GCA_TOKEN Agent token (gca_...)
|
|
1347
1434
|
GCA_API_URL API URL override
|
|
@@ -1351,9 +1438,20 @@ Environment variables:
|
|
|
1351
1438
|
}
|
|
1352
1439
|
function parseArgs() {
|
|
1353
1440
|
loadEnvFile();
|
|
1354
|
-
const
|
|
1441
|
+
const rawArgs = process.argv.slice(2);
|
|
1442
|
+
if (rawArgs[0] === "login") {
|
|
1443
|
+
handleLogin(rawArgs[1] ?? "");
|
|
1444
|
+
process.exit(0);
|
|
1445
|
+
}
|
|
1446
|
+
if (rawArgs[0] === "logout") {
|
|
1447
|
+
handleLogout();
|
|
1448
|
+
process.exit(0);
|
|
1449
|
+
}
|
|
1450
|
+
const globalConfig = readGlobalConfig();
|
|
1451
|
+
const resolvedToken = process.env.GCA_TOKEN || globalConfig.GCA_TOKEN || "";
|
|
1452
|
+
const args = rawArgs;
|
|
1355
1453
|
const config = {
|
|
1356
|
-
token:
|
|
1454
|
+
token: resolvedToken,
|
|
1357
1455
|
apiUrl: process.env.GCA_API_URL ?? API_URL,
|
|
1358
1456
|
convexUrl: process.env.GCA_CONVEX_URL ?? CONVEX_URL,
|
|
1359
1457
|
workDir: process.cwd(),
|
|
@@ -1419,7 +1517,12 @@ function parseArgs() {
|
|
|
1419
1517
|
if (config.command !== "cleanup") {
|
|
1420
1518
|
if (!config.token) {
|
|
1421
1519
|
console.error("Error: No agent token found.");
|
|
1422
|
-
console.error("
|
|
1520
|
+
console.error("");
|
|
1521
|
+
console.error(" To save your token globally (recommended):");
|
|
1522
|
+
console.error(" npx @groupchatai/claude-runner login gca_...");
|
|
1523
|
+
console.error("");
|
|
1524
|
+
console.error(" Or pass it directly:");
|
|
1525
|
+
console.error(" --token gca_... or GCA_TOKEN=gca_... in .env.local");
|
|
1423
1526
|
process.exit(1);
|
|
1424
1527
|
}
|
|
1425
1528
|
if (!config.token.startsWith("gca_")) {
|