ghagga 3.0.0 → 3.2.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.
Files changed (130) hide show
  1. package/dist/commands/login.test.js +0 -2
  2. package/dist/commands/login.test.js.map +1 -1
  3. package/dist/commands/memory/backfill.d.ts +13 -0
  4. package/dist/commands/memory/backfill.d.ts.map +1 -0
  5. package/dist/commands/memory/backfill.js +47 -0
  6. package/dist/commands/memory/backfill.js.map +1 -0
  7. package/dist/commands/memory/backfill.test.d.ts +11 -0
  8. package/dist/commands/memory/backfill.test.d.ts.map +1 -0
  9. package/dist/commands/memory/backfill.test.js +127 -0
  10. package/dist/commands/memory/backfill.test.js.map +1 -0
  11. package/dist/commands/memory/clear.test.js +9 -0
  12. package/dist/commands/memory/clear.test.js.map +1 -1
  13. package/dist/commands/memory/delete.test.js +9 -0
  14. package/dist/commands/memory/delete.test.js.map +1 -1
  15. package/dist/commands/memory/index.d.ts +3 -3
  16. package/dist/commands/memory/index.d.ts.map +1 -1
  17. package/dist/commands/memory/index.js +5 -3
  18. package/dist/commands/memory/index.js.map +1 -1
  19. package/dist/commands/memory/list.test.js +9 -0
  20. package/dist/commands/memory/list.test.js.map +1 -1
  21. package/dist/commands/memory/search.test.js +9 -0
  22. package/dist/commands/memory/search.test.js.map +1 -1
  23. package/dist/commands/memory/show.test.js +9 -0
  24. package/dist/commands/memory/show.test.js.map +1 -1
  25. package/dist/commands/memory/stats.test.js +9 -0
  26. package/dist/commands/memory/stats.test.js.map +1 -1
  27. package/dist/commands/memory/utils.d.ts.map +1 -1
  28. package/dist/commands/memory/utils.js +14 -1
  29. package/dist/commands/memory/utils.js.map +1 -1
  30. package/dist/commands/review-output.test.js +37 -0
  31. package/dist/commands/review-output.test.js.map +1 -1
  32. package/dist/commands/review.d.ts +12 -0
  33. package/dist/commands/review.d.ts.map +1 -1
  34. package/dist/commands/review.js +239 -5
  35. package/dist/commands/review.js.map +1 -1
  36. package/dist/commands/review.mr-exit.test.d.ts +13 -0
  37. package/dist/commands/review.mr-exit.test.d.ts.map +1 -0
  38. package/dist/commands/review.mr-exit.test.js +190 -0
  39. package/dist/commands/review.mr-exit.test.js.map +1 -0
  40. package/dist/commands/review.postback-disclosure.test.d.ts +22 -0
  41. package/dist/commands/review.postback-disclosure.test.d.ts.map +1 -0
  42. package/dist/commands/review.postback-disclosure.test.js +192 -0
  43. package/dist/commands/review.postback-disclosure.test.js.map +1 -0
  44. package/dist/commands/review.pr-exit.test.d.ts +13 -0
  45. package/dist/commands/review.pr-exit.test.d.ts.map +1 -0
  46. package/dist/commands/review.pr-exit.test.js +162 -0
  47. package/dist/commands/review.pr-exit.test.js.map +1 -0
  48. package/dist/commands/review.pr-jenkins.test.d.ts +16 -0
  49. package/dist/commands/review.pr-jenkins.test.d.ts.map +1 -0
  50. package/dist/commands/review.pr-jenkins.test.js +106 -0
  51. package/dist/commands/review.pr-jenkins.test.js.map +1 -0
  52. package/dist/commands/review.pr-nativeid-fallback.test.d.ts +15 -0
  53. package/dist/commands/review.pr-nativeid-fallback.test.d.ts.map +1 -0
  54. package/dist/commands/review.pr-nativeid-fallback.test.js +151 -0
  55. package/dist/commands/review.pr-nativeid-fallback.test.js.map +1 -0
  56. package/dist/commands/review.test.js +13 -0
  57. package/dist/commands/review.test.js.map +1 -1
  58. package/dist/index.js +39 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/lib/cli-github-client-port.d.ts +52 -0
  61. package/dist/lib/cli-github-client-port.d.ts.map +1 -0
  62. package/dist/lib/cli-github-client-port.js +199 -0
  63. package/dist/lib/cli-github-client-port.js.map +1 -0
  64. package/dist/lib/cli-github-client-port.test.d.ts +10 -0
  65. package/dist/lib/cli-github-client-port.test.d.ts.map +1 -0
  66. package/dist/lib/cli-github-client-port.test.js +47 -0
  67. package/dist/lib/cli-github-client-port.test.js.map +1 -0
  68. package/dist/lib/cli-gitlab-client-port.d.ts +62 -0
  69. package/dist/lib/cli-gitlab-client-port.d.ts.map +1 -0
  70. package/dist/lib/cli-gitlab-client-port.js +209 -0
  71. package/dist/lib/cli-gitlab-client-port.js.map +1 -0
  72. package/dist/lib/cli-gitlab-client-port.test.d.ts +9 -0
  73. package/dist/lib/cli-gitlab-client-port.test.d.ts.map +1 -0
  74. package/dist/lib/cli-gitlab-client-port.test.js +292 -0
  75. package/dist/lib/cli-gitlab-client-port.test.js.map +1 -0
  76. package/dist/lib/config.d.ts +23 -0
  77. package/dist/lib/config.d.ts.map +1 -1
  78. package/dist/lib/config.js +9 -0
  79. package/dist/lib/config.js.map +1 -1
  80. package/dist/lib/embedding.d.ts +23 -0
  81. package/dist/lib/embedding.d.ts.map +1 -0
  82. package/dist/lib/embedding.js +39 -0
  83. package/dist/lib/embedding.js.map +1 -0
  84. package/dist/lib/embedding.test.d.ts +8 -0
  85. package/dist/lib/embedding.test.d.ts.map +1 -0
  86. package/dist/lib/embedding.test.js +101 -0
  87. package/dist/lib/embedding.test.js.map +1 -0
  88. package/dist/lib/forge-postback.d.ts +72 -0
  89. package/dist/lib/forge-postback.d.ts.map +1 -0
  90. package/dist/lib/forge-postback.js +39 -0
  91. package/dist/lib/forge-postback.js.map +1 -0
  92. package/dist/lib/gitlab-api.d.ts +50 -0
  93. package/dist/lib/gitlab-api.d.ts.map +1 -0
  94. package/dist/lib/gitlab-api.js +75 -0
  95. package/dist/lib/gitlab-api.js.map +1 -0
  96. package/dist/lib/gitlab-api.test.d.ts +2 -0
  97. package/dist/lib/gitlab-api.test.d.ts.map +1 -0
  98. package/dist/lib/gitlab-api.test.js +79 -0
  99. package/dist/lib/gitlab-api.test.js.map +1 -0
  100. package/dist/lib/gitlab-e2e.manual.test.d.ts +57 -0
  101. package/dist/lib/gitlab-e2e.manual.test.d.ts.map +1 -0
  102. package/dist/lib/gitlab-e2e.manual.test.js +135 -0
  103. package/dist/lib/gitlab-e2e.manual.test.js.map +1 -0
  104. package/dist/lib/gitlab-token.d.ts +17 -0
  105. package/dist/lib/gitlab-token.d.ts.map +1 -0
  106. package/dist/lib/gitlab-token.js +24 -0
  107. package/dist/lib/gitlab-token.js.map +1 -0
  108. package/dist/lib/gitlab-token.test.d.ts +2 -0
  109. package/dist/lib/gitlab-token.test.d.ts.map +1 -0
  110. package/dist/lib/gitlab-token.test.js +38 -0
  111. package/dist/lib/gitlab-token.test.js.map +1 -0
  112. package/dist/lib/oauth.test.js +0 -1
  113. package/dist/lib/oauth.test.js.map +1 -1
  114. package/dist/lib/pr-postback.d.ts +42 -0
  115. package/dist/lib/pr-postback.d.ts.map +1 -0
  116. package/dist/lib/pr-postback.js +44 -0
  117. package/dist/lib/pr-postback.js.map +1 -0
  118. package/dist/lib/pr-postback.test.d.ts +9 -0
  119. package/dist/lib/pr-postback.test.d.ts.map +1 -0
  120. package/dist/lib/pr-postback.test.js +212 -0
  121. package/dist/lib/pr-postback.test.js.map +1 -0
  122. package/dist/lib/pr-token.d.ts +15 -0
  123. package/dist/lib/pr-token.d.ts.map +1 -0
  124. package/dist/lib/pr-token.js +22 -0
  125. package/dist/lib/pr-token.js.map +1 -0
  126. package/dist/lib/pr-token.test.d.ts +2 -0
  127. package/dist/lib/pr-token.test.d.ts.map +1 -0
  128. package/dist/lib/pr-token.test.js +25 -0
  129. package/dist/lib/pr-token.test.js.map +1 -0
  130. package/package.json +9 -8
