@fitlab-ai/agent-infra 0.11.9 → 0.11.10
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/dist/bin/internal-cli.js +0 -6
- package/dist/lib/git/workflow.js +1 -1
- package/dist/lib/internal/cli-route-inventory.js +0 -3
- package/dist/lib/internal/sandbox-control.js +30 -5
- package/dist/lib/internal/task-finalization.js +14 -23
- package/dist/lib/internal/task-operation-registry.js +2 -3
- package/dist/lib/internal/task-platform-recovery.js +9 -4
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/context.js +4 -2
- package/dist/lib/platform/github-client.js +20 -17
- package/dist/lib/platform/github-provider.js +42 -22
- package/dist/lib/platform/github-release-notes.js +4 -2
- package/dist/lib/platform/in-label-sync.js +2 -1
- package/dist/lib/platform/issue-comments.js +13 -9
- package/dist/lib/platform/operation-coordinator.js +1 -1
- package/dist/lib/platform/pr-review.js +2 -1
- package/dist/lib/platform/provider-bridge.js +4 -5
- package/dist/lib/platform/provider-contract.d.ts +3 -6
- package/dist/lib/platform/provider-contract.js +3 -15
- package/dist/lib/platform/provider-validation.js +9 -4
- package/dist/lib/platform/pull-requests.js +4 -2
- package/dist/lib/sandbox/control/client.js +54 -6
- package/dist/lib/sandbox/control/executor.js +8 -3
- package/dist/lib/sandbox/removal.js +0 -1
- package/dist/lib/task/artifact-command.js +11 -1
- package/dist/lib/task/artifact-lifecycle.js +119 -62
- package/dist/lib/task/artifact-selection.js +1 -1
- package/dist/lib/task/capabilities.js +55 -21
- package/dist/lib/task/commands/status.js +0 -27
- package/dist/lib/task/commit-identity.js +22 -7
- package/dist/lib/task/commit-intent.js +67 -26
- package/dist/lib/task/commit-operation.js +19 -8
- package/dist/lib/task/completion-facts.js +32 -10
- package/dist/lib/task/control-authority.js +2 -4
- package/dist/lib/task/current-contract.js +1 -5
- package/dist/lib/task/events.js +8 -274
- package/dist/lib/task/execution-inputs.js +14 -0
- package/dist/lib/task/finalization-envelope.js +4 -0
- package/dist/lib/task/finalization.js +10 -36
- package/dist/lib/task/frontmatter.js +1 -1
- package/dist/lib/task/ledger-intents.js +4 -7
- package/dist/lib/task/orchestration.js +11 -10
- package/dist/lib/task/platform-operation-journal.js +17 -10
- package/dist/lib/task/platform-operation-recovery.js +95 -28
- package/dist/lib/task/review-fingerprint.js +4 -26
- package/dist/lib/task/verification-engine.js +95 -6
- package/dist/lib/task/write.js +0 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/git/workflow.ts +1 -1
- package/lib/internal/cli-route-inventory.ts +0 -2
- package/lib/internal/sandbox-control.ts +39 -6
- package/lib/internal/task-finalization.ts +16 -24
- package/lib/internal/task-operation-registry.ts +2 -3
- package/lib/internal/task-platform-recovery.ts +9 -4
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/context.ts +8 -4
- package/lib/platform/github-client.ts +24 -20
- package/lib/platform/github-provider.ts +47 -26
- package/lib/platform/github-release-notes.ts +5 -3
- package/lib/platform/in-label-sync.ts +2 -1
- package/lib/platform/issue-comments.ts +14 -9
- package/lib/platform/operation-coordinator.ts +1 -1
- package/lib/platform/pr-review.ts +2 -1
- package/lib/platform/provider-bridge.ts +10 -9
- package/lib/platform/provider-contract.ts +6 -21
- package/lib/platform/provider-loader.ts +7 -6
- package/lib/platform/provider-validation.ts +30 -8
- package/lib/platform/pull-requests.ts +4 -2
- package/lib/platform/repository-metadata.ts +3 -2
- package/lib/platform/security-alerts.ts +1 -1
- package/lib/platform/types.ts +2 -0
- package/lib/sandbox/control/client.ts +57 -6
- package/lib/sandbox/control/executor.ts +8 -3
- package/lib/sandbox/removal.ts +2 -10
- package/lib/task/artifact-command.ts +10 -1
- package/lib/task/artifact-lifecycle.ts +100 -52
- package/lib/task/artifact-selection.ts +1 -1
- package/lib/task/capabilities.ts +47 -24
- package/lib/task/commands/status.ts +0 -31
- package/lib/task/commit-identity.ts +24 -7
- package/lib/task/commit-intent.ts +58 -24
- package/lib/task/commit-operation.ts +17 -8
- package/lib/task/completion-facts.ts +31 -9
- package/lib/task/control-authority.ts +1 -4
- package/lib/task/current-contract.ts +1 -6
- package/lib/task/events.ts +8 -247
- package/lib/task/execution-inputs.ts +25 -0
- package/lib/task/finalization-envelope.ts +21 -0
- package/lib/task/finalization.ts +10 -35
- package/lib/task/frontmatter.ts +1 -1
- package/lib/task/ledger-intents.ts +4 -7
- package/lib/task/orchestration.ts +11 -17
- package/lib/task/platform-operation-journal.ts +18 -11
- package/lib/task/platform-operation-recovery.ts +90 -31
- package/lib/task/review-fingerprint.ts +4 -26
- package/lib/task/verification-engine.ts +103 -9
- package/lib/task/write.ts +0 -19
- package/package.json +1 -1
- package/templates/.agents/rules/commit-and-pr.en.md +5 -5
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +5 -5
- package/templates/.agents/rules/decision-qualification.en.md +1 -1
- package/templates/.agents/rules/decision-qualification.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +25 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +26 -9
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +5 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +5 -16
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -12
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -10
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +0 -12
- package/templates/.github/workflows/pr-label.yml +1 -2
- package/dist/lib/internal/task-invalidation.js +0 -63
- package/dist/lib/task/intermediate-cleanup.js +0 -327
- package/dist/lib/task/invalidation-command.js +0 -88
- package/dist/lib/task/invalidation.js +0 -242
- package/lib/internal/task-invalidation.ts +0 -41
- package/lib/task/intermediate-cleanup.ts +0 -393
- package/lib/task/invalidation-command.ts +0 -88
- package/lib/task/invalidation.ts +0 -313
package/dist/bin/internal-cli.js
CHANGED
|
@@ -13,7 +13,6 @@ const taskWorkflowCommand = command === 'task-artifact'
|
|
|
13
13
|
|| command === 'task-review'
|
|
14
14
|
|| command === 'task-event'
|
|
15
15
|
|| command === 'task-ledger'
|
|
16
|
-
|| command === 'task-invalidation'
|
|
17
16
|
|| command === 'task-warning';
|
|
18
17
|
const taskPlatformRecoveryCommand = command === 'task-platform-recovery';
|
|
19
18
|
const manualValidationWorkflowCommand = command === 'manual-validation';
|
|
@@ -231,11 +230,6 @@ if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
|
|
|
231
230
|
await taskEvent(process.argv.slice(3));
|
|
232
231
|
break;
|
|
233
232
|
}
|
|
234
|
-
case 'task-invalidation': {
|
|
235
|
-
const { taskInvalidation } = await import('../lib/internal/task-invalidation.js');
|
|
236
|
-
taskInvalidation(process.argv.slice(3));
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
233
|
case 'task-lifecycle': {
|
|
240
234
|
const { taskLifecycle } = await import('../lib/internal/task-lifecycle.js');
|
|
241
235
|
await taskLifecycle(process.argv.slice(3));
|
package/dist/lib/git/workflow.js
CHANGED
|
@@ -103,7 +103,7 @@ function commitExplicitPaths(input, runner = defaultRunner) {
|
|
|
103
103
|
if (tree !== input.expectedTree)
|
|
104
104
|
return { status: 'failed', changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'applied' }], error: { code: 'GIT_TREE_MISMATCH', message: `Expected staged tree ${input.expectedTree}, received ${tree ?? 'unavailable'}` } };
|
|
105
105
|
}
|
|
106
|
-
const commit = run(runner, input.cwd, ['commit', '-m', input.message]);
|
|
106
|
+
const commit = run(runner, input.cwd, ['commit', '--cleanup=verbatim', '-m', input.message]);
|
|
107
107
|
const after = inspectGitWorkflow(input.cwd, runner);
|
|
108
108
|
if (commit.status !== 0)
|
|
109
109
|
return { status: 'failed', changed: false, snapshot: after.snapshot, operations: [{ name: 'commit', status: 'failed', message: commit.stderr.trim() }], error: { code: 'GIT_COMMIT_FAILED', message: commit.stderr.trim() } };
|
|
@@ -57,7 +57,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
57
57
|
'task-orchestration': ['status', 'progress'],
|
|
58
58
|
'task-review': ['preflight', 'finalize-summary'],
|
|
59
59
|
'task-event': ['event'],
|
|
60
|
-
'task-invalidation': ['reconcile'],
|
|
61
60
|
'task-lifecycle': ['intent'],
|
|
62
61
|
'task-finalization': ['complete'],
|
|
63
62
|
'task-platform-recovery': ['inspect', 'recover'],
|
|
@@ -105,8 +104,6 @@ export function internalRouteSelector(command, args) {
|
|
|
105
104
|
return args[1] === 'status' ? 'status' : args[1] ? 'progress' : '';
|
|
106
105
|
if (command === 'task-review')
|
|
107
106
|
return args[1] ?? '';
|
|
108
|
-
if (command === 'task-invalidation')
|
|
109
|
-
return args[1] ?? '';
|
|
110
107
|
if (command === 'task-artifact')
|
|
111
108
|
return args[1] ?? '';
|
|
112
109
|
if (command === 'platform-pr-review' && first(args) === 'publish') {
|
|
@@ -4,8 +4,9 @@ import { serveSandboxControl } from '../sandbox/control/server.js';
|
|
|
4
4
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
|
|
5
5
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
6
6
|
import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
|
+
function writeFinalizationEnvelope(status, accepted, error, changed = false, result = null, requestId = null) {
|
|
9
|
+
process.stdout.write(serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error }));
|
|
9
10
|
}
|
|
10
11
|
function finalizationErrorStatus(error) {
|
|
11
12
|
if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN')
|
|
@@ -30,6 +31,28 @@ function recoveredExitCode(response) {
|
|
|
30
31
|
}
|
|
31
32
|
return response.exitCode ?? 1;
|
|
32
33
|
}
|
|
34
|
+
function finalizationResponseOutput(response) {
|
|
35
|
+
try {
|
|
36
|
+
const value = JSON.parse(response.stdout);
|
|
37
|
+
if (value.version === 2
|
|
38
|
+
&& value.status
|
|
39
|
+
&& ['completed', 'failed', 'blocked', 'unknown'].includes(value.status)
|
|
40
|
+
&& (typeof value.changed === 'boolean' || value.changed === null)) {
|
|
41
|
+
return serializeTaskFinalizationEnvelope({
|
|
42
|
+
status: value.status,
|
|
43
|
+
changed: value.changed,
|
|
44
|
+
accepted: true,
|
|
45
|
+
requestId: response.id,
|
|
46
|
+
result: value.result ?? null,
|
|
47
|
+
error: value.error ?? null
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Preserve non-envelope output for diagnostics from the executor.
|
|
53
|
+
}
|
|
54
|
+
return response.stdout;
|
|
55
|
+
}
|
|
33
56
|
function sandboxFinalizationClient(args) {
|
|
34
57
|
if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
|
|
35
58
|
const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
|
|
@@ -52,7 +75,8 @@ function sandboxFinalizationClient(args) {
|
|
|
52
75
|
catch (error) {
|
|
53
76
|
if (!(error instanceof SandboxControlClientError))
|
|
54
77
|
throw error;
|
|
55
|
-
|
|
78
|
+
const status = finalizationErrorStatus(error.detail);
|
|
79
|
+
writeFinalizationEnvelope(status, error.accepted, error.detail, status === 'unknown' ? null : false, null, error.requestId);
|
|
56
80
|
writeClientError(error);
|
|
57
81
|
process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
|
|
58
82
|
return;
|
|
@@ -63,12 +87,13 @@ function sandboxFinalizationClient(args) {
|
|
|
63
87
|
message: 'sandbox control rejected the finalization request',
|
|
64
88
|
retryable: false
|
|
65
89
|
};
|
|
66
|
-
|
|
90
|
+
const status = finalizationErrorStatus(error);
|
|
91
|
+
writeFinalizationEnvelope(status, error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error, status === 'unknown' ? null : false, null, response.id);
|
|
67
92
|
process.stderr.write(`${error.message}\n`);
|
|
68
93
|
process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
|
|
69
94
|
return;
|
|
70
95
|
}
|
|
71
|
-
process.stdout.write(response
|
|
96
|
+
process.stdout.write(finalizationResponseOutput(response));
|
|
72
97
|
process.stderr.write(response.stderr);
|
|
73
98
|
process.exitCode = response.exitCode ?? 1;
|
|
74
99
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
|
|
2
|
-
import { applyTaskFinalization
|
|
3
|
-
import { publishTaskFinalizationHandoff } from '../task/finalization-handoff.js';
|
|
2
|
+
import { applyTaskFinalization } from '../task/finalization.js';
|
|
4
3
|
import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
5
|
-
import { verifyTaskEvent } from '../task/verification.js';
|
|
6
4
|
import { resolveSandboxControlTransport } from './task-operation-registry.js';
|
|
7
5
|
import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
|
|
6
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
7
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
9
8
|
const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
|
|
10
|
-
function envelope(status, changed, accepted, result, error) {
|
|
11
|
-
return
|
|
9
|
+
function envelope(status, changed, accepted, result, error, requestId = null) {
|
|
10
|
+
return serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error });
|
|
12
11
|
}
|
|
13
12
|
function exitCode(status) {
|
|
14
13
|
return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
|
|
@@ -65,22 +64,9 @@ async function taskFinalization(args = []) {
|
|
|
65
64
|
};
|
|
66
65
|
const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
|
|
67
66
|
if (transport.kind === 'sandbox-local') {
|
|
68
|
-
const prepared = await prepareTaskFinalization(boundOperation.request, {
|
|
69
|
-
repoRoot,
|
|
70
|
-
preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
|
|
71
|
-
});
|
|
72
|
-
if (prepared.status !== 'prepared') {
|
|
73
|
-
process.stdout.write(envelope(prepared.status, prepared.changed, true, prepared, prepared.error));
|
|
74
|
-
process.exitCode = exitCode(prepared.status);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
67
|
try {
|
|
78
68
|
const response = requestSandboxTaskFinalization({
|
|
79
|
-
agent: boundOperation.request.agent
|
|
80
|
-
prepareHandoff: (binding) => {
|
|
81
|
-
const receipt = bindTaskFinalizationReceipt(repoRoot, boundOperation.request.taskRef, binding);
|
|
82
|
-
return publishTaskFinalizationHandoff('/share/branch', receipt, binding);
|
|
83
|
-
}
|
|
69
|
+
agent: boundOperation.request.agent
|
|
84
70
|
});
|
|
85
71
|
process.stdout.write(response.stdout);
|
|
86
72
|
process.stderr.write(response.stderr);
|
|
@@ -88,11 +74,16 @@ async function taskFinalization(args = []) {
|
|
|
88
74
|
return;
|
|
89
75
|
}
|
|
90
76
|
catch (error) {
|
|
91
|
-
const
|
|
92
|
-
|
|
77
|
+
const clientError = error instanceof SandboxControlClientError ? error : null;
|
|
78
|
+
const detail = clientError
|
|
79
|
+
? clientError.detail
|
|
93
80
|
: { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
const status = detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
82
|
+
? 'unknown'
|
|
83
|
+
: detail.retryable ? 'blocked' : 'failed';
|
|
84
|
+
const changed = status === 'unknown' ? null : false;
|
|
85
|
+
process.stdout.write(envelope(status, changed, clientError?.accepted ?? false, null, detail, clientError?.requestId ?? null));
|
|
86
|
+
process.exitCode = exitCode(status);
|
|
96
87
|
return;
|
|
97
88
|
}
|
|
98
89
|
}
|
|
@@ -102,9 +102,8 @@ function internalTaskRoutes() {
|
|
|
102
102
|
descriptor('internal', 'task-review', 'preflight', 'task-bound', 'artifact-write'),
|
|
103
103
|
descriptor('internal', 'task-review', 'finalize-summary', 'task-bound', 'progress'),
|
|
104
104
|
descriptor('internal', 'task-event', 'event', 'task-bound', 'progress'),
|
|
105
|
-
descriptor('internal', 'task-invalidation', 'reconcile', 'task-bound', 'progress'),
|
|
106
105
|
descriptor('internal', 'task-lifecycle', 'intent', 'task-bound', 'progress'),
|
|
107
|
-
descriptor('internal', 'task-finalization', 'complete', 'task-bound', '
|
|
106
|
+
descriptor('internal', 'task-finalization', 'complete', 'task-bound', 'recovery'),
|
|
108
107
|
descriptor('internal', 'task-platform-recovery', 'inspect', 'task-bound', 'diagnostic'),
|
|
109
108
|
descriptor('internal', 'task-platform-recovery', 'recover', 'task-bound', 'recovery'),
|
|
110
109
|
descriptor('internal', 'task-short-id', 'list', 'conditional', 'diagnostic'),
|
|
@@ -179,7 +178,7 @@ export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
|
179
178
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
180
179
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
181
180
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
182
|
-
'task-orchestration', 'task-review', 'task-event', 'task-
|
|
181
|
+
'task-orchestration', 'task-review', 'task-event', 'task-lifecycle',
|
|
183
182
|
'task-finalization', 'task-platform-recovery', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
|
|
184
183
|
]);
|
|
185
184
|
export const PUBLIC_DISPATCHER_ROUTES = Object.freeze([
|
|
@@ -2,7 +2,7 @@ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
|
2
2
|
import { readPlatformOperationJournal } from '../task/platform-operation-journal.js';
|
|
3
3
|
import { recoverPlatformOperations } from '../task/platform-operation-recovery.js';
|
|
4
4
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
5
|
-
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all]\n';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all] [--attempts <positive-integer>]\n';
|
|
6
6
|
function option(args, name) {
|
|
7
7
|
const index = args.indexOf(name);
|
|
8
8
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -41,14 +41,19 @@ async function taskPlatformRecovery(args = []) {
|
|
|
41
41
|
}
|
|
42
42
|
const agent = option(args, '--agent');
|
|
43
43
|
const selection = option(args, '--selection') ?? 'all';
|
|
44
|
+
const attemptsInput = option(args, '--attempts');
|
|
45
|
+
const attempts = attemptsInput === undefined ? undefined : Number(attemptsInput);
|
|
46
|
+
const validAttempts = attemptsInput === undefined
|
|
47
|
+
|| (attempts !== undefined && Number.isSafeInteger(attempts) && attempts > 0);
|
|
44
48
|
if (!agent || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(agent)
|
|
45
|
-
|| !['required', 'deferred', 'all'].includes(selection)
|
|
46
|
-
|
|
49
|
+
|| !['required', 'deferred', 'all'].includes(selection)
|
|
50
|
+
|| !validAttempts) {
|
|
51
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'PLATFORM_OPERATION_RECOVERY_PAYLOAD_INVALID', message: 'recover requires a valid --agent, --selection, and positive --attempts when supplied' } })}\n`);
|
|
47
52
|
process.stderr.write(USAGE);
|
|
48
53
|
process.exitCode = 1;
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
|
-
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot });
|
|
56
|
+
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot, ...(attempts === undefined ? {} : { attempts }) });
|
|
52
57
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
53
58
|
process.exitCode = result.status === 'applied' || result.status === 'no-op' ? 0 : result.status === 'blocked' ? 2 : 1;
|
|
54
59
|
}
|
|
@@ -8,6 +8,7 @@ import { getOpenWorkflowWarnings } from '../task/workflow-warnings.js';
|
|
|
8
8
|
import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js';
|
|
9
9
|
import { captureTaskWriteMetadata } from '../task/write.js';
|
|
10
10
|
import { syncPlatformComment } from './issue-comments.js';
|
|
11
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
11
12
|
import { platformResult } from './types.js';
|
|
12
13
|
import { taskIssueIdentity } from './task-identities.js';
|
|
13
14
|
function result(base, artifacts, warnings) {
|
|
@@ -124,7 +125,7 @@ async function backfillCompletionComments(taskRef, options) {
|
|
|
124
125
|
if (latest.status === 'failed' || latest.status === 'blocked')
|
|
125
126
|
return result({ ...latest, operations }, artifacts, []);
|
|
126
127
|
if (latest.error)
|
|
127
|
-
return result(platformResult(latest.error.retryable ? 'blocked' : 'failed', {
|
|
128
|
+
return result(platformResult(latest.error.retryable || providerErrorRetryable(latest.error.code) ? 'blocked' : 'failed', {
|
|
128
129
|
platform: latest.platform,
|
|
129
130
|
resource: latest.resource,
|
|
130
131
|
capabilities: latest.capabilities,
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { loadPlatformProvider } from './provider-loader.js';
|
|
3
3
|
import { defaultGitRemote, parseGitHubRemote } from './github-provider.js';
|
|
4
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
4
5
|
import { platformResult } from './types.js';
|
|
5
6
|
function errorStatus(error) {
|
|
6
|
-
if (error.retryable || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
|
+
if (error.retryable || providerErrorRetryable(error.code) || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
8
|
return 'blocked';
|
|
8
9
|
if (error.code === 'REMOTE_MISSING' || error.code === 'PLATFORM_UNSUPPORTED')
|
|
9
10
|
return 'no-op';
|
|
10
11
|
return 'failed';
|
|
11
12
|
}
|
|
12
13
|
function contextError(providerType, error, repository = null) {
|
|
14
|
+
const retryable = error.retryable ?? providerErrorRetryable(error.code);
|
|
13
15
|
return platformResult(errorStatus(error), {
|
|
14
16
|
platform: { type: providerType || null, repository, currentUser: null },
|
|
15
|
-
error: { code: error.code, message: error.message, retryable
|
|
17
|
+
error: { code: error.code, message: error.message, retryable }
|
|
16
18
|
});
|
|
17
19
|
}
|
|
18
20
|
function contextFromSnapshot(providerType, snapshot) {
|
|
@@ -59,30 +59,33 @@ function classifyGitHubFailure(result) {
|
|
|
59
59
|
return {
|
|
60
60
|
code: 'PLATFORM_OUTPUT_TOO_LARGE',
|
|
61
61
|
message: 'GitHub CLI output exceeded the configured limit',
|
|
62
|
-
|
|
62
|
+
requestRetryable: false
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
const detail = boundedFailureDetail(result);
|
|
66
66
|
const lower = detail.toLowerCase();
|
|
67
67
|
if (/\b401\b|bad credentials|authentication required|not logged into/.test(lower)) {
|
|
68
|
-
return { code: 'AUTH_REQUIRED', message: detail || 'GitHub authentication is required',
|
|
68
|
+
return { code: 'AUTH_REQUIRED', message: detail || 'GitHub authentication is required', requestRetryable: false };
|
|
69
69
|
}
|
|
70
70
|
if (/\b429\b|rate limit|secondary rate|\b5\d\d\b|timeout|timed out|econnreset|enotfound|dns|tls|socket|network/.test(lower)) {
|
|
71
|
-
return { code: 'NETWORK_TRANSIENT', message: detail || 'GitHub request failed temporarily',
|
|
71
|
+
return { code: 'NETWORK_TRANSIENT', message: detail || 'GitHub request failed temporarily', requestRetryable: true };
|
|
72
72
|
}
|
|
73
73
|
if (/\b403\b|resource not accessible|permission denied/.test(lower)) {
|
|
74
|
-
return { code: 'PERMISSION_DENIED', message: detail || 'GitHub permission denied',
|
|
74
|
+
return { code: 'PERMISSION_DENIED', message: detail || 'GitHub permission denied', requestRetryable: false };
|
|
75
75
|
}
|
|
76
76
|
if (/\b404\b/.test(lower)) {
|
|
77
|
-
return { code: 'RESOURCE_NOT_FOUND', message: detail || 'GitHub resource not found',
|
|
77
|
+
return { code: 'RESOURCE_NOT_FOUND', message: detail || 'GitHub resource not found', requestRetryable: false };
|
|
78
78
|
}
|
|
79
79
|
if (/\b422\b|validation failed/.test(lower)) {
|
|
80
|
-
return { code: 'PLATFORM_REQUEST_INVALID', message: detail || 'GitHub rejected the request',
|
|
80
|
+
return { code: 'PLATFORM_REQUEST_INVALID', message: detail || 'GitHub rejected the request', requestRetryable: false };
|
|
81
81
|
}
|
|
82
82
|
if (errorCode === 'ENOENT') {
|
|
83
|
-
return { code: 'PLATFORM_DEPENDENCY_MISSING', message: detail || 'GitHub CLI is unavailable',
|
|
83
|
+
return { code: 'PLATFORM_DEPENDENCY_MISSING', message: detail || 'GitHub CLI is unavailable', requestRetryable: false };
|
|
84
84
|
}
|
|
85
|
-
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed',
|
|
85
|
+
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', requestRetryable: false };
|
|
86
|
+
}
|
|
87
|
+
function clientError(error) {
|
|
88
|
+
return { code: error.code, message: error.message };
|
|
86
89
|
}
|
|
87
90
|
function responseUrl(args) {
|
|
88
91
|
const endpoint = args.find((arg, index) => index > 0 && !arg.startsWith('-')) || '';
|
|
@@ -132,14 +135,14 @@ function createGitHubClient(options = {}) {
|
|
|
132
135
|
if (result.status === 0)
|
|
133
136
|
return { ok: true, value: result.stdout };
|
|
134
137
|
const error = classifyGitHubFailure(result);
|
|
135
|
-
if (!retryableMethod || !error.
|
|
136
|
-
return { ok: false, error };
|
|
138
|
+
if (!retryableMethod || !error.requestRetryable)
|
|
139
|
+
return { ok: false, error: clientError(error) };
|
|
137
140
|
if (attempt >= delays.length) {
|
|
138
141
|
return {
|
|
139
142
|
ok: false,
|
|
140
143
|
error: error.code === 'NETWORK_TRANSIENT'
|
|
141
|
-
? { ...error, code: 'NETWORK_RETRY_EXHAUSTED', message: `${error.message} (retry exhausted)` }
|
|
142
|
-
: error
|
|
144
|
+
? { ...clientError(error), code: 'NETWORK_RETRY_EXHAUSTED', message: `${error.message} (retry exhausted)` }
|
|
145
|
+
: clientError(error)
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
sleep(delays[attempt]);
|
|
@@ -150,12 +153,12 @@ function createGitHubClient(options = {}) {
|
|
|
150
153
|
version(request = {}) {
|
|
151
154
|
const result = runner(['--version'], request);
|
|
152
155
|
if (result.status !== 0)
|
|
153
|
-
return { ok: false, error: classifyGitHubFailure(result) };
|
|
156
|
+
return { ok: false, error: clientError(classifyGitHubFailure(result)) };
|
|
154
157
|
const version = result.stdout.match(/^gh version (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?:\s|$)/m)?.[1];
|
|
155
158
|
if (!version || !semver.valid(version)) {
|
|
156
159
|
return {
|
|
157
160
|
ok: false,
|
|
158
|
-
error: { code: 'GH_CLI_VERSION_INVALID', message: 'GitHub CLI returned an invalid version'
|
|
161
|
+
error: { code: 'GH_CLI_VERSION_INVALID', message: 'GitHub CLI returned an invalid version' }
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
return { ok: true, value: version };
|
|
@@ -174,7 +177,7 @@ function createGitHubClient(options = {}) {
|
|
|
174
177
|
catch {
|
|
175
178
|
return {
|
|
176
179
|
ok: false,
|
|
177
|
-
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON'
|
|
180
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON' }
|
|
178
181
|
};
|
|
179
182
|
}
|
|
180
183
|
},
|
|
@@ -189,12 +192,12 @@ function createGitHubClient(options = {}) {
|
|
|
189
192
|
if (!parsed) {
|
|
190
193
|
return {
|
|
191
194
|
ok: false,
|
|
192
|
-
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid'
|
|
195
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid' }
|
|
193
196
|
};
|
|
194
197
|
}
|
|
195
198
|
return { ok: true, value: parsed };
|
|
196
199
|
}
|
|
197
200
|
};
|
|
198
201
|
}
|
|
199
|
-
export {
|
|
202
|
+
export { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION, parseIncludedResponse };
|
|
200
203
|
//# sourceMappingURL=github-client.js.map
|
|
@@ -7,6 +7,32 @@ import semver from 'semver';
|
|
|
7
7
|
import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
|
|
8
8
|
import { resourceIdentityNumber, resourceIdentityString } from './resource-identity.js';
|
|
9
9
|
import { extractPullRequestFileNames, syncLabelDelta } from './in-label-sync.js';
|
|
10
|
+
function providerError(error) {
|
|
11
|
+
return { code: error.code, message: error.message };
|
|
12
|
+
}
|
|
13
|
+
function publicProviderErrors(provider) {
|
|
14
|
+
const sanitize = async (call) => {
|
|
15
|
+
const result = await call();
|
|
16
|
+
if (!result || typeof result !== 'object' || result.ok !== false)
|
|
17
|
+
return result;
|
|
18
|
+
const envelope = result;
|
|
19
|
+
return envelope.error ? { ...result, error: providerError(envelope.error) } : result;
|
|
20
|
+
};
|
|
21
|
+
const wrapped = { ...provider, context: { ...provider.context } };
|
|
22
|
+
wrapped.context.resolve = (input) => sanitize(() => provider.context.resolve(input));
|
|
23
|
+
for (const groupName of ['issues', 'comments', 'changeRequests', 'checks', 'reviews', 'releases', 'securityAlerts', 'repositoryMetadata', 'verification']) {
|
|
24
|
+
const group = provider[groupName];
|
|
25
|
+
if (!group)
|
|
26
|
+
continue;
|
|
27
|
+
const target = { ...group };
|
|
28
|
+
for (const method of Object.keys(group)) {
|
|
29
|
+
const original = group[method];
|
|
30
|
+
target[method] = (input) => sanitize(() => original(input));
|
|
31
|
+
}
|
|
32
|
+
wrapped[groupName] = target;
|
|
33
|
+
}
|
|
34
|
+
return wrapped;
|
|
35
|
+
}
|
|
10
36
|
const CURRENT_USER_QUERY = 'query { viewer { login } }';
|
|
11
37
|
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}}}}}}`;
|
|
12
38
|
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}}}}}';
|
|
@@ -57,7 +83,6 @@ function resolveGitHubChangeRequestGitEvidence({ cwd, repository, number, baseRe
|
|
|
57
83
|
error: {
|
|
58
84
|
code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
|
|
59
85
|
message: 'Pull request repository identity is inconsistent',
|
|
60
|
-
retryable: false
|
|
61
86
|
}
|
|
62
87
|
};
|
|
63
88
|
}
|
|
@@ -80,7 +105,6 @@ function resolveGitHubChangeRequestGitEvidence({ cwd, repository, number, baseRe
|
|
|
80
105
|
error: {
|
|
81
106
|
code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
|
|
82
107
|
message: 'Pull request Git refs are invalid',
|
|
83
|
-
retryable: false
|
|
84
108
|
}
|
|
85
109
|
};
|
|
86
110
|
}
|
|
@@ -97,7 +121,6 @@ function resolveGitHubChangeRequestGitEvidence({ cwd, repository, number, baseRe
|
|
|
97
121
|
error: {
|
|
98
122
|
code: 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE',
|
|
99
123
|
message: `No GitHub remote can provide evidence for ${repository}`,
|
|
100
|
-
retryable: false
|
|
101
124
|
}
|
|
102
125
|
};
|
|
103
126
|
}
|
|
@@ -107,7 +130,7 @@ function resolveGitHubChangeRequestGitEvidence({ cwd, repository, number, baseRe
|
|
|
107
130
|
};
|
|
108
131
|
}
|
|
109
132
|
function failure(error) {
|
|
110
|
-
return { ok: false, error };
|
|
133
|
+
return { ok: false, error: providerError(error) };
|
|
111
134
|
}
|
|
112
135
|
function resolveContext(client, input) {
|
|
113
136
|
const cwd = input.workingDirectory;
|
|
@@ -118,19 +141,17 @@ function resolveContext(client, input) {
|
|
|
118
141
|
return failure({
|
|
119
142
|
code: 'GH_CLI_VERSION_UNSUPPORTED',
|
|
120
143
|
message: 'GitHub CLI ' + version.value + ' is unsupported; install gh >= ' + MINIMUM_GITHUB_CLI_VERSION,
|
|
121
|
-
retryable: false
|
|
122
144
|
});
|
|
123
145
|
}
|
|
124
146
|
const remote = input.gitRemote || defaultGitRemote(cwd);
|
|
125
147
|
if (!remote) {
|
|
126
|
-
return failure({ code: 'REMOTE_MISSING', message: 'Git origin remote is not configured'
|
|
148
|
+
return failure({ code: 'REMOTE_MISSING', message: 'Git origin remote is not configured' });
|
|
127
149
|
}
|
|
128
150
|
const ownerRepo = parseGitHubRemote(remote);
|
|
129
151
|
if (!ownerRepo) {
|
|
130
152
|
return failure({
|
|
131
153
|
code: /github\.com/i.test(remote) ? 'REMOTE_INVALID' : 'PLATFORM_UNSUPPORTED',
|
|
132
154
|
message: 'Unable to parse GitHub owner/repo from configured remote',
|
|
133
|
-
retryable: false
|
|
134
155
|
});
|
|
135
156
|
}
|
|
136
157
|
const repository = client.json(['api', 'repos/' + ownerRepo], { cwd });
|
|
@@ -142,7 +163,6 @@ function resolveContext(client, input) {
|
|
|
142
163
|
return failure({
|
|
143
164
|
code: 'UPSTREAM_UNRESOLVED',
|
|
144
165
|
message: 'Unable to resolve the upstream repository',
|
|
145
|
-
retryable: false
|
|
146
166
|
});
|
|
147
167
|
const user = client.json(['api', 'graphql', '-f', 'query=' + CURRENT_USER_QUERY], { cwd });
|
|
148
168
|
if (!user.ok)
|
|
@@ -193,7 +213,7 @@ function verifyRemoteBranch(cwd, repositoryName, head) {
|
|
|
193
213
|
if (!wanted.ref || !/^[A-Za-z0-9._/-]+$/.test(wanted.ref))
|
|
194
214
|
return {
|
|
195
215
|
ok: false,
|
|
196
|
-
error: { code: 'PR_HEAD_INVALID', message: 'Pull request head ref is invalid'
|
|
216
|
+
error: { code: 'PR_HEAD_INVALID', message: 'Pull request head ref is invalid' }
|
|
197
217
|
};
|
|
198
218
|
const remotes = configuredGitRemotes(cwd);
|
|
199
219
|
const remote = remotes.find((item) => item.repository.toLowerCase() === wanted.repository.toLowerCase())
|
|
@@ -201,7 +221,7 @@ function verifyRemoteBranch(cwd, repositoryName, head) {
|
|
|
201
221
|
if (!remote)
|
|
202
222
|
return {
|
|
203
223
|
ok: false,
|
|
204
|
-
error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `No configured remote can verify ${wanted.repository}:${wanted.ref}
|
|
224
|
+
error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `No configured remote can verify ${wanted.repository}:${wanted.ref}` }
|
|
205
225
|
};
|
|
206
226
|
try {
|
|
207
227
|
const output = execFileSync('git', ['ls-remote', '--refs', remote.name, `refs/heads/${wanted.ref}`], {
|
|
@@ -212,19 +232,19 @@ function verifyRemoteBranch(cwd, repositoryName, head) {
|
|
|
212
232
|
if (!sha || !/^[a-f0-9]{40}$/i.test(sha))
|
|
213
233
|
return {
|
|
214
234
|
ok: false,
|
|
215
|
-
error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `Remote branch ${wanted.repository}:${wanted.ref} does not exist
|
|
235
|
+
error: { code: 'PR_REMOTE_BRANCH_MISSING', message: `Remote branch ${wanted.repository}:${wanted.ref} does not exist` }
|
|
216
236
|
};
|
|
217
237
|
return { ok: true, value: sha };
|
|
218
238
|
}
|
|
219
239
|
catch (error) {
|
|
220
240
|
return {
|
|
221
241
|
ok: false,
|
|
222
|
-
error: { code: 'PR_REMOTE_BRANCH_UNAVAILABLE', message: error instanceof Error ? error.message : String(error)
|
|
242
|
+
error: { code: 'PR_REMOTE_BRANCH_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) }
|
|
223
243
|
};
|
|
224
244
|
}
|
|
225
245
|
}
|
|
226
246
|
function invalid(code, message) {
|
|
227
|
-
return { ok: false, error: { code, message
|
|
247
|
+
return { ok: false, error: { code, message } };
|
|
228
248
|
}
|
|
229
249
|
function changeRequestSnapshot(value) {
|
|
230
250
|
return {
|
|
@@ -292,7 +312,7 @@ function createReceipt(remoteId) {
|
|
|
292
312
|
return { ok: true, value: { changed: true, remoteId } };
|
|
293
313
|
}
|
|
294
314
|
function partialFailure(response, value) {
|
|
295
|
-
return { ok: false, error: response.error, value };
|
|
315
|
+
return { ok: false, error: providerError(response.error), value };
|
|
296
316
|
}
|
|
297
317
|
function labelReconciliation(created, updated, removed, skipped) {
|
|
298
318
|
return {
|
|
@@ -319,8 +339,8 @@ function syncLabels(client, repositoryName, number, cwd, current, target, prefix
|
|
|
319
339
|
status: 'blocked',
|
|
320
340
|
changed: true,
|
|
321
341
|
error: synced.error
|
|
322
|
-
? { ...synced.error, code: 'IN_LABEL_SYNC_PARTIAL', message: `In-label synchronization is partial or unknown: ${synced.error.message}` }
|
|
323
|
-
: { code: 'IN_LABEL_SYNC_PARTIAL', message: 'In-label synchronization is partial or unknown'
|
|
342
|
+
? { ...providerError(synced.error), code: 'IN_LABEL_SYNC_PARTIAL', message: `In-label synchronization is partial or unknown: ${synced.error.message}` }
|
|
343
|
+
: { code: 'IN_LABEL_SYNC_PARTIAL', message: 'In-label synchronization is partial or unknown' }
|
|
324
344
|
};
|
|
325
345
|
return synced;
|
|
326
346
|
}
|
|
@@ -599,7 +619,7 @@ function createGitHubOperations(client) {
|
|
|
599
619
|
localHead = repositoryHead(context.workingDirectory);
|
|
600
620
|
}
|
|
601
621
|
catch (error) {
|
|
602
|
-
return { ok: false, error: { code: 'PR_LOCAL_HEAD_UNAVAILABLE', message: error instanceof Error ? error.message : String(error)
|
|
622
|
+
return { ok: false, error: { code: 'PR_LOCAL_HEAD_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
|
|
603
623
|
}
|
|
604
624
|
const remote = verifyRemoteBranch(context.workingDirectory, repository(context), head);
|
|
605
625
|
if (!remote.ok)
|
|
@@ -607,7 +627,7 @@ function createGitHubOperations(client) {
|
|
|
607
627
|
if (remote.value !== localHead)
|
|
608
628
|
return {
|
|
609
629
|
ok: false,
|
|
610
|
-
error: { code: 'PR_REMOTE_HEAD_MISMATCH', message: `Remote head ${remote.value} does not match local HEAD ${localHead}
|
|
630
|
+
error: { code: 'PR_REMOTE_HEAD_MISMATCH', message: `Remote head ${remote.value} does not match local HEAD ${localHead}` }
|
|
611
631
|
};
|
|
612
632
|
return { ok: true, value: { sha: localHead } };
|
|
613
633
|
},
|
|
@@ -904,7 +924,7 @@ function createGitHubOperations(client) {
|
|
|
904
924
|
const tag = resourceIdentityString(release) || '';
|
|
905
925
|
const result = module.publishGitHubReleaseNotes({ repository: repository(context), tag, title, notesFile }, { cwd: context.workingDirectory, client });
|
|
906
926
|
if (result.status === 'failed' || result.status === 'blocked')
|
|
907
|
-
return { ok: false, error: result.error
|
|
927
|
+
return { ok: false, error: result.error ? providerError(result.error) : { code: 'RELEASE_NOTES_PUBLISH_FAILED', message: 'Release notes publish failed' } };
|
|
908
928
|
return { ok: true, value: { changed: result.changed, remoteId: tag } };
|
|
909
929
|
}
|
|
910
930
|
finally {
|
|
@@ -917,7 +937,7 @@ function createGitHubOperations(client) {
|
|
|
917
937
|
repository: repository(context), commitOids, branch, historyLimit, fromTime, toTime
|
|
918
938
|
}, { cwd: context.workingDirectory, client });
|
|
919
939
|
if (collected.status === 'failed' || collected.status === 'blocked' || !('pullRequests' in collected)) {
|
|
920
|
-
return { ok: false, error: collected.error
|
|
940
|
+
return { ok: false, error: collected.error ? providerError(collected.error) : { code: 'RELEASE_NOTES_COLLECTION_FAILED', message: 'Release notes could not be collected' } };
|
|
921
941
|
}
|
|
922
942
|
const mergedPullRequests = collected.pullRequests.map((item) => ({
|
|
923
943
|
id: String(item.number), identity: { kind: 'number', value: Number(item.number) }, number: Number(item.number),
|
|
@@ -1114,7 +1134,7 @@ function createGitHubOperations(client) {
|
|
|
1114
1134
|
return { issues, comments, changeRequests, checks, reviews, releases, securityAlerts, repositoryMetadata, verification };
|
|
1115
1135
|
}
|
|
1116
1136
|
function createGitHubProvider(input, client = createGitHubClient()) {
|
|
1117
|
-
return {
|
|
1137
|
+
return publicProviderErrors({
|
|
1118
1138
|
type: input.providerType,
|
|
1119
1139
|
contractVersion: 2,
|
|
1120
1140
|
identity: { issue: 'number', 'pull-request': 'number', comment: 'number', release: 'key' },
|
|
@@ -1124,7 +1144,7 @@ function createGitHubProvider(input, client = createGitHubClient()) {
|
|
|
1124
1144
|
return resolveContext(client, contextInput);
|
|
1125
1145
|
}
|
|
1126
1146
|
}
|
|
1127
|
-
};
|
|
1147
|
+
});
|
|
1128
1148
|
}
|
|
1129
1149
|
export { configuredGitRemotes, createGitHubProvider, defaultGitRemote, parseGitHubRemote, resolveGitHubChangeRequestGitEvidence };
|
|
1130
1150
|
//# sourceMappingURL=github-provider.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createGitHubClient } from './github-client.js';
|
|
2
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
2
3
|
function normalizeGitHubActor(actor) {
|
|
3
4
|
const name = String(actor.name || '').trim();
|
|
4
5
|
const email = actor.email ? String(actor.email).trim() : null;
|
|
@@ -26,12 +27,13 @@ function normalizePlatformAuthor(author) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
function failure(error) {
|
|
30
|
+
const retryable = error.retryable ?? providerErrorRetryable(error.code);
|
|
29
31
|
return {
|
|
30
|
-
status:
|
|
32
|
+
status: retryable ? 'blocked' : 'failed',
|
|
31
33
|
changed: false,
|
|
32
34
|
operation: null,
|
|
33
35
|
url: null,
|
|
34
|
-
error
|
|
36
|
+
error: { code: error.code, message: error.message }
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
function publishGitHubReleaseNotes(input, options = {}) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computeInLabels } from './metadata-labels.js';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
3
4
|
function inLabels(labels) {
|
|
4
5
|
return [...new Set(labels.filter((label) => label.startsWith('in:')))].sort();
|
|
5
6
|
}
|
|
@@ -96,7 +97,7 @@ function syncLabelDelta(client, repository, number, cwd, current, target, prefix
|
|
|
96
97
|
const delta = labelDelta(current, target, prefix);
|
|
97
98
|
let changed = false;
|
|
98
99
|
const failure = (error) => {
|
|
99
|
-
if (changed || error.retryable) {
|
|
100
|
+
if (changed || error.retryable || providerErrorRetryable(error.code)) {
|
|
100
101
|
return {
|
|
101
102
|
status: 'blocked',
|
|
102
103
|
changed,
|