fission-worker 0.2.1 → 0.3.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.
Files changed (30) hide show
  1. package/dist/docker.d.ts +1 -1
  2. package/dist/docker.js +65 -57
  3. package/dist/docker.js.map +1 -1
  4. package/package.json +4 -3
  5. package/runner/common/decorators/current-user.decorator.js +10 -0
  6. package/runner/common/decorators/public.decorator.js +7 -0
  7. package/runner/common/decorators/roles.decorator.js +7 -0
  8. package/runner/common/services/activity-log.service.js +48 -0
  9. package/runner/common/services/event-bus.service.js +38 -0
  10. package/runner/modules/github/github.controller.js +155 -0
  11. package/runner/modules/github/github.module.js +22 -0
  12. package/runner/modules/github/github.service.js +104 -0
  13. package/runner/modules/pipeline/data/api-data.service.js +140 -0
  14. package/runner/modules/pipeline/data/pipeline-data.interface.js +2 -0
  15. package/runner/modules/pipeline/data/prisma-data.service.js +149 -0
  16. package/runner/modules/pipeline/pipeline-cto.service.js +129 -0
  17. package/runner/modules/pipeline/pipeline-helpers.service.js +318 -0
  18. package/runner/modules/pipeline/pipeline-orchestrator.js +399 -0
  19. package/runner/modules/pipeline/pipeline-queue.service.js +121 -0
  20. package/runner/modules/pipeline/pipeline-techlead.service.js +127 -0
  21. package/runner/modules/pipeline/pipeline-worker.service.js +343 -0
  22. package/runner/modules/pipeline/pipeline.controller.js +310 -0
  23. package/runner/modules/pipeline/pipeline.module.js +51 -0
  24. package/runner/modules/pipeline/pipeline.service.js +706 -0
  25. package/runner/modules/worker-api/worker-api.controller.js +497 -0
  26. package/runner/modules/worker-api/worker-api.guard.js +41 -0
  27. package/runner/modules/worker-api/worker-api.module.js +25 -0
  28. package/runner/modules/worker-api/worker-dispatch.service.js +87 -0
  29. package/runner/pipeline-runner/index.js +108 -0
  30. package/runner/prisma/prisma.service.js +23 -0
package/dist/docker.d.ts CHANGED
@@ -16,7 +16,7 @@ export interface PipelineJob {
16
16
  }
17
17
  /**
18
18
  * Start a pipeline session on the host machine (no Docker).
19
- * Clones repo to a temp workspace, runs Claude CLI directly,
19
+ * Clones repo to a temp workspace, runs the pipeline engine directly,
20
20
  * cleans up when done. Uses the host's Claude subscription auth.
21
21
  */
22
22
  export declare function startSession(job: PipelineJob, config: WorkerConfig, activeSessions: Map<string, ChildProcess>): void;
package/dist/docker.js CHANGED
@@ -6,7 +6,7 @@ const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  /**
8
8
  * Start a pipeline session on the host machine (no Docker).
9
- * Clones repo to a temp workspace, runs Claude CLI directly,
9
+ * Clones repo to a temp workspace, runs the pipeline engine directly,
10
10
  * cleans up when done. Uses the host's Claude subscription auth.
11
11
  */
