@fitlab-ai/agent-infra 0.9.14 → 0.9.15
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 +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -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 +3 -1
- 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 +322 -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 +15 -262
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-operation-registry.js +101 -3
- package/dist/lib/internal/task-review.js +8 -53
- 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 +2 -2
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +55 -3
- package/dist/lib/platform/pull-requests.js +2 -105
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +127 -142
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +3 -1514
- 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 +491 -120
- package/dist/lib/sandbox/control/state.js +58 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- 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 +101 -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 +23 -69
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +87 -16
- 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 +2 -16
- package/dist/lib/task/control-authority.js +371 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +120 -9
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -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.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +132 -157
- 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 +101 -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 +14 -17
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +125 -193
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/verification-engine.js +116 -323
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -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/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 +3 -2
- package/lib/fs/durable-write.ts +29 -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 +3 -1
- 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 +306 -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 +13 -216
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-operation-registry.ts +111 -3
- package/lib/internal/task-review.ts +6 -51
- 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 +3 -4
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +61 -5
- package/lib/platform/pull-requests.ts +3 -170
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +145 -155
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +3 -1909
- 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 +75 -6
- package/lib/sandbox/control/server.ts +538 -123
- package/lib/sandbox/control/state.ts +78 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- 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 +104 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +22 -68
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +99 -18
- 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 +2 -16
- package/lib/task/control-authority.ts +502 -5
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +131 -10
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +455 -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.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +148 -182
- 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 +152 -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 +19 -19
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +131 -259
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/verification-engine.ts +110 -366
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -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/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- 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/complete-manual-validation/SKILL.en.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
- 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 +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +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/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/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/.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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { accessSandboxTaskView, parseSandboxTaskView, taskViewFromStatus } from '../sandbox/control/task-view.js';
|
|
3
4
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
5
|
import { PUBLIC_CLI_COMMAND_ALIASES, PUBLIC_CLI_SELECTOR_ALIASES, internalRouteSelector } from './cli-route-inventory.js';
|
|
6
|
+
import { readSandboxControlIdentitySentinel } from '../sandbox/control/identity-sentinel.js';
|
|
5
7
|
const descriptor = (dispatcher, command, selector, scope, effect, taskRefSource = scope === 'non-task' ? 'none' : 'argv') => ({ dispatcher, command, selector, scope, effect, taskRefSource, guardBeforeImport: true });
|
|
6
8
|
function internalTaskRoutes() {
|
|
7
9
|
return [
|
|
@@ -14,6 +16,10 @@ function internalTaskRoutes() {
|
|
|
14
16
|
descriptor('internal', 'sandbox-control', 'execute', 'non-task', 'progress', 'none'),
|
|
15
17
|
descriptor('internal', 'sandbox-control', 'recover', 'conditional', 'recovery', 'delegated'),
|
|
16
18
|
descriptor('internal', 'sandbox-control', 'client', 'conditional', 'progress', 'delegated'),
|
|
19
|
+
descriptor('internal', 'host-control', 'serve', 'non-task', 'progress', 'none'),
|
|
20
|
+
descriptor('internal', 'host-control', 'status', 'non-task', 'diagnostic', 'none'),
|
|
21
|
+
descriptor('internal', 'host-control', 'install', 'non-task', 'progress', 'none'),
|
|
22
|
+
descriptor('internal', 'host-control', 'uninstall', 'non-task', 'progress', 'none'),
|
|
17
23
|
descriptor('internal', 'agent-client', 'next-steps', 'non-task', 'diagnostic', 'none'),
|
|
18
24
|
descriptor('internal', 'agent-client', 'model-selection', 'non-task', 'diagnostic', 'none'),
|
|
19
25
|
descriptor('internal', 'codex-lifecycle', 'preflight', 'non-task', 'diagnostic', 'none'),
|
|
@@ -110,7 +116,8 @@ function internalTaskRoutes() {
|
|
|
110
116
|
descriptor('internal', 'task-snapshot', 'snapshot', 'conditional', 'diagnostic'),
|
|
111
117
|
descriptor('internal', 'task-verify', 'event', 'task-bound', 'terminal-verdict'),
|
|
112
118
|
descriptor('internal', 'task-validate', 'snapshot', 'conditional', 'diagnostic'),
|
|
113
|
-
descriptor('internal', 'task-validate', 'inplace', 'task-bound', 'progress')
|
|
119
|
+
descriptor('internal', 'task-validate', 'inplace', 'task-bound', 'progress'),
|
|
120
|
+
descriptor('internal', 'manual-validation', 'transaction', 'task-bound', 'remote-write')
|
|
114
121
|
];
|
|
115
122
|
}
|
|
116
123
|
function publicTaskRoutes() {
|
|
@@ -169,12 +176,12 @@ export const TASK_OPERATION_DESCRIPTORS = Object.freeze([
|
|
|
169
176
|
...PUBLIC_OPERATION_DESCRIPTORS
|
|
170
177
|
]);
|
|
171
178
|
export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
172
|
-
'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
179
|
+
'task-create', 'task-qualification', 'sandbox-control', 'host-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
173
180
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
174
181
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
175
182
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
176
183
|
'task-orchestration', 'task-review', 'task-event', 'task-invalidation', 'task-lifecycle',
|
|
177
|
-
'task-finalization', 'task-override', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate'
|
|
184
|
+
'task-finalization', 'task-override', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
|
|
178
185
|
]);
|
|
179
186
|
export const PUBLIC_DISPATCHER_ROUTES = Object.freeze([
|
|
180
187
|
'agent-client', 'cp', 'data', 'decide', 'help', 'init', 'merge', 'run', 'sandbox', 'server', 'task', 'sync', 'update', 'version'
|
|
@@ -239,6 +246,33 @@ const TASK_CONTROL_MARKER_KEYS = [
|
|
|
239
246
|
'AGENT_INFRA_CONTROL_DIR',
|
|
240
247
|
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
241
248
|
];
|
|
249
|
+
const TASK_CONTROL_CONFIG_KEYS = [
|
|
250
|
+
'AGENT_INFRA_CONTROL_TOKEN',
|
|
251
|
+
'AGENT_INFRA_CONTROL_GENERATION',
|
|
252
|
+
'AGENT_INFRA_CONTROL_ROOT_ID',
|
|
253
|
+
'AGENT_INFRA_CONTROL_DIR',
|
|
254
|
+
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
255
|
+
];
|
|
256
|
+
export const SANDBOX_CONTROL_STATUS_MOUNT = '/run/agent-infra/control-status';
|
|
257
|
+
function nativeDirectoryProbe(candidate) {
|
|
258
|
+
try {
|
|
259
|
+
// The fixed mount is a trust anchor. Do not use a mutable node:fs export:
|
|
260
|
+
// a preload must not turn a mounted sandbox into the direct-host path by
|
|
261
|
+
// replacing the ordinary filesystem probe.
|
|
262
|
+
const binding = process.binding('fs');
|
|
263
|
+
if (!Function.prototype.toString.call(binding.internalModuleStat).includes('[native code]'))
|
|
264
|
+
return 'unknown';
|
|
265
|
+
const result = binding.internalModuleStat(candidate);
|
|
266
|
+
if (result === 1)
|
|
267
|
+
return 'present';
|
|
268
|
+
if (result === -2)
|
|
269
|
+
return 'absent';
|
|
270
|
+
return 'unknown';
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return 'unknown';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
242
276
|
function taskMarkerState(env) {
|
|
243
277
|
const present = (key) => Boolean(env[key]);
|
|
244
278
|
const taskId = present('AGENT_INFRA_TASK_ID');
|
|
@@ -253,6 +287,70 @@ function taskMarkerState(env) {
|
|
|
253
287
|
return 'branch-only';
|
|
254
288
|
return 'incomplete';
|
|
255
289
|
}
|
|
290
|
+
export function resolveSandboxControlTransport(env = process.env, options = {}) {
|
|
291
|
+
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
292
|
+
const fixedStatusDir = options.statusMountPath
|
|
293
|
+
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
294
|
+
const hasAnyMarker = TASK_MARKER_KEYS.some((key) => Boolean(env[key]))
|
|
295
|
+
|| Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING)
|
|
296
|
+
|| Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
297
|
+
const fixedStatusProbe = !env.AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT && path.isAbsolute(fixedStatusDir)
|
|
298
|
+
? nativeDirectoryProbe(fixedStatusDir) : 'absent';
|
|
299
|
+
if (fixedStatusProbe === 'unknown') {
|
|
300
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
301
|
+
}
|
|
302
|
+
const fixedStatusMounted = fixedStatusProbe === 'present';
|
|
303
|
+
const configuredStatusProbe = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
304
|
+
? nativeDirectoryProbe(configuredStatusDir) : 'absent';
|
|
305
|
+
if (configuredStatusProbe === 'unknown') {
|
|
306
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
307
|
+
}
|
|
308
|
+
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir) && configuredStatusProbe === 'present'
|
|
309
|
+
? configuredStatusDir
|
|
310
|
+
: fixedStatusMounted ? fixedStatusDir : null;
|
|
311
|
+
const statusMounted = statusDir !== null;
|
|
312
|
+
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
313
|
+
if (env.AGENT_INFRA_EXECUTOR_MANIFEST || env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING) {
|
|
314
|
+
return { kind: 'fail-closed', reasonCode: 'TASK_CONTROL_TRANSPORT_INVALID' };
|
|
315
|
+
}
|
|
316
|
+
const taskBound = Boolean(env.AGENT_INFRA_TASK_ID);
|
|
317
|
+
const runtime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
|
|
318
|
+
if (statusMounted) {
|
|
319
|
+
let sentinel;
|
|
320
|
+
try {
|
|
321
|
+
sentinel = readSandboxControlIdentitySentinel(statusDir);
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
const message = error instanceof Error ? error.message : '';
|
|
325
|
+
return { kind: 'fail-closed', reasonCode: message.endsWith('MISSING')
|
|
326
|
+
? 'SANDBOX_CONTROL_IDENTITY_MISSING'
|
|
327
|
+
: 'SANDBOX_CONTROL_IDENTITY_MALFORMED' };
|
|
328
|
+
}
|
|
329
|
+
if (sentinel.generation !== env.AGENT_INFRA_CONTROL_GENERATION) {
|
|
330
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_GENERATION_MISMATCH' };
|
|
331
|
+
}
|
|
332
|
+
if (env.AGENT_INFRA_CONTROL_ROOT_ID && sentinel.controlRootId !== env.AGENT_INFRA_CONTROL_ROOT_ID) {
|
|
333
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_ROOT_ID_MISMATCH' };
|
|
334
|
+
}
|
|
335
|
+
if (sentinel.mode === 'task-bound'
|
|
336
|
+
&& (sentinel.taskId !== env.AGENT_INFRA_TASK_ID || !runtime)) {
|
|
337
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
338
|
+
}
|
|
339
|
+
if (sentinel.mode === 'branch-only' && (taskBound || runtime)) {
|
|
340
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
341
|
+
}
|
|
342
|
+
if (!hasCompleteConfig) {
|
|
343
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
344
|
+
}
|
|
345
|
+
return { kind: 'broker-client', reasonCode: null };
|
|
346
|
+
}
|
|
347
|
+
if (!hasAnyMarker)
|
|
348
|
+
return { kind: 'direct-host', reasonCode: null };
|
|
349
|
+
if (!hasCompleteConfig || (taskBound && !runtime) || (!taskBound && runtime)) {
|
|
350
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
351
|
+
}
|
|
352
|
+
return { kind: 'broker-client', reasonCode: null };
|
|
353
|
+
}
|
|
256
354
|
export function hasTaskBoundMarker(env = process.env) {
|
|
257
355
|
return taskMarkerState(env) === 'task-bound';
|
|
258
356
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { parseReviewCommand } from '../task/review-command.js';
|
|
1
2
|
import { finalizeReviewSummary } from '../task/review-finalization.js';
|
|
2
3
|
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
|
|
3
4
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
5
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
5
|
-
import { ensureInternalHandlerRoute
|
|
6
|
+
import { ensureInternalHandlerRoute } from './cli-route-inventory.js';
|
|
6
7
|
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';
|
|
7
8
|
function failUsage(message) {
|
|
8
9
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -21,61 +22,15 @@ async function taskReview(args = []) {
|
|
|
21
22
|
process.stdout.write(USAGE);
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (!internalHandlerRoute('task-review', 'finalize-summary', args[1] ?? '')) {
|
|
29
|
-
failUsage(`unknown intent '${args[1]}'`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
let stage = '';
|
|
33
|
-
let artifact = '';
|
|
34
|
-
let dryRun = false;
|
|
35
|
-
let orchestrated = false;
|
|
36
|
-
let overrideTicket = '';
|
|
37
|
-
let overrideTarget = '';
|
|
38
|
-
let overrideScope = '';
|
|
39
|
-
const seen = new Set();
|
|
40
|
-
for (let index = 2; index < args.length; index += 1) {
|
|
41
|
-
const flag = args[index];
|
|
42
|
-
if (!['--stage', '--artifact', '--orchestrated', '--dry-run', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
43
|
-
failUsage(`unknown option '${flag}'`);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (seen.has(flag)) {
|
|
47
|
-
failUsage(`duplicate option '${flag}'`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
seen.add(flag);
|
|
51
|
-
if (flag === '--dry-run') {
|
|
52
|
-
dryRun = true;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (flag === '--orchestrated') {
|
|
56
|
-
orchestrated = true;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const value = args[++index];
|
|
60
|
-
if (!value || value.startsWith('--')) {
|
|
61
|
-
failUsage(`option '${flag}' requires a value`);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (flag === '--stage')
|
|
65
|
-
stage = value;
|
|
66
|
-
else if (flag === '--artifact')
|
|
67
|
-
artifact = value;
|
|
68
|
-
else if (flag === '--override-ticket')
|
|
69
|
-
overrideTicket = value;
|
|
70
|
-
else if (flag === '--override-target')
|
|
71
|
-
overrideTarget = value;
|
|
72
|
-
else if (flag === '--override-scope')
|
|
73
|
-
overrideScope = value;
|
|
25
|
+
let request;
|
|
26
|
+
try {
|
|
27
|
+
request = parseReviewCommand(args);
|
|
74
28
|
}
|
|
75
|
-
|
|
76
|
-
failUsage(
|
|
29
|
+
catch (error) {
|
|
30
|
+
failUsage(error instanceof Error ? error.message : String(error));
|
|
77
31
|
return;
|
|
78
32
|
}
|
|
33
|
+
const { stage, artifact, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
|
|
79
34
|
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
80
35
|
if (dryRunConflict) {
|
|
81
36
|
failUsage(dryRunConflict.message);
|
|
@@ -16,13 +16,15 @@ import { readSandboxControlManifest } from '../sandbox/control/lifecycle.js';
|
|
|
16
16
|
import { SANDBOX_CONTROL_FUTURE_SKEW_MS, SANDBOX_CONTROL_STATUS_STALE_MS } from '../sandbox/control/protocol.js';
|
|
17
17
|
import { getProcessStartTime } from '../server/process-state.js';
|
|
18
18
|
import { assertGitWorktreeBinding } from '../git/worktree-identity.js';
|
|
19
|
+
import { createManualValidationEvidence, manualValidationEvidenceSummary } from '../task/manual-validation-evidence.js';
|
|
20
|
+
import { writeJsonAtomic } from '../task/manual-validation-shared.js';
|
|
19
21
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
20
|
-
const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] -- <command> [args...]`;
|
|
22
|
+
const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] [--evidence-file <path>] -- <command> [args...]`;
|
|
21
23
|
const MAX_TIMEOUT_MS = 60 * 60 * 1000;
|
|
22
24
|
const RECOVERY_GRACE_MS = 30_000;
|
|
23
25
|
export function parseValidateArgs(args) {
|
|
24
26
|
if (args.includes('--help') || args.includes('-h')) {
|
|
25
|
-
return { target: '', scope: 'snapshot', timeoutMs: 300_000, format: 'text', command: [], help: true };
|
|
27
|
+
return { target: '', scope: 'snapshot', timeoutMs: 300_000, format: 'text', evidenceFile: null, command: [], help: true };
|
|
26
28
|
}
|
|
27
29
|
const separator = args.indexOf('--');
|
|
28
30
|
if (separator < 0)
|
|
@@ -32,6 +34,7 @@ export function parseValidateArgs(args) {
|
|
|
32
34
|
let scope = 'snapshot';
|
|
33
35
|
let timeoutMs = 300_000;
|
|
34
36
|
let format = 'text';
|
|
37
|
+
let evidenceFile = null;
|
|
35
38
|
const positionals = [];
|
|
36
39
|
for (let index = 0; index < optionArgs.length; index += 1) {
|
|
37
40
|
const arg = optionArgs[index];
|
|
@@ -53,6 +56,12 @@ export function parseValidateArgs(args) {
|
|
|
53
56
|
throw new Error('validate --format must be text or json');
|
|
54
57
|
format = value;
|
|
55
58
|
}
|
|
59
|
+
else if (arg === '--evidence-file') {
|
|
60
|
+
const value = optionArgs[++index];
|
|
61
|
+
if (!value || value.startsWith('-'))
|
|
62
|
+
throw new Error('validate --evidence-file requires a path');
|
|
63
|
+
evidenceFile = value;
|
|
64
|
+
}
|
|
56
65
|
else if (arg.startsWith('-')) {
|
|
57
66
|
throw new Error(`Unknown option: ${arg}`);
|
|
58
67
|
}
|
|
@@ -62,7 +71,7 @@ export function parseValidateArgs(args) {
|
|
|
62
71
|
}
|
|
63
72
|
if (positionals.length !== 1 || command.length === 0)
|
|
64
73
|
throw new Error(USAGE);
|
|
65
|
-
return { target: positionals[0], scope, timeoutMs, format, command, help: false };
|
|
74
|
+
return { target: positionals[0], scope, timeoutMs, format, evidenceFile, command, help: false };
|
|
66
75
|
}
|
|
67
76
|
function runValidationCommand(command, cwd, scope, timeoutMs, json) {
|
|
68
77
|
const [file, ...args] = command;
|
|
@@ -298,24 +307,35 @@ function taskValidate(args) {
|
|
|
298
307
|
: internalHandlerRoute('task-validate', 'inplace', options.scope)
|
|
299
308
|
? inplaceValidation(config, target, options)
|
|
300
309
|
: (() => { throw new Error('TASK_VALIDATE_SCOPE_UNREGISTERED'); })();
|
|
301
|
-
const
|
|
302
|
-
|
|
310
|
+
const cleanup = result.cleanup();
|
|
311
|
+
const evidence = createManualValidationEvidence({
|
|
312
|
+
mode: target.workspace.mode,
|
|
303
313
|
taskId: target.workspace.mode === 'task-bound' ? target.workspace.taskId : null,
|
|
304
314
|
branch: target.branch,
|
|
305
|
-
scope: options.scope,
|
|
306
315
|
commit,
|
|
316
|
+
recoverable: target.workspace.mode === 'task-bound',
|
|
317
|
+
scope: options.scope,
|
|
307
318
|
command: path.basename(options.command[0]),
|
|
308
319
|
startedAt,
|
|
309
320
|
completedAt: new Date().toISOString(),
|
|
310
321
|
exitCode: result.exitCode,
|
|
311
322
|
signal: result.signal,
|
|
312
|
-
cleanup
|
|
313
|
-
};
|
|
323
|
+
cleanup
|
|
324
|
+
});
|
|
325
|
+
if (options.evidenceFile) {
|
|
326
|
+
try {
|
|
327
|
+
writeJsonAtomic(path.resolve(options.evidenceFile), evidence);
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
throw new Error(`MANUAL_VALIDATION_EVIDENCE_WRITE_FAILED: ${error instanceof Error ? error.message : String(error)}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const evidenceSummary = manualValidationEvidenceSummary(evidence);
|
|
314
334
|
if (options.format === 'json') {
|
|
315
|
-
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: false, evidence, error: null })}\n`);
|
|
335
|
+
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: false, evidence: evidenceSummary, evidenceFile: options.evidenceFile, error: null })}\n`);
|
|
316
336
|
}
|
|
317
337
|
else {
|
|
318
|
-
process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${
|
|
338
|
+
process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${evidenceSummary.command}, cleanup=${evidenceSummary.cleanup}).\n`);
|
|
319
339
|
}
|
|
320
340
|
process.exitCode = result.exitCode;
|
|
321
341
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
// Inspect descendants without following symlinks. The caller supplies the trusted root.
|
|
4
|
+
export function inspectOwnedPath(rootInput, targetInput, expect) {
|
|
5
|
+
const root = path.resolve(rootInput);
|
|
6
|
+
const target = path.resolve(targetInput);
|
|
7
|
+
const relative = path.relative(root, target);
|
|
8
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative))
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
const rootStat = fs.lstatSync(root);
|
|
12
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
13
|
+
return null;
|
|
14
|
+
let current = root;
|
|
15
|
+
for (const segment of relative.split(path.sep)) {
|
|
16
|
+
current = path.join(current, segment);
|
|
17
|
+
const stat = fs.lstatSync(current);
|
|
18
|
+
if (stat.isSymbolicLink())
|
|
19
|
+
return null;
|
|
20
|
+
if (process.platform !== 'win32' && typeof process.getuid === 'function' && stat.uid !== process.getuid())
|
|
21
|
+
return null;
|
|
22
|
+
if (current === target)
|
|
23
|
+
return (expect === 'file' ? stat.isFile() : stat.isDirectory()) ? stat : null;
|
|
24
|
+
if (!stat.isDirectory())
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=owned-path.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function checkStatusBucket(status) {
|
|
2
|
+
const value = status.toLowerCase();
|
|
3
|
+
if (['pass', 'success', 'successful', 'neutral'].includes(value))
|
|
4
|
+
return 'pass';
|
|
5
|
+
if (['fail', 'failure', 'failed', 'error', 'timed_out', 'action_required'].includes(value))
|
|
6
|
+
return 'fail';
|
|
7
|
+
if (['cancel', 'cancelled', 'canceled', 'skipped', 'stale'].includes(value))
|
|
8
|
+
return 'cancel';
|
|
9
|
+
return 'pending';
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=check-status.js.map
|
|
@@ -168,6 +168,9 @@ function sanitizeMarkdownDocument(value, options = {}) {
|
|
|
168
168
|
return tail;
|
|
169
169
|
return ok(result + tail.value);
|
|
170
170
|
}
|
|
171
|
+
function canonicalizeCommentBody(value) {
|
|
172
|
+
return sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
173
|
+
}
|
|
171
174
|
function renderSafeCodeFence(value, language = '', reservedMarkers = [CONTROL_MARKER_PATTERN]) {
|
|
172
175
|
const content = markerSafe(normalized(value), reservedMarkers);
|
|
173
176
|
const longestRun = Math.max(0, ...(content.match(/`+/g) || []).map((run) => run.length));
|
|
@@ -222,5 +225,5 @@ function splitDocumentPlaceholder(value, placeholder) {
|
|
|
222
225
|
}
|
|
223
226
|
return ok({ prefix: source.slice(0, position), suffix: source.slice(position + placeholder.length) });
|
|
224
227
|
}
|
|
225
|
-
export { CONTROL_MARKER_PATTERN, escapeHtmlAttribute, escapeHtmlText, escapeMarkdownLiteral, fenceRanges, renderSafeCodeFence, sanitizeMarkdownDocument, splitDocumentPlaceholder };
|
|
228
|
+
export { CONTROL_MARKER_PATTERN, escapeHtmlAttribute, escapeHtmlText, escapeMarkdownLiteral, fenceRanges, renderSafeCodeFence, canonicalizeCommentBody, sanitizeMarkdownDocument, splitDocumentPlaceholder };
|
|
226
229
|
//# sourceMappingURL=comment-safety.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { COMPLETION_BACKFILL_FAMILIES, inspectCompletionArtifacts } from '../task/finalization-artifacts.js';
|
|
4
|
-
import { parseArtifactName } from '../task/artifact-
|
|
4
|
+
import { parseArtifactName } from '../task/artifact-name.js';
|
|
5
5
|
import { parseTaskFrontmatter } from '../task/frontmatter.js';
|
|
6
6
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
7
7
|
import { getOpenWorkflowWarnings } from '../task/workflow-warnings.js';
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { checkStatusBucket } from './check-status.js';
|
|
2
|
+
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}}}}}}}}`;
|
|
3
|
+
function normalizeIssue(remote, repository, fallbackNumber) {
|
|
4
|
+
const number = Number.isInteger(remote.number) && Number(remote.number) > 0
|
|
5
|
+
? Number(remote.number)
|
|
6
|
+
: fallbackNumber;
|
|
7
|
+
if (!number || !Number.isSafeInteger(number) || remote.pull_request)
|
|
8
|
+
return null;
|
|
9
|
+
return {
|
|
10
|
+
repository,
|
|
11
|
+
number,
|
|
12
|
+
databaseId: Number.isInteger(remote.id) ? Number(remote.id) : null,
|
|
13
|
+
nodeId: remote.node_id || `issue-${number}`,
|
|
14
|
+
url: remote.html_url || `https://github.com/${repository}/issues/${number}`,
|
|
15
|
+
state: String(remote.state || '').toLowerCase() === 'closed' ? 'closed' : 'open',
|
|
16
|
+
title: remote.title || '',
|
|
17
|
+
body: remote.body || '',
|
|
18
|
+
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
19
|
+
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
20
|
+
milestone: remote.milestone?.title || null,
|
|
21
|
+
issueType: remote.type?.name || null,
|
|
22
|
+
fields: {}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function fieldKind(value) {
|
|
26
|
+
if (value.__typename === 'IssueFieldSingleSelect' || value.__typename === 'IssueFieldSingleSelectValue')
|
|
27
|
+
return 'single-select';
|
|
28
|
+
if (value.__typename === 'IssueFieldDate' || value.__typename === 'IssueFieldDateValue')
|
|
29
|
+
return 'date';
|
|
30
|
+
if (value.__typename === 'IssueFieldText' || value.__typename === 'IssueFieldTextValue')
|
|
31
|
+
return 'text';
|
|
32
|
+
if (value.__typename === 'IssueFieldNumber' || value.__typename === 'IssueFieldNumberValue')
|
|
33
|
+
return 'number';
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function normalizeFieldSchemas(value) {
|
|
37
|
+
if (!Array.isArray(value))
|
|
38
|
+
return [];
|
|
39
|
+
return value.flatMap((raw) => {
|
|
40
|
+
const item = raw;
|
|
41
|
+
const kind = fieldKind(item);
|
|
42
|
+
return item.id && item.name && kind ? [{
|
|
43
|
+
id: item.id,
|
|
44
|
+
name: item.name,
|
|
45
|
+
kind,
|
|
46
|
+
options: (item.options || []).flatMap((option) => option.id && option.name ? [{ id: option.id, name: option.name }] : [])
|
|
47
|
+
}] : [];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function normalizeCurrentFields(value) {
|
|
51
|
+
const issue = value?.data?.repository?.issue;
|
|
52
|
+
const type = issue?.issueType?.name ? {
|
|
53
|
+
id: issue.issueType.id || issue.issueType.name,
|
|
54
|
+
name: issue.issueType.name,
|
|
55
|
+
fields: normalizeFieldSchemas(issue.issueType.pinnedFields)
|
|
56
|
+
} : null;
|
|
57
|
+
const values = (issue?.issueFieldValues?.nodes || []).flatMap((raw) => {
|
|
58
|
+
const item = raw;
|
|
59
|
+
const kind = fieldKind(item);
|
|
60
|
+
const value = kind === 'single-select' ? item.name : item.value;
|
|
61
|
+
return item.field?.name && kind && value !== undefined
|
|
62
|
+
? [{ id: item.field.id || item.field.name, name: item.field.name, kind, value: value ?? null }]
|
|
63
|
+
: [];
|
|
64
|
+
});
|
|
65
|
+
return { issueId: issue?.id || null, type, values };
|
|
66
|
+
}
|
|
67
|
+
function graphState(client, repository, issue, cwd) {
|
|
68
|
+
const [owner, name] = repository.split('/');
|
|
69
|
+
if (!owner || !name)
|
|
70
|
+
return null;
|
|
71
|
+
const current = client.json([
|
|
72
|
+
'api', 'graphql', '-f', `query=${ISSUE_FIELDS_QUERY}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${issue}`
|
|
73
|
+
], { cwd });
|
|
74
|
+
if (!current.ok)
|
|
75
|
+
return null;
|
|
76
|
+
return normalizeCurrentFields(current.value);
|
|
77
|
+
}
|
|
78
|
+
function fetchIssue(client, repository, issue, cwd) {
|
|
79
|
+
return client.json(['api', `repos/${repository}/issues/${issue}`], { cwd });
|
|
80
|
+
}
|
|
81
|
+
function inspectGitHubIssue(client, repository, issue, cwd) {
|
|
82
|
+
const fetched = fetchIssue(client, repository, issue, cwd);
|
|
83
|
+
if (!fetched.ok)
|
|
84
|
+
return fetched;
|
|
85
|
+
const snapshot = normalizeIssue(fetched.value, repository, issue);
|
|
86
|
+
return snapshot
|
|
87
|
+
? { ok: true, value: snapshot }
|
|
88
|
+
: { ok: false, error: { code: 'ISSUE_IDENTITY_INVALID', message: 'Remote resource is not a valid Issue', retryable: false } };
|
|
89
|
+
}
|
|
90
|
+
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}}}}}`;
|
|
91
|
+
function normalizePullRequest(remote, repository) {
|
|
92
|
+
const number = Number(remote.number);
|
|
93
|
+
const headRepository = remote.head?.repo?.full_name;
|
|
94
|
+
const baseRepository = remote.base?.repo?.full_name;
|
|
95
|
+
if (!Number.isInteger(number) || number <= 0 || !remote.node_id || !remote.html_url ||
|
|
96
|
+
!remote.head?.ref || !remote.head.sha || !headRepository || !remote.base?.ref || !baseRepository)
|
|
97
|
+
return null;
|
|
98
|
+
const mergeabilityDetail = remote.mergeable_state?.trim().toLowerCase() || null;
|
|
99
|
+
const mergeability = remote.mergeable === false
|
|
100
|
+
? { state: 'conflicting', detail: mergeabilityDetail }
|
|
101
|
+
: remote.mergeable === true && mergeabilityDetail !== 'dirty'
|
|
102
|
+
? { state: 'mergeable', detail: mergeabilityDetail }
|
|
103
|
+
: { state: 'unknown', detail: mergeabilityDetail };
|
|
104
|
+
return {
|
|
105
|
+
repository,
|
|
106
|
+
number,
|
|
107
|
+
nodeId: remote.node_id,
|
|
108
|
+
url: remote.html_url,
|
|
109
|
+
state: remote.state === 'closed' ? 'closed' : 'open',
|
|
110
|
+
title: remote.title || '',
|
|
111
|
+
body: remote.body || '',
|
|
112
|
+
draft: Boolean(remote.draft),
|
|
113
|
+
head: { repository: headRepository, ref: remote.head.ref, sha: remote.head.sha },
|
|
114
|
+
base: { repository: baseRepository, ref: remote.base.ref, sha: remote.base.sha || '' },
|
|
115
|
+
mergedAt: remote.merged_at || null,
|
|
116
|
+
mergeCommitSha: remote.merge_commit_sha || null,
|
|
117
|
+
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
118
|
+
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
119
|
+
milestone: remote.milestone?.title || null,
|
|
120
|
+
mergeability
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function normalizeClosingPullRequest(node, repository) {
|
|
124
|
+
return normalizePullRequest({
|
|
125
|
+
number: node.number,
|
|
126
|
+
node_id: node.id,
|
|
127
|
+
html_url: node.url,
|
|
128
|
+
state: node.state === 'OPEN' ? 'open' : 'closed',
|
|
129
|
+
title: node.title,
|
|
130
|
+
body: node.body,
|
|
131
|
+
draft: node.isDraft,
|
|
132
|
+
head: { ref: node.headRefName, sha: node.headRefOid, repo: node.headRepository ? { full_name: node.headRepository.nameWithOwner } : null },
|
|
133
|
+
base: { ref: node.baseRefName, sha: node.baseRefOid, repo: node.baseRepository ? { full_name: node.baseRepository.nameWithOwner } : null },
|
|
134
|
+
merged_at: node.mergedAt,
|
|
135
|
+
merge_commit_sha: node.mergeCommit?.oid,
|
|
136
|
+
labels: node.labels?.nodes,
|
|
137
|
+
assignees: node.assignees?.nodes,
|
|
138
|
+
milestone: node.milestone
|
|
139
|
+
}, repository);
|
|
140
|
+
}
|
|
141
|
+
function inspectGitHubIssueClosingChangeRequests(client, repository, issueNumber, cwd) {
|
|
142
|
+
const [owner, name] = repository.split('/');
|
|
143
|
+
if (!owner || !name || !Number.isInteger(issueNumber) || issueNumber <= 0)
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Repository or Issue identity is invalid', retryable: false }
|
|
147
|
+
};
|
|
148
|
+
const candidates = [];
|
|
149
|
+
let cursor = null;
|
|
150
|
+
for (;;) {
|
|
151
|
+
const args = [
|
|
152
|
+
'api', 'graphql', '-f', `query=${CLOSING_PULL_REQUESTS_QUERY}`,
|
|
153
|
+
'-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `issue=${issueNumber}`
|
|
154
|
+
];
|
|
155
|
+
if (cursor)
|
|
156
|
+
args.push('-F', `cursor=${cursor}`);
|
|
157
|
+
const response = client.json(args, { cwd });
|
|
158
|
+
if (!response.ok)
|
|
159
|
+
return response;
|
|
160
|
+
const connection = response.value?.data?.repository?.issue?.closedByPullRequestsReferences;
|
|
161
|
+
if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo)
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request response is incomplete', retryable: false }
|
|
165
|
+
};
|
|
166
|
+
for (const node of connection.nodes) {
|
|
167
|
+
const normalized = normalizeClosingPullRequest(node, repository);
|
|
168
|
+
if (!normalized)
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request identity is incomplete', retryable: false }
|
|
172
|
+
};
|
|
173
|
+
candidates.push(normalized);
|
|
174
|
+
}
|
|
175
|
+
if (!connection.pageInfo.hasNextPage)
|
|
176
|
+
return { ok: true, value: candidates };
|
|
177
|
+
if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor)
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request pagination cursor is invalid', retryable: false }
|
|
181
|
+
};
|
|
182
|
+
cursor = connection.pageInfo.endCursor;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function inspectGitHubPullRequest(client, repository, number, cwd) {
|
|
186
|
+
const fetched = client.json(['api', `repos/${repository}/pulls/${number}`], { cwd });
|
|
187
|
+
if (!fetched.ok)
|
|
188
|
+
return fetched;
|
|
189
|
+
const pullRequest = normalizePullRequest(fetched.value, repository);
|
|
190
|
+
return pullRequest
|
|
191
|
+
? { ok: true, value: pullRequest }
|
|
192
|
+
: { ok: false, error: { code: 'PR_IDENTITY_INVALID', message: 'Remote resource is not a valid pull request', retryable: false } };
|
|
193
|
+
}
|
|
194
|
+
function normalizeGitHubChecks(value) {
|
|
195
|
+
if (!Array.isArray(value))
|
|
196
|
+
return [];
|
|
197
|
+
return value.flatMap((raw) => {
|
|
198
|
+
const item = raw;
|
|
199
|
+
const name = String(item.name || item.context || '');
|
|
200
|
+
return name ? [{
|
|
201
|
+
name,
|
|
202
|
+
status: checkStatusBucket(String(item.bucket || item.status || item.conclusion || item.state || '')),
|
|
203
|
+
conclusion: item.conclusion ? String(item.conclusion) : item.state ? String(item.state) : null,
|
|
204
|
+
detailsUrl: item.link ? String(item.link) : item.detailsUrl ? String(item.detailsUrl) : null
|
|
205
|
+
}] : [];
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function inspectGitHubRequiredChecks(client, repository, number, cwd) {
|
|
209
|
+
const inspected = client.json([
|
|
210
|
+
'pr', 'checks', String(number), '--repo', repository,
|
|
211
|
+
'--json', 'name,state,bucket,link,workflow,startedAt,completedAt'
|
|
212
|
+
], { cwd });
|
|
213
|
+
return inspected.ok
|
|
214
|
+
? { ok: true, value: normalizeGitHubChecks(inspected.value) }
|
|
215
|
+
: { ok: false, error: inspected.error };
|
|
216
|
+
}
|
|
217
|
+
function parseRunJobIdentity(detailsUrl) {
|
|
218
|
+
try {
|
|
219
|
+
const url = new URL(detailsUrl);
|
|
220
|
+
if (url.hostname !== 'github.com')
|
|
221
|
+
return null;
|
|
222
|
+
const match = url.pathname.match(/\/actions\/runs\/(\d+)(?:\/job\/(\d+))?/);
|
|
223
|
+
if (!match)
|
|
224
|
+
return null;
|
|
225
|
+
return { runId: Number(match[1]), jobId: match[2] ? Number(match[2]) : null };
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function fetchCheckLogText(client, args, cwd) {
|
|
232
|
+
if (!client.text)
|
|
233
|
+
return { ok: false, error: { code: 'PLATFORM_CLIENT_TEXT_UNAVAILABLE', message: 'Platform client does not support text responses', retryable: false } };
|
|
234
|
+
const fetched = client.text(args, { cwd });
|
|
235
|
+
if (fetched.ok || args[0] !== 'api' || !/response contains terminal escape sequences/i.test(fetched.error.message)) {
|
|
236
|
+
return fetched;
|
|
237
|
+
}
|
|
238
|
+
return client.text([...args, '--allow-escape-sequences'], { cwd });
|
|
239
|
+
}
|
|
240
|
+
export { ISSUE_FIELDS_QUERY, graphState, inspectGitHubIssue, normalizePullRequest, inspectGitHubIssueClosingChangeRequests, inspectGitHubPullRequest, inspectGitHubRequiredChecks, parseRunJobIdentity, fetchCheckLogText };
|
|
241
|
+
//# sourceMappingURL=github-data.js.map
|