eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
2 lines • 6.93 kB
JavaScript
import{assertFullSha,assertGitRef,assertOperationId}from"./identifiers.js";import{captureSelfModificationProposal,readProposalBlob}from"./proposal.js";import{createHash}from"node:crypto";async function publishGitHubDraftPullRequest(e){assertOperationId(e.operationId),validateText(e.title,`title`,256,!1),validateText(e.description,`description`,65536,!0);let t=await captureSelfModificationProposal({sandbox:e.sandbox,workspace:e.workspace}),n=selfModificationBranchName(e.workspace.baseSha,e.operationId),r=await e.credentialProvider.resolve({capability:`publish`,repository:repository(e.workspace)});if(r.trim().length===0)throw Error(`Self-modification publication credential is empty.`);let i=new GitHubClient(e.fetch??fetch,r.trim());await assertTargetAtBase(i,repository(e.workspace),e.workspace.targetBranch,t.baseSha);let a=await i.ref(repository(e.workspace),n);if(a!==null)return await reconcile(i,e,t,n,a);let o=await upload(i,e,t);try{await i.createRef(repository(e.workspace),n,o)}catch(r){let a=await i.ref(repository(e.workspace),n);if(a===null)throw r;return await reconcile(i,e,t,n,a)}await assertTargetAtBase(i,repository(e.workspace),e.workspace.targetBranch,t.baseSha);let s=await i.createPullRequest(repository(e.workspace),n,e.workspace.targetBranch,e.title.trim(),body(e.description,t));return receipt(e.workspace,t,n,o,s)}function selfModificationBranchName(e,t){return assertFullSha(e,`proposal base revision`),assertOperationId(t),`eve-self-modification/${e.slice(0,12)}/${createHash(`sha256`).update(t).digest(`hex`).slice(0,24)}`}async function reconcile(e,t,n,r,i){let a=await e.commit(repository(t.workspace),i);if(a.parent!==n.baseSha)throw Error(`Self-modification operation conflict: existing branch has a different base.`);if(a.tree!==n.proposedTreeSha)throw Error(`Self-modification operation conflict: existing branch has different edits.`);let o=await e.pullRequest(repository(t.workspace),r,t.workspace.targetBranch);if(o===null)return await assertTargetAtBase(e,repository(t.workspace),t.workspace.targetBranch,n.baseSha),receipt(t.workspace,n,r,i,await e.createPullRequest(repository(t.workspace),r,t.workspace.targetBranch,t.title.trim(),body(t.description,n)));if(!o.draft)throw Error(`Self-modification operation conflict: its pull request is no longer a draft.`);return receipt(t.workspace,n,r,i,o)}async function upload(e,t,n){let r=await Promise.all(n.changes.map(async n=>n.objectId===null?{mode:`100644`,path:n.path,sha:null,type:`blob`}:{mode:n.mode,path:n.path,sha:await e.blob(repository(t.workspace),await readProposalBlob({change:n,sandbox:t.sandbox,workspace:t.workspace})),type:`blob`})),i=await e.tree(repository(t.workspace),n.baseTreeSha,r);if(i!==n.proposedTreeSha)throw Error(`GitHub did not reassemble the validated proposal tree.`);return await e.commitCreate(repository(t.workspace),n.baseSha,i,`eve self-modification proposal\n\nOperation: ${createHash(`sha256`).update(t.operationId).digest(`hex`).slice(0,24)}`)}async function assertTargetAtBase(e,t,n,r){assertGitRef(n);let i=await e.ref(t,n);if(i===null)throw Error(`Self-modification target branch ${JSON.stringify(n)} does not exist.`);if(i!==r.toLowerCase())throw Error(`Self-modification target branch changed while the proposal was being prepared.`)}function repository(e){return e.repository}function receipt(e,t,n,r,i){if(!i.draft)throw Error(`GitHub did not create a draft self-modification pull request.`);let a=repository(e);return{branch:n,changedPaths:t.changes.map(e=>e.path),commitSha:r,deployed:!1,draft:!0,merged:!1,pullRequestUrl:i.url,repository:`github.com/${a.owner}/${a.repo}`,targetBranch:e.targetBranch}}function body(e,t){return`${e}\n\n---\n\nThis is a draft proposal. Merge and deployment have not occurred.\n\n## Changed files\n${t.changes.map(e=>`- \`${e.path}\``).join(`
`)}`}function validateText(e,t,n,r){if(e.trim().length===0||e.length>n||!r&&/[\x00-\x1f\x7f]/u.test(e))throw Error(`Self-modification pull request ${t} is invalid.`)}var GitHubClient=class{requestFetch;token;constructor(e,t){this.requestFetch=e,this.token=t}async ref(e,t){let n=await this.request(e,`GET`,`/git/ref/heads/${t.split(`/`).map(encodeURIComponent).join(`/`)}`,void 0,!0);if(n===null||Array.isArray(n)||!record(n).object)return null;let r=record(record(n).object).sha;if(typeof r!=`string`)throw Error(`GitHub returned an invalid ref.`);return assertFullSha(r,`GitHub ref`),r.toLowerCase()}async commit(e,t){let n=record(await this.request(e,`GET`,`/git/commits/${t}`)),r=n.parents,i=record(n.tree).sha;if(!Array.isArray(r)||r.length!==1||typeof i!=`string`)throw Error(`GitHub returned an invalid commit.`);let a=record(r[0]).sha;if(typeof a!=`string`)throw Error(`GitHub returned an invalid commit.`);return assertFullSha(a,`GitHub parent`),assertFullSha(i,`GitHub tree`),{parent:a.toLowerCase(),tree:i.toLowerCase()}}async blob(e,t){return this.sha(await this.request(e,`POST`,`/git/blobs`,{content:t,encoding:`base64`}))}async tree(e,t,n){return this.sha(await this.request(e,`POST`,`/git/trees`,{base_tree:t,tree:n}))}async commitCreate(e,t,n,r){return this.sha(await this.request(e,`POST`,`/git/commits`,{message:r,parents:[t],tree:n}))}async createRef(e,t,n){await this.request(e,`POST`,`/git/refs`,{ref:`refs/heads/${t}`,sha:n})}async pullRequest(e,t,n){let r=new URLSearchParams({head:`${e.owner}:${t}`,state:`all`}),i=await this.request(e,`GET`,`/pulls?${r}`);if(!Array.isArray(i))throw Error(`GitHub returned an invalid pull request list.`);return i[0]===void 0?null:pullRequest(i[0],e,t,n)}async createPullRequest(e,t,n,r,i){return pullRequest(await this.request(e,`POST`,`/pulls`,{base:n,body:i,draft:!0,head:t,title:r}),e,t,n)}async sha(e){let t=record(e).sha;if(typeof t!=`string`)throw Error(`GitHub returned an invalid Git object.`);return assertFullSha(t,`GitHub object`),t.toLowerCase()}async request(e,t,n,r,i=!1){let a={accept:`application/vnd.github+json`,authorization:`Bearer ${this.token}`,"user-agent":`eve-self-modification`,"x-github-api-version":`2022-11-28`};r!==void 0&&(a[`content-type`]=`application/json`);let o=await this.requestFetch(`https://api.github.com/repos/${encodeURIComponent(e.owner)}/${encodeURIComponent(e.repo)}${n}`,{body:r===void 0?void 0:JSON.stringify(r),headers:a,method:t,signal:AbortSignal.timeout(3e4)});if(i&&o.status===404)return null;if(!o.ok)throw Error(`GitHub ${t} ${n} failed with HTTP ${o.status}.`);return await o.json()}};function record(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`GitHub returned an invalid response.`);return e}function pullRequest(e,t,n,r){let i=record(e),a=i.draft,o=i.state,s=i.html_url;if(typeof a!=`boolean`||o!==`open`||typeof s!=`string`||record(i.head).ref!==n||record(i.base).ref!==r||!s.startsWith(`https://github.com/${t.owner}/${t.repo}/pull/`))throw Error(`GitHub returned an invalid pull request.`);return{draft:a,url:s}}export{publishGitHubDraftPullRequest,selfModificationBranchName};