@fitlab-ai/agent-infra 0.9.13 → 0.9.14

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 (280) hide show
  1. package/bin/cli.ts +26 -9
  2. package/dist/bin/cli.js +173 -157
  3. package/dist/bin/internal-cli.js +42 -21
  4. package/dist/lib/agent-client.js +5 -0
  5. package/dist/lib/internal/agent-client.js +5 -2
  6. package/dist/lib/internal/cli-route-inventory.js +152 -0
  7. package/dist/lib/internal/codex-lifecycle.js +9 -6
  8. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  9. package/dist/lib/internal/git-workflow.js +10 -6
  10. package/dist/lib/internal/platform-checks.js +16 -4
  11. package/dist/lib/internal/platform-comment.js +16 -4
  12. package/dist/lib/internal/platform-context.js +4 -1
  13. package/dist/lib/internal/platform-issue.js +12 -4
  14. package/dist/lib/internal/platform-metadata.js +88 -0
  15. package/dist/lib/internal/platform-pr-review.js +16 -3
  16. package/dist/lib/internal/platform-pr.js +24 -10
  17. package/dist/lib/internal/platform-release-notes.js +6 -3
  18. package/dist/lib/internal/platform-security.js +93 -0
  19. package/dist/lib/internal/pr-review-grade.js +14 -3
  20. package/dist/lib/internal/release-workflow.js +19 -5
  21. package/dist/lib/internal/sandbox-control.js +22 -5
  22. package/dist/lib/internal/task-activity.js +13 -4
  23. package/dist/lib/internal/task-artifact.js +186 -27
  24. package/dist/lib/internal/task-context.js +4 -1
  25. package/dist/lib/internal/task-create.js +48 -15
  26. package/dist/lib/internal/task-delivery.js +4 -1
  27. package/dist/lib/internal/task-event.js +4 -1
  28. package/dist/lib/internal/task-finalization.js +4 -1
  29. package/dist/lib/internal/task-invalidation.js +4 -1
  30. package/dist/lib/internal/task-ledger.js +12 -1
  31. package/dist/lib/internal/task-lifecycle.js +4 -1
  32. package/dist/lib/internal/task-operation-registry.js +421 -0
  33. package/dist/lib/internal/task-orchestration.js +12 -0
  34. package/dist/lib/internal/task-override.js +10 -3
  35. package/dist/lib/internal/task-qualification.js +90 -0
  36. package/dist/lib/internal/task-review.js +4 -1
  37. package/dist/lib/internal/task-short-id.js +11 -1
  38. package/dist/lib/internal/task-snapshot.js +4 -1
  39. package/dist/lib/internal/task-validate.js +7 -2
  40. package/dist/lib/internal/task-verify.js +5 -1
  41. package/dist/lib/internal/task-warning.js +8 -1
  42. package/dist/lib/platform/comment-safety.js +226 -0
  43. package/dist/lib/platform/github-provider.js +233 -6
  44. package/dist/lib/platform/issue-comments.js +146 -34
  45. package/dist/lib/platform/pr-change-report.js +6 -12
  46. package/dist/lib/platform/pr-summary.js +28 -7
  47. package/dist/lib/platform/provider-contract.d.ts +67 -1
  48. package/dist/lib/platform/provider-contract.js +6 -2
  49. package/dist/lib/platform/provider-validation.js +76 -3
  50. package/dist/lib/platform/pull-requests.js +115 -170
  51. package/dist/lib/platform/repository-metadata.js +377 -0
  52. package/dist/lib/platform/security-alerts.js +155 -0
  53. package/dist/lib/platform/verification-sync.js +33 -26
  54. package/dist/lib/process-data/index.js +7 -0
  55. package/dist/lib/run/index.js +5 -0
  56. package/dist/lib/sandbox/commands/control-status.js +22 -0
  57. package/dist/lib/sandbox/commands/create.js +32 -6
  58. package/dist/lib/sandbox/commands/ls.js +5 -0
  59. package/dist/lib/sandbox/commands/rm.js +827 -120
  60. package/dist/lib/sandbox/commands/show.js +16 -0
  61. package/dist/lib/sandbox/control/client.js +83 -9
  62. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  63. package/dist/lib/sandbox/control/container-identity.js +28 -31
  64. package/dist/lib/sandbox/control/executor.js +149 -14
  65. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  66. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  67. package/dist/lib/sandbox/control/server.js +305 -35
  68. package/dist/lib/sandbox/control/state.js +5 -3
  69. package/dist/lib/sandbox/control/task-view.js +183 -0
  70. package/dist/lib/sandbox/engines/authority.js +289 -0
  71. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  72. package/dist/lib/sandbox/index.js +5 -0
  73. package/dist/lib/sandbox/recovery.js +21 -1
  74. package/dist/lib/sandbox/workspace-view.js +2 -1
  75. package/dist/lib/sandbox/worktree-safety.js +4 -1
  76. package/dist/lib/server/index.js +8 -0
  77. package/dist/lib/task/artifact-lifecycle.js +42 -13
  78. package/dist/lib/task/artifact-operations.js +520 -0
  79. package/dist/lib/task/artifact-repair-intent.js +59 -0
  80. package/dist/lib/task/artifact-schema.js +135 -0
  81. package/dist/lib/task/capabilities.js +72 -1
  82. package/dist/lib/task/control-authority.js +4 -0
  83. package/dist/lib/task/create-service.js +103 -4
  84. package/dist/lib/task/create.js +25 -0
  85. package/dist/lib/task/events.js +186 -67
  86. package/dist/lib/task/index.js +11 -3
  87. package/dist/lib/task/local-artifact-finalization.js +75 -150
  88. package/dist/lib/task/orchestration.js +192 -52
  89. package/dist/lib/task/qualification-audit.js +490 -0
  90. package/dist/lib/task/qualification-confirmation.js +109 -0
  91. package/dist/lib/task/qualification-intents.js +148 -0
  92. package/dist/lib/task/review-artifacts.js +22 -23
  93. package/dist/lib/task/review-finalization.js +121 -4
  94. package/dist/lib/task/sections.js +6 -4
  95. package/dist/lib/task/verification-config.js +11 -0
  96. package/dist/lib/task/verification-engine.js +54 -4
  97. package/dist/lifecycle-build-manifest.json +3 -3
  98. package/lib/agent-client.ts +9 -0
  99. package/lib/internal/agent-client.ts +4 -2
  100. package/lib/internal/cli-route-inventory.ts +140 -0
  101. package/lib/internal/codex-lifecycle.ts +8 -6
  102. package/lib/internal/codex-sandbox-controller.ts +7 -2
  103. package/lib/internal/git-workflow.ts +9 -6
  104. package/lib/internal/platform-checks.ts +12 -4
  105. package/lib/internal/platform-comment.ts +12 -4
  106. package/lib/internal/platform-context.ts +3 -1
  107. package/lib/internal/platform-issue.ts +11 -4
  108. package/lib/internal/platform-metadata.ts +70 -0
  109. package/lib/internal/platform-pr-review.ts +14 -3
  110. package/lib/internal/platform-pr.ts +23 -10
  111. package/lib/internal/platform-release-notes.ts +5 -3
  112. package/lib/internal/platform-security.ts +83 -0
  113. package/lib/internal/pr-review-grade.ts +10 -3
  114. package/lib/internal/release-workflow.ts +17 -5
  115. package/lib/internal/sandbox-control.ts +19 -5
  116. package/lib/internal/task-activity.ts +12 -4
  117. package/lib/internal/task-artifact.ts +164 -25
  118. package/lib/internal/task-context.ts +3 -1
  119. package/lib/internal/task-create.ts +76 -15
  120. package/lib/internal/task-delivery.ts +3 -1
  121. package/lib/internal/task-event.ts +3 -1
  122. package/lib/internal/task-finalization.ts +3 -1
  123. package/lib/internal/task-invalidation.ts +3 -1
  124. package/lib/internal/task-ledger.ts +11 -1
  125. package/lib/internal/task-lifecycle.ts +3 -1
  126. package/lib/internal/task-operation-registry.ts +482 -0
  127. package/lib/internal/task-orchestration.ts +12 -0
  128. package/lib/internal/task-override.ts +9 -3
  129. package/lib/internal/task-qualification.ts +61 -0
  130. package/lib/internal/task-review.ts +3 -1
  131. package/lib/internal/task-short-id.ts +10 -1
  132. package/lib/internal/task-snapshot.ts +3 -1
  133. package/lib/internal/task-validate.ts +6 -2
  134. package/lib/internal/task-verify.ts +4 -1
  135. package/lib/internal/task-warning.ts +7 -1
  136. package/lib/platform/comment-safety.ts +250 -0
  137. package/lib/platform/github-provider.ts +229 -7
  138. package/lib/platform/issue-comments.ts +160 -35
  139. package/lib/platform/pr-change-report.ts +6 -12
  140. package/lib/platform/pr-summary.ts +27 -6
  141. package/lib/platform/provider-contract.ts +78 -3
  142. package/lib/platform/provider-validation.ts +85 -3
  143. package/lib/platform/pull-requests.ts +126 -178
  144. package/lib/platform/repository-metadata.ts +453 -0
  145. package/lib/platform/security-alerts.ts +191 -0
  146. package/lib/platform/verification-sync.ts +32 -28
  147. package/lib/process-data/index.ts +7 -0
  148. package/lib/run/index.ts +5 -0
  149. package/lib/sandbox/commands/control-status.ts +26 -0
  150. package/lib/sandbox/commands/create.ts +46 -6
  151. package/lib/sandbox/commands/ls.ts +7 -0
  152. package/lib/sandbox/commands/rm.ts +1000 -87
  153. package/lib/sandbox/commands/show.ts +16 -0
  154. package/lib/sandbox/control/client.ts +103 -9
  155. package/lib/sandbox/control/completed-reentry.ts +98 -0
  156. package/lib/sandbox/control/container-identity.ts +27 -43
  157. package/lib/sandbox/control/executor.ts +177 -12
  158. package/lib/sandbox/control/lifecycle.ts +629 -17
  159. package/lib/sandbox/control/native-file-lock.ts +209 -0
  160. package/lib/sandbox/control/protocol.ts +5 -2
  161. package/lib/sandbox/control/server.ts +319 -37
  162. package/lib/sandbox/control/state.ts +6 -3
  163. package/lib/sandbox/control/task-view.ts +240 -0
  164. package/lib/sandbox/engines/authority.ts +365 -0
  165. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  166. package/lib/sandbox/index.ts +7 -0
  167. package/lib/sandbox/recovery.ts +21 -1
  168. package/lib/sandbox/workspace-view.ts +9 -3
  169. package/lib/sandbox/worktree-safety.ts +4 -1
  170. package/lib/server/index.ts +10 -0
  171. package/lib/task/artifact-lifecycle.ts +38 -16
  172. package/lib/task/artifact-operations.ts +626 -0
  173. package/lib/task/artifact-repair-intent.ts +71 -0
  174. package/lib/task/artifact-schema.ts +192 -0
  175. package/lib/task/capabilities.ts +70 -1
  176. package/lib/task/control-authority.ts +11 -1
  177. package/lib/task/create-service.ts +131 -6
  178. package/lib/task/create.ts +27 -0
  179. package/lib/task/events.ts +161 -65
  180. package/lib/task/index.ts +16 -3
  181. package/lib/task/local-artifact-finalization.ts +87 -218
  182. package/lib/task/orchestration.ts +172 -49
  183. package/lib/task/qualification-audit.ts +599 -0
  184. package/lib/task/qualification-confirmation.ts +117 -0
  185. package/lib/task/qualification-intents.ts +150 -0
  186. package/lib/task/review-artifacts.ts +21 -22
  187. package/lib/task/review-finalization.ts +133 -4
  188. package/lib/task/sections.ts +6 -4
  189. package/lib/task/verification-config.ts +12 -0
  190. package/lib/task/verification-engine.ts +55 -4
  191. package/package.json +6 -1
  192. package/templates/.agents/rules/README.en.md +2 -0
  193. package/templates/.agents/rules/README.zh-CN.md +2 -0
  194. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  195. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  196. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  197. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  198. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  199. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  200. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  201. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  202. package/templates/.agents/rules/security-alerts.en.md +8 -8
  203. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  204. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  206. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  207. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  208. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  213. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  214. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  215. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  216. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  217. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  218. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  219. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  221. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  222. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  223. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
  226. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  227. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  228. package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
  229. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
  230. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  231. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  232. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  233. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  234. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  235. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  236. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  237. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  238. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  239. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  240. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  241. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  242. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  243. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  244. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  245. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  246. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  247. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  248. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  249. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  250. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  251. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  252. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  253. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  254. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  255. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  256. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  257. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  258. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  259. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  260. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  261. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  262. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  263. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  264. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  265. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
  268. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  269. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  270. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  271. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  272. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  273. package/templates/.agents/templates/task.en.md +6 -0
  274. package/templates/.agents/templates/task.zh-CN.md +6 -0
  275. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  276. package/templates/.agents/scripts/security-alerts.sh +0 -31
  277. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  278. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  279. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  280. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { detectRepoRoot } from '../task/resolve-ref.js';
