@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.
- package/bin/cli.ts +26 -9
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +42 -21
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +152 -0
- package/dist/lib/internal/codex-lifecycle.js +9 -6
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/platform-checks.js +16 -4
- package/dist/lib/internal/platform-comment.js +16 -4
- package/dist/lib/internal/platform-context.js +4 -1
- package/dist/lib/internal/platform-issue.js +12 -4
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +16 -3
- package/dist/lib/internal/platform-pr.js +24 -10
- package/dist/lib/internal/platform-release-notes.js +6 -3
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +14 -3
- package/dist/lib/internal/release-workflow.js +19 -5
- package/dist/lib/internal/sandbox-control.js +22 -5
- package/dist/lib/internal/task-activity.js +13 -4
- package/dist/lib/internal/task-artifact.js +186 -27
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +48 -15
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +4 -1
- package/dist/lib/internal/task-finalization.js +4 -1
- package/dist/lib/internal/task-invalidation.js +4 -1
- package/dist/lib/internal/task-ledger.js +12 -1
- package/dist/lib/internal/task-lifecycle.js +4 -1
- package/dist/lib/internal/task-operation-registry.js +421 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +10 -3
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +4 -1
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +7 -2
- package/dist/lib/internal/task-verify.js +5 -1
- package/dist/lib/internal/task-warning.js +8 -1
- package/dist/lib/platform/comment-safety.js +226 -0
- package/dist/lib/platform/github-provider.js +233 -6
- package/dist/lib/platform/issue-comments.js +146 -34
- package/dist/lib/platform/pr-change-report.js +6 -12
- package/dist/lib/platform/pr-summary.js +28 -7
- package/dist/lib/platform/provider-contract.d.ts +67 -1
- package/dist/lib/platform/provider-contract.js +6 -2
- package/dist/lib/platform/provider-validation.js +76 -3
- package/dist/lib/platform/pull-requests.js +115 -170
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/verification-sync.js +33 -26
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/run/index.js +5 -0
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +32 -6
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +827 -120
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/client.js +83 -9
- package/dist/lib/sandbox/control/completed-reentry.js +85 -0
- package/dist/lib/sandbox/control/container-identity.js +28 -31
- package/dist/lib/sandbox/control/executor.js +149 -14
- package/dist/lib/sandbox/control/lifecycle.js +621 -127
- package/dist/lib/sandbox/control/native-file-lock.js +177 -0
- package/dist/lib/sandbox/control/server.js +305 -35
- package/dist/lib/sandbox/control/state.js +5 -3
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +5 -0
- package/dist/lib/sandbox/recovery.js +21 -1
- package/dist/lib/sandbox/workspace-view.js +2 -1
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/task/artifact-lifecycle.js +42 -13
- package/dist/lib/task/artifact-operations.js +520 -0
- package/dist/lib/task/artifact-repair-intent.js +59 -0
- package/dist/lib/task/artifact-schema.js +135 -0
- package/dist/lib/task/capabilities.js +72 -1
- package/dist/lib/task/control-authority.js +4 -0
- package/dist/lib/task/create-service.js +103 -4
- package/dist/lib/task/create.js +25 -0
- package/dist/lib/task/events.js +186 -67
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/local-artifact-finalization.js +75 -150
- package/dist/lib/task/orchestration.js +192 -52
- package/dist/lib/task/qualification-audit.js +490 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +22 -23
- package/dist/lib/task/review-finalization.js +121 -4
- package/dist/lib/task/sections.js +6 -4
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +54 -4
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +140 -0
- package/lib/internal/codex-lifecycle.ts +8 -6
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/platform-checks.ts +12 -4
- package/lib/internal/platform-comment.ts +12 -4
- package/lib/internal/platform-context.ts +3 -1
- package/lib/internal/platform-issue.ts +11 -4
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/platform-pr.ts +23 -10
- package/lib/internal/platform-release-notes.ts +5 -3
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +10 -3
- package/lib/internal/release-workflow.ts +17 -5
- package/lib/internal/sandbox-control.ts +19 -5
- package/lib/internal/task-activity.ts +12 -4
- package/lib/internal/task-artifact.ts +164 -25
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +76 -15
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +3 -1
- package/lib/internal/task-finalization.ts +3 -1
- package/lib/internal/task-invalidation.ts +3 -1
- package/lib/internal/task-ledger.ts +11 -1
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +482 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +9 -3
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +3 -1
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +6 -2
- package/lib/internal/task-verify.ts +4 -1
- package/lib/internal/task-warning.ts +7 -1
- package/lib/platform/comment-safety.ts +250 -0
- package/lib/platform/github-provider.ts +229 -7
- package/lib/platform/issue-comments.ts +160 -35
- package/lib/platform/pr-change-report.ts +6 -12
- package/lib/platform/pr-summary.ts +27 -6
- package/lib/platform/provider-contract.ts +78 -3
- package/lib/platform/provider-validation.ts +85 -3
- package/lib/platform/pull-requests.ts +126 -178
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/verification-sync.ts +32 -28
- package/lib/process-data/index.ts +7 -0
- package/lib/run/index.ts +5 -0
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +46 -6
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +1000 -87
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/client.ts +103 -9
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +177 -12
- package/lib/sandbox/control/lifecycle.ts +629 -17
- package/lib/sandbox/control/native-file-lock.ts +209 -0
- package/lib/sandbox/control/protocol.ts +5 -2
- package/lib/sandbox/control/server.ts +319 -37
- package/lib/sandbox/control/state.ts +6 -3
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +7 -0
- package/lib/sandbox/recovery.ts +21 -1
- package/lib/sandbox/workspace-view.ts +9 -3
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/task/artifact-lifecycle.ts +38 -16
- package/lib/task/artifact-operations.ts +626 -0
- package/lib/task/artifact-repair-intent.ts +71 -0
- package/lib/task/artifact-schema.ts +192 -0
- package/lib/task/capabilities.ts +70 -1
- package/lib/task/control-authority.ts +11 -1
- package/lib/task/create-service.ts +131 -6
- package/lib/task/create.ts +27 -0
- package/lib/task/events.ts +161 -65
- package/lib/task/index.ts +16 -3
- package/lib/task/local-artifact-finalization.ts +87 -218
- package/lib/task/orchestration.ts +172 -49
- package/lib/task/qualification-audit.ts +599 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -22
- package/lib/task/review-finalization.ts +133 -4
- package/lib/task/sections.ts +6 -4
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +55 -4
- package/package.json +6 -1
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/decision-qualification.en.md +22 -0
- package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
- package/templates/.agents/rules/evidence-reporting.en.md +28 -0
- package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
- package/templates/.agents/rules/security-alerts.en.md +8 -8
- package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
- package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
- package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
- package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +13 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/templates/task.en.md +6 -0
- package/templates/.agents/templates/task.zh-CN.md +6 -0
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- 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
|
-
|
|
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 =
|
|
296
|
+
const result = internalHandlerRoute('task-validate', 'snapshot', options.scope)
|
|
294
297
|
? snapshotValidation(config.repoRoot, commit, options)
|
|
295
|
-
:
|
|
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 || ![
|
|
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, '&')
|
|
70
|
+
.replace(/</g, '<')
|
|
71
|
+
.replace(/>/g, '>')
|
|
72
|
+
.replace(/"/g, '"')
|
|
73
|
+
.replace(/'/g, ''');
|
|
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
|
-
...(
|
|
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 {
|