aicommit2 2.5.2 → 2.5.4

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
  <div>
3
- <img src="https://github.com/tak-bro/aicommit2/blob/main/img/demo-min.gif?raw=true" alt="AICommit2"/>
4
- <h1 align="center">AICommit2</h1>
3
+ <img src="https://github.com/tak-bro/aicommit2/blob/main/img/demo-min.gif?raw=true" alt="aicommit2"/>
4
+ <h1 align="center">aicommit2</h1>
5
5
  </div>
6
6
  <p>
7
7
  A Reactive CLI that generates commit messages for Git, YADM, and Jujutsu with Ollama, ChatGPT, Gemini, Claude, Mistral, and other AI
@@ -668,20 +668,27 @@ aicommit2 stats
668
668
  Example output:
669
669
 
670
670
  ```
671
- 📊 AICommit2 Statistics
672
- Period: 2/10/2026 - 3/10/2026
671
+ 📊 aicommit2 Statistics
672
+ Period: 3/16/2026 - 3/17/2026
673
673
 
674
674
  Overview:
675
- Total requests: 126
676
- Success rate: 97.6%
677
- Avg response time: 2.1s
675
+ Total requests: 144
676
+ Success rate: 60.4%
677
+ Avg response time: 1.3s
678
678
 
679
679
  Provider Usage:
680
- OPENAI ████████████████████ 78 (62%) 2.2s 100%
681
- ANTHROPIC ██████████░░░░░░░░░░ 30 (24%) 2.5s 93%
682
- GEMINI ████░░░░░░░░░░░░░░░░ 18 (14%) 1.6s 100%
680
+ Provider Rate Bar Cnt Selected Time
681
+ GROQ 100% ████████████████████ 48 1 (2.1%) 732ms
682
+ OPENAI 0% ░░░░░░░░░░░░░░░░░░░░ 46 0 514ms
683
+ GITHUB_MODELS 96% ███████████████████░ 25 0 2.0s
684
+ GEMINI 29% ██████░░░░░░░░░░░░░░ 14 0 2.8s
683
685
  ```
684
686
 
687
+ **Columns:**
688
+ - **Rate**: Success rate (bar color: 🟢 ≥80%, 🟡 50-79%, 🔴 <50%)
689
+ - **Cnt**: Total request count
690
+ - **Selected**: How many times you chose this provider's message
691
+
685
692
  Options:
686
693
  - `aicommit2 stats -d 7` - Show statistics for the last 7 days
687
694
  - `aicommit2 stats clear` - Clear all statistics