3
3
  import { configuredShortIdLength, executeShortIdCommand } from '../task/short-id.js';
4
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
4
5
  const USAGE = `Usage: agent-infra-internal task-short-id <alloc|release|resolve|list> [argument] [--active-dir <path>] [--short-id-length <N>] [--verify]\n`;
5
6
  function failure(message) {
6
7
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, output: '', error: { code: 'SHORT_ID_PAYLOAD_INVALID', message } })}\n`);
@@ -8,12 +9,21 @@ function failure(message) {
8
9
  process.exitCode = 1;
9
10
  }
10
11
  function taskShortId(args = []) {
12
+ if (!ensureInternalHandlerRoute('task-short-id', args))
13
+ return;
11
14
  if (args[0] === '--help' || args[0] === '-h') {
12
15
  process.stdout.write(USAGE);
13
16
  return;
14
17
  }
15
18
  const operation = args[0];
16
- if (!operation || !['alloc', 'release', 'resolve', 'list'].includes(operation)) {
19
+ const route = operation === 'list' && args.includes('--verify') ? 'list-verify' : operation ?? '';
20
+ if (!operation || ![
21
+ internalHandlerRoute('task-short-id', 'alloc', route),
22
+ internalHandlerRoute('task-short-id', 'release', route),
23
+ internalHandlerRoute('task-short-id', 'resolve', route),
24
+ internalHandlerRoute('task-short-id', 'list', route),
25
+ internalHandlerRoute('task-short-id', 'list-verify', route)
26
+ ].some(Boolean)) {
17
27
  failure('a valid short-id operation is required');
18
28
  return;
19
29
  }
@@ -1,4 +1,5 @@
1
1
  import { collectTaskSnapshot } from '../task/snapshot.js';
2
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
2
3
  const USAGE = 'Usage: agent-infra-internal task-snapshot <N | TASK-id> [--format json|text]\n';
3
4
  function fail(message) {
4
5
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'SNAPSHOT_PAYLOAD_INVALID', message } })}\n`);
@@ -6,11 +7,13 @@ function fail(message) {
6
7
  process.exitCode = 1;
7
8
  }
