@skilljack/mcp 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/github-config.d.ts +83 -0
- package/dist/github-config.js +191 -0
- package/dist/github-polling.d.ts +35 -0
- package/dist/github-polling.js +108 -0
- package/dist/github-sync.d.ts +49 -0
- package/dist/github-sync.js +259 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +309 -53
- package/dist/skill-config-tool.d.ts +22 -0
- package/dist/skill-config-tool.js +495 -0
- package/dist/skill-config.d.ts +163 -0
- package/dist/skill-config.js +450 -0
- package/dist/skill-discovery.d.ts +52 -1
- package/dist/skill-discovery.js +70 -1
- package/dist/skill-display-tool.d.ts +22 -0
- package/dist/skill-display-tool.js +302 -0
- package/dist/skill-prompts.d.ts +4 -0
- package/dist/skill-prompts.js +62 -10
- package/dist/skill-resources.d.ts +6 -3
- package/dist/skill-resources.js +10 -94
- package/dist/skill-tool.js +4 -3
- package/dist/subscriptions.d.ts +1 -1
- package/dist/subscriptions.js +1 -1
- package/dist/ui/mcp-app.d.ts +1 -0
- package/dist/ui/mcp-app.html +278 -0
- package/dist/ui/mcp-app.js +484 -0
- package/dist/ui/skill-display.d.ts +1 -0
- package/dist/ui/skill-display.html +188 -0
- package/dist/ui/skill-display.js +269 -0
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub configuration parsing and URL detection.
|
|
3
|
+
* Handles detection of GitHub URLs, parsing repo specs, and allowlist validation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Parsed GitHub repository specification.
|
|
7
|
+
*/
|
|
8
|
+
export interface GitHubRepoSpec {
|
|
9
|
+
owner: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
ref?: string;
|
|
12
|
+
subpath?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* GitHub-specific configuration from environment variables.
|
|
16
|
+
*/
|
|
17
|
+
export interface GitHubConfig {
|
|
18
|
+
token?: string;
|
|
19
|
+
pollIntervalMs: number;
|
|
20
|
+
cacheDir: string;
|
|
21
|
+
allowedOrgs: string[];
|
|
22
|
+
allowedUsers: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a path is a GitHub URL.
|
|
26
|
+
* Detects paths containing "github.com".
|
|
27
|
+
*/
|
|
28
|
+
export declare function isGitHubUrl(urlOrPath: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a GitHub URL into a GitHubRepoSpec.
|
|
31
|
+
*
|
|
32
|
+
* Supported formats:
|
|
33
|
+
* github.com/owner/repo
|
|
34
|
+
* github.com/owner/repo@ref
|
|
35
|
+
* github.com/owner/repo/subpath
|
|
36
|
+
* github.com/owner/repo/subpath@ref
|
|
37
|
+
* https://github.com/owner/repo
|
|
38
|
+
* https://github.com/owner/repo.git
|
|
39
|
+
*
|
|
40
|
+
* @param url - The GitHub URL to parse
|
|
41
|
+
* @returns Parsed GitHubRepoSpec
|
|
42
|
+
* @throws Error if URL format is invalid
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseGitHubUrl(url: string): GitHubRepoSpec;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a repository is allowed by the allowlist.
|
|
47
|
+
* If no allowlist is configured (both allowedOrgs and allowedUsers empty),
|
|
48
|
+
* all repos are DENIED by default for security.
|
|
49
|
+
*
|
|
50
|
+
* @param spec - The repository specification
|
|
51
|
+
* @param config - GitHub configuration with allowlists
|
|
52
|
+
* @returns true if allowed, false if blocked
|
|
53
|
+
*/
|
|
54
|
+
export declare function isRepoAllowed(spec: GitHubRepoSpec, config: GitHubConfig): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Get GitHub configuration from environment variables and config file.
|
|
57
|
+
* Environment variables take precedence over config file.
|
|
58
|
+
*
|
|
59
|
+
* Environment variables:
|
|
60
|
+
* GITHUB_TOKEN - Authentication token for private repos
|
|
61
|
+
* GITHUB_POLL_INTERVAL_MS - Polling interval (0 to disable, default 300000)
|
|
62
|
+
* SKILLJACK_CACHE_DIR - Cache directory (default ~/.skilljack/github-cache)
|
|
63
|
+
* GITHUB_ALLOWED_ORGS - Comma-separated list of allowed organizations (overrides config)
|
|
64
|
+
* GITHUB_ALLOWED_USERS - Comma-separated list of allowed users (overrides config)
|
|
65
|
+
*/
|
|
66
|
+
export declare function getGitHubConfig(): GitHubConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Get the local cache path for a GitHub repository.
|
|
69
|
+
*
|
|
70
|
+
* @param spec - The repository specification
|
|
71
|
+
* @param cacheDir - Base cache directory
|
|
72
|
+
* @returns Full path to the cached repository (including subpath if specified)
|
|
73
|
+
*/
|
|
74
|
+
export declare function getRepoCachePath(spec: GitHubRepoSpec, cacheDir: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Get the local clone path for a GitHub repository (without subpath).
|
|
77
|
+
* This is where the git repository is cloned to.
|
|
78
|
+
*
|
|
79
|
+
* @param spec - The repository specification
|
|
80
|
+
* @param cacheDir - Base cache directory
|
|
81
|
+
* @returns Full path to the cloned repository root
|
|
82
|
+
*/
|
|
83
|
+
export declare function getRepoClonePath(spec: GitHubRepoSpec, cacheDir: string): string;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub configuration parsing and URL detection.
|
|
3
|
+
* Handles detection of GitHub URLs, parsing repo specs, and allowlist validation.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as os from "node:os";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
/**
|
|
9
|
+
* Default polling interval: 5 minutes.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_POLL_INTERVAL_MS = 5 * 60 * 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Default cache directory.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_CACHE_DIR = path.join(os.homedir(), ".skilljack", "github-cache");
|
|
16
|
+
/**
|
|
17
|
+
* Check if a path is a GitHub URL.
|
|
18
|
+
* Detects paths containing "github.com".
|
|
19
|
+
*/
|
|
20
|
+
export function isGitHubUrl(urlOrPath) {
|
|
21
|
+
return urlOrPath.toLowerCase().includes("github.com");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse a GitHub URL into a GitHubRepoSpec.
|
|
25
|
+
*
|
|
26
|
+
* Supported formats:
|
|
27
|
+
* github.com/owner/repo
|
|
28
|
+
* github.com/owner/repo@ref
|
|
29
|
+
* github.com/owner/repo/subpath
|
|
30
|
+
* github.com/owner/repo/subpath@ref
|
|
31
|
+
* https://github.com/owner/repo
|
|
32
|
+
* https://github.com/owner/repo.git
|
|
33
|
+
*
|
|
34
|
+
* @param url - The GitHub URL to parse
|
|
35
|
+
* @returns Parsed GitHubRepoSpec
|
|
36
|
+
* @throws Error if URL format is invalid
|
|
37
|
+
*/
|
|
38
|
+
export function parseGitHubUrl(url) {
|
|
39
|
+
// Remove protocol prefix if present
|
|
40
|
+
let normalized = url.replace(/^https?:\/\//, "");
|
|
41
|
+
// Remove github.com prefix
|
|
42
|
+
normalized = normalized.replace(/^github\.com\//i, "");
|
|
43
|
+
// Remove trailing .git if present
|
|
44
|
+
normalized = normalized.replace(/\.git$/, "");
|
|
45
|
+
// Extract ref if present (everything after @)
|
|
46
|
+
let ref;
|
|
47
|
+
const atIndex = normalized.lastIndexOf("@");
|
|
48
|
+
if (atIndex !== -1) {
|
|
49
|
+
ref = normalized.slice(atIndex + 1);
|
|
50
|
+
normalized = normalized.slice(0, atIndex);
|
|
51
|
+
}
|
|
52
|
+
// Split remaining path: owner/repo[/subpath...]
|
|
53
|
+
let parts = normalized.split("/").filter((p) => p.length > 0);
|
|
54
|
+
if (parts.length < 2) {
|
|
55
|
+
throw new Error(`Invalid GitHub URL: "${url}". Expected format: github.com/owner/repo[/subpath][@ref]`);
|
|
56
|
+
}
|
|
57
|
+
const owner = parts[0];
|
|
58
|
+
const repo = parts[1];
|
|
59
|
+
// Handle GitHub web URLs with /tree/<ref>/ or /blob/<ref>/ patterns
|
|
60
|
+
// e.g., owner/repo/tree/main/path/to/dir -> extract ref and subpath
|
|
61
|
+
if (parts.length >= 4 && (parts[2] === "tree" || parts[2] === "blob")) {
|
|
62
|
+
// parts[2] is "tree" or "blob", parts[3] is the ref
|
|
63
|
+
if (!ref) {
|
|
64
|
+
ref = parts[3];
|
|
65
|
+
}
|
|
66
|
+
// Everything after the ref is the subpath
|
|
67
|
+
const subpath = parts.length > 4 ? parts.slice(4).join("/") : undefined;
|
|
68
|
+
return { owner, repo, ref, subpath };
|
|
69
|
+
}
|
|
70
|
+
const subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
|
|
71
|
+
return { owner, repo, ref, subpath };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a repository is allowed by the allowlist.
|
|
75
|
+
* If no allowlist is configured (both allowedOrgs and allowedUsers empty),
|
|
76
|
+
* all repos are DENIED by default for security.
|
|
77
|
+
*
|
|
78
|
+
* @param spec - The repository specification
|
|
79
|
+
* @param config - GitHub configuration with allowlists
|
|
80
|
+
* @returns true if allowed, false if blocked
|
|
81
|
+
*/
|
|
82
|
+
export function isRepoAllowed(spec, config) {
|
|
83
|
+
// If no allowlist configured, deny all for security
|
|
84
|
+
if (config.allowedOrgs.length === 0 && config.allowedUsers.length === 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const ownerLower = spec.owner.toLowerCase();
|
|
88
|
+
// Check if owner is in allowed orgs
|
|
89
|
+
if (config.allowedOrgs.some((org) => org.toLowerCase() === ownerLower)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
// Check if owner is in allowed users
|
|
93
|
+
if (config.allowedUsers.some((user) => user.toLowerCase() === ownerLower)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Parse a comma-separated list from an environment variable.
|
|
100
|
+
*/
|
|
101
|
+
function parseCommaList(envValue) {
|
|
102
|
+
if (!envValue) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
return envValue
|
|
106
|
+
.split(",")
|
|
107
|
+
.map((s) => s.trim())
|
|
108
|
+
.filter((s) => s.length > 0);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Path to the config file.
|
|
112
|
+
*/
|
|
113
|
+
const CONFIG_FILE_PATH = path.join(os.homedir(), ".skilljack", "config.json");
|
|
114
|
+
/**
|
|
115
|
+
* Load allowlist from config file.
|
|
116
|
+
* Returns empty arrays if file doesn't exist or can't be parsed.
|
|
117
|
+
*/
|
|
118
|
+
function loadAllowlistFromConfig() {
|
|
119
|
+
try {
|
|
120
|
+
if (fs.existsSync(CONFIG_FILE_PATH)) {
|
|
121
|
+
const content = fs.readFileSync(CONFIG_FILE_PATH, "utf-8");
|
|
122
|
+
const config = JSON.parse(content);
|
|
123
|
+
return {
|
|
124
|
+
orgs: Array.isArray(config.githubAllowedOrgs) ? config.githubAllowedOrgs : [],
|
|
125
|
+
users: Array.isArray(config.githubAllowedUsers) ? config.githubAllowedUsers : [],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Ignore errors reading config file
|
|
131
|
+
}
|
|
132
|
+
return { orgs: [], users: [] };
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get GitHub configuration from environment variables and config file.
|
|
136
|
+
* Environment variables take precedence over config file.
|
|
137
|
+
*
|
|
138
|
+
* Environment variables:
|
|
139
|
+
* GITHUB_TOKEN - Authentication token for private repos
|
|
140
|
+
* GITHUB_POLL_INTERVAL_MS - Polling interval (0 to disable, default 300000)
|
|
141
|
+
* SKILLJACK_CACHE_DIR - Cache directory (default ~/.skilljack/github-cache)
|
|
142
|
+
* GITHUB_ALLOWED_ORGS - Comma-separated list of allowed organizations (overrides config)
|
|
143
|
+
* GITHUB_ALLOWED_USERS - Comma-separated list of allowed users (overrides config)
|
|
144
|
+
*/
|
|
145
|
+
export function getGitHubConfig() {
|
|
146
|
+
const pollIntervalStr = process.env.GITHUB_POLL_INTERVAL_MS;
|
|
147
|
+
let pollIntervalMs = DEFAULT_POLL_INTERVAL_MS;
|
|
148
|
+
if (pollIntervalStr !== undefined) {
|
|
149
|
+
const parsed = parseInt(pollIntervalStr, 10);
|
|
150
|
+
if (!isNaN(parsed) && parsed >= 0) {
|
|
151
|
+
pollIntervalMs = parsed;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Load allowlist from config file as fallback
|
|
155
|
+
const configAllowlist = loadAllowlistFromConfig();
|
|
156
|
+
// Environment variables override config file
|
|
157
|
+
const envOrgs = parseCommaList(process.env.GITHUB_ALLOWED_ORGS);
|
|
158
|
+
const envUsers = parseCommaList(process.env.GITHUB_ALLOWED_USERS);
|
|
159
|
+
return {
|
|
160
|
+
token: process.env.GITHUB_TOKEN,
|
|
161
|
+
pollIntervalMs,
|
|
162
|
+
cacheDir: process.env.SKILLJACK_CACHE_DIR || DEFAULT_CACHE_DIR,
|
|
163
|
+
allowedOrgs: envOrgs.length > 0 ? envOrgs : configAllowlist.orgs,
|
|
164
|
+
allowedUsers: envUsers.length > 0 ? envUsers : configAllowlist.users,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get the local cache path for a GitHub repository.
|
|
169
|
+
*
|
|
170
|
+
* @param spec - The repository specification
|
|
171
|
+
* @param cacheDir - Base cache directory
|
|
172
|
+
* @returns Full path to the cached repository (including subpath if specified)
|
|
173
|
+
*/
|
|
174
|
+
export function getRepoCachePath(spec, cacheDir) {
|
|
175
|
+
const repoPath = path.join(cacheDir, spec.owner, spec.repo);
|
|
176
|
+
if (spec.subpath) {
|
|
177
|
+
return path.join(repoPath, spec.subpath);
|
|
178
|
+
}
|
|
179
|
+
return repoPath;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get the local clone path for a GitHub repository (without subpath).
|
|
183
|
+
* This is where the git repository is cloned to.
|
|
184
|
+
*
|
|
185
|
+
* @param spec - The repository specification
|
|
186
|
+
* @param cacheDir - Base cache directory
|
|
187
|
+
* @returns Full path to the cloned repository root
|
|
188
|
+
*/
|
|
189
|
+
export function getRepoClonePath(spec, cacheDir) {
|
|
190
|
+
return path.join(cacheDir, spec.owner, spec.repo);
|
|
191
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub repository polling for updates.
|
|
3
|
+
* Periodically checks for changes and triggers sync when updates are available.
|
|
4
|
+
*/
|
|
5
|
+
import { GitHubRepoSpec } from "./github-config.js";
|
|
6
|
+
import { SyncOptions, SyncResult } from "./github-sync.js";
|
|
7
|
+
/**
|
|
8
|
+
* Options for the polling manager.
|
|
9
|
+
*/
|
|
10
|
+
export interface PollingOptions {
|
|
11
|
+
intervalMs: number;
|
|
12
|
+
onUpdate: (spec: GitHubRepoSpec, result: SyncResult) => void;
|
|
13
|
+
onError?: (spec: GitHubRepoSpec, error: Error) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Polling manager interface.
|
|
17
|
+
*/
|
|
18
|
+
export interface PollingManager {
|
|
19
|
+
start(): void;
|
|
20
|
+
stop(): void;
|
|
21
|
+
checkNow(): Promise<void>;
|
|
22
|
+
isRunning(): boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create a polling manager for GitHub repositories.
|
|
26
|
+
*
|
|
27
|
+
* The manager periodically checks for updates and syncs repositories
|
|
28
|
+
* when changes are detected. Pinned refs (tags, commits) are skipped.
|
|
29
|
+
*
|
|
30
|
+
* @param specs - Repository specifications to poll
|
|
31
|
+
* @param syncOptions - Options for sync operations
|
|
32
|
+
* @param pollingOptions - Polling configuration
|
|
33
|
+
* @returns Polling manager
|
|
34
|
+
*/
|
|
35
|
+
export declare function createPollingManager(specs: GitHubRepoSpec[], syncOptions: SyncOptions, pollingOptions: PollingOptions): PollingManager;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub repository polling for updates.
|
|
3
|
+
* Periodically checks for changes and triggers sync when updates are available.
|
|
4
|
+
*/
|
|
5
|
+
import { syncRepo, hasRemoteUpdates } from "./github-sync.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create a polling manager for GitHub repositories.
|
|
8
|
+
*
|
|
9
|
+
* The manager periodically checks for updates and syncs repositories
|
|
10
|
+
* when changes are detected. Pinned refs (tags, commits) are skipped.
|
|
11
|
+
*
|
|
12
|
+
* @param specs - Repository specifications to poll
|
|
13
|
+
* @param syncOptions - Options for sync operations
|
|
14
|
+
* @param pollingOptions - Polling configuration
|
|
15
|
+
* @returns Polling manager
|
|
16
|
+
*/
|
|
17
|
+
export function createPollingManager(specs, syncOptions, pollingOptions) {
|
|
18
|
+
let intervalId = null;
|
|
19
|
+
let isChecking = false;
|
|
20
|
+
/**
|
|
21
|
+
* Filter specs to only include those that should be polled.
|
|
22
|
+
* Pinned refs (tags, commits) are excluded.
|
|
23
|
+
*/
|
|
24
|
+
function getPolledSpecs() {
|
|
25
|
+
return specs.filter((spec) => {
|
|
26
|
+
if (!spec.ref) {
|
|
27
|
+
return true; // No ref means default branch, poll it
|
|
28
|
+
}
|
|
29
|
+
// Exclude what looks like a tag version or commit hash
|
|
30
|
+
const isVersionTag = /^v?\d+(\.\d+)*/.test(spec.ref);
|
|
31
|
+
const isCommitHash = /^[0-9a-f]{7,40}$/i.test(spec.ref);
|
|
32
|
+
return !isVersionTag && !isCommitHash;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check all repositories for updates.
|
|
37
|
+
*/
|
|
38
|
+
async function checkForUpdates() {
|
|
39
|
+
if (isChecking) {
|
|
40
|
+
console.error("Polling: Already checking for updates, skipping...");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
isChecking = true;
|
|
44
|
+
const polledSpecs = getPolledSpecs();
|
|
45
|
+
if (polledSpecs.length === 0) {
|
|
46
|
+
console.error("Polling: No repositories to poll (all pinned to specific refs)");
|
|
47
|
+
isChecking = false;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.error(`Polling: Checking ${polledSpecs.length} repo(s) for updates...`);
|
|
51
|
+
for (const spec of polledSpecs) {
|
|
52
|
+
try {
|
|
53
|
+
const hasUpdates = await hasRemoteUpdates(spec, syncOptions);
|
|
54
|
+
if (hasUpdates) {
|
|
55
|
+
console.error(`Polling: Updates available for ${spec.owner}/${spec.repo}`);
|
|
56
|
+
const result = await syncRepo(spec, syncOptions);
|
|
57
|
+
if (!result.error && result.updated) {
|
|
58
|
+
pollingOptions.onUpdate(spec, result);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
64
|
+
console.error(`Polling: Error checking ${spec.owner}/${spec.repo}: ${err.message}`);
|
|
65
|
+
pollingOptions.onError?.(spec, err);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
isChecking = false;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
start() {
|
|
72
|
+
if (intervalId !== null) {
|
|
73
|
+
console.error("Polling: Already running");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (pollingOptions.intervalMs <= 0) {
|
|
77
|
+
console.error("Polling: Disabled (interval <= 0)");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const polledSpecs = getPolledSpecs();
|
|
81
|
+
if (polledSpecs.length === 0) {
|
|
82
|
+
console.error("Polling: Not starting (all repos pinned to specific refs)");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.error(`Polling: Starting with ${pollingOptions.intervalMs}ms interval ` +
|
|
86
|
+
`for ${polledSpecs.length} repo(s)`);
|
|
87
|
+
intervalId = setInterval(() => {
|
|
88
|
+
checkForUpdates().catch((error) => {
|
|
89
|
+
console.error(`Polling: Unexpected error: ${error}`);
|
|
90
|
+
});
|
|
91
|
+
}, pollingOptions.intervalMs);
|
|
92
|
+
},
|
|
93
|
+
stop() {
|
|
94
|
+
if (intervalId === null) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.error("Polling: Stopping");
|
|
98
|
+
clearInterval(intervalId);
|
|
99
|
+
intervalId = null;
|
|
100
|
+
},
|
|
101
|
+
async checkNow() {
|
|
102
|
+
await checkForUpdates();
|
|
103
|
+
},
|
|
104
|
+
isRunning() {
|
|
105
|
+
return intervalId !== null;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub repository synchronization.
|
|
3
|
+
* Handles cloning and pulling repositories to local cache.
|
|
4
|
+
*/
|
|
5
|
+
import { GitHubRepoSpec } from "./github-config.js";
|
|
6
|
+
/**
|
|
7
|
+
* Options for syncing GitHub repositories.
|
|
8
|
+
*/
|
|
9
|
+
export interface SyncOptions {
|
|
10
|
+
cacheDir: string;
|
|
11
|
+
token?: string;
|
|
12
|
+
shallowClone?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of a sync operation.
|
|
16
|
+
*/
|
|
17
|
+
export interface SyncResult {
|
|
18
|
+
spec: GitHubRepoSpec;
|
|
19
|
+
localPath: string;
|
|
20
|
+
clonePath: string;
|
|
21
|
+
updated: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sync a single GitHub repository.
|
|
26
|
+
* Clones if not present, pulls if already cloned.
|
|
27
|
+
*
|
|
28
|
+
* @param spec - Repository specification
|
|
29
|
+
* @param options - Sync options
|
|
30
|
+
* @returns Sync result
|
|
31
|
+
*/
|
|
32
|
+
export declare function syncRepo(spec: GitHubRepoSpec, options: SyncOptions): Promise<SyncResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Sync multiple GitHub repositories.
|
|
35
|
+
*
|
|
36
|
+
* @param specs - Repository specifications
|
|
37
|
+
* @param options - Sync options
|
|
38
|
+
* @returns Array of sync results
|
|
39
|
+
*/
|
|
40
|
+
export declare function syncAllRepos(specs: GitHubRepoSpec[], options: SyncOptions): Promise<SyncResult[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a repository has remote updates available.
|
|
43
|
+
* Uses git fetch --dry-run to check without downloading.
|
|
44
|
+
*
|
|
45
|
+
* @param spec - Repository specification
|
|
46
|
+
* @param options - Sync options
|
|
47
|
+
* @returns true if updates are available
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasRemoteUpdates(spec: GitHubRepoSpec, options: SyncOptions): Promise<boolean>;
|