aicommit2 2.5.19 → 2.5.21
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 +15 -0
- package/dist/{ai.service-6a643e64.mjs → ai.service-d8e94c3a.mjs} +1 -1
- package/dist/{anthropic.service-db73da5c.mjs → anthropic.service-eea2d7ef.mjs} +1 -1
- package/dist/{bedrock.service-f95b65e7.mjs → bedrock.service-29cc4421.mjs} +1 -1
- package/dist/cli-8ee62906.mjs +311 -0
- package/dist/cli.mjs +1 -1
- package/dist/{codestral.service-ac766d55.mjs → codestral.service-d45d6122.mjs} +1 -1
- package/dist/{cohere.service-58d98433.mjs → cohere.service-1ceaf1ef.mjs} +1 -1
- package/dist/{copilot-sdk.service-2cf2c8a7.mjs → copilot-sdk.service-4978100d.mjs} +1 -1
- package/dist/{deep-seek.service-689c41db.mjs → deep-seek.service-f9be94b6.mjs} +1 -1
- package/dist/{gemini.service-8a6d5fda.mjs → gemini.service-6329a802.mjs} +1 -1
- package/dist/{github-models.service-9fa54a50.mjs → github-models.service-1583c927.mjs} +1 -1
- package/dist/{groq.service-4381f607.mjs → groq.service-79123874.mjs} +1 -1
- package/dist/{hugging-face.service-d56491c1.mjs → hugging-face.service-23c52364.mjs} +1 -1
- package/dist/{mistral.service-46c37fa7.mjs → mistral.service-cc903eb9.mjs} +1 -1
- package/dist/{ollama.service-bc04e529.mjs → ollama.service-f4ebd1ee.mjs} +1 -1
- package/dist/{openai-3cfd2e5c.mjs → openai-5eabc0ae.mjs} +1 -1
- package/dist/{openai-compatible.service-74e02ff6.mjs → openai-compatible.service-3cfafd1a.mjs} +1 -1
- package/dist/{openai.service-99101be8.mjs → openai.service-f8e709b6.mjs} +1 -1
- package/dist/{openrouter.service-8beedf3e.mjs → openrouter.service-c6c3af5b.mjs} +1 -1
- package/dist/{perplexity.service-ae3bf8d3.mjs → perplexity.service-9ef820e3.mjs} +1 -1
- package/package.json +1 -1
- package/dist/cli-4bb33bd7.mjs +0 -289
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ _aicommit2_ automatically generates commit messages using AI. It supports [Git](
|
|
|
81
81
|
- **[OpenAI API Compatibility](docs/providers/compatible.md)**: Support for any service that implements the OpenAI API specification
|
|
82
82
|
- **[Reactive CLI](#usage)**: Enables simultaneous requests to multiple AIs and selection of the best commit message
|
|
83
83
|
- **[Code Review](#code-review)**: AI-powered structured code review with severity levels before committing
|
|
84
|
+
- **[Commit Rewrite](#usage)**: Rewrite the commit message of any existing commit with AI (`aicommit2 rewrite`)
|
|
84
85
|
- **[Git Hook Integration](#git-hooks)**: Can be used as a prepare-commit-msg hook
|
|
85
86
|
- **[Custom Prompt](#custom-prompt-template)**: Supports user-defined system prompt templates
|
|
86
87
|
- **[Diff Compression](#diff-compression)**: Reduces token usage by 30-60% with smart diff compression
|
|
@@ -454,6 +455,7 @@ In addition to the main commit message generation, aicommit2 provides several ut
|
|
|
454
455
|
| `aicommit2 config` | Manage configuration (get, set, list, del) |
|
|
455
456
|
| `aicommit2 doctor` | Check health status of AI providers |
|
|
456
457
|
| `aicommit2 stats` | View usage statistics and performance metrics |
|
|
458
|
+
| `aicommit2 rewrite` | Rewrite the commit message of any commit using AI |
|
|
457
459
|
| `aicommit2 hook` | Install/uninstall Git prepare-commit-msg hook |
|
|
458
460
|
| `aicommit2 log` | Manage log files |
|
|
459
461
|
| `aicommit2 github-login` | Login to GitHub for GitHub Models access |
|
|
@@ -478,6 +480,11 @@ aicommit2 stats clear # Clear all stats
|
|
|
478
480
|
# Git hook
|
|
479
481
|
aicommit2 hook install
|
|
480
482
|
aicommit2 hook uninstall
|
|
483
|
+
|
|
484
|
+
# Rewrite commit message
|
|
485
|
+
aicommit2 rewrite # Rewrite HEAD commit message
|
|
486
|
+
aicommit2 rewrite abc1234 # Rewrite specific commit
|
|
487
|
+
aicommit2 rewrite HEAD~2 --dry-run # Preview without rewriting
|
|
481
488
|
```
|
|
482
489
|
|
|
483
490
|
> GitHub Models tip: use `aicommit2 github-login` and set `GITHUB_MODELS.model` in `publisher/model` format (for example, `openai/gpt-5`).
|
|
@@ -1272,6 +1279,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
1272
1279
|
<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>
|
|
1273
1280
|
<td align="center"><a href="https://github.com/lawrence3699"><img src="https://avatars.githubusercontent.com/lawrence3699" width="100px;" alt=""/><br /><sub><b>@lawrence3699</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=lawrence3699" title="Code">💻</a></td>
|
|
1274
1281
|
<td align="center"><a href="https://github.com/atlet99"><img src="https://avatars.githubusercontent.com/atlet99" width="100px;" alt=""/><br /><sub><b>@atlet99</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=atlet99" title="Code">💻</a></td>
|
|
1282
|
+
<td align="center"><a href="https://github.com/HoChihchou"><img src="https://avatars.githubusercontent.com/HoChihchou" width="100px;" alt=""/><br /><sub><b>@HoChihchou</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=HoChihchou" title="Code">💻</a></td>
|
|
1283
|
+
</tr>
|
|
1284
|
+
<tr>
|
|
1285
|
+
<td align="center"><a href="https://github.com/chenmi319"><img src="https://avatars.githubusercontent.com/chenmi319" width="100px;" alt=""/><br /><sub><b>@chenmi319</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=chenmi319" title="Code">💻</a></td>
|
|
1286
|
+
<td align="center"><a href="https://github.com/JiwaniZakir"><img src="https://avatars.githubusercontent.com/JiwaniZakir" width="100px;" alt=""/><br /><sub><b>@JiwaniZakir</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=JiwaniZakir" title="Code">💻</a></td>
|
|
1287
|
+
<td align="center"><a href="https://github.com/qistchan"><img src="https://avatars.githubusercontent.com/qistchan" width="100px;" alt=""/><br /><sub><b>@qistchan</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=qistchan" title="Code">💻</a></td>
|
|
1288
|
+
<td align="center"><a href="https://github.com/Cassius0924"><img src="https://avatars.githubusercontent.com/Cassius0924" width="100px;" alt=""/><br /><sub><b>@Cassius0924</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=Cassius0924" title="Code">💻</a></td>
|
|
1289
|
+
<td align="center"><a href="https://github.com/Xyhlon"><img src="https://avatars.githubusercontent.com/Xyhlon" width="100px;" alt=""/><br /><sub><b>@Xyhlon</b></sub></a><br /><a href="https://github.com/tak-bro/aicommit2/commits?author=Xyhlon" title="Code">💻</a></td>
|
|
1275
1290
|
</tr>
|
|
1276
1291
|
</table>
|
|
1277
1292
|
<!-- markdownlint-restore -->
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{of as L,Observable as _,Subject as T,catchError as B}from"rxjs";import"fs";import"path";import{xxh64 as P}from"@pacote/xxhash";import $ from"winston";import{A as J,l as p,D as F,g as U,d as D,s as b,h as z,e as q,E as H,f as K,i as R}from"./cli-
|
|
1
|
+
import{of as L,Observable as _,Subject as T,catchError as B}from"rxjs";import"fs";import"path";import{xxh64 as P}from"@pacote/xxhash";import $ from"winston";import{A as J,l as p,D as F,g as U,d as D,s as b,h as z,e as q,E as H,f as K,i as R}from"./cli-8ee62906.mjs";const y=new Map,d=(f,r,e)=>{const s=P(0).update(r).digest("hex").substring(0,8),t=`${f}_${s}_${e}`;if(y.has(t))return y.get(t);const n=new Date,o=ee(n,f,r,e),c=`${J}/${o}`,i=$.createLogger({level:"info",format:$.format.combine($.format.timestamp({format:"YYYY-MM-DDTHH:mm:ss.SSSZ"}),$.format.printf(({timestamp:u,level:l,message:a,...m})=>m&&Object.keys(m).length>0?`[${u}] ${l}: ${a} ${JSON.stringify(m,null,2)}`:`[${u}] ${l}: ${a}`)),transports:[new $.transports.File({filename:c})]});return i.info(`=== ${f.toUpperCase()} AI SERVICE LOG ===`),i.info(`Diff Hash: ${s}`),i.info(`Request Type: ${e.toUpperCase()}`),i.info(`Start Time: ${n.toISOString()}`),i.info("=".repeat(50)),i.info(""),y.set(t,i),i},G=f=>{const r={...f},e=["authorization","x-api-key","x-goog-api-key","api-key","x-amzn-bedrock-application-key"];for(const s of e){const t=s.toLowerCase(),n=Object.keys(r).find(o=>o.toLowerCase()===t);n&&r[n]&&typeof r[n]=="string"&&(r[n].startsWith("Bearer ")?r[n]="Bearer [MASKED]":r[n]="[MASKED]")}return r},W=(f,r,e,s,t,n,o=!0)=>{if(!o)return;const c=d(e,f,r);c.info(`Making request to ${e} API with model: ${s}`),c.info(`Request URL: ${t}`),c.info("Request headers:",G(n))},Y=(f,r,e,s,t=!0)=>{if(!t)return;d(e,f,r).info("Request payload:",s)},V=(f,r,e,s,t,n=!0)=>{if(!n)return;const o=d(e,f,r);o.info("System prompt:",{prompt:s}),o.info("User prompt:",{prompt:t})},X=(f,r,e,s,t=!0)=>{if(!t)return;d(e,f,r).info("Response received:",s)},Q=(f,r,e,s,t=!0)=>{if(!t)return;d(e,f,r).error("API request failed:",s)},M=(f,r,e,s,t,n=!0)=>{if(!n)return;const o=d(e,f,r);s?o.info(`Request completed successfully in ${s}ms`):o.info("Request completed successfully"),t&&o.info("Final processed response:",{response:t}),o.info(""),o.info("=".repeat(50)),o.info(`End Time: ${new Date().toISOString()}`),o.info("=== REQUEST COMPLETED ===")},Z=(f,r,e,s,t,n,o,c=!0)=>{if(!c)return;const i=d(e,f,r);o?i.error(`Request failed after ${n}ms:`,{error:o}):(i.info(`Request completed in ${n}ms`),i.info("Response:",{response:t})),M(f,r,e,n,t,c)},ee=(f,r,e,s)=>{const{year:t,month:n,day:o,hours:c,minutes:i,seconds:u}=te(f),a=P(0).update(e).digest("hex").substring(0,8),m=r.toLowerCase().replace(/[^a-z0-9]/g,"").substring(0,20);return s==="review"?`${t}-${n}-${o}_${c}-${i}-${u}_${a}_${m}_review.log`:`${t}-${n}-${o}_${c}-${i}-${u}_${a}_${m}_commit.log`},te=f=>{const r=f.getFullYear().toString(),e=(f.getMonth()+1).toString().padStart(2,"0"),s=f.getDate().toString().padStart(2,"0"),t=f.getHours().toString().padStart(2,"0"),n=f.getMinutes().toString().padStart(2,"0"),o=f.getSeconds().toString().padStart(2,"0");return{year:r,month:e,day:s,hours:t,minutes:n,seconds:o}},w=()=>{for(const[f,r]of y.entries())try{r.close()}catch(e){console.error(`Failed to close logger ${f}:`,e)}y.clear()};process.on("exit",w),process.on("SIGINT",()=>{w(),process.exit(0)}),process.on("SIGTERM",()=>{w(),process.exit(0)});const se=["o1","o3","o4-mini","gpt-5","deepseek-v4-flash","deepseek-v4-pro","deepseek-reasoner","deepseek-r1","qwq","qwen3","phi4-mini-reasoning","smallthinker"],re=["gemini-2.5"],oe=f=>{const r=f.toLowerCase(),e=r.includes("/")&&r.split("/").pop()||r,s=e.includes(":")?e.split(":")[0]:e;return re.some(t=>s.includes(t))?!0:se.some(t=>s===t||s.startsWith(`${t}-`)||s.startsWith(`${t}.`))};class ne{constructor(){this.buffer="",this.arrayStartFound=!1,this.scanPosition=0,this.feed=r=>{this.buffer+=r;const e=[];if(!this.arrayStartFound){const s=this.buffer.indexOf("[");if(s===-1)return e;this.arrayStartFound=!0,this.scanPosition=s+1}for(;;){const s=this.buffer.indexOf("{",this.scanPosition);if(s===-1)break;const t=this.extractBalancedBraces(s);if(!t)break;this.scanPosition=s+t.length;const n=this.tryParseCommitMessage(t);n&&e.push(n)}return e},this.flush=()=>this.feed(""),this.getBuffer=()=>this.buffer,this.getUnparsedBuffer=()=>this.buffer.slice(this.scanPosition),this.extractBalancedBraces=r=>{let e=0,s=!1,t=!1;for(let n=r;n<this.buffer.length;n++){const o=this.buffer[n];if(t){t=!1;continue}if(o==="\\"&&s){t=!0;continue}if(o==='"'){s=!s;continue}if(!s&&(o==="{"&&e++,o==="}"&&e--,e===0))return this.buffer.slice(r,n+1)}return null},this.tryParseCommitMessage=r=>{try{const e=JSON.parse(r);return typeof e.subject!="string"?null:{subject:e.subject,body:typeof e.body=="string"?e.body:void 0,footer:typeof e.footer=="string"?e.footer:void 0}}catch{return null}}}}class ie{constructor(r){this.formatModelSuffix=()=>{const e=this.params.modelNameDisplay??"short",s=this.params.config.model;if(e==="none"||!s)return"";const t=Array.isArray(s)?s[0]:String(s);if(e==="full")return`/${t}`;const n=t.split("/").pop()||t;return n.length>20?`/${n.slice(0,19)}\u2026`:`/${n}`},this.handleError$=e=>{const s=this.getDetailedErrorMessage(e),t=e.status?`HTTP ${e.status}: ${s}`:s;if(this.params.config.logging){const n=this.params.stagedDiff.diff,o=this.serviceName.replace(/\[|\]/g,"").trim();Z(n,"commit",o,"Error occurred","",void 0,t)}return p.error(`${this.errorPrefix} ${t}`),e.stack&&p.error(` ${e.stack}`),e.content&&p.error(` Problematic content: ${e.content}`),e.originalError&&p.error(` Original error: ${e.originalError}`),L({name:`${this.errorPrefix} ${t}`,value:t,isError:!0,disabled:!0})},this.extractJsonObjectFromResponse=e=>{const s=e.indexOf("{");return s!==-1?this.extractBalancedJson(e,s,"{","}"):null},this.buildPromptOptions=()=>{const{systemPrompt:e,systemPromptPath:s,codeReviewPromptPath:t,locale:n,generate:o,type:c,maxLength:i,model:u}=this.params.config,l=Array.isArray(u)?u[0]||"":String(u||"");return{...F,locale:n,maxLength:i,type:c,generate:o,systemPrompt:e,systemPromptPath:s,codeReviewPromptPath:t,vcs_branch:this.params.branchName||"",isReasoning:oe(l)}},this.buildCommitPrompt=()=>U(this.buildPromptOptions()),this.buildUserPrompt=(e,s="commit")=>{const t={recentCommits:this.params.recentCommits,branchName:this.params.branchName};return D(e,s,t)},this.formatRawCommitMessage=(e,s)=>{const t=this.extractMessageAsType(e,s),n=t.subject,o=`${t.subject}${t.body?`
|
|
2
2
|
|
|
3
3
|
${t.body}`:""}${t.footer?`
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import L from"@anthropic-ai/sdk";import P from"chalk";import{concatMap as v,from as w,map as I,catchError as x}from"rxjs";import{fromPromise as S}from"rxjs/internal/observable/innerFrom";import{A as T,l as E,a as $,b,c as _,d as U,e as D}from"./ai.service-
|
|
1
|
+
import L from"@anthropic-ai/sdk";import P from"chalk";import{concatMap as v,from as w,map as I,catchError as x}from"rxjs";import{fromPromise as S}from"rxjs/internal/observable/innerFrom";import{A as T,l as E,a as $,b,c as _,d as U,e as D}from"./ai.service-d8e94c3a.mjs";import{g as F,b as N}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 W=["claude-4","claude-haiku-4","claude-sonnet-4","claude-opus-4"],R=M=>{const o=M.toLowerCase();return W.some(t=>o===t||o.startsWith(`${t}-`)||o.startsWith(`${t}.`))},z=10*60*1e3;class H extends T{constructor(o){super(o),this.params=o,this.generateStreamingCommitMessage$=()=>{const{generate:t,type:e}=this.params.config;return this.createStreamingCommitMessages$(r=>{this.streamChunks(r).catch(m=>r.error(m))},e,t)},this.streamChunks=async t=>{const e=this.params.stagedDiff.diff,{logging:r,temperature:m,maxTokens:u,topP:g,model:c}=this.params.config,n=F(this.buildPromptOptions()),p=this.buildUserPrompt(e,"commit"),l=`${this.params.config.url||"https://api.anthropic.com"}/v1/messages`,k={"Content-Type":"application/json","x-api-key":this.params.config.key,"anthropic-version":"2023-06-01"};E(e,"commit","Anthropic",c,l,k,r),$(e,"commit","Anthropic",n,p,r);const A=R(c),d={max_tokens:u,temperature:m,system:n,messages:[{role:"user",content:p}],model:c,stream:!0,...A?{}:{top_p:g}};b(e,"commit","Anthropic",d,r);const y=Date.now();let h="";try{const i=this.anthropic.messages.stream(d);i.on("text",a=>{h+=a,t.next(a)});const s=await i.finalMessage(),C=Date.now()-y;_(e,"commit","Anthropic",s,r),U(e,"commit","Anthropic",C,h,r),t.complete()}catch(i){D(e,"commit","Anthropic",i,r),t.error(i)}},this.colors={primary:"#AE5630",secondary:"#fff"},this.serviceName=P.bgHex(this.colors.primary).hex(this.colors.secondary).bold(`[Anthropic${this.formatModelSuffix()}]`),this.errorPrefix=P.red.bold(`[Anthropic${this.formatModelSuffix()}]`),this.anthropic=new L({apiKey:this.params.config.key,...this.params.config.timeout>z&&{timeout:this.params.config.timeout}})}getServiceSpecificErrorMessage(o){const t=o.message||"";return t.includes("API key")||t.includes("api_key")?"Invalid API key. Check your Anthropic API key in configuration":t.includes("quota")||t.includes("usage")?"API quota exceeded. Check your Anthropic usage limits":t.includes("model_not_found")||t.includes("Model not found")||/model.*does not exist/i.test(t)?"Model not found or not accessible. Check if the Claude model name is correct":t.includes("403")||t.includes("Forbidden")?"Access denied. Your API key may not have permission for this Claude model":t.includes("404")||t.includes("Not Found")?"Model or endpoint not found. Check your Claude model configuration":t.includes("500")||t.includes("Internal Server Error")?"Anthropic server error. Try again later":null}generateCommitMessage$(){return this.params.config.stream||!1?this.generateStreamingCommitMessage$():S(this.generateMessage("commit")).pipe(v(t=>w(t)),I(this.formatAsChoice),x(this.handleError$))}generateCodeReview$(){return S(this.generateMessage("review")).pipe(v(o=>w(o)),I(this.formatCodeReviewAsChoice),x(this.handleError$))}async generateMessage(o){const t=this.params.stagedDiff.diff,{logging:e,temperature:r,generate:m,type:u,maxTokens:g,topP:c,model:n}=this.params.config,p=this.buildPromptOptions(),f=o==="review"?N(p):F(p),l=this.buildUserPrompt(t,o),A=`${this.params.config.url||"https://api.anthropic.com"}/v1/messages`,d={"Content-Type":"application/json","x-api-key":this.params.config.key,"anthropic-version":"2023-06-01"};E(t,o,"Anthropic",n,A,d,e),$(t,o,"Anthropic",f,l,e);const y=R(n),h={max_tokens:g,temperature:r,system:f,messages:[{role:"user",content:l}],model:n,...y?{}:{top_p:c}};b(t,o,"Anthropic",h,e);const i=Date.now();try{const s=await this.anthropic.messages.create(h),C=Date.now()-i;_(t,o,"Anthropic",s,e);const a=s.content.map(({text:O})=>O).join("");return U(t,o,"Anthropic",C,a,e),o==="review"?this.parseCodeReview(a):this.parseMessage(a,u,m)}catch(s){throw D(t,o,"Anthropic",s,e),s}}}export{H as AnthropicService};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
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-
|
|
1
|
+
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};
|