8
9
  function taskSnapshot(args = []) {
10
+ if (!ensureInternalHandlerRoute('task-snapshot', args))
11
+ return;
9
12
  if (args[0] === '--help' || args[0] === '-h') {
10
13
  process.stdout.write(USAGE);
11
14
  return;
12
15
  }
13
- if (!args[0] || args[0].startsWith('--')) {
16
+ if (!args[0] || args[0].startsWith('--') || !internalHandlerRoute('task-snapshot', 'snapshot', 'snapshot')) {
14
17
  fail('task ref is required');
15
18
  return;
16
19
  }
@@ -16,6 +16,7 @@ import { readSandboxControlManifest } from '../sandbox/control/lifecycle.js';
16
16
  import { SANDBOX_CONTROL_FUTURE_SKEW_MS, SANDBOX_CONTROL_STATUS_STALE_MS } from '../sandbox/control/protocol.js';
17
17
  import { getProcessStartTime } from '../server/process-state.js';
18
18
  import { assertGitWorktreeBinding } from '../git/worktree-identity.js';
19
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
19
20
  const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] -- <command> [args...]`;
20
21
  const MAX_TIMEOUT_MS = 60 * 60 * 1000;
21
22
  const RECOVERY_GRACE_MS = 30_000;
@@ -273,6 +274,8 @@ function sniffFormat(args) {
273
274
  return index >= 0 && args[index + 1] === 'json' ? 'json' : 'text';
274
275
  }
275
276
  function taskValidate(args) {
277
+ if (!ensureInternalHandlerRoute('task-validate', args))
278
+ return;
276
279
  let options;
277
280
  try {
278
281
  options = parseValidateArgs(args);
@@ -290,9 +293,11 @@ function taskValidate(args) {
290
293
  const target = resolveSandboxTarget(options.target, config.repoRoot);
291
294
  const commit = git(config.repoRoot, ['rev-parse', target.branch]);
292
295
  const startedAt = new Date().toISOString();
293
- const result = options.scope === 'snapshot'
296
+ const result = internalHandlerRoute('task-validate', 'snapshot', options.scope)
294
297
  ? snapshotValidation(config.repoRoot, commit, options)
295
- : inplaceValidation(config, target, options);
298
+ : internalHandlerRoute('task-validate', 'inplace', options.scope)
299
+ ? inplaceValidation(config, target, options)
300
+ : (() => { throw new Error('TASK_VALIDATE_SCOPE_UNREGISTERED'); })();
296
301
  const evidence = {
297
302
  version: 1,
298
303
  taskId: target.workspace.mode === 'task-bound' ? target.workspace.taskId : null,
@@ -2,6 +2,7 @@ import { renderTaskVerification, verifyTaskEvent } from '../task/verification.js
2
2
  import { consumeHumanOverride, failureId } from '../task/human-override.js';
3
3
  import { resolveTaskRef } from '../task/resolve-ref.js';
4
4
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
5
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
5
6
  const USAGE = 'Usage: agent-infra-internal task-verify <N | TASK-id> <verification-event> [--artifact <canonical.md>] [--format json|text] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
6
7
  function fail(message) {
7
8
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'VERIFY_PAYLOAD_INVALID', message } })}\n`);
@@ -9,11 +10,14 @@ function fail(message) {
9
10
  process.exitCode = 1;
10
11
  }
