@skyramp/testbot-core 0.12.0-v3.19 → 0.12.0-v3.20

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/main.js CHANGED
@@ -479,7 +479,7 @@ ${s}`}var FT=require("child_process");var TE=H(require("fs")),Dl=H(require("path
479
479
  `)}function UXt(t,e,r){let n=r.map(i=>i.details??"").join(`
480
480
  `),s=[];return/over the 400 cap/.test(n)&&s.push("The length caps are the ONLY thing wrong with those findings \u2014 keep each one, keep its id, and trim the named field. Do not drop a finding to get under a cap."),/changed file|changedFiles/.test(n)&&s.push('diff.changedFiles must be paths git reports. A glob is accepted for a run of near-identical files (`dir/<star>/x.json`), but it must match them and any "(N files)" count must be right \u2014 so list them plainly if you are unsure.'),/is not in requirements\[\]/.test(n)&&s.push("Every satisfies/conflictsWith id must be one you declared in requirements[] \u2014 check the spelling, don't invent one."),/nothing satisfies or conflictsWith it/.test(n)&&s.push("Every requirements[] entry needs a link: add its id to a behavior's satisfies when the diff implements it correctly, or to a finding's conflictsWith when the diff doesn't (or implements something else)."),s.length?s.join(`
481
481
  `):void 0}var dZe=qn({name:"analyze",requires:["preflight"],model:"opus",timeoutMinutes:20,validateInput:_Xt,prepare:uZe,buildPrompt:FXt,allowedTools:["Read","Grep","Glob","Bash(git diff:*)","Bash(git log:*)","Bash(git show:*)","Bash(git blame:*)","Bash(git status:*)"],selfVerification:['Every finding is behavior a test could catch, and one this change causes \u2014 a pre-existing defect is "latent".',"No two behaviors state the same obligation: if ONE test would pin both, they are one entry. Spend the room on a surface nothing describes yet.","You swept every changed file, not only the biggest one \u2014 a file with a single behavior beside one with a dozen is a sweep that stopped early.","Your final message is one JSON object matching the schema."],buildReadDirs:t=>(t.inputs.relatedRepos??[]).map(e=>e.path),exitGates:[rZe,nZe],retryHint:UXt,adversarialReview:iZe,onPassed:(t,e)=>({...sZe(t,e),diff:Vce}),salvage:(t,e)=>cZe(t,e,{diff:Vce})});var zZe=H(require("path")),GZe=require("child_process");var ES=H(require("fs")),Zh=H(require("path")),eg="@skyramp/skyramp",bS="skyramp";function tg(t){return`mcp__${bS}__${t}`}function wS(t,e={}){let r=t.skyrampMcp;if(!r?.command)return;let n={SKYRAMP_FEATURE_TESTBOT:"1",...e};r.runToken?n.SKYRAMP_RUN_TOKEN=r.runToken:r.licensePath&&(n.LICENSE_FILE=r.licensePath);let s=!!t.runnerPlatform&&t.runnerPlatform!=="local";return(r.runToken||r.licensePath||process.env.CI==="true"||s)&&(n.CI="true"),process.env.SKYRAMP_TEST_TOKEN&&(n.SKYRAMP_TEST_TOKEN=process.env.SKYRAMP_TEST_TOKEN),{[bS]:{command:r.command,args:r.args,env:n}}}function iH(t){let e=[t?.command,...t?.args??[]].find(n=>{if(!n)return!1;try{return ES.statSync(Zh.resolve(n)).isFile()}catch{return!1}});if(!e)return;let r=Zh.dirname(Zh.resolve(e));for(let n=0;n<5;n++){let s=Zh.join(r,"package.json");if(ES.existsSync(s))try{let o=JSON.parse(ES.readFileSync(s,"utf-8")),a=o.dependencies?.[eg]??o.devDependencies?.[eg];if(a&&/^\d+\.\d+\.\d+/.test(a))return a}catch{}let i=Zh.dirname(r);if(i===r)break;r=i}}var pZe=["browser_assert","browser_assert_api_request","browser_assert_hidden","browser_assert_table_cell","browser_blueprint","browser_click","browser_drag","browser_evaluate","browser_file_upload","browser_gojs_add_node","browser_gojs_link","browser_hover","browser_mouse_action","browser_navigate","browser_navigate_back","browser_press_key","browser_select_option","browser_snapshot","browser_tabs","browser_take_screenshot","browser_type","browser_visual_snapshot","browser_wait_for"],qXt=[...pZe,"skyramp_export_zip","skyramp_actions","skyramp_analyze_changes","skyramp_analyze_test_health","skyramp_batch_mock_generation","skyramp_batch_scenario_test_generation","skyramp_contract_test_generation","skyramp_e2e_test_generation","skyramp_enhance_assertions","skyramp_enrich_test_with_mocks","skyramp_execute_test","skyramp_fix_errors","skyramp_fuzz_test_generation","skyramp_generate_enriched_integration_test","skyramp_init_scan","skyramp_init_workspace","skyramp_integration_test_generation","skyramp_load_test_generation","skyramp_login","skyramp_logout","skyramp_mock_generation","skyramp_modularization","skyramp_one_click_tool","skyramp_preflight_mock_check","skyramp_query_proxy_mocks","skyramp_recommend_tests","skyramp_register_test_plan","skyramp_reuse_code","skyramp_run_existing_tests","skyramp_setup_local_dev_worker","skyramp_smoke_test_generation","skyramp_start_trace_collection","skyramp_stop_trace_collection","skyramp_submit_report","skyramp_ui_test_generation"],Wce=["skyramp_init_workspace","skyramp_contract_test_generation","skyramp_integration_test_generation","skyramp_mock_generation","skyramp_fix_errors"],Yce=["skyramp_init_workspace","skyramp_ui_test_generation","skyramp_mock_generation","skyramp_fix_errors","skyramp_start_trace_collection","skyramp_stop_trace_collection","skyramp_export_zip",...pZe],Jce=["skyramp_reuse_code","skyramp_modularization","skyramp_enhance_assertions","skyramp_fix_errors","skyramp_execute_test","skyramp_run_existing_tests"],Kce=["skyramp_init_scan","skyramp_init_workspace"],oH=["Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(wc:*)","Bash(find:*)","Bash(grep:*)","Bash(rg:*)","Bash(diff:*)","Bash(sort:*)","Bash(env:*)","Bash(echo:*)","Bash(pwd:*)","Bash(which:*)","Bash(cp:*)","Bash(curl:*)","Bash(jq:*)","Bash(true:*)"],$Xt=t=>`mcp__${bS}__${t}`;function rg(t){let e=new Set(t);return qXt.filter(r=>!e.has(r)).map($Xt).sort()}var UT=H(require("fs")),Ql=H(require("path")),xS=Ql.join(".skyramp","sut"),mZe=Ql.join(".skyramp","workspace.yml");function HXt(t){return t.startsWith(`${xS}${Ql.sep}`)||t.startsWith(".skyramp/sut/")||t===mZe||t===".skyramp/workspace.yml"||/(^|\/)(docker-compose|compose)[^/]*\.ya?ml$/.test(t)||/(^|\/)Dockerfile[^/]*$/.test(t)||/^\.github\/workflows\//.test(t)}function km(t,e,r={}){let n=e.split(/[\\/]+/).filter(Boolean),s=t;for(let[i,o]of n.entries()){s=Ql.join(s,o);let a=UT.lstatSync(s,{throwIfNoEntry:!1}),c=i===n.length-1;if(!a||a.isSymbolicLink()||(c?r.directory?!a.isDirectory():!a.isFile():!a.isDirectory()))return}return n.length?s:void 0}function ng(t,e=[]){let r=p=>km(t,p),n=r(Ql.join(xS,"setup.sh")),s=r(Ql.join(xS,"teardown.sh")),i=r(Ql.join(xS,"get-auth-token.sh")),o=r(mZe),a,c=r(Ql.join(xS,"health.json"));if(c)try{let p=JSON.parse(UT.readFileSync(c,"utf-8"));p&&(p.url||p.command)&&(a=p)}catch{}let l,u=r(Ql.join(xS,"deploy-budget.json"));if(u)try{let p=JSON.parse(UT.readFileSync(u,"utf-8"));typeof p?.minutes=="number"&&Number.isFinite(p.minutes)&&p.minutes>0&&(l=p.minutes)}catch{}let d=e.filter(HXt);return{setupScript:n,teardownScript:s,authTokenScript:i,workspaceFile:o,healthHint:a,budgetMinutes:l,present:!!(n||o),staleFiles:d,stale:d.length>0}}var dH=require("node:crypto");var CS=(r=>(r.Ready="ready",r.Blocked="blocked",r))(CS||{}),Ml=R.string().trim().min(1),zXt=R.object({name:Ml,repository:Ml.describe("owner/repo of the repository the component is built from"),healthy:R.boolean()}),GXt=R.object({component:Ml,problem:Ml,evidence:Ml.describe("the exact command and output that shows it"),fix:Ml.describe("what the user must provide, and where")}),jXt=R.object({framework:Ml,file:Ml.describe("the test file run, relative to its repository root"),result:R.enum(["pass","fail","error"])}),VXt=R.object({input:Ml.describe("the part of the user's SUT input, as given"),reason:Ml}),qT=R.object({schemaVersion:R.literal(1),status:R.nativeEnum(CS).describe("ready only if every component came up healthy and the recipe is written"),components:R.array(zXt),suites:R.array(jXt).default([]).describe("one run per test suite the Target's repositories have; empty when they have none"),inputsUsed:R.array(Ml).default([]).describe("the parts of the user's SUT input the recipe used"),inputsNotUsed:R.array(VXt).default([]).describe("the parts it did not use, and why"),blockers:R.array(GXt).describe("required (non-empty) when status is blocked")});function Xce(t){if(t.status==="blocked"&&!(Array.isArray(t.blockers)&&t.blockers.length>0))return['status "blocked" requires at least one blocker { component, problem, evidence, fix } \u2014 say what stopped the bring-up'];if(t.status==="ready"){let e=Array.isArray(t.components)?t.components:[];if(e.length===0)return['status "ready" requires at least one component \u2014 name each component the recipe brought up healthy'];let r=e.filter(n=>n?.healthy!==!0).map(n=>String(n?.name??"?"));if(r.length>0)return[`status "ready" requires every component healthy; not healthy: ${r.join(", ")} \u2014 answer "blocked" with a blocker for each, or bring them up and answer "ready" again`]}return[]}var PE=64*1024,kE=512*1024,aH=/[\u0000-\u0008\u000b\u000e-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;var fZe=H(require("path"));var $a=R.string().trim().min(1),hZe=R.object({schemaVersion:R.literal(1),status:R.enum(["ready","blocked"]),components:R.array(R.object({name:$a,repository:$a.describe("owner/repo"),healthy:R.boolean(),url:R.string().min(1).nullable()}).strict()),suites:R.array(R.object({framework:$a,file:$a,result:R.enum(["pass","fail","error"])}).strict()),inputsUsed:R.array($a),inputsNotUsed:R.array(R.object({input:$a,reason:$a}).strict()),blockers:R.array(R.object({component:$a,problem:$a,evidence:$a,fix:$a}).strict()),replay:R.object({outcome:$a,durationSeconds:R.number().nonnegative().nullable()}).strict().nullable(),files:R.array(R.object({path:$a,content:R.string()}).strict())}).strict(),WXt=["clean","recipe-drift"];function _E(t){let e=fZe.posix.normalize(t);return e===t&&(e===".skyramp/workspace.yml"||e.startsWith(".skyramp/sut/")&&e.length>13)}function YXt(t,e){let r=e.bringupError,n=e.suspectFiles?.length?` \u2014 it points at ${e.suspectFiles.join(", ")}`:"";return{component:t.components.map(s=>s.name).join(", ")||"SUT",problem:`the recipe worked once but not from nothing: deploy's fresh replay of .skyramp/sut/setup.sh ended ${e.outcome}`,evidence:r?`$ ${r.command}
482
- ${r.output}`:`deploy reported ${e.outcome} without the failing command`,fix:e.outcome==="change-broke-bringup"?`fix the change that broke bring-up${n}, then onboard again`:"make .skyramp/sut/setup.sh bring the SUT up from a clean checkout (what onboard's session had that a fresh run lacks), then onboard again"}}function JXt(t){return{component:t.components.map(e=>e.name).join(", ")||"SUT",problem:"the recipe was never replayed: deploy did not run or wrote no deploy.json",evidence:"outputs/deploy.json is missing from this run",fix:"run deploy after onboard in the same run so it replays .skyramp/sut/setup.sh from a clean start; if deploy ran, its stage log says why it wrote no deploy.json"}}function KXt(t){return{component:"SUT recipe",problem:"deploy fixed recipe drift, but the recipe it fixed was not collected \u2014 onboard's copy is the recipe from before the fix",evidence:`deploy reported recipe-drift: ${(t.recipeFixes??[]).join("; ")||"no fixes listed"}`,fix:"collect .skyramp/sut/ and .skyramp/workspace.yml after deploy (collectRecipeFiles) and build the plan from that collection"}}var Zce=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh",".skyramp/sut/health.json",".skyramp/workspace.yml"];function XXt(t){return`Onboard again so it leaves the whole recipe in the checkout: ${t.map(r=>r===".skyramp/workspace.yml"?"create .skyramp/workspace.yml with skyramp_init_scan and skyramp_init_workspace":`write ${r} as a regular file (a symlink is not uploaded)`).join("; ")}.`}function gZe(t,e,r={}){let n=e!==void 0&&!WXt.includes(e.outcome),s=e&&r.recipeFiles||t,i=e?.outcome==="recipe-drift"&&!r.recipeFiles,o=(s.recipeFilesOmitted??[]).filter(f=>_E(f.path)),a=o.length?[{component:"SUT recipe",problem:`The recipe could not be uploaded whole: ${o.map(f=>f.path).join(", ")} left out.`,evidence:o.map(f=>`${f.path}: ${f.reason}`).join("; "),fix:"Keep each recipe file text-only and under 64 KiB, and the recipe under 512 KiB in total \u2014 move large generated content out of .skyramp/sut/ and have setup.sh produce it."}]:[],c=t.status==="ready",l=new Set(s.recipeFiles.map(f=>f.path)),u=c?Zce.filter(f=>!l.has(f)):[],d=u.length?[{component:"SUT recipe",problem:`The recipe is missing ${u.join(", ")}, so it cannot be replayed.`,evidence:`collected: ${[...l].sort().join(", ")||"nothing"}`,fix:XXt(u)}]:[],p=e?n?[YXt(t,e)]:[]:c?[JXt(t)]:[],m=[...t.blockers,...p,...i&&e?[KXt(e)]:[],...a,...d],h=e?.baseUrls??{};return{schemaVersion:1,status:c&&e!==void 0&&!n&&!i&&o.length===0&&u.length===0?"ready":"blocked",components:t.components.map(f=>({name:f.name,repository:f.repository,healthy:f.healthy&&!n,url:Object.hasOwn(h,f.name)&&h[f.name]||null})),suites:t.suites.map(f=>({framework:f.framework,file:f.file,result:f.result})),inputsUsed:[...t.inputsUsed],inputsNotUsed:t.inputsNotUsed.map(f=>({input:f.input,reason:f.reason})),blockers:m.map(f=>({component:f.component,problem:f.problem,evidence:f.evidence,fix:f.fix})),replay:e?{outcome:e.outcome,durationSeconds:r.deployDurationSeconds??null}:null,files:s.recipeFiles.filter(f=>_E(f.path)).map(f=>({path:f.path,content:f.content}))}}var pH=(e=>(e.GitHub="github",e))(pH||{}),ele=(i=>(i.Trigger="TRIGGER",i.Baseline="BASELINE",i.AuthoritativeRelatedChange="AUTHORITATIVE_RELATED_CHANGE",i.ConfiguredSharedDependency="CONFIGURED_SHARED_DEPENDENCY",i.ManualOverride="MANUAL_OVERRIDE",i))(ele||{}),tle=(i=>(i.PullRequest="pull_request",i.ReadyForReview="ready_for_review",i.Schedule="schedule",i.Manual="manual",i.SutSetup="sut_setup",i))(tle||{}),ZXt=["sut_setup"],eZt=["sut_setup"],tZt=["sut_setup"],rle=(e=>(e.PullRequest="pull_request",e))(rle||{}),mH=(s=>(s.ModelProvider="model_provider",s.SutAccess="sut_access",s.Skyramp="skyramp",s.Other="other",s))(mH||{});var fH=(s=>(s.ReportUploaded="report_uploaded",s.NoReport="no_report",s.Cancelled="cancelled",s.RecipeUploaded="recipe_uploaded",s))(fH||{}),EZe=(r=>(r.Running="running",r.Ended="ended",r))(EZe||{}),hH=(i=>(i.TimedOut="timed_out",i.AgentFailed="agent_failed",i.SetupFailed="setup_failed",i.CredentialMissing="credential_missing",i.RunnerLost="runner_lost",i))(hH||{}),nle=(r=>(r.Onboard="onboard",r.Deploy="deploy",r))(nle||{}),sle=(r=>(r.Started="started",r.Finished="finished",r))(sle||{}),ile=(n=>(n.Passed="passed",n.Failed="failed",n.Skipped="skipped",n))(ile||{}),sg=R.string().regex(/^[1-9][0-9]{0,18}$/),cH=R.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/),_m=R.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/),SS=R.string().regex(/^[0-9a-f]{64}$/),lH=R.object({id:_m,revision:sg}).strict(),Dm=R.object({provider:R.nativeEnum(pH),providerAccountId:sg,providerRepositoryId:sg}).strict(),bZe=Dm.extend({namespace:R.string().min(1),name:R.string().min(1)}).strict(),rZt=R.object({repository:bZe,revision:cH,ref:R.string().nullable(),reason:R.nativeEnum(ele),change:R.object({kind:R.nativeEnum(rle),number:sg,base:R.object({ref:R.string().min(1),revision:cH}).strict().optional()}).strict().nullable()}).strict(),nZt=R.object({schemaVersion:R.literal(1),runId:_m,skyrampOrgId:_m,target:lH,policy:lH.nullable(),trigger:R.object({kind:R.nativeEnum(tle),requestedAt:R.string().datetime()}).strict(),configuration:R.object({captureId:_m,revision:sg.nullable(),hash:SS}).strict(),baseline:R.object({confirmedRunId:_m.nullable()}).strict(),sources:R.array(rZt).min(1),execution:R.object({provider:R.nativeEnum(pH),repository:bZe,workflowPath:R.string(),ref:R.string()}).strict(),credentials:R.array(R.object({name:R.string(),purpose:R.nativeEnum(mH),required:R.boolean()}).strict())}).strict().superRefine((t,e)=>{let r=t.trigger.kind,n=ZXt.includes(r);n!==(t.policy===null)&&e.addIssue({code:R.ZodIssueCode.custom,path:["policy"],message:n?`a ${r} run has no policy`:"names the policy revision"}),eZt.includes(r)&&t.sources.forEach((s,i)=>{s.reason!=="BASELINE"&&e.addIssue({code:R.ZodIssueCode.custom,path:["sources",i,"reason"],message:`a ${r} run selects only BASELINE sources`})})});var sZt=R.object({schemaVersion:R.literal(1),attempt:R.object({id:_m,number:R.number().int().positive(),deadlineAt:R.string().datetime()}).strict(),manifest:nZt,manifestHash:SS,entitlements:R.array(R.string()).optional(),runToken:R.string().min(1).optional()}).strict(),iZt=R.object({schemaVersion:R.literal(1),runId:_m,execution:R.object({provider:R.nativeEnum(pH),repository:Dm,providerRunId:sg,providerRunAttempt:R.number().int().positive(),workflowRef:R.string().min(1)}).strict()}).strict(),AZe=R.object({repository:Dm,number:sg}).strict(),oZt=R.discriminatedUnion("status",[R.object({status:R.literal("none")}).strict(),R.object({status:R.literal("delivered"),pullRequests:R.array(AZe).min(1)}).strict(),R.object({status:R.literal("partial"),pullRequests:R.array(AZe).min(1),failures:R.array(R.object({repository:Dm,message:R.string()}).strict()).min(1)}).strict(),R.object({status:R.literal("failed"),message:R.string()}).strict()]),aZt=R.object({sufficiency:R.nativeEnum(cT),rationale:R.string().min(1),gaps:R.array(R.string().min(1)),acceptedEvidence:R.array(R.object({kind:R.nativeEnum($h),repository:R.string().min(1),revision:cH,reference:R.string().min(1),summary:R.string().min(1)}).strict()),actionRequired:R.array(R.nativeEnum(y$))}).strict().superRefine((t,e)=>{t.sufficiency==="sufficient"!=(t.gaps.length===0)&&e.addIssue({code:R.ZodIssueCode.custom,path:["gaps"],message:"sufficient evidence has no gaps, and insufficient evidence names at least one"}),new Set(t.actionRequired).size!==t.actionRequired.length&&e.addIssue({code:R.ZodIssueCode.custom,path:["actionRequired"],message:"a reason appears twice"})}),cZt=R.object({testResults:R.array(R.object({resultId:R.string().regex(/^[^\u0000-\u001f]{1,512}$/),repository:R.string().regex(/^[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}$/),testType:R.string(),endpoint:R.string(),status:R.enum(["Pass","Fail","Skipped"]),details:R.string(),fileName:R.string().optional()}).strict()),validation:aZt}).passthrough().superRefine((t,e)=>{let r=new Set;t.testResults.forEach((n,s)=>{let i=["testResults",s,"resultId"],o=n.resultId.slice(`${n.repository}:${n.testType}:`.length);(!n.resultId.startsWith(`${n.repository}:${n.testType}:`)||o===""||o.startsWith("/")||o.startsWith("#"))&&e.addIssue({code:R.ZodIssueCode.custom,path:i,message:"is not repository:testType:path for this result"}),r.has(n.resultId)&&e.addIssue({code:R.ZodIssueCode.custom,path:i,message:"stable identity repeated"}),r.add(n.resultId)})}),yZe=t=>t.replace(/[%:#]/g,e=>encodeURIComponent(e));function gH(t,e,r,n){let s=r.replace(/\\/g,"/"),i=s.split("/").filter(c=>c!==""&&c!==".");if(s.startsWith("/")||/^[A-Za-z]:/.test(s)||i.length===0||i.includes(".."))throw new Error(`testResultId needs a path relative to the repository root, got "${r}"`);let o=i.join("/"),a=`${t}:${e}:${yZe(o)}`;return n?`${a}#${yZe(n)}`:a}var wZe=R.object({schemaVersion:R.literal(1),run:R.object({runId:_m,attemptNumber:R.number().int().positive(),manifestHash:SS}).strict(),target:lH,policy:lH,provenance:R.object({sources:R.array(R.object({repository:Dm,revision:cH}).strict())}).strict(),delivery:R.object({generatedTests:oZt}).strict(),producer:R.object({testbot:R.string().regex(/^[0-9A-Za-z.+-]{1,64}$/),mcp:R.string().regex(/^[0-9A-Za-z.+-]{1,64}$/)}).strict(),report:cZt}).strict(),lZt=wZe.pick({schemaVersion:!0,run:!0,target:!0,provenance:!0,producer:!0}).extend({recipe:hZe}).strict().superRefine(({recipe:t},e)=>{let r=(i,o)=>e.addIssue({code:R.ZodIssueCode.custom,path:["recipe",...i],message:o});t.status==="blocked"&&t.blockers.length===0&&r(["blockers"],"a blocked recipe names at least one blocker");let n=new Set,s=0;t.files.forEach((i,o)=>{_E(i.path)||r(["files",o,"path"],"is not .skyramp/workspace.yml or a file under .skyramp/sut/"),n.has(i.path)&&r(["files",o,"path"],"file repeated"),n.add(i.path),aH.test(i.content)&&r(["files",o,"content"],"is not text");let a=Buffer.byteLength(i.content,"utf8");s+=a,a>PE&&r(["files",o,"content"],`larger than ${PE} bytes`)}),s>kE&&r(["files"],`recipe text over ${kE} bytes in total`)}),xZe=R.object({schemaVersion:R.literal(1),stage:R.nativeEnum(nle),event:R.nativeEnum(sle),status:R.nativeEnum(ile).optional()}).strict().refine(t=>t.status===void 0||t.event==="finished",{path:["status"],message:"only a finished stage has a status"}),CZe=R.object({schemaVersion:R.literal(1),configuration:R.object({captureId:_m,revision:sg.nullable(),hash:SS}).strict(),capture:R.object({schemaVersion:R.literal(1),settings:R.object({target:R.object({environment:R.unknown()}).passthrough(),timeoutMinutes:R.number().int().positive()}).passthrough(),provenance:R.object({}).passthrough(),hash:SS}).strict()}).strict(),SZe=R.object({schemaVersion:R.literal(1),runtimePlan:R.object({version:R.number().int().positive(),contentHash:SS,files:R.array(R.object({path:R.string().refine(_E,"not a recipe file path"),content:R.string()}).strict()).min(1)}).strict().nullable()}).strict(),IZe=R.object({schemaVersion:R.literal(1),state:R.nativeEnum(EZe),ending:R.nativeEnum(fH).nullable(),cause:R.nativeEnum(hH).nullable(),deadlineAt:R.string().datetime()}).strict();function ole(t,e){return t.state==="ended"?t.ending==="cancelled"?"cancelled":t.cause==="runner_lost"?"expired":"ended":e.getTime()>=Date.parse(t.deadlineAt)?"expired":null}function $T(t){if(typeof t=="number"&&!Number.isFinite(t))throw new TypeError("Canonical JSON requires JSON values");if(Array.isArray(t))return`[${t.map($T).join(",")}]`;if(t!==null&&typeof t=="object"){let r=t;return`{${Object.keys(r).sort().map(n=>`${JSON.stringify(n)}:${$T(r[n])}`).join(",")}}`}let e=JSON.stringify(t);if(e===void 0)throw new TypeError("Canonical JSON requires JSON values");return e}function ale(t){return(0,dH.createHash)("sha256").update($T(t),"utf8").digest("hex")}function RZe(t){let e=[...t].sort((r,n)=>r.path<n.path?-1:r.path>n.path?1:0).map(r=>({path:r.path,content:r.content}));return(0,dH.createHash)("sha256").update($T(e),"utf8").digest("hex")}function vZe(t){return(0,dH.createHash)("sha256").update($T({schemaVersion:t.schemaVersion,settings:t.settings,provenance:t.provenance}),"utf8").digest("hex")}var uH=t=>({provider:t.provider,providerAccountId:t.providerAccountId,providerRepositoryId:t.providerRepositoryId});function NZe(t,e){let r=s=>{let i=e[s];if(!i)throw new Error(`${s} is not set; the V3 runner handshake needs it`);return i},n=r("GITHUB_RUN_ATTEMPT");if(!/^[1-9][0-9]*$/.test(n))throw new Error(`GITHUB_RUN_ATTEMPT is "${n}", not a positive whole number`);return iZt.parse({schemaVersion:1,runId:t,execution:{provider:"github",repository:{provider:"github",providerAccountId:r("GITHUB_REPOSITORY_OWNER_ID"),providerRepositoryId:r("GITHUB_REPOSITORY_ID")},providerRunId:r("GITHUB_RUN_ID"),providerRunAttempt:Number(n),workflowRef:r("GITHUB_WORKFLOW_REF")}})}function BZe(t){let e=sZt.parse(t);if(ale(e.manifest)!==e.manifestHash)throw new Error("The handshake manifest hash does not match its manifest");return e}function uZt(t){let e=r=>r.map(n=>({repository:uH(n.repository),number:n.number}));return t.status==="delivered"?{status:t.status,pullRequests:e(t.pullRequests)}:t.status==="partial"?{status:t.status,pullRequests:e(t.pullRequests),failures:t.failures.map(r=>({repository:uH(r.repository),message:r.message}))}:t}function TZe(t){return t.issues.map(e=>`${e.path.map((n,s)=>typeof n=="number"?`[${n}]`:s===0?n:`.${n}`).join("")}: ${e.message}`).join("; ")}function PZe(t){let{manifest:e}=t;if(tZt.includes(e.trigger.kind))throw new Error(`The report upload is invalid: a ${e.trigger.kind} run uploads its SUT recipe, never a report`);let r=wZe.safeParse({schemaVersion:1,run:{runId:e.runId,attemptNumber:t.attemptNumber,manifestHash:ale(e)},target:{id:e.target.id,revision:e.target.revision},policy:e.policy&&{id:e.policy.id,revision:e.policy.revision},provenance:{sources:t.checkedOut.map(n=>({repository:uH(n.repository),revision:n.revision}))},delivery:{generatedTests:uZt(t.generatedTests)},producer:t.producer,report:t.report});if(!r.success)throw new Error(`The report upload is invalid: ${TZe(r.error)}`);return dZt(r.data,e),r.data}function kZe(t,e,r,n,s){let i=lZt.safeParse({schemaVersion:1,run:{runId:t.runId,attemptNumber:e,manifestHash:ale(t)},target:{id:t.target.id,revision:t.target.revision},provenance:{sources:r.map(o=>({repository:uH(o.repository),revision:o.revision}))},producer:s,recipe:n});if(!i.success)throw new Error(`The SUT recipe upload is invalid: ${TZe(i.error)}`);return i.data}function dZt(t,e){let r=new Map(e.sources.map(n=>[`${n.repository.namespace}/${n.repository.name}`.toLowerCase(),n.revision]));t.report.validation.acceptedEvidence.forEach((n,s)=>{let i=`report.validation.acceptedEvidence[${s}]`,o=r.get(n.repository.toLowerCase());if(!o)throw new Error(`${i}.repository: ${n.repository} is outside the run's snapshot`);if(n.revision!==o)throw new Error(`${i}.revision: evidence is not from the frozen revision ${o}`)})}var pZt=50,mZt=50,fZt=4096,hZt=8e3,gZt=2048,cle=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/,AZt=/^(?!\/)(?!.*(?:^|\/)\.\.?(?:\/|$))[A-Za-z0-9._/ -]{1,255}$/,yZt=/^\.github\/workflows\/[A-Za-z0-9._-]+\.ya?ml$/,EZt=/^(?!GITHUB_)(?!SKYRAMP_RUNNER_)[A-Z][A-Z0-9_]{0,99}$/,DZe=(i=>(i.Command="command",i.ComposeFile="compose_file",i.DeploymentWorkflow="deployment_workflow",i.Url="url",i.Guidance="guidance",i))(DZe||{}),bZt={command:"optional",compose_file:"required",deployment_workflow:"required",url:"none",guidance:"none"},_Ze=t=>R.string().max(t).refine(e=>e.trim().length>0&&!cle.test(e),"must be non-blank text with no control characters other than tab, LF and CR");function wZt(t){let e;try{e=new URL(t)}catch{return"invalid URL"}if(e.protocol!=="https:"&&e.protocol!=="http:")return"only http and https URLs";if(e.username||e.password)return"a URL cannot carry a login"}var xZt={command:_Ze(fZt),compose_file:R.string().regex(AZt,"must be a relative path inside the repository"),deployment_workflow:R.string().regex(yZt,"must be a .github/workflows/*.yml path"),url:R.string().max(gZt).superRefine((t,e)=>{let r=wZt(t);r&&e.addIssue({code:R.ZodIssueCode.custom,message:r})}),guidance:_Ze(hZt)},CZt=R.object({kind:R.nativeEnum(DZe),value:R.string(),repository:Dm.nullable()}).strict().superRefine((t,e)=>{let r=bZt[t.kind];r==="required"&&!t.repository&&e.addIssue({code:R.ZodIssueCode.custom,path:["repository"],message:"a file belongs to a repository"}),r==="none"&&t.repository&&e.addIssue({code:R.ZodIssueCode.custom,path:["repository"],message:`a ${t.kind} names no repository`});let n=xZt[t.kind].safeParse(t.value);if(!n.success)for(let s of n.error.issues)e.addIssue({code:R.ZodIssueCode.custom,path:["value"],message:s.message})}),SZt=R.object({name:R.string().regex(EZt,"must be a GitHub Actions secret name (A-Z, 0-9, _; not GITHUB_ or SKYRAMP_RUNNER_)"),purpose:R.nativeEnum(mH),required:R.boolean()}).strict(),IZt=R.object({knowledge:R.array(CZt).max(pZt),credentials:R.array(SZt).max(mZt)}).strict().superRefine((t,e)=>{let r=new Set;t.credentials.forEach((n,s)=>{r.has(n.name)&&e.addIssue({code:R.ZodIssueCode.custom,path:["credentials",s,"name"],message:"named twice"}),r.add(n.name)})});function RZt(t){return t.map(e=>`$${e.path.map(r=>typeof r=="number"?`[${r}]`:`.${r}`).join("")}: ${e.message}`).join("; ")}function lle(t,e="SUT environment"){let r=IZt.safeParse(t);if(!r.success)throw new Error(`${e} is not valid SUT / Environment settings \u2014 ${RZt(r.error.issues)}`);return r.data}function QZe(t,e="SUT environment"){if(!t.trim())return;let r;try{r=JSON.parse(t)}catch(n){throw new Error(`${e} is not JSON: ${n instanceof Error?n.message:String(n)}`)}return lle(r,e)}function AH(t,e=process.env){return(t?.credentials??[]).map(r=>e[r.name]).filter(r=>!!r?.trim())}var vZt={command:"command",compose_file:"compose file",deployment_workflow:"deployment workflow",url:"URL",guidance:"guidance"},NZt={model_provider:"model provider",sut_access:"SUT access",skyramp:"Skyramp",other:"other"};function MZe(t,e){let r=t.knowledge.map(n=>{let s=n.repository,i=s?` in ${e(s)??`the Target repository with ${s.provider} id ${s.providerRepositoryId}`}`:"";return`- ${vZt[n.kind]}${i}: ${n.value.trim().split(/\r?\n/).join(`
482
+ ${r.output}`:`deploy reported ${e.outcome} without the failing command`,fix:e.outcome==="change-broke-bringup"?`fix the change that broke bring-up${n}, then onboard again`:"make .skyramp/sut/setup.sh bring the SUT up from a clean checkout (what onboard's session had that a fresh run lacks), then onboard again"}}function JXt(t){return{component:t.components.map(e=>e.name).join(", ")||"SUT",problem:"the recipe was never replayed: deploy did not run or wrote no deploy.json",evidence:"outputs/deploy.json is missing from this run",fix:"run deploy after onboard in the same run so it replays .skyramp/sut/setup.sh from a clean start; if deploy ran, its stage log says why it wrote no deploy.json"}}function KXt(t){return{component:"SUT recipe",problem:"deploy fixed recipe drift, but the recipe it fixed was not collected \u2014 onboard's copy is the recipe from before the fix",evidence:`deploy reported recipe-drift: ${(t.recipeFixes??[]).join("; ")||"no fixes listed"}`,fix:"collect .skyramp/sut/ and .skyramp/workspace.yml after deploy (collectRecipeFiles) and build the plan from that collection"}}var Zce=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh",".skyramp/sut/health.json",".skyramp/workspace.yml"];function XXt(t){return`Onboard again so it leaves the whole recipe in the checkout: ${t.map(r=>r===".skyramp/workspace.yml"?"create .skyramp/workspace.yml with skyramp_init_scan and skyramp_init_workspace":`write ${r} as a regular file (a symlink is not uploaded)`).join("; ")}.`}function gZe(t,e,r={}){let n=e!==void 0&&!WXt.includes(e.outcome),s=e&&r.recipeFiles||t,i=e?.outcome==="recipe-drift"&&!r.recipeFiles,o=(s.recipeFilesOmitted??[]).filter(f=>_E(f.path)),a=o.length?[{component:"SUT recipe",problem:`The recipe could not be uploaded whole: ${o.map(f=>f.path).join(", ")} left out.`,evidence:o.map(f=>`${f.path}: ${f.reason}`).join("; "),fix:"Keep each recipe file text-only and under 64 KiB, and the recipe under 512 KiB in total \u2014 move large generated content out of .skyramp/sut/ and have setup.sh produce it."}]:[],c=t.status==="ready",l=new Set(s.recipeFiles.map(f=>f.path)),u=c?Zce.filter(f=>!l.has(f)):[],d=u.length?[{component:"SUT recipe",problem:`The recipe is missing ${u.join(", ")}, so it cannot be replayed.`,evidence:`collected: ${[...l].sort().join(", ")||"nothing"}`,fix:XXt(u)}]:[],p=e?n?[YXt(t,e)]:[]:c?[JXt(t)]:[],m=[...t.blockers,...p,...i&&e?[KXt(e)]:[],...a,...d],h=e?.baseUrls??{};return{schemaVersion:1,status:c&&e!==void 0&&!n&&!i&&o.length===0&&u.length===0?"ready":"blocked",components:t.components.map(f=>({name:f.name,repository:f.repository,healthy:f.healthy&&!n,url:Object.hasOwn(h,f.name)&&h[f.name]||null})),suites:t.suites.map(f=>({framework:f.framework,file:f.file,result:f.result})),inputsUsed:[...t.inputsUsed],inputsNotUsed:t.inputsNotUsed.map(f=>({input:f.input,reason:f.reason})),blockers:m.map(f=>({component:f.component,problem:f.problem,evidence:f.evidence,fix:f.fix})),replay:e?{outcome:e.outcome,durationSeconds:r.deployDurationSeconds??null}:null,files:s.recipeFiles.filter(f=>_E(f.path)).map(f=>({path:f.path,content:f.content}))}}var pH=(e=>(e.GitHub="github",e))(pH||{}),ele=(i=>(i.Trigger="TRIGGER",i.Baseline="BASELINE",i.AuthoritativeRelatedChange="AUTHORITATIVE_RELATED_CHANGE",i.ConfiguredSharedDependency="CONFIGURED_SHARED_DEPENDENCY",i.ManualOverride="MANUAL_OVERRIDE",i))(ele||{}),tle=(i=>(i.PullRequest="pull_request",i.ReadyForReview="ready_for_review",i.Schedule="schedule",i.Manual="manual",i.SutSetup="sut_setup",i))(tle||{}),ZXt=["sut_setup"],eZt=["sut_setup"],tZt=["sut_setup"],rle=(e=>(e.PullRequest="pull_request",e))(rle||{}),mH=(s=>(s.ModelProvider="model_provider",s.SutAccess="sut_access",s.Skyramp="skyramp",s.Other="other",s))(mH||{});var fH=(s=>(s.ReportUploaded="report_uploaded",s.NoReport="no_report",s.Cancelled="cancelled",s.RecipeUploaded="recipe_uploaded",s))(fH||{}),EZe=(r=>(r.Running="running",r.Ended="ended",r))(EZe||{}),hH=(i=>(i.TimedOut="timed_out",i.AgentFailed="agent_failed",i.SetupFailed="setup_failed",i.CredentialMissing="credential_missing",i.RunnerLost="runner_lost",i))(hH||{}),nle=(r=>(r.Onboard="onboard",r.Deploy="deploy",r))(nle||{}),sle=(r=>(r.Started="started",r.Finished="finished",r))(sle||{}),ile=(n=>(n.Passed="passed",n.Failed="failed",n.Skipped="skipped",n))(ile||{}),sg=R.string().regex(/^[1-9][0-9]{0,18}$/),cH=R.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/),_m=R.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/),SS=R.string().regex(/^[0-9a-f]{64}$/),lH=R.object({id:_m,revision:sg}).strict(),Dm=R.object({provider:R.nativeEnum(pH),providerAccountId:sg,providerRepositoryId:sg}).strict(),bZe=Dm.extend({namespace:R.string().min(1),name:R.string().min(1)}).strict(),rZt=R.object({repository:bZe,revision:cH,ref:R.string().nullable(),reason:R.nativeEnum(ele),change:R.object({kind:R.nativeEnum(rle),number:sg,base:R.object({ref:R.string().min(1),revision:cH}).strict().optional()}).strict().nullable()}).strict(),nZt=R.object({schemaVersion:R.literal(1),runId:_m,skyrampOrgId:_m,target:lH,policy:lH.nullable(),trigger:R.object({kind:R.nativeEnum(tle),requestedAt:R.string().datetime()}).strict(),configuration:R.object({captureId:_m,revision:sg.nullable(),hash:SS}).strict(),baseline:R.object({confirmedRunId:_m.nullable()}).strict(),sources:R.array(rZt).min(1),execution:R.object({provider:R.nativeEnum(pH),repository:bZe,workflowPath:R.string(),ref:R.string()}).strict(),credentials:R.array(R.object({name:R.string(),purpose:R.nativeEnum(mH),required:R.boolean()}).strict())}).strict().superRefine((t,e)=>{let r=t.trigger.kind,n=ZXt.includes(r);n!==(t.policy===null)&&e.addIssue({code:R.ZodIssueCode.custom,path:["policy"],message:n?`a ${r} run has no policy`:"names the policy revision"}),eZt.includes(r)&&t.sources.forEach((s,i)=>{s.reason!=="BASELINE"&&e.addIssue({code:R.ZodIssueCode.custom,path:["sources",i,"reason"],message:`a ${r} run selects only BASELINE sources`})})});var sZt=R.object({schemaVersion:R.literal(1),attempt:R.object({id:_m,number:R.number().int().positive(),deadlineAt:R.string().datetime()}).strict(),manifest:nZt,manifestHash:SS,entitlements:R.array(R.string()).optional(),runToken:R.string().min(1).optional()}).strict(),iZt=R.object({schemaVersion:R.literal(1),runId:_m,execution:R.object({provider:R.nativeEnum(pH),repository:Dm,providerRunId:sg,providerRunAttempt:R.number().int().positive(),workflowRef:R.string().min(1)}).strict()}).strict(),AZe=R.object({repository:Dm,number:sg}).strict(),oZt=R.discriminatedUnion("status",[R.object({status:R.literal("none")}).strict(),R.object({status:R.literal("delivered"),pullRequests:R.array(AZe).min(1)}).strict(),R.object({status:R.literal("partial"),pullRequests:R.array(AZe).min(1),failures:R.array(R.object({repository:Dm,message:R.string()}).strict()).min(1)}).strict(),R.object({status:R.literal("failed"),message:R.string()}).strict()]),aZt=R.object({sufficiency:R.nativeEnum(cT),rationale:R.string().min(1),gaps:R.array(R.string().min(1)),acceptedEvidence:R.array(R.object({kind:R.nativeEnum($h),repository:R.string().min(1),revision:cH,reference:R.string().min(1),summary:R.string().min(1)}).strict()),actionRequired:R.array(R.nativeEnum(y$))}).strict().superRefine((t,e)=>{t.sufficiency==="sufficient"!=(t.gaps.length===0)&&e.addIssue({code:R.ZodIssueCode.custom,path:["gaps"],message:"sufficient evidence has no gaps, and insufficient evidence names at least one"}),new Set(t.actionRequired).size!==t.actionRequired.length&&e.addIssue({code:R.ZodIssueCode.custom,path:["actionRequired"],message:"a reason appears twice"})}),cZt=R.object({testResults:R.array(R.object({resultId:R.string().regex(/^[^\u0000-\u001f]{1,512}$/),repository:R.string().regex(/^[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}$/),testType:R.string(),endpoint:R.string(),status:R.enum(["Pass","Fail","Skipped"]),details:R.string(),fileName:R.string().optional(),testId:R.string().regex(/^[^\u0000-\u001f]{1,512}$/).optional()}).strict()),validation:aZt}).passthrough().superRefine((t,e)=>{let r=new Set;t.testResults.forEach((n,s)=>{let i=["testResults",s,"resultId"],o=n.resultId.slice(`${n.repository}:${n.testType}:`.length);(!n.resultId.startsWith(`${n.repository}:${n.testType}:`)||o===""||o.startsWith("/")||o.startsWith("#"))&&e.addIssue({code:R.ZodIssueCode.custom,path:i,message:"is not repository:testType:path for this result"}),r.has(n.resultId)&&e.addIssue({code:R.ZodIssueCode.custom,path:i,message:"stable identity repeated"}),r.add(n.resultId)})}),yZe=t=>t.replace(/[%:#]/g,e=>encodeURIComponent(e));function gH(t,e,r,n){let s=r.replace(/\\/g,"/"),i=s.split("/").filter(c=>c!==""&&c!==".");if(s.startsWith("/")||/^[A-Za-z]:/.test(s)||i.length===0||i.includes(".."))throw new Error(`testResultId needs a path relative to the repository root, got "${r}"`);let o=i.join("/"),a=`${t}:${e}:${yZe(o)}`;return n?`${a}#${yZe(n)}`:a}var wZe=R.object({schemaVersion:R.literal(1),run:R.object({runId:_m,attemptNumber:R.number().int().positive(),manifestHash:SS}).strict(),target:lH,policy:lH,provenance:R.object({sources:R.array(R.object({repository:Dm,revision:cH}).strict())}).strict(),delivery:R.object({generatedTests:oZt}).strict(),producer:R.object({testbot:R.string().regex(/^[0-9A-Za-z.+-]{1,64}$/),mcp:R.string().regex(/^[0-9A-Za-z.+-]{1,64}$/)}).strict(),report:cZt}).strict(),lZt=wZe.pick({schemaVersion:!0,run:!0,target:!0,provenance:!0,producer:!0}).extend({recipe:hZe}).strict().superRefine(({recipe:t},e)=>{let r=(i,o)=>e.addIssue({code:R.ZodIssueCode.custom,path:["recipe",...i],message:o});t.status==="blocked"&&t.blockers.length===0&&r(["blockers"],"a blocked recipe names at least one blocker");let n=new Set,s=0;t.files.forEach((i,o)=>{_E(i.path)||r(["files",o,"path"],"is not .skyramp/workspace.yml or a file under .skyramp/sut/"),n.has(i.path)&&r(["files",o,"path"],"file repeated"),n.add(i.path),aH.test(i.content)&&r(["files",o,"content"],"is not text");let a=Buffer.byteLength(i.content,"utf8");s+=a,a>PE&&r(["files",o,"content"],`larger than ${PE} bytes`)}),s>kE&&r(["files"],`recipe text over ${kE} bytes in total`)}),xZe=R.object({schemaVersion:R.literal(1),stage:R.nativeEnum(nle),event:R.nativeEnum(sle),status:R.nativeEnum(ile).optional()}).strict().refine(t=>t.status===void 0||t.event==="finished",{path:["status"],message:"only a finished stage has a status"}),CZe=R.object({schemaVersion:R.literal(1),configuration:R.object({captureId:_m,revision:sg.nullable(),hash:SS}).strict(),capture:R.object({schemaVersion:R.literal(1),settings:R.object({target:R.object({environment:R.unknown()}).passthrough(),timeoutMinutes:R.number().int().positive()}).passthrough(),provenance:R.object({}).passthrough(),hash:SS}).strict()}).strict(),SZe=R.object({schemaVersion:R.literal(1),runtimePlan:R.object({version:R.number().int().positive(),contentHash:SS,files:R.array(R.object({path:R.string().refine(_E,"not a recipe file path"),content:R.string()}).strict()).min(1)}).strict().nullable()}).strict(),IZe=R.object({schemaVersion:R.literal(1),state:R.nativeEnum(EZe),ending:R.nativeEnum(fH).nullable(),cause:R.nativeEnum(hH).nullable(),deadlineAt:R.string().datetime()}).strict();function ole(t,e){return t.state==="ended"?t.ending==="cancelled"?"cancelled":t.cause==="runner_lost"?"expired":"ended":e.getTime()>=Date.parse(t.deadlineAt)?"expired":null}function $T(t){if(typeof t=="number"&&!Number.isFinite(t))throw new TypeError("Canonical JSON requires JSON values");if(Array.isArray(t))return`[${t.map($T).join(",")}]`;if(t!==null&&typeof t=="object"){let r=t;return`{${Object.keys(r).sort().map(n=>`${JSON.stringify(n)}:${$T(r[n])}`).join(",")}}`}let e=JSON.stringify(t);if(e===void 0)throw new TypeError("Canonical JSON requires JSON values");return e}function ale(t){return(0,dH.createHash)("sha256").update($T(t),"utf8").digest("hex")}function RZe(t){let e=[...t].sort((r,n)=>r.path<n.path?-1:r.path>n.path?1:0).map(r=>({path:r.path,content:r.content}));return(0,dH.createHash)("sha256").update($T(e),"utf8").digest("hex")}function vZe(t){return(0,dH.createHash)("sha256").update($T({schemaVersion:t.schemaVersion,settings:t.settings,provenance:t.provenance}),"utf8").digest("hex")}var uH=t=>({provider:t.provider,providerAccountId:t.providerAccountId,providerRepositoryId:t.providerRepositoryId});function NZe(t,e){let r=s=>{let i=e[s];if(!i)throw new Error(`${s} is not set; the V3 runner handshake needs it`);return i},n=r("GITHUB_RUN_ATTEMPT");if(!/^[1-9][0-9]*$/.test(n))throw new Error(`GITHUB_RUN_ATTEMPT is "${n}", not a positive whole number`);return iZt.parse({schemaVersion:1,runId:t,execution:{provider:"github",repository:{provider:"github",providerAccountId:r("GITHUB_REPOSITORY_OWNER_ID"),providerRepositoryId:r("GITHUB_REPOSITORY_ID")},providerRunId:r("GITHUB_RUN_ID"),providerRunAttempt:Number(n),workflowRef:r("GITHUB_WORKFLOW_REF")}})}function BZe(t){let e=sZt.parse(t);if(ale(e.manifest)!==e.manifestHash)throw new Error("The handshake manifest hash does not match its manifest");return e}function uZt(t){let e=r=>r.map(n=>({repository:uH(n.repository),number:n.number}));return t.status==="delivered"?{status:t.status,pullRequests:e(t.pullRequests)}:t.status==="partial"?{status:t.status,pullRequests:e(t.pullRequests),failures:t.failures.map(r=>({repository:uH(r.repository),message:r.message}))}:t}function TZe(t){return t.issues.map(e=>`${e.path.map((n,s)=>typeof n=="number"?`[${n}]`:s===0?n:`.${n}`).join("")}: ${e.message}`).join("; ")}function PZe(t){let{manifest:e}=t;if(tZt.includes(e.trigger.kind))throw new Error(`The report upload is invalid: a ${e.trigger.kind} run uploads its SUT recipe, never a report`);let r=wZe.safeParse({schemaVersion:1,run:{runId:e.runId,attemptNumber:t.attemptNumber,manifestHash:ale(e)},target:{id:e.target.id,revision:e.target.revision},policy:e.policy&&{id:e.policy.id,revision:e.policy.revision},provenance:{sources:t.checkedOut.map(n=>({repository:uH(n.repository),revision:n.revision}))},delivery:{generatedTests:uZt(t.generatedTests)},producer:t.producer,report:t.report});if(!r.success)throw new Error(`The report upload is invalid: ${TZe(r.error)}`);return dZt(r.data,e),r.data}function kZe(t,e,r,n,s){let i=lZt.safeParse({schemaVersion:1,run:{runId:t.runId,attemptNumber:e,manifestHash:ale(t)},target:{id:t.target.id,revision:t.target.revision},provenance:{sources:r.map(o=>({repository:uH(o.repository),revision:o.revision}))},producer:s,recipe:n});if(!i.success)throw new Error(`The SUT recipe upload is invalid: ${TZe(i.error)}`);return i.data}function dZt(t,e){let r=new Map(e.sources.map(n=>[`${n.repository.namespace}/${n.repository.name}`.toLowerCase(),n.revision]));t.report.validation.acceptedEvidence.forEach((n,s)=>{let i=`report.validation.acceptedEvidence[${s}]`,o=r.get(n.repository.toLowerCase());if(!o)throw new Error(`${i}.repository: ${n.repository} is outside the run's snapshot`);if(n.revision!==o)throw new Error(`${i}.revision: evidence is not from the frozen revision ${o}`)})}var pZt=50,mZt=50,fZt=4096,hZt=8e3,gZt=2048,cle=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/,AZt=/^(?!\/)(?!.*(?:^|\/)\.\.?(?:\/|$))[A-Za-z0-9._/ -]{1,255}$/,yZt=/^\.github\/workflows\/[A-Za-z0-9._-]+\.ya?ml$/,EZt=/^(?!GITHUB_)(?!SKYRAMP_RUNNER_)[A-Z][A-Z0-9_]{0,99}$/,DZe=(i=>(i.Command="command",i.ComposeFile="compose_file",i.DeploymentWorkflow="deployment_workflow",i.Url="url",i.Guidance="guidance",i))(DZe||{}),bZt={command:"optional",compose_file:"required",deployment_workflow:"required",url:"none",guidance:"none"},_Ze=t=>R.string().max(t).refine(e=>e.trim().length>0&&!cle.test(e),"must be non-blank text with no control characters other than tab, LF and CR");function wZt(t){let e;try{e=new URL(t)}catch{return"invalid URL"}if(e.protocol!=="https:"&&e.protocol!=="http:")return"only http and https URLs";if(e.username||e.password)return"a URL cannot carry a login"}var xZt={command:_Ze(fZt),compose_file:R.string().regex(AZt,"must be a relative path inside the repository"),deployment_workflow:R.string().regex(yZt,"must be a .github/workflows/*.yml path"),url:R.string().max(gZt).superRefine((t,e)=>{let r=wZt(t);r&&e.addIssue({code:R.ZodIssueCode.custom,message:r})}),guidance:_Ze(hZt)},CZt=R.object({kind:R.nativeEnum(DZe),value:R.string(),repository:Dm.nullable()}).strict().superRefine((t,e)=>{let r=bZt[t.kind];r==="required"&&!t.repository&&e.addIssue({code:R.ZodIssueCode.custom,path:["repository"],message:"a file belongs to a repository"}),r==="none"&&t.repository&&e.addIssue({code:R.ZodIssueCode.custom,path:["repository"],message:`a ${t.kind} names no repository`});let n=xZt[t.kind].safeParse(t.value);if(!n.success)for(let s of n.error.issues)e.addIssue({code:R.ZodIssueCode.custom,path:["value"],message:s.message})}),SZt=R.object({name:R.string().regex(EZt,"must be a GitHub Actions secret name (A-Z, 0-9, _; not GITHUB_ or SKYRAMP_RUNNER_)"),purpose:R.nativeEnum(mH),required:R.boolean()}).strict(),IZt=R.object({knowledge:R.array(CZt).max(pZt),credentials:R.array(SZt).max(mZt)}).strict().superRefine((t,e)=>{let r=new Set;t.credentials.forEach((n,s)=>{r.has(n.name)&&e.addIssue({code:R.ZodIssueCode.custom,path:["credentials",s,"name"],message:"named twice"}),r.add(n.name)})});function RZt(t){return t.map(e=>`$${e.path.map(r=>typeof r=="number"?`[${r}]`:`.${r}`).join("")}: ${e.message}`).join("; ")}function lle(t,e="SUT environment"){let r=IZt.safeParse(t);if(!r.success)throw new Error(`${e} is not valid SUT / Environment settings \u2014 ${RZt(r.error.issues)}`);return r.data}function QZe(t,e="SUT environment"){if(!t.trim())return;let r;try{r=JSON.parse(t)}catch(n){throw new Error(`${e} is not JSON: ${n instanceof Error?n.message:String(n)}`)}return lle(r,e)}function AH(t,e=process.env){return(t?.credentials??[]).map(r=>e[r.name]).filter(r=>!!r?.trim())}var vZt={command:"command",compose_file:"compose file",deployment_workflow:"deployment workflow",url:"URL",guidance:"guidance"},NZt={model_provider:"model provider",sut_access:"SUT access",skyramp:"Skyramp",other:"other"};function MZe(t,e){let r=t.knowledge.map(n=>{let s=n.repository,i=s?` in ${e(s)??`the Target repository with ${s.provider} id ${s.providerRepositoryId}`}`:"";return`- ${vZt[n.kind]}${i}: ${n.value.trim().split(/\r?\n/).join(`
483
483
  `)}`});if(t.credentials.length>0){let n=t.credentials.map(s=>`${s.name} (${NZt[s.purpose]}${s.required?"":", optional"})`);r.push(`- Secrets the workflow provides as environment variables: ${n.join(", ")} \u2014 the recipe reads each by exactly that variable name.`)}return r}var IS=H(require("fs")),EH=H(require("path"));function OZe(t){let{value:e,error:r}=jr(t);if(r)return{errors:[r]};let n=nce(e)&&e.schemaVersion===void 0?{schemaVersion:1,...e}:e,{value:s,errors:i}=KC(qT,Xce,n);return s?{onboard:s,errors:[]}:{errors:i}}var yH=".skyramp/sut/health.json",BZt={[yH]:`${yH} (with a url or command)`,".skyramp/workspace.yml":".skyramp/workspace.yml (create it with skyramp_init_scan and skyramp_init_workspace)"};function LZe(){return{name:"onboard-recipe",verify(t,e){let{onboard:r,errors:n}=OZe(e.stdout);if(!r)return Le(this.name,n,{fatal:!0});if(r.status==="blocked")return{name:this.name,passed:!0};let s=t.inputs.repositoryPath,i=ng(s),o=Zce.filter(a=>!km(s,a)).map(a=>BZt[a]??a);return km(s,yH)&&!i.healthHint&&o.push(`${yH} (with a url or command)`),i.present&&o.length===0?{name:this.name,passed:!0}:Le(this.name,[`status is "ready" but the recipe deploy replays is incomplete in ${t.inputs.repositoryPath} \u2014 missing (each must be a regular file, not a symlink): ${o.join(", ")}. Write the missing files as the skill specifies and answer "ready" again, or answer "blocked" with the blocker that stopped you.`])}}}function TZt(t){try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(t)}catch{return}}function ule(t){let e=[],r=o=>{let a;try{a=IS.readdirSync(EH.join(t,o),{withFileTypes:!0})}catch{return}for(let c of a.sort((l,u)=>l.name.localeCompare(u.name))){let l=EH.posix.join(o,c.name);c.isDirectory()&&!c.name.startsWith(".")?r(l):c.isFile()&&e.push(l)}};km(t,".skyramp/sut",{directory:!0})&&r(".skyramp/sut"),km(t,".skyramp/workspace.yml")&&e.push(".skyramp/workspace.yml");let n=[],s=[],i=0;for(let o of e){let a=km(t,o);if(!a)continue;let l=IS.statSync(a).size;if(l>PE){s.push({path:o,reason:`larger than ${PE} bytes`});continue}if(i+l>kE){s.push({path:o,reason:`recipe text over ${kE} bytes in total`});continue}let u=TZt(IS.readFileSync(a));if(u===void 0||aH.test(u)){s.push({path:o,reason:"not text"});continue}n.push({path:o,content:u}),i+=l}return{recipeFiles:n,recipeFilesOmitted:s}}function FZe(t,e){let{onboard:r,errors:n}=OZe(e.stdout),s=r&&{...r,...ule(t.inputs.repositoryPath)};return Nr(t,"onboard",Yo(s,t.secretValues??[]),n)}function dle(t){if(t.state.stages.onboard?.status==="passed")return Me(t,"onboard.json")}var HT=H(require("fs")),zT=H(require("path")),qZe=require("child_process");var ple={BASH_DEFAULT_TIMEOUT_MS:"600000",BASH_MAX_TIMEOUT_MS:"1800000"},mle=["ScheduleWakeup"],fle=["This session is non-interactive: it ends as soon as you stop, and a background command can","never notify you. Wait for long builds in the foreground (one Bash call may run up to 30","minutes) and keep checking until they finish; never end your turn while setup is running."],hle=["Bash(docker:*)","Bash(docker compose:*)","Bash(docker-compose:*)","Bash(bash:*)","Bash(sh:*)","Bash(.skyramp/sut/*)","Bash(./.skyramp/sut/*)","Bash(curl:*)","Bash(sleep:*)",...oH,"Read","Write","Edit"];function PZt(t){let e=t.state.stages.onboard;if(e&&(e.status==="failed"||e.status==="aborted")){let i=e.reason?` \u2014 ${e.reason.length>300?`${e.reason.slice(0,300)}\u2026`:e.reason}`:"";return{ok:!1,reasons:[`onboarding ran and did not pass (${e.status}${i}) \u2014 deploy does not replay its recipe; fix that and onboard again`]}}let r=Bl(t),n=dle(t);return n?.status==="blocked"?{ok:!1,reasons:[`onboarding reported blocked (${n.blockers.length} blocker(s)) \u2014 see outputs/onboard.json`]}:r&&!r.sutRequired&&n?.status!=="ready"?{ok:!1,reasons:["preflight determined no SUT is required (non-application change)"]}:ng(t.inputs.repositoryPath,r?.changedFiles.map(i=>i.path)).present?{ok:!0,reasons:[]}:{ok:!1,reasons:["no deploy recipe found (.skyramp/sut/setup.sh or .skyramp/workspace.yml) \u2014 run onboarding first"]}}function kZt(t){let e=t.prepared?.recipe?.budgetMinutes;if(e===void 0)return;let r=Math.min(e+5,90);return r>25?r:void 0}function _Zt(t){let e=Bl(t);return{recipe:ng(t.inputs.repositoryPath,e?.changedFiles.map(n=>n.path))}}function DZt(t,e){return[e.setupScript,e.teardownScript,e.authTokenScript,e.workspaceFile].filter(r=>!!r).map(r=>zT.relative(t,r))}function QZt(t){let{recipe:e}=t.prepared,r=t.inputs.repositoryPath,n=DZt(r,e);return['You are the "deploy" stage of the Skyramp Testbot.',"","Purpose: bring the system under test (SUT) up from its recorded recipe so","later stages can run tests against it. You do NOT design the deployment \u2014 the","recipe already exists. Execute it, verify it works, and if it has drifted","slightly out of date, correct it and continue (rather than reverting).","",`Repository: ${r} \u2014 work inside it.`,`Recipe files present: ${n.join(", ")||"(workspace only)"}`,"",...fle,e.stale?`
484
484
  STALENESS WARNING: this PR changed recipe/infra files (${e.staleFiles.join(", ")}).
485
485
  The recorded recipe may no longer match. If bring-up fails, first decide