@smwb/onreel-git-mcp 0.9.0 → 0.10.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.
@@ -1817,9 +1817,105 @@ function commentOf(raw) {
1817
1817
  return { id: typeof item.id === "number" ? String(item.id) : "?", ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } };
1818
1818
  }
1819
1819
 
1820
- // src/tracker/github.lib.ts
1820
+ // src/tracker/repo.schema.ts
1821
+ import { z as z3 } from "zod";
1822
+ var repoVisibilitySchema = z3.enum(["public", "internal", "private"]);
1823
+ var branchFilterSchema = z3.enum(["search"]);
1824
+ var repoSchema = z3.object({
1825
+ /** Имя репозитория, как его называет трекер: GitHub name, GitLab name, SourceCraft slug. */
1826
+ name: z3.string().min(1).max(500),
1827
+ /** Ветка по умолчанию; у пустого репозитория её нет. */
1828
+ defaultBranch: z3.string().min(1).max(500).optional(),
1829
+ description: z3.string().max(1e5).optional(),
1830
+ visibility: repoVisibilitySchema.optional(),
1831
+ /** Ссылки: страница репозитория в трекере (url первой). */
1832
+ links: z3.array(issueLinkSchema).max(10)
1833
+ });
1834
+ var branchSchema = z3.object({
1835
+ /** Имя ветки без refs/heads/. */
1836
+ name: z3.string().min(1).max(500),
1837
+ /** Коммит головы ветки. */
1838
+ sha: z3.string().regex(/^[0-9a-f]{7,64}$/).optional(),
1839
+ /** Ветка защищена; трекер, который признака не отдаёт, поле не заполняет. */
1840
+ protected: z3.boolean().optional(),
1841
+ /** Ветка по умолчанию репозитория; трекер, который признака не отдаёт, поле не заполняет. */
1842
+ isDefault: z3.boolean().optional()
1843
+ });
1844
+
1845
+ // src/tracker/github-repo.lib.ts
1821
1846
  var DEFAULT_API4 = "https://api.github.com";
1822
1847
  var TARGET5 = /^[\w.-]+\/[\w.-]+$/;
1848
+ var SHA3 = /^[0-9a-f]{7,64}$/;
1849
+ var BRANCHES_SUBJECT = "\u0412\u0435\u0442\u043A\u0438 GitHub";
1850
+ var BRANCH_FILTERS = branchFilterSchema.options;
1851
+ var CAPABILITIES4 = {
1852
+ branches: { filters: BRANCH_FILTERS, clientSide: BRANCH_FILTERS, sorts: [] }
1853
+ };
1854
+ function githubRepo() {
1855
+ return { capabilities: CAPABILITIES4, create: (context) => createGithubRepoAdapter(context) };
1856
+ }
1857
+ function createGithubRepoAdapter({ connection, fetch }) {
1858
+ const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
1859
+ if (!TARGET5.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1860
+ const api = baseUrl(connection.url, DEFAULT_API4);
1861
+ const origin = new URL(api).origin;
1862
+ const root = `${api}/repos/${target}`;
1863
+ const headers = {
1864
+ accept: "application/vnd.github+json",
1865
+ "x-github-api-version": "2022-11-28",
1866
+ ...connection.token === void 0 || connection.token === "" ? {} : { authorization: `Bearer ${connection.token}` }
1867
+ };
1868
+ const missing = { failure: "not_found", message: `\u0412 GitHub \u043D\u0435\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F ${target}.` };
1869
+ return {
1870
+ get: async () => repoOf((await requestJson(root, { fetch, origin, headers, missing })).body, target),
1871
+ branches: async (query) => {
1872
+ assertListQuery(BRANCHES_SUBJECT, CAPABILITIES4.branches, query, BRANCH_FILTERS);
1873
+ const page = pageOf4(query.cursor);
1874
+ const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
1875
+ const { body, headers: responseHeaders } = await requestJson(`${root}/branches?${params.toString()}`, { fetch, origin, headers, missing });
1876
+ const items = (Array.isArray(body) ? body : []).map(branchOf).filter((branch) => query.search === void 0 || containsText([branch.name], query.search));
1877
+ return { items, ...nextOf2(responseHeaders.get("link"), page) };
1878
+ }
1879
+ };
1880
+ }
1881
+ function pageOf4(cursor) {
1882
+ const page = cursor === void 0 ? 1 : Number(cursor);
1883
+ return Number.isInteger(page) && page >= 1 ? page : 1;
1884
+ }
1885
+ function nextOf2(link, page) {
1886
+ return link !== null && /rel="next"/.test(link) ? { nextCursor: String(page + 1) } : {};
1887
+ }
1888
+ function repoOf(raw, target) {
1889
+ const item = isRecord(raw) ? raw : {};
1890
+ const defaultBranch = text(item.default_branch);
1891
+ const description = text(item.description);
1892
+ const visibility = visibilityOf(item.visibility);
1893
+ const url = text(item.html_url);
1894
+ return {
1895
+ name: text(item.name) ?? text(item.full_name) ?? target,
1896
+ ...defaultBranch === void 0 ? {} : { defaultBranch },
1897
+ ...description === void 0 ? {} : { description },
1898
+ ...visibility === void 0 ? {} : { visibility },
1899
+ links: url === void 0 ? [] : [{ url, title: "GitHub" }]
1900
+ };
1901
+ }
1902
+ function branchOf(raw) {
1903
+ const item = isRecord(raw) ? raw : {};
1904
+ const sha = isRecord(item.commit) ? text(item.commit.sha) : void 0;
1905
+ return {
1906
+ name: text(item.name) ?? "?",
1907
+ ...sha === void 0 || !SHA3.test(sha) ? {} : { sha },
1908
+ ...typeof item.protected === "boolean" ? { protected: item.protected } : {}
1909
+ };
1910
+ }
1911
+ function visibilityOf(value) {
1912
+ const parsed = repoVisibilitySchema.safeParse(value);
1913
+ return parsed.success ? parsed.data : void 0;
1914
+ }
1915
+
1916
+ // src/tracker/github.lib.ts
1917
+ var DEFAULT_API5 = "https://api.github.com";
1918
+ var TARGET6 = /^[\w.-]+\/[\w.-]+$/;
1823
1919
  var NUMBER2 = /^[1-9]\d{0,14}$/;
1824
1920
  var LIST_SUBJECT2 = "\u0417\u0430\u0434\u0430\u0447\u0438 GitHub";
1825
1921
  var COMMENTS_SUBJECT2 = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitHub";
@@ -1840,16 +1936,17 @@ function githubTracker() {
1840
1936
  features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: true, statuses: ["open", "in_progress", "done", "declined"] }
1841
1937
  },