11
12
  async function taskVerify(args = []) {
13
+ if (!ensureInternalHandlerRoute('task-verify', args))
14
+ return;
12
15
  if (args[0] === '--help' || args[0] === '-h') {
13
16
  process.stdout.write(USAGE);
14
17
  return;
15
18
  }
16
- if (!args[0] || !args[1] || args[0].startsWith('--') || args[1].startsWith('--')) {
19
+ if (!args[0] || !args[1] || args[0].startsWith('--') || args[1].startsWith('--')
20
+ || !internalHandlerRoute('task-verify', 'event', args[1] ? 'event' : '')) {
17
21
  fail('task ref and verification event are required');
18
22
  return;
19
23
  }
@@ -2,6 +2,7 @@ import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js'
2
2
  import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
3
3
  import { resolveTaskRef } from '../task/resolve-ref.js';
4
4
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
5
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
5
6
  const USAGE = `Usage: agent-infra-internal task-warning <task-ref> <add|set-status|list> [intent flags] [--dry-run]\n`;
6
7
  const FLAGS = {
7
8
  '--step': 'step', '--severity': 'severity', '--code': 'code', '--target': 'target',
@@ -14,12 +15,18 @@ function usageFailure(message) {
14
15
  process.exitCode = 1;
15
16
  }
16
17
  async function taskWarning(args = []) {
18
+ if (!ensureInternalHandlerRoute('task-warning', args))
19
+ return;
17
20
  if (args[0] === '--help' || args[0] === '-h') {
18
21
  process.stdout.write(USAGE);
19
22
  return;
20
23
  }
21
24
  const [taskRef, kind] = args;
22
- if (!taskRef || !kind || !['add', 'set-status', 'list'].includes(kind)) {
25
+ if (!taskRef || !kind || ![
26
+ internalHandlerRoute('task-warning', 'add', kind ?? ''),
27
+ internalHandlerRoute('task-warning', 'set-status', kind ?? ''),
28
+ internalHandlerRoute('task-warning', 'list', kind ?? '')
29
+ ].some(Boolean)) {
23
30
  usageFailure('task ref and a valid intent are required');
24
31
  return;
25
32
  }
@@ -0,0 +1,226 @@
1
+ const CONTROL_MARKER_PATTERN = /<!--\s*(?:sync-issue|sync-pr|last-commit)\b[\s\S]*?-->|<!--\s*canonical-pr-change-report\s*-->/gi;
2
+ function ok(value) {
3
+ return { ok: true, value };
4
+ }
5
+ function invalid(message, offset) {
6
+ return { ok: false, error: { code: 'COMMENT_DOCUMENT_INVALID', message, offset } };
7
+ }
8
+ function normalized(value) {
9
+ return value.replace(/\r\n?/g, '\n');
10
+ }
11
+ function linesOf(value) {
12
+ const lines = [];
13
+ let start = 0;
14
+ while (start < value.length) {
15
+ const newline = value.indexOf('\n', start);
16
+ const end = newline === -1 ? value.length : newline;
17
+ lines.push({ start, end: newline === -1 ? end : newline + 1, text: value.slice(start, end) });
18
+ start = newline === -1 ? value.length : newline + 1;
19
+ }
20
+ if (value.length === 0)
21
+ lines.push({ start: 0, end: 0, text: '' });
22
+ return lines;
23
+ }
24
+ function openingFence(line) {
25
+ const match = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
26
+ if (!match)
27
+ return null;
28
+ const character = match[1][0];
29
+ const info = match[2];
30
+ if (character === '`' && info.includes('`'))
31
+ return 'invalid';
32
+ return { character, length: match[1].length };
33
+ }
34
+ function closesFence(line, fence) {
35
+ const escaped = fence.character === '`' ? '`' : '~';
36
+ return new RegExp(`^ {0,3}${escaped}{${fence.length},}[ \\t]*$`).test(line);
37
+ }
38
+ function fenceRanges(value) {
39
+ const ranges = [];
40
+ let fence = null;
41
+ for (const line of linesOf(value)) {
42
+ if (!fence) {
43
+ const opening = openingFence(line.text);
44
+ if (opening === 'invalid')
45
+ return invalid('fenced code info string is invalid', line.start);
46
+ if (opening)
47
+ fence = { ...opening, start: line.start, openingEnd: line.end };
48
+ continue;
49
+ }
50
+ if (closesFence(line.text, fence)) {
51
+ ranges.push({
52
+ start: fence.start,
53
+ end: line.end,
54
+ openingEnd: fence.openingEnd,
55
+ closingStart: line.start,
56
+ character: fence.character,
57
+ opening: value.slice(fence.start, fence.openingEnd),
58
+ closing: value.slice(line.start, line.end)
59
+ });
60
+ fence = null;
61
+ }
62
+ }
63
+ if (fence)
64
+ return invalid('fenced code block is not closed', fence.start);
65
+ return ok(ranges);
66
+ }
67
+ function escapeHtml(value) {
68
+ return value
69
+ .replace(/&/g, '&amp;')
70
+ .replace(/</g, '&lt;')
71
+ .replace(/>/g, '&gt;')
72
+ .replace(/"/g, '&quot;')
73
+ .replace(/'/g, '&#39;');
74
+ }
75
+ function escapeHtmlText(value) {
76
+ return escapeHtml(value);
77
+ }
78
+ function escapeHtmlAttribute(value) {
79
+ return escapeHtml(value);
80
+ }
81
+ function escapeMarkdownLiteral(value) {
82
+ return value.replace(/[\\`*_{}\[\]()#+.!|<>-]/g, '\\$&');
83
+ }
84
+ function markerSafe(value, markers) {
85
+ let result = value;
86
+ for (const marker of markers) {
87
+ const flags = marker.flags.includes('g') ? marker.flags : `${marker.flags}g`;
88
+ const pattern = new RegExp(marker.source, flags);
89
+ result = result.replace(pattern, (match) => escapeHtmlText(match));
90
+ }
91
+ return result;
92
+ }
93
+ function tagEnd(value, start) {
94
+ let quote = null;
95
+ for (let index = start + 1; index < value.length; index += 1) {
96
+ const character = value[index];
97
+ if (quote) {
98
+ if (character === quote)
99
+ quote = null;
100
+ }
101
+ else if (character === '"' || character === "'") {
102
+ quote = character;
103
+ }
104
+ else if (character === '>') {
105
+ return index;
106
+ }
107
+ }
108
+ return -1;
109
+ }
110
+ function isMarkdownAutolink(value, start) {
111
+ const end = value.indexOf('>', start + 1);
112
+ if (end === -1)
113
+ return false;
114
+ const target = value.slice(start + 1, end);
115
+ return /^(?:https?:\/\/|mailto:)[^<>\s]+$/i.test(target)
116
+ || /^[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+$/.test(target);
117
+ }
118
+ function sanitizeOutside(value, baseOffset) {
119
+ let result = '';
120
+ let index = 0;
121
+ while (index < value.length) {
122
+ if (value.startsWith('<!--', index)) {
123
+ const end = value.indexOf('-->', index + 4);
124
+ if (end === -1)
125
+ return invalid('HTML comment is not closed', baseOffset + index);
126
+ const token = value.slice(index, end + 3);
127
+ result += escapeHtmlText(token);
128
+ index = end + 3;
129
+ continue;
130
+ }
131
+ if (value[index] === '<' && isMarkdownAutolink(value, index)) {
132
+ const end = value.indexOf('>', index + 1);
133
+ result += value.slice(index, end + 1);
134
+ index = end + 1;
135
+ continue;
136
+ }
137
+ if (value[index] === '<' && /[\\/!?A-Za-z]/.test(value[index + 1] || '')) {
138
+ const end = tagEnd(value, index);
139
+ if (end === -1)
140
+ return invalid('HTML-like tag is not closed', baseOffset + index);
141
+ result += escapeHtmlText(value.slice(index, end + 1));
142
+ index = end + 1;
143
+ continue;
144
+ }
145
+ result += value[index];
146
+ index += 1;
147
+ }
148
+ return ok(result);
149
+ }
150
+ function sanitizeMarkdownDocument(value, options = {}) {
151
+ const source = normalized(value);
152
+ const ranges = fenceRanges(source);
153
+ if (!ranges.ok)
154
+ return ranges;
155
+ const markers = options.reservedMarkers || [];
156
+ let result = '';
157
+ let cursor = 0;
158
+ for (const range of ranges.value) {
159
+ const outside = sanitizeOutside(source.slice(cursor, range.start), cursor);
160
+ if (!outside.ok)
161
+ return outside;
162
+ result += outside.value;
163
+ result += markerSafe(source.slice(range.start, range.end), markers);
164
+ cursor = range.end;
165
+ }
166
+ const tail = sanitizeOutside(source.slice(cursor), cursor);
167
+ if (!tail.ok)
168
+ return tail;
169
+ return ok(result + tail.value);
170
+ }
171
+ function renderSafeCodeFence(value, language = '', reservedMarkers = [CONTROL_MARKER_PATTERN]) {
172
+ const content = markerSafe(normalized(value), reservedMarkers);
173
+ const longestRun = Math.max(0, ...(content.match(/`+/g) || []).map((run) => run.length));
174
+ const delimiter = '`'.repeat(Math.max(3, longestRun + 1));
175
+ const suffix = content.endsWith('\n') ? '' : '\n';
176
+ return `${delimiter}${language}\n${content}${suffix}${delimiter}`;
177
+ }
178
+ function commentRangesOutsideFences(source, ranges) {
179
+ const comments = [];
180
+ let cursor = 0;
181
+ for (const fence of [...ranges, { start: source.length, end: source.length }]) {
182
+ const segment = source.slice(cursor, fence.start);
183
+ let index = 0;
184
+ while (index < segment.length) {
185
+ const opening = segment.indexOf('<!--', index);
186
+ if (opening === -1)
187
+ break;
188
+ const absoluteStart = cursor + opening;
189
+ const closing = segment.indexOf('-->', opening + 4);
190
+ if (closing === -1)
191
+ return invalid('HTML comment is not closed', absoluteStart);
192
+ comments.push({ start: absoluteStart, end: cursor + closing + 3 });
193
+ index = closing + 3;
194
+ }
195
+ cursor = fence.end;
196
+ }
197
+ return ok(comments);
198
+ }
199
+ function splitDocumentPlaceholder(value, placeholder) {
200
+ const source = normalized(value);
201
+ const ranges = fenceRanges(source);
202
+ if (!ranges.ok)
203
+ return ranges;
204
+ const positions = [];
205
+ let cursor = source.indexOf(placeholder);
206
+ while (cursor !== -1) {
207
+ positions.push(cursor);
208
+ cursor = source.indexOf(placeholder, cursor + placeholder.length);
209
+ }
210
+ if (positions.length !== 1)
211
+ return invalid('document must contain exactly one placeholder', positions[1] ?? positions[0] ?? source.length);
212
+ const position = positions[0];
213
+ if (ranges.value.some((range) => position >= range.start && position < range.end)) {
214
+ return invalid('placeholder must be outside fenced code', position);
215
+ }
216
+ const comments = commentRangesOutsideFences(source, ranges.value);
217
+ if (!comments.ok)
218
+ return comments;
219
+ const containing = comments.value.find((comment) => position >= comment.start && position < comment.end);
220
+ if (containing && (containing.start !== position || containing.end !== position + placeholder.length)) {
221
+ return invalid('placeholder must not be nested in another HTML comment', position);
222
+ }
223
+ return ok({ prefix: source.slice(0, position), suffix: source.slice(position + placeholder.length) });
224
+ }
225
+ export { CONTROL_MARKER_PATTERN, escapeHtmlAttribute, escapeHtmlText, escapeMarkdownLiteral, fenceRanges, renderSafeCodeFence, sanitizeMarkdownDocument, splitDocumentPlaceholder };
226
+ //# sourceMappingURL=comment-safety.js.map
@@ -5,10 +5,11 @@ import path from 'node:path';
5
5
  import semver from 'semver';
6
6
  import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
7
7
  import { resourceIdentityNumber, resourceIdentityString } from './resource-identity.js';
8
- import { syncLabelDelta } from './in-label-sync.js';
8
+ import { extractPullRequestFileNames, syncLabelDelta } from './in-label-sync.js';
9
9
  const CURRENT_USER_QUERY = 'query { viewer { login } }';
10
10
  const ISSUE_TYPES_QUERY = `query($owner:String!){organization(login:$owner){issueTypes(first:20){nodes{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}}}}}`;
11
11
  const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){id issueType{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{id name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{id name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{id name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{id name}}}}}}}}`;
12
+ const CLOSING_ISSUES_QUERY = 'query($owner:String!,$name:String!,$number:Int!,$cursor:String){repository(owner:$owner,name:$name){pullRequest(number:$number){closingIssuesReferences(first:100,after:$cursor){nodes{number} pageInfo{hasNextPage endCursor}}}}}';
12
13
  function parseGitHubRemote(remote) {
13
14
  const trimmed = remote.trim().replace(/\.git$/, '');
14
15
  const match = trimmed.match(/^(?:https?:\/\/github\.com\/|ssh:\/\/(?:git@)?github\.com\/|git@github\.com:)([^/\s]+\/[^/\s]+)$/i);
@@ -289,6 +290,21 @@ function commentSnapshot(value) {
289
290
  function createReceipt(remoteId) {
290
291
  return { ok: true, value: { changed: true, remoteId } };
291
292
  }
293
+ function partialFailure(response, value) {
294
+ return { ok: false, error: response.error, value };
295
+ }
296
+ function labelReconciliation(created, updated, removed, skipped) {
297
+ return {
298
+ changed: created.length > 0 || updated.length > 0 || removed.length > 0,
299
+ created,
300
+ updated,
301
+ removed,
302
+ skipped
303
+ };
304
+ }
305
+ function milestoneInitialization(created, skipped) {
306
+ return { changed: created.length > 0, created, skipped };
307
+ }
292
308
  function githubResourceToken(identity) {
293
309
  return resourceIdentityString(identity) || (identity?.kind === 'number' ? String(identity.value) : null);
294
310
  }
@@ -391,6 +407,20 @@ function createGitHubOperations(client) {
391
407
  return { ok: true, value: issueSnapshot(fetched.value, graph || undefined) };
392
408
  },
393
409
  async create({ context, desired }) {
410
+ let milestone = null;
411
+ if (desired.milestone) {
412
+ const milestones = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=open&per_page=100`], { cwd: context.workingDirectory });
413
+ if (!milestones.ok)
414
+ return milestones;
415
+ const values = Array.isArray(milestones.value)
416
+ ? milestones.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
417
+ : [];
418
+ const selected = values.find((item) => item?.title === desired.milestone);
419
+ if (!Number.isSafeInteger(selected?.number) || selected.number <= 0) {
420
+ return invalid('MILESTONE_IDENTITY_INVALID', 'Milestone title does not resolve to a positive number');
421
+ }
422
+ milestone = selected.number;
423
+ }
394
424
  const response = client.json(['api', `repos/${repository(context)}/issues`, '-X', 'POST', '--input', '-'], {
395
425
  cwd: context.workingDirectory,
396
426
  method: 'POST',
@@ -399,7 +429,7 @@ function createGitHubOperations(client) {
399
429
  body: desired.body,
400
430
  labels: desired.labels,
401
431
  assignees: desired.assignees,
402
- ...(desired.milestone ? { milestone: desired.milestone } : {})
432
+ ...(milestone ? { milestone } : {})
403
433
  })
404
434
  });
