@oss-autopilot/core 0.43.0 → 0.44.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.
- package/dist/cli.bundle.cjs +261 -17002
- package/dist/cli.js +0 -17
- package/dist/commands/config.js +2 -1
- package/dist/commands/daily.js +57 -36
- package/dist/commands/dashboard-data.d.ts +10 -7
- package/dist/commands/dashboard-data.js +36 -3
- package/dist/commands/dashboard-formatters.d.ts +2 -10
- package/dist/commands/dashboard-formatters.js +2 -16
- package/dist/commands/dashboard-lifecycle.d.ts +1 -1
- package/dist/commands/dashboard-lifecycle.js +1 -1
- package/dist/commands/dashboard-scripts.d.ts +1 -1
- package/dist/commands/dashboard-server.js +29 -48
- package/dist/commands/dashboard-templates.d.ts +3 -2
- package/dist/commands/dashboard-templates.js +3 -2
- package/dist/commands/dashboard.d.ts +5 -9
- package/dist/commands/dashboard.js +7 -111
- package/dist/commands/startup.d.ts +3 -2
- package/dist/commands/startup.js +33 -30
- package/dist/commands/vet.js +2 -1
- package/dist/core/concurrency.d.ts +2 -1
- package/dist/core/concurrency.js +12 -2
- package/dist/core/daily-logic.js +1 -1
- package/dist/core/pr-monitor.js +12 -12
- package/dist/core/utils.d.ts +4 -8
- package/dist/core/utils.js +7 -8
- package/dist/formatters/json.d.ts +3 -2
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Pure functions with no side effects — all data is passed in as arguments.
|
|
7
7
|
*/
|
|
8
8
|
import type { DailyDigest, AgentState, CommentedIssueWithResponse } from '../core/types.js';
|
|
9
|
-
import {
|
|
10
|
-
export { escapeHtml
|
|
9
|
+
import type { DashboardStats } from './dashboard-data.js';
|
|
10
|
+
export { escapeHtml } from './dashboard-formatters.js';
|
|
11
|
+
export { buildDashboardStats, type DashboardStats } from './dashboard-data.js';
|
|
11
12
|
export declare function generateDashboardHtml(stats: DashboardStats, monthlyMerged: Record<string, number>, monthlyClosed: Record<string, number>, monthlyOpened: Record<string, number>, digest: DailyDigest, state: Readonly<AgentState>, issueResponses?: CommentedIssueWithResponse[]): string;
|
|
@@ -9,8 +9,9 @@ import { escapeHtml } from './dashboard-formatters.js';
|
|
|
9
9
|
import { DASHBOARD_CSS } from './dashboard-styles.js';
|
|
10
10
|
import { SVG_ICONS, truncateTitle, renderHealthItems, titleMeta } from './dashboard-components.js';
|
|
11
11
|
import { generateDashboardScripts } from './dashboard-scripts.js';
|
|
12
|
-
// Re-export public API so
|
|
13
|
-
export { escapeHtml
|
|
12
|
+
// Re-export public API so consumers can import from this module
|
|
13
|
+
export { escapeHtml } from './dashboard-formatters.js';
|
|
14
|
+
export { buildDashboardStats } from './dashboard-data.js';
|
|
14
15
|
export function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state, issueResponses = []) {
|
|
15
16
|
const approachingDormantDays = state.config?.approachingDormantDays ?? 25;
|
|
16
17
|
const shelvedPRs = digest.shelvedPRs || [];
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dashboard command —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Dashboard command — serves the interactive Preact SPA dashboard.
|
|
3
|
+
* Also provides writeDashboardFromState() for generating a static HTML fallback
|
|
4
|
+
* when the SPA cannot be launched (e.g., assets not built).
|
|
5
5
|
*/
|
|
6
|
-
interface DashboardOptions {
|
|
7
|
-
open?: boolean;
|
|
8
|
-
json?: boolean;
|
|
9
|
-
offline?: boolean;
|
|
10
|
-
}
|
|
11
|
-
export declare function runDashboard(options: DashboardOptions): Promise<void>;
|
|
12
6
|
/**
|
|
13
7
|
* Generate dashboard HTML from state (no GitHub fetch).
|
|
14
8
|
* Call after executeDailyCheck() which saves fresh data to state.
|
|
15
9
|
* Returns the path to the generated dashboard HTML file.
|
|
10
|
+
*
|
|
11
|
+
* Used as a safety net when the interactive SPA dashboard cannot be launched.
|
|
16
12
|
*/
|
|
17
13
|
export declare function writeDashboardFromState(): string;
|
|
18
14
|
interface ServeOptions {
|
|
@@ -1,124 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dashboard command —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Dashboard command — serves the interactive Preact SPA dashboard.
|
|
3
|
+
* Also provides writeDashboardFromState() for generating a static HTML fallback
|
|
4
|
+
* when the SPA cannot be launched (e.g., assets not built).
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import { execFile } from 'child_process';
|
|
9
8
|
import { getStateManager, getDashboardPath, getGitHubToken } from '../core/index.js';
|
|
10
|
-
import {
|
|
11
|
-
import { outputJson } from '../formatters/json.js';
|
|
12
|
-
import { fetchDashboardData, computePRsByRepo, computeTopRepos, getMonthlyData } from './dashboard-data.js';
|
|
9
|
+
import { getMonthlyData } from './dashboard-data.js';
|
|
13
10
|
import { buildDashboardStats, generateDashboardHtml } from './dashboard-templates.js';
|
|
14
|
-
|
|
15
|
-
const stateManager = getStateManager();
|
|
16
|
-
const token = options.offline ? null : getGitHubToken();
|
|
17
|
-
let digest;
|
|
18
|
-
let commentedIssues = [];
|
|
19
|
-
// In offline mode, skip all GitHub API calls
|
|
20
|
-
if (options.offline) {
|
|
21
|
-
const state = stateManager.getState();
|
|
22
|
-
digest = state.lastDigest;
|
|
23
|
-
if (!digest) {
|
|
24
|
-
if (options.json) {
|
|
25
|
-
outputJson({ error: 'No cached data found. Run without --offline first.', offline: true });
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.error('No cached data found. Run without --offline first.');
|
|
29
|
-
}
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const lastUpdated = digest.generatedAt || state.lastDigestAt || state.lastRunAt;
|
|
33
|
-
console.error(`Offline mode: using cached data from ${lastUpdated}`);
|
|
34
|
-
}
|
|
35
|
-
else if (token) {
|
|
36
|
-
console.error('Fetching fresh data from GitHub...');
|
|
37
|
-
try {
|
|
38
|
-
const result = await fetchDashboardData(token);
|
|
39
|
-
digest = result.digest;
|
|
40
|
-
commentedIssues = result.commentedIssues;
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
console.error('Failed to fetch fresh data:', errorMessage(error));
|
|
44
|
-
console.error('Falling back to cached data (issue conversations unavailable)...');
|
|
45
|
-
digest = stateManager.getState().lastDigest;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
// No token and not offline — fall back to cached digest with warning
|
|
50
|
-
console.error('Warning: No GitHub token found. Using cached data (may be stale).');
|
|
51
|
-
console.error('Set GITHUB_TOKEN or run `gh auth login` for fresh data.');
|
|
52
|
-
digest = stateManager.getState().lastDigest;
|
|
53
|
-
}
|
|
54
|
-
// Check if we have a digest to display
|
|
55
|
-
if (!digest) {
|
|
56
|
-
if (options.json) {
|
|
57
|
-
outputJson({ error: 'No data available. Run daily check first with GITHUB_TOKEN.' });
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
console.error('No dashboard data available. Run the daily check first:');
|
|
61
|
-
console.error(' GITHUB_TOKEN=$(gh auth token) npm start -- daily');
|
|
62
|
-
}
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const state = stateManager.getState();
|
|
66
|
-
// Gather data for charts from digest
|
|
67
|
-
const prsByRepo = computePRsByRepo(digest, state);
|
|
68
|
-
const topRepos = computeTopRepos(prsByRepo);
|
|
69
|
-
const { monthlyMerged, monthlyClosed, monthlyOpened } = getMonthlyData(state);
|
|
70
|
-
const stats = buildDashboardStats(digest, state);
|
|
71
|
-
if (options.json) {
|
|
72
|
-
const issueResponses = commentedIssues.filter((i) => i.status === 'new_response');
|
|
73
|
-
const jsonData = {
|
|
74
|
-
stats,
|
|
75
|
-
prsByRepo,
|
|
76
|
-
topRepos: topRepos.map(([repo, data]) => ({ repo, ...data })),
|
|
77
|
-
monthlyMerged,
|
|
78
|
-
activePRs: digest.openPRs || [],
|
|
79
|
-
commentedIssues,
|
|
80
|
-
issueResponses,
|
|
81
|
-
};
|
|
82
|
-
if (options.offline) {
|
|
83
|
-
jsonData.offline = true;
|
|
84
|
-
jsonData.lastUpdated = digest.generatedAt || state.lastDigestAt || state.lastRunAt;
|
|
85
|
-
}
|
|
86
|
-
outputJson(jsonData);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const issueResponses = commentedIssues.filter((i) => i.status === 'new_response');
|
|
90
|
-
const html = generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state, issueResponses);
|
|
91
|
-
// Write to file in ~/.oss-autopilot/
|
|
92
|
-
const dashboardPath = getDashboardPath();
|
|
93
|
-
fs.writeFileSync(dashboardPath, html, { mode: 0o644 });
|
|
94
|
-
if (options.offline) {
|
|
95
|
-
const lastUpdated = digest.generatedAt || state.lastDigestAt || state.lastRunAt;
|
|
96
|
-
console.log(`\n📊 Dashboard generated (offline, cached data from ${lastUpdated}): ${dashboardPath}`);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
console.log(`\n📊 Dashboard generated: ${dashboardPath}`);
|
|
100
|
-
}
|
|
101
|
-
if (options.open) {
|
|
102
|
-
// Use platform-specific open command - path is hardcoded, not user input
|
|
103
|
-
const isWindows = process.platform === 'win32';
|
|
104
|
-
const openCmd = process.platform === 'darwin' ? 'open' : isWindows ? 'cmd' : 'xdg-open';
|
|
105
|
-
const args = isWindows ? ['/c', 'start', '', dashboardPath] : [dashboardPath];
|
|
106
|
-
console.log(`Dashboard: ${dashboardPath}`);
|
|
107
|
-
execFile(openCmd, args, (error) => {
|
|
108
|
-
if (error) {
|
|
109
|
-
console.error('Failed to open browser:', error.message);
|
|
110
|
-
console.error(`Open manually: ${dashboardPath}`);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
console.log('Run with --open to open in browser');
|
|
116
|
-
}
|
|
117
|
-
}
|
|
11
|
+
// ── Static HTML fallback ────────────────────────────────────────────────────
|
|
118
12
|
/**
|
|
119
13
|
* Generate dashboard HTML from state (no GitHub fetch).
|
|
120
14
|
* Call after executeDailyCheck() which saves fresh data to state.
|
|
121
15
|
* Returns the path to the generated dashboard HTML file.
|
|
16
|
+
*
|
|
17
|
+
* Used as a safety net when the interactive SPA dashboard cannot be launched.
|
|
122
18
|
*/
|
|
123
19
|
export function writeDashboardFromState() {
|
|
124
20
|
const stateManager = getStateManager();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Startup command
|
|
3
3
|
* Combines all pre-flight checks into a single CLI invocation:
|
|
4
|
-
* auth check, setup check, daily fetch, dashboard
|
|
4
|
+
* auth check, setup check, daily fetch, dashboard launch, version detection, issue list detection.
|
|
5
5
|
*
|
|
6
6
|
* Replaces the ~100-line inline bash script in commands/oss.md with a single
|
|
7
7
|
* `node cli.bundle.cjs startup --json` call, reducing UI noise in Claude Code.
|
|
@@ -26,12 +26,13 @@ export declare function countIssueListItems(content: string): {
|
|
|
26
26
|
* Returns IssueListInfo or undefined if no list found.
|
|
27
27
|
*/
|
|
28
28
|
export declare function detectIssueList(): IssueListInfo | undefined;
|
|
29
|
+
export declare function openInBrowser(url: string): void;
|
|
29
30
|
/**
|
|
30
31
|
* Run startup checks and return structured output.
|
|
31
32
|
* Returns StartupOutput with one of three shapes:
|
|
32
33
|
* 1. Setup incomplete: { version, setupComplete: false }
|
|
33
34
|
* 2. Auth failure: { version, setupComplete: true, authError: "..." }
|
|
34
|
-
* 3. Success: { version, setupComplete: true, daily,
|
|
35
|
+
* 3. Success: { version, setupComplete: true, daily, dashboardUrl?, dashboardPath?, issueList? }
|
|
35
36
|
*
|
|
36
37
|
* Errors from the daily check propagate to the caller.
|
|
37
38
|
*/
|
package/dist/commands/startup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Startup command
|
|
3
3
|
* Combines all pre-flight checks into a single CLI invocation:
|
|
4
|
-
* auth check, setup check, daily fetch, dashboard
|
|
4
|
+
* auth check, setup check, daily fetch, dashboard launch, version detection, issue list detection.
|
|
5
5
|
*
|
|
6
6
|
* Replaces the ~100-line inline bash script in commands/oss.md with a single
|
|
7
7
|
* `node cli.bundle.cjs startup --json` call, reducing UI noise in Claude Code.
|
|
@@ -11,8 +11,8 @@ import { execFile } from 'child_process';
|
|
|
11
11
|
import { getStateManager, getGitHubToken, getCLIVersion } from '../core/index.js';
|
|
12
12
|
import { errorMessage } from '../core/errors.js';
|
|
13
13
|
import { executeDailyCheck } from './daily.js';
|
|
14
|
-
import { writeDashboardFromState } from './dashboard.js';
|
|
15
14
|
import { launchDashboardServer } from './dashboard-lifecycle.js';
|
|
15
|
+
import { writeDashboardFromState } from './dashboard.js';
|
|
16
16
|
/**
|
|
17
17
|
* Parse issueListPath from a config file's YAML frontmatter.
|
|
18
18
|
* Returns the path string or undefined if not found.
|
|
@@ -93,21 +93,21 @@ export function detectIssueList() {
|
|
|
93
93
|
return { path: issueListPath, source, availableCount: 0, completedCount: 0 };
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
function openInBrowser(
|
|
96
|
+
export function openInBrowser(url) {
|
|
97
97
|
let openCmd;
|
|
98
98
|
let args;
|
|
99
99
|
switch (process.platform) {
|
|
100
100
|
case 'darwin':
|
|
101
101
|
openCmd = 'open';
|
|
102
|
-
args = [
|
|
102
|
+
args = [url];
|
|
103
103
|
break;
|
|
104
104
|
case 'win32':
|
|
105
105
|
openCmd = 'cmd';
|
|
106
|
-
args = ['/c', 'start', '',
|
|
106
|
+
args = ['/c', 'start', '', url];
|
|
107
107
|
break;
|
|
108
108
|
default:
|
|
109
109
|
openCmd = 'xdg-open';
|
|
110
|
-
args = [
|
|
110
|
+
args = [url];
|
|
111
111
|
break;
|
|
112
112
|
}
|
|
113
113
|
execFile(openCmd, args, (error) => {
|
|
@@ -121,7 +121,7 @@ function openInBrowser(filePath) {
|
|
|
121
121
|
* Returns StartupOutput with one of three shapes:
|
|
122
122
|
* 1. Setup incomplete: { version, setupComplete: false }
|
|
123
123
|
* 2. Auth failure: { version, setupComplete: true, authError: "..." }
|
|
124
|
-
* 3. Success: { version, setupComplete: true, daily,
|
|
124
|
+
* 3. Success: { version, setupComplete: true, daily, dashboardUrl?, dashboardPath?, issueList? }
|
|
125
125
|
*
|
|
126
126
|
* Errors from the daily check propagate to the caller.
|
|
127
127
|
*/
|
|
@@ -143,49 +143,52 @@ export async function runStartup() {
|
|
|
143
143
|
}
|
|
144
144
|
// 3. Run daily check
|
|
145
145
|
const daily = await executeDailyCheck(token);
|
|
146
|
-
// 4.
|
|
147
|
-
let dashboardPath;
|
|
148
|
-
try {
|
|
149
|
-
dashboardPath = writeDashboardFromState();
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
console.error('[STARTUP] Dashboard generation failed:', errorMessage(error));
|
|
153
|
-
}
|
|
154
|
-
// 5. Launch interactive SPA dashboard (preferred) with static HTML fallback
|
|
146
|
+
// 4. Launch interactive SPA dashboard (with static HTML fallback)
|
|
155
147
|
// Skip opening on first run (0 PRs) — the welcome flow handles onboarding
|
|
156
148
|
let dashboardUrl;
|
|
149
|
+
let dashboardPath;
|
|
157
150
|
let dashboardOpened = false;
|
|
151
|
+
function tryStaticHtmlFallback() {
|
|
152
|
+
try {
|
|
153
|
+
dashboardPath = writeDashboardFromState();
|
|
154
|
+
openInBrowser(dashboardPath);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch (htmlError) {
|
|
158
|
+
console.error('[STARTUP] Static HTML dashboard fallback also failed:', errorMessage(htmlError));
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
158
162
|
if (daily.digest.summary.totalActivePRs > 0) {
|
|
159
|
-
let spaResult = null;
|
|
160
163
|
try {
|
|
161
|
-
spaResult = await launchDashboardServer();
|
|
164
|
+
const spaResult = await launchDashboardServer();
|
|
165
|
+
if (spaResult) {
|
|
166
|
+
dashboardUrl = spaResult.url;
|
|
167
|
+
openInBrowser(spaResult.url);
|
|
168
|
+
dashboardOpened = true;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.error('[STARTUP] Dashboard SPA assets not found, falling back to static HTML dashboard');
|
|
172
|
+
dashboardOpened = tryStaticHtmlFallback();
|
|
173
|
+
}
|
|
162
174
|
}
|
|
163
175
|
catch (error) {
|
|
164
176
|
console.error('[STARTUP] SPA dashboard launch failed:', errorMessage(error));
|
|
165
|
-
|
|
166
|
-
if (spaResult) {
|
|
167
|
-
dashboardUrl = spaResult.url;
|
|
168
|
-
openInBrowser(spaResult.url);
|
|
169
|
-
dashboardOpened = true;
|
|
170
|
-
}
|
|
171
|
-
else if (dashboardPath) {
|
|
172
|
-
// SPA unavailable (assets not built) — fall back to static HTML
|
|
173
|
-
openInBrowser(dashboardPath);
|
|
174
|
-
dashboardOpened = true;
|
|
177
|
+
dashboardOpened = tryStaticHtmlFallback();
|
|
175
178
|
}
|
|
176
179
|
}
|
|
177
180
|
// Append dashboard status to brief summary (only startup opens the browser, not daily)
|
|
178
181
|
if (dashboardOpened) {
|
|
179
182
|
daily.briefSummary += ' | Dashboard opened in browser';
|
|
180
183
|
}
|
|
181
|
-
//
|
|
184
|
+
// 5. Detect issue list
|
|
182
185
|
const issueList = detectIssueList();
|
|
183
186
|
return {
|
|
184
187
|
version,
|
|
185
188
|
setupComplete: true,
|
|
186
189
|
daily,
|
|
187
|
-
dashboardPath,
|
|
188
190
|
dashboardUrl,
|
|
191
|
+
dashboardPath,
|
|
189
192
|
issueList,
|
|
190
193
|
};
|
|
191
194
|
}
|
package/dist/commands/vet.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Vets a specific issue before working on it
|
|
4
4
|
*/
|
|
5
5
|
import { IssueDiscovery, requireGitHubToken } from '../core/index.js';
|
|
6
|
-
import { validateUrl } from './validation.js';
|
|
6
|
+
import { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl } from './validation.js';
|
|
7
7
|
export async function runVet(options) {
|
|
8
8
|
validateUrl(options.issueUrl);
|
|
9
|
+
validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, 'issue');
|
|
9
10
|
const token = requireGitHubToken();
|
|
10
11
|
const discovery = new IssueDiscovery(token);
|
|
11
12
|
const candidate = await discovery.vetIssue(options.issueUrl);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runs a worker pool that processes items with bounded concurrency.
|
|
3
|
-
* N workers consume from a shared index
|
|
3
|
+
* N workers consume from a shared index. On any worker error, remaining
|
|
4
|
+
* workers are aborted via a shared flag and the error is propagated.
|
|
4
5
|
*/
|
|
5
6
|
export declare function runWorkerPool<T>(items: T[], worker: (item: T) => Promise<void>, concurrency: number): Promise<void>;
|
package/dist/core/concurrency.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runs a worker pool that processes items with bounded concurrency.
|
|
3
|
-
* N workers consume from a shared index
|
|
3
|
+
* N workers consume from a shared index. On any worker error, remaining
|
|
4
|
+
* workers are aborted via a shared flag and the error is propagated.
|
|
4
5
|
*/
|
|
5
6
|
export async function runWorkerPool(items, worker, concurrency) {
|
|
6
7
|
let index = 0;
|
|
8
|
+
let aborted = false;
|
|
7
9
|
const poolWorker = async () => {
|
|
8
10
|
while (index < items.length) {
|
|
11
|
+
if (aborted)
|
|
12
|
+
break;
|
|
9
13
|
const item = items[index++];
|
|
10
|
-
|
|
14
|
+
try {
|
|
15
|
+
await worker(item);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
aborted = true;
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
11
21
|
}
|
|
12
22
|
};
|
|
13
23
|
const workerCount = Math.min(concurrency, items.length);
|
package/dist/core/daily-logic.js
CHANGED
|
@@ -509,5 +509,5 @@ export function printDigest(digest, capacity, commentedIssues = []) {
|
|
|
509
509
|
console.log('');
|
|
510
510
|
}
|
|
511
511
|
console.log('Run with --json for structured output');
|
|
512
|
-
console.log('Run "dashboard
|
|
512
|
+
console.log('Run "dashboard serve" for browser view');
|
|
513
513
|
}
|
package/dist/core/pr-monitor.js
CHANGED
|
@@ -350,11 +350,7 @@ export class PRMonitor {
|
|
|
350
350
|
* Check if PR has merge conflict
|
|
351
351
|
*/
|
|
352
352
|
hasMergeConflict(mergeable, mergeableState) {
|
|
353
|
-
|
|
354
|
-
return true;
|
|
355
|
-
if (mergeableState === 'dirty')
|
|
356
|
-
return true;
|
|
357
|
-
return false;
|
|
353
|
+
return mergeable === false || mergeableState === 'dirty';
|
|
358
354
|
}
|
|
359
355
|
/**
|
|
360
356
|
* Get CI status from combined status API and check runs.
|
|
@@ -371,6 +367,14 @@ export class PRMonitor {
|
|
|
371
367
|
// 404 is expected for repos without check runs configured; log other errors for debugging
|
|
372
368
|
this.octokit.checks.listForRef({ owner, repo, ref: sha }).catch((err) => {
|
|
373
369
|
const status = getHttpStatusCode(err);
|
|
370
|
+
// Rate limit errors must propagate — matches listReviewComments pattern (#481)
|
|
371
|
+
if (status === 429)
|
|
372
|
+
throw err;
|
|
373
|
+
if (status === 403) {
|
|
374
|
+
const msg = errorMessage(err).toLowerCase();
|
|
375
|
+
if (msg.includes('rate limit') || msg.includes('abuse detection'))
|
|
376
|
+
throw err;
|
|
377
|
+
}
|
|
374
378
|
if (status === 404) {
|
|
375
379
|
debug('pr-monitor', `Check runs 404 for ${owner}/${repo}@${sha.slice(0, 7)} (no checks configured)`);
|
|
376
380
|
}
|
|
@@ -400,12 +404,8 @@ export class PRMonitor {
|
|
|
400
404
|
}
|
|
401
405
|
catch (error) {
|
|
402
406
|
const statusCode = getHttpStatusCode(error);
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
warn('pr-monitor', `CI check failed for ${owner}/${repo}: Invalid token`);
|
|
406
|
-
}
|
|
407
|
-
else if (statusCode === 403) {
|
|
408
|
-
warn('pr-monitor', `CI check failed for ${owner}/${repo}: Rate limit exceeded`);
|
|
407
|
+
if (statusCode === 401 || statusCode === 403 || statusCode === 429) {
|
|
408
|
+
throw error;
|
|
409
409
|
}
|
|
410
410
|
else if (statusCode === 404) {
|
|
411
411
|
// Repo might not have CI configured, this is normal
|
|
@@ -413,7 +413,7 @@ export class PRMonitor {
|
|
|
413
413
|
return { status: 'unknown', failingCheckNames: [], failingCheckConclusions: new Map() };
|
|
414
414
|
}
|
|
415
415
|
else {
|
|
416
|
-
warn('pr-monitor', `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${
|
|
416
|
+
warn('pr-monitor', `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${errorMessage(error)}`);
|
|
417
417
|
}
|
|
418
418
|
return { status: 'unknown', failingCheckNames: [], failingCheckConclusions: new Map() };
|
|
419
419
|
}
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Returns the oss-autopilot data directory path, creating it if it does not exist.
|
|
6
6
|
*
|
|
7
7
|
* The directory is located at `~/.oss-autopilot/` and serves as the root for
|
|
8
|
-
* all persisted user data (state, backups,
|
|
8
|
+
* all persisted user data (state, backups, cache).
|
|
9
9
|
*
|
|
10
10
|
* @returns Absolute path to the data directory (e.g., `/Users/you/.oss-autopilot`)
|
|
11
11
|
*
|
|
@@ -53,11 +53,8 @@ export declare function getBackupDir(): string;
|
|
|
53
53
|
*/
|
|
54
54
|
export declare function getCacheDir(): string;
|
|
55
55
|
/**
|
|
56
|
-
* Returns the path to the
|
|
57
|
-
*
|
|
58
|
-
* Implicitly creates the data directory via {@link getDataDir} if it does not exist.
|
|
59
|
-
*
|
|
60
|
-
* @returns Absolute path to `dashboard.html`
|
|
56
|
+
* Returns the path to the static HTML dashboard file (~/.oss-autopilot/dashboard.html).
|
|
57
|
+
* Used as a fallback when the interactive SPA dashboard cannot be launched.
|
|
61
58
|
*
|
|
62
59
|
* @example
|
|
63
60
|
* const dashPath = getDashboardPath();
|
|
@@ -144,10 +141,9 @@ export declare function daysBetween(from: Date, to?: Date): number;
|
|
|
144
141
|
/**
|
|
145
142
|
* Splits an `"owner/repo"` string into its owner and repo components.
|
|
146
143
|
*
|
|
147
|
-
* Does not validate the input format; if no `/` is present, `repo` will be `undefined`.
|
|
148
|
-
*
|
|
149
144
|
* @param repoFullName - Full repository name in `"owner/repo"` format
|
|
150
145
|
* @returns Object with `owner` and `repo` string properties
|
|
146
|
+
* @throws {Error} If the input does not contain both an owner and repo separated by `/`
|
|
151
147
|
*
|
|
152
148
|
* @example
|
|
153
149
|
* splitRepo('facebook/react')
|
package/dist/core/utils.js
CHANGED
|
@@ -15,7 +15,7 @@ let tokenFetchAttempted = false;
|
|
|
15
15
|
* Returns the oss-autopilot data directory path, creating it if it does not exist.
|
|
16
16
|
*
|
|
17
17
|
* The directory is located at `~/.oss-autopilot/` and serves as the root for
|
|
18
|
-
* all persisted user data (state, backups,
|
|
18
|
+
* all persisted user data (state, backups, cache).
|
|
19
19
|
*
|
|
20
20
|
* @returns Absolute path to the data directory (e.g., `/Users/you/.oss-autopilot`)
|
|
21
21
|
*
|
|
@@ -83,11 +83,8 @@ export function getCacheDir() {
|
|
|
83
83
|
return dir;
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
|
-
* Returns the path to the
|
|
87
|
-
*
|
|
88
|
-
* Implicitly creates the data directory via {@link getDataDir} if it does not exist.
|
|
89
|
-
*
|
|
90
|
-
* @returns Absolute path to `dashboard.html`
|
|
86
|
+
* Returns the path to the static HTML dashboard file (~/.oss-autopilot/dashboard.html).
|
|
87
|
+
* Used as a fallback when the interactive SPA dashboard cannot be launched.
|
|
91
88
|
*
|
|
92
89
|
* @example
|
|
93
90
|
* const dashPath = getDashboardPath();
|
|
@@ -215,10 +212,9 @@ export function daysBetween(from, to = new Date()) {
|
|
|
215
212
|
/**
|
|
216
213
|
* Splits an `"owner/repo"` string into its owner and repo components.
|
|
217
214
|
*
|
|
218
|
-
* Does not validate the input format; if no `/` is present, `repo` will be `undefined`.
|
|
219
|
-
*
|
|
220
215
|
* @param repoFullName - Full repository name in `"owner/repo"` format
|
|
221
216
|
* @returns Object with `owner` and `repo` string properties
|
|
217
|
+
* @throws {Error} If the input does not contain both an owner and repo separated by `/`
|
|
222
218
|
*
|
|
223
219
|
* @example
|
|
224
220
|
* splitRepo('facebook/react')
|
|
@@ -226,6 +222,9 @@ export function daysBetween(from, to = new Date()) {
|
|
|
226
222
|
*/
|
|
227
223
|
export function splitRepo(repoFullName) {
|
|
228
224
|
const [owner, repo] = repoFullName.split('/');
|
|
225
|
+
if (!owner || !repo) {
|
|
226
|
+
throw new Error(`Invalid repo format: expected "owner/repo", got "${repoFullName}"`);
|
|
227
|
+
}
|
|
229
228
|
return { owner, repo };
|
|
230
229
|
}
|
|
231
230
|
/**
|
|
@@ -200,16 +200,17 @@ export interface IssueListInfo {
|
|
|
200
200
|
* Three valid shapes:
|
|
201
201
|
* 1. Setup incomplete: { version, setupComplete: false }
|
|
202
202
|
* 2. Auth failure: { version, setupComplete: true, authError: "..." }
|
|
203
|
-
* 3. Success: { version, setupComplete: true, daily,
|
|
203
|
+
* 3. Success: { version, setupComplete: true, daily, dashboardUrl?, issueList? }
|
|
204
204
|
*/
|
|
205
205
|
export interface StartupOutput {
|
|
206
206
|
version: string;
|
|
207
207
|
setupComplete: boolean;
|
|
208
208
|
authError?: string;
|
|
209
209
|
daily?: DailyOutput;
|
|
210
|
-
dashboardPath?: string;
|
|
211
210
|
/** URL of the interactive SPA dashboard server, when running (e.g., "http://localhost:3000") */
|
|
212
211
|
dashboardUrl?: string;
|
|
212
|
+
/** Path to the static HTML dashboard file (fallback when SPA cannot launch) */
|
|
213
|
+
dashboardPath?: string;
|
|
213
214
|
issueList?: IssueListInfo;
|
|
214
215
|
}
|
|
215
216
|
/** A single parsed issue from a markdown list (#82) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-autopilot/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "CLI and core library for managing open source contributions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsc",
|
|
63
|
-
"bundle": "esbuild src/cli.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cli.bundle.cjs",
|
|
63
|
+
"bundle": "esbuild src/cli.ts --bundle --platform=node --target=node20 --format=cjs --minify --outfile=dist/cli.bundle.cjs",
|
|
64
64
|
"start": "tsx src/cli.ts",
|
|
65
65
|
"dev": "tsx watch src/cli.ts",
|
|
66
66
|
"typecheck": "tsc --noEmit",
|