aicommit2
Version:
A Reactive CLI that generates commit messages for Git and Jujutsu with various AI
2 lines (1 loc) • 9.54 kB
JavaScript
import F from"https";import h from"chalk";import{concatMap as $,from as x,map as T,catchError as W}from"rxjs";import{fromPromise as K}from"rxjs/internal/observable/innerFrom";import{A as q,l as Y,a as H,b as z,d as V,e as M,c as G}from"./ai.service-d8e94c3a.mjs";import{b as J,g as j,v as O,s as Q}from"./cli-8ee62906.mjs";import"fs";import"path";import"@pacote/xxhash";import"winston";import"cleye";import"module";import"crypto";import"os";import"node:buffer";import"node:path";import"node:child_process";import"node:process";import"child_process";import"node:url";import"node:os";import"assert";import"events";import"node:fs";import"buffer";import"stream";import"util";import"node:util";import"inquirer";import"fs/promises";import"readline";import"figlet";import"gradient-string";import"ora";import"inquirer-reactive-list-prompt";import"winston-daily-rotate-file";import"axios";import"url";import"node:fs/promises";import"chokidar";import"rxjs/operators";const g="Bedrock",S={MISSING_DEPENDENCY:"MissingDependencyError",MISSING_REGION:"MissingRegionError",MISSING_MODEL_ID:"MissingModelIdError",MISSING_API_KEY:"MissingApiKeyError",MISSING_APPLICATION_KEY:"MissingApplicationKeyError",INVALID_RESPONSE:"InvalidResponseError",EMPTY_RESPONSE:"EmptyResponseError"},i=f=>typeof f=="string"&&f.length>0;let _=null,v=null;const L=f=>{const e=new Error('Amazon Bedrock support requires "@aws-sdk/client-bedrock-runtime" and "@aws-sdk/credential-providers". Install them with `pnpm add @aws-sdk/client-bedrock-runtime @aws-sdk/credential-providers`.');return e.name=S.MISSING_DEPENDENCY,e.originalError=f,e},X=async()=>{if(_)return _;try{return _=await import("@aws-sdk/client-bedrock-runtime"),_}catch(f){throw _=null,L(f)}},U=async()=>{if(v)return v;try{return v=await import("@aws-sdk/credential-providers"),v}catch(f){throw v=null,L(f)}};class Z extends q{constructor(e){super(e),this.params=e,this.credentialCache=void 0,this.credentialCacheTimestamp=0,this.CREDENTIAL_CACHE_TTL=5*60*1e3,this.bedrockConfig=this.params.config,this.colors={primary:"#232F3E",secondary:"#FF9900"},this.serviceName=h.bgHex(this.colors.primary).hex(this.colors.secondary).bold(`[${g}${this.formatModelSuffix()}]`),this.errorPrefix=h.red.bold(`[${g}${this.formatModelSuffix()}]`),this.validateConfiguration()}validateConfiguration(){const e=this.bedrockConfig;if(!i(e.model)){const o=new Error("Model ID or inference profile ARN is required.");throw o.name=S.MISSING_MODEL_ID,o}if(!this.getRegion()){const o=new Error("AWS region is required. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.");throw o.name=S.MISSING_REGION,o}const t=i(e.key),r=this.canUseAwsSdk();if(!t&&!r){const o=new Error("Authentication required: Configure AWS credentials (profile, access keys, IAM role) or API key (BEDROCK.key).");throw o.name=S.MISSING_API_KEY,o}if(t&&!r&&!this.getRegion()&&!i(e.applicationBaseUrl)){const o=new Error("Bearer token authentication requires region or applicationBaseUrl to construct endpoint.");throw o.name=S.MISSING_REGION,o}}canUseAwsSdk(){const e=this.bedrockConfig,t=i(e.profile)||i(process.env.AWS_PROFILE),r=i(e.accessKeyId)&&i(e.secretAccessKey)||i(process.env.AWS_ACCESS_KEY_ID)&&i(process.env.AWS_SECRET_ACCESS_KEY);return t||r}determineAuthMethod(){const e=i(this.bedrockConfig.key);if(this.canUseAwsSdk())return"aws-sdk";if(e)return"bearer-token";throw new Error("No authentication method configured")}getServiceSpecificErrorMessage(e){const t=e?.name||e.code,r=e.message||"";switch(t){case"UnrecognizedClientException":case"InvalidSignatureException":return"Authentication with AWS failed. Check your IAM credentials or Bedrock API key settings.";case"AccessDeniedException":return"Access denied. Ensure the IAM principal or application key has permission to invoke the Bedrock resource.";case"ValidationException":return"Invalid request for the selected Bedrock model. Verify the model ID and payload.";case"ResourceNotFoundException":return"The specified Bedrock model, endpoint, or inference profile could not be found.";case"ThrottlingException":return"Request throttled by Bedrock. Reduce request rate or check service quotas."}return r.includes("Region")?"AWS region is required for Bedrock. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.":null}generateCommitMessage$(){return K(this.generateMessage("commit")).pipe($(e=>x(e)),T(this.formatAsChoice),W(this.handleError$))}generateCodeReview$(){return K(this.generateMessage("review")).pipe($(e=>x(e)),T(this.formatCodeReviewAsChoice),W(this.handleError$))}async generateMessage(e){const t=this.params.stagedDiff.diff,r=this.bedrockConfig,o=r.model,{logging:c,inferenceParameters:n}=r,s=this.buildPromptOptions(),a=e==="review"?J(s):j(s),l=this.buildUserPrompt(t,e),w={region:this.getRegion(),profile:r.profile,modelId:o},y=`https://bedrock-runtime.${this.getRegion()||"unknown"}.amazonaws.com/model/${encodeURIComponent(o)}/converse`;Y(t,e,g,o,y,w,c),H(t,e,g,a,l,c);const R={modelId:o,systemPrompt:a,userPrompt:l,...n&&Object.keys(n).length>0&&{inferenceConfig:n}};z(t,e,g,R,c);const u=Date.now();try{const m=this.determineAuthMethod();O()&&(console.log(h.cyan(`[Bedrock] Authentication method: ${m}`)),console.log(h.cyan(`[Bedrock] Model ID: ${o}`)),console.log(h.cyan(`[Bedrock] Region: ${this.getRegion()}`)),console.log(h.cyan(`[Bedrock] Has AWS credentials: ${this.canUseAwsSdk()}`)),console.log(h.cyan(`[Bedrock] Has API key: ${i(r.key)}`)));const E=m==="bearer-token"?await this.invokeWithBearerToken({model:o,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:t,inferenceConfig:n}):await this.invokeWithAwsSdk({model:o,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:t,inferenceConfig:n}),I=Date.now()-u;return V(t,e,g,I,E,c),e==="review"?this.parseCodeReview(E):this.parseMessage(E,r.type,r.generate)}catch(m){throw M(t,e,g,m,c),m instanceof Error&&(m.status=m?.status||m?.$metadata?.httpStatusCode),m}}async invokeWithAwsSdk(e){const t=this.getRegion(),{model:r,systemPrompt:o,userPrompt:c,inferenceConfig:n,logging:s,requestType:a,diff:l}=e,{BedrockRuntimeClient:w,ConverseCommand:y}=await X(),R=this.bedrockConfig,u={region:t,requestHandler:{requestTimeout:R.timeout||12e4}},m=await this.resolveCredentials();m&&(u.credentials=m);const E=new w(u),I=new y({modelId:r,messages:[{role:"user",content:[{text:c}]}],...o?{system:[{text:o}]}:{},...n&&Object.keys(n).length>0&&{inferenceConfig:n}});O()&&console.log(h.cyan(`[Bedrock] Sending ConverseCommand with modelId: ${r}`));let C;try{C=await E.send(I),G(l,a,g,C,s)}catch(d){throw O()&&(console.error(h.red(`[Bedrock] AWS SDK Error: ${d.name}`)),console.error(h.red(`[Bedrock] Error message: ${d.message}`)),d.$metadata&&(console.error(h.red(`[Bedrock] Request ID: ${d.$metadata.requestId}`)),console.error(h.red(`[Bedrock] HTTP Status: ${d.$metadata.httpStatusCode}`))),d.$fault&&console.error(h.red(`[Bedrock] Fault: ${d.$fault}`))),d}const A=C.output?.message?.content?.[0]?.text||"";if(!A){const d=new Error("No text content found in Bedrock response.");throw d.name=S.EMPTY_RESPONSE,d.content=C,d}return A}invokeWithBearerToken(e){const{model:t,systemPrompt:r,userPrompt:o,inferenceConfig:c,logging:n,requestType:s,diff:a}=e,l=this.bedrockConfig,w=this.getRegion(),y=encodeURIComponent(t),R=l.applicationBaseUrl||`https://bedrock-runtime.${w}.amazonaws.com/model/${y}/converse`,u=new URL(R),m={modelId:t,messages:[{role:"user",content:[{text:o}]}],...c&&Object.keys(c).length>0&&{inferenceConfig:c}};r&&(m.system=[{text:r}]);const E=JSON.stringify(m),I={"Content-Type":"application/json","Content-Length":Buffer.byteLength(E).toString()};return i(l.key)&&(I.Authorization=`Bearer ${l.key}`),i(l.applicationInferenceProfileArn)&&(I["x-amzn-bedrock-inference-profile-arn"]=l.applicationInferenceProfileArn),i(l.applicationEndpointId)&&(I["x-amzn-bedrock-endpoint-id"]=l.applicationEndpointId),new Promise((C,A)=>{const d=F.request({method:"POST",protocol:u.protocol,hostname:u.hostname,port:u.port,path:u.pathname+u.search,headers:I,timeout:l.timeout},k=>{const P=[];k.on("data",N=>P.push(N)),k.on("end",()=>{const N=Buffer.concat(P).toString("utf8");if(k.statusCode&&k.statusCode>=400){const p=new Error(`Bedrock application endpoint responded with status ${k.statusCode}.`);return p.status=k.statusCode,p.content=N,M(a,s,g,p,n),A(p)}const b=Q(N);if(!b.ok){const p=new Error("Failed to parse Bedrock application response as JSON. The Bedrock Converse API should always return valid JSON.");return p.name=S.INVALID_RESPONSE,p.content=N,M(a,s,g,p,n),A(p)}const B=b.data;G(a,s,g,B,n);const D=B.output?.message?.content?.[0]?.text||"";if(!D){const p=new Error("No text content found in Bedrock response.");return p.name=S.EMPTY_RESPONSE,p.content=B,M(a,s,g,p,n),A(p)}C(D)})});d.on("error",k=>{const P=k;M(a,s,g,P,n),A(P)}),d.write(E),d.end()})}getRegion(){return this.bedrockConfig.region||process.env.AWS_REGION||process.env.AWS_DEFAULT_REGION||""}async resolveCredentials(){const e=Date.now();if(this.credentialCache&&e-this.credentialCacheTimestamp<this.CREDENTIAL_CACHE_TTL)return this.credentialCache;const t=this.bedrockConfig,r=t.profile,o=t.accessKeyId,c=t.secretAccessKey,n=t.sessionToken;let s;if(i(r)){const{fromIni:a}=await U();s=a({profile:r})}else if(i(o)&&i(c))s=async()=>({accessKeyId:o,secretAccessKey:c,sessionToken:n||process.env.AWS_SESSION_TOKEN||void 0});else if(process.env.AWS_PROFILE){const{fromIni:a}=await U();s=a({profile:process.env.AWS_PROFILE})}return s&&(this.credentialCache=s,this.credentialCacheTimestamp=e),s}}export{Z as BedrockService};