@@ -0,0 +1,52 @@
1
+ /**
2
+ * CLI-side implementation of the forge `GitHubClientPort`.
3
+ *
4
+ * The `GitHubForgeAdapter` (packages/forge) depends on an injected
5
+ * {@link GitHubClientPort} (dependency inversion — forge MUST NOT import any
6
+ * concrete HTTP client). The SERVER injects `apps/server/src/github/client.ts`.
7
+ * The CLI has its OWN minimal client built on the same native-fetch +
8
+ * {@link apiHeaders} / {@link GitHubApiError} pattern as `github-api.ts`.
9
+ *
10
+ * SCOPE (P3 — PR summary post-back only):
11
+ * REAL members — the write/idempotency fns `upsertSummaryComment` folds over:
12
+ * - postComment (POST /repos/{o}/{r}/issues/{n}/comments)
13
+ * - findExistingComment(GET /repos/{o}/{r}/issues/{n}/comments — marker match)
14
+ * - deleteComment (DELETE /repos/{o}/{r}/issues/comments/{id})
15
+ * - updateComment (PATCH /repos/{o}/{r}/issues/comments/{id})
16
+ * STUBBED members — read fns the CLI never needs (it has the diff locally and
17
+ * does not consume reactions / graph in the `--pr` flow):
18
+ * - fetchPRDiff, fetchPRDetails, getPRFileList, getPRCommitMessages
19
+ * - addCommentReaction
20
+ * - fetchGraphFromBranch, fetchGraphMetadata
21
+ * Each stub THROWS so any accidental call surfaces loudly instead of silently
22
+ * returning bad data. The `--pr` post-back path (find → delete → post) only
23
+ * ever touches the REAL members; see pr-postback.ts + tests.
24
+ *
25
+ * The stale-comment match here mirrors the server EXACTLY: bot-authored-or-not is
26
+ * NOT inspected (the CLI token's own comments carry the marker), the match is the
27
+ * fixed `<!-- ghagga-review -->` marker substring (REVIEW_COMMENT_MARKER), latest
28
+ * = last in chronological order, the rest are stale. This keeps idempotency
29
+ * byte-compatible with the server post-back.
30
+ */
31
+ import type { GitHubClientPort } from 'ghagga-forge';
32
+ /**
33
+ * Resolve the GitHub NUMERIC repo id from an owner/name pair (forge identity).
34
+ *
35
+ * `GET /repos/{owner}/{repo}` → `.id`. owner/repo is the MUTABLE display path
36
+ * (a rename/transfer changes it but NOT the numeric id), so the numeric id is the
37
+ * canonical {@link RepoRef.nativeId}. This MIRRORS the GitLab `--mr` flow's
38
+ * {@link resolveGitLabProjectId} (one pre-construction call that turns the mutable
39
+ * path into the immutable id). It is NOT part of the {@link GitHubClientPort} — the
40
+ * adapter's actual API calls still key on owner/repo from its ctor; nativeId is
41
+ * identity metadata only.
42
+ *
43
+ * @returns the numeric repo id as a STRING (RepoRef.nativeId is a string,
44
+ * consistent with GitLab's `String(id)`).
45
+ */
46
+ export declare function resolveGitHubRepoId(owner: string, repo: string, token: string): Promise<string>;
47
+ /**
48
+ * Build a {@link GitHubClientPort} backed by native fetch for the CLI `--pr`
49
+ * post-back. Only the comment-write members are real; read members throw.
50
+ */
51
+ export declare function createCliGitHubClientPort(): GitHubClientPort;
52
+ //# sourceMappingURL=cli-github-client-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-github-client-port.d.ts","sourceRoot":"","sources":["../../src/lib/cli-github-client-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,cAAc,CAAC;AAoC5E;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAWD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,gBAAgB,CAoH5D"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * CLI-side implementation of the forge `GitHubClientPort`.
3
+ *
4
+ * The `GitHubForgeAdapter` (packages/forge) depends on an injected
5
+ * {@link GitHubClientPort} (dependency inversion — forge MUST NOT import any
6
+ * concrete HTTP client). The SERVER injects `apps/server/src/github/client.ts`.
7
+ * The CLI has its OWN minimal client built on the same native-fetch +
8
+ * {@link apiHeaders} / {@link GitHubApiError} pattern as `github-api.ts`.
9
+ *
10
+ * SCOPE (P3 — PR summary post-back only):
11
+ * REAL members — the write/idempotency fns `upsertSummaryComment` folds over:
12
+ * - postComment (POST /repos/{o}/{r}/issues/{n}/comments)
13
+ * - findExistingComment(GET /repos/{o}/{r}/issues/{n}/comments — marker match)
14
+ * - deleteComment (DELETE /repos/{o}/{r}/issues/comments/{id})
15
+ * - updateComment (PATCH /repos/{o}/{r}/issues/comments/{id})
16
+ * STUBBED members — read fns the CLI never needs (it has the diff locally and
17
+ * does not consume reactions / graph in the `--pr` flow):
18
+ * - fetchPRDiff, fetchPRDetails, getPRFileList, getPRCommitMessages
19
+ * - addCommentReaction
20
+ * - fetchGraphFromBranch, fetchGraphMetadata
21
+ * Each stub THROWS so any accidental call surfaces loudly instead of silently
22
+ * returning bad data. The `--pr` post-back path (find → delete → post) only
23
+ * ever touches the REAL members; see pr-postback.ts + tests.
24
+ *
25
+ * The stale-comment match here mirrors the server EXACTLY: bot-authored-or-not is
26
+ * NOT inspected (the CLI token's own comments carry the marker), the match is the
27
+ * fixed `<!-- ghagga-review -->` marker substring (REVIEW_COMMENT_MARKER), latest
28
+ * = last in chronological order, the rest are stale. This keeps idempotency
29
+ * byte-compatible with the server post-back.
30
+ */
31
+ import { REVIEW_COMMENT_MARKER } from 'ghagga-core';
32
+ import { GitHubApiError } from './github-api.js';
33
+ // The fixed marker the summary body carries. SINGLE SOURCE OF TRUTH: imported
34
+ // from ghagga-core (REVIEW_COMMENT_MARKER) so the stale-comment match here can
35
+ // never drift from the marker the body actually carries.
36
+ const REVIEW_MARKER = REVIEW_COMMENT_MARKER;
37
+ const API_BASE = 'https://api.github.com';
38
+ /**
39
+ * Per-request timeout for the CLI's GitHub fetch calls. Mirrors the server
40
+ * client's `AbortSignal.timeout(10_000)` so a slow/hung GitHub can never hang a
41
+ * CI job indefinitely — the fetch aborts and surfaces as a thrown error.
42
+ */
43
+ const REQUEST_TIMEOUT_MS = 10_000;
44
+ /** Shared auth/version headers — same shape as github-api.ts apiHeaders. */
45
+ function apiHeaders(token) {
46
+ return {
47
+ Authorization: `Bearer ${token}`,
48
+ Accept: 'application/vnd.github+json',
49
+ 'X-GitHub-Api-Version': '2022-11-28',
50
+ 'Content-Type': 'application/json',
51
+ };
52
+ }
53
+ async function failOn(res, action) {
54
+ const body = await res.text();
55
+ throw new GitHubApiError(`GitHub API error ${action}: ${res.status} ${res.statusText}`, res.status, body);
56
+ }
57
+ /**
58
+ * Resolve the GitHub NUMERIC repo id from an owner/name pair (forge identity).
59
+ *
60
+ * `GET /repos/{owner}/{repo}` → `.id`. owner/repo is the MUTABLE display path
61
+ * (a rename/transfer changes it but NOT the numeric id), so the numeric id is the
62
+ * canonical {@link RepoRef.nativeId}. This MIRRORS the GitLab `--mr` flow's
63
+ * {@link resolveGitLabProjectId} (one pre-construction call that turns the mutable
64
+ * path into the immutable id). It is NOT part of the {@link GitHubClientPort} — the
65
+ * adapter's actual API calls still key on owner/repo from its ctor; nativeId is
66
+ * identity metadata only.
67
+ *
68
+ * @returns the numeric repo id as a STRING (RepoRef.nativeId is a string,
69
+ * consistent with GitLab's `String(id)`).
70
+ */
71
+ export async function resolveGitHubRepoId(owner, repo, token) {
72
+ const url = `${API_BASE}/repos/${owner}/${repo}`;
73
+ const res = await fetch(url, {
74
+ headers: apiHeaders(token),
75
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
76
+ });
77
+ if (!res.ok)
78
+ await failOn(res, 'resolving repo id');
79
+ const data = (await res.json());
80
+ if (typeof data.id !== 'number') {
81
+ throw new GitHubApiError(`GitHub API error resolving repo id: response had no numeric "id" for "${owner}/${repo}"`, res.status, JSON.stringify(data));
82
+ }
83
+ return String(data.id);
84
+ }
85
+ /** Thrown by every stubbed port member so an accidental call is loud, not silent. */
86
+ function unsupported(member) {
87
+ throw new Error(`CliGitHubClientPort.${member} is not supported in CLI context — the CLI only ` +
88
+ 'posts the PR summary comment (postComment/findExistingComment/deleteComment/' +
89
+ 'updateComment). The diff/commits/file-list/graph are sourced locally.');
90
+ }
91
+ /**
92
+ * Build a {@link GitHubClientPort} backed by native fetch for the CLI `--pr`
93
+ * post-back. Only the comment-write members are real; read members throw.
94
+ */
95
+ export function createCliGitHubClientPort() {
96
+ return {
97
+ // ── REAL: PR/issue comment writes (the upsert fold) ──────────
98
+ async postComment(owner, repo, prNumber, body, token) {
99
+ const url = `${API_BASE}/repos/${owner}/${repo}/issues/${prNumber}/comments`;
100
+ const res = await fetch(url, {
101
+ method: 'POST',
102
+ headers: apiHeaders(token),
103
+ body: JSON.stringify({ body }),
104
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
105
+ });
106
+ if (!res.ok)
107
+ await failOn(res, 'posting PR comment');
108
+ const data = (await res.json());
109
+ return { id: data.id };
110
+ },
111
+ async findExistingComment(owner, repo, prNumber, token) {
112
+ const baseUrl = `${API_BASE}/repos/${owner}/${repo}/issues/${prNumber}/comments`;
113
+ // Paginate until exhausted (a page with < 100 items is the last page).
114
+ // MAX_PAGES is a safety upper bound (50 × 100 = 5000 comments) so a
115
+ // pathological PR can't loop forever; if hit we log rather than silently
116
+ // truncate, since a stale marker beyond the bound would yield a DUPLICATE
117
+ // comment. (Was 5 pages / 500 items — backlog #6.)
118
+ const MAX_PAGES = 50;
119
+ const allMatchIds = [];
120
+ for (let page = 1; page <= MAX_PAGES; page++) {
121
+ const url = `${baseUrl}?per_page=100&page=${page}`;
122
+ const res = await fetch(url, {
123
+ headers: apiHeaders(token),
124
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
125
+ });
126
+ if (!res.ok)
127
+ await failOn(res, 'listing PR comments');
128
+ const comments = (await res.json());
129
+ for (const comment of comments) {
130
+ if (comment.body.includes(REVIEW_MARKER)) {
131
+ allMatchIds.push(comment.id);
132
+ }
133
+ }
134
+ if (comments.length < 100)
135
+ break;
136
+ if (page === MAX_PAGES) {
137
+ console.warn(`[ghagga] findExistingComment hit MAX_PAGES (${MAX_PAGES}) for ${owner}/${repo}#${prNumber}; comment listing may be truncated`);
138
+ }
139
+ }
140
+ if (allMatchIds.length === 0)
141
+ return null;
142
+ // Latest = last (GitHub returns oldest first); the rest are stale.
143
+ const latestId = allMatchIds[allMatchIds.length - 1];
144
+ if (latestId == null)
145
+ return null;
146
+ const staleIds = allMatchIds.slice(0, -1);
147
+ return { latestId, staleIds };
148
+ },
149
+ async deleteComment(owner, repo, commentId, token) {
150
+ const url = `${API_BASE}/repos/${owner}/${repo}/issues/comments/${commentId}`;
151
+ const res = await fetch(url, {
152
+ method: 'DELETE',
153
+ headers: apiHeaders(token),
154
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
155
+ });
156
+ // The adapter's upsert tolerates delete failures (best-effort), but we still
157
+ // surface a non-2xx so `deleted[]` only reflects genuinely-removed ids.
158
+ if (!res.ok && res.status !== 404)
159
+ await failOn(res, 'deleting PR comment');
160
+ },
161
+ async updateComment(owner, repo, commentId, body, token) {
162
+ const url = `${API_BASE}/repos/${owner}/${repo}/issues/comments/${commentId}`;
163
+ const res = await fetch(url, {
164
+ method: 'PATCH',
165
+ headers: apiHeaders(token),
166
+ body: JSON.stringify({ body }),
167
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
168
+ });
169
+ if (!res.ok)
170
+ await failOn(res, 'updating PR comment');
171
+ },
172
+ // ── STUBBED: read/reaction/graph members (never hit in --pr flow) ──
173
+ // Declared `async` so the throw surfaces as a REJECTED promise (not a sync
174
+ // throw): callers `await` these, and a rejected promise is the contract-true
175
+ // failure shape — also keeps test spies/`.rejects` well-behaved.
176
+ async fetchPRDiff() {
177
+ return unsupported('fetchPRDiff');
178
+ },
179
+ async fetchPRDetails() {
180
+ return unsupported('fetchPRDetails');
181
+ },
182
+ async getPRFileList() {
183
+ return unsupported('getPRFileList');
184
+ },
185
+ async getPRCommitMessages() {
186
+ return unsupported('getPRCommitMessages');
187
+ },
188
+ async addCommentReaction(_owner, _repo, _commentId, _reaction, _token) {
189
+ return unsupported('addCommentReaction');
190
+ },
191
+ async fetchGraphFromBranch() {
192
+ return unsupported('fetchGraphFromBranch');
193
+ },
194
+ async fetchGraphMetadata() {
195
+ return unsupported('fetchGraphMetadata');
196
+ },
197
+ };
198
+ }
199
+ //# sourceMappingURL=cli-github-client-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-github-client-port.js","sourceRoot":"","sources":["../../src/lib/cli-github-client-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * resolveGitHubRepoId — numeric repo id resolution for RepoRef.nativeId.
3
+ *
4
+ * The CLI `--pr` flow resolves the IMMUTABLE numeric GitHub repo id once before
5
+ * the post-back (mirroring the GitLab `--mr` flow's resolveGitLabProjectId). We
6
+ * mock ONLY `fetch` (the network seam) so this exercises the genuine GET
7
+ * /repos/{owner}/{repo} → .id call + its error handling.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=cli-github-client-port.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-github-client-port.test.d.ts","sourceRoot":"","sources":["../../src/lib/cli-github-client-port.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * resolveGitHubRepoId — numeric repo id resolution for RepoRef.nativeId.
3
+ *
4
+ * The CLI `--pr` flow resolves the IMMUTABLE numeric GitHub repo id once before
5
+ * the post-back (mirroring the GitLab `--mr` flow's resolveGitLabProjectId). We
6
+ * mock ONLY `fetch` (the network seam) so this exercises the genuine GET
7
+ * /repos/{owner}/{repo} → .id call + its error handling.
8
+ */
9
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
10
+ import { resolveGitHubRepoId } from './cli-github-client-port.js';
11
+ const mockFetch = vi.fn();
12
+ vi.stubGlobal('fetch', mockFetch);
13
+ function jsonResponse(status, body) {
14
+ return {
15
+ ok: status >= 200 && status < 300,
16
+ status,
17
+ statusText: status === 200 ? 'OK' : 'ERR',
18
+ text: () => Promise.resolve(JSON.stringify(body)),
19
+ json: () => Promise.resolve(body),
20
+ };
21
+ }
22
+ describe('resolveGitHubRepoId (numeric repo id resolution → RepoRef.nativeId)', () => {
23
+ beforeEach(() => mockFetch.mockReset());
24
+ it('GET /repos/{owner}/{repo} → numeric id as string', async () => {
25
+ mockFetch.mockResolvedValueOnce(jsonResponse(200, { id: 555000111, full_name: 'acme/widgets' }));
26
+ const id = await resolveGitHubRepoId('acme', 'widgets', 'ghp');
27
+ // Stringified to match RepoRef.nativeId's string type (consistent with GitLab).
28
+ expect(id).toBe('555000111');
29
+ const [url] = mockFetch.mock.calls[0];
30
+ expect(url).toBe('https://api.github.com/repos/acme/widgets');
31
+ });
32
+ it('passes an AbortSignal (timeout) so CI cannot hang', async () => {
33
+ mockFetch.mockResolvedValueOnce(jsonResponse(200, { id: 1 }));
34
+ await resolveGitHubRepoId('acme', 'widgets', 'ghp');
35
+ const [, init] = mockFetch.mock.calls[0];
36
+ expect(init.signal).toBeInstanceOf(AbortSignal);
37
+ });
38
+ it('throws on a non-ok response (e.g. 404)', async () => {
39
+ mockFetch.mockResolvedValueOnce(jsonResponse(404, { message: 'Not Found' }));
40
+ await expect(resolveGitHubRepoId('nope', 'nope', 'ghp')).rejects.toThrow(/resolving repo id/);
41
+ });
42
+ it('throws when the response has no numeric "id"', async () => {
43
+ mockFetch.mockResolvedValueOnce(jsonResponse(200, { full_name: 'acme/widgets' }));
44
+ await expect(resolveGitHubRepoId('acme', 'widgets', 'ghp')).rejects.toThrow(/no numeric "id"/);
45
+ });
46
+ });
47
+ //# sourceMappingURL=cli-github-client-port.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-github-client-port.test.js","sourceRoot":"","sources":["../../src/lib/cli-github-client-port.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * CLI-side implementation of the forge `GitLabClientPort` + numeric-project-id
3
+ * resolution for the `ghagga review --mr N` MR summary post-back.
4
+ *
5
+ * The `GitLabForgeAdapter` (packages/forge) depends on an injected
6
+ * {@link GitLabClientPort} (dependency inversion — forge MUST NOT import any
7
+ * concrete HTTP client). The CLI provides the real native-fetch implementation
8
+ * here, on the same `AbortSignal.timeout` + error-class pattern as
9
+ * `cli-github-client-port.ts`.
10
+ *
11
+ * AUTH: we send ONLY the `PRIVATE-TOKEN: <pat>` header — GitLab's recommended
12
+ * header for Personal/Project/Group Access Tokens (the kind `ghagga` uses). We do
13
+ * NOT also send `Authorization: Bearer`, which is for OAuth2 access tokens; mixing
14
+ * both is unnecessary and the PAT header is the canonical PAT auth path.
15
+ *
16
+ * HOST: the API base is derived from the resolved GitLab host
17
+ * (`https://<host>/api/v4`) so self-managed instances work, with a `GITLAB_HOST`
18
+ * env override for the rare case the API host differs from the git remote host.
19
+ *
20
+ * SCOPE (P4 — MR summary post-back): the four note members the adapter's
21
+ * `upsertSummaryComment` folds over (list/create/delete/update) plus
22
+ * `publishInline` (create). All four route through the NUMERIC project id (the
23
+ * canonical R-GITLAB identity). `resolveGitLabProjectId` is the pre-construction
24
+ * step that turns the mutable group/project path into that numeric id.
25
+ */
26
+ import type { GitLabClientPort } from 'ghagga-forge';
27
+ /**
28
+ * Resolve the GitLab API base (`https://<host>/api/v4`) for a remote host.
29
+ *
30
+ * Precedence:
31
+ * 1. `GITLAB_API_BASE` — a FULL API base override. Required for a self-managed
32
+ * GitLab served under a SUBPATH (e.g. `https://example.com/gitlab`), where a
33
+ * host-only value cannot express the `/gitlab` prefix. Used verbatim (only a
34
+ * trailing slash is trimmed); supply the complete `.../api/v4` URL.
35
+ * 2. `GITLAB_HOST` — a host-only override (for when the API host differs from
36
+ * the git remote host, e.g. a reverse proxy on a different hostname).
37
+ * 3. the host parsed from the git remote.
38
+ * For (2)/(3) a bare host is expected; a stray scheme/trailing slash is tolerated.
39
+ */
40
+ export declare function resolveGitLabApiBase(host: string, env?: NodeJS.ProcessEnv): string;
41
+ /**
42
+ * Resolve the GitLab NUMERIC project id from a group/project path (R-GITLAB).
43
+ *
44
+ * `GET /projects/:url-encoded-path` → `.id`. The path is mutable, so the numeric
45
+ * id is the canonical `RepoRef.nativeId`. This runs ONCE before constructing the
46
+ * adapter; it is NOT part of the {@link GitLabClientPort} (the adapter only ever
47
+ * speaks numeric ids).
48
+ *
49
+ * @param apiBase the resolved API base (`https://<host>/api/v4`) — see
50
+ * {@link resolveGitLabApiBase}. Defaults to gitlab.com for back-compat callers.
51
+ * @returns the numeric project id as a STRING (RepoRef.nativeId is a string).
52
+ */
53
+ export declare function resolveGitLabProjectId(path: string, token: string, apiBase?: string): Promise<string>;
54
+ /**
55
+ * Build a {@link GitLabClientPort} backed by native fetch for the CLI `--mr`
56
+ * post-back. All members route through the numeric project id.
57
+ *
58
+ * @param apiBase the resolved API base (`https://<host>/api/v4`) — see
59
+ * {@link resolveGitLabApiBase}. Defaults to gitlab.com for back-compat callers.
60
+ */
61
+ export declare function createCliGitLabClientPort(apiBase?: string): GitLabClientPort;
62
+ //# sourceMappingURL=cli-gitlab-client-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-gitlab-client-port.d.ts","sourceRoot":"","sources":["../../src/lib/cli-gitlab-client-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAc,MAAM,cAAc,CAAC;AAUjE;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAU/F;AAwBD;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,SAA8B,GACpC,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,SAA8B,GAAG,gBAAgB,CA2GjG"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * CLI-side implementation of the forge `GitLabClientPort` + numeric-project-id
3
+ * resolution for the `ghagga review --mr N` MR summary post-back.
4
+ *
5
+ * The `GitLabForgeAdapter` (packages/forge) depends on an injected
6
+ * {@link GitLabClientPort} (dependency inversion — forge MUST NOT import any
7
+ * concrete HTTP client). The CLI provides the real native-fetch implementation
8
+ * here, on the same `AbortSignal.timeout` + error-class pattern as
9
+ * `cli-github-client-port.ts`.
10
+ *
11
+ * AUTH: we send ONLY the `PRIVATE-TOKEN: <pat>` header — GitLab's recommended
12
+ * header for Personal/Project/Group Access Tokens (the kind `ghagga` uses). We do
13
+ * NOT also send `Authorization: Bearer`, which is for OAuth2 access tokens; mixing
14
+ * both is unnecessary and the PAT header is the canonical PAT auth path.
15
+ *
16
+ * HOST: the API base is derived from the resolved GitLab host
17
+ * (`https://<host>/api/v4`) so self-managed instances work, with a `GITLAB_HOST`
18
+ * env override for the rare case the API host differs from the git remote host.
19
+ *
20
+ * SCOPE (P4 — MR summary post-back): the four note members the adapter's
21
+ * `upsertSummaryComment` folds over (list/create/delete/update) plus
22
+ * `publishInline` (create). All four route through the NUMERIC project id (the
23
+ * canonical R-GITLAB identity). `resolveGitLabProjectId` is the pre-construction
24
+ * step that turns the mutable group/project path into that numeric id.
25
+ */
26
+ import { GitLabApiError } from './gitlab-api.js';
27
+ /**
28
+ * Per-request timeout for the CLI's GitLab fetch calls. Mirrors the GitHub CLI
29
+ * port's `AbortSignal.timeout(10_000)` so a slow/hung GitLab can never hang a CI
30
+ * job indefinitely.
31
+ */
32
+ const REQUEST_TIMEOUT_MS = 10_000;
33
+ /**
34
+ * Resolve the GitLab API base (`https://<host>/api/v4`) for a remote host.
35
+ *
36
+ * Precedence:
37
+ * 1. `GITLAB_API_BASE` — a FULL API base override. Required for a self-managed
38
+ * GitLab served under a SUBPATH (e.g. `https://example.com/gitlab`), where a
39
+ * host-only value cannot express the `/gitlab` prefix. Used verbatim (only a
40
+ * trailing slash is trimmed); supply the complete `.../api/v4` URL.
41
+ * 2. `GITLAB_HOST` — a host-only override (for when the API host differs from
42
+ * the git remote host, e.g. a reverse proxy on a different hostname).
43
+ * 3. the host parsed from the git remote.
44
+ * For (2)/(3) a bare host is expected; a stray scheme/trailing slash is tolerated.
45
+ */
46
+ export function resolveGitLabApiBase(host, env = process.env) {
47
+ const apiBaseOverride = env.GITLAB_API_BASE?.trim();
48
+ if (apiBaseOverride) {
49
+ return apiBaseOverride.replace(/\/+$/, '');
50
+ }
51
+ const hostOverride = env.GITLAB_HOST?.trim();
52
+ const chosen = hostOverride || host;
53
+ // Tolerate an override that includes a scheme and/or trailing slash.
54
+ const bare = chosen.replace(/^https?:\/\//i, '').replace(/\/+$/, '');
55
+ return `https://${bare}/api/v4`;
56
+ }
57
+ /** Auth headers — PRIVATE-TOKEN only (GitLab's recommended header for PATs). */
58
+ function apiHeaders(token) {
59
+ return {
60
+ 'PRIVATE-TOKEN': token,
61
+ 'Content-Type': 'application/json',
62
+ };
63
+ }
64
+ async function failOn(res, action) {
65
+ const body = await res.text();
66
+ throw new GitLabApiError(`GitLab API error ${action}: ${res.status} ${res.statusText}`, res.status, body);
67
+ }
68
+ /** Encode a path segment that itself contains slashes (group/subgroup/project). */
69
+ function encodeProjectPath(path) {
70
+ return encodeURIComponent(path);
71
+ }
72
+ /**
73
+ * Resolve the GitLab NUMERIC project id from a group/project path (R-GITLAB).
74
+ *
75
+ * `GET /projects/:url-encoded-path` → `.id`. The path is mutable, so the numeric
76
+ * id is the canonical `RepoRef.nativeId`. This runs ONCE before constructing the
77
+ * adapter; it is NOT part of the {@link GitLabClientPort} (the adapter only ever
78
+ * speaks numeric ids).
79
+ *
80
+ * @param apiBase the resolved API base (`https://<host>/api/v4`) — see
81
+ * {@link resolveGitLabApiBase}. Defaults to gitlab.com for back-compat callers.
82
+ * @returns the numeric project id as a STRING (RepoRef.nativeId is a string).
83
+ */
84
+ export async function resolveGitLabProjectId(path, token, apiBase = 'https://gitlab.com/api/v4') {
85
+ const url = `${apiBase}/projects/${encodeProjectPath(path)}`;
86
+ const res = await fetch(url, {
87
+ headers: apiHeaders(token),
88
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
89
+ });
90
+ if (!res.ok)
91
+ await failOn(res, 'resolving project id');
92
+ const data = (await res.json());
93
+ if (typeof data.id !== 'number') {
94
+ throw new GitLabApiError(`GitLab API error resolving project id: response had no numeric "id" for path "${path}"`, res.status, JSON.stringify(data));
95
+ }
96
+ return String(data.id);
97
+ }
98
+ /**
99
+ * Build a {@link GitLabClientPort} backed by native fetch for the CLI `--mr`
100
+ * post-back. All members route through the numeric project id.
101
+ *
102
+ * @param apiBase the resolved API base (`https://<host>/api/v4`) — see
103
+ * {@link resolveGitLabApiBase}. Defaults to gitlab.com for back-compat callers.
104
+ */
105
+ export function createCliGitLabClientPort(apiBase = 'https://gitlab.com/api/v4') {
106
+ return {
107
+ async listMrNotes(projectId, mrIid, token) {
108
+ // Paginate until exhausted (a page with < 100 items is the last page),
109
+ // like the GitHub port. MAX_PAGES is a safety upper bound (50 × 100 = 5000
110
+ // notes) so a pathological MR can't loop forever; if hit we log rather
111
+ // than silently truncate, since a stale marker beyond the bound would
112
+ // yield a DUPLICATE note. (Was 5 pages / 500 items — backlog #6.)
113
+ const baseUrl = `${apiBase}/projects/${projectId}/merge_requests/${mrIid}/notes`;
114
+ const MAX_PAGES = 50;
115
+ const out = [];
116
+ for (let page = 1; page <= MAX_PAGES; page++) {
117
+ const url = `${baseUrl}?per_page=100&page=${page}&sort=asc&order_by=created_at`;
118
+ const res = await fetch(url, {
119
+ headers: apiHeaders(token),
120
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
121
+ });
122
+ if (!res.ok)
123
+ await failOn(res, 'listing MR notes');
124
+ const notes = (await res.json());
125
+ for (const note of notes) {
126
+ out.push({ id: note.id, body: note.body });
127
+ }
128
+ if (notes.length < 100)
129
+ break;
130
+ if (page === MAX_PAGES) {
131
+ console.warn(`[ghagga] listMrNotes hit MAX_PAGES (${MAX_PAGES}) for project ${projectId} MR !${mrIid}; note listing may be truncated`);
132
+ }
133
+ }
134
+ return out;
135
+ },
136
+ async createMrNote(projectId, mrIid, body, token) {
137
+ const url = `${apiBase}/projects/${projectId}/merge_requests/${mrIid}/notes`;
138
+ const res = await fetch(url, {
139
+ method: 'POST',
140
+ headers: apiHeaders(token),
141
+ body: JSON.stringify({ body }),
142
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
143
+ });
144
+ if (!res.ok)
145
+ await failOn(res, 'creating MR note');
146
+ const data = (await res.json());
147
+ return { id: data.id };
148
+ },
149
+ async deleteMrNote(projectId, mrIid, noteId, token) {
150
+ const url = `${apiBase}/projects/${projectId}/merge_requests/${mrIid}/notes/${noteId}`;
151
+ const res = await fetch(url, {
152
+ method: 'DELETE',
153
+ headers: apiHeaders(token),
154
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
155
+ });
156
+ // The adapter's upsert tolerates delete failures (best-effort), but we
157
+ // still surface a non-2xx (except 404) so `deleted[]` only reflects
158
+ // genuinely-removed ids.
159
+ if (!res.ok && res.status !== 404)
160
+ await failOn(res, 'deleting MR note');
161
+ },
162
+ async createMrDiscussion(projectId, mrIid, body, position, token) {
163
+ // True diff-anchored discussion: POST /merge_requests/:iid/discussions with
164
+ // position[position_type]=text + the three SHAs + old/new path + line(s).
165
+ const url = `${apiBase}/projects/${projectId}/merge_requests/${mrIid}/discussions`;
166
+ const payload = {
167
+ body,
168
+ position: {
169
+ position_type: 'text',
170
+ base_sha: position.baseSha,
171
+ head_sha: position.headSha,
172
+ start_sha: position.startSha,
173
+ old_path: position.oldPath,
174
+ new_path: position.newPath,
175
+ ...(position.oldLine != null ? { old_line: position.oldLine } : {}),
176
+ ...(position.newLine != null ? { new_line: position.newLine } : {}),
177
+ },
178
+ };
179
+ const res = await fetch(url, {
180
+ method: 'POST',
181
+ headers: apiHeaders(token),
182
+ body: JSON.stringify(payload),
183
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
184
+ });
185
+ if (!res.ok)
186
+ await failOn(res, 'creating MR discussion');
187
+ // The discussion response nests its notes; box the FIRST note's id so the
188
+ // result matches the createMrNote contract ({ id: number }).
189
+ const data = (await res.json());
190
+ const firstNoteId = data.notes?.[0]?.id;
191
+ if (typeof firstNoteId !== 'number') {
192
+ throw new GitLabApiError('GitLab API error creating MR discussion: response had no numeric notes[0].id', res.status, JSON.stringify(data));
193
+ }
194
+ return { id: firstNoteId };
195
+ },
196
+ async updateMrNote(projectId, mrIid, noteId, body, token) {
197
+ const url = `${apiBase}/projects/${projectId}/merge_requests/${mrIid}/notes/${noteId}`;
198
+ const res = await fetch(url, {
199
+ method: 'PUT',
200
+ headers: apiHeaders(token),
201
+ body: JSON.stringify({ body }),
202
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
203
+ });
204
+ if (!res.ok)
205
+ await failOn(res, 'updating MR note');
206
+ },
207
+ };
208
+ }
209
+ //# sourceMappingURL=cli-gitlab-client-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-gitlab-client-port.js","sourceRoot":"","sources":["../../src/lib/cli-gitlab-client-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * R-GITLAB / R-LEAK-PUBLISH: the `--mr` post-back routes through the REAL
3
+ * GitLabForgeAdapter folding over the CLI's own GitLabClientPort. We mock ONLY
4
+ * `fetch` (the network seam), so this exercises the genuine adapter
5
+ * find→delete→repost idempotency AND the genuine CLI port REST calls — a
6
+ * faithful simulation against a mocked GitLab.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=cli-gitlab-client-port.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-gitlab-client-port.test.d.ts","sourceRoot":"","sources":["../../src/lib/cli-gitlab-client-port.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}