@invect/version-control 0.0.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/dist/backend/flow-serializer.d.ts +38 -0
  3. package/dist/backend/flow-serializer.d.ts.map +1 -0
  4. package/dist/backend/git-provider.d.ts +77 -0
  5. package/dist/backend/git-provider.d.ts.map +1 -0
  6. package/dist/backend/github-provider.d.ts +14 -0
  7. package/dist/backend/github-provider.d.ts.map +1 -0
  8. package/dist/backend/index.cjs +1045 -0
  9. package/dist/backend/index.cjs.map +1 -0
  10. package/dist/backend/index.d.cts +51 -0
  11. package/dist/backend/index.d.cts.map +1 -0
  12. package/dist/backend/index.d.mts +51 -0
  13. package/dist/backend/index.d.mts.map +1 -0
  14. package/dist/backend/index.d.ts +5 -0
  15. package/dist/backend/index.d.ts.map +1 -0
  16. package/dist/backend/index.mjs +1043 -0
  17. package/dist/backend/index.mjs.map +1 -0
  18. package/dist/backend/plugin.d.ts +24 -0
  19. package/dist/backend/plugin.d.ts.map +1 -0
  20. package/dist/backend/schema.d.ts +3 -0
  21. package/dist/backend/schema.d.ts.map +1 -0
  22. package/dist/backend/sync-service.d.ts +47 -0
  23. package/dist/backend/sync-service.d.ts.map +1 -0
  24. package/dist/backend/types.d.ts +20 -0
  25. package/dist/backend/types.d.ts.map +1 -0
  26. package/dist/frontend/index.cjs +0 -0
  27. package/dist/frontend/index.d.cts +2 -0
  28. package/dist/frontend/index.d.mts +2 -0
  29. package/dist/frontend/index.d.ts +2 -0
  30. package/dist/frontend/index.d.ts.map +1 -0
  31. package/dist/frontend/index.mjs +1 -0
  32. package/dist/git-provider-BD8MMEXB.d.mts +80 -0
  33. package/dist/git-provider-BD8MMEXB.d.mts.map +1 -0
  34. package/dist/git-provider-CjMtpb86.d.cts +80 -0
  35. package/dist/git-provider-CjMtpb86.d.cts.map +1 -0
  36. package/dist/providers/github.cjs +191 -0
  37. package/dist/providers/github.cjs.map +1 -0
  38. package/dist/providers/github.d.cts +17 -0
  39. package/dist/providers/github.d.cts.map +1 -0
  40. package/dist/providers/github.d.mts +17 -0
  41. package/dist/providers/github.d.mts.map +1 -0
  42. package/dist/providers/github.d.ts +2 -0
  43. package/dist/providers/github.d.ts.map +1 -0
  44. package/dist/providers/github.mjs +190 -0
  45. package/dist/providers/github.mjs.map +1 -0
  46. package/dist/shared/types.cjs +0 -0
  47. package/dist/shared/types.d.cts +2 -0
  48. package/dist/shared/types.d.mts +2 -0
  49. package/dist/shared/types.d.ts +77 -0
  50. package/dist/shared/types.d.ts.map +1 -0
  51. package/dist/shared/types.mjs +1 -0
  52. package/dist/types-B32wGtx7.d.cts +80 -0
  53. package/dist/types-B32wGtx7.d.cts.map +1 -0
  54. package/dist/types-B7fFBAOX.d.mts +80 -0
  55. package/dist/types-B7fFBAOX.d.mts.map +1 -0
  56. package/package.json +53 -0