1842
1938
  create: (context) => createGithubAdapter(context, issues),
1843
- origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_API4)).origin,
1939
+ origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_API5)).origin,
1844
1940
  issues,
1845
1941
  pulls: githubPulls(),
1846
- ci: githubCi()
1942
+ ci: githubCi(),
1943
+ repo: githubRepo()
1847
1944
  };
1848
1945
  }
1849
1946
  function createGithubAdapter({ connection, fetch }, capabilities) {
1850
1947
  const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
1851
- if (!TARGET5.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1852
- const api = baseUrl(connection.url, DEFAULT_API4);
1948
+ if (!TARGET6.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1949
+ const api = baseUrl(connection.url, DEFAULT_API5);
1853
1950
  const origin = new URL(api).origin;
1854
1951
  const root = `${api}/repos/${target}/issues`;
1855
1952
  const headers = {
@@ -1869,7 +1966,7 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
1869
1966
  list: async (query) => {
1870
1967
  assertListQuery(LIST_SUBJECT2, capabilities.list, query, ISSUE_FILTERS);
1871
1968
  assertCsvLabels(query.labels);
1872
- const page = pageOf4(query.cursor);
1969
+ const page = pageOf5(query.cursor);
1873
1970
  const search = needsSearch(query);
1874
1971
  const params = search ? searchParams(target, query, page) : issuesParams(query, page);
1875
1972
  const { body, headers: responseHeaders } = await requestJson(`${search ? `${api}/search/issues` : root}?${params.toString()}`, { fetch, origin, headers });
@@ -1893,7 +1990,7 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
1893
1990
  },
1894
1991
  comments: async (key, query) => {
1895
1992
  assertCommentQuery(COMMENTS_SUBJECT2, capabilities.comments, query);
1896
- const page = pageOf4(query.cursor);
1993
+ const page = pageOf5(query.cursor);
1897
1994
  const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
1898
1995
  if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
1899
1996
  const { body, headers: responseHeaders } = await requestJson(`${root}/${number(key)}/comments?${params.toString()}`, { fetch, origin, headers, missingIsIssue: true });
@@ -1902,7 +1999,7 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
1902
1999
  comment: async (key, body) => commentOf2((await requestJson(`${root}/${number(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
1903
2000
  };
1904
2001
  }
1905
- function pageOf4(cursor) {
2002
+ function pageOf5(cursor) {
1906
2003
  const page = cursor === void 0 ? 1 : Number(cursor);
1907
2004
  return Number.isInteger(page) && page >= 1 ? page : 1;
1908
2005
  }
@@ -2023,9 +2120,9 @@ function commentOf2(raw) {
2023
2120
 
2024
2121
  // src/tracker/gitlab-pull.lib.ts
2025
2122
  var DEFAULT_URL2 = "https://gitlab.com";
2026
- var TARGET6 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2123
+ var TARGET7 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2027
2124
  var IID = /^[1-9]\d{0,19}$/;
2028
- var SHA3 = /^[0-9a-f]{7,64}$/;
2125
+ var SHA4 = /^[0-9a-f]{7,64}$/;
2029
2126
  var PULL_CAPABILITIES2 = {
2030
2127
  list: { filters: pullRequestFilterSchema.options, clientSide: ["status"], sorts: ["created", "updated", "title"] },
2031
2128
  comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] },
@@ -2046,7 +2143,7 @@ function gitlabPulls() {
2046
2143
  }
2047
2144
  function createGitlabPullAdapter({ connection, fetch }) {
2048
2145
  const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2049
- if (!TARGET6.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2146
+ if (!TARGET7.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2050
2147
  const site = baseUrl(connection.url, DEFAULT_URL2);
2051
2148
  const origin = new URL(site).origin;
2052
2149
  const root = `${site}/api/v4/projects/${encodeURIComponent(target)}/merge_requests`;
@@ -2060,7 +2157,7 @@ function createGitlabPullAdapter({ connection, fetch }) {
2060
2157
  list: async (query) => {
2061
2158
  assertListQuery("PR GitLab", PULL_CAPABILITIES2.list, query, pullRequestFilterSchema.options);
2062
2159
  const status = usedStatuses2(query.status);
2063
- const page = pageOf5(query.cursor);
2160
+ const page = pageOf6(query.cursor);
2064
2161
  const params = new URLSearchParams({
2065
2162
  state: stateFilter3(status),
2066
2163
  per_page: String(query.limit),
@@ -2122,7 +2219,7 @@ function createGitlabPullAdapter({ connection, fetch }) {
2122
2219
  },
2123
2220
  comments: async (key, query) => {
2124
2221
  assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", PULL_CAPABILITIES2.comments, query);
2125
- const page = pageOf5(query.cursor);
2222
+ const page = pageOf6(query.cursor);
2126
2223
  const params = new URLSearchParams({ sort: query.order ?? "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
2127
2224
  const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } });
2128
2225
  const next = responseHeaders.get("x-next-page");
@@ -2132,7 +2229,7 @@ function createGitlabPullAdapter({ connection, fetch }) {
2132
2229
  comment: async (key, body) => commentOf3((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body)
2133
2230
  };
2134
2231
  }
2135
- function pageOf5(cursor) {
2232
+ function pageOf6(cursor) {
2136
2233
  const page = cursor === void 0 ? 1 : Number(cursor);
2137
2234
  return Number.isInteger(page) && page >= 1 ? page : 1;
2138
2235
  }
@@ -2200,7 +2297,7 @@ function pullOf2(raw) {
2200
2297
  ...author === void 0 ? {} : { author },
2201
2298
  source: text(item.source_branch) ?? "?",
2202
2299
  target: text(item.target_branch) ?? "?",
2203
- ...sha !== void 0 && SHA3.test(sha) ? { headSha: sha } : {},
2300
+ ...sha !== void 0 && SHA4.test(sha) ? { headSha: sha } : {},
2204
2301
  labels: labelNames(item.labels),
2205
2302
  reviewers: usernames(item.reviewers),
2206
2303
  links: url === void 0 ? [] : [{ url, title: "GitLab" }],
@@ -2217,9 +2314,78 @@ function commentOf3(raw) {
2217
2314
  return { id: typeof item.id === "number" ? String(item.id) : "?", ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } };
2218
2315
  }
2219
2316
 
2220
- // src/tracker/gitlab.lib.ts
2317
+ // src/tracker/gitlab-repo.lib.ts
2221
2318
  var DEFAULT_URL3 = "https://gitlab.com";
2222
- var TARGET7 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2319
+ var TARGET8 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2320
+ var SHA5 = /^[0-9a-f]{7,64}$/;
2321
+ var BRANCHES_SUBJECT2 = "\u0412\u0435\u0442\u043A\u0438 GitLab";
2322
+ var BRANCH_FILTERS2 = branchFilterSchema.options;
2323
+ var CAPABILITIES5 = {
2324
+ branches: { filters: BRANCH_FILTERS2, clientSide: [], sorts: [] }
2325
+ };
2326
+ function gitlabRepo() {
2327
+ return { capabilities: CAPABILITIES5, create: (context) => createGitlabRepoAdapter(context) };
2328
+ }
2329
+ function createGitlabRepoAdapter({ connection, fetch }) {
2330
+ const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2331
+ if (!TARGET8.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2332
+ const site = baseUrl(connection.url, DEFAULT_URL3);
2333
+ const origin = new URL(site).origin;
2334
+ const project = `${site}/api/v4/projects/${encodeURIComponent(target)}`;
2335
+ const headers = connection.token === void 0 || connection.token === "" ? {} : { "private-token": connection.token };
2336
+ const missing = { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 ${target}.` };
2337
+ return {
2338
+ get: async () => repoOf2((await requestJson(project, { fetch, origin, headers, missing })).body, target),
2339
+ branches: async (query) => {
2340
+ assertListQuery(BRANCHES_SUBJECT2, CAPABILITIES5.branches, query, BRANCH_FILTERS2);
2341
+ const params = new URLSearchParams({ per_page: String(query.limit), page: String(pageOf7(query.cursor)) });
2342
+ if (query.search !== void 0) params.set("search", searchOf(query.search));
2343
+ const { body, headers: responseHeaders } = await requestJson(`${project}/repository/branches?${params.toString()}`, { fetch, origin, headers, missing });
2344
+ const next = responseHeaders.get("x-next-page");
2345
+ return { items: (Array.isArray(body) ? body : []).map(branchOf2), ...next === null || next === "" ? {} : { nextCursor: next } };
2346
+ }
2347
+ };
2348
+ }
2349
+ function pageOf7(cursor) {
2350
+ const page = cursor === void 0 ? 1 : Number(cursor);
2351
+ return Number.isInteger(page) && page >= 1 ? page : 1;
2352
+ }
2353
+ function searchOf(value) {
2354
+ const stripped = value.replace(/^\^/, "").replace(/\$$/, "");
2355
+ return stripped === "" ? value : stripped;
2356
+ }
2357
+ function repoOf2(raw, target) {
2358
+ const item = isRecord(raw) ? raw : {};
2359
+ const defaultBranch = text(item.default_branch);
2360
+ const description = text(item.description);
2361
+ const visibility = visibilityOf2(item.visibility);
2362
+ const url = text(item.web_url);
2363
+ return {
2364
+ name: text(item.name) ?? text(item.path_with_namespace) ?? target,
2365
+ ...defaultBranch === void 0 ? {} : { defaultBranch },
2366
+ ...description === void 0 ? {} : { description },
2367
+ ...visibility === void 0 ? {} : { visibility },
2368
+ links: url === void 0 ? [] : [{ url, title: "GitLab" }]
2369
+ };
2370
+ }
2371
+ function branchOf2(raw) {
2372
+ const item = isRecord(raw) ? raw : {};
2373
+ const sha = isRecord(item.commit) ? text(item.commit.id) : void 0;
2374
+ return {
2375
+ name: text(item.name) ?? "?",
2376
+ ...sha === void 0 || !SHA5.test(sha) ? {} : { sha },
2377
+ ...typeof item.protected === "boolean" ? { protected: item.protected } : {},
2378
+ ...typeof item.default === "boolean" ? { isDefault: item.default } : {}
2379
+ };
2380
+ }
2381
+ function visibilityOf2(value) {
2382
+ const parsed = repoVisibilitySchema.safeParse(value);
2383
+ return parsed.success ? parsed.data : void 0;
2384
+ }
2385
+
2386
+ // src/tracker/gitlab.lib.ts
2387
+ var DEFAULT_URL4 = "https://gitlab.com";
2388
+ var TARGET9 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2223
2389
  var IID2 = /^[1-9]\d{0,14}$/;
2224
2390
  var ISSUE_CAPABILITIES = {
2225
2391
  list: { filters: ISSUE_FILTERS, clientSide: ["status"], sorts: ["created", "updated", "priority", "title"] },
@@ -2244,16 +2410,17 @@ function gitlabTracker() {
2244
2410
  features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: false, statuses: ["open", "in_progress", "done"] }
2245
2411
  },
2246
2412
  create: (context) => createGitlabAdapter(context),
2247
- origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_URL3)).origin,
2413
+ origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_URL4)).origin,
2248
2414
  issues: ISSUE_CAPABILITIES,
2249
2415
  pulls: gitlabPulls(),
2250
- ci: gitlabCi()
2416
+ ci: gitlabCi(),
2417
+ repo: gitlabRepo()
2251
2418
  };
2252
2419
  }
