talon-agent 1.3.0 → 1.4.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.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * GitHub plugin — GitHub API access via the official GitHub MCP server.
3
+ *
4
+ * Registers the GitHub MCP server (Docker image: ghcr.io/github/github-mcp-server),
5
+ * giving the agent access to repository management, issues, PRs, code search, etc.
6
+ *
7
+ * Configuration in ~/.talon/config.json:
8
+ * "github": {
9
+ * "enabled": true,
10
+ * "token": "ghp_..." // optional, defaults to `gh auth token` output
11
+ * }
12
+ */
13
+
14
+ import { execFileSync } from "node:child_process";
15
+ import type { TalonPlugin } from "../../core/plugin.js";
16
+ import { log, logWarn } from "../../util/log.js";
17
+
18
+ /**
19
+ * Resolve a GitHub personal access token.
20
+ * Priority: explicit config > `gh auth token` CLI.
21
+ */
22
+ function resolveToken(configToken?: string): string | undefined {
23
+ const trimmed = configToken?.trim();
24
+ if (trimmed) return trimmed;
25
+ try {
26
+ return execFileSync("gh", ["auth", "token"], {
27
+ timeout: 5_000,
28
+ stdio: ["pipe", "pipe", "pipe"],
29
+ })
30
+ .toString("utf-8")
31
+ .trim();
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ }
36
+
37
+ export function createGitHubPlugin(config: { token?: string }): TalonPlugin {
38
+ const token = resolveToken(config.token);
39
+
40
+ return {
41
+ name: "github",
42
+ description: "GitHub API access via the official GitHub MCP server",
43
+ version: "1.0.0",
44
+
45
+ mcpServer: {
46
+ command: "docker",
47
+ args: [
48
+ "run",
49
+ "--rm",
50
+ "-i",
51
+ "-e",
52
+ "GITHUB_PERSONAL_ACCESS_TOKEN",
53
+ "ghcr.io/github/github-mcp-server",
54
+ ],
55
+ },
56
+
57
+ validateConfig() {
58
+ const errors: string[] = [];
59
+
60
+ if (!token) {
61
+ errors.push(
62
+ 'No GitHub token found. Set "token" in github config or run `gh auth login`.',
63
+ );
64
+ }
65
+
66
+ // Check Docker is available
67
+ try {
68
+ execFileSync("docker", ["info"], {
69
+ timeout: 10_000,
70
+ stdio: "pipe",
71
+ });
72
+ } catch {
73
+ errors.push(
74
+ "Docker is not available or not running. The GitHub MCP server requires Docker.",
75
+ );
76
+ }
77
+
78
+ return errors.length > 0 ? errors : undefined;
79
+ },
80
+
81
+ async init() {
82
+ // Verify the Docker image exists locally
83
+ try {
84
+ execFileSync(
85
+ "docker",
86
+ ["image", "inspect", "ghcr.io/github/github-mcp-server"],
87
+ { timeout: 10_000, stdio: "pipe" },
88
+ );
89
+ log("github", "Docker image verified");
90
+ } catch {
91
+ logWarn(
92
+ "github",
93
+ "Docker image not found locally — will pull on first use (may be slow)",
94
+ );
95
+ }
96
+
97
+ log("github", "Ready");
98
+ },
99
+
100
+ getEnvVars() {
101
+ return {
102
+ ...(token ? { GITHUB_PERSONAL_ACCESS_TOKEN: token } : {}),
103
+ };
104
+ },
105
+ };
106
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Playwright plugin — browser automation via the official Playwright MCP server.
3
+ *
4
+ * Gives the agent headless Chromium for web scraping, screenshots, PDF generation,
5
+ * and general browser automation.
6
+ *
7
+ * Configuration in ~/.talon/config.json:
8
+ * "playwright": {
9
+ * "enabled": true,
10
+ * "browser": "chromium", // optional, default "chromium"
11
+ * "headless": true // optional, default true
12
+ * }
13
+ */
14
+
15
+ import { existsSync } from "node:fs";
16
+ import { resolve } from "node:path";
17
+ import type { TalonPlugin } from "../../core/plugin.js";
18
+ import { log } from "../../util/log.js";
19
+
20
+ export function createPlaywrightPlugin(config: {
21
+ browser?: string;
22
+ headless?: boolean;
23
+ }): TalonPlugin {
24
+ const browser = config.browser ?? "chromium";
25
+ const headless = config.headless !== false; // default true
26
+
27
+ // Resolve path from Talon's node_modules
28
+ const mcpBin = resolve(
29
+ import.meta.dirname ?? ".",
30
+ "../../../node_modules/@playwright/mcp/cli.js",
31
+ );
32
+
33
+ const args = ["--no-sandbox"];
34
+
35
+ if (headless) {
36
+ args.push("--headless");
37
+ }
38
+
39
+ if (browser !== "chromium") {
40
+ args.push("--browser", browser);
41
+ }
42
+
43
+ return {
44
+ name: "playwright",
45
+ description: "Browser automation via Playwright MCP (headless Chromium)",
46
+ version: "1.0.0",
47
+
48
+ mcpServer: {
49
+ command: "node",
50
+ args: [mcpBin, ...args],
51
+ },
52
+
53
+ validateConfig() {
54
+ const errors: string[] = [];
55
+
56
+ const validBrowsers = [
57
+ "chromium",
58
+ "chrome",
59
+ "firefox",
60
+ "webkit",
61
+ "msedge",
62
+ ];
63
+ if (!validBrowsers.includes(browser)) {
64
+ errors.push(
65
+ `Invalid browser "${browser}". Valid options: ${validBrowsers.join(", ")}`,
66
+ );
67
+ }
68
+
69
+ if (!existsSync(mcpBin)) {
70
+ errors.push(
71
+ `@playwright/mcp not found at ${mcpBin} — run "npm install @playwright/mcp"`,
72
+ );
73
+ }
74
+
75
+ return errors.length > 0 ? errors : undefined;
76
+ },
77
+
78
+ async init() {
79
+ log("playwright", `Ready (${browser}, headless=${headless})`);
80
+ },
81
+ };
82
+ }
@@ -82,16 +82,6 @@ export function loadSessions(): void {
82
82
  );
83
83
  store = {};
84
84
  }
85
- // SDK sessions don't survive process restarts — the embedded Claude Code
86
- // subprocess is gone. Clear stale session IDs so we don't try to resume
87
- // a dead session (which causes the SDK to hang silently on Windows).
88
- // Keep turns/usage intact — they're historical data used by /resume.
89
- for (const session of Object.values(store)) {
90
- if (session.sessionId) {
91
- session.sessionId = undefined;
92
- dirty = true;
93
- }
94
- }
95
85
  }