@@ -0,0 +1,191 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let node_crypto = require("node:crypto");
3
+ //#region src/backend/github-provider.ts
4
+ /**
5
+ * Create a GitHub provider instance.
6
+ *
7
+ * Uses the GitHub REST API directly (no Octokit dependency).
8
+ * Supports PAT, GitHub App, and Invect credential-based auth.
9
+ */
10
+ function githubProvider(options) {
11
+ let resolvedToken = null;
12
+ let appTokenExpiresAt = 0;
13
+ async function getToken() {
14
+ if (options.auth.type === "token") return options.auth.token;
15
+ if (options.auth.type === "app") {
16
+ if (resolvedToken && Date.now() < appTokenExpiresAt - 300 * 1e3) return resolvedToken;
17
+ const jwt = await createAppJwt(options.auth.appId, options.auth.privateKey);
18
+ let installationId = options.auth.installationId;
19
+ if (!installationId) {
20
+ const res = await fetch("https://api.github.com/app/installations", { headers: {
21
+ Accept: "application/vnd.github+json",
22
+ Authorization: `Bearer ${jwt}`
23
+ } });
24
+ if (!res.ok) throw new Error(`Failed to list GitHub App installations: ${res.status}`);
25
+ const installations = await res.json();
26
+ if (installations.length === 0) throw new Error("GitHub App has no installations. Install the app on a repository first.");
27
+ installationId = installations[0].id;
28
+ }
29
+ const tokenRes = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
30
+ method: "POST",
31
+ headers: {
32
+ Accept: "application/vnd.github+json",
33
+ Authorization: `Bearer ${jwt}`
34
+ }
35
+ });
36
+ if (!tokenRes.ok) throw new Error(`Failed to create installation token: ${tokenRes.status}`);
37
+ const tokenData = await tokenRes.json();
38
+ resolvedToken = tokenData.token;
39
+ appTokenExpiresAt = new Date(tokenData.expires_at).getTime();
40
+ return resolvedToken;
41
+ }
42
+ if (options.auth.type === "credential") throw new Error("GitHub credential-based auth must be resolved during plugin init. Use { type: \"token\" } or { type: \"app\" } for direct configuration.");
43
+ throw new Error(`Unsupported auth type: ${options.auth.type}`);
44
+ }
45
+ /** Create a JWT for GitHub App authentication (RS256, 10-min expiry). */
46
+ async function createAppJwt(appId, privateKey) {
47
+ const { createSign } = await import("node:crypto");
48
+ const now = Math.floor(Date.now() / 1e3);
49
+ const header = {
50
+ alg: "RS256",
51
+ typ: "JWT"
52
+ };
53
+ const payload = {
54
+ iss: appId,
55
+ iat: now - 60,
56
+ exp: now + 600
57
+ };
58
+ const b64 = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
59
+ const unsigned = `${b64(header)}.${b64(payload)}`;
60
+ const sign = createSign("RSA-SHA256");
61
+ sign.update(unsigned);
62
+ return `${unsigned}.${sign.sign(privateKey, "base64url")}`;
63
+ }
64
+ async function request(method, path, body) {
65
+ const token = await getToken();
66
+ const url = `https://api.github.com${path}`;
67
+ const headers = {
68
+ Accept: "application/vnd.github+json",
69
+ Authorization: `Bearer ${token}`,
70
+ "X-GitHub-Api-Version": "2022-11-28"
71
+ };
72
+ if (body) headers["Content-Type"] = "application/json";
73
+ const response = await fetch(url, {
74
+ method,
75
+ headers,
76
+ body: body ? JSON.stringify(body) : void 0
77
+ });
78
+ if (!response.ok && response.status !== 404) {
79
+ const text = await response.text();
80
+ throw new Error(`GitHub API error ${response.status}: ${text}`);
81
+ }
82
+ const data = response.status === 204 ? void 0 : await response.json();
83
+ return {
84
+ status: response.status,
85
+ data
86
+ };
87
+ }
88
+ function parseRepo(repo) {
89
+ const [owner, name] = repo.split("/");
90
+ if (!owner || !name) throw new Error(`Invalid repo format "${repo}". Expected "owner/repo".`);
91
+ return {
92
+ owner,
93
+ repo: name
94
+ };
95
+ }
96
+ return {
97
+ id: "github",
98
+ name: "GitHub",
99
+ async getFileContent(repo, path, ref) {
100
+ const { owner, repo: name } = parseRepo(repo);
101
+ const query = ref ? `?ref=${encodeURIComponent(ref)}` : "";
102
+ const { status, data } = await request("GET", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}${query}`);
103
+ if (status === 404) return null;
104
+ return {
105
+ content: data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content,
106
+ sha: data.sha
107
+ };
108
+ },
109
+ async createOrUpdateFile(repo, path, content, message, opts) {
110
+ const { owner, repo: name } = parseRepo(repo);
111
+ const body = {
112
+ message,
113
+ content: Buffer.from(content).toString("base64"),
114
+ branch: opts.branch
115
+ };
116
+ if (opts.sha) body.sha = opts.sha;
117
+ const { data } = await request("PUT", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, body);
118
+ return { commitSha: data.commit.sha };
119
+ },
120
+ async deleteFile(repo, path, message, opts) {
121
+ const { owner, repo: name } = parseRepo(repo);
122
+ await request("DELETE", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, {
123
+ message,
124
+ sha: opts.sha,
125
+ branch: opts.branch
126
+ });
127
+ },
128
+ async createBranch(repo, branch, fromRef) {
129
+ const { owner, repo: name } = parseRepo(repo);
130
+ const { data: refData } = await request("GET", `/repos/${owner}/${name}/git/ref/heads/${encodeURIComponent(fromRef)}`);
131
+ await request("POST", `/repos/${owner}/${name}/git/refs`, {
132
+ ref: `refs/heads/${branch}`,
133
+ sha: refData.object.sha
134
+ });
135
+ },
136
+ async deleteBranch(repo, branch) {
137
+ const { owner, repo: name } = parseRepo(repo);
138
+ await request("DELETE", `/repos/${owner}/${name}/git/refs/heads/${encodeURIComponent(branch)}`);
139
+ },
140
+ async getBranch(repo, branch) {
141
+ const { owner, repo: name } = parseRepo(repo);
142
+ const { status, data } = await request("GET", `/repos/${owner}/${name}/branches/${encodeURIComponent(branch)}`);
143
+ if (status === 404) return null;
144
+ return { sha: data.commit.sha };
145
+ },
146
+ async createPullRequest(repo, opts) {
147
+ const { owner, repo: name } = parseRepo(repo);
148
+ const { data } = await request("POST", `/repos/${owner}/${name}/pulls`, {
149
+ title: opts.title,
150
+ body: opts.body,
151
+ head: opts.head,
152
+ base: opts.base,
153
+ draft: opts.draft ?? false
154
+ });
155
+ return {
156
+ number: data.number,
157
+ url: data.html_url
158
+ };
159
+ },
160
+ async updatePullRequest(repo, number, opts) {
161
+ const { owner, repo: name } = parseRepo(repo);
162
+ await request("PATCH", `/repos/${owner}/${name}/pulls/${number}`, opts);
163
+ },
164
+ async getPullRequest(repo, number) {
165
+ const { owner, repo: name } = parseRepo(repo);
166
+ const { data } = await request("GET", `/repos/${owner}/${name}/pulls/${number}`);
167
+ let state;
168
+ if (data.merged) state = "merged";
169
+ else if (data.state === "closed") state = "closed";
170
+ else state = "open";
171
+ return {
172
+ state,
173
+ mergedAt: data.merged_at ?? void 0
174
+ };
175
+ },
176
+ async closePullRequest(repo, number, comment) {
177
+ const { owner, repo: name } = parseRepo(repo);
178
+ if (comment) await request("POST", `/repos/${owner}/${name}/issues/${number}/comments`, { body: comment });
179
+ await request("PATCH", `/repos/${owner}/${name}/pulls/${number}`, { state: "closed" });
180
+ },
181
+ verifyWebhookSignature(payload, signature, secret) {
182
+ const expected = `sha256=${(0, node_crypto.createHmac)("sha256", secret).update(payload).digest("hex")}`;
183
+ if (expected.length !== signature.length) return false;
184
+ return (0, node_crypto.timingSafeEqual)(Buffer.from(expected), Buffer.from(signature));
185
+ }
186
+ };
187
+ }
188
+ //#endregion
189
+ exports.githubProvider = githubProvider;
190
+
191
+ //# sourceMappingURL=github.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.cjs","names":[],"sources":["../../src/backend/github-provider.ts"],"sourcesContent":["// =============================================================================\n// GitHub Provider — implements GitProvider using the GitHub REST API (Octokit)\n// =============================================================================\n\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nimport type {\n CreatePullRequestOptions,\n GitBranchInfo,\n GitCommitResult,\n GitFileContent,\n GitFileUpdateOptions,\n GitProvider,\n GitPullRequestInfo,\n GitPullRequestResult,\n} from './git-provider';\nimport type { GitProviderAuth } from '../shared/types';\n\ninterface GitHubProviderOptions {\n auth: GitProviderAuth;\n}\n\n/**\n * Create a GitHub provider instance.\n *\n * Uses the GitHub REST API directly (no Octokit dependency).\n * Supports PAT, GitHub App, and Invect credential-based auth.\n */\nexport function githubProvider(options: GitHubProviderOptions): GitProvider {\n let resolvedToken: string | null = null;\n let appTokenExpiresAt: number = 0;\n\n async function getToken(): Promise<string> {\n // PAT — simple static token\n if (options.auth.type === 'token') {\n return options.auth.token;\n }\n\n // GitHub App — generate JWT → exchange for installation token\n if (options.auth.type === 'app') {\n // Cache: reuse token if not expired (tokens last 1 hour, refresh 5 min early)\n if (resolvedToken && Date.now() < appTokenExpiresAt - 5 * 60 * 1000) {\n return resolvedToken;\n }\n\n const jwt = await createAppJwt(options.auth.appId, options.auth.privateKey);\n\n // Find installation ID (if not provided, get it from the App)\n let installationId = options.auth.installationId;\n if (!installationId) {\n const res = await fetch('https://api.github.com/app/installations', {\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${jwt}`,\n },\n });\n if (!res.ok) {\n throw new Error(`Failed to list GitHub App installations: ${res.status}`);\n }\n const installations = (await res.json()) as Array<{ id: number }>;\n if (installations.length === 0) {\n throw new Error(\n 'GitHub App has no installations. Install the app on a repository first.',\n );\n }\n installationId = installations[0].id;\n }\n\n // Exchange JWT for installation access token\n const tokenRes = await fetch(\n `https://api.github.com/app/installations/${installationId}/access_tokens`,\n {\n method: 'POST',\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${jwt}`,\n },\n },\n );\n if (!tokenRes.ok) {\n throw new Error(`Failed to create installation token: ${tokenRes.status}`);\n }\n\n const tokenData = (await tokenRes.json()) as { token: string; expires_at: string };\n resolvedToken = tokenData.token;\n appTokenExpiresAt = new Date(tokenData.expires_at).getTime();\n return resolvedToken;\n }\n\n if (options.auth.type === 'credential') {\n throw new Error(\n 'GitHub credential-based auth must be resolved during plugin init. ' +\n 'Use { type: \"token\" } or { type: \"app\" } for direct configuration.',\n );\n }\n\n throw new Error(`Unsupported auth type: ${(options.auth as { type: string }).type}`);\n }\n\n /** Create a JWT for GitHub App authentication (RS256, 10-min expiry). */\n async function createAppJwt(appId: string, privateKey: string): Promise<string> {\n const { createSign } = await import('node:crypto');\n\n const now = Math.floor(Date.now() / 1000);\n const header = { alg: 'RS256', typ: 'JWT' };\n const payload = { iss: appId, iat: now - 60, exp: now + 10 * 60 };\n\n const b64 = (obj: unknown) => Buffer.from(JSON.stringify(obj)).toString('base64url');\n const unsigned = `${b64(header)}.${b64(payload)}`;\n\n const sign = createSign('RSA-SHA256');\n sign.update(unsigned);\n const signature = sign.sign(privateKey, 'base64url');\n\n return `${unsigned}.${signature}`;\n }\n\n async function request<T = unknown>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<{ status: number; data: T }> {\n const token = await getToken();\n const url = `https://api.github.com${path}`;\n\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${token}`,\n 'X-GitHub-Api-Version': '2022-11-28',\n };\n\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok && response.status !== 404) {\n const text = await response.text();\n throw new Error(`GitHub API error ${response.status}: ${text}`);\n }\n\n const data = response.status === 204 ? (undefined as T) : ((await response.json()) as T);\n return { status: response.status, data };\n }\n\n function parseRepo(repo: string): { owner: string; repo: string } {\n const [owner, name] = repo.split('/');\n if (!owner || !name) {\n throw new Error(`Invalid repo format \"${repo}\". Expected \"owner/repo\".`);\n }\n return { owner, repo: name };\n }\n\n const provider: GitProvider = {\n id: 'github',\n name: 'GitHub',\n\n async getFileContent(repo: string, path: string, ref?: string): Promise<GitFileContent | null> {\n const { owner, repo: name } = parseRepo(repo);\n const query = ref ? `?ref=${encodeURIComponent(ref)}` : '';\n const { status, data } = await request<{\n content: string;\n sha: string;\n encoding: string;\n }>('GET', `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}${query}`);\n\n if (status === 404) {\n return null;\n }\n\n const content =\n data.encoding === 'base64'\n ? Buffer.from(data.content, 'base64').toString('utf-8')\n : data.content;\n\n return { content, sha: data.sha };\n },\n\n async createOrUpdateFile(\n repo: string,\n path: string,\n content: string,\n message: string,\n opts: GitFileUpdateOptions,\n ): Promise<GitCommitResult> {\n const { owner, repo: name } = parseRepo(repo);\n const body: Record<string, unknown> = {\n message,\n content: Buffer.from(content).toString('base64'),\n branch: opts.branch,\n };\n if (opts.sha) {\n body.sha = opts.sha;\n }\n\n const { data } = await request<{ commit: { sha: string } }>(\n 'PUT',\n `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`,\n body,\n );\n\n return { commitSha: data.commit.sha };\n },\n\n async deleteFile(\n repo: string,\n path: string,\n message: string,\n opts: { branch: string; sha: string },\n ): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request('DELETE', `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, {\n message,\n sha: opts.sha,\n branch: opts.branch,\n });\n },\n\n async createBranch(repo: string, branch: string, fromRef: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n\n // Resolve fromRef to a SHA\n const { data: refData } = await request<{ object: { sha: string } }>(\n 'GET',\n `/repos/${owner}/${name}/git/ref/heads/${encodeURIComponent(fromRef)}`,\n );\n\n await request('POST', `/repos/${owner}/${name}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: refData.object.sha,\n });\n },\n\n async deleteBranch(repo: string, branch: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request(\n 'DELETE',\n `/repos/${owner}/${name}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n },\n\n async getBranch(repo: string, branch: string): Promise<GitBranchInfo | null> {\n const { owner, repo: name } = parseRepo(repo);\n const { status, data } = await request<{ commit: { sha: string } }>(\n 'GET',\n `/repos/${owner}/${name}/branches/${encodeURIComponent(branch)}`,\n );\n if (status === 404) {\n return null;\n }\n return { sha: data.commit.sha };\n },\n\n async createPullRequest(\n repo: string,\n opts: CreatePullRequestOptions,\n ): Promise<GitPullRequestResult> {\n const { owner, repo: name } = parseRepo(repo);\n const { data } = await request<{ number: number; html_url: string }>(\n 'POST',\n `/repos/${owner}/${name}/pulls`,\n {\n title: opts.title,\n body: opts.body,\n head: opts.head,\n base: opts.base,\n draft: opts.draft ?? false,\n },\n );\n return { number: data.number, url: data.html_url };\n },\n\n async updatePullRequest(\n repo: string,\n number: number,\n opts: { title?: string; body?: string },\n ): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request('PATCH', `/repos/${owner}/${name}/pulls/${number}`, opts);\n },\n\n async getPullRequest(repo: string, number: number): Promise<GitPullRequestInfo> {\n const { owner, repo: name } = parseRepo(repo);\n const { data } = await request<{\n state: string;\n merged: boolean;\n merged_at: string | null;\n }>('GET', `/repos/${owner}/${name}/pulls/${number}`);\n\n let state: 'open' | 'closed' | 'merged';\n if (data.merged) {\n state = 'merged';\n } else if (data.state === 'closed') {\n state = 'closed';\n } else {\n state = 'open';\n }\n\n return { state, mergedAt: data.merged_at ?? undefined };\n },\n\n async closePullRequest(repo: string, number: number, comment?: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n if (comment) {\n await request('POST', `/repos/${owner}/${name}/issues/${number}/comments`, {\n body: comment,\n });\n }\n await request('PATCH', `/repos/${owner}/${name}/pulls/${number}`, { state: 'closed' });\n },\n\n verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {\n const expected = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;\n if (expected.length !== signature.length) {\n return false;\n }\n return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n },\n };\n\n return provider;\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { t as GitProvider } from "../git-provider-CjMtpb86.cjs";
2
+ import { n as GitProviderAuth } from "../types-B32wGtx7.cjs";
3
+
4
+ //#region src/backend/github-provider.d.ts
5
+ interface GitHubProviderOptions {
6
+ auth: GitProviderAuth;
7
+ }
8
+ /**
9
+ * Create a GitHub provider instance.
10
+ *
11
+ * Uses the GitHub REST API directly (no Octokit dependency).
12
+ * Supports PAT, GitHub App, and Invect credential-based auth.
13
+ */
14
+ declare function githubProvider(options: GitHubProviderOptions): GitProvider;
15
+ //#endregion
16
+ export { githubProvider };
17
+ //# sourceMappingURL=github.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.cts","names":[],"sources":["../../src/backend/github-provider.ts"],"mappings":";;;;UAkBU,qBAAA;EACR,IAAA,EAAM,eAAA;AAAA;;;;;AASR;;iBAAgB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,WAAA"}
@@ -0,0 +1,17 @@
1
+ import { t as GitProvider } from "../git-provider-BD8MMEXB.mjs";
2
+ import { n as GitProviderAuth } from "../types-B7fFBAOX.mjs";
3
+
4
+ //#region src/backend/github-provider.d.ts
5
+ interface GitHubProviderOptions {
6
+ auth: GitProviderAuth;
7
+ }
8
+ /**
9
+ * Create a GitHub provider instance.
10
+ *
11
+ * Uses the GitHub REST API directly (no Octokit dependency).
12
+ * Supports PAT, GitHub App, and Invect credential-based auth.
13
+ */
14
+ declare function githubProvider(options: GitHubProviderOptions): GitProvider;
15
+ //#endregion
16
+ export { githubProvider };
17
+ //# sourceMappingURL=github.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.mts","names":[],"sources":["../../src/backend/github-provider.ts"],"mappings":";;;;UAkBU,qBAAA;EACR,IAAA,EAAM,eAAA;AAAA;;;;;AASR;;iBAAgB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,WAAA"}
@@ -0,0 +1,2 @@
1
+ export { githubProvider } from '../backend/github-provider';
2
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,190 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ //#region src/backend/github-provider.ts
3
+ /**
4
+ * Create a GitHub provider instance.
5
+ *
6
+ * Uses the GitHub REST API directly (no Octokit dependency).
7
+ * Supports PAT, GitHub App, and Invect credential-based auth.
8
+ */
9
+ function githubProvider(options) {
10
+ let resolvedToken = null;
11
+ let appTokenExpiresAt = 0;
12
+ async function getToken() {
13
+ if (options.auth.type === "token") return options.auth.token;
14
+ if (options.auth.type === "app") {
15
+ if (resolvedToken && Date.now() < appTokenExpiresAt - 300 * 1e3) return resolvedToken;
16
+ const jwt = await createAppJwt(options.auth.appId, options.auth.privateKey);
17
+ let installationId = options.auth.installationId;
18
+ if (!installationId) {
19
+ const res = await fetch("https://api.github.com/app/installations", { headers: {
20
+ Accept: "application/vnd.github+json",
21
+ Authorization: `Bearer ${jwt}`
22
+ } });
23
+ if (!res.ok) throw new Error(`Failed to list GitHub App installations: ${res.status}`);
24
+ const installations = await res.json();
25
+ if (installations.length === 0) throw new Error("GitHub App has no installations. Install the app on a repository first.");
26
+ installationId = installations[0].id;
27
+ }
28
+ const tokenRes = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
29
+ method: "POST",
30
+ headers: {
31
+ Accept: "application/vnd.github+json",
32
+ Authorization: `Bearer ${jwt}`
33
+ }
34
+ });
35
+ if (!tokenRes.ok) throw new Error(`Failed to create installation token: ${tokenRes.status}`);
36
+ const tokenData = await tokenRes.json();
37
+ resolvedToken = tokenData.token;
38
+ appTokenExpiresAt = new Date(tokenData.expires_at).getTime();
39
+ return resolvedToken;
40
+ }
41
+ if (options.auth.type === "credential") throw new Error("GitHub credential-based auth must be resolved during plugin init. Use { type: \"token\" } or { type: \"app\" } for direct configuration.");
42
+ throw new Error(`Unsupported auth type: ${options.auth.type}`);
43
+ }
44
+ /** Create a JWT for GitHub App authentication (RS256, 10-min expiry). */
45
+ async function createAppJwt(appId, privateKey) {
46
+ const { createSign } = await import("node:crypto");
47
+ const now = Math.floor(Date.now() / 1e3);
48
+ const header = {
49
+ alg: "RS256",
50
+ typ: "JWT"
51
+ };
52
+ const payload = {
53
+ iss: appId,
54
+ iat: now - 60,
55
+ exp: now + 600
56
+ };
57
+ const b64 = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
58
+ const unsigned = `${b64(header)}.${b64(payload)}`;
59
+ const sign = createSign("RSA-SHA256");
60
+ sign.update(unsigned);
61
+ return `${unsigned}.${sign.sign(privateKey, "base64url")}`;
62
+ }
63
+ async function request(method, path, body) {
64
+ const token = await getToken();
65
+ const url = `https://api.github.com${path}`;
66
+ const headers = {
67
+ Accept: "application/vnd.github+json",
68
+ Authorization: `Bearer ${token}`,
69
+ "X-GitHub-Api-Version": "2022-11-28"
70
+ };
71
+ if (body) headers["Content-Type"] = "application/json";
72
+ const response = await fetch(url, {
73
+ method,
74
+ headers,
75
+ body: body ? JSON.stringify(body) : void 0
76
+ });
77
+ if (!response.ok && response.status !== 404) {
78
+ const text = await response.text();
79
+ throw new Error(`GitHub API error ${response.status}: ${text}`);
80
+ }
81
+ const data = response.status === 204 ? void 0 : await response.json();
82
+ return {
83
+ status: response.status,
84
+ data
85
+ };
86
+ }
87
+ function parseRepo(repo) {
88
+ const [owner, name] = repo.split("/");
89
+ if (!owner || !name) throw new Error(`Invalid repo format "${repo}". Expected "owner/repo".`);
90
+ return {
91
+ owner,
92
+ repo: name
93
+ };
94
+ }
95
+ return {
96
+ id: "github",
97
+ name: "GitHub",
98
+ async getFileContent(repo, path, ref) {
99
+ const { owner, repo: name } = parseRepo(repo);
100
+ const query = ref ? `?ref=${encodeURIComponent(ref)}` : "";
101
+ const { status, data } = await request("GET", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}${query}`);
102
+ if (status === 404) return null;
103
+ return {
104
+ content: data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content,
105
+ sha: data.sha
106
+ };
107
+ },
108
+ async createOrUpdateFile(repo, path, content, message, opts) {
109
+ const { owner, repo: name } = parseRepo(repo);
110
+ const body = {
111
+ message,
112
+ content: Buffer.from(content).toString("base64"),
113
+ branch: opts.branch
114
+ };
115
+ if (opts.sha) body.sha = opts.sha;
116
+ const { data } = await request("PUT", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, body);
117
+ return { commitSha: data.commit.sha };
118
+ },
119
+ async deleteFile(repo, path, message, opts) {
120
+ const { owner, repo: name } = parseRepo(repo);
121
+ await request("DELETE", `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, {
122
+ message,
123
+ sha: opts.sha,
124
+ branch: opts.branch
125
+ });
126
+ },
127
+ async createBranch(repo, branch, fromRef) {
128
+ const { owner, repo: name } = parseRepo(repo);
129
+ const { data: refData } = await request("GET", `/repos/${owner}/${name}/git/ref/heads/${encodeURIComponent(fromRef)}`);
130
+ await request("POST", `/repos/${owner}/${name}/git/refs`, {
131
+ ref: `refs/heads/${branch}`,
132
+ sha: refData.object.sha
133
+ });
134
+ },
135
+ async deleteBranch(repo, branch) {
136
+ const { owner, repo: name } = parseRepo(repo);
137
+ await request("DELETE", `/repos/${owner}/${name}/git/refs/heads/${encodeURIComponent(branch)}`);
138
+ },
139
+ async getBranch(repo, branch) {
140
+ const { owner, repo: name } = parseRepo(repo);
141
+ const { status, data } = await request("GET", `/repos/${owner}/${name}/branches/${encodeURIComponent(branch)}`);
142
+ if (status === 404) return null;
143
+ return { sha: data.commit.sha };
144
+ },
145
+ async createPullRequest(repo, opts) {
146
+ const { owner, repo: name } = parseRepo(repo);
147
+ const { data } = await request("POST", `/repos/${owner}/${name}/pulls`, {
148
+ title: opts.title,
149
+ body: opts.body,
150
+ head: opts.head,
151
+ base: opts.base,
152
+ draft: opts.draft ?? false
153
+ });
154
+ return {
155
+ number: data.number,
156
+ url: data.html_url
157
+ };
158
+ },
159
+ async updatePullRequest(repo, number, opts) {
160
+ const { owner, repo: name } = parseRepo(repo);
161
+ await request("PATCH", `/repos/${owner}/${name}/pulls/${number}`, opts);
162
+ },
163
+ async getPullRequest(repo, number) {
164
+ const { owner, repo: name } = parseRepo(repo);
165
+ const { data } = await request("GET", `/repos/${owner}/${name}/pulls/${number}`);
166
+ let state;
167
+ if (data.merged) state = "merged";
168
+ else if (data.state === "closed") state = "closed";
169
+ else state = "open";
170
+ return {
171
+ state,
172
+ mergedAt: data.merged_at ?? void 0
173
+ };
174
+ },
175
+ async closePullRequest(repo, number, comment) {
176
+ const { owner, repo: name } = parseRepo(repo);
177
+ if (comment) await request("POST", `/repos/${owner}/${name}/issues/${number}/comments`, { body: comment });
178
+ await request("PATCH", `/repos/${owner}/${name}/pulls/${number}`, { state: "closed" });
179
+ },
180
+ verifyWebhookSignature(payload, signature, secret) {
181
+ const expected = `sha256=${createHmac("sha256", secret).update(payload).digest("hex")}`;
182
+ if (expected.length !== signature.length) return false;
183
+ return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
184
+ }
185
+ };
186
+ }
187
+ //#endregion
188
+ export { githubProvider };
189
+
190
+ //# sourceMappingURL=github.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.mjs","names":[],"sources":["../../src/backend/github-provider.ts"],"sourcesContent":["// =============================================================================\n// GitHub Provider — implements GitProvider using the GitHub REST API (Octokit)\n// =============================================================================\n\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nimport type {\n CreatePullRequestOptions,\n GitBranchInfo,\n GitCommitResult,\n GitFileContent,\n GitFileUpdateOptions,\n GitProvider,\n GitPullRequestInfo,\n GitPullRequestResult,\n} from './git-provider';\nimport type { GitProviderAuth } from '../shared/types';\n\ninterface GitHubProviderOptions {\n auth: GitProviderAuth;\n}\n\n/**\n * Create a GitHub provider instance.\n *\n * Uses the GitHub REST API directly (no Octokit dependency).\n * Supports PAT, GitHub App, and Invect credential-based auth.\n */\nexport function githubProvider(options: GitHubProviderOptions): GitProvider {\n let resolvedToken: string | null = null;\n let appTokenExpiresAt: number = 0;\n\n async function getToken(): Promise<string> {\n // PAT — simple static token\n if (options.auth.type === 'token') {\n return options.auth.token;\n }\n\n // GitHub App — generate JWT → exchange for installation token\n if (options.auth.type === 'app') {\n // Cache: reuse token if not expired (tokens last 1 hour, refresh 5 min early)\n if (resolvedToken && Date.now() < appTokenExpiresAt - 5 * 60 * 1000) {\n return resolvedToken;\n }\n\n const jwt = await createAppJwt(options.auth.appId, options.auth.privateKey);\n\n // Find installation ID (if not provided, get it from the App)\n let installationId = options.auth.installationId;\n if (!installationId) {\n const res = await fetch('https://api.github.com/app/installations', {\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${jwt}`,\n },\n });\n if (!res.ok) {\n throw new Error(`Failed to list GitHub App installations: ${res.status}`);\n }\n const installations = (await res.json()) as Array<{ id: number }>;\n if (installations.length === 0) {\n throw new Error(\n 'GitHub App has no installations. Install the app on a repository first.',\n );\n }\n installationId = installations[0].id;\n }\n\n // Exchange JWT for installation access token\n const tokenRes = await fetch(\n `https://api.github.com/app/installations/${installationId}/access_tokens`,\n {\n method: 'POST',\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${jwt}`,\n },\n },\n );\n if (!tokenRes.ok) {\n throw new Error(`Failed to create installation token: ${tokenRes.status}`);\n }\n\n const tokenData = (await tokenRes.json()) as { token: string; expires_at: string };\n resolvedToken = tokenData.token;\n appTokenExpiresAt = new Date(tokenData.expires_at).getTime();\n return resolvedToken;\n }\n\n if (options.auth.type === 'credential') {\n throw new Error(\n 'GitHub credential-based auth must be resolved during plugin init. ' +\n 'Use { type: \"token\" } or { type: \"app\" } for direct configuration.',\n );\n }\n\n throw new Error(`Unsupported auth type: ${(options.auth as { type: string }).type}`);\n }\n\n /** Create a JWT for GitHub App authentication (RS256, 10-min expiry). */\n async function createAppJwt(appId: string, privateKey: string): Promise<string> {\n const { createSign } = await import('node:crypto');\n\n const now = Math.floor(Date.now() / 1000);\n const header = { alg: 'RS256', typ: 'JWT' };\n const payload = { iss: appId, iat: now - 60, exp: now + 10 * 60 };\n\n const b64 = (obj: unknown) => Buffer.from(JSON.stringify(obj)).toString('base64url');\n const unsigned = `${b64(header)}.${b64(payload)}`;\n\n const sign = createSign('RSA-SHA256');\n sign.update(unsigned);\n const signature = sign.sign(privateKey, 'base64url');\n\n return `${unsigned}.${signature}`;\n }\n\n async function request<T = unknown>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<{ status: number; data: T }> {\n const token = await getToken();\n const url = `https://api.github.com${path}`;\n\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${token}`,\n 'X-GitHub-Api-Version': '2022-11-28',\n };\n\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok && response.status !== 404) {\n const text = await response.text();\n throw new Error(`GitHub API error ${response.status}: ${text}`);\n }\n\n const data = response.status === 204 ? (undefined as T) : ((await response.json()) as T);\n return { status: response.status, data };\n }\n\n function parseRepo(repo: string): { owner: string; repo: string } {\n const [owner, name] = repo.split('/');\n if (!owner || !name) {\n throw new Error(`Invalid repo format \"${repo}\". Expected \"owner/repo\".`);\n }\n return { owner, repo: name };\n }\n\n const provider: GitProvider = {\n id: 'github',\n name: 'GitHub',\n\n async getFileContent(repo: string, path: string, ref?: string): Promise<GitFileContent | null> {\n const { owner, repo: name } = parseRepo(repo);\n const query = ref ? `?ref=${encodeURIComponent(ref)}` : '';\n const { status, data } = await request<{\n content: string;\n sha: string;\n encoding: string;\n }>('GET', `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}${query}`);\n\n if (status === 404) {\n return null;\n }\n\n const content =\n data.encoding === 'base64'\n ? Buffer.from(data.content, 'base64').toString('utf-8')\n : data.content;\n\n return { content, sha: data.sha };\n },\n\n async createOrUpdateFile(\n repo: string,\n path: string,\n content: string,\n message: string,\n opts: GitFileUpdateOptions,\n ): Promise<GitCommitResult> {\n const { owner, repo: name } = parseRepo(repo);\n const body: Record<string, unknown> = {\n message,\n content: Buffer.from(content).toString('base64'),\n branch: opts.branch,\n };\n if (opts.sha) {\n body.sha = opts.sha;\n }\n\n const { data } = await request<{ commit: { sha: string } }>(\n 'PUT',\n `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`,\n body,\n );\n\n return { commitSha: data.commit.sha };\n },\n\n async deleteFile(\n repo: string,\n path: string,\n message: string,\n opts: { branch: string; sha: string },\n ): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request('DELETE', `/repos/${owner}/${name}/contents/${encodeURIComponent(path)}`, {\n message,\n sha: opts.sha,\n branch: opts.branch,\n });\n },\n\n async createBranch(repo: string, branch: string, fromRef: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n\n // Resolve fromRef to a SHA\n const { data: refData } = await request<{ object: { sha: string } }>(\n 'GET',\n `/repos/${owner}/${name}/git/ref/heads/${encodeURIComponent(fromRef)}`,\n );\n\n await request('POST', `/repos/${owner}/${name}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: refData.object.sha,\n });\n },\n\n async deleteBranch(repo: string, branch: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request(\n 'DELETE',\n `/repos/${owner}/${name}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n },\n\n async getBranch(repo: string, branch: string): Promise<GitBranchInfo | null> {\n const { owner, repo: name } = parseRepo(repo);\n const { status, data } = await request<{ commit: { sha: string } }>(\n 'GET',\n `/repos/${owner}/${name}/branches/${encodeURIComponent(branch)}`,\n );\n if (status === 404) {\n return null;\n }\n return { sha: data.commit.sha };\n },\n\n async createPullRequest(\n repo: string,\n opts: CreatePullRequestOptions,\n ): Promise<GitPullRequestResult> {\n const { owner, repo: name } = parseRepo(repo);\n const { data } = await request<{ number: number; html_url: string }>(\n 'POST',\n `/repos/${owner}/${name}/pulls`,\n {\n title: opts.title,\n body: opts.body,\n head: opts.head,\n base: opts.base,\n draft: opts.draft ?? false,\n },\n );\n return { number: data.number, url: data.html_url };\n },\n\n async updatePullRequest(\n repo: string,\n number: number,\n opts: { title?: string; body?: string },\n ): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n await request('PATCH', `/repos/${owner}/${name}/pulls/${number}`, opts);\n },\n\n async getPullRequest(repo: string, number: number): Promise<GitPullRequestInfo> {\n const { owner, repo: name } = parseRepo(repo);\n const { data } = await request<{\n state: string;\n merged: boolean;\n merged_at: string | null;\n }>('GET', `/repos/${owner}/${name}/pulls/${number}`);\n\n let state: 'open' | 'closed' | 'merged';\n if (data.merged) {\n state = 'merged';\n } else if (data.state === 'closed') {\n state = 'closed';\n } else {\n state = 'open';\n }\n\n return { state, mergedAt: data.merged_at ?? undefined };\n },\n\n async closePullRequest(repo: string, number: number, comment?: string): Promise<void> {\n const { owner, repo: name } = parseRepo(repo);\n if (comment) {\n await request('POST', `/repos/${owner}/${name}/issues/${number}/comments`, {\n body: comment,\n });\n }\n await request('PATCH', `/repos/${owner}/${name}/pulls/${number}`, { state: 'closed' });\n },\n\n verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {\n const expected = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;\n if (expected.length !== signature.length) {\n return false;\n }\n return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n },\n };\n\n return provider;\n}\n"],"mappings":"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"}
File without changes
@@ -0,0 +1,2 @@
1
+ import { a as VcSyncConfig, c as VcSyncMode, i as VcSyncAction, l as VcSyncResult, n as GitProviderAuth, o as VcSyncDirection, r as VcFlowSyncStatus, s as VcSyncHistoryRecord, t as ConfigureSyncInput, u as VcSyncStatus } from "../types-B32wGtx7.cjs";
2
+ export { ConfigureSyncInput, GitProviderAuth, VcFlowSyncStatus, VcSyncAction, VcSyncConfig, VcSyncDirection, VcSyncHistoryRecord, VcSyncMode, VcSyncResult, VcSyncStatus };
@@ -0,0 +1,2 @@
1
+ import { a as VcSyncConfig, c as VcSyncMode, i as VcSyncAction, l as VcSyncResult, n as GitProviderAuth, o as VcSyncDirection, r as VcFlowSyncStatus, s as VcSyncHistoryRecord, t as ConfigureSyncInput, u as VcSyncStatus } from "../types-B7fFBAOX.mjs";
2
+ export { ConfigureSyncInput, GitProviderAuth, VcFlowSyncStatus, VcSyncAction, VcSyncConfig, VcSyncDirection, VcSyncHistoryRecord, VcSyncMode, VcSyncResult, VcSyncStatus };
@@ -0,0 +1,77 @@
1
+ /** Supported sync modes */
2
+ export type VcSyncMode = 'direct-commit' | 'pr-per-save' | 'pr-per-publish';
3
+ /** Sync direction */
4
+ export type VcSyncDirection = 'push' | 'pull' | 'bidirectional';
5
+ /** Sync history action types */
6
+ export type VcSyncAction = 'push' | 'pull' | 'pr-created' | 'pr-merged' | 'conflict';
7
+ /** Status of a synced flow */
8
+ export type VcSyncStatus = 'synced' | 'pending' | 'conflict' | 'not-connected' | 'error';
9
+ /** Git provider authentication config */
10
+ export type GitProviderAuth = {
11
+ type: 'token';
12
+ token: string;
13
+ } | {
14
+ type: 'app';
15
+ appId: string;
16
+ privateKey: string;
17
+ installationId?: number;
18
+ } | {
19
+ type: 'credential';
20
+ credentialId: string;
21
+ };
22
+ /** Sync config record (mirrors vc_sync_config table) */
23
+ export interface VcSyncConfig {
24
+ id: string;
25
+ flowId: string;
26
+ provider: string;
27
+ repo: string;
28
+ branch: string;
29
+ filePath: string;
30
+ mode: VcSyncMode;
31
+ syncDirection: VcSyncDirection;
32
+ lastSyncedAt: string | null;
33
+ lastCommitSha: string | null;
34
+ lastSyncedVersion: number | null;
35
+ draftBranch: string | null;
36
+ activePrNumber: number | null;
37
+ activePrUrl: string | null;
38
+ enabled: boolean;
39
+ }
40
+ /** Sync history record (mirrors vc_sync_history table) */
41
+ export interface VcSyncHistoryRecord {
42
+ id: string;
43
+ flowId: string;
44
+ action: VcSyncAction;
45
+ commitSha: string | null;
46
+ prNumber: number | null;
47
+ version: number | null;
48
+ message: string | null;
49
+ createdAt: string;
50
+ createdBy: string | null;
51
+ }
52
+ /** Sync status response for a flow */
53
+ export interface VcFlowSyncStatus {
54
+ flowId: string;
55
+ status: VcSyncStatus;
56
+ config: VcSyncConfig | null;
57
+ lastSync: VcSyncHistoryRecord | null;
58
+ }
59
+ /** Configure sync request body */
60
+ export interface ConfigureSyncInput {
61
+ repo?: string;
62
+ branch?: string;
63
+ filePath?: string;
64
+ mode?: VcSyncMode;
65
+ syncDirection?: VcSyncDirection;
66
+ enabled?: boolean;
67
+ }
68
+ /** Push/pull result */
69
+ export interface VcSyncResult {
70
+ success: boolean;
71
+ commitSha?: string;
72
+ prNumber?: number;
73
+ prUrl?: string;
74
+ error?: string;
75
+ action: VcSyncAction;
76
+ }
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};