12
12
  function startSession(job, config, activeSessions) {
@@ -44,45 +44,62 @@ function startSession(job, config, activeSessions) {
44
44
  return;
45
45
  }
46
46
  console.log(`[${tag}] Clone complete, starting pipeline...`);
47
- // Emit initial progress to the dashboard
48
- emitEvent(config, job.projectId, "phase_changed", {
49
- sessionId: job.sessionId,
50
- phase: "REVIEW",
51
- reason: "Worker starting pipeline",
52
- });
53
- // Run Claude in the cloned repo — uses host's Claude subscription
54
- const prompt = `You are an autonomous R&D pipeline. Analyze this codebase and do the following:\n\n` +
55
- `1. REVIEW: Scan for the top 3-5 issues (bugs, security, performance, improvements)\n` +
56
- `2. PLAN: For each issue, create a concrete implementation task\n` +
57
- `3. BUILD: Implement the highest priority task — make the changes and commit\n` +
58
- `4. REVIEW: Check your work for correctness\n\n` +
59
- `Work autonomously. Make real code changes and commit them with descriptive messages.\n` +
60
- `When done, report what you accomplished as JSON:\n` +
61
- `{summary: string, tasksCompleted: string[], filesChanged: string[], commits: string[]}\n` +
62
- `Return ONLY valid JSON.`;
63
- const proc = (0, child_process_1.spawn)("claude", [
64
- "-p", prompt,
65
- "--output-format", "json",
66
- "--verbose",
67
- ], {
68
- cwd: repoDir,
69
- env: { ...process.env },
70
- stdio: ["ignore", "pipe", "pipe"],
71
- });
47
+ // Find the pipeline runner script (built from Fission repo)
48
+ const runnerScript = findRunnerScript();
49
+ // Environment for the pipeline process
50
+ const pipelineEnv = {
51
+ ...process.env,
52
+ FISSION_API_URL: config.fissionUrl,
53
+ FISSION_API_KEY: config.apiKey,
54
+ SESSION_ID: job.sessionId,
55
+ PROJECT_ID: job.projectId,
56
+ REPO_URL: repo.repoUrl,
57
+ REPO_BRANCH: repo.branch || "main",
58
+ GITHUB_TOKEN: gitToken || "",
59
+ // Override workspace for the runner it clones again but we already did it
60
+ FISSION_REPO_PATH: repoDir,
61
+ };
62
+ let proc;
63
+ if (runnerScript) {
64
+ // Full pipeline engine — runs CTO → Tech Lead → Workers loop
65
+ console.log(`[${tag}] Using pipeline runner: ${runnerScript}`);
66
+ proc = (0, child_process_1.spawn)("node", [runnerScript], {
67
+ cwd: repoDir,
68
+ env: pipelineEnv,
69
+ stdio: ["ignore", "pipe", "pipe"],
70
+ });
71
+ }
72
+ else {
73
+ // Fallback — run Claude directly with a comprehensive prompt
74
+ console.log(`[${tag}] Pipeline runner not found, using Claude directly`);
75
+ const prompt = `You are an autonomous R&D pipeline. Analyze this codebase and do the following:\n\n` +
76
+ `1. REVIEW: Scan for the top 3-5 issues (bugs, security, performance, improvements)\n` +
77
+ `2. PLAN: For each issue, create a concrete implementation task\n` +
78
+ `3. BUILD: Implement the highest priority task — make the changes and commit\n` +
79
+ `4. REVIEW: Check your work for correctness\n\n` +
80
+ `Work autonomously. Make real code changes and commit them with descriptive messages.\n` +
81
+ `When done, report what you accomplished as JSON:\n` +
82
+ `{summary: string, tasksCompleted: string[], filesChanged: string[], commits: string[]}\n` +
83
+ `Return ONLY valid JSON.`;
84
+ proc = (0, child_process_1.spawn)("claude", ["-p", prompt, "--output-format", "json", "--verbose"], {
85
+ cwd: repoDir,
86
+ env: pipelineEnv,
87
+ stdio: ["ignore", "pipe", "pipe"],
88
+ });
89
+ }
72
90
  activeSessions.set(job.sessionId, proc);
73
91
  let stdout = "";
74
92
  proc.stdout?.on("data", (chunk) => {
93
+ const text = chunk.toString().trim();
75
94
  stdout += chunk.toString();
95
+ if (text)
96
+ console.log(`[${tag}] ${text.slice(0, 300)}`);
76
97
  });
77
98
  proc.stderr?.on("data", (chunk) => {
78
99
  const text = chunk.toString().trim();
79
100
  if (text) {
80
101
  console.log(`[${tag}] ${text.slice(0, 200)}`);
81
- // Stream Claude's activity to the dashboard
82
- emitEvent(config, job.projectId, "claude_output", {
83
- text,
84
- phase: "BUILD",
85
- });
102
+ emitEvent(config, job.projectId, "claude_output", { text, phase: "BUILD" });
86
103
  }
87
104
  });
88
105
  proc.on("close", (code) => {
@@ -90,9 +107,7 @@ function startSession(job, config, activeSessions) {
90
107
  activeSessions.delete(job.sessionId);
91
108
  // Push commits back to GitHub (if any were made)
92
109
  try {
93
- const hasCommits = (0, child_process_1.execSync)("git log --oneline origin/HEAD..HEAD 2>/dev/null || git log --oneline @{u}..HEAD 2>/dev/null", {
94
- cwd: repoDir, encoding: "utf-8",
95
- }).trim();
110
+ const hasCommits = (0, child_process_1.execSync)("git log --oneline @{u}..HEAD 2>/dev/null || echo ''", { cwd: repoDir, encoding: "utf-8" }).trim();
96
111
  if (hasCommits) {
97
112
  console.log(`[${tag}] Pushing ${hasCommits.split("\n").length} commit(s)...`);
98
113
  (0, child_process_1.execSync)("git push", { cwd: repoDir, stdio: "pipe" });
@@ -102,34 +117,27 @@ function startSession(job, config, activeSessions) {
102
117
  catch (pushErr) {
103
118
  console.error(`[${tag}] Push failed: ${pushErr}`);
104
119
  }
105
- // Try to parse and report results
106
- try {
107
- const jsonStart = stdout.indexOf("{");
108
- const jsonEnd = stdout.lastIndexOf("}");
109
- if (jsonStart !== -1 && jsonEnd > jsonStart) {
110
- const result = JSON.parse(stdout.slice(jsonStart, jsonEnd + 1));
111
- console.log(`[${tag}] Summary: ${result.summary || "No summary"}`);
112
- emitEvent(config, job.projectId, "pipeline_summary", {
113
- sessionId: job.sessionId,
114
- summary: {
115
- sessionId: job.sessionId,
116
- projectName: job.project.name,
117
- duration: "unknown",
118
- tasksCompleted: (result.tasksCompleted || []).map((t) => ({ title: t })),
119
- tasksFailed: [],
120
- findingsCreated: 0,
121
- filesChanged: result.filesChanged || [],
122
- commitMessages: result.commits || [],
123
- },
124
- });
125
- }
126
- }
127
- catch { /* ignore parse errors */ }
128
120
  emitEvent(config, job.projectId, "pipeline_complete", { sessionId: job.sessionId });
129
121
  cleanup(workspaceDir);
130
122
  reportCompletion(config, job.sessionId, code ?? 1);
131
123
  });
132
124
  }
125
+ /**
126
+ * Find the built pipeline-runner script on the host.
127
+ */
128
+ function findRunnerScript() {
129
+ const candidates = [
130
+ // Bundled with the npm package (primary — works for all users)
131
+ (0, path_1.join)(__dirname, "..", "runner", "pipeline-runner", "index.js"),
132
+ // Dev: local Fission repo build
133
+ (0, path_1.join)(__dirname, "..", "..", "..", "..", "dist-runner", "pipeline-runner", "index.js"),
134
+ ];
135
+ for (const candidate of candidates) {
136
+ if ((0, fs_1.existsSync)(candidate))
137
+ return candidate;
138
+ }
139
+ return null;
140
+ }
133
141
  function emitEvent(config, projectId, type, data) {
134
142
  fetch(`${config.fissionUrl}/api/v1/worker/events`, {
135
143
  method: "POST",
@@ -1 +1 @@
1
- {"version":3,"file":"docker.js","sourceRoot":"","sources":["../src/docker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../src/docker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fission-worker",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Fission Worker Agent — polls Fission API for pipeline jobs, runs them in Docker containers",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -8,13 +8,14 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
11
- "prepublishOnly": "npm run build",
11
+ "prepublishOnly": "npm run build && npm run bundle-runner",
12
+ "bundle-runner": "rm -rf runner && mkdir -p runner && cp -r ../../dist-runner/* runner/",
12
13
  "start": "node dist/index.js start",
13
14
  "dev": "tsx src/index.ts start"
14
15
  },
15
16
  "keywords": ["fission", "pipeline", "worker", "docker", "claude"],
16
17
  "license": "MIT",
17
- "files": ["dist", "README.md"],
18
+ "files": ["dist", "runner", "README.md"],
18
19
  "dependencies": {
19
20
  "commander": "^13.0.0",
20
21
  "inquirer": "^12.0.0"
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CurrentUser = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.CurrentUser = (0, common_1.createParamDecorator)((data, ctx) => {
6
+ const request = ctx.switchToHttp().getRequest();
7
+ if (data)
8
+ return request.user?.[data];
9
+ return request.user;
10
+ });
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Public = exports.IS_PUBLIC_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.IS_PUBLIC_KEY = "isPublic";
6
+ const Public = () => (0, common_1.SetMetadata)(exports.IS_PUBLIC_KEY, true);
7
+ exports.Public = Public;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Roles = exports.ROLES_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.ROLES_KEY = "roles";
6
+ const Roles = (...roles) => (0, common_1.SetMetadata)(exports.ROLES_KEY, roles);
7
+ exports.Roles = Roles;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var ActivityLogService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ActivityLogService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const prisma_service_1 = require("../../prisma/prisma.service");
16
+ let ActivityLogService = ActivityLogService_1 = class ActivityLogService {
17
+ prisma;
18
+ logger = new common_1.Logger(ActivityLogService_1.name);
19
+ constructor(prisma) {
20
+ this.prisma = prisma;
21
+ }
22
+ /**
23
+ * Write an audit log entry. Never throws — log failures are swallowed
24
+ * so they cannot disrupt the parent operation.
25
+ */
26
+ async log(action, entityType, entityId, userId, details, ipAddress) {
27
+ try {
28
+ await this.prisma.activityLog.create({
29
+ data: {
30
+ action,
31
+ entityType,
32
+ entityId,
33
+ userId: userId ?? null,
34
+ details: (details ?? undefined),
35
+ ipAddress: ipAddress ?? null,
36
+ },
37
+ });
38
+ }
39
+ catch (error) {
40
+ this.logger.error(`Failed to write activity log [${action} ${entityType}/${entityId}]: ${error}`);
41
+ }
42
+ }
43
+ };
44
+ exports.ActivityLogService = ActivityLogService;
45
+ exports.ActivityLogService = ActivityLogService = ActivityLogService_1 = __decorate([
46
+ (0, common_1.Injectable)(),
47
+ __metadata("design:paramtypes", [prisma_service_1.PrismaService])
48
+ ], ActivityLogService);
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var EventBusService_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.EventBusService = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const rxjs_1 = require("rxjs");
13
+ const operators_1 = require("rxjs/operators");
14
+ let EventBusService = EventBusService_1 = class EventBusService {
15
+ logger = new common_1.Logger(EventBusService_1.name);
16
+ subject = new rxjs_1.Subject();
17
+ /**
18
+ * Emit an event for a project. All SSE subscribers for that project
19
+ * will receive it.
20
+ */
21
+ emit(projectId, type, data) {
22
+ this.logger.debug(`Event [${type}] for project ${projectId}`);
23
+ this.subject.next({ projectId, type, data });
24
+ }
25
+ /**
26
+ * Get an observable stream of MessageEvents filtered to a specific project.
27
+ * Each emitted value conforms to NestJS's MessageEvent interface for SSE.
28
+ */
29
+ getStream(projectId) {
30
+ return this.subject.pipe((0, operators_1.filter)((e) => e.projectId === projectId), (0, operators_1.map)((e) => ({
31
+ data: JSON.stringify({ type: e.type, data: e.data }),
32
+ })));
33
+ }
34
+ };
35
+ exports.EventBusService = EventBusService;
36
+ exports.EventBusService = EventBusService = EventBusService_1 = __decorate([
37
+ (0, common_1.Injectable)()
38
+ ], EventBusService);
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var GithubController_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.GithubController = void 0;
17
+ const common_1 = require("@nestjs/common");
18
+ const throttler_1 = require("@nestjs/throttler");
19
+ const prisma_service_1 = require("../../prisma/prisma.service");
20
+ const public_decorator_1 = require("../../common/decorators/public.decorator");
21
+ const github_service_1 = require("./github.service");
22
+ let GithubController = GithubController_1 = class GithubController {
23
+ github;
24
+ prisma;
25
+ logger = new common_1.Logger(GithubController_1.name);
26
+ constructor(github, prisma) {
27
+ this.github = github;
28
+ this.prisma = prisma;
29
+ }
30
+ /**
31
+ * GET /github/install?projectId=xxx
32
+ * Redirects the user to GitHub to install the Fission Pipeline app.
33
+ * The projectId is passed as state so we can link the installation on callback.
34
+ */
35
+ async install(projectId, res) {
36
+ const url = this.github.getInstallUrl(projectId);
37
+ res.status(302).header("Location", url).send();
38
+ }
39
+ /**
40
+ * GET /github/callback?installation_id=xxx&setup_action=install&state=projectId
41
+ * GitHub redirects here after the user installs the app.
42
+ * We store the installation ID on the project's repositories.
43
+ */
44
+ async callback(installationId, setupAction, state, res) {
45
+ this.logger.log(`GitHub callback: installation_id=${installationId} action=${setupAction} state=${state}`);
46
+ if (setupAction === "install" && installationId && state) {
47
+ const installId = parseInt(installationId, 10);
48
+ const projectId = state;
49
+ // Get the list of repos this installation has access to
50
+ try {
51
+ const repos = await this.github.getInstallationRepos(installId);
52
+ // Update all matching repositories for this project
53
+ const projectRepos = await this.prisma.repository.findMany({
54
+ where: { projectId },
55
+ });
56
+ for (const projRepo of projectRepos) {
57
+ // Match by comparing the repoUrl with the GitHub repo full_name
58
+ const match = repos.find((r) => {
59
+ const fullName = r.full_name.toLowerCase();
60
+ return (projRepo.repoUrl.toLowerCase().includes(fullName) ||
61
+ fullName.includes(projRepo.name.toLowerCase()));
62
+ });
63
+ if (match) {
64
+ const [owner, repo] = match.full_name.split("/");
65
+ await this.prisma.repository.update({
66
+ where: { id: projRepo.id },
67
+ data: {
68
+ githubInstallationId: installId,
69
+ githubOwner: owner,
70
+ githubRepo: repo,
71
+ },
72
+ });
73
+ this.logger.log(`Linked repo ${projRepo.name} → ${match.full_name} (installation: ${installId})`);
74
+ }
75
+ }
76
+ // If no existing repos matched, store installation on all project repos
77
+ if (projectRepos.length > 0) {
78
+ await this.prisma.repository.updateMany({
79
+ where: { projectId, githubInstallationId: null },
80
+ data: { githubInstallationId: installId },
81
+ });
82
+ }
83
+ }
84
+ catch (err) {
85
+ this.logger.error(`Failed to process GitHub installation: ${err}`);
86
+ }
87
+ }
88
+ // Redirect back to the project page in the dashboard
89
+ const redirectUrl = state
90
+ ? `/projects/${state}`
91
+ : "/projects";
92
+ res.status(302).header("Location", redirectUrl).send();
93
+ }
94
+ /**
95
+ * GET /github/repos/:installationId
96
+ * List repositories accessible to a GitHub App installation.
97
+ */
98
+ async listRepos(installationId) {
99
+ const repos = await this.github.getInstallationRepos(parseInt(installationId, 10));
100
+ return {
101
+ data: repos.map((r) => ({
102
+ fullName: r.full_name,
103
+ private: r.private,
104
+ cloneUrl: r.clone_url,
105
+ defaultBranch: r.default_branch,
106
+ })),
107
+ };
108
+ }
109
+ /**
110
+ * GET /github/status
111
+ * Check if GitHub App is configured.
112
+ */
113
+ async status() {
114
+ return { data: { configured: this.github.isConfigured() } };
115
+ }
116
+ };
117
+ exports.GithubController = GithubController;
118
+ __decorate([
119
+ (0, common_1.Get)("install"),
120
+ __param(0, (0, common_1.Query)("projectId")),
121
+ __param(1, (0, common_1.Res)()),
122
+ __metadata("design:type", Function),
123
+ __metadata("design:paramtypes", [String, Object]),
124
+ __metadata("design:returntype", Promise)
125
+ ], GithubController.prototype, "install", null);
126
+ __decorate([
127
+ (0, common_1.Get)("callback"),
128
+ __param(0, (0, common_1.Query)("installation_id")),
129
+ __param(1, (0, common_1.Query)("setup_action")),
130
+ __param(2, (0, common_1.Query)("state")),
131
+ __param(3, (0, common_1.Res)()),
132
+ __metadata("design:type", Function),
133
+ __metadata("design:paramtypes", [String, String, String, Object]),
134
+ __metadata("design:returntype", Promise)
135
+ ], GithubController.prototype, "callback", null);
136
+ __decorate([
137
+ (0, common_1.Get)("repos/:installationId"),
138
+ __param(0, (0, common_1.Param)("installationId")),
139
+ __metadata("design:type", Function),
140
+ __metadata("design:paramtypes", [String]),
141
+ __metadata("design:returntype", Promise)
142
+ ], GithubController.prototype, "listRepos", null);
143
+ __decorate([
144
+ (0, common_1.Get)("status"),
145
+ __metadata("design:type", Function),
146
+ __metadata("design:paramtypes", []),
147
+ __metadata("design:returntype", Promise)
148
+ ], GithubController.prototype, "status", null);
149
+ exports.GithubController = GithubController = GithubController_1 = __decorate([
150
+ (0, common_1.Controller)("github"),
151
+ (0, public_decorator_1.Public)(),
152
+ (0, throttler_1.SkipThrottle)({ global: true }),
153
+ __metadata("design:paramtypes", [github_service_1.GithubService,
154
+ prisma_service_1.PrismaService])
155
+ ], GithubController);
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.GithubModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const github_service_1 = require("./github.service");
12
+ const github_controller_1 = require("./github.controller");
13
+ let GithubModule = class GithubModule {
14
+ };
15
+ exports.GithubModule = GithubModule;
16
+ exports.GithubModule = GithubModule = __decorate([
17
+ (0, common_1.Module)({
18
+ controllers: [github_controller_1.GithubController],
19
+ providers: [github_service_1.GithubService],
20
+ exports: [github_service_1.GithubService],
21
+ })
22
+ ], GithubModule);
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var GithubService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.GithubService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const config_1 = require("@nestjs/config");
16
+ const fs_1 = require("fs");
17
+ const jsonwebtoken_1 = require("jsonwebtoken");
18
+ let GithubService = GithubService_1 = class GithubService {
19
+ config;
20
+ logger = new common_1.Logger(GithubService_1.name);
21
+ appId;
22
+ privateKey;
23
+ constructor(config) {
24
+ this.config = config;
25
+ this.appId = this.config.get("GITHUB_APP_ID", "");
26
+ const keyPath = this.config.get("GITHUB_PRIVATE_KEY_PATH", "");
27
+ try {
28
+ this.privateKey = keyPath ? (0, fs_1.readFileSync)(keyPath, "utf-8") : "";
29
+ }
30
+ catch {
31
+ this.logger.warn("GitHub App private key not found — GitHub integration disabled");
32
+ this.privateKey = "";
33
+ }
34
+ }
35
+ /** Whether the GitHub App is configured. */
36
+ isConfigured() {
37
+ return !!(this.appId && this.privateKey);
38
+ }
39
+ /**
40
+ * Generate a JWT for authenticating as the GitHub App.
41
+ * Valid for 10 minutes (GitHub max).
42
+ */
43
+ generateAppJwt() {
44
+ const now = Math.floor(Date.now() / 1000);
45
+ return (0, jsonwebtoken_1.sign)({
46
+ iat: now - 60, // 60 seconds in the past for clock drift
47
+ exp: now + 10 * 60, // 10 minutes
48
+ iss: this.appId,
49
+ }, this.privateKey, { algorithm: "RS256" });
50
+ }
51
+ /**
52
+ * Generate a short-lived installation access token for a specific installation.
53
+ * This token can clone/push to repos the installation has access to.
54
+ */
55
+ async getInstallationToken(installationId) {
56
+ const jwt = this.generateAppJwt();
57
+ const res = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
58
+ method: "POST",
59
+ headers: {
60
+ Authorization: `Bearer ${jwt}`,
61
+ Accept: "application/vnd.github+json",
62
+ "X-GitHub-Api-Version": "2022-11-28",
63
+ },
64
+ });
65
+ if (!res.ok) {
66
+ const body = await res.text();
67
+ throw new Error(`GitHub installation token failed: ${res.status} ${body}`);
68
+ }
69
+ const data = (await res.json());
70
+ return data.token;
71
+ }
72
+ /**
73
+ * List repositories accessible to an installation.
74
+ */
75
+ async getInstallationRepos(installationId) {
76
+ const token = await this.getInstallationToken(installationId);
77
+ const res = await fetch("https://api.github.com/installation/repositories?per_page=100", {
78
+ headers: {
79
+ Authorization: `Bearer ${token}`,
80
+ Accept: "application/vnd.github+json",
81
+ "X-GitHub-Api-Version": "2022-11-28",
82
+ },
83
+ });
84
+ if (!res.ok) {
85
+ const body = await res.text();
86
+ throw new Error(`GitHub list repos failed: ${res.status} ${body}`);
87
+ }
88
+ const data = (await res.json());
89
+ return data.repositories;
90
+ }
91
+ /**
92
+ * Get the installation URL for the GitHub App.
93
+ * Redirect users here to install the app on their repos.
94
+ */
95
+ getInstallUrl(state) {
96
+ const base = `https://github.com/apps/fission-pipeline/installations/new`;
97
+ return state ? `${base}?state=${encodeURIComponent(state)}` : base;
98
+ }
99
+ };
100
+ exports.GithubService = GithubService;
101
+ exports.GithubService = GithubService = GithubService_1 = __decorate([
102
+ (0, common_1.Injectable)(),
103
+ __metadata("design:paramtypes", [config_1.ConfigService])
104
+ ], GithubService);