11import { rmSync } from "node:fs"
22import { appendFile , mkdir , readFile , rm , writeFile } from "node:fs/promises"
3- import { join , resolve } from "node:path"
3+ import { join , relative , resolve } from "node:path"
44import { spawn } from "node:child_process"
55import { materializeExternalNativePackage , materializeRuntimeSources , normalizeExternalPackageSource , normalizeRuntimeSources , parseExternalPackageSourcePolicy , validateRuntimeSourceModel } from "./materialize-external-native-package.mjs"
66import { readNativeResult } from "./native-result-file.mjs"
@@ -14,6 +14,8 @@ const outputPath = process.env.GITHUB_OUTPUT
1414const MAX_CAPTURE_BYTES = 32768
1515const MAX_OUTPUT_CHARS = 8192
1616const secretValues = [ "OPENAI_API_KEY" , "MODEL_PROVIDER_SECRET_1" , "MODEL_PROVIDER_SECRET_2" , "MODEL_PROVIDER_SECRET_3" , "MODEL_PROVIDER_SECRET_4" , "MODEL_PROVIDER_SECRET_5" , "GITHUB_TOKEN" , "GH_TOKEN" , "ACCESS_TOKEN" , "EXTERNAL_PACKAGE_SOURCE_POLICY" ] . map ( ( name ) => process . env [ name ] ) . filter ( Boolean )
17+ let privateRuntimeSourceRoot = ""
18+ let privateRuntimeSourceRootForSanitization = ""
1719function redact ( value ) {
1820 if ( typeof value === "string" ) return secretValues . reduce ( ( output , secret ) => output . split ( secret ) . join ( "[REDACTED]" ) , value )
1921 if ( Array . isArray ( value ) ) return value . map ( redact )
@@ -173,6 +175,45 @@ function projections(value, runtimeResult) {
173175 return output
174176}
175177
178+ function workflowPath ( path ) {
179+ const relativePath = relative ( workspace , resolve ( path ) )
180+ return relativePath && ! relativePath . startsWith ( ".." ) ? relativePath . replaceAll ( "\\" , "/" ) : ".codebox/agent-task-workflow-result.json"
181+ }
182+
183+ function failureClassification ( error ) {
184+ const message = error instanceof Error ? error . message : String ( error )
185+ const code = typeof error ?. code === "string" && error . code ? error . code : ""
186+ if ( code . includes ( ".policy" ) ) return { code, classification : "policy" }
187+ if ( code && ! / m a t e r i a l i z | f e t c h | d o w n l o a d | a r c h i v e | e n t r y p o i n t | g i t f a i l e d | s p a w n g i t / i. test ( message ) ) return { code, classification : "native-agent-task" }
188+ if ( / p o l i c y | a u t h o r i z e d | a l l o w l i s t e d | a l l o w e d _ r e p o s | A C C E S S _ T O K E N / i. test ( message ) ) return { code : "wp-codebox.agent-task.policy" , classification : "policy" }
189+ if ( / m a t e r i a l i z | f e t c h | d o w n l o a d | a r c h i v e | e n t r y p o i n t | g i t f a i l e d | s p a w n g i t / i. test ( message ) ) return { code : "wp-codebox.agent-task.materialization" , classification : "materialization" }
190+ if ( / a p p r o v a l | p u b l i c a t i o n | p u l l r e q u e s t / i. test ( message ) ) return { code : "wp-codebox.agent-task.approval" , classification : "approval" }
191+ if ( / p r o j e c t i o n / i. test ( message ) ) return { code : "wp-codebox.agent-task.output-projection" , classification : "output-projection" }
192+ return { code : "wp-codebox.agent-task.execution" , classification : "execution" }
193+ }
194+
195+ async function writeNormalizedFailure ( error , request = { } ) {
196+ const resultPath = join ( workspace , ".codebox" , "agent-task-workflow-result.json" )
197+ const failure = failureClassification ( error )
198+ const message = bounded ( error instanceof Error ? error . message : String ( error ) , MAX_OUTPUT_CHARS )
199+ const accessError = failure . classification === "policy" && / a l l o w e d _ r e p o s | A C C E S S _ T O K E N | G i t H u b t o k e n | C a l l e r r e p o s i t o r y | a u t h o r i z e d / i. test ( message )
200+ const result = {
201+ schema : "wp-codebox/agent-task-workflow-result/v1" ,
202+ run_id : `${ record ( request ) . workload ?. id || "agent-task" } -${ process . env . GITHUB_RUN_ID || "local" } ` . replace ( / [ ^ A - Z a - z 0 - 9 . _ - ] + / g, "-" ) ,
203+ status : "failed" ,
204+ success : false ,
205+ request_path : workflowPath ( requestPath ) ,
206+ failure : { ...failure , message } ,
207+ ...( accessError ? { access : { authorized : false , error : message } } : { } ) ,
208+ }
209+ await mkdir ( join ( workspace , ".codebox" ) , { recursive : true } )
210+ const sanitized = sanitizeRuntimeSourceValue ( redact ( result ) , privateRuntimeSourceRootForSanitization )
211+ assertNoRuntimeSourcePaths ( sanitized , privateRuntimeSourceRootForSanitization )
212+ await writeFile ( resultPath , `${ JSON . stringify ( sanitized , null , 2 ) } \n` )
213+ await output ( "job_status" , "failed" )
214+ await output ( "result_path" , ".codebox/agent-task-workflow-result.json" )
215+ }
216+
176217async function redactArtifactFiles ( directory ) {
177218 const { readdir, stat } = await import ( "node:fs/promises" )
178219 for ( const entry of await readdir ( directory , { withFileTypes : true } ) ) {
@@ -189,6 +230,7 @@ async function redactArtifactFiles(directory) {
189230 }
190231}
191232
233+ async function executeNativeAgentTask ( ) {
192234const request = JSON . parse ( await readFile ( requestPath , "utf8" ) )
193235const verificationCommands = commandEntries ( request . verification_commands , "verification_commands" )
194236const driftChecks = commandEntries ( request . drift_checks , "drift_checks" )
@@ -202,8 +244,6 @@ const runtimeInputPath = join(workspace, ".codebox", "native-agent-task-input.js
202244const resultPath = join ( workspace , ".codebox" , "agent-task-workflow-result.json" )
203245const controlledCodeboxPath = resolve ( requestPath , ".." )
204246const nativeResultPath = join ( controlledCodeboxPath , "native-agent-task-result.json" )
205- let privateRuntimeSourceRoot = ""
206- let privateRuntimeSourceRootForSanitization = ""
207247let cleaningPrivateRuntimeSources = false
208248async function cleanupPrivateRuntimeSources ( ) {
209249 if ( cleaningPrivateRuntimeSources || ! privateRuntimeSourceRoot ) return
@@ -232,11 +272,9 @@ await mkdir(artifactsPath, { recursive: true })
232272
233273const accessError = accessFailure ( request )
234274if ( accessError ) {
235- const result = { schema : "wp-codebox/agent-task-workflow-result/v1" , run_id : runId , status : "failed" , success : false , request_path : requestPath , access : { authorized : false , error : accessError } }
236- await writeFile ( resultPath , `${ JSON . stringify ( result , null , 2 ) } \n` )
237- await output ( "job_status" , "failed" )
238- process . exitCode = 1
239- process . exit ( )
275+ const error = new Error ( accessError )
276+ error . code = "wp-codebox.agent-task.policy"
277+ throw error
240278}
241279
242280const materializedPackage = request . run_agent && ! request . dry_run
@@ -404,3 +442,20 @@ await output("declared_artifacts_json", result.artifacts.declarations)
404442await output ( "result_path" , ".codebox/agent-task-workflow-result.json" )
405443
406444if ( ! success ) process . exitCode = 1
445+ }
446+
447+ try {
448+ await executeNativeAgentTask ( )
449+ } catch ( error ) {
450+ try {
451+ await writeNormalizedFailure ( error )
452+ } finally {
453+ if ( privateRuntimeSourceRoot ) {
454+ const root = privateRuntimeSourceRoot
455+ privateRuntimeSourceRoot = ""
456+ await rm ( root , { recursive : true , force : true } )
457+ }
458+ }
459+ console . error ( bounded ( error instanceof Error ? error . message : String ( error ) , MAX_OUTPUT_CHARS ) )
460+ process . exitCode = 1
461+ }
0 commit comments