@hifullmoon/aicommit 2.2.2 → 2.2.3
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/CHANGELOG.md +10 -1
- package/docs/distribution.md +1 -1
- package/package.json +1 -1
- package/src/api.js +14 -8
- package/src/setup.js +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.2.3] - 2026-09-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Prevented accepted provider requests from being replayed after response-body interruptions, avoiding duplicate generations and potential duplicate billing.
|
|
12
|
+
- Honored provider retry timing safely by failing clearly when `Retry-After` exceeds the configured maximum delay instead of retrying too early.
|
|
13
|
+
- Stopped setup from replacing valid JSON configurations with unsupported schemas or invalid structures.
|
|
14
|
+
|
|
7
15
|
## [2.2.2] - 2026-08-31
|
|
8
16
|
|
|
9
17
|
### Added
|
|
@@ -170,7 +178,8 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
170
178
|
- Added file-level split planning and execution with Git-state concurrency checks.
|
|
171
179
|
- Added provider presets and user/project configuration boundaries.
|
|
172
180
|
|
|
173
|
-
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.2.
|
|
181
|
+
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.2.3...HEAD
|
|
182
|
+
[2.2.3]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.2.3
|
|
174
183
|
[2.2.2]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.2.2
|
|
175
184
|
[2.2.1]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.2.1
|
|
176
185
|
[2.2.0]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.2.0
|
package/docs/distribution.md
CHANGED
|
@@ -26,7 +26,7 @@ The release workflow uses npm Trusted Publishing without a long-lived `NPM_TOKEN
|
|
|
26
26
|
```bash
|
|
27
27
|
workdir=$(mktemp -d)
|
|
28
28
|
cd "$workdir"
|
|
29
|
-
npm install --package-lock-only @hifullmoon/aicommit@2.2.
|
|
29
|
+
npm install --package-lock-only @hifullmoon/aicommit@2.2.3
|
|
30
30
|
npm audit signatures
|
|
31
31
|
```
|
|
32
32
|
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -136,18 +136,24 @@ async function fetchWithRetry(apiUrl, init, timeout, configuredPolicy, consume)
|
|
|
136
136
|
} catch (err) {
|
|
137
137
|
const wrappedTimeout = timeoutError(err, timeout);
|
|
138
138
|
if (wrappedTimeout) throw wrappedTimeout;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
// Once the provider has accepted a generation request, replaying it is
|
|
140
|
+
// unsafe: the first request may already have completed and been billed
|
|
141
|
+
// even though its response body was interrupted locally.
|
|
142
|
+
throw err;
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
if (RETRYABLE_STATUS.has(response.status) && attempt < policy.maxAttempts) {
|
|
146
146
|
const requestedDelay = retryAfterMs(response.headers.get('retry-after'), policy.now);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
if (requestedDelay !== null && requestedDelay > policy.maxDelayMs) {
|
|
148
|
+
await response.body?.cancel().catch(() => {});
|
|
149
|
+
throw new Error(
|
|
150
|
+
`HTTP ${response.status}: provider requested a retry after ${Math.ceil(
|
|
151
|
+
requestedDelay / 1000,
|
|
152
|
+
)}s, exceeding the configured retry.maxDelayMs limit.`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const delay =
|
|
156
|
+
requestedDelay ?? Math.min(policy.baseDelayMs * 2 ** (attempt - 1), policy.maxDelayMs);
|
|
151
157
|
await response.body?.cancel().catch(() => {});
|
|
152
158
|
await policy.sleep(delay);
|
|
153
159
|
continue;
|
package/src/setup.js
CHANGED
|
@@ -74,9 +74,9 @@ export function mergeSetupConfig(existing, { providerName, entry, language }) {
|
|
|
74
74
|
|
|
75
75
|
async function readExistingConfig(path) {
|
|
76
76
|
if (!(await fileExists(path))) return {};
|
|
77
|
+
let parsed;
|
|
77
78
|
try {
|
|
78
|
-
|
|
79
|
-
return validateUserConfig(parsed);
|
|
79
|
+
parsed = JSON.parse(await readFile(path, 'utf-8'));
|
|
80
80
|
} catch (err) {
|
|
81
81
|
const backup = `${path}.invalid-${Date.now()}.bak`;
|
|
82
82
|
await copyFile(path, backup);
|
|
@@ -84,6 +84,16 @@ async function readExistingConfig(path) {
|
|
|
84
84
|
console.log(chalk.dim(` The original file was preserved at ${backup}.`));
|
|
85
85
|
return {};
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
return validateUserConfig(parsed);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Existing config ${path} is valid JSON but has an unsupported structure: ${err.message} ` +
|
|
93
|
+
'Fix it or move it aside before running setup.',
|
|
94
|
+
{ cause: err },
|
|
95
|
+
);
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
async function writeConfigAtomic(path, value) {
|