@fitlab-ai/agent-infra 0.9.14 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +39 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +4 -2
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +331 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +16 -263
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +101 -4
- package/dist/lib/internal/task-review.js +11 -56
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +18 -12
- package/dist/lib/platform/issues.js +2 -180
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +117 -33
- package/dist/lib/platform/pull-requests.js +5 -108
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +132 -143
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +19 -1511
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +518 -127
- package/dist/lib/sandbox/control/state.js +64 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +89 -0
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +822 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +79 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +29 -289
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +134 -29
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +33 -20
- package/dist/lib/task/control-authority.js +403 -6
- package/dist/lib/task/control-recovery.js +140 -0
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +275 -22
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +8 -14
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +441 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +31 -4
- package/dist/lib/task/local-artifact-finalization.js +197 -197
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +105 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +66 -19
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +26 -0
- package/dist/lib/task/review-finalization.js +175 -216
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +119 -337
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +16 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +30 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +4 -2
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +315 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +14 -217
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +111 -4
- package/lib/internal/task-review.ts +9 -54
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +21 -14
- package/lib/platform/issues.ts +5 -246
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +123 -35
- package/lib/platform/pull-requests.ts +6 -173
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +150 -156
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +21 -1895
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +80 -6
- package/lib/sandbox/control/server.ts +567 -132
- package/lib/sandbox/control/state.ts +86 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +92 -0
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +1142 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +85 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +34 -310
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +162 -33
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +37 -20
- package/lib/task/control-authority.ts +540 -10
- package/lib/task/control-recovery.ts +186 -0
- package/lib/task/create-service.ts +1 -10
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +309 -24
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +8 -16
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +493 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +30 -5
- package/lib/task/local-artifact-finalization.ts +244 -234
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +156 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +84 -19
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +24 -0
- package/lib/task/review-finalization.ts +183 -292
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +113 -383
- package/lib/task/verification-types.ts +15 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +17 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/lib/version.ts +1 -15
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -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-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import type { PlatformClient as GitHubClient } from './context.ts';
|
|
2
|
+
import type { PlatformIssueSnapshot as IssueSnapshot, PlatformChangeRequestSnapshot as PullRequestSnapshot } from './snapshots.ts';
|
|
3
|
+
import type { RequiredCheckSnapshot } from './provider-contract.ts';
|
|
4
|
+
import { checkStatusBucket } from './check-status.ts';
|
|
5
|
+
|
|
6
|
+
type RemoteIssue = {
|
|
7
|
+
number?: number;
|
|
8
|
+
id?: number;
|
|
9
|
+
node_id?: string;
|
|
10
|
+
html_url?: string;
|
|
11
|
+
state?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
body?: string | null;
|
|
14
|
+
labels?: Array<string | { name?: string }>;
|
|
15
|
+
assignees?: Array<{ login?: string }>;
|
|
16
|
+
milestone?: { title?: string } | null;
|
|
17
|
+
type?: { name?: string } | null;
|
|
18
|
+
pull_request?: unknown;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type IssueFieldSchema = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
kind: 'single-select' | 'date' | 'text' | 'number';
|
|
25
|
+
options: Array<{ id: string; name: string }>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type IssueTypeSchema = { id: string; name: string; fields: IssueFieldSchema[] };
|
|
29
|
+
|
|
30
|
+
type CurrentField = { id: string; name: string; kind: IssueFieldSchema['kind']; value: string | number | null };
|
|
31
|
+
|
|
32
|
+
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}}}}}}}}`;
|
|
33
|
+
|
|
34
|
+
function normalizeIssue(remote: RemoteIssue, repository: string, fallbackNumber?: number): IssueSnapshot | null {
|
|
35
|
+
const number = Number.isInteger(remote.number) && Number(remote.number) > 0
|
|
36
|
+
? Number(remote.number)
|
|
37
|
+
: fallbackNumber;
|
|
38
|
+
if (!number || !Number.isSafeInteger(number) || remote.pull_request) return null;
|
|
39
|
+
return {
|
|
40
|
+
repository,
|
|
41
|
+
number,
|
|
42
|
+
databaseId: Number.isInteger(remote.id) ? Number(remote.id) : null,
|
|
43
|
+
nodeId: remote.node_id || `issue-${number}`,
|
|
44
|
+
url: remote.html_url || `https://github.com/${repository}/issues/${number}`,
|
|
45
|
+
state: String(remote.state || '').toLowerCase() === 'closed' ? 'closed' : 'open',
|
|
46
|
+
title: remote.title || '',
|
|
47
|
+
body: remote.body || '',
|
|
48
|
+
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
49
|
+
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
50
|
+
milestone: remote.milestone?.title || null,
|
|
51
|
+
issueType: remote.type?.name || null,
|
|
52
|
+
fields: {}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fieldKind(value: { __typename?: string }): IssueFieldSchema['kind'] | null {
|
|
57
|
+
if (value.__typename === 'IssueFieldSingleSelect' || value.__typename === 'IssueFieldSingleSelectValue') return 'single-select';
|
|
58
|
+
if (value.__typename === 'IssueFieldDate' || value.__typename === 'IssueFieldDateValue') return 'date';
|
|
59
|
+
if (value.__typename === 'IssueFieldText' || value.__typename === 'IssueFieldTextValue') return 'text';
|
|
60
|
+
if (value.__typename === 'IssueFieldNumber' || value.__typename === 'IssueFieldNumberValue') return 'number';
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeFieldSchemas(value: unknown): IssueFieldSchema[] {
|
|
65
|
+
if (!Array.isArray(value)) return [];
|
|
66
|
+
return value.flatMap((raw) => {
|
|
67
|
+
const item = raw as { id?: string; name?: string; __typename?: string; options?: Array<{ id?: string; name?: string }> };
|
|
68
|
+
const kind = fieldKind(item);
|
|
69
|
+
return item.id && item.name && kind ? [{
|
|
70
|
+
id: item.id,
|
|
71
|
+
name: item.name,
|
|
72
|
+
kind,
|
|
73
|
+
options: (item.options || []).flatMap((option) => option.id && option.name ? [{ id: option.id, name: option.name }] : [])
|
|
74
|
+
}] : [];
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeCurrentFields(value: unknown): { issueId: string | null; type: IssueTypeSchema | null; values: CurrentField[] } {
|
|
79
|
+
const issue = (value as { data?: { repository?: { issue?: {
|
|
80
|
+
id?: string;
|
|
81
|
+
issueType?: { id?: string; name?: string; pinnedFields?: unknown[] } | null;
|
|
82
|
+
issueFieldValues?: { nodes?: unknown[] };
|
|
83
|
+
} } } })?.data?.repository?.issue;
|
|
84
|
+
const type = issue?.issueType?.name ? {
|
|
85
|
+
id: issue.issueType.id || issue.issueType.name,
|
|
86
|
+
name: issue.issueType.name,
|
|
87
|
+
fields: normalizeFieldSchemas(issue.issueType.pinnedFields)
|
|
88
|
+
} : null;
|
|
89
|
+
const values = (issue?.issueFieldValues?.nodes || []).flatMap((raw) => {
|
|
90
|
+
const item = raw as { __typename?: string; name?: string; value?: string | number; field?: { id?: string; name?: string } };
|
|
91
|
+
const kind = fieldKind(item);
|
|
92
|
+
const value = kind === 'single-select' ? item.name : item.value;
|
|
93
|
+
return item.field?.name && kind && value !== undefined
|
|
94
|
+
? [{ id: item.field.id || item.field.name, name: item.field.name, kind, value: value ?? null }]
|
|
95
|
+
: [];
|
|
96
|
+
});
|
|
97
|
+
return { issueId: issue?.id || null, type, values };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function graphState(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
101
|
+
const [owner, name] = repository.split('/');
|
|
102
|
+
if (!owner || !name) return null;
|
|
103
|
+
const current = client.json<unknown>([
|
|
104
|
+
'api', 'graphql', '-f', `query=${ISSUE_FIELDS_QUERY}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${issue}`
|
|
105
|
+
], { cwd });
|
|
106
|
+
if (!current.ok) return null;
|
|
107
|
+
return normalizeCurrentFields(current.value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function fetchIssue(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
111
|
+
return client.json<RemoteIssue>(['api', `repos/${repository}/issues/${issue}`], { cwd });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function inspectGitHubIssue(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
115
|
+
const fetched = fetchIssue(client, repository, issue, cwd);
|
|
116
|
+
if (!fetched.ok) return fetched;
|
|
117
|
+
const snapshot = normalizeIssue(fetched.value, repository, issue);
|
|
118
|
+
return snapshot
|
|
119
|
+
? { ok: true as const, value: snapshot }
|
|
120
|
+
: { ok: false as const, error: { code: 'ISSUE_IDENTITY_INVALID', message: 'Remote resource is not a valid Issue', retryable: false } };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type RemotePullRequest = {
|
|
124
|
+
number?: number;
|
|
125
|
+
node_id?: string;
|
|
126
|
+
html_url?: string;
|
|
127
|
+
state?: string;
|
|
128
|
+
title?: string;
|
|
129
|
+
body?: string | null;
|
|
130
|
+
draft?: boolean;
|
|
131
|
+
pull_request?: unknown;
|
|
132
|
+
head?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
|
|
133
|
+
base?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
|
|
134
|
+
merged_at?: string | null;
|
|
135
|
+
merge_commit_sha?: string | null;
|
|
136
|
+
labels?: Array<string | { name?: string }>;
|
|
137
|
+
assignees?: Array<{ login?: string }>;
|
|
138
|
+
milestone?: { title?: string } | null;
|
|
139
|
+
mergeable?: boolean | null;
|
|
140
|
+
mergeable_state?: string | null;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
type ClosingPullRequestNode = {
|
|
144
|
+
number?: number;
|
|
145
|
+
id?: string;
|
|
146
|
+
url?: string;
|
|
147
|
+
state?: string;
|
|
148
|
+
title?: string;
|
|
149
|
+
body?: string | null;
|
|
150
|
+
isDraft?: boolean;
|
|
151
|
+
headRefName?: string;
|
|
152
|
+
headRefOid?: string;
|
|
153
|
+
headRepository?: { nameWithOwner?: string } | null;
|
|
154
|
+
baseRefName?: string;
|
|
155
|
+
baseRefOid?: string;
|
|
156
|
+
baseRepository?: { nameWithOwner?: string } | null;
|
|
157
|
+
mergedAt?: string | null;
|
|
158
|
+
mergeCommit?: { oid?: string } | null;
|
|
159
|
+
labels?: { nodes?: Array<{ name?: string }> };
|
|
160
|
+
assignees?: { nodes?: Array<{ login?: string }> };
|
|
161
|
+
milestone?: { title?: string } | null;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
type ClosingPullRequestPage = {
|
|
165
|
+
data?: { repository?: { issue?: { closedByPullRequestsReferences?: {
|
|
166
|
+
nodes?: ClosingPullRequestNode[];
|
|
167
|
+
pageInfo?: { hasNextPage?: boolean; endCursor?: string | null };
|
|
168
|
+
} } } };
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const CLOSING_PULL_REQUESTS_QUERY = `query($owner:String!,$name:String!,$issue:Int!,$cursor:String){repository(owner:$owner,name:$name){issue(number:$issue){closedByPullRequestsReferences(first:100,after:$cursor){nodes{number id url state title body isDraft headRefName headRefOid headRepository{nameWithOwner} baseRefName baseRefOid baseRepository{nameWithOwner} mergedAt mergeCommit{oid} labels(first:100){nodes{name}} assignees(first:100){nodes{login}} milestone{title}} pageInfo{hasNextPage endCursor}}}}}`;
|
|
172
|
+
|
|
173
|
+
function normalizePullRequest(remote: RemotePullRequest, repository: string): PullRequestSnapshot | null {
|
|
174
|
+
const number = Number(remote.number);
|
|
175
|
+
const headRepository = remote.head?.repo?.full_name;
|
|
176
|
+
const baseRepository = remote.base?.repo?.full_name;
|
|
177
|
+
if (!Number.isInteger(number) || number <= 0 || !remote.node_id || !remote.html_url ||
|
|
178
|
+
!remote.head?.ref || !remote.head.sha || !headRepository || !remote.base?.ref || !baseRepository) return null;
|
|
179
|
+
const mergeabilityDetail = remote.mergeable_state?.trim().toLowerCase() || null;
|
|
180
|
+
const mergeability = remote.mergeable === false
|
|
181
|
+
? { state: 'conflicting' as const, detail: mergeabilityDetail }
|
|
182
|
+
: remote.mergeable === true && mergeabilityDetail !== 'dirty'
|
|
183
|
+
? { state: 'mergeable' as const, detail: mergeabilityDetail }
|
|
184
|
+
: { state: 'unknown' as const, detail: mergeabilityDetail };
|
|
185
|
+
return {
|
|
186
|
+
repository,
|
|
187
|
+
number,
|
|
188
|
+
nodeId: remote.node_id,
|
|
189
|
+
url: remote.html_url,
|
|
190
|
+
state: remote.state === 'closed' ? 'closed' : 'open',
|
|
191
|
+
title: remote.title || '',
|
|
192
|
+
body: remote.body || '',
|
|
193
|
+
draft: Boolean(remote.draft),
|
|
194
|
+
head: { repository: headRepository, ref: remote.head.ref, sha: remote.head.sha },
|
|
195
|
+
base: { repository: baseRepository, ref: remote.base.ref, sha: remote.base.sha || '' },
|
|
196
|
+
mergedAt: remote.merged_at || null,
|
|
197
|
+
mergeCommitSha: remote.merge_commit_sha || null,
|
|
198
|
+
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
199
|
+
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
200
|
+
milestone: remote.milestone?.title || null,
|
|
201
|
+
mergeability
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function normalizeClosingPullRequest(node: ClosingPullRequestNode, repository: string): PullRequestSnapshot | null {
|
|
206
|
+
return normalizePullRequest({
|
|
207
|
+
number: node.number,
|
|
208
|
+
node_id: node.id,
|
|
209
|
+
html_url: node.url,
|
|
210
|
+
state: node.state === 'OPEN' ? 'open' : 'closed',
|
|
211
|
+
title: node.title,
|
|
212
|
+
body: node.body,
|
|
213
|
+
draft: node.isDraft,
|
|
214
|
+
head: { ref: node.headRefName, sha: node.headRefOid, repo: node.headRepository ? { full_name: node.headRepository.nameWithOwner } : null },
|
|
215
|
+
base: { ref: node.baseRefName, sha: node.baseRefOid, repo: node.baseRepository ? { full_name: node.baseRepository.nameWithOwner } : null },
|
|
216
|
+
merged_at: node.mergedAt,
|
|
217
|
+
merge_commit_sha: node.mergeCommit?.oid,
|
|
218
|
+
labels: node.labels?.nodes,
|
|
219
|
+
assignees: node.assignees?.nodes,
|
|
220
|
+
milestone: node.milestone
|
|
221
|
+
}, repository);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function inspectGitHubIssueClosingChangeRequests(
|
|
225
|
+
client: GitHubClient,
|
|
226
|
+
repository: string,
|
|
227
|
+
issueNumber: number,
|
|
228
|
+
cwd: string
|
|
229
|
+
) {
|
|
230
|
+
const [owner, name] = repository.split('/');
|
|
231
|
+
if (!owner || !name || !Number.isInteger(issueNumber) || issueNumber <= 0) return {
|
|
232
|
+
ok: false as const,
|
|
233
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Repository or Issue identity is invalid', retryable: false }
|
|
234
|
+
};
|
|
235
|
+
const candidates: PullRequestSnapshot[] = [];
|
|
236
|
+
let cursor: string | null = null;
|
|
237
|
+
for (;;) {
|
|
238
|
+
const args = [
|
|
239
|
+
'api', 'graphql', '-f', `query=${CLOSING_PULL_REQUESTS_QUERY}`,
|
|
240
|
+
'-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `issue=${issueNumber}`
|
|
241
|
+
];
|
|
242
|
+
if (cursor) args.push('-F', `cursor=${cursor}`);
|
|
243
|
+
const response = client.json<ClosingPullRequestPage>(args, { cwd });
|
|
244
|
+
if (!response.ok) return response;
|
|
245
|
+
const connection = response.value?.data?.repository?.issue?.closedByPullRequestsReferences;
|
|
246
|
+
if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo) return {
|
|
247
|
+
ok: false as const,
|
|
248
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request response is incomplete', retryable: false }
|
|
249
|
+
};
|
|
250
|
+
for (const node of connection.nodes) {
|
|
251
|
+
const normalized = normalizeClosingPullRequest(node, repository);
|
|
252
|
+
if (!normalized) return {
|
|
253
|
+
ok: false as const,
|
|
254
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request identity is incomplete', retryable: false }
|
|
255
|
+
};
|
|
256
|
+
candidates.push(normalized);
|
|
257
|
+
}
|
|
258
|
+
if (!connection.pageInfo.hasNextPage) return { ok: true as const, value: candidates };
|
|
259
|
+
if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor) return {
|
|
260
|
+
ok: false as const,
|
|
261
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request pagination cursor is invalid', retryable: false }
|
|
262
|
+
};
|
|
263
|
+
cursor = connection.pageInfo.endCursor;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function inspectGitHubPullRequest(client: GitHubClient, repository: string, number: number, cwd: string) {
|
|
268
|
+
const fetched = client.json<RemotePullRequest>(['api', `repos/${repository}/pulls/${number}`], { cwd });
|
|
269
|
+
if (!fetched.ok) return fetched;
|
|
270
|
+
const pullRequest = normalizePullRequest(fetched.value, repository);
|
|
271
|
+
return pullRequest
|
|
272
|
+
? { ok: true as const, value: pullRequest }
|
|
273
|
+
: { ok: false as const, error: { code: 'PR_IDENTITY_INVALID', message: 'Remote resource is not a valid pull request', retryable: false } };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function normalizeGitHubChecks(value: unknown): RequiredCheckSnapshot[] {
|
|
277
|
+
if (!Array.isArray(value)) return [];
|
|
278
|
+
return value.flatMap((raw) => {
|
|
279
|
+
const item = raw as Record<string, unknown>;
|
|
280
|
+
const name = String(item.name || item.context || '');
|
|
281
|
+
return name ? [{
|
|
282
|
+
name,
|
|
283
|
+
status: checkStatusBucket(String(item.bucket || item.status || item.conclusion || item.state || '')),
|
|
284
|
+
conclusion: item.conclusion ? String(item.conclusion) : item.state ? String(item.state) : null,
|
|
285
|
+
detailsUrl: item.link ? String(item.link) : item.detailsUrl ? String(item.detailsUrl) : null
|
|
286
|
+
}] : [];
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function inspectGitHubRequiredChecks(client: GitHubClient, repository: string, number: number, cwd: string) {
|
|
291
|
+
const inspected = client.json<unknown>([
|
|
292
|
+
'pr', 'checks', String(number), '--repo', repository,
|
|
293
|
+
'--json', 'name,state,bucket,link,workflow,startedAt,completedAt'
|
|
294
|
+
], { cwd });
|
|
295
|
+
return inspected.ok
|
|
296
|
+
? { ok: true as const, value: normalizeGitHubChecks(inspected.value) }
|
|
297
|
+
: { ok: false as const, error: inspected.error };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function parseRunJobIdentity(detailsUrl: string): { runId: number; jobId: number | null } | null {
|
|
301
|
+
try {
|
|
302
|
+
const url = new URL(detailsUrl);
|
|
303
|
+
if (url.hostname !== 'github.com') return null;
|
|
304
|
+
const match = url.pathname.match(/\/actions\/runs\/(\d+)(?:\/job\/(\d+))?/);
|
|
305
|
+
if (!match) return null;
|
|
306
|
+
return { runId: Number(match[1]), jobId: match[2] ? Number(match[2]) : null };
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function fetchCheckLogText(client: GitHubClient, args: string[], cwd: string) {
|
|
313
|
+
if (!client.text) return { ok: false as const, error: { code: 'PLATFORM_CLIENT_TEXT_UNAVAILABLE', message: 'Platform client does not support text responses', retryable: false } };
|
|
314
|
+
const fetched = client.text(args, { cwd });
|
|
315
|
+
if (fetched.ok || args[0] !== 'api' || !/response contains terminal escape sequences/i.test(fetched.error.message)) {
|
|
316
|
+
return fetched;
|
|
317
|
+
}
|
|
318
|
+
return client.text([...args, '--allow-escape-sequences'], { cwd });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export { ISSUE_FIELDS_QUERY, graphState, inspectGitHubIssue, normalizePullRequest, inspectGitHubIssueClosingChangeRequests, inspectGitHubPullRequest, inspectGitHubRequiredChecks, parseRunJobIdentity, fetchCheckLogText };
|
|
322
|
+
export type { IssueFieldSchema, RemotePullRequest };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ISSUE_FIELDS_QUERY, graphState, inspectGitHubIssue, inspectGitHubPullRequest, inspectGitHubIssueClosingChangeRequests, inspectGitHubRequiredChecks, parseRunJobIdentity, fetchCheckLogText } from './github-data.ts';
|
|
1
2
|
import { execFileSync } from 'node:child_process';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import os from 'node:os';
|
|
@@ -30,7 +31,6 @@ import type {
|
|
|
30
31
|
RepositoryMetadataSnapshot,
|
|
31
32
|
ReleaseSnapshot,
|
|
32
33
|
RemoteCommentSnapshot,
|
|
33
|
-
RequiredCheckSnapshot,
|
|
34
34
|
ReviewSnapshot,
|
|
35
35
|
SecurityAlertKind,
|
|
36
36
|
SecurityAlertSnapshot,
|
|
@@ -41,7 +41,6 @@ import { extractPullRequestFileNames, syncLabelDelta } from './in-label-sync.ts'
|
|
|
41
41
|
|
|
42
42
|
const CURRENT_USER_QUERY = 'query { viewer { login } }';
|
|
43
43
|
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}}}}}}`;
|
|
44
|
-
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}}}}}}}}`;
|
|
45
44
|
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}}}}}';
|
|
46
45
|
|
|
47
46
|
function parseGitHubRemote(remote: string): string | null {
|
|
@@ -465,10 +464,9 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
465
464
|
async inspect({ context, target }) {
|
|
466
465
|
const number = resourceIdentityNumber(target);
|
|
467
466
|
if (!number) return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
|
|
468
|
-
const
|
|
469
|
-
const fetched = module.inspectGitHubIssue(client, repository(context), number, context.workingDirectory);
|
|
467
|
+
const fetched = inspectGitHubIssue(client, repository(context), number, context.workingDirectory);
|
|
470
468
|
if (!fetched.ok) return fetched;
|
|
471
|
-
const graph =
|
|
469
|
+
const graph = graphState(client, repository(context), number, context.workingDirectory);
|
|
472
470
|
return { ok: true, value: issueSnapshot(fetched.value, graph || undefined) };
|
|
473
471
|
},
|
|
474
472
|
async create({ context, desired }) {
|
|
@@ -648,8 +646,7 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
648
646
|
async inspect({ context, target }) {
|
|
649
647
|
const number = resourceIdentityNumber(target);
|
|
650
648
|
if (!number) return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
|
|
651
|
-
const
|
|
652
|
-
const fetched = module.inspectGitHubPullRequest(client, repository(context), number, context.workingDirectory);
|
|
649
|
+
const fetched = inspectGitHubPullRequest(client, repository(context), number, context.workingDirectory);
|
|
653
650
|
if (!fetched.ok) return fetched;
|
|
654
651
|
return { ok: true, value: changeRequestSnapshot(fetched.value) };
|
|
655
652
|
},
|
|
@@ -708,8 +705,7 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
708
705
|
async listClosing({ context, issue }) {
|
|
709
706
|
const number = resourceIdentityNumber(issue);
|
|
710
707
|
if (!number) return invalid('ISSUE_NUMBER_INVALID', 'Issue number must be positive');
|
|
711
|
-
const
|
|
712
|
-
const fetched = module.inspectGitHubIssueClosingChangeRequests(client, repository(context), number, context.workingDirectory);
|
|
708
|
+
const fetched = inspectGitHubIssueClosingChangeRequests(client, repository(context), number, context.workingDirectory);
|
|
713
709
|
if (!fetched.ok) return fetched;
|
|
714
710
|
return { ok: true, value: fetched.value.map(changeRequestSnapshot) };
|
|
715
711
|
},
|
|
@@ -780,8 +776,7 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
780
776
|
async resolveGitEvidence({ context, target, expected }) {
|
|
781
777
|
const number = resourceIdentityNumber(target);
|
|
782
778
|
if (!number) return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
|
|
783
|
-
|
|
784
|
-
return module.resolveGitHubChangeRequestGitEvidence({
|
|
779
|
+
return resolveGitHubChangeRequestGitEvidence({
|
|
785
780
|
cwd: context.workingDirectory,
|
|
786
781
|
repository: repository(context),
|
|
787
782
|
number,
|
|
@@ -795,16 +790,12 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
795
790
|
async inspectRequired({ context, changeRequest }) {
|
|
796
791
|
const number = resourceIdentityNumber(changeRequest);
|
|
797
792
|
if (!number) return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
|
|
798
|
-
|
|
799
|
-
const fetched = module.inspectGitHubRequiredChecks(client, repository(context), number, context.workingDirectory);
|
|
800
|
-
if (!fetched.ok) return fetched;
|
|
801
|
-
return { ok: true, value: fetched.value.map((check: any): RequiredCheckSnapshot => ({ name: check.name, status: check.bucket, conclusion: check.conclusion, detailsUrl: check.detailsUrl })) };
|
|
793
|
+
return inspectGitHubRequiredChecks(client, repository(context), number, context.workingDirectory);
|
|
802
794
|
},
|
|
803
795
|
async resolveRun({ context, changeRequest, checkName, detailsUrl }) {
|
|
804
796
|
const number = resourceIdentityNumber(changeRequest);
|
|
805
797
|
if (!number) return invalid('PR_NUMBER_INVALID', 'Pull request number must be positive');
|
|
806
|
-
const
|
|
807
|
-
const direct = detailsUrl ? module.parseRunJobIdentity(detailsUrl) : null;
|
|
798
|
+
const direct = detailsUrl ? parseRunJobIdentity(detailsUrl) : null;
|
|
808
799
|
if (direct) {
|
|
809
800
|
const run = client.json<any>(['api', `repos/${repository(context)}/actions/runs/${direct.runId}`], { cwd: context.workingDirectory });
|
|
810
801
|
if (!run.ok) return run;
|
|
@@ -818,11 +809,10 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
818
809
|
: invalid('CHECK_RUN_NOT_FOUND', 'Check run was not found');
|
|
819
810
|
},
|
|
820
811
|
async fetchLogs({ context, runId, jobId }) {
|
|
821
|
-
const module = await import('./pr-checks.ts');
|
|
822
812
|
const args = jobId
|
|
823
813
|
? ['api', `repos/${repository(context)}/actions/jobs/${jobId}/logs`]
|
|
824
814
|
: ['run', 'view', runId, '--repo', repository(context), '--log-failed'];
|
|
825
|
-
const fetched =
|
|
815
|
+
const fetched = fetchCheckLogText(client, args, context.workingDirectory);
|
|
826
816
|
if (!fetched.ok) return fetched;
|
|
827
817
|
return { ok: true, value: { runId, jobId, text: fetched.value || '' } } as ProviderResult<CheckLogSnapshot>;
|
|
828
818
|
}
|
|
@@ -891,7 +881,12 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
891
881
|
}
|
|
892
882
|
continue;
|
|
893
883
|
}
|
|
894
|
-
const response = client.json([
|
|
884
|
+
const response = client.json([
|
|
885
|
+
'api', '--method', 'POST', `repos/${repository(context)}/milestones`,
|
|
886
|
+
'-f', `title=${item.title}`,
|
|
887
|
+
'-f', `description=${item.description}`,
|
|
888
|
+
'-f', `state=${item.state}`
|
|
889
|
+
], { cwd: context.workingDirectory });
|
|
895
890
|
if (!response.ok) return response;
|
|
896
891
|
created.push(item.title);
|
|
897
892
|
}
|
|
@@ -17,11 +17,10 @@ import {
|
|
|
17
17
|
import { resourceIdentityNumber } from './resource-identity.ts';
|
|
18
18
|
import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.ts';
|
|
19
19
|
import {
|
|
20
|
-
|
|
20
|
+
canonicalizeCommentBody,
|
|
21
21
|
escapeHtmlText,
|
|
22
22
|
fenceRanges,
|
|
23
|
-
renderSafeCodeFence
|
|
24
|
-
sanitizeMarkdownDocument
|
|
23
|
+
renderSafeCodeFence
|
|
25
24
|
} from './comment-safety.ts';
|
|
26
25
|
import type { FenceRange } from './comment-safety.ts';
|
|
27
26
|
|
|
@@ -51,6 +50,7 @@ const MARKERS = {
|
|
|
51
50
|
summary: (taskId: string) => `<!-- sync-issue:${taskId}:summary -->`,
|
|
52
51
|
cancel: (taskId: string) => `<!-- sync-issue:${taskId}:cancel -->`
|
|
53
52
|
};
|
|
53
|
+
const COMMENT_BYTE_LIMIT = 60_000;
|
|
54
54
|
|
|
55
55
|
const ARTIFACT_TITLES: Record<string, string> = {
|
|
56
56
|
analysis: '需求分析',
|
|
@@ -68,6 +68,10 @@ function normalizeCommentContent(content: string): string {
|
|
|
68
68
|
return content.replace(/\r\n/g, '\n').replace(/\n+$/, '\n');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function isTaskCommentTooLarge(content: string): boolean {
|
|
72
|
+
return Buffer.byteLength(content, 'utf8') > COMMENT_BYTE_LIMIT;
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
function splitFrontmatter(content: string): { frontmatter: string | null; body: string } {
|
|
72
76
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
73
77
|
if (!match) return { frontmatter: null, body: content };
|
|
@@ -75,7 +79,7 @@ function splitFrontmatter(content: string): { frontmatter: string | null; body:
|
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
function sanitizeCommentBody(value: string, label: string): string {
|
|
78
|
-
const result =
|
|
82
|
+
const result = canonicalizeCommentBody(value);
|
|
79
83
|
if (!result.ok) throw new Error(`${result.error.code}: ${label}: ${result.error.message} at offset ${result.error.offset}`);
|
|
80
84
|
return result.value;
|
|
81
85
|
}
|
|
@@ -270,7 +274,7 @@ function chunkArtifactComment(input: {
|
|
|
270
274
|
byteLimit?: number;
|
|
271
275
|
backfill?: boolean;
|
|
272
276
|
}): RenderedChunk[] {
|
|
273
|
-
const byteLimit = input.byteLimit ||
|
|
277
|
+
const byteLimit = input.byteLimit || COMMENT_BYTE_LIMIT;
|
|
274
278
|
const identity = artifactIdentity(input.artifact);
|
|
275
279
|
const safeBody = sanitizeCommentBody(input.body, 'artifact body');
|
|
276
280
|
const single = buildArtifactChunk(
|
|
@@ -324,12 +328,6 @@ function flattenComments(value: unknown): RemoteComment[] {
|
|
|
324
328
|
);
|
|
325
329
|
}
|
|
326
330
|
|
|
327
|
-
function issueNumberFromTask(content: string): number | null {
|
|
328
|
-
const value = parseTaskFrontmatter(content).issue_number;
|
|
329
|
-
const number = Number(value);
|
|
330
|
-
return Number.isInteger(number) && number > 0 ? number : null;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
331
|
function listRemoteComments(client: PlatformClient, repo: string, issue: number, cwd: string) {
|
|
334
332
|
const result = client.json<unknown>([
|
|
335
333
|
'api', '--paginate', '--slurp', `repos/${repo}/issues/${issue}/comments?per_page=100`
|
|
@@ -453,11 +451,18 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
453
451
|
}
|
|
454
452
|
const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
455
453
|
let issueIdentityFromTask: ReturnType<typeof taskIssueIdentity>;
|
|
456
|
-
try { issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent)
|
|
454
|
+
try { issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent)); }
|
|
457
455
|
catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
|
|
458
456
|
if (!issueIdentityFromTask) {
|
|
459
457
|
return platformResult('no-op', {
|
|
460
|
-
error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid
|
|
458
|
+
error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false }
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
if (options.kind === 'task' && isTaskCommentTooLarge(taskContent)) {
|
|
462
|
+
return platformResult('no-op', {
|
|
463
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask), identity: issueIdentityFromTask },
|
|
464
|
+
operations: [{ name: `comment:${MARKERS.task(resolved.taskId)}`, status: 'skipped', reasonCode: 'COMMENT_PAYLOAD_TOO_LARGE' }],
|
|
465
|
+
error: null
|
|
461
466
|
});
|
|
462
467
|
}
|
|
463
468
|
let desired: RenderedChunk[];
|
|
@@ -615,7 +620,7 @@ async function checkPlatformCommentOwner(taskRef: string, options: { cwd?: strin
|
|
|
615
620
|
if (!resolved.ok) return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
|
|
616
621
|
const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
617
622
|
let issueIdentity: ReturnType<typeof taskIssueIdentity>;
|
|
618
|
-
try { issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content)
|
|
623
|
+
try { issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content)); }
|
|
619
624
|
catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
|
|
620
625
|
if (!issueIdentity) return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false } });
|
|
621
626
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
@@ -641,6 +646,7 @@ async function checkPlatformCommentOwner(taskRef: string, options: { cwd?: strin
|
|
|
641
646
|
}
|
|
642
647
|
|
|
643
648
|
export {
|
|
649
|
+
COMMENT_BYTE_LIMIT,
|
|
644
650
|
MARKERS,
|
|
645
651
|
chunkArtifactComment,
|
|
646
652
|
findMarkerComments,
|
|
@@ -649,6 +655,7 @@ export {
|
|
|
649
655
|
listPlatformComments,
|
|
650
656
|
normalizeCommentContent,
|
|
651
657
|
renderTaskComment,
|
|
658
|
+
isTaskCommentTooLarge,
|
|
652
659
|
syncPlatformComment,
|
|
653
660
|
validateRelatedMarkerSet,
|
|
654
661
|
writeComment
|