plugin-git-manager 1.0.10 → 1.0.12

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.
@@ -11,8 +11,8 @@ module.exports = {
11
11
  "react": "18.2.0",
12
12
  "antd": "5.24.2",
13
13
  "@ant-design/icons": "5.6.1",
14
- "@nocobase/client": "2.0.46",
15
- "@nocobase/server": "2.0.46",
16
- "@nocobase/actions": "2.0.46",
17
- "@nocobase/database": "2.0.46"
14
+ "@nocobase/client": "2.0.47",
15
+ "@nocobase/server": "2.0.47",
16
+ "@nocobase/actions": "2.0.47",
17
+ "@nocobase/database": "2.0.47"
18
18
  };
@@ -53,7 +53,8 @@ module.exports = __toCommonJS(git_actions_exports);
53
53
  var import_simple_git = __toESM(require("simple-git"));
54
54
  var path = __toESM(require("path"));
55
55
  var fs = __toESM(require("fs"));
56
- const REF_PATTERN = /^[a-zA-Z0-9._\-\/]+$/;
56
+ var import_redact = require("../utils/redact");
57
+ const REF_PATTERN = /^(?!-)[a-zA-Z0-9._\-\/]+$/;
57
58
  const repoLocks = /* @__PURE__ */ new Map();
58
59
  function acquireLock(key) {
59
60
  const prev = repoLocks.get(key) || Promise.resolve();
@@ -89,14 +90,16 @@ function validateRepoUrl(repoUrl) {
89
90
  throw new Error("Only HTTPS repository URLs are allowed");
90
91
  }
91
92
  }
