@saeed42/worktree-worker 1.3.5 → 1.4.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/main.js +183 -2
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/main.ts
4
4
  import { serve } from "@hono/node-server";
5
- import { Hono as Hono4 } from "hono";
5
+ import { Hono as Hono5 } from "hono";
6
6
  import { cors } from "hono/cors";
7
7
  import { logger as honoLogger } from "hono/logger";
8
8
  import { secureHeaders } from "hono/secure-headers";
@@ -466,6 +466,48 @@ var GitService = class {
466
466
  const result = await this.exec(["remote", "get-url", remote], cwd);
467
467
  return result.code === 0 ? result.stdout.trim() : null;
468
468
  }
469
+ /**
470
+ * Configure git credential helper with token (temporary, for operations like worktree add)
471
+ * SECURITY: Token is stored in /tmp and should be cleared after operation
472
+ */
473
+ async configureCredentials(githubToken) {
474
+ const log = logger.child({ service: "git", operation: "configureCredentials" });
475
+ log.debug("Configuring git credentials");
476
+ await this.exec(["config", "--global", "credential.helper", "store --file=/tmp/.git-credentials"]);
477
+ await this.exec(["config", "--global", "core.askPass", ""]);
478
+ const { writeFile: writeFile2 } = await import("fs/promises");
479
+ const credLine = `https://x-access-token:${githubToken}@github.com
480
+ `;
481
+ await writeFile2("/tmp/.git-credentials", credLine, { mode: 384 });
482
+ log.debug("Git credentials configured");
483
+ }
484
+ /**
485
+ * Clear git credentials (call after operations complete)
486
+ */
487
+ async clearCredentials() {
488
+ const log = logger.child({ service: "git", operation: "clearCredentials" });
489
+ log.debug("Clearing git credentials");
490
+ const { unlink: unlink2 } = await import("fs/promises");
491
+ try {
492
+ await unlink2("/tmp/.git-credentials");
493
+ } catch {
494
+ }
495
+ await this.exec(["config", "--global", "--unset", "credential.helper"]).catch(() => {
496
+ });
497
+ log.debug("Git credentials cleared");
498
+ }
499
+ /**
500
+ * Run an operation with temporary credentials
501
+ * SECURITY: Credentials are cleared after operation completes (success or failure)
502
+ */
503
+ async withCredentials(githubToken, operation) {
504
+ await this.configureCredentials(githubToken);
505
+ try {
506
+ return await operation();
507
+ } finally {
508
+ await this.clearCredentials();
509
+ }
510
+ }
469
511
  };
470
512
  var gitService = new GitService();
471
513
 
@@ -1472,8 +1514,146 @@ worktree.post("/worktrees/cleanup", async (c) => {
1472
1514
  }
1473
1515
  });
1474
1516
 