2253
2420
  function createGitlabAdapter({ connection, fetch }) {
2254
2421
  const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2255
- if (!TARGET7.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2256
- const site = baseUrl(connection.url, DEFAULT_URL3);
2422
+ if (!TARGET9.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2423
+ const site = baseUrl(connection.url, DEFAULT_URL4);
2257
2424
  const origin = new URL(site).origin;
2258
2425
  const root = `${site}/api/v4/projects/${encodeURIComponent(target)}/issues`;
2259
2426
  const headers = connection.token === void 0 || connection.token === "" ? {} : { "private-token": connection.token };
@@ -2265,7 +2432,7 @@ function createGitlabAdapter({ connection, fetch }) {
2265
2432
  return {
2266
2433
  list: async (query) => {
2267
2434
  assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 GitLab", ISSUE_CAPABILITIES.list, query, ISSUE_FILTERS);
2268
- const page = pageOf6(query.cursor);
2435
+ const page = pageOf8(query.cursor);
2269
2436
  const params = new URLSearchParams({
2270
2437
  state: stateFilter4(query.status),
2271
2438
  per_page: String(query.limit),
@@ -2304,7 +2471,7 @@ function createGitlabAdapter({ connection, fetch }) {
2304
2471
  },
2305
2472
  comments: async (key, query) => {
2306
2473
  assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", ISSUE_CAPABILITIES.comments, query);
2307
- const page = pageOf6(query.cursor);
2474
+ const page = pageOf8(query.cursor);
2308
2475
  const params = new URLSearchParams({ sort: query.order ?? "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
2309
2476
  const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, origin, headers, missingIsIssue: true });
2310
2477
  const next = responseHeaders.get("x-next-page");
@@ -2314,7 +2481,7 @@ function createGitlabAdapter({ connection, fetch }) {
2314
2481
  comment: async (key, body) => commentOf4((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
2315
2482
  };
2316
2483
  }
2317
- function pageOf6(cursor) {
2484
+ function pageOf8(cursor) {
2318
2485
  const page = cursor === void 0 ? 1 : Number(cursor);
2319
2486
  return Number.isInteger(page) && page >= 1 ? page : 1;
2320
2487
  }
@@ -2639,10 +2806,10 @@ function toComment(row) {
2639
2806
  }
2640
2807
 
2641
2808
  // src/tracker/sourcecraft-pull.lib.ts
2642
- var DEFAULT_API5 = "https://api.sourcecraft.tech";
2643
- var TARGET8 = /^[\w.-]+\/[\w.-]+$/;
2809
+ var DEFAULT_API6 = "https://api.sourcecraft.tech";
2810
+ var TARGET10 = /^[\w.-]+\/[\w.-]+$/;
2644
2811
  var SLUG = /^[\w-]{1,100}$/;
2645
- var SHA4 = /^[0-9a-f]{7,64}$/;
2812
+ var SHA6 = /^[0-9a-f]{7,64}$/;
2646
2813
  var PULL_SORT_FIELDS_UPSTREAM = {
2647
2814
  created: "created_at",
2648
2815
  updated: "updated_at",
@@ -2668,8 +2835,8 @@ function sourcecraftPulls() {
2668
2835
  }
2669
2836
  function createPullAdapter({ connection, fetch }) {
2670
2837
  const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
2671
- if (!TARGET8.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
2672
- const api = baseUrl(connection.url, DEFAULT_API5);
2838
+ if (!TARGET10.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
2839
+ const api = baseUrl(connection.url, DEFAULT_API6);
2673
2840
  const origin = new URL(api).origin;
2674
2841
  const root = `${api}/repos/${target}/pulls`;
2675
2842
  const headers = { authorization: `Bearer ${connection.token ?? ""}` };
@@ -2691,7 +2858,7 @@ function createPullAdapter({ connection, fetch }) {
2691
2858
  const { body } = await requestJson(`${root}?${params.toString()}`, { fetch, origin, headers });
2692
2859
  const record = isRecord(body) ? body : {};
2693
2860
  const pulls = (Array.isArray(record.pull_requests) ? record.pull_requests : []).map(pullOf3).filter((pull) => matchesPage2(pull, query));
2694
- return { items: pulls, ...nextOf2(record.next_page_token) };
2861
+ return { items: pulls, ...nextOf3(record.next_page_token) };
2695
2862
  },
2696
2863
  get: (key) => getPull(key),
2697
2864
  create: async (input) => pullOf3((await requestJson(root, { fetch, origin, headers, method: "POST", body: createBody2(input) })).body),
@@ -2755,7 +2922,7 @@ function createPullAdapter({ connection, fetch }) {
2755
2922
  const rawComments = Array.isArray(record.pull_request_comments) ? record.pull_request_comments : [];
2756
2923
  const visible = rawComments.filter((raw) => !(isRecord(raw) && raw.is_deleted === true));
2757
2924
  const filtered = query.updatedAfter === void 0 ? visible : visible.filter((raw) => inTimeRange(toTimestamp(isRecord(raw) ? raw.updated_at : void 0), query.updatedAfter, void 0));
2758
- return { items: filtered.map(commentOf6), ...nextOf2(record.next_page_token) };
2925
+ return { items: filtered.map(commentOf6), ...nextOf3(record.next_page_token) };
2759
2926
  },
2760
2927
  comment: async (key, body) => commentOf6((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missing: missing(key) })).body)
2761
2928
  };
@@ -2800,7 +2967,7 @@ function pullFilterOf(query, status) {
2800
2967
  if (query.target !== void 0) clauses.push(`target_branch=${quoted(query.target)}`);
2801
2968
  return clauses.length === 0 ? void 0 : clauses.join(" and ");
2802
2969
  }
2803
- function nextOf2(token) {
2970
+ function nextOf3(token) {
2804
2971
  const value = text(token);
2805
2972
  return value === void 0 ? {} : { nextCursor: value };
2806
2973
  }
@@ -2825,7 +2992,7 @@ function pullOf3(raw) {
2825
2992
  ...author === void 0 ? {} : { author },
2826
2993
  source: text(source.ref) ?? text(item.source_branch) ?? "?",
2827
2994
  target: text(target.ref) ?? text(item.target_branch) ?? "?",
2828
- ...headSha !== void 0 && SHA4.test(headSha) ? { headSha } : {},
2995
+ ...headSha !== void 0 && SHA6.test(headSha) ? { headSha } : {},
2829
2996
  labels: Array.isArray(item.labels) ? stringList(item.labels.map((label) => isRecord(label) ? label.name : void 0)) : [],
2830
2997
  links: [],
2831
2998
  ...createdAt === void 0 ? {} : { createdAt },
@@ -2841,10 +3008,73 @@ function commentOf6(raw) {
2841
3008
  return { id: text(item.id) ?? "?", ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } };
2842
3009
  }
2843
3010
 
3011
+ // src/tracker/sourcecraft-repo.lib.ts
3012
+ var DEFAULT_API7 = "https://api.sourcecraft.tech";
3013
+ var TARGET11 = /^[\w.-]+\/[\w.-]+$/;
3014
+ var SHA7 = /^[0-9a-f]{7,64}$/;
3015
+ var BRANCHES_SUBJECT3 = "\u0412\u0435\u0442\u043A\u0438 SourceCraft";
3016
+ var BRANCH_FILTERS3 = branchFilterSchema.options;
3017
+ var PAGE_SIZE_MAX = 100;
3018
+ var CAPABILITIES6 = {
3019
+ branches: { filters: BRANCH_FILTERS3, clientSide: [], sorts: [] }
3020
+ };
3021
+ function sourcecraftRepo() {
3022
+ return { capabilities: CAPABILITIES6, create: (context) => createSourceCraftRepoAdapter(context) };
3023
+ }
3024
+ function createSourceCraftRepoAdapter({ connection, fetch }) {
3025
+ const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
3026
+ if (!TARGET11.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3027
+ const api = baseUrl(connection.url, DEFAULT_API7);
3028
+ const origin = new URL(api).origin;
3029
+ const repo = `${api}/repos/${target}`;
3030
+ const headers = { authorization: `Bearer ${connection.token ?? ""}` };
3031
+ const missing = { failure: "not_found", message: `\u0412 SourceCraft \u043D\u0435\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F ${target}.` };
3032
+ return {
3033
+ get: async () => repoOf3((await requestJson(repo, { fetch, origin, headers, missing })).body, target),
3034
+ branches: async (query) => {
3035
+ assertListQuery(BRANCHES_SUBJECT3, CAPABILITIES6.branches, query, BRANCH_FILTERS3);
3036
+ const params = new URLSearchParams({ page_size: String(Math.min(query.limit, PAGE_SIZE_MAX)) });
3037
+ if (query.cursor !== void 0) params.set("page_token", query.cursor);
3038
+ if (query.search !== void 0) params.set("filter", query.search);
3039
+ const { body } = await requestJson(`${repo}/branches?${params.toString()}`, { fetch, origin, headers, missing });
3040
+ const record = isRecord(body) ? body : {};
3041
+ const branches = Array.isArray(record.branches) ? record.branches : [];
3042
+ const next = text(record.next_page_token);
3043
+ return { items: branches.map(branchOf3), ...next === void 0 ? {} : { nextCursor: next } };
3044
+ }
3045
+ };
3046
+ }
3047
+ function repoOf3(raw, target) {
3048
+ const item = isRecord(raw) ? raw : {};
3049
+ const defaultBranch = text(item.default_branch);
3050
+ const description = text(item.description);
3051
+ const visibility = visibilityOf3(item.visibility);
3052
+ const url = text(item.web_url);
3053
+ return {
3054
+ name: text(item.slug) ?? text(item.name) ?? target,
3055
+ ...defaultBranch === void 0 ? {} : { defaultBranch },
3056
+ ...description === void 0 ? {} : { description },
3057
+ ...visibility === void 0 ? {} : { visibility },
3058
+ links: url === void 0 ? [] : [{ url, title: "SourceCraft" }]
3059
+ };
3060
+ }
3061
+ function branchOf3(raw) {
3062
+ const item = isRecord(raw) ? raw : {};
3063
+ const sha = isRecord(item.commit) ? text(item.commit.hash) : void 0;
3064
+ return {
3065
+ name: text(item.name) ?? "?",
3066
+ ...sha === void 0 || !SHA7.test(sha) ? {} : { sha }
3067
+ };
3068
+ }
3069
+ function visibilityOf3(value) {
3070
+ const parsed = repoVisibilitySchema.safeParse(value);
3071
+ return parsed.success ? parsed.data : void 0;
3072
+ }
3073
+
2844
3074
  // src/tracker/sourcecraft.lib.ts
2845
- var DEFAULT_API6 = "https://api.sourcecraft.tech";
3075
+ var DEFAULT_API8 = "https://api.sourcecraft.tech";
2846
3076
  var DEFAULT_WEB = "https://sourcecraft.dev";
2847
- var TARGET9 = /^[\w.-]+\/[\w.-]+$/;
3077
+ var TARGET12 = /^[\w.-]+\/[\w.-]+$/;
2848
3078
  var SLUG2 = /^[\w-]{1,100}$/;
2849
3079
  var ISSUE_SORT_FIELDS_UPSTREAM = {
2850
3080
  created: "created_at",
@@ -2870,15 +3100,16 @@ function sourcecraftTracker() {
2870
3100
  },
2871
3101
  issues: ISSUE_CAPABILITIES4,
2872
3102
  create: (context) => createSourceCraftAdapter(context),
2873
- origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_API6)).origin,
3103
+ origin: (connection) => new URL(baseUrl(connection.url, DEFAULT_API8)).origin,
2874
3104
  pulls: sourcecraftPulls(),
2875
- ci: sourcecraftCi()
3105
+ ci: sourcecraftCi(),
3106
+ repo: sourcecraftRepo()
2876
3107
  };
2877
3108
  }
2878
3109
  function createSourceCraftAdapter({ connection, fetch }) {
2879
3110
  const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
2880
- if (!TARGET9.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
2881
- const api = baseUrl(connection.url, DEFAULT_API6);
3111
+ if (!TARGET12.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3112
+ const api = baseUrl(connection.url, DEFAULT_API8);
2882
3113
  const origin = new URL(api).origin;
2883
3114
  const root = `${api}/repos/${target}/issues`;
2884
3115
  const headers = { authorization: `Bearer ${connection.token ?? ""}` };
@@ -2901,7 +3132,7 @@ function createSourceCraftAdapter({ connection, fetch }) {
2901
3132
  let issues = (Array.isArray(record.issues) ? record.issues : []).map(toIssue2);
2902
3133
  if (query.status !== void 0) issues = issues.filter((issue) => query.status?.includes(issue.status.category) === true);
2903
3134
  if (query.assignee === null) issues = issues.filter((issue) => issue.assignee === void 0);
2904
- return { items: issues, ...nextOf3(record.next_page_token) };
3135
+ return { items: issues, ...nextOf4(record.next_page_token) };
2905
3136
  },
2906
3137
  get: async (key) => toIssue2((await requestJson(`${root}/${slug(key)}`, { fetch, origin, headers, missingIsIssue: true })).body),
2907
3138
  create: async (input) => {
@@ -2918,7 +3149,7 @@ function createSourceCraftAdapter({ connection, fetch }) {
2918
3149
  const record = isRecord(body) ? body : {};
2919
3150
  const rawComments = Array.isArray(record.issue_comments) ? record.issue_comments : [];
2920
3151
  const filtered = query.updatedAfter === void 0 ? rawComments : rawComments.filter((raw) => inTimeRange(toTimestamp(isRecord(raw) ? raw.updated_at : void 0), query.updatedAfter, void 0));
2921
- return { items: filtered.map(commentOf7), ...nextOf3(record.next_page_token) };
3152
+ return { items: filtered.map(commentOf7), ...nextOf4(record.next_page_token) };
2922
3153
  },
2923
3154
  comment: async (key, body) => commentOf7((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
2924
3155
  };
@@ -2931,7 +3162,7 @@ function patchBody5(patch) {
2931
3162
  ...patch.priority === void 0 ? {} : { priority: patch.priority }
2932
3163
  };
2933
3164
  }
2934
- function nextOf3(token) {
3165
+ function nextOf4(token) {
2935
3166
  const value = text(token);
2936
3167
  return value === void 0 ? {} : { nextCursor: value };
2937
3168
  }
@@ -3049,6 +3280,12 @@ function pullRequestAdapterOf(definition, context) {
3049
3280
  if (definition.pulls === void 0) throw new TrackerError("unsupported", `\u0422\u0440\u0435\u043A\u0435\u0440 ${definition.descriptor.title} \u043D\u0435 \u0432\u0435\u0434\u0451\u0442 pull request-\u044B.`);
3050
3281
  return definition.pulls.create(context);
3051
3282
  }
3283
+
3284
+ // src/tracker/repo.lib.ts
3285
+ function repoAdapterOf(definition, context) {
3286
+ if (definition.repo === void 0) throw new TrackerError("unsupported", `\u0422\u0440\u0435\u043A\u0435\u0440 ${definition.descriptor.title} \u043D\u0435 \u0432\u0435\u0434\u0451\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439.`);
3287
+ return definition.repo.create(context);
3288
+ }
3052
3289
  export {
3053
3290
  ALL_STATUSES,
3054
3291
  CI_FINISHED_STATUSES,
@@ -3095,6 +3332,8 @@ export {
3095
3332
  assertCommentQuery,
3096
3333
  assertListQuery,
3097
3334
  baseUrl,
3335
+ branchFilterSchema,
3336
+ branchSchema,
3098
3337
  ciAdapterOf,
3099
3338
  ciEventCategorySchema,
3100
3339
  ciJobFilterSchema,
@@ -3113,9 +3352,11 @@ export {
3113
3352
  findTracker,
3114
3353
  githubCi,
3115
3354
  githubPulls,
3355
+ githubRepo,
3116
3356
  githubTracker,
3117
3357
  gitlabCi,
3118
3358
  gitlabPulls,
3359
+ gitlabRepo,
3119
3360
  gitlabTracker,
3120
3361
  hasTrackerToken,
3121
3362
  inTimeRange,
@@ -3143,11 +3384,15 @@ export {
3143
3384
  readTrackerToken,
3144
3385
  registerBuiltinTrackers,
3145
3386
  registerTracker,
3387
+ repoAdapterOf,
3388
+ repoSchema,
3389
+ repoVisibilitySchema,
3146
3390
  requestJson,
3147
3391
  requestText,
3148
3392
  requireTarget,
3149
3393
  sourcecraftCi,
3150
3394
  sourcecraftPulls,
3395
+ sourcecraftRepo,
3151
3396
  sourcecraftTracker,
3152
3397
  stringList,
3153
3398
  text,