92
- async function withAuth(git, repoUrl, pat, fn, username) {
93
- const lockKey = repoUrl;
93
+ async function withAuth(git, localPath, repoUrl, pat, fn, username) {
94
+ const lockKey = localPath;
94
95
  const lock = acquireLock(lockKey);
95
96
  await lock.promise;
96
97
  const authUrl = getAuthUrl(repoUrl, pat, username);
97
98
  await git.remote(["set-url", "origin", authUrl]);
98
99
  try {
99
100
  return await fn();
101
+ } catch (err) {
102
+ throw (0, import_redact.redactError)(err);
100
103
  } finally {
101
104
  try {
102
105
  await git.remote(["set-url", "origin", repoUrl]);
@@ -105,8 +108,8 @@ async function withAuth(git, repoUrl, pat, fn, username) {
105
108
  await git.remote(["set-url", "origin", repoUrl]);
106
109
  } catch {
107
110
  console.error(
108
- `[plugin-git-manager] CRITICAL: failed to remove PAT from remote URL for ${repoUrl}. Manual cleanup of .git/config may be required.`,
109
- cleanupErr
111
+ `[plugin-git-manager] CRITICAL: failed to remove PAT from remote URL for ${(0, import_redact.redactPat)(repoUrl)}. Manual cleanup of .git/config may be required.`,
112
+ (0, import_redact.redactError)(cleanupErr)
110
113
  );
111
114
  }
112
115
  }
@@ -114,9 +117,9 @@ async function withAuth(git, repoUrl, pat, fn, username) {
114
117
  }
115
118
  }
116
119
  function getAuthUrl(repoUrl, pat, username) {
117
- const url = new URL(repoUrl);
118
- url.username = username || "oauth2";
119
- url.password = pat;
120
+ const url = new URL(repoUrl.trim());
121
+ url.username = (username || "oauth2").trim();
122
+ url.password = pat.trim();
120
123
  return url.toString();
121
124
  }
122
125
  function getGit(localPath) {
@@ -144,10 +147,12 @@ function validateLocalPath(localPath) {
144
147
  async function clone(ctx, next) {
145
148
  const repo = await getRepo(ctx);
146
149
  const localPath = validateLocalPath(repo.get("localPath"));
147
- const repoUrl = repo.get("repoUrl");
148
- const pat = repo.get("pat");
149
- const username = repo.get("username");
150
+ const repoUrl = (repo.get("repoUrl") || "").trim();
151
+ const pat = (repo.get("pat") || "").trim();
152
+ const username = (repo.get("username") || "").trim();
153
+ const defaultBranch = (repo.get("defaultBranch") || "main").trim() || "main";
150
154
  validateRepoUrl(repoUrl);
155
+ validateBranch(defaultBranch);
151
156
  if (fs.existsSync(localPath)) {
152
157
  ctx.throw(400, "Directory already exists. Remove it before cloning again.");
153
158
  }
@@ -156,7 +161,7 @@ async function clone(ctx, next) {
156
161
  }
157
162
  const authUrl = getAuthUrl(repoUrl, pat, username);
158
163
  try {
159
- await (0, import_simple_git.default)().clone(authUrl, localPath, ["--branch", repo.get("defaultBranch") || "main"]);
164
+ await (0, import_simple_git.default)().clone(authUrl, localPath, ["--branch", defaultBranch]);
160
165
  await (0, import_simple_git.default)(localPath).remote(["set-url", "origin", repoUrl]);
161
166
  await ctx.db.getRepository("gitRepositories").update({
162
167
  filterByTk: repo.get("id"),
@@ -168,40 +173,40 @@ async function clone(ctx, next) {
168
173
  filterByTk: repo.get("id"),
169
174
  values: { status: "error" }
170
175
  });
171
- throw err;
176
+ throw (0, import_redact.redactError)(err);
172
177
  }
173
178
  await next();
174
179
  }
175
180
  async function pull(ctx, next) {
176
181
  const repo = await getRepo(ctx);
177
182
  const localPath = validateLocalPath(repo.get("localPath"));
178
- const pat = repo.get("pat");
179
- const repoUrl = repo.get("repoUrl");
180
- const username = repo.get("username");
183
+ const pat = (repo.get("pat") || "").trim();
184
+ const repoUrl = (repo.get("repoUrl") || "").trim();
185
+ const username = (repo.get("username") || "").trim();
181
186
  const git = getGit(localPath);
182
- const result = await withAuth(git, repoUrl, pat, () => git.pull(), username);
187
+ const result = await withAuth(git, localPath, repoUrl, pat, () => git.pull(), username);
183
188
  ctx.body = { success: true, data: result };
184
189
  await next();
185
190
  }
186
191
  async function push(ctx, next) {
187
192
  const repo = await getRepo(ctx);
188
193
  const localPath = validateLocalPath(repo.get("localPath"));
189
- const pat = repo.get("pat");
190
- const repoUrl = repo.get("repoUrl");
191
- const username = repo.get("username");
194
+ const pat = (repo.get("pat") || "").trim();
195
+ const repoUrl = (repo.get("repoUrl") || "").trim();
196
+ const username = (repo.get("username") || "").trim();
192
197
  const git = getGit(localPath);
193
- const result = await withAuth(git, repoUrl, pat, () => git.push(), username);
198
+ const result = await withAuth(git, localPath, repoUrl, pat, () => git.push(), username);
194
199
  ctx.body = { success: true, data: result };
195
200
  await next();
196
201
  }
197
202
  async function fetch(ctx, next) {
198
203
  const repo = await getRepo(ctx);
199
204
  const localPath = validateLocalPath(repo.get("localPath"));
200
- const pat = repo.get("pat");
201
- const repoUrl = repo.get("repoUrl");
202
- const username = repo.get("username");
205
+ const pat = (repo.get("pat") || "").trim();
206
+ const repoUrl = (repo.get("repoUrl") || "").trim();
207
+ const username = (repo.get("username") || "").trim();
203
208
  const git = getGit(localPath);
204
- const result = await withAuth(git, repoUrl, pat, () => git.fetch(), username);
209
+ const result = await withAuth(git, localPath, repoUrl, pat, () => git.fetch(), username);
205
210
  ctx.body = { success: true, data: result };
206
211
  await next();
207
212
  }
@@ -66,8 +66,8 @@ async function getRepoApiContext(ctx) {
66
66
  if (!repo) {
67
67
  ctx.throw(404, "Repository not found");
68
68
  }
69
- const pat = repo.get("pat");
70
- const repoUrl = repo.get("repoUrl");
69
+ const pat = (repo.get("pat") || "").trim();
70
+ const repoUrl = (repo.get("repoUrl") || "").trim();
71
71
  const isGitHub = repoUrl.includes("github.com");
72
72
  const { apiBase, encodedProject, projectPath } = (0, import_gitlab_url.parseGitLabProject)(repoUrl);
73
73
  return { repo, pat, apiBase, encodedProject, projectPath, isGitHub };
@@ -128,8 +128,14 @@ async function mergeRequests(ctx, next) {
128
128
  direction: sort
129
129
  });
130
130
  let pullRequests = result.data || [];
131
+ let mergedFilterApplied = false;
131
132
  if (state === "merged") {
132
133
  pullRequests = pullRequests.filter((pr) => pr.merged_at);
134
+ mergedFilterApplied = true;
135
+ }
136
+ if (mergedFilterApplied) {
137
+ result.totalPages = null;
138
+ result.total = null;
133
139
  }
134
140
  items = pullRequests.map((pr) => {
135
141
  var _a2, _b;
@@ -147,18 +153,18 @@ async function mergeRequests(ctx, next) {
147
153
  labels: (pr.labels || []).map((l) => l.name),
148
154
  draft: pr.draft || false,
149
155
  mergedBy: null,
150
- // Not returned in list API
156
+ // Not returned by the PR list endpoint
151
157
  mergedAt: pr.merged_at,
152
158
  createdAt: pr.created_at,
153
159
  updatedAt: pr.updated_at,
154
- userNotesCount: 0,
155
- // Not returned in pull list API
160
+ userNotesCount: typeof pr.comments === "number" ? pr.comments : 0,
156
161
  upvotes: 0,
157
162
  downvotes: 0,
158
163
  webUrl: pr.html_url,
159
- hasConflicts: false,
160
- // Not guaranteed in list
161
- changesCount: 0
164
+ // GitHub's PR list does not include mergeability info — surface as
165
+ // `null` (unknown) so the UI doesn't render a misleading "no conflicts".
166
+ hasConflicts: null,
167
+ changesCount: typeof pr.changed_files === "number" ? pr.changed_files : null
162
168
  };
163
169
  });
164
170
  } else {
@@ -252,8 +258,15 @@ async function mergeRequestDetail(ctx, next) {
252
258
  // Not always readily available on GitHub without extra call
253
259
  closedAt: pr.closed_at,
254
260
  webUrl: pr.html_url,
255
- hasConflicts: pr.mergeable_state === "dirty",
256
- diffStats: { additions: pr.additions },
261
+ // `mergeable_state === 'unknown'` when GitHub is still computing — surface
262
+ // `null` instead of `false` so the UI can distinguish "no conflicts" from
263
+ // "not yet known".
264
+ hasConflicts: pr.mergeable_state === "dirty" ? true : pr.mergeable_state === "unknown" || pr.mergeable === null ? null : false,
265
+ diffStats: {
266
+ additions: typeof pr.additions === "number" ? pr.additions : null,
267
+ deletions: typeof pr.deletions === "number" ? pr.deletions : null,
268
+ changedFiles: typeof pr.changed_files === "number" ? pr.changed_files : null
269
+ },
257
270
  changes
258
271
  };
259
272
  } else {
@@ -33,4 +33,5 @@ export declare function reviewApprovePost(ctx: Context, next: () => Promise<void
33
33
  export declare function reviewReject(ctx: Context, next: () => Promise<void>): Promise<void>;
34
34
  export declare function branchMatches(flow: any, branch: string): boolean;
35
35
  export declare function pickFlowMatchingBranch(flows: any[], branch?: string): any | null;
36
+ export declare function recoverStuckReviews(app: Application): Promise<number>;
36
37
  export {};
@@ -28,6 +28,7 @@ var review_exports = {};
28
28
  __export(review_exports, {
29
29
  branchMatches: () => branchMatches,
30
30
  pickFlowMatchingBranch: () => pickFlowMatchingBranch,
31
+ recoverStuckReviews: () => recoverStuckReviews,
31
32
  reviewApprovePost: () => reviewApprovePost,
32
33
  reviewReject: () => reviewReject,
33
34
  triggerReview: () => triggerReview,
@@ -35,6 +36,17 @@ __export(review_exports, {
35
36
  });
36
37
  module.exports = __toCommonJS(review_exports);
37
38
  var import_gitlab_url = require("../utils/gitlab-url");
39
+ var import_redact = require("../utils/redact");
40
+ function targetKey(args) {
41
+ if (args.targetType === "mr") return `${args.repositoryId}:mr:${args.mrIid}`;
42
+ if (args.targetType === "commit") return `${args.repositoryId}:commit:${args.commitSha}`;
43
+ return `${args.repositoryId}:branch:${args.branch}`;
44
+ }
45
+ const TRIGGER_LOCK_TTL_MS = 3e4;
46
+ async function withTriggerLock(app, key, fn) {
47
+ const lockKey = `git-review:trigger:${key}`;
48
+ return app.lockManager.runExclusive(lockKey, fn, TRIGGER_LOCK_TTL_MS);
49
+ }
38
50
  async function triggerReview(ctx, next) {
39
51
  var _a, _b, _c;
40
52
  const params = { ...ctx.action.params, ...(_a = ctx.action.params) == null ? void 0 : _a.values, ...ctx.request.body || {} };
@@ -73,6 +85,9 @@ async function triggerReview(ctx, next) {
73
85
  await next();
74
86
  }
75
87
  async function triggerReviewInternal(app, args) {
88
+ return withTriggerLock(app, targetKey(args), () => triggerReviewInternalLocked(app, args));
89
+ }
90
+ async function triggerReviewInternalLocked(app, args) {
76
91
  const db = app.db;
77
92
  const flowsRepo = db.getRepository("gitReviewFlows");
78
93
  let flow = null;
@@ -123,12 +138,19 @@ async function triggerReviewInternal(app, args) {
123
138
  latestSha: headSha || existingLatestSha || null,
124
139
  triggeredBy: args.triggeredBy || "manual",
125
140
  status: "pending",
141
+ // Stamp startedAt synchronously so `recoverStuckReviews` can sweep rows
142
+ // that get stuck in `pending` (process died before runReview ran).
143
+ // runReview's own update will refresh this on actual start.
144
+ startedAt: /* @__PURE__ */ new Date(),
145
+ finishedAt: null,
146
+ durationMs: null,
126
147
  postStatus: flow.get("postMode") === "disabled" ? "skipped" : "pending_approval",
127
148
  error: null
128
149
  };
129
150
  let reviewId;
130
151
  if (existing) {
131
- if (existing.get("status") === "running") {
152
+ const st = existing.get("status");
153
+ if (st === "running" || st === "pending") {
132
154
  return existing.get("id");
133
155
  }
134
156
  await reviewsRepo.update({
@@ -251,6 +273,7 @@ async function runReview(app, args) {
251
273
  db,
252
274
  state: { currentUser: args.userId ? { id: args.userId } : null },
253
275
  req: { headers: { "x-timezone": "UTC", "x-locale": "en-US" } },
276
+ log: app.logger || console,
254
277
  get(name) {
255
278
  return this.req.headers[String(name).toLowerCase()];
256
279
  },
@@ -356,11 +379,12 @@ async function runReview(app, args) {
356
379
  }
357
380
  } catch (err) {
358
381
  const finishedAt = /* @__PURE__ */ new Date();
382
+ const safeMessage = (0, import_redact.redactPat)((err == null ? void 0 : err.message) || String(err));
359
383
  await reviewsRepo.update({
360
384
  filterByTk: args.reviewId,
361
385
  values: {
362
386
  status: "failed",
363
- error: (err == null ? void 0 : err.message) || String(err),
387
+ error: safeMessage,
364
388
  finishedAt,
365
389
  durationMs: finishedAt.getTime() - startedAt.getTime()
366
390
  }
@@ -409,18 +433,39 @@ function buildReviewPrompt(args) {
409
433
  return lines.join("\n");
410
434
  }
411
435
  function extractLastAiMessageContent(result) {
412
- var _a;
413
- if (!(result == null ? void 0 : result.messages) || !Array.isArray(result.messages)) return "";
414
- for (let i = result.messages.length - 1; i >= 0; i--) {
415
- const msg = result.messages[i];
416
- if (!msg) continue;
417
- const className = (_a = msg == null ? void 0 : msg.constructor) == null ? void 0 : _a.name;
418
- if (className === "HumanMessage" || className === "ToolMessage") continue;
436
+ const messages = result == null ? void 0 : result.messages;
437
+ if (!Array.isArray(messages)) return "";
438
+ const isAiMessage = (msg) => {
439
+ var _a;
440
+ if (!msg) return false;
441
+ if (typeof msg._getType === "function") {
442
+ try {
443
+ return msg._getType() === "ai";
444
+ } catch {
445
+ }
446
+ }
447
+ if (typeof msg.role === "string") {
448
+ const r = msg.role.toLowerCase();
449
+ return r === "assistant" || r === "ai";
450
+ }
451
+ if (typeof msg.type === "string" && msg.type.toLowerCase() === "ai") return true;
452
+ const name = (_a = msg == null ? void 0 : msg.constructor) == null ? void 0 : _a.name;
453
+ if (name === "AIMessage" || name === "AIMessageChunk") return true;
454
+ return false;
455
+ };
456
+ const getContent = (msg) => {
419
457
  if (typeof msg.content === "string") return msg.content;
420
458
  if (Array.isArray(msg.content)) {
421
- const textBlock = msg.content.find((c) => c.type === "text");
422
- if (textBlock == null ? void 0 : textBlock.text) return textBlock.text;
459
+ const textBlock = msg.content.find((c) => (c == null ? void 0 : c.type) === "text");
460
+ if (typeof (textBlock == null ? void 0 : textBlock.text) === "string") return textBlock.text;
423
461
  }
462
+ return "";
463
+ };
464
+ for (let i = messages.length - 1; i >= 0; i--) {
465
+ const msg = messages[i];
466
+ if (!isAiMessage(msg)) continue;
467
+ const content = getContent(msg);
468
+ if (content) return content;
424
469
  }
425
470
  return "";
426
471
  }
@@ -495,14 +540,26 @@ async function postNoteToGitLab(repo, mrIid, body) {
495
540
  }
496
541
  }
497
542
  const MAX_BRANCH_FILTER_LENGTH = 200;
543
+ const loggedBadFilters = /* @__PURE__ */ new Set();
544
+ function warnInvalidBranchFilter(filter, reason) {
545
+ if (loggedBadFilters.has(filter)) return;
546
+ loggedBadFilters.add(filter);
547
+ console.warn(
548
+ `[plugin-git-manager] branchFilter rejected (${reason}): ${JSON.stringify(filter)}. Flow will not match any branch.`
549
+ );
550
+ }
498
551
  function branchMatches(flow, branch) {
499
552
  const filter = flow.get("branchFilter");
500
553
  if (!filter) return true;
501
- if (filter.length > MAX_BRANCH_FILTER_LENGTH) return true;
554
+ if (filter.length > MAX_BRANCH_FILTER_LENGTH) {
555
+ warnInvalidBranchFilter(filter, `too long (>${MAX_BRANCH_FILTER_LENGTH} chars)`);
556
+ return false;
557
+ }
502
558
  try {
503
559
  return new RegExp(filter).test(branch);
504
- } catch {
505
- return true;
560
+ } catch (err) {
561
+ warnInvalidBranchFilter(filter, `invalid regex: ${(err == null ? void 0 : err.message) || err}`);
562
+ return false;
506
563
  }
507
564
  }
508
565
  function pickFlowMatchingBranch(flows, branch) {
@@ -518,10 +575,45 @@ function throwHttp(status, message) {
518
575
  err.status = status;
519
576
  throw err;
520
577
  }
578
+ const STUCK_REVIEW_CUTOFF_MS = 10 * 60 * 1e3;
579
+ async function recoverStuckReviews(app) {
580
+ var _a, _b, _c, _d;
581
+ try {
582
+ const reviewsRepo = app.db.getRepository("gitCodeReviews");
583
+ const cutoff = new Date(Date.now() - STUCK_REVIEW_CUTOFF_MS);
584
+ const stuck = await reviewsRepo.find({
585
+ filter: {
586
+ status: { $in: ["running", "pending"] },
587
+ startedAt: { $lt: cutoff }
588
+ }
589
+ });
590
+ if (!(stuck == null ? void 0 : stuck.length)) return 0;
591
+ const finishedAt = /* @__PURE__ */ new Date();
592
+ for (const review of stuck) {
593
+ const startedAt = review.get("startedAt");
594
+ const durationMs = startedAt ? finishedAt.getTime() - new Date(startedAt).getTime() : null;
595
+ await reviewsRepo.update({
596
+ filterByTk: review.get("id"),
597
+ values: {
598
+ status: "failed",
599
+ error: "Review interrupted by application restart",
600
+ finishedAt,
601
+ durationMs
602
+ }
603
+ });
604
+ }
605
+ (_b = (_a = app.log) == null ? void 0 : _a.info) == null ? void 0 : _b.call(_a, `plugin-git-manager: marked ${stuck.length} stuck review(s) as failed after restart`);
606
+ return stuck.length;
607
+ } catch (err) {
608
+ (_d = (_c = app.log) == null ? void 0 : _c.error) == null ? void 0 : _d.call(_c, `plugin-git-manager: recoverStuckReviews failed: ${err == null ? void 0 : err.message}`);
609
+ return 0;
610
+ }
611
+ }
521
612
  // Annotate the CommonJS export names for ESM import in node:
522
613
  0 && (module.exports = {
523
614
  branchMatches,
524
615
  pickFlowMatchingBranch,
616
+ recoverStuckReviews,
525
617
  reviewApprovePost,
526
618
  reviewReject,
527
619
  triggerReview,
@@ -49,7 +49,31 @@ function registerGitReviewAiTools(app) {
49
49
  (_c = (_b = app.log) == null ? void 0 : _b.warn) == null ? void 0 : _c.call(_b, "plugin-git-manager: AIManager.toolsManager not available; skipping AI tool registration");
50
50
  return;
51
51
  }
52
- const runResourceAction = async (ctx, handler, params) => {
52
+ const enforceAcl = (ctx, resource, action) => {
53
+ var _a2, _b2, _c2, _d2;
54
+ const user = (_a2 = ctx == null ? void 0 : ctx.state) == null ? void 0 : _a2.currentUser;
55
+ if (!(user == null ? void 0 : user.id)) {
56
+ const err = new Error("AI tool requires an authenticated user context");
57
+ err.status = 401;
58
+ throw err;
59
+ }
60
+ const acl = (_b2 = ctx.app) == null ? void 0 : _b2.acl;
61
+ if (!(acl == null ? void 0 : acl.can)) return;
62
+ const role = ((_c2 = ctx == null ? void 0 : ctx.state) == null ? void 0 : _c2.currentRole) || Array.isArray(user.roles) && ((_d2 = user.roles[0]) == null ? void 0 : _d2.name) || null;
63
+ if (!role) {
64
+ const err = new Error("AI tool requires a resolvable role on the current user");
65
+ err.status = 403;
66
+ throw err;
67
+ }
68
+ const allowed = acl.can({ role, resource, action });
69
+ if (!allowed) {
70
+ const err = new Error(`Permission denied: ${resource}:${action}`);
71
+ err.status = 403;
72
+ throw err;
73
+ }
74
+ };
75
+ const runResourceAction = async (ctx, handler, params, gate) => {
76
+ enforceAcl(ctx, gate.resource, gate.action);
53
77
  const synthCtx = {
54
78
  ...ctx,
55
79
  app: ctx.app,
@@ -80,7 +104,10 @@ function registerGitReviewAiTools(app) {
80
104
  })
81
105
  },
82
106
  invoke: async (ctx, args) => {
83
- const body = await runResourceAction(ctx, gitlabApi.mergeRequestDetail, args);
107
+ const body = await runResourceAction(ctx, gitlabApi.mergeRequestDetail, args, {
108
+ resource: "gitManager",
109
+ action: "mergeRequestDetail"
110
+ });
84
111
  return (body == null ? void 0 : body.data) ?? body;
85
112
  }
86
113
  },
@@ -102,7 +129,10 @@ function registerGitReviewAiTools(app) {
102
129
  })
103
130
  },
104
131
  invoke: async (ctx, args) => {
105
- const body = await runResourceAction(ctx, gitlabApi.mergeRequests, args);
132
+ const body = await runResourceAction(ctx, gitlabApi.mergeRequests, args, {
133
+ resource: "gitManager",
134
+ action: "mergeRequests"
135
+ });
106
136
  return (body == null ? void 0 : body.data) ?? body;
107
137
  }
108
138
  },
@@ -121,7 +151,10 @@ function registerGitReviewAiTools(app) {
121
151
  })
122
152
  },
123
153
  invoke: async (ctx, args) => {
124
- const body = await runResourceAction(ctx, gitlabApi.mergeRequestNotes, args);
154
+ const body = await runResourceAction(ctx, gitlabApi.mergeRequestNotes, args, {
155
+ resource: "gitManager",
156
+ action: "mergeRequestNotes"
157
+ });
125
158
  return (body == null ? void 0 : body.data) ?? body;
126
159
  }
127
160
  },
@@ -140,7 +173,10 @@ function registerGitReviewAiTools(app) {
140
173
  })
141
174
  },
142
175
  invoke: async (ctx, args) => {
143
- const body = await runResourceAction(ctx, gitActions.commitDetail, args);
176
+ const body = await runResourceAction(ctx, gitActions.commitDetail, args, {
177
+ resource: "gitManager",
178
+ action: "commitDetail"
179
+ });
144
180
  return (body == null ? void 0 : body.data) ?? body;
145
181
  }
146
182
  },
@@ -161,7 +197,10 @@ function registerGitReviewAiTools(app) {
161
197
  })
162
198
  },
163
199
  invoke: async (ctx, args) => {
164
- const body = await runResourceAction(ctx, gitActions.diff, args);
200
+ const body = await runResourceAction(ctx, gitActions.diff, args, {
201
+ resource: "gitManager",
202
+ action: "diff"
203
+ });
165
204
  return (body == null ? void 0 : body.data) ?? body;
166
205
  }
167
206
  },
@@ -181,7 +220,10 @@ function registerGitReviewAiTools(app) {
181
220
  })
182
221
  },
183
222
  invoke: async (ctx, args) => {
184
- const body = await runResourceAction(ctx, gitActions.fileContent, args);
223
+ const body = await runResourceAction(ctx, gitActions.fileContent, args, {
224
+ resource: "gitManager",
225
+ action: "fileContent"
226
+ });
185
227
  return (body == null ? void 0 : body.data) ?? body;
186
228
  }
187
229
  },
@@ -197,7 +239,10 @@ function registerGitReviewAiTools(app) {
197
239
  schema: import_zod.z.object({ repositoryId: import_zod.z.number() })
198
240
  },
199
241
  invoke: async (ctx, args) => {
200
- const body = await runResourceAction(ctx, gitActions.branches, args);
242
+ const body = await runResourceAction(ctx, gitActions.branches, args, {
243
+ resource: "gitManager",
244
+ action: "branches"
245
+ });
201
246
  return (body == null ? void 0 : body.data) ?? body;
202
247
  }
203
248
  },
@@ -217,7 +262,10 @@ function registerGitReviewAiTools(app) {
217
262
  })
218
263
  },
219
264
  invoke: async (ctx, args) => {
220
- const body = await runResourceAction(ctx, gitActions.log, args);
265
+ const body = await runResourceAction(ctx, gitActions.log, args, {
266
+ resource: "gitManager",
267
+ action: "log"
268
+ });
221
269
  return (body == null ? void 0 : body.data) ?? body;
222
270
  }
223
271
  }
@@ -62,7 +62,7 @@ var gitRepositories_default = (0, import_database.defineCollection)({
62
62
  uiSchema: { title: "Local Path", type: "string", "x-component": "Input" }
63
63
  },
64
64
  {
65
- type: "password",
65
+ type: "string",
66
66
  name: "pat",
67
67
  interface: "password",
68
68
  uiSchema: { title: "Personal Access Token", type: "string", "x-component": "Password" }
@@ -46,6 +46,7 @@ var gitActions = __toESM(require("./actions/git-actions"));
46
46
  var gitlabApi = __toESM(require("./actions/gitlab-api"));
47
47
  var reviewActions = __toESM(require("./actions/review"));
48
48
  var pollerActions = __toESM(require("./actions/poller"));
49
+ var import_review = require("./actions/review");
49
50
  var import_ai_tools = require("./ai-tools");
50
51
  var import_poller = require("./poller");
51
52
  class PluginGitManagerServer extends import_server.Plugin {
@@ -78,8 +79,26 @@ class PluginGitManagerServer extends import_server.Plugin {
78
79
  pollerStatus: pollerActions.pollerStatus
79
80
  }
80
81
  });
82
+ this.app.use(async (ctx, next) => {
83
+ if (ctx.logger && ctx.logger.warn) {
84
+ const originalWarn = ctx.logger.warn.bind(ctx.logger);
85
+ ctx.logger.warn = (message, ...args) => {
86
+ if (typeof message === "string" && message.includes("[Workflow") && message.includes("collection") && message.includes("not found")) {
87
+ return ctx.logger;
88
+ }
89
+ return originalWarn(message, ...args);
90
+ };
91
+ }
92
+ return next();
93
+ });
81
94
  (0, import_ai_tools.registerGitReviewAiTools)(this.app);
82
95
  this.app.on("afterStart", () => {
96
+ (0, import_review.recoverStuckReviews)(this.app).catch(
97
+ (err) => {
98
+ var _a, _b;
99
+ return (_b = (_a = this.app.log) == null ? void 0 : _a.error) == null ? void 0 : _b.call(_a, "plugin-git-manager: recoverStuckReviews error", err);
100
+ }
101
+ );
83
102
  (0, import_poller.startPoller)(this.app);
84
103
  });
85
104
  this.app.on("beforeStop", () => {