vgxness 1.19.5 → 1.19.6
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.
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export function buildResumeAfterApprovalResult(input) {
|
|
2
|
+
const operationKind = stringFromObject(input.executorOutput, 'operationKind') ?? operationKindFromOperation(input.operation);
|
|
3
|
+
const command = operationKind === 'allowlisted-command' ? extractAllowlistedCommandMetadata(input.executorOutput) : undefined;
|
|
4
|
+
return {
|
|
5
|
+
kind: 'resume-after-approval-result',
|
|
6
|
+
version: 1,
|
|
7
|
+
operationKind,
|
|
8
|
+
runId: input.runId,
|
|
9
|
+
approvalId: input.approvalId,
|
|
10
|
+
attemptId: input.attemptId,
|
|
11
|
+
permissionEventId: input.permissionEventId,
|
|
12
|
+
pendingExecutionEventId: input.pendingExecutionEventId,
|
|
13
|
+
operationFingerprint: input.operationFingerprint,
|
|
14
|
+
executor: input.executorName,
|
|
15
|
+
status: input.status,
|
|
16
|
+
...(command === undefined ? {} : { command }),
|
|
17
|
+
error: input.error === undefined ? null : { code: input.error.code, message: input.error.message },
|
|
18
|
+
safety: buildSafety(input.executorOutput),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function extractAllowlistedCommandMetadata(output) {
|
|
22
|
+
if (!isObject(output))
|
|
23
|
+
return undefined;
|
|
24
|
+
const command = isObject(output.command) ? output.command : output;
|
|
25
|
+
const commandId = stringField(command.commandId) ?? stringField(output.commandId);
|
|
26
|
+
const argv = stringArray(command.argv) ?? stringArray(output.argv);
|
|
27
|
+
if (commandId === undefined || argv === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const result = { commandId, argv };
|
|
30
|
+
setOptionalNumberOrNull(result, 'exitCode', command.exitCode);
|
|
31
|
+
setOptionalStringOrNull(result, 'signal', command.signal);
|
|
32
|
+
setOptionalBoolean(result, 'timedOut', command.timedOut);
|
|
33
|
+
setOptionalBoolean(result, 'outputTruncated', command.outputTruncated);
|
|
34
|
+
setOptionalNumber(result, 'stdoutBytes', command.stdoutBytes);
|
|
35
|
+
setOptionalNumber(result, 'stderrBytes', command.stderrBytes);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function buildSafety(output) {
|
|
39
|
+
const source = isObject(output) && isObject(output.command) && isObject(output.command.safety) ? output.command.safety : isObject(output) && isObject(output.safety) ? output.safety : undefined;
|
|
40
|
+
return {
|
|
41
|
+
metadataOnly: true,
|
|
42
|
+
rawStdoutPersisted: false,
|
|
43
|
+
rawStderrPersisted: false,
|
|
44
|
+
providerPayloadPersisted: false,
|
|
45
|
+
executorOutputWhitelisted: true,
|
|
46
|
+
...(typeof source?.boundedProcess === 'boolean' ? { boundedProcess: source.boundedProcess } : {}),
|
|
47
|
+
...(typeof source?.shellDisabled === 'boolean' ? { shellDisabled: source.shellDisabled } : {}),
|
|
48
|
+
...(typeof source?.argvArrayOnly === 'boolean' ? { argvArrayOnly: source.argvArrayOnly } : {}),
|
|
49
|
+
...(typeof source?.providerConfigWritesBlocked === 'boolean' ? { providerConfigWritesBlocked: source.providerConfigWritesBlocked } : {}),
|
|
50
|
+
...(typeof source?.createsSandbox === 'boolean' ? { createsSandbox: source.createsSandbox } : {}),
|
|
51
|
+
...(typeof source?.createsWorktree === 'boolean' ? { createsWorktree: source.createsWorktree } : {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function operationKindFromOperation(operation) {
|
|
55
|
+
return isObject(operation.input) && typeof operation.input.operationKind === 'string' ? operation.input.operationKind : 'generic-operation';
|
|
56
|
+
}
|
|
57
|
+
function stringFromObject(value, key) {
|
|
58
|
+
return isObject(value) ? stringField(value[key]) : undefined;
|
|
59
|
+
}
|
|
60
|
+
function stringField(value) {
|
|
61
|
+
return typeof value === 'string' ? value : undefined;
|
|
62
|
+
}
|
|
63
|
+
function stringArray(value) {
|
|
64
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string') ? value : undefined;
|
|
65
|
+
}
|
|
66
|
+
function setOptionalNumber(target, key, value) {
|
|
67
|
+
if (typeof value === 'number')
|
|
68
|
+
target[key] = value;
|
|
69
|
+
}
|
|
70
|
+
function setOptionalBoolean(target, key, value) {
|
|
71
|
+
if (typeof value === 'boolean')
|
|
72
|
+
target[key] = value;
|
|
73
|
+
}
|
|
74
|
+
function setOptionalNumberOrNull(target, key, value) {
|
|
75
|
+
if (typeof value === 'number' || value === null)
|
|
76
|
+
target[key] = value;
|
|
77
|
+
}
|
|
78
|
+
function setOptionalStringOrNull(target, key, value) {
|
|
79
|
+
if (typeof value === 'string' || value === null)
|
|
80
|
+
target[key] = value;
|
|
81
|
+
}
|
|
82
|
+
function isObject(value) {
|
|
83
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
84
|
+
}
|
package/dist/runs/run-service.js
CHANGED
|
@@ -7,6 +7,7 @@ import { evaluateOperationRetry as evaluateOperationRetryPolicy } from './operat
|
|
|
7
7
|
import { AllowlistedApplyProgressCommandExecutor } from '../workflows/command-allowlist-adapter.js';
|
|
8
8
|
import { RunRepository, } from './repositories/runs.js';
|
|
9
9
|
import { buildRunInsights, buildRunOperatorResumePlan } from './run-insights.js';
|
|
10
|
+
import { buildResumeAfterApprovalResult } from './resume-after-approval-result.js';
|
|
10
11
|
const preflightSafety = {
|
|
11
12
|
operationExecuted: false,
|
|
12
13
|
executorInvoked: false,
|
|
@@ -459,7 +460,19 @@ export class RunService {
|
|
|
459
460
|
const executed = input.executor.execute({ runId: approval.value.runId, operation });
|
|
460
461
|
if (!executed.ok) {
|
|
461
462
|
const error = { code: executed.error.code, message: executed.error.message };
|
|
462
|
-
const
|
|
463
|
+
const result = buildResumeAfterApprovalResult({
|
|
464
|
+
runId: approval.value.runId,
|
|
465
|
+
approvalId: approval.value.id,
|
|
466
|
+
attemptId: reserved.value.id,
|
|
467
|
+
permissionEventId: permissionEvent.id,
|
|
468
|
+
pendingExecutionEventId: pendingExecutionEvent.id,
|
|
469
|
+
operationFingerprint: replayGuard.operationFingerprint,
|
|
470
|
+
executorName: input.executor.name,
|
|
471
|
+
operation,
|
|
472
|
+
status: 'failed',
|
|
473
|
+
error,
|
|
474
|
+
});
|
|
475
|
+
const attempt = this.runs.completeOperationAttempt({ attemptId: reserved.value.id, status: 'failed', output: result, error });
|
|
463
476
|
if (!attempt.ok)
|
|
464
477
|
return attempt;
|
|
465
478
|
const executionEvent = this.runs.appendEvent({
|
|
@@ -478,6 +491,7 @@ export class RunService {
|
|
|
478
491
|
operationFingerprint: replayGuard.operationFingerprint,
|
|
479
492
|
resumedFromApprovalId: approval.value.id,
|
|
480
493
|
executorInvoked: true,
|
|
494
|
+
result,
|
|
481
495
|
error,
|
|
482
496
|
}),
|
|
483
497
|
relatedType: 'approval',
|
|
@@ -486,7 +500,7 @@ export class RunService {
|
|
|
486
500
|
if (!executionEvent.ok)
|
|
487
501
|
return { ok: false, error: executionEvent.error };
|
|
488
502
|
const linkedAttempt = this.runs.attachAttemptResultEvent(attempt.value.id, executionEvent.value.id);
|
|
489
|
-
const evidence = appendApplyProgressAllowlistEvidenceIfNeeded(this.runs, approval.value.id, permissionEvent, pendingExecutionEvent, linkedAttempt.ok ? linkedAttempt.value : attempt.value, executionEvent.value, replayGuard.operationFingerprint, input.executor.name, operation,
|
|
503
|
+
const evidence = appendApplyProgressAllowlistEvidenceIfNeeded(this.runs, approval.value.id, permissionEvent, pendingExecutionEvent, linkedAttempt.ok ? linkedAttempt.value : attempt.value, executionEvent.value, replayGuard.operationFingerprint, input.executor.name, operation, result);
|
|
490
504
|
if (!evidence.ok)
|
|
491
505
|
return { ok: false, error: evidence.error };
|
|
492
506
|
return {
|
|
@@ -498,12 +512,25 @@ export class RunService {
|
|
|
498
512
|
attempt: linkedAttempt.ok ? linkedAttempt.value : attempt.value,
|
|
499
513
|
executionEvent: executionEvent.value,
|
|
500
514
|
status: 'failed',
|
|
515
|
+
output: result,
|
|
501
516
|
...(evidence.value === undefined ? {} : evidence.value),
|
|
502
517
|
},
|
|
503
518
|
};
|
|
504
519
|
}
|
|
505
520
|
const output = executed.value.output;
|
|
506
|
-
const
|
|
521
|
+
const result = buildResumeAfterApprovalResult({
|
|
522
|
+
runId: approval.value.runId,
|
|
523
|
+
approvalId: approval.value.id,
|
|
524
|
+
attemptId: reserved.value.id,
|
|
525
|
+
permissionEventId: permissionEvent.id,
|
|
526
|
+
pendingExecutionEventId: pendingExecutionEvent.id,
|
|
527
|
+
operationFingerprint: replayGuard.operationFingerprint,
|
|
528
|
+
executorName: input.executor.name,
|
|
529
|
+
operation,
|
|
530
|
+
status: 'succeeded',
|
|
531
|
+
...(output === undefined ? {} : { executorOutput: output }),
|
|
532
|
+
});
|
|
533
|
+
const attempt = this.runs.completeOperationAttempt({ attemptId: reserved.value.id, status: 'succeeded', output: result });
|
|
507
534
|
if (!attempt.ok)
|
|
508
535
|
return attempt;
|
|
509
536
|
const executionEvent = this.runs.appendEvent({
|
|
@@ -522,7 +549,7 @@ export class RunService {
|
|
|
522
549
|
operationFingerprint: replayGuard.operationFingerprint,
|
|
523
550
|
resumedFromApprovalId: approval.value.id,
|
|
524
551
|
executorInvoked: true,
|
|
525
|
-
|
|
552
|
+
result,
|
|
526
553
|
}),
|
|
527
554
|
relatedType: 'approval',
|
|
528
555
|
relatedId: approval.value.id,
|
|
@@ -530,7 +557,7 @@ export class RunService {
|
|
|
530
557
|
if (!executionEvent.ok)
|
|
531
558
|
return { ok: false, error: executionEvent.error };
|
|
532
559
|
const linkedAttempt = this.runs.attachAttemptResultEvent(attempt.value.id, executionEvent.value.id);
|
|
533
|
-
const evidence = appendApplyProgressAllowlistEvidenceIfNeeded(this.runs, approval.value.id, permissionEvent, pendingExecutionEvent, linkedAttempt.ok ? linkedAttempt.value : attempt.value, executionEvent.value, replayGuard.operationFingerprint, input.executor.name, operation,
|
|
560
|
+
const evidence = appendApplyProgressAllowlistEvidenceIfNeeded(this.runs, approval.value.id, permissionEvent, pendingExecutionEvent, linkedAttempt.ok ? linkedAttempt.value : attempt.value, executionEvent.value, replayGuard.operationFingerprint, input.executor.name, operation, result);
|
|
534
561
|
if (!evidence.ok)
|
|
535
562
|
return { ok: false, error: evidence.error };
|
|
536
563
|
return {
|
|
@@ -542,7 +569,7 @@ export class RunService {
|
|
|
542
569
|
attempt: linkedAttempt.ok ? linkedAttempt.value : attempt.value,
|
|
543
570
|
executionEvent: executionEvent.value,
|
|
544
571
|
status: 'succeeded',
|
|
545
|
-
|
|
572
|
+
output: result,
|
|
546
573
|
...(evidence.value === undefined ? {} : evidence.value),
|
|
547
574
|
},
|
|
548
575
|
};
|
|
@@ -1002,24 +1029,21 @@ function workflowConflictDecision(input, runWorkflow) {
|
|
|
1002
1029
|
auditEvidence: [`runWorkflow:${runWorkflow}`, `explicitWorkflow:${input.workflow}`],
|
|
1003
1030
|
};
|
|
1004
1031
|
}
|
|
1005
|
-
function appendApplyProgressAllowlistEvidenceIfNeeded(runs, approvalId, permissionEvent, pendingExecutionEvent, attempt, executionEvent, operationFingerprint, executorName, operation,
|
|
1032
|
+
function appendApplyProgressAllowlistEvidenceIfNeeded(runs, approvalId, permissionEvent, pendingExecutionEvent, attempt, executionEvent, operationFingerprint, executorName, operation, result) {
|
|
1006
1033
|
if (!isApplyProgressAllowlistedCommandOperation(operation))
|
|
1007
1034
|
return { ok: true, value: undefined };
|
|
1008
1035
|
const base = {
|
|
1009
|
-
|
|
1010
|
-
operationKind: 'allowlisted-command',
|
|
1011
|
-
runId: attempt.runId,
|
|
1012
|
-
approvalId,
|
|
1013
|
-
attemptId: attempt.id,
|
|
1014
|
-
pendingExecutionEventId: pendingExecutionEvent.id,
|
|
1015
|
-
permissionEventId: permissionEvent.id,
|
|
1036
|
+
...result,
|
|
1016
1037
|
executionEventId: executionEvent.id,
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1038
|
+
audit: {
|
|
1039
|
+
metadataOnly: true,
|
|
1040
|
+
operationFingerprint,
|
|
1041
|
+
executor: executorName,
|
|
1042
|
+
approvalId,
|
|
1043
|
+
pendingExecutionEventId: pendingExecutionEvent.id,
|
|
1044
|
+
permissionEventId: permissionEvent.id,
|
|
1045
|
+
attemptId: attempt.id,
|
|
1046
|
+
},
|
|
1023
1047
|
};
|
|
1024
1048
|
const evidence = runs.appendEvent({
|
|
1025
1049
|
runId: attempt.runId,
|
|
@@ -162,6 +162,11 @@ function acceptedEvidence(workspaceRoot, cwd, checks) {
|
|
|
162
162
|
enforceable: true,
|
|
163
163
|
createsSandbox: false,
|
|
164
164
|
createsWorktree: false,
|
|
165
|
+
filesystemIsolation: false,
|
|
166
|
+
networkIsolation: false,
|
|
167
|
+
shellDisabled: true,
|
|
168
|
+
argvArrayOnly: true,
|
|
169
|
+
providerConfigWritesBlocked: true,
|
|
165
170
|
workspaceRoot,
|
|
166
171
|
cwd,
|
|
167
172
|
capabilities: {
|
|
@@ -55,16 +55,31 @@ export class CommandAllowlistAdapter {
|
|
|
55
55
|
const result = this.runner(this.request);
|
|
56
56
|
if (!result.ok)
|
|
57
57
|
throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
58
|
+
const stdoutBytes = Buffer.byteLength(result.value.stdout);
|
|
59
|
+
const stderrBytes = Buffer.byteLength(result.value.stderr);
|
|
60
|
+
const safety = {
|
|
61
|
+
strategy: result.value.evidence.strategy,
|
|
62
|
+
boundedProcess: true,
|
|
63
|
+
createsSandbox: result.value.evidence.createsSandbox,
|
|
64
|
+
createsWorktree: result.value.evidence.createsWorktree,
|
|
65
|
+
filesystemIsolation: result.value.evidence.filesystemIsolation,
|
|
66
|
+
networkIsolation: result.value.evidence.networkIsolation,
|
|
67
|
+
shellDisabled: result.value.evidence.shellDisabled,
|
|
68
|
+
argvArrayOnly: result.value.evidence.argvArrayOnly,
|
|
69
|
+
providerConfigWritesBlocked: result.value.evidence.providerConfigWritesBlocked,
|
|
70
|
+
};
|
|
58
71
|
const output = {
|
|
59
|
-
|
|
72
|
+
commandId: this.commandId,
|
|
60
73
|
executable: this.entry.executable,
|
|
61
|
-
|
|
74
|
+
argv: [this.entry.executable, ...this.entry.args],
|
|
75
|
+
cwd: this.plan.request.cwd,
|
|
62
76
|
exitCode: result.value.exitCode,
|
|
63
77
|
signal: result.value.signal,
|
|
64
78
|
timedOut: result.value.timedOut,
|
|
65
79
|
outputTruncated: result.value.outputTruncated,
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
stdoutBytes,
|
|
81
|
+
stderrBytes,
|
|
82
|
+
safety,
|
|
68
83
|
};
|
|
69
84
|
const failed = result.value.timedOut || result.value.exitCode !== 0;
|
|
70
85
|
return {
|
|
@@ -77,6 +92,11 @@ export class CommandAllowlistAdapter {
|
|
|
77
92
|
shell: false,
|
|
78
93
|
argv: [this.entry.executable, ...this.entry.args],
|
|
79
94
|
cwd: this.plan.request.cwd,
|
|
95
|
+
stdoutBytes,
|
|
96
|
+
stderrBytes,
|
|
97
|
+
outputTruncated: result.value.outputTruncated,
|
|
98
|
+
timedOut: result.value.timedOut,
|
|
99
|
+
safety,
|
|
80
100
|
},
|
|
81
101
|
...(failed
|
|
82
102
|
? {
|