@@ -867,6 +874,8 @@ For detailed information about all available settings, see the [General Settings
867
874
  | `includeBody` | Whether the commit message includes body | false |
868
875
  | `codeReview` | Enable automated code review | false |
869
876
  | `autoCopy` | Auto-copy commit message to clipboard (commits normally) | false |
877
+ | `useStats` | Enable usage statistics tracking | true |
878
+ | `statsDays` | Days to retain statistics data (auto-cleanup) | 30 |
870
879
  | `systemPromptPath` | Path to custom system prompt file | - |
871
880
 
872
881
  ```bash
@@ -1123,6 +1132,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
1123
1132
  <td align="center"><a href="https://github.com/jaytaylor"><img src="https://avatars.githubusercontent.com/jaytaylor" width="100px;" alt=""/><br /><sub><b>@jaytaylor</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=jaytaylor" title="Code">💻</a></td>
1124
1133
  <td align="center"><a href="https://github.com/denniswebb"><img src="https://avatars.githubusercontent.com/denniswebb" width="100px;" alt=""/><br /><sub><b>@denniswebb</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=denniswebb" title="Code">💻</a></td>
1125
1134
  <td align="center"><a href="https://github.com/peinan"><img src="https://avatars.githubusercontent.com/peinan" width="100px;" alt=""/><br /><sub><b>@peinan</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/issues/215" title="Documentation">📖</a></td>
1135
+ <td align="center"><a href="https://github.com/totoroot"><img src="https://avatars.githubusercontent.com/totoroot" width="100px;" alt=""/><br /><sub><b>@totoroot</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=totoroot" title="Code">💻</a></td>
1126
1136
  </tr>
1127
1137
  </table>
1128
1138
  <!-- markdownlint-restore -->
@@ -0,0 +1,5 @@
1
+ import{of as v}from"rxjs";import"fs";import"path";import{xxh64 as S}from"@pacote/xxhash";import p from"winston";import{A as C,l as d,h as b,d as M,E as w,e as I,s as A,f as y}from"./cli-6797d3ee.mjs";const h=new Map,u=(i,e,t)=>{const r=S(0).update(e).digest("hex").substring(0,8),s=`${i}_${r}_${t}`;if(h.has(s))return h.get(s);const n=new Date,o=N(n,i,e,t),l=`${C}/${o}`,a=p.createLogger({level:"info",format:p.format.combine(p.format.timestamp({format:"YYYY-MM-DDTHH:mm:ss.SSSZ"}),p.format.printf(({timestamp:g,level:m,message:c,...f})=>f&&Object.keys(f).length>0?`[${g}] ${m}: ${c} ${JSON.stringify(f,null,2)}`:`[${g}] ${m}: ${c}`)),transports:[new p.transports.File({filename:l})]});return a.info(`=== ${i.toUpperCase()} AI SERVICE LOG ===`),a.info(`Diff Hash: ${r}`),a.info(`Request Type: ${t.toUpperCase()}`),a.info(`Start Time: ${n.toISOString()}`),a.info("=".repeat(50)),a.info(""),h.set(s,a),a},R=i=>{const e={...i},t=["authorization","x-api-key","x-goog-api-key","api-key","x-amzn-bedrock-application-key"];for(const r of t){const s=r.toLowerCase(),n=Object.keys(e).find(o=>o.toLowerCase()===s);n&&e[n]&&typeof e[n]=="string"&&(e[n].startsWith("Bearer ")?e[n]="Bearer [MASKED]":e[n]="[MASKED]")}return e},P=(i,e,t,r,s,n,o=!0)=>{if(!o)return;const l=u(t,i,e);l.info(`Making request to ${t} API with model: ${r}`),l.info(`Request URL: ${s}`),l.info("Request headers:",R(n))},L=(i,e,t,r,s=!0)=>{if(!s)return;u(t,i,e).info("Request payload:",r)},k=(i,e,t,r,s,n=!0)=>{if(!n)return;const o=u(t,i,e);o.info("System prompt:",{prompt:r}),o.info("User prompt:",{prompt:s})},j=(i,e,t,r,s=!0)=>{if(!s)return;u(t,i,e).info("Response received:",r)},x=(i,e,t,r,s=!0)=>{if(!s)return;u(t,i,e).error("API request failed:",r)},E=(i,e,t,r,s,n=!0)=>{if(!n)return;const o=u(t,i,e);r?o.info(`Request completed successfully in ${r}ms`):o.info("Request completed successfully"),s&&o.info("Final processed response:",{response:s}),o.info(""),o.info("=".repeat(50)),o.info(`End Time: ${new Date().toISOString()}`),o.info("=== REQUEST COMPLETED ===")},O=(i,e,t,r,s,n,o,l=!0)=>{if(!l)return;const a=u(t,i,e);o?a.error(`Request failed after ${n}ms:`,{error:o}):(a.info(`Request completed in ${n}ms`),a.info("Response:",{response:s})),E(i,e,t,n,s,l)},N=(i,e,t,r)=>{const{year:s,month:n,day:o,hours:l,minutes:a,seconds:g}=J(i),c=S(0).update(t).digest("hex").substring(0,8),f=e.toLowerCase().replace(/[^a-z0-9]/g,"").substring(0,20);return r==="review"?`${s}-${n}-${o}_${l}-${a}-${g}_${c}_${f}_review.log`:`${s}-${n}-${o}_${l}-${a}-${g}_${c}_${f}_commit.log`},J=i=>{const e=i.getFullYear().toString(),t=(i.getMonth()+1).toString().padStart(2,"0"),r=i.getDate().toString().padStart(2,"0"),s=i.getHours().toString().padStart(2,"0"),n=i.getMinutes().toString().padStart(2,"0"),o=i.getSeconds().toString().padStart(2,"0");return{year:e,month:t,day:r,hours:s,minutes:n,seconds:o}},$=()=>{for(const[i,e]of h.entries())try{e.close()}catch(t){console.error(`Failed to close logger ${i}:`,t)}h.clear()};process.on("exit",$),process.on("SIGINT",()=>{$(),process.exit(0)}),process.on("SIGTERM",()=>{$(),process.exit(0)});class F{constructor(e){this.handleError$=t=>{const r=this.getDetailedErrorMessage(t),s=t.status?`HTTP ${t.status}: ${r}`:r;if(this.params.config.logging){const n=this.params.stagedDiff.diff,o=this.serviceName.replace(/\[|\]/g,"").trim();O(n,"commit",o,"Error occurred","",void 0,s)}return d.error(`${this.errorPrefix} ${s}`),t.stack&&d.error(` ${t.stack}`),t.content&&d.error(` Problematic content: ${t.content}`),t.originalError&&d.error(` Original error: ${t.originalError}`),v({name:`${this.errorPrefix} ${s}`,value:s,isError:!0,disabled:!0})},this.serviceName="AI",this.errorPrefix="ERROR",this.colors={primary:""},this.params=e,this.logSessionId=e.logSessionId}getProviderName(){const e=String.fromCharCode(27),t=new RegExp(`${e}\\[[0-9;]*m`,"g");return this.serviceName.replace(t,"").replace(/\[|\]/g,"").trim()}getDetailedErrorMessage(e){const t=e.message||"",r=this.getProviderName(),s=this.params.config.model?.[0],n=this.params.config.timeout,o=this.getServiceSpecificErrorMessage(e);if(o)return o;const l=e.code||(e.status?b(e.status):M(t));return l!==w.UNKNOWN?I(l,{provider:r,model:s,timeout:n}):t||"Unknown error occurred"}getServiceSpecificErrorMessage(e){return null}cleanJsonCodeBlock(e){const t=/```(?:json|JSON)?\s*([\s\S]*?)\s*```/,r=e.match(t);return r?r[1].trim():e}extractJsonFromResponse(e){let t=e.indexOf("[");if(t!==-1){const r=this.extractBalancedJson(e,t,"[","]");if(r)return r}if(t=e.indexOf("{"),t!==-1){const r=this.extractBalancedJson(e,t,"{","}");if(r)return r}return null}extractBalancedJson(e,t,r,s){let n=0,o=!1,l=!1;for(let a=t;a<e.length;a++){const g=e[a];if(l){l=!1;continue}if(g==="\\"&&o){l=!0;continue}if(g==='"'){o=!o;continue}if(!o&&(g===r&&n++,g===s&&n--,n===0))return e.slice(t,a+1)}return null}parseMessage(e,t,r){const s=this.cleanJsonCodeBlock(e),n=this.extractJsonFromResponse(s);if(!n){const c=new Error("AI response did not contain a valid JSON object or array.");throw c.name="InvalidJsonResponse",c.content=e,c}const o=A(n);if(!o.ok){const c=new Error("Failed to parse AI response as JSON");throw c.name="JsonParseError",c.content=n,c.originalError=o.error,c}const l=o.data,a=Array.isArray(l)?l:[l];if(!a.length||!a.every(c=>typeof c.subject=="string")){const c=new Error("AI response contained malformed commit message data.");throw c.name="MalformedCommitMessage",c.content=e,c}const m=a.map(c=>this.extractMessageAsType(c,t)).map(c=>({title:`${c.subject}`,value:`${c.subject}${c.body?`
2
+
3
+ ${c.body}`:""}${c.footer?`
4
+
5
+ ${c.footer}`:""}`})).slice(0,r);if(this.isLoggingEnabled()){const c=m.map(f=>f.title).join(", ");d.info(`${this.serviceName} Parsed ${m.length} commit messages: ${c}`)}return m}extractMessageAsType(e,t){switch(t){case"conventional":const r=/(\w+)(?:\(.*?\))?:\s*(.*)/,s=e.subject.match(r),n=s?s[0]:e.subject;return{...e,subject:this.normalizeCommitMessage(n)};case"gitmoji":const o=/:\w*:\s*(.*)/,l=e.subject.match(o),a=this.params.config.disableLowerCase??!1;return{...e,subject:l&&!a?l[0].toLowerCase():e.subject};default:return e}}normalizeCommitMessage(e){const t=/^(\w+)(\(.*?\))?:\s(.*)$/,r=e.match(t);if(r){const[,s,n,o]=r,l=this.params.config.disableLowerCase??!1,a=s.toLowerCase(),g=l?o:o.charAt(0).toLowerCase()+o.slice(1);e=`${a}${n||""}: ${g}`}return e}sanitizeResponse(e){if(typeof e=="string")try{return[{title:`${y(e)}...`,value:e}]}catch{return[]}return e.map(t=>{try{return{title:`${y(t)}...`,value:t}}catch{return{title:"",value:""}}})}isLoggingEnabled(){return this.params.config.logging&&!!this.logSessionId}}export{F as A,k as a,L as b,j as c,E as d,x as e,P as l};
@@ -0,0 +1 @@
1
+ import F from"@anthropic-ai/sdk";import d from"chalk";import{concatMap as u,from as g,map as f,catchError as A}from"rxjs";import{fromPromise as v}from"rxjs/internal/observable/innerFrom";import{A as S,l as U,a as O,b as L,c as N,d as j,e as z}from"./ai.service-e4638af5.mjs";import{D as B,a as T,b as W,k as H}from"./cli-6797d3ee.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 K=["claude-4","claude-haiku-4","claude-sonnet-4","claude-opus-4"],X=n=>{const e=n.toLowerCase();return K.some(o=>e===o||e.startsWith(`${o}-`)||e.startsWith(`${o}.`))},Y=10*60*1e3;class G extends S{constructor(e){super(e),this.params=e,this.colors={primary:"#AE5630",secondary:"#fff"},this.serviceName=d.bgHex(this.colors.primary).hex(this.colors.secondary).bold("[Anthropic]"),this.errorPrefix=d.red.bold("[Anthropic]"),this.anthropic=new F({apiKey:this.params.config.key,...this.params.config.timeout>Y&&{timeout:this.params.config.timeout}})}getServiceSpecificErrorMessage(e){const o=e.message||"";return o.includes("API key")||o.includes("api_key")?"Invalid API key. Check your Anthropic API key in configuration":o.includes("quota")||o.includes("usage")?"API quota exceeded. Check your Anthropic usage limits":o.includes("model_not_found")||o.includes("Model not found")||/model.*does not exist/i.test(o)?"Model not found or not accessible. Check if the Claude model name is correct":o.includes("403")||o.includes("Forbidden")?"Access denied. Your API key may not have permission for this Claude model":o.includes("404")||o.includes("Not Found")?"Model or endpoint not found. Check your Claude model configuration":o.includes("500")||o.includes("Internal Server Error")?"Anthropic server error. Try again later":null}generateCommitMessage$(){return v(this.generateMessage("commit")).pipe(u(e=>g(e)),f(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:this.params.config.includeBody?e.value:e.title,description:this.params.config.includeBody?e.value:"",isError:!1})),A(this.handleError$))}generateCodeReview$(){return v(this.generateMessage("review")).pipe(u(e=>g(e)),f(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:e.value,description:e.value,isError:!1})),A(this.handleError$))}async generateMessage(e){const o=this.params.stagedDiff.diff,{systemPrompt:P,systemPromptPath:y,codeReviewPromptPath:k,logging:r,temperature:I,locale:M,generate:a,type:c,maxLength:E,maxTokens:C,topP:w,model:i}=this.params.config,m={...B,locale:M,maxLength:E,type:c,generate:a,systemPrompt:P,systemPromptPath:y,codeReviewPromptPath:k,vcs_branch:this.params.branchName||""},p=e==="review"?T(m):W(m),l=H(o,e),_=`${this.params.config.url||"https://api.anthropic.com"}/v1/messages`,b={"Content-Type":"application/json","x-api-key":this.params.config.key,"anthropic-version":"2023-06-01"};U(o,e,"Anthropic",i,_,b,r),O(o,e,"Anthropic",p,l,r);const $=X(i),h={max_tokens:C,temperature:I,system:p,messages:[{role:"user",content:l}],model:i,...$?{}:{top_p:w}};L(o,e,"Anthropic",h,r);const x=Date.now();try{const t=await this.anthropic.messages.create(h),R=Date.now()-x;N(o,e,"Anthropic",t,r);const s=t.content.map(({text:D})=>D).join("");return j(o,e,"Anthropic",R,s,r),e==="review"?this.sanitizeResponse(s):this.parseMessage(s,c,a)}catch(t){throw z(o,e,"Anthropic",t,r),t}}}export{G as AnthropicService};
@@ -0,0 +1 @@
1
+ import F from"https";import h from"chalk";import{concatMap as b,from as T,map as x,catchError as L}from"rxjs";import{fromPromise as W}from"rxjs/internal/observable/innerFrom";import{A as q,l as Y,a as z,b as H,d as V,e as N,c as K}from"./ai.service-e4638af5.mjs";import{D as J,a as j,b as Q,p as O,s as X,k as Z}from"./cli-6797d3ee.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=u=>typeof u=="string"&&u.length>0;let _=null,M=null;const G=u=>{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=u,e},ee=async()=>{if(_)return _;try{return _=await import("@aws-sdk/client-bedrock-runtime"),_}catch(u){throw _=null,G(u)}},U=async()=>{if(M)return M;try{return M=await import("@aws-sdk/credential-providers"),M}catch(u){throw M=null,G(u)}};class oe 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.errorPrefix=h.red.bold(`[${g}]`),this.validateConfiguration()}validateConfiguration(){const e=this.bedrockConfig;if(!i(e.model)){const t=new Error("Model ID or inference profile ARN is required.");throw t.name=S.MISSING_MODEL_ID,t}if(!this.getRegion()){const t=new Error("AWS region is required. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.");throw t.name=S.MISSING_REGION,t}const r=i(e.key),o=this.canUseAwsSdk();if(!r&&!o){const t=new Error("Authentication required: Configure AWS credentials (profile, access keys, IAM role) or API key (BEDROCK.key).");throw t.name=S.MISSING_API_KEY,t}if(r&&!o&&!this.getRegion()&&!i(e.applicationBaseUrl)){const t=new Error("Bearer token authentication requires region or applicationBaseUrl to construct endpoint.");throw t.name=S.MISSING_REGION,t}}canUseAwsSdk(){const e=this.bedrockConfig,r=i(e.profile)||i(process.env.AWS_PROFILE),o=i(e.accessKeyId)&&i(e.secretAccessKey)||i(process.env.AWS_ACCESS_KEY_ID)&&i(process.env.AWS_SECRET_ACCESS_KEY);return r||o}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 r=e?.name||e.code,o=e.message||"";switch(r){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 o.includes("Region")?"AWS region is required for Bedrock. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.":null}generateCommitMessage$(){return W(this.generateMessage("commit")).pipe(b(e=>T(e)),x(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:this.params.config.includeBody?e.value:e.title,description:this.params.config.includeBody?e.value:"",isError:!1})),L(this.handleError$))}generateCodeReview$(){return W(this.generateMessage("review")).pipe(b(e=>T(e)),x(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:e.value,description:e.value,isError:!1})),L(this.handleError$))}async generateMessage(e){const r=this.params.stagedDiff.diff,o=this.bedrockConfig,t=o.model,{logging:c,inferenceParameters:n}=o,s={...J,locale:o.locale,maxLength:o.maxLength,type:o.type,generate:o.generate,systemPrompt:o.systemPrompt,systemPromptPath:o.systemPromptPath,codeReviewPromptPath:o.codeReviewPromptPath},a=e==="review"?j(s):Q(s),l=Z(r,e),C={region:this.getRegion(),profile:o.profile,modelId:t},w=`https://bedrock-runtime.${this.getRegion()||"unknown"}.amazonaws.com/model/${encodeURIComponent(t)}/converse`;Y(r,e,g,t,w,C,c),z(r,e,g,a,l,c);const P={modelId:t,systemPrompt:a,userPrompt:l,...n&&Object.keys(n).length>0&&{inferenceConfig:n}};H(r,e,g,P,c);const f=Date.now();try{const m=this.determineAuthMethod();O()&&(console.log(h.cyan(`[Bedrock] Authentication method: ${m}`)),console.log(h.cyan(`[Bedrock] Model ID: ${t}`)),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(o.key)}`)));const E=m==="bearer-token"?await this.invokeWithBearerToken({model:t,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:r,inferenceConfig:n}):await this.invokeWithAwsSdk({model:t,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:r,inferenceConfig:n}),I=Date.now()-f;return V(r,e,g,I,E,c),e==="review"?this.sanitizeResponse(E):this.parseMessage(E,o.type,o.generate)}catch(m){throw N(r,e,g,m,c),m instanceof Error&&(m.status=m?.status||m?.$metadata?.httpStatusCode),m}}async invokeWithAwsSdk(e){const r=this.getRegion(),{model:o,systemPrompt:t,userPrompt:c,inferenceConfig:n,logging:s,requestType:a,diff:l}=e,{BedrockRuntimeClient:C,ConverseCommand:w}=await ee(),P=this.bedrockConfig,f={region:r,requestHandler:{requestTimeout:P.timeout||12e4}},m=await this.resolveCredentials();m&&(f.credentials=m);const E=new C(f),I=new w({modelId:o,messages:[{role:"user",content:[{text:c}]}],...t?{system:[{text:t}]}:{},...n&&Object.keys(n).length>0&&{inferenceConfig:n}});O()&&console.log(h.cyan(`[Bedrock] Sending ConverseCommand with modelId: ${o}`));let y;try{y=await E.send(I),K(l,a,g,y,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=y.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=y,d}return A}invokeWithBearerToken(e){const{model:r,systemPrompt:o,userPrompt:t,inferenceConfig:c,logging:n,requestType:s,diff:a}=e,l=this.bedrockConfig,C=this.getRegion(),w=encodeURIComponent(r),P=l.applicationBaseUrl||`https://bedrock-runtime.${C}.amazonaws.com/model/${w}/converse`,f=new URL(P),m={modelId:r,messages:[{role:"user",content:[{text:t}]}],...c&&Object.keys(c).length>0&&{inferenceConfig:c}};o&&(m.system=[{text:o}]);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((y,A)=>{const d=F.request({method:"POST",protocol:f.protocol,hostname:f.hostname,port:f.port,path:f.pathname+f.search,headers:I,timeout:l.timeout},k=>{const R=[];k.on("data",v=>R.push(v)),k.on("end",()=>{const v=Buffer.concat(R).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=v,N(a,s,g,p,n),A(p)}const D=X(v);if(!D.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=v,N(a,s,g,p,n),A(p)}const B=D.data;K(a,s,g,B,n);const $=B.output?.message?.content?.[0]?.text||"";if(!$){const p=new Error("No text content found in Bedrock response.");return p.name=S.EMPTY_RESPONSE,p.content=B,N(a,s,g,p,n),A(p)}y($)})});d.on("error",k=>{const R=k;N(a,s,g,R,n),A(R)}),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 r=this.bedrockConfig,o=r.profile,t=r.accessKeyId,c=r.secretAccessKey,n=r.sessionToken;let s;if(i(o)){const{fromIni:a}=await U();s=a({profile:o})}else if(i(t)&&i(c))s=async()=>({accessKeyId:t,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{oe as BedrockService};