1517
+ // src/routes/git.routes.ts
1518
+ import { Hono as Hono4 } from "hono";
1519
+ import { z as z3 } from "zod";
1520
+ import * as fs from "fs/promises";
1521
+ var git = new Hono4();
1522
+ var gitConfigSchema = z3.object({
1523
+ user: z3.object({
1524
+ name: z3.string().min(1, "Name is required"),
1525
+ email: z3.string().email("Valid email is required")
1526
+ }).optional(),
1527
+ credentials: z3.object({
1528
+ githubToken: z3.string().min(1, "GitHub token is required")
1529
+ }).optional()
1530
+ });
1531
+ git.post("/config", async (c) => {
1532
+ const log = logger.child({ route: "git/config" });
1533
+ try {
1534
+ const body = await c.req.json().catch(() => ({}));
1535
+ const parsed = gitConfigSchema.safeParse(body);
1536
+ if (!parsed.success) {
1537
+ return c.json(
1538
+ { success: false, error: { code: "VALIDATION_ERROR", message: parsed.error.message } },
1539
+ 400
1540
+ );
1541
+ }
1542
+ const { user, credentials } = parsed.data;
1543
+ const configured = [];
1544
+ if (user) {
1545
+ log.info("Configuring git user identity", { name: user.name, email: user.email });
1546
+ const nameResult = await gitService.exec(["config", "--global", "user.name", user.name]);
1547
+ if (nameResult.code !== 0) {
1548
+ log.error("Failed to set git user.name", { stderr: nameResult.stderr });
1549
+ return c.json(
1550
+ { success: false, error: { code: "GIT_CONFIG_ERROR", message: "Failed to set user.name" } },
1551
+ 500
1552
+ );
1553
+ }
1554
+ const emailResult = await gitService.exec(["config", "--global", "user.email", user.email]);
1555
+ if (emailResult.code !== 0) {
1556
+ log.error("Failed to set git user.email", { stderr: emailResult.stderr });
1557
+ return c.json(
1558
+ { success: false, error: { code: "GIT_CONFIG_ERROR", message: "Failed to set user.email" } },
1559
+ 500
1560
+ );
1561
+ }
1562
+ configured.push("user_configured");
1563
+ log.debug("Git user identity configured successfully");
1564
+ }
1565
+ if (credentials?.githubToken) {
1566
+ log.info("Configuring git credentials");
1567
+ await gitService.exec([
1568
+ "config",
1569
+ "--global",
1570
+ "credential.helper",
1571
+ "store --file=/tmp/.git-credentials"
1572
+ ]);
1573
+ await gitService.exec(["config", "--global", "core.askPass", ""]);
1574
+ const credLine = `https://x-access-token:${credentials.githubToken}@github.com
1575
+ `;
1576
+ await fs.writeFile("/tmp/.git-credentials", credLine, { mode: 384 });
1577
+ configured.push("credentials_configured");
1578
+ log.debug("Git credentials configured successfully");
1579
+ }
1580
+ if (configured.length === 0) {
1581
+ return c.json(
1582
+ { success: false, error: { code: "NO_CONFIG", message: "No configuration provided" } },
1583
+ 400
1584
+ );
1585
+ }
1586
+ return c.json({
1587
+ success: true,
1588
+ data: { configured }
1589
+ });
1590
+ } catch (err) {
1591
+ const error = err;
1592
+ log.error("Failed to configure git", { error: error.message });
1593
+ return c.json(
1594
+ { success: false, error: { code: "INTERNAL_ERROR", message: error.message } },
1595
+ 500
1596
+ );
1597
+ }
1598
+ });
1599
+ git.get("/config", async (c) => {
1600
+ const log = logger.child({ route: "git/config" });
1601
+ try {
1602
+ const nameResult = await gitService.exec(["config", "--global", "user.name"]);
1603
+ const name = nameResult.code === 0 ? nameResult.stdout.trim() : null;
1604
+ const emailResult = await gitService.exec(["config", "--global", "user.email"]);
1605
+ const email = emailResult.code === 0 ? emailResult.stdout.trim() : null;
1606
+ let credentialsConfigured = false;
1607
+ try {
1608
+ await fs.access("/tmp/.git-credentials");
1609
+ credentialsConfigured = true;
1610
+ } catch {
1611
+ }
1612
+ return c.json({
1613
+ success: true,
1614
+ data: {
1615
+ user: {
1616
+ name: name || null,
1617
+ email: email || null
1618
+ },
1619
+ credentialsConfigured
1620
+ }
1621
+ });
1622
+ } catch (err) {
1623
+ const error = err;
1624
+ log.error("Failed to get git config", { error: error.message });
1625
+ return c.json(
1626
+ { success: false, error: { code: "INTERNAL_ERROR", message: error.message } },
1627
+ 500
1628
+ );
1629
+ }
1630
+ });
1631
+ git.delete("/config", async (c) => {
1632
+ const log = logger.child({ route: "git/config" });
1633
+ try {
1634
+ await gitService.exec(["config", "--global", "--unset", "user.name"]);
1635
+ await gitService.exec(["config", "--global", "--unset", "user.email"]);
1636
+ try {
1637
+ await fs.unlink("/tmp/.git-credentials");
1638
+ } catch {
1639
+ }
1640
+ log.info("Git config cleared");
1641
+ return c.json({
1642
+ success: true,
1643
+ data: { cleared: true }
1644
+ });
1645
+ } catch (err) {
1646
+ const error = err;
1647
+ log.error("Failed to clear git config", { error: error.message });
1648
+ return c.json(
1649
+ { success: false, error: { code: "INTERNAL_ERROR", message: error.message } },
1650
+ 500
1651
+ );
1652
+ }
1653
+ });
1654
+
1475
1655
  // src/main.ts
1476
- var app = new Hono4();
1656
+ var app = new Hono5();
1477
1657
  app.use("*", secureHeaders());
1478
1658
  app.use(
1479
1659
  "*",
@@ -1504,6 +1684,7 @@ app.get("/", (c) => {
1504
1684
  });
1505
1685
  app.use("/v1/*", authMiddleware);
1506
1686
  app.route("/v1/repo", repo);
1687
+ app.route("/v1/git", git);
1507
1688
  app.route("/v1", worktree);
1508
1689
  app.onError((err, c) => {
1509
1690
  const requestId = c.req.header("X-Request-ID") || "unknown";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saeed42/worktree-worker",
3
- "version": "1.3.5",
3
+ "version": "1.4.1",
4
4
  "description": "Git worktree management service for AI agent trials",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",