patchwork-os 0.2.0-alpha.0 → 0.2.0-alpha.10
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/README.md +41 -46
- package/dist/bridge.js +23 -10
- package/dist/bridge.js.map +1 -1
- package/dist/claudeDriver.d.ts +3 -1
- package/dist/claudeDriver.js +48 -0
- package/dist/claudeDriver.js.map +1 -1
- package/dist/commands/dashboard.d.ts +47 -0
- package/dist/commands/dashboard.js +319 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +5 -2
- package/dist/config.js.map +1 -1
- package/dist/connectors/github.d.ts +94 -0
- package/dist/connectors/github.js +350 -0
- package/dist/connectors/github.js.map +1 -0
- package/dist/connectors/gmail.d.ts +40 -0
- package/dist/connectors/gmail.js +304 -0
- package/dist/connectors/gmail.js.map +1 -0
- package/dist/connectors/googleCalendar.d.ts +57 -0
- package/dist/connectors/googleCalendar.js +308 -0
- package/dist/connectors/googleCalendar.js.map +1 -0
- package/dist/connectors/linear.d.ts +117 -0
- package/dist/connectors/linear.js +248 -0
- package/dist/connectors/linear.js.map +1 -0
- package/dist/connectors/mcpClient.d.ts +56 -0
- package/dist/connectors/mcpClient.js +189 -0
- package/dist/connectors/mcpClient.js.map +1 -0
- package/dist/connectors/mcpOAuth.d.ts +83 -0
- package/dist/connectors/mcpOAuth.js +363 -0
- package/dist/connectors/mcpOAuth.js.map +1 -0
- package/dist/connectors/sentry.d.ts +43 -0
- package/dist/connectors/sentry.js +197 -0
- package/dist/connectors/sentry.js.map +1 -0
- package/dist/connectors/slack.d.ts +50 -0
- package/dist/connectors/slack.js +254 -0
- package/dist/connectors/slack.js.map +1 -0
- package/dist/drivers/claude/api.d.ts +11 -0
- package/dist/drivers/claude/api.js +54 -0
- package/dist/drivers/claude/api.js.map +1 -0
- package/dist/drivers/claude/envSanitizer.d.ts +7 -0
- package/dist/drivers/claude/envSanitizer.js +18 -0
- package/dist/drivers/claude/envSanitizer.js.map +1 -0
- package/dist/drivers/claude/streamParser.d.ts +38 -0
- package/dist/drivers/claude/streamParser.js +34 -0
- package/dist/drivers/claude/streamParser.js.map +1 -0
- package/dist/drivers/claude/subprocess.d.ts +19 -0
- package/dist/drivers/claude/subprocess.js +216 -0
- package/dist/drivers/claude/subprocess.js.map +1 -0
- package/dist/drivers/claude/subprocessSettings.d.ts +9 -0
- package/dist/drivers/claude/subprocessSettings.js +55 -0
- package/dist/drivers/claude/subprocessSettings.js.map +1 -0
- package/dist/drivers/gemini/index.d.ts +14 -0
- package/dist/drivers/gemini/index.js +176 -0
- package/dist/drivers/gemini/index.js.map +1 -0
- package/dist/drivers/grok/index.d.ts +11 -0
- package/dist/drivers/grok/index.js +22 -0
- package/dist/drivers/grok/index.js.map +1 -0
- package/dist/drivers/index.d.ts +18 -0
- package/dist/drivers/index.js +31 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/openai/index.d.ts +24 -0
- package/dist/drivers/openai/index.js +110 -0
- package/dist/drivers/openai/index.js.map +1 -0
- package/dist/drivers/types.d.ts +72 -0
- package/dist/drivers/types.js +30 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/index.js +116 -22
- package/dist/index.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +95 -0
- package/dist/recipes/yamlRunner.js +588 -0
- package/dist/recipes/yamlRunner.js.map +1 -0
- package/dist/recipesHttp.d.ts +13 -1
- package/dist/recipesHttp.js +9 -1
- package/dist/recipesHttp.js.map +1 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +490 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/addLinearComment.d.ts +55 -0
- package/dist/tools/addLinearComment.js +70 -0
- package/dist/tools/addLinearComment.js.map +1 -0
- package/dist/tools/createLinearIssue.d.ts +84 -0
- package/dist/tools/createLinearIssue.js +146 -0
- package/dist/tools/createLinearIssue.js.map +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +4 -1
- package/dist/tools/ctxGetTaskContext.js +45 -2
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/fetchCalendarEvents.d.ts +94 -0
- package/dist/tools/fetchCalendarEvents.js +97 -0
- package/dist/tools/fetchCalendarEvents.js.map +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +80 -0
- package/dist/tools/fetchGithubIssue.js +84 -0
- package/dist/tools/fetchGithubIssue.js.map +1 -0
- package/dist/tools/fetchGithubPR.d.ts +89 -0
- package/dist/tools/fetchGithubPR.js +96 -0
- package/dist/tools/fetchGithubPR.js.map +1 -0
- package/dist/tools/fetchLinearIssue.d.ts +112 -0
- package/dist/tools/fetchLinearIssue.js +129 -0
- package/dist/tools/fetchLinearIssue.js.map +1 -0
- package/dist/tools/fetchSentryIssue.d.ts +143 -0
- package/dist/tools/fetchSentryIssue.js +150 -0
- package/dist/tools/fetchSentryIssue.js.map +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +43 -0
- package/dist/tools/fetchSlackProfile.js +43 -0
- package/dist/tools/fetchSlackProfile.js.map +1 -0
- package/dist/tools/getConnectorStatus.d.ts +58 -0
- package/dist/tools/getConnectorStatus.js +56 -0
- package/dist/tools/getConnectorStatus.js.map +1 -0
- package/dist/tools/github/index.d.ts +1 -1
- package/dist/tools/github/index.js +1 -1
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/pr.d.ts +122 -0
- package/dist/tools/github/pr.js +152 -0
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/index.js +27 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/slackListChannels.d.ts +65 -0
- package/dist/tools/slackListChannels.js +70 -0
- package/dist/tools/slackListChannels.js.map +1 -0
- package/dist/tools/slackPostMessage.d.ts +57 -0
- package/dist/tools/slackPostMessage.js +72 -0
- package/dist/tools/slackPostMessage.js.map +1 -0
- package/dist/tools/updateLinearIssue.d.ts +89 -0
- package/dist/tools/updateLinearIssue.js +103 -0
- package/dist/tools/updateLinearIssue.js.map +1 -0
- package/package.json +1 -1
- package/scripts/start-all.sh +56 -19
- package/templates/recipes/ctx-loop-test.yaml +75 -0
- package/templates/recipes/gmail-health-check.yaml +19 -0
- package/templates/recipes/inbox-triage.yaml +15 -0
- package/templates/recipes/morning-brief.yaml +72 -0
- package/templates/recipes/sentry-to-linear.yaml +77 -0
- package/templates/scheduled-tasks/morning-brief/SKILL.md +37 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentry connector — routes through Sentry's official MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Endpoint: https://mcp.sentry.dev/mcp
|
|
5
|
+
* Auth: OAuth 2.1 w/ PKCE; dynamic client registration (RFC 7591).
|
|
6
|
+
*
|
|
7
|
+
* HTTP routes (wired in src/server.ts):
|
|
8
|
+
* GET /connections/sentry/authorize — returns { url } for popup
|
|
9
|
+
* GET /connections/sentry/callback — token exchange
|
|
10
|
+
* POST /connections/sentry/test — ping MCP server
|
|
11
|
+
* DELETE /connections/sentry — revoke + delete token
|
|
12
|
+
*
|
|
13
|
+
* MCP tool: fetchSentryIssue — fetches a Sentry issue/event and returns
|
|
14
|
+
* the stack trace string, ready to pass into enrichStackTrace.
|
|
15
|
+
*/
|
|
16
|
+
import { McpClient } from "./mcpClient.js";
|
|
17
|
+
import { completeAuthorize, getAccessToken, loadTokenFile, revoke, startAuthorize, vendorConfig, } from "./mcpOAuth.js";
|
|
18
|
+
const SENTRY_MCP_ENDPOINT = "https://mcp.sentry.dev/mcp";
|
|
19
|
+
// ── MCP client ───────────────────────────────────────────────────────────────
|
|
20
|
+
let _client = null;
|
|
21
|
+
function client() {
|
|
22
|
+
if (!_client) {
|
|
23
|
+
_client = new McpClient(SENTRY_MCP_ENDPOINT, () => getAccessToken("sentry"));
|
|
24
|
+
}
|
|
25
|
+
return _client;
|
|
26
|
+
}
|
|
27
|
+
// ── Back-compat ──────────────────────────────────────────────────────────────
|
|
28
|
+
export function loadTokens() {
|
|
29
|
+
const file = loadTokenFile("sentry");
|
|
30
|
+
if (!file)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
auth_token: file.access_token,
|
|
34
|
+
org: file.profile?.org,
|
|
35
|
+
connected_at: file.connected_at,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function getStatus() {
|
|
39
|
+
const file = loadTokenFile("sentry");
|
|
40
|
+
return {
|
|
41
|
+
id: "sentry",
|
|
42
|
+
status: file ? "connected" : "disconnected",
|
|
43
|
+
lastSync: file?.connected_at,
|
|
44
|
+
org: file?.profile?.org,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ── Issue fetch ──────────────────────────────────────────────────────────────
|
|
48
|
+
function extractIssueId(issueIdOrUrl) {
|
|
49
|
+
const urlMatch = issueIdOrUrl.match(/\/issues\/(\d+)/);
|
|
50
|
+
if (urlMatch)
|
|
51
|
+
return urlMatch[1];
|
|
52
|
+
const trimmed = issueIdOrUrl.trim();
|
|
53
|
+
if (/^\d+$/.test(trimmed))
|
|
54
|
+
return trimmed;
|
|
55
|
+
throw new Error(`Cannot parse Sentry issue ID from: ${issueIdOrUrl}`);
|
|
56
|
+
}
|
|
57
|
+
function extractOrgSlug(issueIdOrUrl) {
|
|
58
|
+
const m = issueIdOrUrl.match(/https?:\/\/([^.]+)\.sentry\.io/);
|
|
59
|
+
return m ? m[1] : null;
|
|
60
|
+
}
|
|
61
|
+
function buildSentryIssueUrl(issueId, orgSlug) {
|
|
62
|
+
return `https://${orgSlug}.sentry.io/issues/${issueId}/`;
|
|
63
|
+
}
|
|
64
|
+
export async function fetchIssueStackTrace(issueIdOrUrl, signal) {
|
|
65
|
+
if (!loadTokens())
|
|
66
|
+
throw new Error("Sentry not connected. GET /connections/sentry/authorize first.");
|
|
67
|
+
const issueId = extractIssueId(issueIdOrUrl);
|
|
68
|
+
const orgSlug = extractOrgSlug(issueIdOrUrl) ?? loadTokenFile("sentry")?.profile?.org;
|
|
69
|
+
if (!orgSlug)
|
|
70
|
+
throw new Error("Cannot determine Sentry org slug. Pass full sentry.io issue URL.");
|
|
71
|
+
const issueUrl = buildSentryIssueUrl(issueId, orgSlug);
|
|
72
|
+
const res = await client().callTool("get_sentry_resource", { url: issueUrl }, { signal });
|
|
73
|
+
// get_sentry_resource returns markdown text — extract title and stacktrace
|
|
74
|
+
const text = res.content?.[0]?.text ?? "";
|
|
75
|
+
const titleMatch = text.match(/\*\*Description\*\*:\s*(.+)/);
|
|
76
|
+
const title = titleMatch
|
|
77
|
+
? titleMatch[1].trim()
|
|
78
|
+
: `Sentry issue ${issueId}`;
|
|
79
|
+
// Extract stacktrace block
|
|
80
|
+
const stMatch = text.match(/```(?:\w+)?\n([\s\S]*?)\n```/);
|
|
81
|
+
if (!stMatch) {
|
|
82
|
+
throw new Error(`Sentry returned no stack trace for issue ${issueId}`);
|
|
83
|
+
}
|
|
84
|
+
const stackTrace = stMatch[1].trim();
|
|
85
|
+
return { stackTrace, title, issueId };
|
|
86
|
+
}
|
|
87
|
+
// ── HTTP handlers ────────────────────────────────────────────────────────────
|
|
88
|
+
export async function handleSentryAuthorize() {
|
|
89
|
+
try {
|
|
90
|
+
const { url } = await startAuthorize(vendorConfig("sentry"));
|
|
91
|
+
return { status: 302, body: "", redirect: url };
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
status: 400,
|
|
96
|
+
contentType: "application/json",
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
ok: false,
|
|
99
|
+
error: err instanceof Error ? err.message : String(err),
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function handleSentryCallback(code, state, error) {
|
|
105
|
+
if (error) {
|
|
106
|
+
return {
|
|
107
|
+
status: 400,
|
|
108
|
+
contentType: "text/html",
|
|
109
|
+
body: `<html><body><h2>Sentry connect failed</h2><pre>${error}</pre></body></html>`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (!code || !state) {
|
|
113
|
+
return {
|
|
114
|
+
status: 400,
|
|
115
|
+
contentType: "text/html",
|
|
116
|
+
body: `<html><body><h2>Sentry connect failed</h2><pre>missing code/state</pre></body></html>`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await completeAuthorize(vendorConfig("sentry"), code, state);
|
|
121
|
+
// Best-effort org capture
|
|
122
|
+
try {
|
|
123
|
+
const res = await client().callTool("find_organizations", {}, {
|
|
124
|
+
timeoutMs: 10_000,
|
|
125
|
+
});
|
|
126
|
+
const orgs = McpClient.extractJson(res);
|
|
127
|
+
const first = Array.isArray(orgs)
|
|
128
|
+
? orgs[0]
|
|
129
|
+
: (orgs.organizations ?? [])[0];
|
|
130
|
+
const org = first?.slug;
|
|
131
|
+
if (org) {
|
|
132
|
+
const file = loadTokenFile("sentry");
|
|
133
|
+
if (file) {
|
|
134
|
+
const { writeFileSync, mkdirSync } = await import("node:fs");
|
|
135
|
+
const { homedir } = await import("node:os");
|
|
136
|
+
const path = await import("node:path");
|
|
137
|
+
const p = path.join(homedir(), ".patchwork", "tokens", "sentry-mcp.json");
|
|
138
|
+
mkdirSync(path.dirname(p), { recursive: true, mode: 0o700 });
|
|
139
|
+
file.profile = { ...(file.profile ?? {}), org };
|
|
140
|
+
writeFileSync(p, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Profile fetch is best-effort
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
status: 200,
|
|
149
|
+
contentType: "text/html",
|
|
150
|
+
body: `<html><body><h2>Sentry connected</h2><script>window.close();</script></body></html>`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return {
|
|
155
|
+
status: 400,
|
|
156
|
+
contentType: "text/html",
|
|
157
|
+
body: `<html><body><h2>Sentry connect failed</h2><pre>${err instanceof Error ? err.message : String(err)}</pre></body></html>`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export async function handleSentryTest() {
|
|
162
|
+
if (!loadTokens()) {
|
|
163
|
+
return {
|
|
164
|
+
status: 400,
|
|
165
|
+
contentType: "application/json",
|
|
166
|
+
body: JSON.stringify({ ok: false, error: "Sentry not connected" }),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const ok = await client().ping({ timeoutMs: 10_000 });
|
|
171
|
+
return {
|
|
172
|
+
status: ok ? 200 : 400,
|
|
173
|
+
contentType: "application/json",
|
|
174
|
+
body: JSON.stringify({ ok, message: ok ? "connected" : "ping failed" }),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
return {
|
|
179
|
+
status: 400,
|
|
180
|
+
contentType: "application/json",
|
|
181
|
+
body: JSON.stringify({
|
|
182
|
+
ok: false,
|
|
183
|
+
error: err instanceof Error ? err.message : String(err),
|
|
184
|
+
}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export async function handleSentryDisconnect() {
|
|
189
|
+
await revoke("sentry");
|
|
190
|
+
_client = null;
|
|
191
|
+
return {
|
|
192
|
+
status: 200,
|
|
193
|
+
contentType: "application/json",
|
|
194
|
+
body: JSON.stringify({ ok: true }),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=sentry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/connectors/sentry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack connector — calls Slack Web API directly with bot token.
|
|
3
|
+
*
|
|
4
|
+
* OAuth 2.0 (not PKCE) — Slack uses its own flow with bot token in response.
|
|
5
|
+
* Tokens stored at ~/.patchwork/tokens/slack.json (mode 0600).
|
|
6
|
+
* Client credentials: PATCHWORK_SLACK_CLIENT_ID / PATCHWORK_SLACK_CLIENT_SECRET
|
|
7
|
+
*
|
|
8
|
+
* HTTP routes (wired in src/server.ts):
|
|
9
|
+
* GET /connections/slack/authorize — redirect to Slack consent
|
|
10
|
+
* GET /connections/slack/callback — exchange code for bot token
|
|
11
|
+
* POST /connections/slack/test — ping Slack API
|
|
12
|
+
* DELETE /connections/slack — delete stored token
|
|
13
|
+
*/
|
|
14
|
+
export interface SlackTokenFile {
|
|
15
|
+
access_token: string;
|
|
16
|
+
team_id: string;
|
|
17
|
+
team_name: string;
|
|
18
|
+
bot_user_id: string;
|
|
19
|
+
connected_at: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ConnectorHandlerResult {
|
|
22
|
+
status: number;
|
|
23
|
+
body: string;
|
|
24
|
+
contentType?: string;
|
|
25
|
+
redirect?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function loadTokens(): SlackTokenFile | null;
|
|
28
|
+
export declare function isConnected(): boolean;
|
|
29
|
+
export declare function postMessage(channel: string, text: string, threadTs?: string, signal?: AbortSignal): Promise<{
|
|
30
|
+
ts: string;
|
|
31
|
+
channel: string;
|
|
32
|
+
}>;
|
|
33
|
+
export interface SlackChannel {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
isMember: boolean;
|
|
37
|
+
isPrivate: boolean;
|
|
38
|
+
numMembers?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare function listChannels(limit?: number, signal?: AbortSignal): Promise<SlackChannel[]>;
|
|
41
|
+
export interface SlackProfile {
|
|
42
|
+
teamId: string;
|
|
43
|
+
teamName: string;
|
|
44
|
+
botUserId: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function getProfile(): SlackProfile | null;
|
|
47
|
+
export declare function handleSlackAuthorize(): ConnectorHandlerResult;
|
|
48
|
+
export declare function handleSlackCallback(code: string | null, state: string | null, error: string | null): Promise<ConnectorHandlerResult>;
|
|
49
|
+
export declare function handleSlackTest(): Promise<ConnectorHandlerResult>;
|
|
50
|
+
export declare function handleSlackDisconnect(): ConnectorHandlerResult;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack connector — calls Slack Web API directly with bot token.
|
|
3
|
+
*
|
|
4
|
+
* OAuth 2.0 (not PKCE) — Slack uses its own flow with bot token in response.
|
|
5
|
+
* Tokens stored at ~/.patchwork/tokens/slack.json (mode 0600).
|
|
6
|
+
* Client credentials: PATCHWORK_SLACK_CLIENT_ID / PATCHWORK_SLACK_CLIENT_SECRET
|
|
7
|
+
*
|
|
8
|
+
* HTTP routes (wired in src/server.ts):
|
|
9
|
+
* GET /connections/slack/authorize — redirect to Slack consent
|
|
10
|
+
* GET /connections/slack/callback — exchange code for bot token
|
|
11
|
+
* POST /connections/slack/test — ping Slack API
|
|
12
|
+
* DELETE /connections/slack — delete stored token
|
|
13
|
+
*/
|
|
14
|
+
import crypto from "node:crypto";
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
const SLACK_AUTH_URL = "https://slack.com/oauth/v2/authorize";
|
|
19
|
+
const SLACK_TOKEN_URL = "https://slack.com/api/oauth.v2.access";
|
|
20
|
+
const SCOPES = ["chat:write", "channels:read", "channels:history", "users:read"].join(",");
|
|
21
|
+
const TOKEN_PATH = path.join(homedir(), ".patchwork", "tokens", "slack.json");
|
|
22
|
+
const STATE_PATH = path.join(homedir(), ".patchwork", "tokens", "slack-state.json");
|
|
23
|
+
// ── Config ───────────────────────────────────────────────────────────────────
|
|
24
|
+
function clientId() {
|
|
25
|
+
return process.env.PATCHWORK_SLACK_CLIENT_ID ?? "";
|
|
26
|
+
}
|
|
27
|
+
function clientSecret() {
|
|
28
|
+
return process.env.PATCHWORK_SLACK_CLIENT_SECRET ?? "";
|
|
29
|
+
}
|
|
30
|
+
function redirectUri() {
|
|
31
|
+
const base = (process.env.PATCHWORK_BRIDGE_URL ??
|
|
32
|
+
`http://localhost:${process.env.PATCHWORK_BRIDGE_PORT ?? "3101"}`).replace(/\/$/, "");
|
|
33
|
+
return `${base}/connections/slack/callback`;
|
|
34
|
+
}
|
|
35
|
+
// ── Token storage ─────────────────────────────────────────────────────────────
|
|
36
|
+
export function loadTokens() {
|
|
37
|
+
if (!existsSync(TOKEN_PATH))
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(TOKEN_PATH, "utf-8"));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function saveTokens(file) {
|
|
47
|
+
mkdirSync(path.dirname(TOKEN_PATH), { recursive: true, mode: 0o700 });
|
|
48
|
+
writeFileSync(TOKEN_PATH, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
function deleteTokens() {
|
|
51
|
+
if (existsSync(TOKEN_PATH))
|
|
52
|
+
unlinkSync(TOKEN_PATH);
|
|
53
|
+
if (existsSync(STATE_PATH))
|
|
54
|
+
unlinkSync(STATE_PATH);
|
|
55
|
+
}
|
|
56
|
+
export function isConnected() {
|
|
57
|
+
return loadTokens() !== null;
|
|
58
|
+
}
|
|
59
|
+
// ── State (CSRF) ──────────────────────────────────────────────────────────────
|
|
60
|
+
function saveState(state) {
|
|
61
|
+
mkdirSync(path.dirname(STATE_PATH), { recursive: true, mode: 0o700 });
|
|
62
|
+
writeFileSync(STATE_PATH, JSON.stringify({ state, ts: Date.now() }), { mode: 0o600 });
|
|
63
|
+
}
|
|
64
|
+
function consumeState() {
|
|
65
|
+
if (!existsSync(STATE_PATH))
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
const { state, ts } = JSON.parse(readFileSync(STATE_PATH, "utf-8"));
|
|
69
|
+
unlinkSync(STATE_PATH);
|
|
70
|
+
if (Date.now() - ts > 10 * 60 * 1000)
|
|
71
|
+
return null; // 10-min TTL
|
|
72
|
+
return state;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── API helper ────────────────────────────────────────────────────────────────
|
|
79
|
+
async function slackGet(method, params, token, signal) {
|
|
80
|
+
const url = new URL(`https://slack.com/api/${method}`);
|
|
81
|
+
for (const [k, v] of Object.entries(params))
|
|
82
|
+
url.searchParams.set(k, v);
|
|
83
|
+
const res = await fetch(url.toString(), {
|
|
84
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
85
|
+
signal,
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok)
|
|
88
|
+
throw new Error(`Slack API ${method} HTTP ${res.status}`);
|
|
89
|
+
const json = (await res.json());
|
|
90
|
+
if (!json.ok)
|
|
91
|
+
throw new Error(`Slack API ${method} error: ${json.error ?? "unknown"}`);
|
|
92
|
+
return json;
|
|
93
|
+
}
|
|
94
|
+
async function slackPost(method, body, token, signal) {
|
|
95
|
+
const res = await fetch(`https://slack.com/api/${method}`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: {
|
|
98
|
+
Authorization: `Bearer ${token}`,
|
|
99
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
100
|
+
Accept: "application/json",
|
|
101
|
+
},
|
|
102
|
+
body: JSON.stringify(body),
|
|
103
|
+
signal,
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok)
|
|
106
|
+
throw new Error(`Slack API ${method} HTTP ${res.status}`);
|
|
107
|
+
const json = (await res.json());
|
|
108
|
+
if (!json.ok)
|
|
109
|
+
throw new Error(`Slack API ${method} error: ${json.error ?? "unknown"}`);
|
|
110
|
+
return json;
|
|
111
|
+
}
|
|
112
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
113
|
+
export async function postMessage(channel, text, threadTs, signal) {
|
|
114
|
+
const tokens = loadTokens();
|
|
115
|
+
if (!tokens)
|
|
116
|
+
throw new Error("Slack not connected. GET /connections/slack/authorize first.");
|
|
117
|
+
const body = { channel, text };
|
|
118
|
+
if (threadTs)
|
|
119
|
+
body.thread_ts = threadTs;
|
|
120
|
+
const res = await slackPost("chat.postMessage", body, tokens.access_token, signal);
|
|
121
|
+
return { ts: res.ts ?? "", channel: res.channel ?? channel };
|
|
122
|
+
}
|
|
123
|
+
export async function listChannels(limit = 100, signal) {
|
|
124
|
+
const tokens = loadTokens();
|
|
125
|
+
if (!tokens)
|
|
126
|
+
throw new Error("Slack not connected. GET /connections/slack/authorize first.");
|
|
127
|
+
const res = await slackGet("conversations.list", { types: "public_channel", exclude_archived: "true", limit: String(Math.min(limit, 200)) }, tokens.access_token, signal);
|
|
128
|
+
const channels = res.channels ?? [];
|
|
129
|
+
return channels.map((c) => ({
|
|
130
|
+
id: c.id ?? "",
|
|
131
|
+
name: c.name ?? "",
|
|
132
|
+
isMember: Boolean(c.is_member),
|
|
133
|
+
isPrivate: Boolean(c.is_private),
|
|
134
|
+
numMembers: typeof c.num_members === "number" ? c.num_members : undefined,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
export function getProfile() {
|
|
138
|
+
const tokens = loadTokens();
|
|
139
|
+
if (!tokens)
|
|
140
|
+
return null;
|
|
141
|
+
return { teamId: tokens.team_id, teamName: tokens.team_name, botUserId: tokens.bot_user_id };
|
|
142
|
+
}
|
|
143
|
+
// ── HTTP handlers ─────────────────────────────────────────────────────────────
|
|
144
|
+
export function handleSlackAuthorize() {
|
|
145
|
+
if (!clientId()) {
|
|
146
|
+
return {
|
|
147
|
+
status: 400,
|
|
148
|
+
contentType: "application/json",
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
ok: false,
|
|
151
|
+
error: "PATCHWORK_SLACK_CLIENT_ID not set — create a Slack app first",
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const state = crypto.randomBytes(16).toString("hex");
|
|
156
|
+
saveState(state);
|
|
157
|
+
const params = new URLSearchParams({
|
|
158
|
+
client_id: clientId(),
|
|
159
|
+
scope: SCOPES,
|
|
160
|
+
redirect_uri: redirectUri(),
|
|
161
|
+
state,
|
|
162
|
+
});
|
|
163
|
+
return { status: 302, body: "", redirect: `${SLACK_AUTH_URL}?${params}` };
|
|
164
|
+
}
|
|
165
|
+
export async function handleSlackCallback(code, state, error) {
|
|
166
|
+
if (error) {
|
|
167
|
+
return {
|
|
168
|
+
status: 400,
|
|
169
|
+
contentType: "text/html",
|
|
170
|
+
body: `<html><body><h2>Slack connect failed</h2><pre>${error}</pre></body></html>`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (!code || !state) {
|
|
174
|
+
return {
|
|
175
|
+
status: 400,
|
|
176
|
+
contentType: "text/html",
|
|
177
|
+
body: `<html><body><h2>Slack connect failed</h2><pre>missing code or state</pre></body></html>`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const savedState = consumeState();
|
|
181
|
+
if (!savedState || savedState !== state) {
|
|
182
|
+
return {
|
|
183
|
+
status: 400,
|
|
184
|
+
contentType: "text/html",
|
|
185
|
+
body: `<html><body><h2>Slack connect failed</h2><pre>invalid state</pre></body></html>`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const params = new URLSearchParams({
|
|
190
|
+
code,
|
|
191
|
+
client_id: clientId(),
|
|
192
|
+
client_secret: clientSecret(),
|
|
193
|
+
redirect_uri: redirectUri(),
|
|
194
|
+
});
|
|
195
|
+
const res = await fetch(SLACK_TOKEN_URL, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
198
|
+
body: params.toString(),
|
|
199
|
+
});
|
|
200
|
+
if (!res.ok)
|
|
201
|
+
throw new Error(`Token exchange HTTP ${res.status}`);
|
|
202
|
+
const json = (await res.json());
|
|
203
|
+
if (!json.ok)
|
|
204
|
+
throw new Error(`Token exchange error: ${json.error ?? "unknown"}`);
|
|
205
|
+
const botToken = json.access_token ?? "";
|
|
206
|
+
const team = json.team ?? {};
|
|
207
|
+
const botUser = json.bot_user_id ?? "";
|
|
208
|
+
saveTokens({
|
|
209
|
+
access_token: botToken,
|
|
210
|
+
team_id: team.id ?? "",
|
|
211
|
+
team_name: team.name ?? "",
|
|
212
|
+
bot_user_id: botUser,
|
|
213
|
+
connected_at: new Date().toISOString(),
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
status: 200,
|
|
217
|
+
contentType: "text/html",
|
|
218
|
+
body: `<html><body><h2>Slack connected to ${team.name ?? "workspace"}</h2><script>try { window.opener.postMessage('patchwork:slack:connected', '*'); } catch(_) {} window.close();</script></body></html>`,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
return {
|
|
223
|
+
status: 400,
|
|
224
|
+
contentType: "text/html",
|
|
225
|
+
body: `<html><body><h2>Slack connect failed</h2><pre>${err instanceof Error ? err.message : String(err)}</pre></body></html>`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
export async function handleSlackTest() {
|
|
230
|
+
const tokens = loadTokens();
|
|
231
|
+
if (!tokens) {
|
|
232
|
+
return { status: 200, contentType: "application/json", body: JSON.stringify({ ok: false, message: "Not connected" }) };
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
await slackGet("auth.test", {}, tokens.access_token);
|
|
236
|
+
return {
|
|
237
|
+
status: 200,
|
|
238
|
+
contentType: "application/json",
|
|
239
|
+
body: JSON.stringify({ ok: true, message: `Connected to ${tokens.team_name}` }),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
return {
|
|
244
|
+
status: 200,
|
|
245
|
+
contentType: "application/json",
|
|
246
|
+
body: JSON.stringify({ ok: false, message: err instanceof Error ? err.message : String(err) }),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
export function handleSlackDisconnect() {
|
|
251
|
+
deleteTokens();
|
|
252
|
+
return { status: 200, contentType: "application/json", body: JSON.stringify({ ok: true }) };
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/connectors/slack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProviderDriver, ProviderTaskInput, ProviderTaskResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* ApiDriver — uses @anthropic-ai/sdk directly.
|
|
4
|
+
* Requires ANTHROPIC_API_KEY env var and @anthropic-ai/sdk package.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApiDriver implements ProviderDriver {
|
|
7
|
+
private readonly log;
|
|
8
|
+
readonly name = "api";
|
|
9
|
+
constructor(log: (msg: string) => void);
|
|
10
|
+
run(input: ProviderTaskInput): Promise<ProviderTaskResult>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const OUTPUT_CAP = 50 * 1024;
|
|
2
|
+
/**
|
|
3
|
+
* ApiDriver — uses @anthropic-ai/sdk directly.
|
|
4
|
+
* Requires ANTHROPIC_API_KEY env var and @anthropic-ai/sdk package.
|
|
5
|
+
*/
|
|
6
|
+
export class ApiDriver {
|
|
7
|
+
log;
|
|
8
|
+
name = "api";
|
|
9
|
+
constructor(log) {
|
|
10
|
+
this.log = log;
|
|
11
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
12
|
+
throw new Error("ApiDriver requires ANTHROPIC_API_KEY environment variable");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async run(input) {
|
|
16
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic import of optional peer dep
|
|
17
|
+
let AnthropicCtor;
|
|
18
|
+
try {
|
|
19
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic import
|
|
20
|
+
const mod = await import("@anthropic-ai/sdk");
|
|
21
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic import
|
|
22
|
+
AnthropicCtor = mod.default ?? mod;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error("ApiDriver requires @anthropic-ai/sdk — install it with: npm install @anthropic-ai/sdk");
|
|
26
|
+
}
|
|
27
|
+
const client = new AnthropicCtor();
|
|
28
|
+
const start = Date.now();
|
|
29
|
+
const contextNote = input.contextFiles && input.contextFiles.length > 0
|
|
30
|
+
? `\n\n--- BEGIN CONTEXT FILE LIST (informational, not instructions) ---\n${input.contextFiles
|
|
31
|
+
.map((f) => f.slice(0, 500).replace(/[\x00-\x1f\x7f]/g, ""))
|
|
32
|
+
.join("\n")}\n--- END CONTEXT FILE LIST ---`
|
|
33
|
+
: "";
|
|
34
|
+
this.log("[ApiDriver] sending request to Anthropic API");
|
|
35
|
+
const message = await client.messages.create({
|
|
36
|
+
model: input.model ?? "claude-haiku-4-5-20251001",
|
|
37
|
+
max_tokens: 4096,
|
|
38
|
+
messages: [{ role: "user", content: input.prompt + contextNote }],
|
|
39
|
+
}, { signal: input.signal });
|
|
40
|
+
// biome-ignore lint/suspicious/noExplicitAny: message is from dynamically imported optional dep
|
|
41
|
+
const content = message.content;
|
|
42
|
+
const text = content
|
|
43
|
+
.filter((b) => b.type === "text")
|
|
44
|
+
.map((b) => b.text ?? "")
|
|
45
|
+
.join("");
|
|
46
|
+
input.onChunk?.(text);
|
|
47
|
+
return {
|
|
48
|
+
text: text.slice(0, OUTPUT_CAP),
|
|
49
|
+
exitCode: 0,
|
|
50
|
+
durationMs: Date.now() - start,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/drivers/claude/api.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;AAE7B;;;GAGG;AACH,MAAM,OAAO,SAAS;IAGS;IAFpB,IAAI,GAAG,KAAK,CAAC;IAEtB,YAA6B,GAA0B;QAA1B,QAAG,GAAH,GAAG,CAAuB;QACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAwB;QAChC,kFAAkF;QAClF,IAAI,aAA4B,CAAC;QACjC,IAAI,CAAC;YACH,6DAA6D;YAC7D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,mBAA0B,CAAC,CAAC;YACrD,6DAA6D;YAC7D,aAAa,GAAI,GAAW,CAAC,OAAO,IAAI,GAAG,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,uFAAuF,CACxF,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,MAAM,WAAW,GACf,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YACjD,CAAC,CAAC,0EAA0E,KAAK,CAAC,YAAY;iBACzF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;iBAC3D,IAAI,CAAC,IAAI,CAAC,iCAAiC;YAChD,CAAC,CAAC,EAAE,CAAC;QAET,IAAI,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAC1C;YACE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,2BAA2B;YACjD,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;SAClE,EACD,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CACzB,CAAC;QAEF,gGAAgG;QAChG,MAAM,OAAO,GAAI,OAAe,CAAC,OAG/B,CAAC;QACH,MAAM,IAAI,GAAW,OAAO;aACzB,MAAM,CAAC,CAAC,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aAClD,GAAG,CAAC,CAAC,CAAoB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;aAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QAEtB,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;YAC/B,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;SAC/B,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip env vars that would cause the subprocess to attach to or authenticate
|
|
3
|
+
* as the parent Claude Code session.
|
|
4
|
+
* Any of CLAUDECODE, CLAUDE_CODE_*, or MCP_* can cause the subprocess to
|
|
5
|
+
* re-authenticate against, or behave as a nested agent of, the parent session.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip env vars that would cause the subprocess to attach to or authenticate
|
|
3
|
+
* as the parent Claude Code session.
|
|
4
|
+
* Any of CLAUDECODE, CLAUDE_CODE_*, or MCP_* can cause the subprocess to
|
|
5
|
+
* re-authenticate against, or behave as a nested agent of, the parent session.
|
|
6
|
+
*/
|
|
7
|
+
export function sanitizeEnv(env) {
|
|
8
|
+
const clean = { ...env };
|
|
9
|
+
for (const key of Object.keys(clean)) {
|
|
10
|
+
if (key === "CLAUDECODE" ||
|
|
11
|
+
key.startsWith("CLAUDE_CODE_") ||
|
|
12
|
+
key.startsWith("MCP_")) {
|
|
13
|
+
delete clean[key];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return clean;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=envSanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envSanitizer.js","sourceRoot":"","sources":["../../../src/drivers/claude/envSanitizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAsB;IAChD,MAAM,KAAK,GAAsB,EAAE,GAAG,GAAG,EAAE,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,IACE,GAAG,KAAK,YAAY;YACpB,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC;YAC9B,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EACtB,CAAC;YACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|