mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
fix: atomic writer race condition and bulk replace reset to defaults
1. AtomicWriter Race Condition Fix (libs/utils/src/atomic-writer.ts):
- Changed temp file naming from Date.now() to Date.now() + random hex
- Uses crypto.randomBytes(4).toString('hex') for uniqueness
- Prevents ENOENT errors when multiple concurrent writes happen
within the same millisecond
2. Bulk Replace "Anthropic Direct" Reset (both dialogs):
- When selecting "Anthropic Direct", now uses DEFAULT_PHASE_MODELS
- Properly resets thinking levels and other settings to defaults
- Added thinkingLevel to the change detection comparison
- Affects both global and project-level bulk replace dialogs
This commit is contained in:
@@ -121,11 +121,12 @@ export function ProjectBulkReplaceDialog({
|
|||||||
// Find the model from provider that maps to a specific Claude model
|
// Find the model from provider that maps to a specific Claude model
|
||||||
const findModelForClaudeAlias = (
|
const findModelForClaudeAlias = (
|
||||||
provider: ClaudeCompatibleProvider | null,
|
provider: ClaudeCompatibleProvider | null,
|
||||||
claudeAlias: ClaudeModelAlias
|
claudeAlias: ClaudeModelAlias,
|
||||||
|
phase: PhaseModelKey
|
||||||
): PhaseModelEntry => {
|
): PhaseModelEntry => {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
// Anthropic Direct - use Claude model directly
|
// Anthropic Direct - reset to default phase model (includes correct thinking levels)
|
||||||
return { model: claudeAlias };
|
return DEFAULT_PHASE_MODELS[phase];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find model that maps to this Claude alias
|
// Find model that maps to this Claude alias
|
||||||
@@ -152,7 +153,7 @@ export function ProjectBulkReplaceDialog({
|
|||||||
const globalEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
|
const globalEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
|
||||||
const currentEntry = projectOverrides[phase] || globalEntry;
|
const currentEntry = projectOverrides[phase] || globalEntry;
|
||||||
const claudeAlias = getClaudeModelAlias(currentEntry);
|
const claudeAlias = getClaudeModelAlias(currentEntry);
|
||||||
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias);
|
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase);
|
||||||
|
|
||||||
// Get display names
|
// Get display names
|
||||||
const getCurrentDisplay = (): string => {
|
const getCurrentDisplay = (): string => {
|
||||||
@@ -175,7 +176,9 @@ export function ProjectBulkReplaceDialog({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isChanged =
|
const isChanged =
|
||||||
currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId;
|
currentEntry.model !== newEntry.model ||
|
||||||
|
currentEntry.providerId !== newEntry.providerId ||
|
||||||
|
currentEntry.thinkingLevel !== newEntry.thinkingLevel;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
phase,
|
phase,
|
||||||
|
|||||||
@@ -112,11 +112,12 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
|
|||||||
// Find the model from provider that maps to a specific Claude model
|
// Find the model from provider that maps to a specific Claude model
|
||||||
const findModelForClaudeAlias = (
|
const findModelForClaudeAlias = (
|
||||||
provider: ClaudeCompatibleProvider | null,
|
provider: ClaudeCompatibleProvider | null,
|
||||||
claudeAlias: ClaudeModelAlias
|
claudeAlias: ClaudeModelAlias,
|
||||||
|
phase: PhaseModelKey
|
||||||
): PhaseModelEntry => {
|
): PhaseModelEntry => {
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
// Anthropic Direct - use Claude model directly
|
// Anthropic Direct - reset to default phase model (includes correct thinking levels)
|
||||||
return { model: claudeAlias };
|
return DEFAULT_PHASE_MODELS[phase];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find model that maps to this Claude alias
|
// Find model that maps to this Claude alias
|
||||||
@@ -141,7 +142,7 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
|
|||||||
return ALL_PHASES.map((phase) => {
|
return ALL_PHASES.map((phase) => {
|
||||||
const currentEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
|
const currentEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
|
||||||
const claudeAlias = getClaudeModelAlias(currentEntry);
|
const claudeAlias = getClaudeModelAlias(currentEntry);
|
||||||
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias);
|
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase);
|
||||||
|
|
||||||
// Get display names
|
// Get display names
|
||||||
const getCurrentDisplay = (): string => {
|
const getCurrentDisplay = (): string => {
|
||||||
@@ -164,7 +165,9 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isChanged =
|
const isChanged =
|
||||||
currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId;
|
currentEntry.model !== newEntry.model ||
|
||||||
|
currentEntry.providerId !== newEntry.providerId ||
|
||||||
|
currentEntry.thinkingLevel !== newEntry.thinkingLevel;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
phase,
|
phase,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import { secureFs } from '@automaker/platform';
|
import { secureFs } from '@automaker/platform';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import crypto from 'crypto';
|
||||||
import { createLogger } from './logger.js';
|
import { createLogger } from './logger.js';
|
||||||
import { mkdirSafe } from './fs-utils.js';
|
import { mkdirSafe } from './fs-utils.js';
|
||||||
|
|
||||||
@@ -99,7 +100,9 @@ export async function atomicWriteJson<T>(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { indent = 2, createDirs = false, backupCount = 0 } = options;
|
const { indent = 2, createDirs = false, backupCount = 0 } = options;
|
||||||
const resolvedPath = path.resolve(filePath);
|
const resolvedPath = path.resolve(filePath);
|
||||||
const tempPath = `${resolvedPath}.tmp.${Date.now()}`;
|
// Use timestamp + random suffix to ensure uniqueness even for concurrent writes
|
||||||
|
const uniqueSuffix = `${Date.now()}.${crypto.randomBytes(4).toString('hex')}`;
|
||||||
|
const tempPath = `${resolvedPath}.tmp.${uniqueSuffix}`;
|
||||||
|
|
||||||
// Create parent directories if requested
|
// Create parent directories if requested
|
||||||
if (createDirs) {
|
if (createDirs) {
|
||||||
|
|||||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -6218,6 +6218,7 @@
|
|||||||
"version": "19.2.7",
|
"version": "19.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -6227,7 +6228,7 @@
|
|||||||
"version": "19.2.3",
|
"version": "19.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
@@ -8438,6 +8439,7 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/d3-color": {
|
"node_modules/d3-color": {
|
||||||
@@ -11331,7 +11333,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11353,7 +11354,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11375,7 +11375,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11397,7 +11396,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11419,7 +11417,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11441,7 +11438,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11463,7 +11459,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11485,7 +11480,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11507,7 +11501,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11529,7 +11522,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11551,7 +11543,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user