@open330/oac 2026.2.5
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 +115 -0
- package/LICENSE +21 -0
- package/README.md +597 -0
- package/dist/budget/index.d.ts +117 -0
- package/dist/budget/index.js +23 -0
- package/dist/budget/index.js.map +1 -0
- package/dist/chunk-4IUL7ECC.js +3152 -0
- package/dist/chunk-4IUL7ECC.js.map +1 -0
- package/dist/chunk-5GAUWC3L.js +469 -0
- package/dist/chunk-5GAUWC3L.js.map +1 -0
- package/dist/chunk-6A37SKAJ.js +58 -0
- package/dist/chunk-6A37SKAJ.js.map +1 -0
- package/dist/chunk-7C7SC4TZ.js +358 -0
- package/dist/chunk-7C7SC4TZ.js.map +1 -0
- package/dist/chunk-CJAJ4MBO.js +475 -0
- package/dist/chunk-CJAJ4MBO.js.map +1 -0
- package/dist/chunk-LQC5DLT7.js +317 -0
- package/dist/chunk-LQC5DLT7.js.map +1 -0
- package/dist/chunk-OTPXGXO7.js +2368 -0
- package/dist/chunk-OTPXGXO7.js.map +1 -0
- package/dist/chunk-QPVNC7S4.js +1833 -0
- package/dist/chunk-QPVNC7S4.js.map +1 -0
- package/dist/cli/cli.d.ts +13 -0
- package/dist/cli/cli.js +16 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/completion/index.d.ts +91 -0
- package/dist/completion/index.js +587 -0
- package/dist/completion/index.js.map +1 -0
- package/dist/config-DequKoFA.d.ts +1468 -0
- package/dist/core/index.d.ts +64 -0
- package/dist/core/index.js +87 -0
- package/dist/core/index.js.map +1 -0
- package/dist/dashboard/index.d.ts +14 -0
- package/dist/dashboard/index.js +1253 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/discovery/index.d.ts +285 -0
- package/dist/discovery/index.js +50 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/event-bus-KiuR6e3P.d.ts +91 -0
- package/dist/execution/index.d.ts +215 -0
- package/dist/execution/index.js +27 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/repo/index.d.ts +33 -0
- package/dist/repo/index.js +19 -0
- package/dist/repo/index.js.map +1 -0
- package/dist/tracking/index.d.ts +357 -0
- package/dist/tracking/index.js +15 -0
- package/dist/tracking/index.js.map +1 -0
- package/dist/types-CYCwgojB.d.ts +34 -0
- package/dist/types-Ck7IucqK.d.ts +195 -0
- package/docs/config-reference.md +271 -0
- package/docs/multi-agent-support-technical-spec.md +312 -0
- package/package.json +82 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
// src/repo/resolver.ts
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import { homedir as homedir2 } from "os";
|
|
4
|
+
import { join as join2 } from "path";
|
|
5
|
+
import { Octokit } from "@octokit/rest";
|
|
6
|
+
|
|
7
|
+
// src/repo/metadata-cache.ts
|
|
8
|
+
import { constants as fsConstants } from "fs";
|
|
9
|
+
import { access, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
var DEFAULT_METADATA_CACHE_PATH = join(homedir(), ".oac", "cache", "repos.json");
|
|
13
|
+
var DEFAULT_METADATA_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
14
|
+
var EMPTY_CACHE = {
|
|
15
|
+
version: 1,
|
|
16
|
+
entries: {}
|
|
17
|
+
};
|
|
18
|
+
var MetadataCache = class {
|
|
19
|
+
filePath;
|
|
20
|
+
ttlMs;
|
|
21
|
+
now;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.filePath = expandHomePath(options.filePath ?? DEFAULT_METADATA_CACHE_PATH);
|
|
24
|
+
this.ttlMs = options.ttlMs ?? DEFAULT_METADATA_CACHE_TTL_MS;
|
|
25
|
+
this.now = options.now ?? Date.now;
|
|
26
|
+
}
|
|
27
|
+
async get(fullName) {
|
|
28
|
+
const cache = await this.readCache();
|
|
29
|
+
const key = normalizeCacheKey(fullName);
|
|
30
|
+
const entry = cache.entries[key];
|
|
31
|
+
if (!entry) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (this.now() - entry.cachedAt > this.ttlMs) {
|
|
35
|
+
delete cache.entries[key];
|
|
36
|
+
await this.writeCache(cache);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return entry.repo;
|
|
40
|
+
}
|
|
41
|
+
async set(fullName, repo) {
|
|
42
|
+
const cache = await this.readCache();
|
|
43
|
+
const key = normalizeCacheKey(fullName);
|
|
44
|
+
cache.entries[key] = {
|
|
45
|
+
cachedAt: this.now(),
|
|
46
|
+
repo
|
|
47
|
+
};
|
|
48
|
+
await this.writeCache(cache);
|
|
49
|
+
}
|
|
50
|
+
async invalidate(fullName) {
|
|
51
|
+
if (!fullName) {
|
|
52
|
+
await this.writeCache(EMPTY_CACHE);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const cache = await this.readCache();
|
|
56
|
+
const key = normalizeCacheKey(fullName);
|
|
57
|
+
if (!(key in cache.entries)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
delete cache.entries[key];
|
|
61
|
+
await this.writeCache(cache);
|
|
62
|
+
}
|
|
63
|
+
async readCache() {
|
|
64
|
+
if (!await pathExists(this.filePath)) {
|
|
65
|
+
return { ...EMPTY_CACHE, entries: {} };
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const raw = await readFile(this.filePath, "utf8");
|
|
69
|
+
const parsed = JSON.parse(raw);
|
|
70
|
+
if (parsed.version !== 1 || typeof parsed.entries !== "object") {
|
|
71
|
+
return { ...EMPTY_CACHE, entries: {} };
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
version: 1,
|
|
75
|
+
entries: parsed.entries
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return { ...EMPTY_CACHE, entries: {} };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async writeCache(cache) {
|
|
82
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
83
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
84
|
+
await writeFile(tempPath, JSON.stringify(cache, null, 2), "utf8");
|
|
85
|
+
await rename(tempPath, this.filePath);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function normalizeCacheKey(fullName) {
|
|
89
|
+
return fullName.trim().toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
function expandHomePath(path) {
|
|
92
|
+
if (path === "~") {
|
|
93
|
+
return homedir();
|
|
94
|
+
}
|
|
95
|
+
if (path.startsWith("~/")) {
|
|
96
|
+
return join(homedir(), path.slice(2));
|
|
97
|
+
}
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
100
|
+
async function pathExists(path) {
|
|
101
|
+
try {
|
|
102
|
+
await access(path, fsConstants.F_OK);
|
|
103
|
+
return true;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/repo/resolver.ts
|
|
110
|
+
var OWNER_REPO_PATTERN = /^(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+?)(?:\.git)?$/;
|
|
111
|
+
var GITHUB_SSH_PATTERN = /^git@github\.com:(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+?)(?:\.git)?$/;
|
|
112
|
+
var RepoResolutionError = class extends Error {
|
|
113
|
+
code;
|
|
114
|
+
constructor(message, code, cause) {
|
|
115
|
+
super(message, { cause });
|
|
116
|
+
this.name = "RepoResolutionError";
|
|
117
|
+
this.code = code;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
async function resolveRepo(input) {
|
|
121
|
+
const parsed = parseRepoInput(input);
|
|
122
|
+
const cache = new MetadataCache();
|
|
123
|
+
const cacheKey = `${parsed.owner}/${parsed.name}`;
|
|
124
|
+
const cached = await cache.get(cacheKey);
|
|
125
|
+
if (cached) {
|
|
126
|
+
return cached;
|
|
127
|
+
}
|
|
128
|
+
const octokit = new Octokit({
|
|
129
|
+
auth: resolveGitHubToken()
|
|
130
|
+
});
|
|
131
|
+
const repoData = await fetchRepo(octokit, parsed.owner, parsed.name);
|
|
132
|
+
if (repoData.archived) {
|
|
133
|
+
throw new RepoResolutionError(
|
|
134
|
+
`Repository "${repoData.full_name}" is archived and cannot be used for contributions.`,
|
|
135
|
+
"ARCHIVED"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const permissions = normalizePermissions(repoData.private, repoData.permissions);
|
|
139
|
+
if (!permissions.pull) {
|
|
140
|
+
throw new RepoResolutionError(
|
|
141
|
+
`Missing pull permission for "${repoData.full_name}".`,
|
|
142
|
+
"FORBIDDEN"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const [languages, headSha] = await Promise.all([
|
|
146
|
+
fetchLanguages(octokit, repoData.owner.login, repoData.name),
|
|
147
|
+
fetchHeadSha(octokit, repoData.owner.login, repoData.name, repoData.default_branch)
|
|
148
|
+
]);
|
|
149
|
+
const localPath = defaultLocalPath(repoData.owner.login, repoData.name);
|
|
150
|
+
const resolved = {
|
|
151
|
+
fullName: repoData.full_name,
|
|
152
|
+
owner: repoData.owner.login,
|
|
153
|
+
name: repoData.name,
|
|
154
|
+
localPath,
|
|
155
|
+
worktreePath: join2(localPath, "..", ".oac-worktrees", repoData.default_branch),
|
|
156
|
+
meta: {
|
|
157
|
+
defaultBranch: repoData.default_branch,
|
|
158
|
+
language: repoData.language,
|
|
159
|
+
languages,
|
|
160
|
+
size: repoData.size,
|
|
161
|
+
stars: repoData.stargazers_count,
|
|
162
|
+
openIssuesCount: repoData.open_issues_count,
|
|
163
|
+
topics: repoData.topics ?? [],
|
|
164
|
+
license: normalizeLicense(repoData.license?.spdx_id ?? null),
|
|
165
|
+
isArchived: repoData.archived,
|
|
166
|
+
isFork: repoData.fork,
|
|
167
|
+
permissions
|
|
168
|
+
},
|
|
169
|
+
git: {
|
|
170
|
+
headSha,
|
|
171
|
+
remoteUrl: repoData.clone_url ?? `https://github.com/${repoData.full_name}.git`,
|
|
172
|
+
isShallowClone: true
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
await cache.set(resolved.fullName, resolved);
|
|
176
|
+
return resolved;
|
|
177
|
+
}
|
|
178
|
+
function parseRepoInput(input) {
|
|
179
|
+
const normalized = input.trim();
|
|
180
|
+
if (!normalized) {
|
|
181
|
+
throw new RepoResolutionError("Repository input cannot be empty.", "INVALID_INPUT");
|
|
182
|
+
}
|
|
183
|
+
const ownerRepoMatch = normalized.match(OWNER_REPO_PATTERN);
|
|
184
|
+
if (ownerRepoMatch?.groups) {
|
|
185
|
+
return {
|
|
186
|
+
owner: ownerRepoMatch.groups.owner,
|
|
187
|
+
name: ownerRepoMatch.groups.repo
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const sshMatch = normalized.match(GITHUB_SSH_PATTERN);
|
|
191
|
+
if (sshMatch?.groups) {
|
|
192
|
+
return {
|
|
193
|
+
owner: sshMatch.groups.owner,
|
|
194
|
+
name: sshMatch.groups.repo
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const normalizedUrlInput = normalized.startsWith("github.com/") ? `https://${normalized}` : normalized;
|
|
198
|
+
try {
|
|
199
|
+
const url = new URL(normalizedUrlInput);
|
|
200
|
+
if (!isGitHubHost(url.hostname)) {
|
|
201
|
+
throw new RepoResolutionError(
|
|
202
|
+
`Only github.com repository URLs are supported, received "${url.hostname}".`,
|
|
203
|
+
"INVALID_INPUT"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
207
|
+
if (pathParts.length < 2) {
|
|
208
|
+
throw new RepoResolutionError(`Invalid GitHub repository URL "${input}".`, "INVALID_INPUT");
|
|
209
|
+
}
|
|
210
|
+
const owner = pathParts[0];
|
|
211
|
+
const name = stripGitSuffix(pathParts[1]);
|
|
212
|
+
if (!owner || !name) {
|
|
213
|
+
throw new RepoResolutionError(`Invalid GitHub repository URL "${input}".`, "INVALID_INPUT");
|
|
214
|
+
}
|
|
215
|
+
return { owner, name };
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error instanceof RepoResolutionError) {
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
throw new RepoResolutionError(
|
|
221
|
+
`Expected "owner/repo" or a GitHub repository URL, received "${input}".`,
|
|
222
|
+
"INVALID_INPUT",
|
|
223
|
+
error
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function fetchRepo(octokit, owner, repo) {
|
|
228
|
+
try {
|
|
229
|
+
return (await octokit.repos.get({ owner, repo })).data;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
throw toResolutionError(owner, repo, error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function fetchLanguages(octokit, owner, repo) {
|
|
235
|
+
try {
|
|
236
|
+
const response = await octokit.repos.listLanguages({ owner, repo });
|
|
237
|
+
return response.data;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
throw toResolutionError(owner, repo, error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function fetchHeadSha(octokit, owner, repo, defaultBranch) {
|
|
243
|
+
try {
|
|
244
|
+
const branch = await octokit.repos.getBranch({
|
|
245
|
+
owner,
|
|
246
|
+
repo,
|
|
247
|
+
branch: defaultBranch
|
|
248
|
+
});
|
|
249
|
+
return branch.data.commit.sha;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
throw toResolutionError(owner, repo, error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function toResolutionError(owner, repo, error) {
|
|
255
|
+
const fullName = `${owner}/${repo}`;
|
|
256
|
+
const status = isApiError(error) ? error.status : void 0;
|
|
257
|
+
const message = typeof error === "object" && error && "message" in error ? String(error.message) : "unknown error";
|
|
258
|
+
if (status === 404) {
|
|
259
|
+
const hasToken = !!(process.env.GITHUB_TOKEN || process.env.GH_TOKEN);
|
|
260
|
+
const hint = hasToken ? `If this is a private repo, ensure your token has the "repo" scope: gh auth refresh -s repo` : "If this is a private repo, authenticate first: gh auth login";
|
|
261
|
+
return new RepoResolutionError(
|
|
262
|
+
`Repository "${fullName}" was not found on GitHub. ${hint}`,
|
|
263
|
+
"NOT_FOUND",
|
|
264
|
+
error
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (status === 403) {
|
|
268
|
+
return new RepoResolutionError(
|
|
269
|
+
`Access denied for "${fullName}". Ensure your token has the "repo" scope: gh auth refresh -s repo`,
|
|
270
|
+
"FORBIDDEN",
|
|
271
|
+
error
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return new RepoResolutionError(
|
|
275
|
+
`Failed to resolve repository "${fullName}": ${message}`,
|
|
276
|
+
"UNKNOWN",
|
|
277
|
+
error
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
function normalizePermissions(isPrivateRepo, permissions) {
|
|
281
|
+
const pull = permissions?.pull ?? !isPrivateRepo;
|
|
282
|
+
return {
|
|
283
|
+
push: permissions?.push ?? false,
|
|
284
|
+
pull,
|
|
285
|
+
admin: permissions?.admin ?? false
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function normalizeLicense(spdxId) {
|
|
289
|
+
if (!spdxId || spdxId === "NOASSERTION") {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
return spdxId;
|
|
293
|
+
}
|
|
294
|
+
function isGitHubHost(hostname) {
|
|
295
|
+
const normalized = hostname.toLowerCase();
|
|
296
|
+
return normalized === "github.com" || normalized === "www.github.com";
|
|
297
|
+
}
|
|
298
|
+
function stripGitSuffix(repo) {
|
|
299
|
+
return repo.replace(/\.git$/i, "");
|
|
300
|
+
}
|
|
301
|
+
function isApiError(error) {
|
|
302
|
+
return typeof error === "object" && error !== null && "status" in error;
|
|
303
|
+
}
|
|
304
|
+
function defaultLocalPath(owner, name) {
|
|
305
|
+
return join2(homedir2(), ".oac", "cache", "repos", owner, name);
|
|
306
|
+
}
|
|
307
|
+
function resolveGitHubToken() {
|
|
308
|
+
const githubToken = process.env.GITHUB_TOKEN?.trim();
|
|
309
|
+
if (githubToken) {
|
|
310
|
+
process.env.GITHUB_TOKEN = githubToken;
|
|
311
|
+
return githubToken;
|
|
312
|
+
}
|
|
313
|
+
const ghToken = process.env.GH_TOKEN?.trim();
|
|
314
|
+
if (ghToken) {
|
|
315
|
+
process.env.GITHUB_TOKEN = ghToken;
|
|
316
|
+
return ghToken;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
320
|
+
timeout: 5e3,
|
|
321
|
+
encoding: "utf-8",
|
|
322
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
323
|
+
}).trim();
|
|
324
|
+
if (token.length > 0) {
|
|
325
|
+
process.env.GITHUB_TOKEN = token;
|
|
326
|
+
return token;
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/repo/cloner.ts
|
|
334
|
+
import { constants as fsConstants2 } from "fs";
|
|
335
|
+
import { access as access2, mkdir as mkdir2 } from "fs/promises";
|
|
336
|
+
import { homedir as homedir3 } from "os";
|
|
337
|
+
import { dirname as dirname2, join as join3, resolve } from "path";
|
|
338
|
+
import { simpleGit } from "simple-git";
|
|
339
|
+
var CLONE_RETRY_BACKOFF_MS = [1e3, 4e3, 16e3];
|
|
340
|
+
var DEFAULT_REPO_CACHE_DIR = join3(homedir3(), ".oac", "cache", "repos");
|
|
341
|
+
async function cloneRepo(repo, cacheDir = DEFAULT_REPO_CACHE_DIR) {
|
|
342
|
+
const cacheRoot = resolveCacheDir(cacheDir);
|
|
343
|
+
const localPath = join3(cacheRoot, repo.owner, repo.name);
|
|
344
|
+
await mkdir2(dirname2(localPath), { recursive: true });
|
|
345
|
+
if (await isGitRepository(localPath)) {
|
|
346
|
+
await pullExistingClone(repo, localPath);
|
|
347
|
+
} else if (await pathExists2(localPath)) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`Cannot clone "${repo.fullName}" into "${localPath}" because the directory exists and is not a git repository.`
|
|
350
|
+
);
|
|
351
|
+
} else {
|
|
352
|
+
await cloneNewRepository(repo, localPath);
|
|
353
|
+
}
|
|
354
|
+
const git = simpleGit(localPath);
|
|
355
|
+
repo.localPath = localPath;
|
|
356
|
+
repo.worktreePath = join3(localPath, "..", ".oac-worktrees", repo.meta.defaultBranch);
|
|
357
|
+
repo.git.headSha = (await git.revparse(["HEAD"])).trim();
|
|
358
|
+
repo.git.isShallowClone = await isShallowClone(git);
|
|
359
|
+
repo.git.remoteUrl = await getOriginUrl(git, repo.git.remoteUrl);
|
|
360
|
+
return localPath;
|
|
361
|
+
}
|
|
362
|
+
async function cloneNewRepository(repo, localPath) {
|
|
363
|
+
const git = simpleGit();
|
|
364
|
+
await retryGitOperation(
|
|
365
|
+
() => git.clone(repo.git.remoteUrl, localPath, [
|
|
366
|
+
"--depth",
|
|
367
|
+
"1",
|
|
368
|
+
"--branch",
|
|
369
|
+
repo.meta.defaultBranch
|
|
370
|
+
]),
|
|
371
|
+
`clone ${repo.fullName}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
async function pullExistingClone(repo, localPath) {
|
|
375
|
+
const git = simpleGit(localPath);
|
|
376
|
+
await ensureOriginRemote(git, repo.git.remoteUrl);
|
|
377
|
+
await retryGitOperation(
|
|
378
|
+
() => git.fetch("origin", repo.meta.defaultBranch, ["--depth=1", "--prune"]),
|
|
379
|
+
`fetch ${repo.fullName}`
|
|
380
|
+
);
|
|
381
|
+
await checkoutDefaultBranch(git, repo.meta.defaultBranch);
|
|
382
|
+
await retryGitOperation(
|
|
383
|
+
() => hardSyncDefaultBranch(git, repo.meta.defaultBranch),
|
|
384
|
+
`sync ${repo.fullName}`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
async function checkoutDefaultBranch(git, branchName) {
|
|
388
|
+
try {
|
|
389
|
+
await git.checkout(branchName);
|
|
390
|
+
} catch {
|
|
391
|
+
await git.raw(["checkout", "-B", branchName, `origin/${branchName}`]);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function hardSyncDefaultBranch(git, branchName) {
|
|
395
|
+
await git.raw(["reset", "--hard", `origin/${branchName}`]);
|
|
396
|
+
await git.raw(["clean", "-fd"]);
|
|
397
|
+
}
|
|
398
|
+
async function ensureOriginRemote(git, remoteUrl) {
|
|
399
|
+
const remotes = await git.getRemotes(true);
|
|
400
|
+
const origin = remotes.find((remote) => remote.name === "origin");
|
|
401
|
+
if (!origin) {
|
|
402
|
+
await git.addRemote("origin", remoteUrl);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (origin.refs.fetch !== remoteUrl && origin.refs.push !== remoteUrl) {
|
|
406
|
+
await git.remote(["set-url", "origin", remoteUrl]);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
async function getOriginUrl(git, fallbackUrl) {
|
|
410
|
+
const remotes = await git.getRemotes(true);
|
|
411
|
+
const origin = remotes.find((remote) => remote.name === "origin");
|
|
412
|
+
return origin?.refs.fetch ?? fallbackUrl;
|
|
413
|
+
}
|
|
414
|
+
async function isShallowClone(git) {
|
|
415
|
+
const output = await git.raw(["rev-parse", "--is-shallow-repository"]);
|
|
416
|
+
return output.trim() === "true";
|
|
417
|
+
}
|
|
418
|
+
async function retryGitOperation(operation, operationName) {
|
|
419
|
+
let lastError;
|
|
420
|
+
for (let attempt = 0; attempt <= CLONE_RETRY_BACKOFF_MS.length; attempt += 1) {
|
|
421
|
+
try {
|
|
422
|
+
return await operation();
|
|
423
|
+
} catch (error) {
|
|
424
|
+
lastError = error;
|
|
425
|
+
if (attempt === CLONE_RETRY_BACKOFF_MS.length) {
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
await sleep(CLONE_RETRY_BACKOFF_MS[attempt]);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Git operation failed after ${CLONE_RETRY_BACKOFF_MS.length + 1} attempts (${operationName}).`,
|
|
433
|
+
{ cause: lastError }
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
function resolveCacheDir(cacheDir) {
|
|
437
|
+
const selected = cacheDir.trim().length > 0 ? cacheDir : DEFAULT_REPO_CACHE_DIR;
|
|
438
|
+
return resolve(expandHomePath2(selected));
|
|
439
|
+
}
|
|
440
|
+
function expandHomePath2(path) {
|
|
441
|
+
if (path === "~") {
|
|
442
|
+
return homedir3();
|
|
443
|
+
}
|
|
444
|
+
if (path.startsWith("~/")) {
|
|
445
|
+
return join3(homedir3(), path.slice(2));
|
|
446
|
+
}
|
|
447
|
+
return path;
|
|
448
|
+
}
|
|
449
|
+
async function pathExists2(path) {
|
|
450
|
+
try {
|
|
451
|
+
await access2(path, fsConstants2.F_OK);
|
|
452
|
+
return true;
|
|
453
|
+
} catch {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
async function isGitRepository(path) {
|
|
458
|
+
return pathExists2(join3(path, ".git"));
|
|
459
|
+
}
|
|
460
|
+
function sleep(ms) {
|
|
461
|
+
return new Promise((resolvePromise) => {
|
|
462
|
+
setTimeout(resolvePromise, ms);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export {
|
|
467
|
+
DEFAULT_METADATA_CACHE_PATH,
|
|
468
|
+
DEFAULT_METADATA_CACHE_TTL_MS,
|
|
469
|
+
MetadataCache,
|
|
470
|
+
RepoResolutionError,
|
|
471
|
+
resolveRepo,
|
|
472
|
+
DEFAULT_REPO_CACHE_DIR,
|
|
473
|
+
cloneRepo
|
|
474
|
+
};
|
|
475
|
+
//# sourceMappingURL=chunk-CJAJ4MBO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/repo/resolver.ts","../src/repo/metadata-cache.ts","../src/repo/cloner.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Octokit } from \"@octokit/rest\";\nimport { MetadataCache } from \"./metadata-cache.js\";\nimport type { RepoPermissions, ResolvedRepo } from \"./types.js\";\n\nconst OWNER_REPO_PATTERN = /^(?<owner>[A-Za-z0-9_.-]+)\\/(?<repo>[A-Za-z0-9_.-]+?)(?:\\.git)?$/;\nconst GITHUB_SSH_PATTERN =\n /^git@github\\.com:(?<owner>[A-Za-z0-9_.-]+)\\/(?<repo>[A-Za-z0-9_.-]+?)(?:\\.git)?$/;\n\nexport type RepoResolutionErrorCode =\n | \"INVALID_INPUT\"\n | \"NOT_FOUND\"\n | \"FORBIDDEN\"\n | \"ARCHIVED\"\n | \"UNKNOWN\";\n\nexport class RepoResolutionError extends Error {\n public readonly code: RepoResolutionErrorCode;\n\n public constructor(message: string, code: RepoResolutionErrorCode, cause?: unknown) {\n super(message, { cause });\n this.name = \"RepoResolutionError\";\n this.code = code;\n }\n}\n\ninterface ParsedRepoInput {\n owner: string;\n name: string;\n}\n\nexport async function resolveRepo(input: string): Promise<ResolvedRepo> {\n const parsed = parseRepoInput(input);\n const cache = new MetadataCache();\n const cacheKey = `${parsed.owner}/${parsed.name}`;\n const cached = await cache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const octokit = new Octokit({\n auth: resolveGitHubToken(),\n });\n\n const repoData = await fetchRepo(octokit, parsed.owner, parsed.name);\n if (repoData.archived) {\n throw new RepoResolutionError(\n `Repository \"${repoData.full_name}\" is archived and cannot be used for contributions.`,\n \"ARCHIVED\",\n );\n }\n\n const permissions = normalizePermissions(repoData.private, repoData.permissions);\n if (!permissions.pull) {\n throw new RepoResolutionError(\n `Missing pull permission for \"${repoData.full_name}\".`,\n \"FORBIDDEN\",\n );\n }\n\n const [languages, headSha] = await Promise.all([\n fetchLanguages(octokit, repoData.owner.login, repoData.name),\n fetchHeadSha(octokit, repoData.owner.login, repoData.name, repoData.default_branch),\n ]);\n\n const localPath = defaultLocalPath(repoData.owner.login, repoData.name);\n const resolved: ResolvedRepo = {\n fullName: repoData.full_name,\n owner: repoData.owner.login,\n name: repoData.name,\n localPath,\n worktreePath: join(localPath, \"..\", \".oac-worktrees\", repoData.default_branch),\n meta: {\n defaultBranch: repoData.default_branch,\n language: repoData.language,\n languages,\n size: repoData.size,\n stars: repoData.stargazers_count,\n openIssuesCount: repoData.open_issues_count,\n topics: repoData.topics ?? [],\n license: normalizeLicense(repoData.license?.spdx_id ?? null),\n isArchived: repoData.archived,\n isFork: repoData.fork,\n permissions,\n },\n git: {\n headSha,\n remoteUrl: repoData.clone_url ?? `https://github.com/${repoData.full_name}.git`,\n isShallowClone: true,\n },\n };\n\n await cache.set(resolved.fullName, resolved);\n return resolved;\n}\n\nfunction parseRepoInput(input: string): ParsedRepoInput {\n const normalized = input.trim();\n if (!normalized) {\n throw new RepoResolutionError(\"Repository input cannot be empty.\", \"INVALID_INPUT\");\n }\n\n const ownerRepoMatch = normalized.match(OWNER_REPO_PATTERN);\n if (ownerRepoMatch?.groups) {\n return {\n owner: ownerRepoMatch.groups.owner,\n name: ownerRepoMatch.groups.repo,\n };\n }\n\n const sshMatch = normalized.match(GITHUB_SSH_PATTERN);\n if (sshMatch?.groups) {\n return {\n owner: sshMatch.groups.owner,\n name: sshMatch.groups.repo,\n };\n }\n\n const normalizedUrlInput = normalized.startsWith(\"github.com/\")\n ? `https://${normalized}`\n : normalized;\n\n try {\n const url = new URL(normalizedUrlInput);\n if (!isGitHubHost(url.hostname)) {\n throw new RepoResolutionError(\n `Only github.com repository URLs are supported, received \"${url.hostname}\".`,\n \"INVALID_INPUT\",\n );\n }\n\n const pathParts = url.pathname.split(\"/\").filter(Boolean);\n if (pathParts.length < 2) {\n throw new RepoResolutionError(`Invalid GitHub repository URL \"${input}\".`, \"INVALID_INPUT\");\n }\n\n const owner = pathParts[0];\n const name = stripGitSuffix(pathParts[1]);\n if (!owner || !name) {\n throw new RepoResolutionError(`Invalid GitHub repository URL \"${input}\".`, \"INVALID_INPUT\");\n }\n\n return { owner, name };\n } catch (error) {\n if (error instanceof RepoResolutionError) {\n throw error;\n }\n\n throw new RepoResolutionError(\n `Expected \"owner/repo\" or a GitHub repository URL, received \"${input}\".`,\n \"INVALID_INPUT\",\n error,\n );\n }\n}\n\nasync function fetchRepo(octokit: Octokit, owner: string, repo: string) {\n try {\n return (await octokit.repos.get({ owner, repo })).data;\n } catch (error) {\n throw toResolutionError(owner, repo, error);\n }\n}\n\nasync function fetchLanguages(\n octokit: Octokit,\n owner: string,\n repo: string,\n): Promise<Record<string, number>> {\n try {\n const response = await octokit.repos.listLanguages({ owner, repo });\n return response.data;\n } catch (error) {\n throw toResolutionError(owner, repo, error);\n }\n}\n\nasync function fetchHeadSha(\n octokit: Octokit,\n owner: string,\n repo: string,\n defaultBranch: string,\n): Promise<string> {\n try {\n const branch = await octokit.repos.getBranch({\n owner,\n repo,\n branch: defaultBranch,\n });\n return branch.data.commit.sha;\n } catch (error) {\n throw toResolutionError(owner, repo, error);\n }\n}\n\nfunction toResolutionError(owner: string, repo: string, error: unknown): RepoResolutionError {\n const fullName = `${owner}/${repo}`;\n const status = isApiError(error) ? error.status : undefined;\n const message =\n typeof error === \"object\" && error && \"message\" in error\n ? String(error.message)\n : \"unknown error\";\n\n if (status === 404) {\n const hasToken = !!(process.env.GITHUB_TOKEN || process.env.GH_TOKEN);\n const hint = hasToken\n ? `If this is a private repo, ensure your token has the \"repo\" scope: gh auth refresh -s repo`\n : \"If this is a private repo, authenticate first: gh auth login\";\n return new RepoResolutionError(\n `Repository \"${fullName}\" was not found on GitHub. ${hint}`,\n \"NOT_FOUND\",\n error,\n );\n }\n\n if (status === 403) {\n return new RepoResolutionError(\n `Access denied for \"${fullName}\". Ensure your token has the \"repo\" scope: gh auth refresh -s repo`,\n \"FORBIDDEN\",\n error,\n );\n }\n\n return new RepoResolutionError(\n `Failed to resolve repository \"${fullName}\": ${message}`,\n \"UNKNOWN\",\n error,\n );\n}\n\nfunction normalizePermissions(\n isPrivateRepo: boolean,\n permissions:\n | {\n admin?: boolean;\n push?: boolean;\n pull?: boolean;\n }\n | undefined,\n): RepoPermissions {\n const pull = permissions?.pull ?? !isPrivateRepo;\n\n return {\n push: permissions?.push ?? false,\n pull,\n admin: permissions?.admin ?? false,\n };\n}\n\nfunction normalizeLicense(spdxId: string | null): string | null {\n if (!spdxId || spdxId === \"NOASSERTION\") {\n return null;\n }\n\n return spdxId;\n}\n\nfunction isGitHubHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase();\n return normalized === \"github.com\" || normalized === \"www.github.com\";\n}\n\nfunction stripGitSuffix(repo: string): string {\n return repo.replace(/\\.git$/i, \"\");\n}\n\nfunction isApiError(error: unknown): error is { status?: number } {\n return typeof error === \"object\" && error !== null && \"status\" in error;\n}\n\nfunction defaultLocalPath(owner: string, name: string): string {\n return join(homedir(), \".oac\", \"cache\", \"repos\", owner, name);\n}\n\nfunction resolveGitHubToken(): string | undefined {\n const githubToken = process.env.GITHUB_TOKEN?.trim();\n if (githubToken) {\n process.env.GITHUB_TOKEN = githubToken;\n return githubToken;\n }\n\n const ghToken = process.env.GH_TOKEN?.trim();\n if (ghToken) {\n process.env.GITHUB_TOKEN = ghToken;\n return ghToken;\n }\n\n try {\n const token = execFileSync(\"gh\", [\"auth\", \"token\"], {\n timeout: 5_000,\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n\n if (token.length > 0) {\n process.env.GITHUB_TOKEN = token;\n return token;\n }\n } catch {\n // gh not installed or not authenticated\n }\n\n return undefined;\n}\n","import { constants as fsConstants } from \"node:fs\";\nimport { access, mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { ResolvedRepo } from \"./types.js\";\n\ninterface MetadataCacheEntry {\n cachedAt: number;\n repo: ResolvedRepo;\n}\n\ninterface MetadataCacheFile {\n version: 1;\n entries: Record<string, MetadataCacheEntry>;\n}\n\nexport interface MetadataCacheOptions {\n filePath?: string;\n ttlMs?: number;\n now?: () => number;\n}\n\nexport const DEFAULT_METADATA_CACHE_PATH = join(homedir(), \".oac\", \"cache\", \"repos.json\");\n\nexport const DEFAULT_METADATA_CACHE_TTL_MS = 60 * 60 * 1000;\n\nconst EMPTY_CACHE: MetadataCacheFile = {\n version: 1,\n entries: {},\n};\n\nexport class MetadataCache {\n private readonly filePath: string;\n private readonly ttlMs: number;\n private readonly now: () => number;\n\n public constructor(options: MetadataCacheOptions = {}) {\n this.filePath = expandHomePath(options.filePath ?? DEFAULT_METADATA_CACHE_PATH);\n this.ttlMs = options.ttlMs ?? DEFAULT_METADATA_CACHE_TTL_MS;\n this.now = options.now ?? Date.now;\n }\n\n public async get(fullName: string): Promise<ResolvedRepo | null> {\n const cache = await this.readCache();\n const key = normalizeCacheKey(fullName);\n const entry = cache.entries[key];\n\n if (!entry) {\n return null;\n }\n\n if (this.now() - entry.cachedAt > this.ttlMs) {\n delete cache.entries[key];\n await this.writeCache(cache);\n return null;\n }\n\n return entry.repo;\n }\n\n public async set(fullName: string, repo: ResolvedRepo): Promise<void> {\n const cache = await this.readCache();\n const key = normalizeCacheKey(fullName);\n cache.entries[key] = {\n cachedAt: this.now(),\n repo,\n };\n await this.writeCache(cache);\n }\n\n public async invalidate(fullName?: string): Promise<void> {\n if (!fullName) {\n await this.writeCache(EMPTY_CACHE);\n return;\n }\n\n const cache = await this.readCache();\n const key = normalizeCacheKey(fullName);\n if (!(key in cache.entries)) {\n return;\n }\n\n delete cache.entries[key];\n await this.writeCache(cache);\n }\n\n private async readCache(): Promise<MetadataCacheFile> {\n if (!(await pathExists(this.filePath))) {\n return { ...EMPTY_CACHE, entries: {} };\n }\n\n try {\n const raw = await readFile(this.filePath, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<MetadataCacheFile>;\n\n if (parsed.version !== 1 || typeof parsed.entries !== \"object\") {\n return { ...EMPTY_CACHE, entries: {} };\n }\n\n return {\n version: 1,\n entries: parsed.entries as Record<string, MetadataCacheEntry>,\n };\n } catch {\n return { ...EMPTY_CACHE, entries: {} };\n }\n }\n\n private async writeCache(cache: MetadataCacheFile): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true });\n const tempPath = `${this.filePath}.tmp`;\n await writeFile(tempPath, JSON.stringify(cache, null, 2), \"utf8\");\n await rename(tempPath, this.filePath);\n }\n}\n\nfunction normalizeCacheKey(fullName: string): string {\n return fullName.trim().toLowerCase();\n}\n\nfunction expandHomePath(path: string): string {\n if (path === \"~\") {\n return homedir();\n }\n\n if (path.startsWith(\"~/\")) {\n return join(homedir(), path.slice(2));\n }\n\n return path;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n","import { constants as fsConstants } from \"node:fs\";\nimport { access, mkdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport type { ResolvedRepo } from \"./types.js\";\n\nconst CLONE_RETRY_BACKOFF_MS = [1000, 4000, 16000] as const;\n\nexport const DEFAULT_REPO_CACHE_DIR = join(homedir(), \".oac\", \"cache\", \"repos\");\n\nexport async function cloneRepo(\n repo: ResolvedRepo,\n cacheDir: string = DEFAULT_REPO_CACHE_DIR,\n): Promise<string> {\n const cacheRoot = resolveCacheDir(cacheDir);\n const localPath = join(cacheRoot, repo.owner, repo.name);\n await mkdir(dirname(localPath), { recursive: true });\n\n if (await isGitRepository(localPath)) {\n await pullExistingClone(repo, localPath);\n } else if (await pathExists(localPath)) {\n throw new Error(\n `Cannot clone \"${repo.fullName}\" into \"${localPath}\" because the directory exists and is not a git repository.`,\n );\n } else {\n await cloneNewRepository(repo, localPath);\n }\n\n const git = simpleGit(localPath);\n repo.localPath = localPath;\n repo.worktreePath = join(localPath, \"..\", \".oac-worktrees\", repo.meta.defaultBranch);\n repo.git.headSha = (await git.revparse([\"HEAD\"])).trim();\n repo.git.isShallowClone = await isShallowClone(git);\n repo.git.remoteUrl = await getOriginUrl(git, repo.git.remoteUrl);\n\n return localPath;\n}\n\nasync function cloneNewRepository(repo: ResolvedRepo, localPath: string): Promise<void> {\n const git = simpleGit();\n await retryGitOperation(\n () =>\n git.clone(repo.git.remoteUrl, localPath, [\n \"--depth\",\n \"1\",\n \"--branch\",\n repo.meta.defaultBranch,\n ]),\n `clone ${repo.fullName}`,\n );\n}\n\nasync function pullExistingClone(repo: ResolvedRepo, localPath: string): Promise<void> {\n const git = simpleGit(localPath);\n await ensureOriginRemote(git, repo.git.remoteUrl);\n\n await retryGitOperation(\n () => git.fetch(\"origin\", repo.meta.defaultBranch, [\"--depth=1\", \"--prune\"]),\n `fetch ${repo.fullName}`,\n );\n\n await checkoutDefaultBranch(git, repo.meta.defaultBranch);\n\n await retryGitOperation(\n () => hardSyncDefaultBranch(git, repo.meta.defaultBranch),\n `sync ${repo.fullName}`,\n );\n}\n\nasync function checkoutDefaultBranch(git: SimpleGit, branchName: string): Promise<void> {\n try {\n await git.checkout(branchName);\n } catch {\n await git.raw([\"checkout\", \"-B\", branchName, `origin/${branchName}`]);\n }\n}\n\nasync function hardSyncDefaultBranch(git: SimpleGit, branchName: string): Promise<void> {\n // The cache clone is disposable, so force-align it with origin to avoid stale divergence.\n await git.raw([\"reset\", \"--hard\", `origin/${branchName}`]);\n await git.raw([\"clean\", \"-fd\"]);\n}\n\nasync function ensureOriginRemote(git: SimpleGit, remoteUrl: string): Promise<void> {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((remote) => remote.name === \"origin\");\n\n if (!origin) {\n await git.addRemote(\"origin\", remoteUrl);\n return;\n }\n\n if (origin.refs.fetch !== remoteUrl && origin.refs.push !== remoteUrl) {\n await git.remote([\"set-url\", \"origin\", remoteUrl]);\n }\n}\n\nasync function getOriginUrl(git: SimpleGit, fallbackUrl: string): Promise<string> {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((remote) => remote.name === \"origin\");\n return origin?.refs.fetch ?? fallbackUrl;\n}\n\nasync function isShallowClone(git: SimpleGit): Promise<boolean> {\n const output = await git.raw([\"rev-parse\", \"--is-shallow-repository\"]);\n return output.trim() === \"true\";\n}\n\nasync function retryGitOperation<T>(\n operation: () => Promise<T>,\n operationName: string,\n): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= CLONE_RETRY_BACKOFF_MS.length; attempt += 1) {\n try {\n return await operation();\n } catch (error) {\n lastError = error;\n if (attempt === CLONE_RETRY_BACKOFF_MS.length) {\n break;\n }\n await sleep(CLONE_RETRY_BACKOFF_MS[attempt]);\n }\n }\n\n throw new Error(\n `Git operation failed after ${CLONE_RETRY_BACKOFF_MS.length + 1} attempts (${operationName}).`,\n { cause: lastError },\n );\n}\n\nfunction resolveCacheDir(cacheDir: string): string {\n const selected = cacheDir.trim().length > 0 ? cacheDir : DEFAULT_REPO_CACHE_DIR;\n return resolve(expandHomePath(selected));\n}\n\nfunction expandHomePath(path: string): string {\n if (path === \"~\") {\n return homedir();\n }\n\n if (path.startsWith(\"~/\")) {\n return join(homedir(), path.slice(2));\n }\n\n return path;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isGitRepository(path: string): Promise<boolean> {\n return pathExists(join(path, \".git\"));\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolvePromise) => {\n setTimeout(resolvePromise, ms);\n });\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,WAAAA,gBAAe;AACxB,SAAS,QAAAC,aAAY;AACrB,SAAS,eAAe;;;ACHxB,SAAS,aAAa,mBAAmB;AACzC,SAAS,QAAQ,OAAO,UAAU,QAAQ,iBAAiB;AAC3D,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAmBvB,IAAM,8BAA8B,KAAK,QAAQ,GAAG,QAAQ,SAAS,YAAY;AAEjF,IAAM,gCAAgC,KAAK,KAAK;AAEvD,IAAM,cAAiC;AAAA,EACrC,SAAS;AAAA,EACT,SAAS,CAAC;AACZ;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,UAAgC,CAAC,GAAG;AACrD,SAAK,WAAW,eAAe,QAAQ,YAAY,2BAA2B;AAC9E,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,MAAM,QAAQ,OAAO,KAAK;AAAA,EACjC;AAAA,EAEA,MAAa,IAAI,UAAgD;AAC/D,UAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,UAAM,MAAM,kBAAkB,QAAQ;AACtC,UAAM,QAAQ,MAAM,QAAQ,GAAG;AAE/B,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,IAAI,IAAI,MAAM,WAAW,KAAK,OAAO;AAC5C,aAAO,MAAM,QAAQ,GAAG;AACxB,YAAM,KAAK,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAa,IAAI,UAAkB,MAAmC;AACpE,UAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,UAAM,MAAM,kBAAkB,QAAQ;AACtC,UAAM,QAAQ,GAAG,IAAI;AAAA,MACnB,UAAU,KAAK,IAAI;AAAA,MACnB;AAAA,IACF;AACA,UAAM,KAAK,WAAW,KAAK;AAAA,EAC7B;AAAA,EAEA,MAAa,WAAW,UAAkC;AACxD,QAAI,CAAC,UAAU;AACb,YAAM,KAAK,WAAW,WAAW;AACjC;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,UAAM,MAAM,kBAAkB,QAAQ;AACtC,QAAI,EAAE,OAAO,MAAM,UAAU;AAC3B;AAAA,IACF;AAEA,WAAO,MAAM,QAAQ,GAAG;AACxB,UAAM,KAAK,WAAW,KAAK;AAAA,EAC7B;AAAA,EAEA,MAAc,YAAwC;AACpD,QAAI,CAAE,MAAM,WAAW,KAAK,QAAQ,GAAI;AACtC,aAAO,EAAE,GAAG,aAAa,SAAS,CAAC,EAAE;AAAA,IACvC;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,KAAK,UAAU,MAAM;AAChD,YAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,UAAI,OAAO,YAAY,KAAK,OAAO,OAAO,YAAY,UAAU;AAC9D,eAAO,EAAE,GAAG,aAAa,SAAS,CAAC,EAAE;AAAA,MACvC;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,QAAQ;AACN,aAAO,EAAE,GAAG,aAAa,SAAS,CAAC,EAAE;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,OAAyC;AAChE,UAAM,MAAM,QAAQ,KAAK,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,UAAM,WAAW,GAAG,KAAK,QAAQ;AACjC,UAAM,UAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AAChE,UAAM,OAAO,UAAU,KAAK,QAAQ;AAAA,EACtC;AACF;AAEA,SAAS,kBAAkB,UAA0B;AACnD,SAAO,SAAS,KAAK,EAAE,YAAY;AACrC;AAEA,SAAS,eAAe,MAAsB;AAC5C,MAAI,SAAS,KAAK;AAChB,WAAO,QAAQ;AAAA,EACjB;AAEA,MAAI,KAAK,WAAW,IAAI,GAAG;AACzB,WAAO,KAAK,QAAQ,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,EACtC;AAEA,SAAO;AACT;AAEA,eAAe,WAAW,MAAgC;AACxD,MAAI;AACF,UAAM,OAAO,MAAM,YAAY,IAAI;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADpIA,IAAM,qBAAqB;AAC3B,IAAM,qBACJ;AASK,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7B;AAAA,EAET,YAAY,SAAiB,MAA+B,OAAiB;AAClF,UAAM,SAAS,EAAE,MAAM,CAAC;AACxB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAOA,eAAsB,YAAY,OAAsC;AACtE,QAAM,SAAS,eAAe,KAAK;AACnC,QAAM,QAAQ,IAAI,cAAc;AAChC,QAAM,WAAW,GAAG,OAAO,KAAK,IAAI,OAAO,IAAI;AAC/C,QAAM,SAAS,MAAM,MAAM,IAAI,QAAQ;AAEvC,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,MAAM,mBAAmB;AAAA,EAC3B,CAAC;AAED,QAAM,WAAW,MAAM,UAAU,SAAS,OAAO,OAAO,OAAO,IAAI;AACnE,MAAI,SAAS,UAAU;AACrB,UAAM,IAAI;AAAA,MACR,eAAe,SAAS,SAAS;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,qBAAqB,SAAS,SAAS,SAAS,WAAW;AAC/E,MAAI,CAAC,YAAY,MAAM;AACrB,UAAM,IAAI;AAAA,MACR,gCAAgC,SAAS,SAAS;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,CAAC,WAAW,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7C,eAAe,SAAS,SAAS,MAAM,OAAO,SAAS,IAAI;AAAA,IAC3D,aAAa,SAAS,SAAS,MAAM,OAAO,SAAS,MAAM,SAAS,cAAc;AAAA,EACpF,CAAC;AAED,QAAM,YAAY,iBAAiB,SAAS,MAAM,OAAO,SAAS,IAAI;AACtE,QAAM,WAAyB;AAAA,IAC7B,UAAU,SAAS;AAAA,IACnB,OAAO,SAAS,MAAM;AAAA,IACtB,MAAM,SAAS;AAAA,IACf;AAAA,IACA,cAAcC,MAAK,WAAW,MAAM,kBAAkB,SAAS,cAAc;AAAA,IAC7E,MAAM;AAAA,MACJ,eAAe,SAAS;AAAA,MACxB,UAAU,SAAS;AAAA,MACnB;AAAA,MACA,MAAM,SAAS;AAAA,MACf,OAAO,SAAS;AAAA,MAChB,iBAAiB,SAAS;AAAA,MAC1B,QAAQ,SAAS,UAAU,CAAC;AAAA,MAC5B,SAAS,iBAAiB,SAAS,SAAS,WAAW,IAAI;AAAA,MAC3D,YAAY,SAAS;AAAA,MACrB,QAAQ,SAAS;AAAA,MACjB;AAAA,IACF;AAAA,IACA,KAAK;AAAA,MACH;AAAA,MACA,WAAW,SAAS,aAAa,sBAAsB,SAAS,SAAS;AAAA,MACzE,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,SAAS,UAAU,QAAQ;AAC3C,SAAO;AACT;AAEA,SAAS,eAAe,OAAgC;AACtD,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,oBAAoB,qCAAqC,eAAe;AAAA,EACpF;AAEA,QAAM,iBAAiB,WAAW,MAAM,kBAAkB;AAC1D,MAAI,gBAAgB,QAAQ;AAC1B,WAAO;AAAA,MACL,OAAO,eAAe,OAAO;AAAA,MAC7B,MAAM,eAAe,OAAO;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,MAAM,kBAAkB;AACpD,MAAI,UAAU,QAAQ;AACpB,WAAO;AAAA,MACL,OAAO,SAAS,OAAO;AAAA,MACvB,MAAM,SAAS,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,qBAAqB,WAAW,WAAW,aAAa,IAC1D,WAAW,UAAU,KACrB;AAEJ,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,kBAAkB;AACtC,QAAI,CAAC,aAAa,IAAI,QAAQ,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,4DAA4D,IAAI,QAAQ;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACxD,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,IAAI,oBAAoB,kCAAkC,KAAK,MAAM,eAAe;AAAA,IAC5F;AAEA,UAAM,QAAQ,UAAU,CAAC;AACzB,UAAM,OAAO,eAAe,UAAU,CAAC,CAAC;AACxC,QAAI,CAAC,SAAS,CAAC,MAAM;AACnB,YAAM,IAAI,oBAAoB,kCAAkC,KAAK,MAAM,eAAe;AAAA,IAC5F;AAEA,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB,SAAS,OAAO;AACd,QAAI,iBAAiB,qBAAqB;AACxC,YAAM;AAAA,IACR;AAEA,UAAM,IAAI;AAAA,MACR,+DAA+D,KAAK;AAAA,MACpE;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,UAAU,SAAkB,OAAe,MAAc;AACtE,MAAI;AACF,YAAQ,MAAM,QAAQ,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC,GAAG;AAAA,EACpD,SAAS,OAAO;AACd,UAAM,kBAAkB,OAAO,MAAM,KAAK;AAAA,EAC5C;AACF;AAEA,eAAe,eACb,SACA,OACA,MACiC;AACjC,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,MAAM,cAAc,EAAE,OAAO,KAAK,CAAC;AAClE,WAAO,SAAS;AAAA,EAClB,SAAS,OAAO;AACd,UAAM,kBAAkB,OAAO,MAAM,KAAK;AAAA,EAC5C;AACF;AAEA,eAAe,aACb,SACA,OACA,MACA,eACiB;AACjB,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,MAAM,UAAU;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,OAAO,KAAK,OAAO;AAAA,EAC5B,SAAS,OAAO;AACd,UAAM,kBAAkB,OAAO,MAAM,KAAK;AAAA,EAC5C;AACF;AAEA,SAAS,kBAAkB,OAAe,MAAc,OAAqC;AAC3F,QAAM,WAAW,GAAG,KAAK,IAAI,IAAI;AACjC,QAAM,SAAS,WAAW,KAAK,IAAI,MAAM,SAAS;AAClD,QAAM,UACJ,OAAO,UAAU,YAAY,SAAS,aAAa,QAC/C,OAAO,MAAM,OAAO,IACpB;AAEN,MAAI,WAAW,KAAK;AAClB,UAAM,WAAW,CAAC,EAAE,QAAQ,IAAI,gBAAgB,QAAQ,IAAI;AAC5D,UAAM,OAAO,WACT,+FACA;AACJ,WAAO,IAAI;AAAA,MACT,eAAe,QAAQ,8BAA8B,IAAI;AAAA,MACzD;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW,KAAK;AAClB,WAAO,IAAI;AAAA,MACT,sBAAsB,QAAQ;AAAA,MAC9B;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,IAAI;AAAA,IACT,iCAAiC,QAAQ,MAAM,OAAO;AAAA,IACtD;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,qBACP,eACA,aAOiB;AACjB,QAAM,OAAO,aAAa,QAAQ,CAAC;AAEnC,SAAO;AAAA,IACL,MAAM,aAAa,QAAQ;AAAA,IAC3B;AAAA,IACA,OAAO,aAAa,SAAS;AAAA,EAC/B;AACF;AAEA,SAAS,iBAAiB,QAAsC;AAC9D,MAAI,CAAC,UAAU,WAAW,eAAe;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,UAA2B;AAC/C,QAAM,aAAa,SAAS,YAAY;AACxC,SAAO,eAAe,gBAAgB,eAAe;AACvD;AAEA,SAAS,eAAe,MAAsB;AAC5C,SAAO,KAAK,QAAQ,WAAW,EAAE;AACnC;AAEA,SAAS,WAAW,OAA8C;AAChE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY;AACpE;AAEA,SAAS,iBAAiB,OAAe,MAAsB;AAC7D,SAAOA,MAAKC,SAAQ,GAAG,QAAQ,SAAS,SAAS,OAAO,IAAI;AAC9D;AAEA,SAAS,qBAAyC;AAChD,QAAM,cAAc,QAAQ,IAAI,cAAc,KAAK;AACnD,MAAI,aAAa;AACf,YAAQ,IAAI,eAAe;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,IAAI,UAAU,KAAK;AAC3C,MAAI,SAAS;AACX,YAAQ,IAAI,eAAe;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,QAAQ,aAAa,MAAM,CAAC,QAAQ,OAAO,GAAG;AAAA,MAClD,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC,EAAE,KAAK;AAER,QAAI,MAAM,SAAS,GAAG;AACpB,cAAQ,IAAI,eAAe;AAC3B,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;;;AElTA,SAAS,aAAaC,oBAAmB;AACzC,SAAS,UAAAC,SAAQ,SAAAC,cAAa;AAC9B,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,OAAM,eAAe;AACvC,SAAyB,iBAAiB;AAG1C,IAAM,yBAAyB,CAAC,KAAM,KAAM,IAAK;AAE1C,IAAM,yBAAyBA,MAAKF,SAAQ,GAAG,QAAQ,SAAS,OAAO;AAE9E,eAAsB,UACpB,MACA,WAAmB,wBACF;AACjB,QAAM,YAAY,gBAAgB,QAAQ;AAC1C,QAAM,YAAYE,MAAK,WAAW,KAAK,OAAO,KAAK,IAAI;AACvD,QAAMH,OAAME,SAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAEnD,MAAI,MAAM,gBAAgB,SAAS,GAAG;AACpC,UAAM,kBAAkB,MAAM,SAAS;AAAA,EACzC,WAAW,MAAME,YAAW,SAAS,GAAG;AACtC,UAAM,IAAI;AAAA,MACR,iBAAiB,KAAK,QAAQ,WAAW,SAAS;AAAA,IACpD;AAAA,EACF,OAAO;AACL,UAAM,mBAAmB,MAAM,SAAS;AAAA,EAC1C;AAEA,QAAM,MAAM,UAAU,SAAS;AAC/B,OAAK,YAAY;AACjB,OAAK,eAAeD,MAAK,WAAW,MAAM,kBAAkB,KAAK,KAAK,aAAa;AACnF,OAAK,IAAI,WAAW,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,GAAG,KAAK;AACvD,OAAK,IAAI,iBAAiB,MAAM,eAAe,GAAG;AAClD,OAAK,IAAI,YAAY,MAAM,aAAa,KAAK,KAAK,IAAI,SAAS;AAE/D,SAAO;AACT;AAEA,eAAe,mBAAmB,MAAoB,WAAkC;AACtF,QAAM,MAAM,UAAU;AACtB,QAAM;AAAA,IACJ,MACE,IAAI,MAAM,KAAK,IAAI,WAAW,WAAW;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,KAAK;AAAA,IACZ,CAAC;AAAA,IACH,SAAS,KAAK,QAAQ;AAAA,EACxB;AACF;AAEA,eAAe,kBAAkB,MAAoB,WAAkC;AACrF,QAAM,MAAM,UAAU,SAAS;AAC/B,QAAM,mBAAmB,KAAK,KAAK,IAAI,SAAS;AAEhD,QAAM;AAAA,IACJ,MAAM,IAAI,MAAM,UAAU,KAAK,KAAK,eAAe,CAAC,aAAa,SAAS,CAAC;AAAA,IAC3E,SAAS,KAAK,QAAQ;AAAA,EACxB;AAEA,QAAM,sBAAsB,KAAK,KAAK,KAAK,aAAa;AAExD,QAAM;AAAA,IACJ,MAAM,sBAAsB,KAAK,KAAK,KAAK,aAAa;AAAA,IACxD,QAAQ,KAAK,QAAQ;AAAA,EACvB;AACF;AAEA,eAAe,sBAAsB,KAAgB,YAAmC;AACtF,MAAI;AACF,UAAM,IAAI,SAAS,UAAU;AAAA,EAC/B,QAAQ;AACN,UAAM,IAAI,IAAI,CAAC,YAAY,MAAM,YAAY,UAAU,UAAU,EAAE,CAAC;AAAA,EACtE;AACF;AAEA,eAAe,sBAAsB,KAAgB,YAAmC;AAEtF,QAAM,IAAI,IAAI,CAAC,SAAS,UAAU,UAAU,UAAU,EAAE,CAAC;AACzD,QAAM,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC;AAChC;AAEA,eAAe,mBAAmB,KAAgB,WAAkC;AAClF,QAAM,UAAU,MAAM,IAAI,WAAW,IAAI;AACzC,QAAM,SAAS,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,QAAQ;AAEhE,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,UAAU,SAAS;AACvC;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,UAAU,aAAa,OAAO,KAAK,SAAS,WAAW;AACrE,UAAM,IAAI,OAAO,CAAC,WAAW,UAAU,SAAS,CAAC;AAAA,EACnD;AACF;AAEA,eAAe,aAAa,KAAgB,aAAsC;AAChF,QAAM,UAAU,MAAM,IAAI,WAAW,IAAI;AACzC,QAAM,SAAS,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,QAAQ;AAChE,SAAO,QAAQ,KAAK,SAAS;AAC/B;AAEA,eAAe,eAAe,KAAkC;AAC9D,QAAM,SAAS,MAAM,IAAI,IAAI,CAAC,aAAa,yBAAyB,CAAC;AACrE,SAAO,OAAO,KAAK,MAAM;AAC3B;AAEA,eAAe,kBACb,WACA,eACY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,uBAAuB,QAAQ,WAAW,GAAG;AAC5E,QAAI;AACF,aAAO,MAAM,UAAU;AAAA,IACzB,SAAS,OAAO;AACd,kBAAY;AACZ,UAAI,YAAY,uBAAuB,QAAQ;AAC7C;AAAA,MACF;AACA,YAAM,MAAM,uBAAuB,OAAO,CAAC;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,8BAA8B,uBAAuB,SAAS,CAAC,cAAc,aAAa;AAAA,IAC1F,EAAE,OAAO,UAAU;AAAA,EACrB;AACF;AAEA,SAAS,gBAAgB,UAA0B;AACjD,QAAM,WAAW,SAAS,KAAK,EAAE,SAAS,IAAI,WAAW;AACzD,SAAO,QAAQE,gBAAe,QAAQ,CAAC;AACzC;AAEA,SAASA,gBAAe,MAAsB;AAC5C,MAAI,SAAS,KAAK;AAChB,WAAOJ,SAAQ;AAAA,EACjB;AAEA,MAAI,KAAK,WAAW,IAAI,GAAG;AACzB,WAAOE,MAAKF,SAAQ,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,EACtC;AAEA,SAAO;AACT;AAEA,eAAeG,YAAW,MAAgC;AACxD,MAAI;AACF,UAAML,QAAO,MAAMD,aAAY,IAAI;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,gBAAgB,MAAgC;AAC7D,SAAOM,YAAWD,MAAK,MAAM,MAAM,CAAC;AACtC;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,mBAAmB;AACrC,eAAW,gBAAgB,EAAE;AAAA,EAC/B,CAAC;AACH;","names":["homedir","join","join","homedir","fsConstants","access","mkdir","homedir","dirname","join","pathExists","expandHomePath"]}
|