96
86
 
97
87
  function saveSessions(): void {
@@ -40,10 +40,18 @@ const configSchema = z.object({
40
40
  heartbeatIntervalMinutes: z.number().int().min(5).default(60),
41
41
  heartbeatModel: z.string().optional(), // Model for heartbeat agent (defaults to main model)
42
42
  braveApiKey: z.string().optional(),
43
- searxngUrl: z.string().default("http://localhost:8080"),
44
43
  timezone: z.string().optional(),
45
44
  plugins: z.array(pluginEntrySchema).default([]),
46
45
 
46
+ // GitHub — GitHub API access via official MCP server
47
+ github: z
48
+ .object({
49
+ enabled: z.boolean().default(false),
50
+ /** GitHub personal access token (default: from `gh auth token`) */
51
+ token: z.string().min(1).optional(),
52
+ })
53
+ .optional(),
54
+
47
55
  // MemPalace — structured long-term memory with vector search
48
56
  mempalace: z
49
57
  .object({
@@ -55,6 +63,17 @@ const configSchema = z.object({
55
63
  })
56
64
  .optional(),
57
65
 
66
+ // Playwright — headless browser automation via MCP
67
+ playwright: z
68
+ .object({
69
+ enabled: z.boolean().default(false),
70
+ /** Browser engine: chromium (default), chrome, firefox, webkit, msedge */
71
+ browser: z.string().optional(),
72
+ /** Run headless (default: true) */
73
+ headless: z.boolean().default(true),
74
+ })
75
+ .optional(),
76
+
58
77
  // Display name shown in terminal UI (defaults to "Talon")
59
78
  botDisplayName: z.string().default("Talon"),
60
79
 
package/src/util/log.ts CHANGED
@@ -42,7 +42,9 @@ export type LogComponent =
42
42
  | "teams"
43
43
  | "config"
44
44
  | "access"
45
- | "mempalace";
45
+ | "github"
46
+ | "mempalace"
47
+ | "playwright";
46
48
 
47
49
  const LOG_FILE = files.log;
48
50