hub-launch 1.0.3
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/CHANGELOG.md +144 -0
- package/README.md +195 -0
- package/bin/hublaunch +20 -0
- package/dist/commands/checkout.d.ts +7 -0
- package/dist/commands/checkout.d.ts.map +1 -0
- package/dist/commands/checkout.js +164 -0
- package/dist/commands/checkout.js.map +1 -0
- package/dist/commands/clean.d.ts +7 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +206 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/close.d.ts +7 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +109 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +367 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/diff.d.ts +7 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +98 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/execute.d.ts +7 -0
- package/dist/commands/execute.d.ts.map +1 -0
- package/dist/commands/execute.js +376 -0
- package/dist/commands/execute.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1243 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/interactive.d.ts +12 -0
- package/dist/commands/interactive.d.ts.map +1 -0
- package/dist/commands/interactive.js +1071 -0
- package/dist/commands/interactive.js.map +1 -0
- package/dist/commands/launch.d.ts +16 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +684 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/login.d.ts +40 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +469 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +22 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +105 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/merge.d.ts +7 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +240 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/preview.d.ts +7 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +160 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/project.d.ts +18 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +352 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/respond.d.ts +7 -0
- package/dist/commands/respond.d.ts.map +1 -0
- package/dist/commands/respond.js +112 -0
- package/dist/commands/respond.js.map +1 -0
- package/dist/commands/setPriority.d.ts +7 -0
- package/dist/commands/setPriority.d.ts.map +1 -0
- package/dist/commands/setPriority.js +112 -0
- package/dist/commands/setPriority.js.map +1 -0
- package/dist/commands/track.d.ts +7 -0
- package/dist/commands/track.d.ts.map +1 -0
- package/dist/commands/track.js +140 -0
- package/dist/commands/track.js.map +1 -0
- package/dist/commands/upload.d.ts +21 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +145 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/commands/view.d.ts +7 -0
- package/dist/commands/view.d.ts.map +1 -0
- package/dist/commands/view.js +168 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/commands/watch.d.ts +7 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +438 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/worktree.d.ts +7 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +122 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/config/command-names.d.ts +16 -0
- package/dist/config/command-names.d.ts.map +1 -0
- package/dist/config/command-names.js +17 -0
- package/dist/config/command-names.js.map +1 -0
- package/dist/config/constants.d.ts +12 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +12 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/index.d.ts +47 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +175 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/services/api/HulaApiClient.d.ts +170 -0
- package/dist/services/api/HulaApiClient.d.ts.map +1 -0
- package/dist/services/api/HulaApiClient.js +233 -0
- package/dist/services/api/HulaApiClient.js.map +1 -0
- package/dist/services/diff/DiffService.d.ts +44 -0
- package/dist/services/diff/DiffService.d.ts.map +1 -0
- package/dist/services/diff/DiffService.js +126 -0
- package/dist/services/diff/DiffService.js.map +1 -0
- package/dist/services/editor/EditorService.d.ts +53 -0
- package/dist/services/editor/EditorService.d.ts.map +1 -0
- package/dist/services/editor/EditorService.js +161 -0
- package/dist/services/editor/EditorService.js.map +1 -0
- package/dist/services/git/GitService.d.ts +124 -0
- package/dist/services/git/GitService.d.ts.map +1 -0
- package/dist/services/git/GitService.js +423 -0
- package/dist/services/git/GitService.js.map +1 -0
- package/dist/services/git/WorktreeService.d.ts +144 -0
- package/dist/services/git/WorktreeService.d.ts.map +1 -0
- package/dist/services/git/WorktreeService.js +505 -0
- package/dist/services/git/WorktreeService.js.map +1 -0
- package/dist/services/github/ActionsService.d.ts +66 -0
- package/dist/services/github/ActionsService.d.ts.map +1 -0
- package/dist/services/github/ActionsService.js +114 -0
- package/dist/services/github/ActionsService.js.map +1 -0
- package/dist/services/github/AssetUploadService.d.ts +103 -0
- package/dist/services/github/AssetUploadService.d.ts.map +1 -0
- package/dist/services/github/AssetUploadService.js +299 -0
- package/dist/services/github/AssetUploadService.js.map +1 -0
- package/dist/services/github/BatchCopilotDetectionService.d.ts +58 -0
- package/dist/services/github/BatchCopilotDetectionService.d.ts.map +1 -0
- package/dist/services/github/BatchCopilotDetectionService.js +321 -0
- package/dist/services/github/BatchCopilotDetectionService.js.map +1 -0
- package/dist/services/github/CopilotDetectionService.d.ts +40 -0
- package/dist/services/github/CopilotDetectionService.d.ts.map +1 -0
- package/dist/services/github/CopilotDetectionService.js +280 -0
- package/dist/services/github/CopilotDetectionService.js.map +1 -0
- package/dist/services/github/CopilotService.d.ts +27 -0
- package/dist/services/github/CopilotService.d.ts.map +1 -0
- package/dist/services/github/CopilotService.js +249 -0
- package/dist/services/github/CopilotService.js.map +1 -0
- package/dist/services/github/DeploymentDetectionService.d.ts +38 -0
- package/dist/services/github/DeploymentDetectionService.d.ts.map +1 -0
- package/dist/services/github/DeploymentDetectionService.js +104 -0
- package/dist/services/github/DeploymentDetectionService.js.map +1 -0
- package/dist/services/github/GithubAppService.d.ts +40 -0
- package/dist/services/github/GithubAppService.d.ts.map +1 -0
- package/dist/services/github/GithubAppService.js +187 -0
- package/dist/services/github/GithubAppService.js.map +1 -0
- package/dist/services/github/IssueService.d.ts +65 -0
- package/dist/services/github/IssueService.d.ts.map +1 -0
- package/dist/services/github/IssueService.js +231 -0
- package/dist/services/github/IssueService.js.map +1 -0
- package/dist/services/github/LabelService.d.ts +36 -0
- package/dist/services/github/LabelService.d.ts.map +1 -0
- package/dist/services/github/LabelService.js +122 -0
- package/dist/services/github/LabelService.js.map +1 -0
- package/dist/services/github/PRService.d.ts +77 -0
- package/dist/services/github/PRService.d.ts.map +1 -0
- package/dist/services/github/PRService.js +287 -0
- package/dist/services/github/PRService.js.map +1 -0
- package/dist/services/github/ProjectService.d.ts +144 -0
- package/dist/services/github/ProjectService.d.ts.map +1 -0
- package/dist/services/github/ProjectService.js +804 -0
- package/dist/services/github/ProjectService.js.map +1 -0
- package/dist/services/hooks/HookExecutor.d.ts +66 -0
- package/dist/services/hooks/HookExecutor.d.ts.map +1 -0
- package/dist/services/hooks/HookExecutor.js +227 -0
- package/dist/services/hooks/HookExecutor.js.map +1 -0
- package/dist/services/hooks/index.d.ts +2 -0
- package/dist/services/hooks/index.d.ts.map +1 -0
- package/dist/services/hooks/index.js +2 -0
- package/dist/services/hooks/index.js.map +1 -0
- package/dist/services/logs/LogFormatterService.d.ts +17 -0
- package/dist/services/logs/LogFormatterService.d.ts.map +1 -0
- package/dist/services/logs/LogFormatterService.js +35 -0
- package/dist/services/logs/LogFormatterService.js.map +1 -0
- package/dist/services/logs/TempFileService.d.ts +27 -0
- package/dist/services/logs/TempFileService.d.ts.map +1 -0
- package/dist/services/logs/TempFileService.js +90 -0
- package/dist/services/logs/TempFileService.js.map +1 -0
- package/dist/services/logs/index.d.ts +3 -0
- package/dist/services/logs/index.d.ts.map +1 -0
- package/dist/services/logs/index.js +4 -0
- package/dist/services/logs/index.js.map +1 -0
- package/dist/services/plan/PlanService.d.ts +70 -0
- package/dist/services/plan/PlanService.d.ts.map +1 -0
- package/dist/services/plan/PlanService.js +211 -0
- package/dist/services/plan/PlanService.js.map +1 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.d.ts +16 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.js +87 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.js.map +1 -0
- package/dist/services/polling/HulaServerPollingAdapter.d.ts +23 -0
- package/dist/services/polling/HulaServerPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/HulaServerPollingAdapter.js +78 -0
- package/dist/services/polling/HulaServerPollingAdapter.js.map +1 -0
- package/dist/services/polling/IStatusPollingAdapter.d.ts +29 -0
- package/dist/services/polling/IStatusPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/IStatusPollingAdapter.js +2 -0
- package/dist/services/polling/IStatusPollingAdapter.js.map +1 -0
- package/dist/services/polling/StatusPollingAdapterFactory.d.ts +14 -0
- package/dist/services/polling/StatusPollingAdapterFactory.d.ts.map +1 -0
- package/dist/services/polling/StatusPollingAdapterFactory.js +41 -0
- package/dist/services/polling/StatusPollingAdapterFactory.js.map +1 -0
- package/dist/services/polling/index.d.ts +5 -0
- package/dist/services/polling/index.d.ts.map +1 -0
- package/dist/services/polling/index.js +5 -0
- package/dist/services/polling/index.js.map +1 -0
- package/dist/services/preview/PreviewService.d.ts +86 -0
- package/dist/services/preview/PreviewService.d.ts.map +1 -0
- package/dist/services/preview/PreviewService.js +439 -0
- package/dist/services/preview/PreviewService.js.map +1 -0
- package/dist/services/tracking/ProjectTrackingAdapter.d.ts +41 -0
- package/dist/services/tracking/ProjectTrackingAdapter.d.ts.map +1 -0
- package/dist/services/tracking/ProjectTrackingAdapter.js +230 -0
- package/dist/services/tracking/ProjectTrackingAdapter.js.map +1 -0
- package/dist/services/tracking/RemoteTrackingAdapter.d.ts +62 -0
- package/dist/services/tracking/RemoteTrackingAdapter.d.ts.map +1 -0
- package/dist/services/tracking/RemoteTrackingAdapter.js +242 -0
- package/dist/services/tracking/RemoteTrackingAdapter.js.map +1 -0
- package/dist/services/tracking/TrackingAdapterFactory.d.ts +25 -0
- package/dist/services/tracking/TrackingAdapterFactory.d.ts.map +1 -0
- package/dist/services/tracking/TrackingAdapterFactory.js +55 -0
- package/dist/services/tracking/TrackingAdapterFactory.js.map +1 -0
- package/dist/services/tracking/TrackingService.d.ts +104 -0
- package/dist/services/tracking/TrackingService.d.ts.map +1 -0
- package/dist/services/tracking/TrackingService.js +256 -0
- package/dist/services/tracking/TrackingService.js.map +1 -0
- package/dist/services/tracking/UntrackedIssuesService.d.ts +20 -0
- package/dist/services/tracking/UntrackedIssuesService.d.ts.map +1 -0
- package/dist/services/tracking/UntrackedIssuesService.js +148 -0
- package/dist/services/tracking/UntrackedIssuesService.js.map +1 -0
- package/dist/services/tracking/local-file-migration.d.ts +16 -0
- package/dist/services/tracking/local-file-migration.d.ts.map +1 -0
- package/dist/services/tracking/local-file-migration.js +86 -0
- package/dist/services/tracking/local-file-migration.js.map +1 -0
- package/dist/templates/planning-instructions.md +752 -0
- package/dist/templates/proceed-instructions.md +453 -0
- package/dist/templates/scripts/hula-fix-commit.sh +110 -0
- package/dist/templates/scripts/hula-fix-setup.sh +145 -0
- package/dist/templates/scripts/hula-launch-run.sh +89 -0
- package/dist/templates/scripts/hula-merge-local.sh +138 -0
- package/dist/templates/scripts/hula-merge-remote.sh +74 -0
- package/dist/templates/scripts/hula-read-config.sh +38 -0
- package/dist/templates/scripts/hula-verify-gather.sh +135 -0
- package/dist/templates/scripts/hula-verify-post.sh +84 -0
- package/dist/templates/skills/hula-confirm/SKILL.md +361 -0
- package/dist/templates/skills/hula-create/SKILL.md +211 -0
- package/dist/templates/skills/hula-fix/SKILL.md +224 -0
- package/dist/templates/skills/hula-launch/SKILL.md +159 -0
- package/dist/templates/skills/hula-log/SKILL.md +105 -0
- package/dist/templates/skills/hula-merge/SKILL.md +387 -0
- package/dist/templates/skills/hula-plan/SKILL.md +148 -0
- package/dist/templates/skills/hula-upload/SKILL.md +115 -0
- package/dist/templates/skills/hula-verify/SKILL.md +754 -0
- package/dist/types/common.schema.d.ts +46 -0
- package/dist/types/common.schema.d.ts.map +1 -0
- package/dist/types/common.schema.js +60 -0
- package/dist/types/common.schema.js.map +1 -0
- package/dist/types/config.schema.d.ts +443 -0
- package/dist/types/config.schema.d.ts.map +1 -0
- package/dist/types/config.schema.js +126 -0
- package/dist/types/config.schema.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/issue.schema.d.ts +136 -0
- package/dist/types/issue.schema.d.ts.map +1 -0
- package/dist/types/issue.schema.js +49 -0
- package/dist/types/issue.schema.js.map +1 -0
- package/dist/types/pr.schema.d.ts +118 -0
- package/dist/types/pr.schema.d.ts.map +1 -0
- package/dist/types/pr.schema.js +40 -0
- package/dist/types/pr.schema.js.map +1 -0
- package/dist/types/tracking.schema.d.ts +340 -0
- package/dist/types/tracking.schema.d.ts.map +1 -0
- package/dist/types/tracking.schema.js +56 -0
- package/dist/types/tracking.schema.js.map +1 -0
- package/dist/utils/browser-launcher.d.ts +60 -0
- package/dist/utils/browser-launcher.d.ts.map +1 -0
- package/dist/utils/browser-launcher.js +263 -0
- package/dist/utils/browser-launcher.js.map +1 -0
- package/dist/utils/cache.d.ts +44 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +94 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config-parser.d.ts +16 -0
- package/dist/utils/config-parser.d.ts.map +1 -0
- package/dist/utils/config-parser.js +210 -0
- package/dist/utils/config-parser.js.map +1 -0
- package/dist/utils/editor.d.ts +21 -0
- package/dist/utils/editor.d.ts.map +1 -0
- package/dist/utils/editor.js +213 -0
- package/dist/utils/editor.js.map +1 -0
- package/dist/utils/git-utils.d.ts +5 -0
- package/dist/utils/git-utils.d.ts.map +1 -0
- package/dist/utils/git-utils.js +18 -0
- package/dist/utils/git-utils.js.map +1 -0
- package/dist/utils/github-cli.d.ts +62 -0
- package/dist/utils/github-cli.d.ts.map +1 -0
- package/dist/utils/github-cli.js +492 -0
- package/dist/utils/github-cli.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +118 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/open-url.d.ts +19 -0
- package/dist/utils/open-url.d.ts.map +1 -0
- package/dist/utils/open-url.js +37 -0
- package/dist/utils/open-url.js.map +1 -0
- package/dist/utils/project-resolver.d.ts +13 -0
- package/dist/utils/project-resolver.d.ts.map +1 -0
- package/dist/utils/project-resolver.js +35 -0
- package/dist/utils/project-resolver.js.map +1 -0
- package/dist/utils/prompts.d.ts +80 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +326 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/shell.d.ts +54 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +152 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/validators.d.ts +32 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +64 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions workflow run status
|
|
3
|
+
*/
|
|
4
|
+
export type WorkflowRunStatus = "queued" | "in_progress" | "completed" | "waiting";
|
|
5
|
+
/**
|
|
6
|
+
* GitHub Actions workflow run conclusion
|
|
7
|
+
*/
|
|
8
|
+
export type WorkflowRunConclusion = "success" | "failure" | "cancelled" | "skipped" | "timed_out" | "action_required" | null;
|
|
9
|
+
/**
|
|
10
|
+
* Workflow run result
|
|
11
|
+
*/
|
|
12
|
+
export interface WorkflowRun {
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
status: WorkflowRunStatus;
|
|
16
|
+
conclusion: WorkflowRunConclusion;
|
|
17
|
+
html_url: string;
|
|
18
|
+
created_at: string;
|
|
19
|
+
updated_at: string;
|
|
20
|
+
run_started_at?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Test result summary
|
|
24
|
+
*/
|
|
25
|
+
export interface TestResult {
|
|
26
|
+
passed: boolean;
|
|
27
|
+
duration: number;
|
|
28
|
+
workflowUrl: string;
|
|
29
|
+
workflowRunId: number;
|
|
30
|
+
logs?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Service for GitHub Actions operations
|
|
34
|
+
* Single Responsibility: Monitor and interact with GitHub Actions workflows
|
|
35
|
+
*/
|
|
36
|
+
export declare class ActionsService {
|
|
37
|
+
/**
|
|
38
|
+
* Get workflow runs for a specific PR
|
|
39
|
+
* Note: Fetches recent workflow runs and filters by time in getLatestWorkflowRunAfter().
|
|
40
|
+
* Supports workflows triggered by both pull_request and issue_comment events.
|
|
41
|
+
*/
|
|
42
|
+
getWorkflowRunsForPR(_prNumber: number): Promise<WorkflowRun[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific workflow run by ID
|
|
45
|
+
*/
|
|
46
|
+
getWorkflowRun(runId: number): Promise<WorkflowRun>;
|
|
47
|
+
/**
|
|
48
|
+
* Get the latest workflow run for a PR after a specific time
|
|
49
|
+
* Filters for "Run Test" workflow to avoid picking up other workflows
|
|
50
|
+
*/
|
|
51
|
+
getLatestWorkflowRunAfter(prNumber: number, afterTime: Date): Promise<WorkflowRun | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Poll for workflow completion
|
|
54
|
+
* Returns the completed workflow run or null if timeout
|
|
55
|
+
*/
|
|
56
|
+
pollForCompletion(runId: number, timeoutSeconds?: number, pollIntervalSeconds?: number): Promise<WorkflowRun | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Get workflow run logs (simplified - actual implementation would need job ID)
|
|
59
|
+
*/
|
|
60
|
+
getWorkflowLogs(runId: number): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Convert workflow run to test result
|
|
63
|
+
*/
|
|
64
|
+
convertToTestResult(run: WorkflowRun): Promise<TestResult>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=ActionsService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionsService.d.ts","sourceRoot":"","sources":["../../../src/services/github/ActionsService.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,SAAS,GACT,WAAW,GACX,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,IAAI,CAAC;AAET;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,qBAAqB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACG,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAYrE;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAUzD;;;OAGG;IACG,yBAAyB,CAC7B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAuB9B;;;OAGG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,cAAc,GAAE,MAAY,EAC5B,mBAAmB,GAAE,MAAW,GAC/B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAsC9B;;OAEG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcrD;;OAEG;IACG,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAqBjE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-inferrable-types */
|
|
2
|
+
import { ghApi, getRepoInfo } from "../../utils/github-cli.js";
|
|
3
|
+
import { logger } from "../../utils/logger.js";
|
|
4
|
+
/**
|
|
5
|
+
* Service for GitHub Actions operations
|
|
6
|
+
* Single Responsibility: Monitor and interact with GitHub Actions workflows
|
|
7
|
+
*/
|
|
8
|
+
export class ActionsService {
|
|
9
|
+
/**
|
|
10
|
+
* Get workflow runs for a specific PR
|
|
11
|
+
* Note: Fetches recent workflow runs and filters by time in getLatestWorkflowRunAfter().
|
|
12
|
+
* Supports workflows triggered by both pull_request and issue_comment events.
|
|
13
|
+
*/
|
|
14
|
+
async getWorkflowRunsForPR(_prNumber) {
|
|
15
|
+
const repo = await getRepoInfo();
|
|
16
|
+
// Get recent workflow runs with pagination limit to avoid buffer overflow
|
|
17
|
+
// No event filter to support both pull_request and issue_comment triggers
|
|
18
|
+
const response = await ghApi(`/repos/${repo.owner}/${repo.name}/actions/runs?per_page=30`);
|
|
19
|
+
return response.workflow_runs || [];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get a specific workflow run by ID
|
|
23
|
+
*/
|
|
24
|
+
async getWorkflowRun(runId) {
|
|
25
|
+
const repo = await getRepoInfo();
|
|
26
|
+
const response = await ghApi(`/repos/${repo.owner}/${repo.name}/actions/runs/${runId}`);
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the latest workflow run for a PR after a specific time
|
|
31
|
+
* Filters for "Run Test" workflow to avoid picking up other workflows
|
|
32
|
+
*/
|
|
33
|
+
async getLatestWorkflowRunAfter(prNumber, afterTime) {
|
|
34
|
+
const runs = await this.getWorkflowRunsForPR(prNumber);
|
|
35
|
+
// Find the most recent "Run Test" workflow run after the specified time
|
|
36
|
+
const recentRuns = runs.filter((run) => {
|
|
37
|
+
const createdAt = new Date(run.created_at);
|
|
38
|
+
const isRunTestWorkflow = run.name === "Run Test";
|
|
39
|
+
return createdAt > afterTime && isRunTestWorkflow;
|
|
40
|
+
});
|
|
41
|
+
if (recentRuns.length === 0) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
// Sort by created_at descending and return the first one
|
|
45
|
+
recentRuns.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
46
|
+
return recentRuns[0] ?? null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Poll for workflow completion
|
|
50
|
+
* Returns the completed workflow run or null if timeout
|
|
51
|
+
*/
|
|
52
|
+
async pollForCompletion(runId, timeoutSeconds = 120, pollIntervalSeconds = 10) {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
const pollIntervalMs = pollIntervalSeconds * 1000;
|
|
55
|
+
process.stdout.write(`⏳ Waiting for workflow run #${runId} to complete...\n`);
|
|
56
|
+
while (Date.now() - startTime < timeoutSeconds * 1000) {
|
|
57
|
+
const run = await this.getWorkflowRun(runId);
|
|
58
|
+
if (run.status === "completed") {
|
|
59
|
+
// Clear the status line and show completion message
|
|
60
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
61
|
+
logger.success(`✓ Workflow completed with conclusion: ${run.conclusion}`);
|
|
62
|
+
return run;
|
|
63
|
+
}
|
|
64
|
+
const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
|
|
65
|
+
// Update the same line with current status
|
|
66
|
+
process.stdout.write(`\r⏳ Workflow status: ${run.status} (${elapsedSeconds}s elapsed)` +
|
|
67
|
+
" ".repeat(20));
|
|
68
|
+
// Wait before next poll
|
|
69
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
70
|
+
}
|
|
71
|
+
// Clear the status line before showing timeout message
|
|
72
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
73
|
+
logger.warning(`⏱️ Timeout waiting for workflow completion`);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get workflow run logs (simplified - actual implementation would need job ID)
|
|
78
|
+
*/
|
|
79
|
+
async getWorkflowLogs(runId) {
|
|
80
|
+
const repo = await getRepoInfo();
|
|
81
|
+
try {
|
|
82
|
+
// This would download logs as a zip file in real implementation
|
|
83
|
+
// For now, we'll return a placeholder
|
|
84
|
+
logger.debug(`Fetching logs for workflow run #${runId}`);
|
|
85
|
+
return `Logs for workflow run #${runId}\nView full logs at: https://github.com/${repo.owner}/${repo.name}/actions/runs/${runId}`;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
logger.debug(`Could not fetch logs: ${String(error)}`);
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Convert workflow run to test result
|
|
94
|
+
*/
|
|
95
|
+
async convertToTestResult(run) {
|
|
96
|
+
const passed = run.conclusion === "success";
|
|
97
|
+
// Calculate duration
|
|
98
|
+
const startTime = run.run_started_at
|
|
99
|
+
? new Date(run.run_started_at)
|
|
100
|
+
: new Date(run.created_at);
|
|
101
|
+
const endTime = new Date(run.updated_at);
|
|
102
|
+
const durationMs = endTime.getTime() - startTime.getTime();
|
|
103
|
+
const durationSeconds = Math.floor(durationMs / 1000);
|
|
104
|
+
const logs = await this.getWorkflowLogs(run.id);
|
|
105
|
+
return {
|
|
106
|
+
passed,
|
|
107
|
+
duration: durationSeconds,
|
|
108
|
+
workflowUrl: run.html_url,
|
|
109
|
+
workflowRunId: run.id,
|
|
110
|
+
logs,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=ActionsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionsService.js","sourceRoot":"","sources":["../../../src/services/github/ActionsService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of uploading a single image asset.
|
|
3
|
+
*/
|
|
4
|
+
export interface UploadedAsset {
|
|
5
|
+
/** Original local file path */
|
|
6
|
+
localPath: string;
|
|
7
|
+
/** The filename (e.g., "screenshot.png") */
|
|
8
|
+
filename: string;
|
|
9
|
+
/** The GitHub URL for the uploaded image */
|
|
10
|
+
url: string;
|
|
11
|
+
/** Markdown image reference:  */
|
|
12
|
+
markdown: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Service for uploading image assets to GitHub via the REST API.
|
|
16
|
+
*
|
|
17
|
+
* Uploads images to a dedicated `hublaunch-assets` branch in the
|
|
18
|
+
* repository using the Git database API (blobs, trees, commits).
|
|
19
|
+
* This works with the standard OAuth token from `gh auth` / `hula login`
|
|
20
|
+
* and supports files up to 100 MB.
|
|
21
|
+
*
|
|
22
|
+
* The resulting raw.githubusercontent.com URLs inherit the repository's
|
|
23
|
+
* visibility — images on private repos require authentication to view.
|
|
24
|
+
*/
|
|
25
|
+
export declare class AssetUploadService {
|
|
26
|
+
private token;
|
|
27
|
+
private repoInfo;
|
|
28
|
+
private assetsBranchReady;
|
|
29
|
+
/**
|
|
30
|
+
* Get the GitHub auth token from the gh CLI.
|
|
31
|
+
*/
|
|
32
|
+
private getToken;
|
|
33
|
+
/**
|
|
34
|
+
* Get repo info (cached).
|
|
35
|
+
*/
|
|
36
|
+
private getRepo;
|
|
37
|
+
/**
|
|
38
|
+
* Make an authenticated GitHub API request.
|
|
39
|
+
*/
|
|
40
|
+
private githubApi;
|
|
41
|
+
/**
|
|
42
|
+
* Ensure the assets branch exists in the remote. Creates it from the
|
|
43
|
+
* default branch if it doesn't exist yet.
|
|
44
|
+
*/
|
|
45
|
+
private ensureAssetsBranch;
|
|
46
|
+
/**
|
|
47
|
+
* Validate that a file path points to a supported image file.
|
|
48
|
+
* Returns the resolved absolute path, or throws with a descriptive error.
|
|
49
|
+
*/
|
|
50
|
+
validateImageFile(filePath: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Upload a single image file via the GitHub Git database API.
|
|
53
|
+
*
|
|
54
|
+
* Uses the blob → tree → commit → ref update flow to support
|
|
55
|
+
* files up to 100 MB without command-line length limits.
|
|
56
|
+
*
|
|
57
|
+
* @param filePath - Absolute or relative path to the image file
|
|
58
|
+
* @returns UploadedAsset with the GitHub URL and markdown reference
|
|
59
|
+
*/
|
|
60
|
+
uploadImage(filePath: string): Promise<UploadedAsset>;
|
|
61
|
+
/**
|
|
62
|
+
* Upload multiple image files.
|
|
63
|
+
*
|
|
64
|
+
* @param filePaths - Array of file paths to upload
|
|
65
|
+
* @returns Array of UploadedAsset results (one per file)
|
|
66
|
+
*/
|
|
67
|
+
uploadImages(filePaths: string[]): Promise<UploadedAsset[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Scan a markdown body for local image references and return the
|
|
70
|
+
* file paths that need uploading.
|
|
71
|
+
*
|
|
72
|
+
* Detects patterns like:
|
|
73
|
+
* - 
|
|
74
|
+
* - 
|
|
75
|
+
* - 
|
|
76
|
+
* - 
|
|
77
|
+
*
|
|
78
|
+
* Only matches paths that:
|
|
79
|
+
* 1. Have a supported image extension
|
|
80
|
+
* 2. Do NOT start with http:// or https:// (those are already URLs)
|
|
81
|
+
* 3. Point to files that exist on disk
|
|
82
|
+
*
|
|
83
|
+
* @param body - The markdown content to scan
|
|
84
|
+
* @param basePath - The directory to resolve relative paths against
|
|
85
|
+
* (typically the directory containing the plan file)
|
|
86
|
+
* @returns Array of { match, localPath, altText } for each local image found
|
|
87
|
+
*/
|
|
88
|
+
scanForLocalImages(body: string, basePath: string): Array<{
|
|
89
|
+
match: string;
|
|
90
|
+
localPath: string;
|
|
91
|
+
altText: string;
|
|
92
|
+
}>;
|
|
93
|
+
/**
|
|
94
|
+
* Process a plan body: find local image references, upload them,
|
|
95
|
+
* and replace the local paths with GitHub URLs.
|
|
96
|
+
*
|
|
97
|
+
* @param body - The plan markdown body
|
|
98
|
+
* @param basePath - Directory to resolve relative image paths against
|
|
99
|
+
* @returns The updated body with local image paths replaced by GitHub URLs
|
|
100
|
+
*/
|
|
101
|
+
resolveLocalImages(body: string, basePath: string): Promise<string>;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=AssetUploadService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetUploadService.d.ts","sourceRoot":"","sources":["../../../src/services/github/AssetUploadService.ts"],"names":[],"mappings":"AAwBA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAgD;IAChE,OAAO,CAAC,iBAAiB,CAAS;IAElC;;OAEG;YACW,QAAQ;IAYtB;;OAEG;YACW,OAAO;IAMrB;;OAEG;YACW,SAAS;IAqBvB;;;OAGG;YACW,kBAAkB;IAqChC;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAgC3C;;;;;;;;OAQG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA8E3D;;;;;OAKG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAuBjE;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAiD/D;;;;;;;OAOG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAkC1E"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { existsSync, statSync, readFileSync } from 'fs';
|
|
2
|
+
import { basename, extname, resolve, isAbsolute } from 'path';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { runCommand } from '../../utils/shell.js';
|
|
5
|
+
import { getRepoInfo } from '../../utils/github-cli.js';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
/**
|
|
8
|
+
* Supported image extensions for attachment uploads.
|
|
9
|
+
* Only image formats are allowed (screenshots use case).
|
|
10
|
+
*/
|
|
11
|
+
const SUPPORTED_IMAGE_EXTENSIONS = new Set([
|
|
12
|
+
'.png',
|
|
13
|
+
'.jpg',
|
|
14
|
+
'.jpeg',
|
|
15
|
+
'.gif',
|
|
16
|
+
'.webp',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Branch used to store uploaded image assets.
|
|
20
|
+
*/
|
|
21
|
+
const ASSETS_BRANCH = 'hublaunch-assets';
|
|
22
|
+
/**
|
|
23
|
+
* Service for uploading image assets to GitHub via the REST API.
|
|
24
|
+
*
|
|
25
|
+
* Uploads images to a dedicated `hublaunch-assets` branch in the
|
|
26
|
+
* repository using the Git database API (blobs, trees, commits).
|
|
27
|
+
* This works with the standard OAuth token from `gh auth` / `hula login`
|
|
28
|
+
* and supports files up to 100 MB.
|
|
29
|
+
*
|
|
30
|
+
* The resulting raw.githubusercontent.com URLs inherit the repository's
|
|
31
|
+
* visibility — images on private repos require authentication to view.
|
|
32
|
+
*/
|
|
33
|
+
export class AssetUploadService {
|
|
34
|
+
token = null;
|
|
35
|
+
repoInfo = null;
|
|
36
|
+
assetsBranchReady = false;
|
|
37
|
+
/**
|
|
38
|
+
* Get the GitHub auth token from the gh CLI.
|
|
39
|
+
*/
|
|
40
|
+
async getToken() {
|
|
41
|
+
if (this.token)
|
|
42
|
+
return this.token;
|
|
43
|
+
try {
|
|
44
|
+
this.token = await runCommand('gh auth token', { silent: true });
|
|
45
|
+
return this.token;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error('GitHub authentication required for image uploads. Run `hula login` first.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get repo info (cached).
|
|
53
|
+
*/
|
|
54
|
+
async getRepo() {
|
|
55
|
+
if (this.repoInfo)
|
|
56
|
+
return this.repoInfo;
|
|
57
|
+
this.repoInfo = await getRepoInfo();
|
|
58
|
+
return this.repoInfo;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Make an authenticated GitHub API request.
|
|
62
|
+
*/
|
|
63
|
+
async githubApi(method, path, data) {
|
|
64
|
+
const token = await this.getToken();
|
|
65
|
+
const url = path.startsWith('https://')
|
|
66
|
+
? path
|
|
67
|
+
: `https://api.github.com${path}`;
|
|
68
|
+
const response = await axios({
|
|
69
|
+
method,
|
|
70
|
+
url,
|
|
71
|
+
data,
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `token ${token}`,
|
|
74
|
+
Accept: 'application/vnd.github.v3+json',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return response.data;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Ensure the assets branch exists in the remote. Creates it from the
|
|
81
|
+
* default branch if it doesn't exist yet.
|
|
82
|
+
*/
|
|
83
|
+
async ensureAssetsBranch() {
|
|
84
|
+
if (this.assetsBranchReady)
|
|
85
|
+
return;
|
|
86
|
+
const repo = await this.getRepo();
|
|
87
|
+
try {
|
|
88
|
+
await this.githubApi('get', `/repos/${repo.owner}/${repo.name}/git/refs/heads/${ASSETS_BRANCH}`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error.response?.status === 404) {
|
|
92
|
+
logger.debug(`Creating ${ASSETS_BRANCH} branch...`);
|
|
93
|
+
const repoData = await this.githubApi('get', `/repos/${repo.owner}/${repo.name}`);
|
|
94
|
+
const refData = await this.githubApi('get', `/repos/${repo.owner}/${repo.name}/git/refs/heads/${repoData.default_branch}`);
|
|
95
|
+
await this.githubApi('post', `/repos/${repo.owner}/${repo.name}/git/refs`, {
|
|
96
|
+
ref: `refs/heads/${ASSETS_BRANCH}`,
|
|
97
|
+
sha: refData.object.sha,
|
|
98
|
+
});
|
|
99
|
+
logger.debug(`Created ${ASSETS_BRANCH} branch`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.assetsBranchReady = true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Validate that a file path points to a supported image file.
|
|
109
|
+
* Returns the resolved absolute path, or throws with a descriptive error.
|
|
110
|
+
*/
|
|
111
|
+
validateImageFile(filePath) {
|
|
112
|
+
const resolved = isAbsolute(filePath) ? filePath : resolve(filePath);
|
|
113
|
+
if (!existsSync(resolved)) {
|
|
114
|
+
throw new Error(`Attachment file not found: ${filePath}`);
|
|
115
|
+
}
|
|
116
|
+
const ext = extname(resolved).toLowerCase();
|
|
117
|
+
if (!SUPPORTED_IMAGE_EXTENSIONS.has(ext)) {
|
|
118
|
+
throw new Error(`Unsupported file type: ${ext} (${basename(resolved)}). ` +
|
|
119
|
+
`Supported: ${[...SUPPORTED_IMAGE_EXTENSIONS].join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
// Basic size check — GitHub rejects uploads > 10 MB
|
|
122
|
+
const stats = statSync(resolved);
|
|
123
|
+
const maxSizeBytes = 10 * 1024 * 1024; // 10 MB
|
|
124
|
+
if (stats.size > maxSizeBytes) {
|
|
125
|
+
throw new Error(`File too large: ${basename(resolved)} (${(stats.size / 1024 / 1024).toFixed(1)} MB). ` +
|
|
126
|
+
`Maximum size is 10 MB.`);
|
|
127
|
+
}
|
|
128
|
+
if (stats.size === 0) {
|
|
129
|
+
throw new Error(`File is empty: ${basename(resolved)}`);
|
|
130
|
+
}
|
|
131
|
+
return resolved;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Upload a single image file via the GitHub Git database API.
|
|
135
|
+
*
|
|
136
|
+
* Uses the blob → tree → commit → ref update flow to support
|
|
137
|
+
* files up to 100 MB without command-line length limits.
|
|
138
|
+
*
|
|
139
|
+
* @param filePath - Absolute or relative path to the image file
|
|
140
|
+
* @returns UploadedAsset with the GitHub URL and markdown reference
|
|
141
|
+
*/
|
|
142
|
+
async uploadImage(filePath) {
|
|
143
|
+
const resolvedPath = this.validateImageFile(filePath);
|
|
144
|
+
const filename = basename(resolvedPath);
|
|
145
|
+
const repo = await this.getRepo();
|
|
146
|
+
await this.ensureAssetsBranch();
|
|
147
|
+
logger.debug(`Uploading: ${filename}`);
|
|
148
|
+
// Read and base64-encode the file
|
|
149
|
+
const content = readFileSync(resolvedPath).toString('base64');
|
|
150
|
+
const uniqueFilename = `${Date.now()}-${filename}`;
|
|
151
|
+
const targetPath = `attachments/${uniqueFilename}`;
|
|
152
|
+
// 1. Create a blob with the file content
|
|
153
|
+
const blobData = await this.githubApi('post', `/repos/${repo.owner}/${repo.name}/git/blobs`, { content, encoding: 'base64' });
|
|
154
|
+
// 2. Get the current branch tip
|
|
155
|
+
const refData = await this.githubApi('get', `/repos/${repo.owner}/${repo.name}/git/refs/heads/${ASSETS_BRANCH}`);
|
|
156
|
+
const currentCommitSha = refData.object.sha;
|
|
157
|
+
const commitData = await this.githubApi('get', `/repos/${repo.owner}/${repo.name}/git/commits/${currentCommitSha}`);
|
|
158
|
+
// 3. Create a new tree that includes the uploaded file
|
|
159
|
+
const treeData = await this.githubApi('post', `/repos/${repo.owner}/${repo.name}/git/trees`, {
|
|
160
|
+
base_tree: commitData.tree.sha,
|
|
161
|
+
tree: [
|
|
162
|
+
{
|
|
163
|
+
path: targetPath,
|
|
164
|
+
mode: '100644',
|
|
165
|
+
type: 'blob',
|
|
166
|
+
sha: blobData.sha,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
// 4. Create a commit pointing to the new tree
|
|
171
|
+
const newCommitData = await this.githubApi('post', `/repos/${repo.owner}/${repo.name}/git/commits`, {
|
|
172
|
+
message: `chore: upload attachment ${filename}`,
|
|
173
|
+
tree: treeData.sha,
|
|
174
|
+
parents: [currentCommitSha],
|
|
175
|
+
});
|
|
176
|
+
// 5. Update the branch ref to the new commit
|
|
177
|
+
await this.githubApi('patch', `/repos/${repo.owner}/${repo.name}/git/refs/heads/${ASSETS_BRANCH}`, { sha: newCommitData.sha });
|
|
178
|
+
const url = `https://github.com/${repo.owner}/${repo.name}/raw/${ASSETS_BRANCH}/${targetPath}`;
|
|
179
|
+
return {
|
|
180
|
+
localPath: resolvedPath,
|
|
181
|
+
filename,
|
|
182
|
+
url,
|
|
183
|
+
markdown: ``,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Upload multiple image files.
|
|
188
|
+
*
|
|
189
|
+
* @param filePaths - Array of file paths to upload
|
|
190
|
+
* @returns Array of UploadedAsset results (one per file)
|
|
191
|
+
*/
|
|
192
|
+
async uploadImages(filePaths) {
|
|
193
|
+
if (filePaths.length === 0)
|
|
194
|
+
return [];
|
|
195
|
+
await this.ensureAssetsBranch();
|
|
196
|
+
const results = [];
|
|
197
|
+
for (const filePath of filePaths) {
|
|
198
|
+
try {
|
|
199
|
+
const result = await this.uploadImage(filePath);
|
|
200
|
+
results.push(result);
|
|
201
|
+
logger.success(`Uploaded: ${result.filename}`);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
logger.error(`Failed to upload ${basename(filePath)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return results;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Scan a markdown body for local image references and return the
|
|
212
|
+
* file paths that need uploading.
|
|
213
|
+
*
|
|
214
|
+
* Detects patterns like:
|
|
215
|
+
* - 
|
|
216
|
+
* - 
|
|
217
|
+
* - 
|
|
218
|
+
* - 
|
|
219
|
+
*
|
|
220
|
+
* Only matches paths that:
|
|
221
|
+
* 1. Have a supported image extension
|
|
222
|
+
* 2. Do NOT start with http:// or https:// (those are already URLs)
|
|
223
|
+
* 3. Point to files that exist on disk
|
|
224
|
+
*
|
|
225
|
+
* @param body - The markdown content to scan
|
|
226
|
+
* @param basePath - The directory to resolve relative paths against
|
|
227
|
+
* (typically the directory containing the plan file)
|
|
228
|
+
* @returns Array of { match, localPath, altText } for each local image found
|
|
229
|
+
*/
|
|
230
|
+
scanForLocalImages(body, basePath) {
|
|
231
|
+
const results = [];
|
|
232
|
+
// Match markdown image syntax: 
|
|
233
|
+
const imageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
234
|
+
let regexMatch;
|
|
235
|
+
while ((regexMatch = imageRegex.exec(body)) !== null) {
|
|
236
|
+
const altText = regexMatch[1];
|
|
237
|
+
const imagePath = regexMatch[2];
|
|
238
|
+
// Skip URLs — those are already hosted
|
|
239
|
+
if (imagePath.startsWith('http://') ||
|
|
240
|
+
imagePath.startsWith('https://')) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
// Check extension
|
|
244
|
+
const ext = extname(imagePath).toLowerCase();
|
|
245
|
+
if (!SUPPORTED_IMAGE_EXTENSIONS.has(ext)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
// Resolve the path relative to the plan file's directory
|
|
249
|
+
const resolvedPath = isAbsolute(imagePath)
|
|
250
|
+
? imagePath
|
|
251
|
+
: resolve(basePath, imagePath);
|
|
252
|
+
// Only include if the file actually exists
|
|
253
|
+
if (existsSync(resolvedPath)) {
|
|
254
|
+
results.push({
|
|
255
|
+
match: regexMatch[0],
|
|
256
|
+
localPath: resolvedPath,
|
|
257
|
+
altText,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
logger.debug(`Local image not found (skipping): ${imagePath}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return results;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Process a plan body: find local image references, upload them,
|
|
268
|
+
* and replace the local paths with GitHub URLs.
|
|
269
|
+
*
|
|
270
|
+
* @param body - The plan markdown body
|
|
271
|
+
* @param basePath - Directory to resolve relative image paths against
|
|
272
|
+
* @returns The updated body with local image paths replaced by GitHub URLs
|
|
273
|
+
*/
|
|
274
|
+
async resolveLocalImages(body, basePath) {
|
|
275
|
+
const localImages = this.scanForLocalImages(body, basePath);
|
|
276
|
+
if (localImages.length === 0) {
|
|
277
|
+
return body;
|
|
278
|
+
}
|
|
279
|
+
logger.info(`Found ${localImages.length} local image(s) in plan, uploading...`);
|
|
280
|
+
await this.ensureAssetsBranch();
|
|
281
|
+
let updatedBody = body;
|
|
282
|
+
for (const img of localImages) {
|
|
283
|
+
try {
|
|
284
|
+
const uploaded = await this.uploadImage(img.localPath);
|
|
285
|
+
// Replace the local reference with the uploaded URL
|
|
286
|
+
// Use the original alt text from the plan
|
|
287
|
+
const newMarkdown = ``;
|
|
288
|
+
updatedBody = updatedBody.replaceAll(img.match, newMarkdown);
|
|
289
|
+
logger.success(`Resolved: ${basename(img.localPath)} → ${ASSETS_BRANCH}`);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
logger.warning(`Could not upload ${basename(img.localPath)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
293
|
+
// Leave the original reference in place — non-fatal
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return updatedBody;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=AssetUploadService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetUploadService.js","sourceRoot":"","sources":["../../../src/services/github/AssetUploadService.ts"],"names":[],"mappings":"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"}
|