405
435
  if (!response.ok)
@@ -582,6 +612,61 @@ function createGitHubOperations(client) {
582
612
  return fetched;
583
613
  return { ok: true, value: changeRequestSnapshot(fetched.value) };
584
614
  },
615
+ async listFiles({ context, target }) {
616
+ const number = resourceIdentityNumber(target);
617
+ if (!number)
618
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
619
+ const response = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/pulls/${number}/files?per_page=100`], { cwd: context.workingDirectory });
620
+ if (!response.ok)
621
+ return response;
622
+ const files = extractPullRequestFileNames(response.value);
623
+ return files
624
+ ? { ok: true, value: files }
625
+ : invalid('IN_LABEL_SYNC_FILES_INVALID', 'Pull request files response is incomplete');
626
+ },
627
+ async listClosingIssues({ context, target }) {
628
+ const number = resourceIdentityNumber(target);
629
+ if (!number)
630
+ return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
631
+ const [owner, name] = repository(context).split('/');
632
+ if (!owner || !name)
633
+ return invalid('IN_LABEL_SYNC_REPOSITORY_INVALID', 'Repository identity is invalid');
634
+ const identities = [];
635
+ let cursor = null;
636
+ for (;;) {
637
+ const args = ['api', 'graphql', '-f', `query=${CLOSING_ISSUES_QUERY}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${number}`];
638
+ if (cursor)
639
+ args.push('-F', `cursor=${cursor}`);
640
+ const response = client.json(args, { cwd: context.workingDirectory });
641
+ if (!response.ok)
642
+ return response;
643
+ const connection = response.value?.data?.repository?.pullRequest?.closingIssuesReferences;
644
+ if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo) {
645
+ return invalid('IN_LABEL_SYNC_ASSOCIATION_INVALID', 'Closing Issue response is incomplete');
646
+ }
647
+ for (const node of connection.nodes) {
648
+ const issue = Number(node?.number);
649
+ if (!Number.isSafeInteger(issue) || issue <= 0) {
650
+ return invalid('IN_LABEL_SYNC_ASSOCIATION_INVALID', 'Closing Issue identity is invalid');
651
+ }
652
+ identities.push({ kind: 'number', value: issue });
653
+ }
654
+ if (!connection.pageInfo.hasNextPage) {
655
+ const seen = new Set();
656
+ return { ok: true, value: identities.filter((identity) => {
657
+ const key = `${identity.kind}:${identity.value}`;
658
+ if (seen.has(key))
659
+ return false;
660
+ seen.add(key);
661
+ return true;
662
+ }) };
663
+ }
664
+ if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor) {
665
+ return invalid('IN_LABEL_SYNC_ASSOCIATION_INVALID', 'Closing Issue pagination cursor is invalid');
666
+ }
667
+ cursor = connection.pageInfo.endCursor;
668
+ }
669
+ },
585
670
  async listClosing({ context, issue }) {
586
671
  const number = resourceIdentityNumber(issue);
587
672
  if (!number)
@@ -855,23 +940,165 @@ function createGitHubOperations(client) {
855
940
  };
856
941
  }
857
942
  };
943
+ const securityAlerts = {
944
+ async inspect({ context, kind, number }) {
945
+ if (!Number.isSafeInteger(number) || number <= 0)
946
+ return invalid('SECURITY_NUMBER_INVALID', 'Alert number must be a positive integer');
947
+ const suffix = kind === 'dependabot' ? 'dependabot' : kind === 'code-scanning' ? 'code-scanning' : null;
948
+ if (!suffix)
949
+ return invalid('SECURITY_KIND_INVALID', 'Alert kind is invalid');
950
+ const response = client.json(['api', `repos/${repository(context)}/${suffix}/alerts/${number}`], { cwd: context.workingDirectory });
951
+ if (!response.ok)
952
+ return response;
953
+ const data = response.value;
954
+ if (!data || typeof data !== 'object' || Array.isArray(data))
955
+ return invalid('SECURITY_RESPONSE_INVALID', 'The platform returned an invalid security alert');
956
+ const value = {
957
+ kind,
958
+ number,
959
+ state: typeof data.state === 'string' ? data.state : 'invalid',
960
+ data
961
+ };
962
+ return { ok: true, value };
963
+ },
964
+ async dismiss({ context, kind, number, reason, comment }) {
965
+ const suffix = kind === 'dependabot' ? 'dependabot' : kind === 'code-scanning' ? 'code-scanning' : null;
966
+ if (!suffix)
967
+ return invalid('SECURITY_KIND_INVALID', 'Alert kind is invalid');
968
+ if (!Number.isSafeInteger(number) || number <= 0)
969
+ return invalid('SECURITY_NUMBER_INVALID', 'Alert number must be a positive integer');
970
+ const response = client.json(['api', `repos/${repository(context)}/${suffix}/alerts/${number}`, '-X', 'PATCH', '--input', '-'], {
971
+ cwd: context.workingDirectory,
972
+ method: 'PATCH',
973
+ input: JSON.stringify({ state: 'dismissed', dismissed_reason: reason, dismissed_comment: comment })
974
+ });
975
+ if (!response.ok)
976
+ return response;
977
+ return createReceipt(String(response.value?.id || `${kind}:${number}`));
978
+ }
979
+ };
980
+ const repositoryMetadata = {
981
+ async reconcileLabels({ context, desired, cleanupStaleIn }) {
982
+ const endpoint = `repos/${repository(context)}/labels?per_page=100`;
983
+ const listed = client.json(['api', '--paginate', '--slurp', endpoint], { cwd: context.workingDirectory });
984
+ if (!listed.ok)
985
+ return listed;
986
+ if (!Array.isArray(listed.value))
987
+ return invalid('LABEL_RESPONSE_INVALID', 'The platform returned an invalid label list');
988
+ const values = Array.isArray(listed.value)
989
+ ? listed.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
990
+ : [];
991
+ if (values.some((entry) => !entry || typeof entry.name !== 'string' || !entry.name)) {
992
+ return invalid('LABEL_RESPONSE_INVALID', 'The platform returned a label without a valid name');
993
+ }
994
+ const current = new Map(values.map((entry) => [String(entry.name), entry]));
995
+ const created = [];
996
+ const updated = [];
997
+ const skipped = [];
998
+ const removed = [];
999
+ for (const label of desired) {
1000
+ const existing = current.get(label.name);
1001
+ if (!existing) {
1002
+ const response = client.json(['api', `repos/${repository(context)}/labels`, '-X', 'POST', '--input', '-'], {
1003
+ cwd: context.workingDirectory,
1004
+ method: 'POST',
1005
+ input: JSON.stringify({ name: label.name, color: label.color, description: label.description })
1006
+ });
1007
+ if (!response.ok)
1008
+ return partialFailure(response, labelReconciliation(created, updated, removed, skipped));
1009
+ created.push(label.name);
1010
+ continue;
1011
+ }
1012
+ const sameColor = typeof existing.color === 'string' && existing.color.toUpperCase() === label.color.toUpperCase();
1013
+ const sameDescription = String(existing.description || '') === label.description;
1014
+ if (sameColor && sameDescription) {
1015
+ skipped.push(label.name);
1016
+ continue;
1017
+ }
1018
+ const response = client.json(['api', `repos/${repository(context)}/labels/${encodeURIComponent(label.name)}`, '-X', 'PATCH', '--input', '-'], {
1019
+ cwd: context.workingDirectory,
1020
+ method: 'PATCH',
1021
+ input: JSON.stringify({ new_name: label.name, color: label.color, description: label.description })
1022
+ });
1023
+ if (!response.ok)
1024
+ return partialFailure(response, labelReconciliation(created, updated, removed, skipped));
1025
+ updated.push(label.name);
1026
+ }
1027
+ const desiredNames = new Set(desired.map((label) => label.name));
1028
+ if (cleanupStaleIn) {
1029
+ for (const name of current.keys()) {
1030
+ if (!name.startsWith('in:') || desiredNames.has(name))
1031
+ continue;
1032
+ const response = client.text(['api', `repos/${repository(context)}/labels/${encodeURIComponent(name)}`, '-X', 'DELETE'], {
1033
+ cwd: context.workingDirectory,
1034
+ method: 'DELETE'
1035
+ });
1036
+ if (!response.ok)
1037
+ return partialFailure(response, labelReconciliation(created, updated, removed, skipped));
1038
+ removed.push(name);
1039
+ }
1040
+ }
1041
+ return {
1042
+ ok: true,
1043
+ value: {
1044
+ changed: created.length > 0 || updated.length > 0 || removed.length > 0,
1045
+ created,
1046
+ updated,
1047
+ removed,
1048
+ skipped
1049
+ }
1050
+ };
1051
+ },
1052
+ async reconcileMilestones({ context, desired }) {
1053
+ const listed = client.json(['api', '--paginate', '--slurp', `repos/${repository(context)}/milestones?state=all&per_page=100`], { cwd: context.workingDirectory });
1054
+ if (!listed.ok)
1055
+ return listed;
1056
+ if (!Array.isArray(listed.value))
1057
+ return invalid('MILESTONE_RESPONSE_INVALID', 'The platform returned an invalid milestone list');
1058
+ const values = Array.isArray(listed.value)
1059
+ ? listed.value.flatMap((entry) => Array.isArray(entry) ? entry : [entry])
1060
+ : [];
1061
+ if (values.some((entry) => !entry || typeof entry.title !== 'string' || !entry.title)) {
1062
+ return invalid('MILESTONE_RESPONSE_INVALID', 'The platform returned a milestone without a valid title');
1063
+ }
1064
+ const existing = new Set(values.map((entry) => String(entry.title)));
1065
+ const created = [];
1066
+ const skipped = [];
1067
+ for (const milestone of desired) {
1068
+ if (existing.has(milestone.title)) {
1069
+ skipped.push(milestone.title);
1070
+ continue;
1071
+ }
1072
+ const response = client.json(['api', `repos/${repository(context)}/milestones`, '-X', 'POST', '--input', '-'], {
1073
+ cwd: context.workingDirectory,
1074
+ method: 'POST',
1075
+ input: JSON.stringify({ title: milestone.title, description: milestone.description, state: milestone.state })
1076
+ });
1077
+ if (!response.ok)
1078
+ return partialFailure(response, milestoneInitialization(created, skipped));
1079
+ existing.add(milestone.title);
1080
+ created.push(milestone.title);
1081
+ }
1082
+ return { ok: true, value: { changed: created.length > 0, created, skipped } };
1083
+ }
1084
+ };
858
1085
  const verification = {
859
1086
  async fetchRemoteFacts(input) {
860
1087
  const issue = input.issue && issues.inspect
861
1088
  ? await issues.inspect({ context: input.context, target: input.issue })
862
1089
  : { ok: true, value: null };
863
1090
  if (!issue.ok)
864
- return issue;
1091
+ return { ok: false, error: issue.error };
865
1092
  const commentFacts = input.includeComments && input.issue && comments.list
866
1093
  ? await comments.list({ context: input.context, parent: input.issue })
867
1094
  : { ok: true, value: [] };
868
1095
  if (!commentFacts.ok)
869
- return commentFacts;
1096
+ return { ok: false, error: commentFacts.error };
870
1097
  const changeRequest = input.changeRequest && changeRequests.inspect
871
1098
  ? await changeRequests.inspect({ context: input.context, target: input.changeRequest })
872
1099
  : { ok: true, value: null };
873
1100
  if (!changeRequest.ok)
874
- return changeRequest;
1101
+ return { ok: false, error: changeRequest.error };
875
1102
  return {
876
1103
  ok: true,
877
1104
  value: {
@@ -884,7 +1111,7 @@ function createGitHubOperations(client) {
884
1111
  };
885
1112
  }
886
1113
  };
887
- return { issues, comments, changeRequests, checks, reviews, releases, verification };
1114
+ return { issues, comments, changeRequests, checks, reviews, releases, securityAlerts, repositoryMetadata, verification };
888
1115
  }
889
1116
  function createGitHubProvider(input, client = createGitHubClient()) {
890
1117
  return {