@fitlab-ai/agent-infra 0.9.6 → 0.9.8
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 +10 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +47 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +245 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +398 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +23 -6
- package/dist/lib/decide.js +68 -14
- package/dist/lib/defaults.json +10 -2
- package/dist/lib/git/workflow.js +30 -3
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +69 -1
- package/dist/lib/internal/codex-sandbox-controller.js +106 -0
- package/dist/lib/internal/release-workflow.js +1 -3
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +48 -8
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/context.js +6 -4
- package/dist/lib/platform/github-client.js +55 -2
- package/dist/lib/platform/pr-summary.js +5 -3
- package/dist/lib/process-data/checkpoint.js +319 -0
- package/dist/lib/process-data/index.js +128 -60
- package/dist/lib/process-data/materialize.js +84 -0
- package/dist/lib/process-data/normalize.js +34 -1
- package/dist/lib/process-data/reconcile.js +55 -1
- package/dist/lib/process-data/sources.js +422 -1
- package/dist/lib/process-data/store.js +341 -11
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +394 -264
- package/dist/lib/sandbox/commands/rm.js +64 -15
- package/dist/lib/sandbox/commands/start.js +4 -3
- package/dist/lib/sandbox/control/container-identity.js +64 -0
- package/dist/lib/sandbox/control/executor.js +23 -9
- package/dist/lib/sandbox/control/lifecycle.js +590 -61
- package/dist/lib/sandbox/control/protocol.js +7 -0
- package/dist/lib/sandbox/control/server.js +202 -26
- package/dist/lib/sandbox/control/state.js +51 -25
- package/dist/lib/sandbox/control/timing.js +8 -0
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/mounts.js +7 -0
- package/dist/lib/sandbox/recovery.js +135 -50
- package/dist/lib/sandbox/workspace-view.js +59 -20
- package/dist/lib/server/daemon.js +1 -1
- package/dist/lib/server/process-control.js +11 -1
- package/dist/lib/server/process-state.js +107 -20
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +82 -11
- package/dist/lib/task/artifact-receipts.js +128 -0
- package/dist/lib/task/codex-orchestration.js +81 -6
- package/dist/lib/task/commands/validate.js +1 -1
- package/dist/lib/task/create.js +10 -2
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +120 -17
- package/dist/lib/task/events.js +148 -7
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/ledger.js +13 -1
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/orchestration.js +293 -18
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/sections.js +5 -1
- package/dist/lib/task/task-execution-lock.js +5 -4
- package/dist/lib/task/verification-engine.js +80 -22
- package/dist/lib/task/verification.js +13 -1
- package/dist/lib/task/workflow-warning-intents.js +4 -2
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +6 -0
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +62 -4
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +322 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +480 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +35 -5
- package/lib/decide.ts +59 -14
- package/lib/defaults.json +10 -2
- package/lib/git/workflow.ts +25 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +68 -1
- package/lib/internal/codex-sandbox-controller.ts +109 -0
- package/lib/internal/release-workflow.ts +1 -3
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +43 -8
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/context.ts +8 -5
- package/lib/platform/github-client.ts +61 -3
- package/lib/platform/pr-summary.ts +10 -3
- package/lib/process-data/checkpoint.ts +335 -0
- package/lib/process-data/index.ts +112 -60
- package/lib/process-data/materialize.ts +83 -0
- package/lib/process-data/normalize.ts +29 -1
- package/lib/process-data/reconcile.ts +64 -2
- package/lib/process-data/sources.ts +449 -2
- package/lib/process-data/store.ts +304 -11
- package/lib/process-data/types.ts +124 -2
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +139 -6
- package/lib/sandbox/commands/rm.ts +64 -14
- package/lib/sandbox/commands/start.ts +4 -3
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/container-identity.ts +93 -0
- package/lib/sandbox/control/executor.ts +24 -12
- package/lib/sandbox/control/lifecycle.ts +685 -67
- package/lib/sandbox/control/protocol.ts +35 -6
- package/lib/sandbox/control/server.ts +184 -24
- package/lib/sandbox/control/state.ts +67 -33
- package/lib/sandbox/control/timing.ts +12 -0
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/mounts.ts +8 -0
- package/lib/sandbox/recovery.ts +159 -49
- package/lib/sandbox/workspace-view.ts +73 -22
- package/lib/server/daemon.ts +1 -1
- package/lib/server/process-control.ts +13 -3
- package/lib/server/process-state.ts +129 -27
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/artifact-lifecycle.ts +73 -10
- package/lib/task/artifact-receipts.ts +161 -0
- package/lib/task/codex-orchestration.ts +91 -5
- package/lib/task/commands/validate.ts +1 -1
- package/lib/task/create.ts +11 -2
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +205 -25
- package/lib/task/events.ts +132 -6
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/ledger.ts +16 -1
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/orchestration.ts +443 -22
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/sections.ts +6 -2
- package/lib/task/task-execution-lock.ts +11 -9
- package/lib/task/verification-engine.ts +93 -32
- package/lib/task/verification.ts +15 -1
- package/lib/task/workflow-warning-intents.ts +4 -2
- package/lib/task/write.ts +4 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +7 -0
- package/templates/.agents/README.zh-CN.md +5 -0
- package/templates/.agents/hooks/lifecycle-delegation.js +33 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +10 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +10 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.en.md +3 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +25 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +25 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -7
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -7
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +5 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
- package/templates/.agents/workspace/README.en.md +0 -26
- package/templates/.agents/workspace/README.zh-CN.md +0 -26
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
|
|
2
|
-
import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, pauseOrchestration, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt } from '../task/orchestration.js';
|
|
2
|
+
import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, dispatchOrchestrationDelegation, pauseOrchestration, recoverCommitIntent, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt } from '../task/orchestration.js';
|
|
3
3
|
import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.js';
|
|
4
4
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
5
|
-
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
|
|
6
6
|
function usageFailure(message) {
|
|
7
7
|
process.stdout.write(`${JSON.stringify({
|
|
8
8
|
status: 'failed', changed: false,
|
|
@@ -22,7 +22,8 @@ async function taskOrchestration(args = []) {
|
|
|
22
22
|
}
|
|
23
23
|
const [taskRef, intent] = args;
|
|
24
24
|
if (![
|
|
25
|
-
'begin-or-resume', 'route', 'prepare', '
|
|
25
|
+
'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
|
|
26
|
+
'hook-start', 'hook-stop', 'advance', 'pause', 'status',
|
|
26
27
|
'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
|
|
27
28
|
].includes(intent)) {
|
|
28
29
|
usageFailure(`unknown intent '${intent}'`);
|
|
@@ -45,7 +46,9 @@ async function taskOrchestration(args = []) {
|
|
|
45
46
|
if (![
|
|
46
47
|
'--max-steps', '--executor-model', '--executor-reasoning-effort',
|
|
47
48
|
'--reviewer-model', '--reviewer-reasoning-effort',
|
|
48
|
-
'--client', '--requested-model', '--requested-reasoning-effort', '--
|
|
49
|
+
'--client', '--requested-model', '--requested-reasoning-effort', '--capability-token',
|
|
50
|
+
'--parent-id', '--before-fingerprint',
|
|
51
|
+
'--stage', '--round', '--artifact', '--role',
|
|
49
52
|
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
|
|
50
53
|
'--model-fallback-reason', '--reasoning-effort-fallback-reason',
|
|
51
54
|
'--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
|
|
@@ -264,13 +267,42 @@ async function taskOrchestration(args = []) {
|
|
|
264
267
|
result = await prepareCodexOrchestrationDelegation(taskRef, {
|
|
265
268
|
client: values['--client'],
|
|
266
269
|
requestedModel: values['--requested-model'],
|
|
267
|
-
requestedReasoningEffort: values['--requested-reasoning-effort']
|
|
270
|
+
requestedReasoningEffort: values['--requested-reasoning-effort'],
|
|
271
|
+
capabilityToken: values['--capability-token']
|
|
268
272
|
}, { orchestrationOptions: coreOptions });
|
|
269
273
|
}
|
|
274
|
+
else if (intent === 'dispatch') {
|
|
275
|
+
result = dispatchOrchestrationDelegation(taskRef, coreOptions);
|
|
276
|
+
}
|
|
277
|
+
else if (intent === 'await-activation') {
|
|
278
|
+
const missing = requireValues(['--stage', '--round', '--artifact', '--role']);
|
|
279
|
+
if (missing) {
|
|
280
|
+
usageFailure(`intent 'await-activation' requires '${missing}'`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const round = Number(values['--round']);
|
|
284
|
+
if (!Number.isSafeInteger(round) || round < 1) {
|
|
285
|
+
usageFailure('--round must be a positive integer');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (!['executor', 'reviewer'].includes(values['--role'])) {
|
|
289
|
+
usageFailure('--role must be executor or reviewer');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
result = await awaitOrchestrationDelegationActivation(taskRef, {
|
|
293
|
+
stage: values['--stage'],
|
|
294
|
+
round,
|
|
295
|
+
artifact: values['--artifact'],
|
|
296
|
+
role: values['--role']
|
|
297
|
+
}, coreOptions);
|
|
298
|
+
}
|
|
299
|
+
else if (intent === 'recover-prepared') {
|
|
300
|
+
result = recoverPreparedOrchestrationDelegation(taskRef, coreOptions);
|
|
301
|
+
}
|
|
270
302
|
else if (intent === 'hook-start') {
|
|
271
303
|
const missing = requireValues([
|
|
272
304
|
...(taskRef === 'auto' ? ['--client'] : []),
|
|
273
|
-
'--native-agent', '--child-id', '--parent-id'
|
|
305
|
+
'--native-agent', '--child-id', '--parent-id'
|
|
274
306
|
]);
|
|
275
307
|
if (missing) {
|
|
276
308
|
usageFailure(`intent 'hook-start' requires '${missing}'`);
|
|
@@ -296,7 +328,11 @@ async function taskOrchestration(args = []) {
|
|
|
296
328
|
return;
|
|
297
329
|
}
|
|
298
330
|
result = sealMatchingOrchestrationDelegation(values['--client'], {
|
|
299
|
-
nativeAgent: values['--native-agent'], childId: values['--child-id']
|
|
331
|
+
nativeAgent: values['--native-agent'], childId: values['--child-id'],
|
|
332
|
+
actualModel: values['--actual-model'],
|
|
333
|
+
actualReasoningEffort: values['--actual-reasoning-effort'],
|
|
334
|
+
modelFallbackReason: values['--model-fallback-reason'],
|
|
335
|
+
reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
|
|
300
336
|
}, coreOptions);
|
|
301
337
|
}
|
|
302
338
|
else {
|
|
@@ -313,7 +349,11 @@ async function taskOrchestration(args = []) {
|
|
|
313
349
|
result = sealOrchestrationDelegation(taskRef, {
|
|
314
350
|
childId: values['--child-id'], exitCode,
|
|
315
351
|
afterFingerprint: values['--after-fingerprint'],
|
|
316
|
-
changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : []
|
|
352
|
+
changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : [],
|
|
353
|
+
actualModel: values['--actual-model'],
|
|
354
|
+
actualReasoningEffort: values['--actual-reasoning-effort'],
|
|
355
|
+
modelFallbackReason: values['--model-fallback-reason'],
|
|
356
|
+
reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
|
|
317
357
|
}, coreOptions);
|
|
318
358
|
}
|
|
319
359
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { consumeHumanOverride, diagnoseHumanOverrideForTask, issueHumanOverride } from '../task/human-override.js';
|
|
2
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
3
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
4
|
+
const USAGE = `Usage: agent-infra-internal task-override <task-ref> <diagnose|issue|consume> [options]
|
|
5
|
+
|
|
6
|
+
Options: --failure-id <id> --target <target> --operator <operator> --reason <reason> --scope <scope>
|
|
7
|
+
--intent <lifecycle-intent> --alert-number <N> --issue-number <N> --staging-dir <path>
|
|
8
|
+
--expires-at <timestamp> --ticket <ticket-id> --pr-number <N>
|
|
9
|
+
`;
|
|
10
|
+
const FLAGS = {
|
|
11
|
+
'--failure-id': 'failureId',
|
|
12
|
+
'--target': 'target',
|
|
13
|
+
'--operator': 'operator',
|
|
14
|
+
'--reason': 'reason',
|
|
15
|
+
'--scope': 'scope',
|
|
16
|
+
'--intent': 'intent',
|
|
17
|
+
'--failure-digest': 'failureDigest',
|
|
18
|
+
'--expires-at': 'expiresAt',
|
|
19
|
+
'--ticket': 'ticketId',
|
|
20
|
+
'--facts': 'observedFacts',
|
|
21
|
+
'--alert-number': 'alertNumber',
|
|
22
|
+
'--issue-number': 'issueNumber',
|
|
23
|
+
'--pr-number': 'pullRequestNumber',
|
|
24
|
+
'--staging-dir': 'stagingDir'
|
|
25
|
+
};
|
|
26
|
+
function usageFailure(message) {
|
|
27
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'OVERRIDE_PAYLOAD_INVALID', message } })}\n`);
|
|
28
|
+
process.stderr.write(USAGE);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
function taskOverride(args = []) {
|
|
32
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
33
|
+
process.stdout.write(USAGE);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const [taskRef, operation] = args;
|
|
37
|
+
if (!taskRef || !operation || !['diagnose', 'issue', 'consume'].includes(operation)) {
|
|
38
|
+
usageFailure('task ref and a valid operation are required');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const values = { taskRef };
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
44
|
+
const flag = args[index];
|
|
45
|
+
const key = FLAGS[flag];
|
|
46
|
+
if (!key) {
|
|
47
|
+
usageFailure(`unknown option '${flag}'`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (seen.has(flag)) {
|
|
51
|
+
usageFailure(`duplicate option '${flag}'`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const value = args[++index];
|
|
55
|
+
if (value === undefined || value.startsWith('--')) {
|
|
56
|
+
usageFailure(`option '${flag}' requires a value`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
seen.add(flag);
|
|
60
|
+
values[key] = key === 'observedFacts' ? value.split(',').map((fact) => fact.trim()).filter(Boolean)
|
|
61
|
+
: key === 'alertNumber' || key === 'issueNumber' || key === 'pullRequestNumber' ? Number(value) : value;
|
|
62
|
+
}
|
|
63
|
+
const required = {
|
|
64
|
+
diagnose: [],
|
|
65
|
+
issue: ['failureId', 'target', 'operator', 'reason', 'scope', 'expiresAt'],
|
|
66
|
+
consume: ['failureId', 'target', 'scope', 'ticketId']
|
|
67
|
+
};
|
|
68
|
+
const missing = required[operation].find((key) => values[key] === undefined);
|
|
69
|
+
if (missing) {
|
|
70
|
+
usageFailure(`${operation} requires '--${missing.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}'`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const resolved = resolveTaskRef(taskRef);
|
|
74
|
+
if (!resolved.ok) {
|
|
75
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let result;
|
|
80
|
+
try {
|
|
81
|
+
if (operation === 'diagnose') {
|
|
82
|
+
result = diagnoseHumanOverrideForTask(taskRef, values.failureId ? String(values.failureId) : undefined, values.target ? String(values.target) : undefined);
|
|
83
|
+
}
|
|
84
|
+
else if (operation === 'issue') {
|
|
85
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.issue', () => issueHumanOverride({
|
|
86
|
+
taskRef,
|
|
87
|
+
failureId: String(values.failureId),
|
|
88
|
+
target: String(values.target),
|
|
89
|
+
operator: String(values.operator),
|
|
90
|
+
reason: String(values.reason),
|
|
91
|
+
scope: String(values.scope),
|
|
92
|
+
expiresAt: String(values.expiresAt),
|
|
93
|
+
...(values.intent ? { intent: String(values.intent) } : {}),
|
|
94
|
+
...(values.alertNumber !== undefined ? { alertNumber: Number(values.alertNumber) } : {}),
|
|
95
|
+
...(values.issueNumber !== undefined ? { issueNumber: Number(values.issueNumber) } : {}),
|
|
96
|
+
...(values.pullRequestNumber !== undefined ? { pullRequestNumber: Number(values.pullRequestNumber) } : {}),
|
|
97
|
+
...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {}),
|
|
98
|
+
...(values.failureDigest ? { failureDigest: String(values.failureDigest) } : {})
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.consume', () => consumeHumanOverride({
|
|
103
|
+
taskRef,
|
|
104
|
+
ticketId: String(values.ticketId),
|
|
105
|
+
failureId: String(values.failureId),
|
|
106
|
+
target: String(values.target),
|
|
107
|
+
scope: String(values.scope),
|
|
108
|
+
...(values.intent ? { intent: String(values.intent) } : {}),
|
|
109
|
+
...(values.alertNumber !== undefined ? { alertNumber: Number(values.alertNumber) } : {}),
|
|
110
|
+
...(values.issueNumber !== undefined ? { issueNumber: Number(values.issueNumber) } : {}),
|
|
111
|
+
...(values.pullRequestNumber !== undefined ? { pullRequestNumber: Number(values.pullRequestNumber) } : {}),
|
|
112
|
+
...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {}),
|
|
113
|
+
...(values.failureDigest ? { failureDigest: String(values.failureDigest) } : {}),
|
|
114
|
+
...(values.observedFacts ? { observedFacts: values.observedFacts } : {})
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
if (cause instanceof TaskExecutionLockError) {
|
|
120
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: cause.code, message: cause.message } })}\n`);
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
throw cause;
|
|
125
|
+
}
|
|
126
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
127
|
+
if (result.status === 'failed')
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
export { taskOverride };
|
|
131
|
+
//# sourceMappingURL=task-override.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { finalizeReviewSummary } from '../task/review-finalization.js';
|
|
2
|
-
|
|
2
|
+
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
3
6
|
function failUsage(message) {
|
|
4
7
|
process.stdout.write(`${JSON.stringify({
|
|
5
8
|
status: 'failed',
|
|
@@ -27,10 +30,13 @@ function taskReview(args = []) {
|
|
|
27
30
|
let artifact = '';
|
|
28
31
|
let dryRun = false;
|
|
29
32
|
let orchestrated = false;
|
|
33
|
+
let overrideTicket = '';
|
|
34
|
+
let overrideTarget = '';
|
|
35
|
+
let overrideScope = '';
|
|
30
36
|
const seen = new Set();
|
|
31
37
|
for (let index = 2; index < args.length; index += 1) {
|
|
32
38
|
const flag = args[index];
|
|
33
|
-
if (!['--stage', '--artifact', '--orchestrated', '--dry-run'].includes(flag)) {
|
|
39
|
+
if (!['--stage', '--artifact', '--orchestrated', '--dry-run', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
34
40
|
failUsage(`unknown option '${flag}'`);
|
|
35
41
|
return;
|
|
36
42
|
}
|
|
@@ -54,16 +60,68 @@ function taskReview(args = []) {
|
|
|
54
60
|
}
|
|
55
61
|
if (flag === '--stage')
|
|
56
62
|
stage = value;
|
|
57
|
-
else
|
|
63
|
+
else if (flag === '--artifact')
|
|
58
64
|
artifact = value;
|
|
65
|
+
else if (flag === '--override-ticket')
|
|
66
|
+
overrideTicket = value;
|
|
67
|
+
else if (flag === '--override-target')
|
|
68
|
+
overrideTarget = value;
|
|
69
|
+
else if (flag === '--override-scope')
|
|
70
|
+
overrideScope = value;
|
|
59
71
|
}
|
|
60
72
|
if (!stage || !artifact) {
|
|
61
73
|
failUsage("options '--stage' and '--artifact' are required");
|
|
62
74
|
return;
|
|
63
75
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
77
|
+
if (dryRunConflict) {
|
|
78
|
+
failUsage(dryRunConflict.message);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const resolved = resolveTaskRef(args[0]);
|
|
82
|
+
if (!resolved.ok) {
|
|
83
|
+
failUsage(resolved.message);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let result;
|
|
87
|
+
let humanOverride = null;
|
|
88
|
+
try {
|
|
89
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', () => {
|
|
90
|
+
let current = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
|
|
91
|
+
if (current.status !== 'failed' || !overrideTicket)
|
|
92
|
+
return current;
|
|
93
|
+
if (!overrideTarget || !overrideScope) {
|
|
94
|
+
failUsage('override ticket requires target and scope');
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
const consumed = consumeHumanOverride({
|
|
98
|
+
taskRef: args[0], ticketId: overrideTicket,
|
|
99
|
+
failureId: failureId('review-finalization', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
100
|
+
target: overrideTarget, scope: overrideScope
|
|
101
|
+
}, {
|
|
102
|
+
effectExecutor: (capability) => {
|
|
103
|
+
const retried = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true, manualOverride: capability });
|
|
104
|
+
current = retried;
|
|
105
|
+
return retried.status === 'failed' || retried.status === 'planned'
|
|
106
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no review effect was committed' : `${retried.error?.code ?? 'REVIEW_FAILED'}: ${retried.error?.message ?? 'manual review effect failed'}` }
|
|
107
|
+
: null;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
humanOverride = consumed;
|
|
111
|
+
return current;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
116
|
+
throw error;
|
|
117
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
|
|
122
|
+
const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
|
|
123
|
+
humanOverride.status === 'failed');
|
|
124
|
+
if (result.status === 'failed' || overrideFailed)
|
|
67
125
|
process.exitCode = 1;
|
|
68
126
|
}
|
|
69
127
|
export { taskReview };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { renderTaskVerification, verifyTaskEvent } from '../task/verification.js';
|
|
2
|
-
|
|
2
|
+
import { consumeHumanOverride, failureId } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-verify <N | TASK-id> <verification-event> [--artifact <canonical.md>] [--format json|text] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
3
6
|
function fail(message) {
|
|
4
7
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'VERIFY_PAYLOAD_INVALID', message } })}\n`);
|
|
5
8
|
process.stderr.write(USAGE);
|
|
@@ -14,12 +17,17 @@ function taskVerify(args = []) {
|
|
|
14
17
|
fail('task ref and verification event are required');
|
|
15
18
|
return;
|
|
16
19
|
}
|
|
20
|
+
const taskRef = args[0];
|
|
21
|
+
const event = args[1];
|
|
17
22
|
let artifact;
|
|
18
23
|
let format = 'json';
|
|
24
|
+
let overrideTicket;
|
|
25
|
+
let overrideTarget;
|
|
26
|
+
let overrideScope;
|
|
19
27
|
const seen = new Set();
|
|
20
28
|
for (let index = 2; index < args.length; index += 1) {
|
|
21
29
|
const flag = args[index];
|
|
22
|
-
if (
|
|
30
|
+
if (!['--artifact', '--format', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
23
31
|
fail(`unknown option '${flag}'`);
|
|
24
32
|
return;
|
|
25
33
|
}
|
|
@@ -35,6 +43,12 @@ function taskVerify(args = []) {
|
|
|
35
43
|
seen.add(flag);
|
|
36
44
|
if (flag === '--artifact')
|
|
37
45
|
artifact = value;
|
|
46
|
+
else if (flag === '--override-ticket')
|
|
47
|
+
overrideTicket = value;
|
|
48
|
+
else if (flag === '--override-target')
|
|
49
|
+
overrideTarget = value;
|
|
50
|
+
else if (flag === '--override-scope')
|
|
51
|
+
overrideScope = value;
|
|
38
52
|
else if (value === 'json' || value === 'text')
|
|
39
53
|
format = value;
|
|
40
54
|
else {
|
|
@@ -42,7 +56,55 @@ function taskVerify(args = []) {
|
|
|
42
56
|
return;
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
|
-
const
|
|
59
|
+
const resolved = resolveTaskRef(taskRef);
|
|
60
|
+
if (!resolved.ok) {
|
|
61
|
+
fail(resolved.message);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let result;
|
|
65
|
+
let humanOverride = null;
|
|
66
|
+
try {
|
|
67
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-verify.${event}`, () => {
|
|
68
|
+
let current = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) });
|
|
69
|
+
if (current.status === 'pass' || !overrideTicket)
|
|
70
|
+
return current;
|
|
71
|
+
if (!overrideTarget || !overrideScope) {
|
|
72
|
+
fail('override ticket requires target and scope');
|
|
73
|
+
return current;
|
|
74
|
+
}
|
|
75
|
+
const failureProducer = current.status === 'blocked' || current.status === 'fail'
|
|
76
|
+
? 'verification-engine'
|
|
77
|
+
: 'task-verify';
|
|
78
|
+
const failureCode = current.status === 'blocked'
|
|
79
|
+
? 'CHECK_BLOCKED'
|
|
80
|
+
: current.status === 'fail'
|
|
81
|
+
? 'CHECK_FAILED'
|
|
82
|
+
: current.error?.code ?? 'VERIFY_TASK_STATE_MISMATCH';
|
|
83
|
+
const consumed = consumeHumanOverride({
|
|
84
|
+
taskRef, ticketId: overrideTicket,
|
|
85
|
+
failureId: failureId(failureProducer, failureCode),
|
|
86
|
+
target: overrideTarget, scope: overrideScope
|
|
87
|
+
}, {
|
|
88
|
+
effectExecutor: (capability) => {
|
|
89
|
+
const retried = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) }, { manualOverride: capability });
|
|
90
|
+
current = retried;
|
|
91
|
+
return retried.status !== 'pass'
|
|
92
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'VERIFY_FAILED'}: ${retried.error?.message ?? 'manual verification effect failed'}` }
|
|
93
|
+
: null;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
humanOverride = consumed;
|
|
97
|
+
return current;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
102
|
+
throw error;
|
|
103
|
+
fail(`${error.code}: ${error.message}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (humanOverride)
|
|
107
|
+
result = { ...result, humanOverride };
|
|
46
108
|
process.stdout.write(format === 'text' ? renderTaskVerification(result) : `${JSON.stringify(result)}\n`);
|
|
47
109
|
process.exitCode = result.status === 'pass' ? 0 : result.status === 'blocked' ? 2 : 1;
|
|
48
110
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js';
|
|
2
|
+
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
|
|
3
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
2
5
|
const USAGE = `Usage: agent-infra-internal task-warning <task-ref> <add|set-status|list> [intent flags] [--dry-run]\n`;
|
|
3
6
|
const FLAGS = {
|
|
4
7
|
'--step': 'step', '--severity': 'severity', '--code': 'code', '--target': 'target',
|
|
5
|
-
'--message': 'message', '--action': 'action', '--id': 'id', '--status': 'status', '--resolution': 'resolution'
|
|
8
|
+
'--message': 'message', '--action': 'action', '--id': 'id', '--status': 'status', '--resolution': 'resolution',
|
|
9
|
+
'--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
|
|
6
10
|
};
|
|
7
11
|
function usageFailure(message) {
|
|
8
12
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'WARNING_PAYLOAD_INVALID', message } })}\n`);
|
|
@@ -55,6 +59,9 @@ function taskWarning(args = []) {
|
|
|
55
59
|
};
|
|
56
60
|
const optional = kind === 'list' ? ['status'] : [];
|
|
57
61
|
const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...optional]);
|
|
62
|
+
allowed.add('overrideTicket');
|
|
63
|
+
allowed.add('overrideTarget');
|
|
64
|
+
allowed.add('overrideScope');
|
|
58
65
|
const unexpected = Object.keys(values).find((key) => !allowed.has(key));
|
|
59
66
|
const missing = required[kind].find((key) => values[key] === undefined);
|
|
60
67
|
if (unexpected) {
|
|
@@ -65,9 +72,55 @@ function taskWarning(args = []) {
|
|
|
65
72
|
usageFailure(`${kind} requires '${missing}'`);
|
|
66
73
|
return;
|
|
67
74
|
}
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
const dryRunConflict = overrideDryRunConflict(values);
|
|
76
|
+
if (dryRunConflict) {
|
|
77
|
+
usageFailure(dryRunConflict.message);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const resolved = resolveTaskRef(taskRef);
|
|
81
|
+
if (!resolved.ok) {
|
|
82
|
+
usageFailure(resolved.message);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let result;
|
|
86
|
+
let humanOverride = null;
|
|
87
|
+
try {
|
|
88
|
+
result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-warning.${kind}`, () => {
|
|
89
|
+
let current = applyWorkflowWarningIntent(values);
|
|
90
|
+
if (current.status !== 'failed' || !values.overrideTicket)
|
|
91
|
+
return current;
|
|
92
|
+
if (!values.overrideTarget || !values.overrideScope) {
|
|
93
|
+
usageFailure('override ticket requires target and scope');
|
|
94
|
+
return current;
|
|
95
|
+
}
|
|
96
|
+
const consumed = consumeHumanOverride({
|
|
97
|
+
taskRef, ticketId: String(values.overrideTicket),
|
|
98
|
+
failureId: failureId('workflow-warning', current.error?.code ?? 'TASK_STATE_MISMATCH'),
|
|
99
|
+
target: String(values.overrideTarget), scope: String(values.overrideScope)
|
|
100
|
+
}, {
|
|
101
|
+
effectExecutor: (capability) => {
|
|
102
|
+
const retried = applyWorkflowWarningIntent(values, { manualOverride: capability });
|
|
103
|
+
current = retried;
|
|
104
|
+
return retried.status === 'failed' || retried.status === 'planned'
|
|
105
|
+
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no workflow warning effect was committed' : `${retried.error?.code ?? 'WARNING_FAILED'}: ${retried.error?.message ?? 'manual warning effect failed'}` }
|
|
106
|
+
: null;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
humanOverride = consumed;
|
|
110
|
+
return current;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
115
|
+
throw error;
|
|
116
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
|
|
121
|
+
const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
|
|
122
|
+
humanOverride.status === 'failed');
|
|
123
|
+
if (result.status === 'failed' || overrideFailed)
|
|
71
124
|
process.exitCode = 1;
|
|
72
125
|
}
|
|
73
126
|
export { taskWarning };
|
|
@@ -5,6 +5,7 @@ import semver from 'semver';
|
|
|
5
5
|
import { getPlatformAdapter, registerPlatformAdapter } from './adapters.js';
|
|
6
6
|
import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
|
|
7
7
|
import { platformResult } from './types.js';
|
|
8
|
+
const CURRENT_USER_QUERY = 'query { viewer { login } }';
|
|
8
9
|
function findRepoRoot(cwd) {
|
|
9
10
|
try {
|
|
10
11
|
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
@@ -89,18 +90,19 @@ function resolveGitHubContext(options) {
|
|
|
89
90
|
error: { code: 'UPSTREAM_UNRESOLVED', message: 'Unable to resolve the upstream repository', retryable: false }
|
|
90
91
|
});
|
|
91
92
|
}
|
|
92
|
-
const user = client.json(['api', '
|
|
93
|
+
const user = client.json(['api', 'graphql', '-f', `query=${CURRENT_USER_QUERY}`], { cwd });
|
|
93
94
|
if (!user.ok)
|
|
94
95
|
return failure(user.error, upstream);
|
|
95
96
|
const userValue = user.value;
|
|
97
|
+
const currentUser = userValue?.data?.viewer?.login || null;
|
|
96
98
|
const permissions = client.json(['api', `repos/${upstream}`], { cwd });
|
|
97
99
|
if (!permissions.ok)
|
|
98
100
|
return failure(permissions.error, upstream);
|
|
99
101
|
const permissionValue = permissions.value;
|
|
100
102
|
const values = permissionValue?.permissions || {};
|
|
101
103
|
const capabilities = {
|
|
102
|
-
authenticated: Boolean(
|
|
103
|
-
comment: Boolean(
|
|
104
|
+
authenticated: Boolean(currentUser),
|
|
105
|
+
comment: Boolean(currentUser),
|
|
104
106
|
triage: Boolean(values.triage || values.push || values.admin),
|
|
105
107
|
push: Boolean(values.push || values.admin),
|
|
106
108
|
admin: Boolean(values.admin)
|
|
@@ -108,7 +110,7 @@ function resolveGitHubContext(options) {
|
|
|
108
110
|
const status = Object.values(capabilities).every(Boolean) ? 'no-op' : 'degraded';
|
|
109
111
|
return platformResult(status, {
|
|
110
112
|
changed: false,
|
|
111
|
-
platform: { type: platform, repository: upstream, currentUser
|
|
113
|
+
platform: { type: platform, repository: upstream, currentUser },
|
|
112
114
|
resource: { kind: 'repository', number: null },
|
|
113
115
|
capabilities,
|
|
114
116
|
operations: [{ name: 'resolve', status: 'no-op', reasonCode: null }]
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import spawn from 'cross-spawn';
|
|
3
3
|
import semver from 'semver';
|
|
4
|
-
|
|
4
|
+
// closingIssuesReferences (introduced in gh 2.72.0) is the highest-versioned gh feature
|
|
5
|
+
// the platform layer depends on; see github-release-notes.ts:102.
|
|
6
|
+
const MINIMUM_GITHUB_CLI_VERSION = '2.72.0';
|
|
5
7
|
const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
|
|
6
8
|
const ERROR_DETAIL_LIMIT = 4096;
|
|
7
9
|
function boundedFailureDetail(result) {
|
|
@@ -76,6 +78,41 @@ function classifyGitHubFailure(result) {
|
|
|
76
78
|
}
|
|
77
79
|
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', retryable: false };
|
|
78
80
|
}
|
|
81
|
+
function responseUrl(args) {
|
|
82
|
+
const endpoint = args.find((arg, index) => index > 0 && !arg.startsWith('-')) || '';
|
|
83
|
+
return endpoint.startsWith('http') ? endpoint : `https://api.github.com/${endpoint.replace(/^\//, '')}`;
|
|
84
|
+
}
|
|
85
|
+
function parseIncludedResponse(stdout, args) {
|
|
86
|
+
const statusBlocks = [...stdout.matchAll(/^HTTP\/[^\r\n]+/gim)];
|
|
87
|
+
const headerStart = statusBlocks.at(-1)?.index;
|
|
88
|
+
if (headerStart === undefined)
|
|
89
|
+
return null;
|
|
90
|
+
const response = stdout.slice(headerStart);
|
|
91
|
+
const separator = response.match(/\r?\n\r?\n/);
|
|
92
|
+
if (!separator || separator.index === undefined)
|
|
93
|
+
return null;
|
|
94
|
+
const header = response.slice(0, separator.index);
|
|
95
|
+
const body = response.slice(separator.index + separator[0].length);
|
|
96
|
+
const status = header.match(/^HTTP\/[^ ]+\s+(\d{3})/mi)?.[1];
|
|
97
|
+
if (!status)
|
|
98
|
+
return null;
|
|
99
|
+
const date = header.match(/^Date:\s*(.+)$/mi)?.[1]?.trim();
|
|
100
|
+
const links = [...header.matchAll(/^Link:\s*(.+)$/gim)].flatMap((match) => match[1].split(',').map((value) => value.trim()));
|
|
101
|
+
try {
|
|
102
|
+
return {
|
|
103
|
+
value: JSON.parse(body || 'null'),
|
|
104
|
+
metadata: {
|
|
105
|
+
status: Number(status),
|
|
106
|
+
requestUrl: responseUrl(args),
|
|
107
|
+
...(date ? { date } : {}),
|
|
108
|
+
links
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
79
116
|
function createGitHubClient(options = {}) {
|
|
80
117
|
const runner = options.runner || defaultRunner;
|
|
81
118
|
const delays = options.retryDelaysMs || retryDelaysFromEnvironment();
|
|
@@ -134,8 +171,24 @@ function createGitHubClient(options = {}) {
|
|
|
134
171
|
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON', retryable: true }
|
|
135
172
|
};
|
|
136
173
|
}
|
|
174
|
+
},
|
|
175
|
+
jsonWithMetadata(args, request = {}) {
|
|
176
|
+
const includeArgs = args[0] === 'api' && args[1] !== '--include'
|
|
177
|
+
? [args[0], '--include', ...args.slice(1)]
|
|
178
|
+
: args;
|
|
179
|
+
const result = run(includeArgs, request);
|
|
180
|
+
if (!result.ok)
|
|
181
|
+
return result;
|
|
182
|
+
const parsed = parseIncludedResponse(result.value, args);
|
|
183
|
+
if (!parsed) {
|
|
184
|
+
return {
|
|
185
|
+
ok: false,
|
|
186
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid', retryable: true }
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return { ok: true, value: parsed };
|
|
137
190
|
}
|
|
138
191
|
};
|
|
139
192
|
}
|
|
140
|
-
export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION };
|
|
193
|
+
export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION, parseIncludedResponse };
|
|
141
194
|
//# sourceMappingURL=github-client.js.map
|