mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
Merge remote-tracking branch 'upstream/v0.11.0rc' into patchcraft
This commit is contained in:
@@ -129,10 +129,30 @@ export const TOOL_PRESETS = {
|
||||
specGeneration: ['Read', 'Glob', 'Grep'] as const,
|
||||
|
||||
/** Full tool access for feature implementation */
|
||||
fullAccess: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch', 'TodoWrite'] as const,
|
||||
fullAccess: [
|
||||
'Read',
|
||||
'Write',
|
||||
'Edit',
|
||||
'Glob',
|
||||
'Grep',
|
||||
'Bash',
|
||||
'WebSearch',
|
||||
'WebFetch',
|
||||
'TodoWrite',
|
||||
] as const,
|
||||
|
||||
/** Tools for chat/interactive mode */
|
||||
chat: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch', 'TodoWrite'] as const,
|
||||
chat: [
|
||||
'Read',
|
||||
'Write',
|
||||
'Edit',
|
||||
'Glob',
|
||||
'Grep',
|
||||
'Bash',
|
||||
'WebSearch',
|
||||
'WebFetch',
|
||||
'TodoWrite',
|
||||
] as const,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,6 +70,8 @@ const eslintConfig = defineConfig([
|
||||
AbortSignal: 'readonly',
|
||||
Audio: 'readonly',
|
||||
ScrollBehavior: 'readonly',
|
||||
URL: 'readonly',
|
||||
URLSearchParams: 'readonly',
|
||||
// Timers
|
||||
setTimeout: 'readonly',
|
||||
setInterval: 'readonly',
|
||||
|
||||
@@ -422,6 +422,31 @@ export function BoardView() {
|
||||
const selectedWorktreeBranch =
|
||||
currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main';
|
||||
|
||||
// Helper function to add and select a worktree
|
||||
const addAndSelectWorktree = useCallback(
|
||||
(worktreeResult: { path: string; branch: string }) => {
|
||||
if (!currentProject) return;
|
||||
|
||||
const currentWorktrees = getWorktrees(currentProject.path);
|
||||
const existingWorktree = currentWorktrees.find((w) => w.branch === worktreeResult.branch);
|
||||
|
||||
// Only add if it doesn't already exist (to avoid duplicates)
|
||||
if (!existingWorktree) {
|
||||
const newWorktreeInfo = {
|
||||
path: worktreeResult.path,
|
||||
branch: worktreeResult.branch,
|
||||
isMain: false,
|
||||
isCurrent: false,
|
||||
hasWorktree: true,
|
||||
};
|
||||
setWorktrees(currentProject.path, [...currentWorktrees, newWorktreeInfo]);
|
||||
}
|
||||
// Select the worktree (whether it existed or was just added)
|
||||
setCurrentWorktree(currentProject.path, worktreeResult.path, worktreeResult.branch);
|
||||
},
|
||||
[currentProject, getWorktrees, setWorktrees, setCurrentWorktree]
|
||||
);
|
||||
|
||||
// Extract all action handlers into a hook
|
||||
const {
|
||||
handleAddFeature,
|
||||
@@ -467,43 +492,90 @@ export function BoardView() {
|
||||
outputFeature,
|
||||
projectPath: currentProject?.path || null,
|
||||
onWorktreeCreated: () => setWorktreeRefreshKey((k) => k + 1),
|
||||
onWorktreeAutoSelect: (newWorktree) => {
|
||||
if (!currentProject) return;
|
||||
// Check if worktree already exists in the store (by branch name)
|
||||
const currentWorktrees = getWorktrees(currentProject.path);
|
||||
const existingWorktree = currentWorktrees.find((w) => w.branch === newWorktree.branch);
|
||||
|
||||
// Only add if it doesn't already exist (to avoid duplicates)
|
||||
if (!existingWorktree) {
|
||||
const newWorktreeInfo = {
|
||||
path: newWorktree.path,
|
||||
branch: newWorktree.branch,
|
||||
isMain: false,
|
||||
isCurrent: false,
|
||||
hasWorktree: true,
|
||||
};
|
||||
setWorktrees(currentProject.path, [...currentWorktrees, newWorktreeInfo]);
|
||||
}
|
||||
// Select the worktree (whether it existed or was just added)
|
||||
setCurrentWorktree(currentProject.path, newWorktree.path, newWorktree.branch);
|
||||
},
|
||||
onWorktreeAutoSelect: addAndSelectWorktree,
|
||||
currentWorktreeBranch,
|
||||
});
|
||||
|
||||
// Handler for bulk updating multiple features
|
||||
const handleBulkUpdate = useCallback(
|
||||
async (updates: Partial<Feature>) => {
|
||||
async (updates: Partial<Feature>, workMode: 'current' | 'auto' | 'custom') => {
|
||||
if (!currentProject || selectedFeatureIds.size === 0) return;
|
||||
|
||||
try {
|
||||
// Determine final branch name based on work mode:
|
||||
// - 'current': Empty string to clear branch assignment (work on main/current branch)
|
||||
// - 'auto': Auto-generate branch name based on current branch
|
||||
// - 'custom': Use the provided branch name
|
||||
let finalBranchName: string | undefined;
|
||||
|
||||
if (workMode === 'current') {
|
||||
// Empty string clears the branch assignment, moving features to main/current branch
|
||||
finalBranchName = '';
|
||||
} else if (workMode === 'auto') {
|
||||
// Auto-generate a branch name based on current branch and timestamp
|
||||
const baseBranch =
|
||||
currentWorktreeBranch || getPrimaryWorktreeBranch(currentProject.path) || 'main';
|
||||
const timestamp = Date.now();
|
||||
const randomSuffix = Math.random().toString(36).substring(2, 6);
|
||||
finalBranchName = `feature/${baseBranch}-${timestamp}-${randomSuffix}`;
|
||||
} else {
|
||||
// Custom mode - use provided branch name
|
||||
finalBranchName = updates.branchName || undefined;
|
||||
}
|
||||
|
||||
// Create worktree for 'auto' or 'custom' modes when we have a branch name
|
||||
if ((workMode === 'auto' || workMode === 'custom') && finalBranchName) {
|
||||
try {
|
||||
const electronApi = getElectronAPI();
|
||||
if (electronApi?.worktree?.create) {
|
||||
const result = await electronApi.worktree.create(
|
||||
currentProject.path,
|
||||
finalBranchName
|
||||
);
|
||||
if (result.success && result.worktree) {
|
||||
logger.info(
|
||||
`Worktree for branch "${finalBranchName}" ${
|
||||
result.worktree?.isNew ? 'created' : 'already exists'
|
||||
}`
|
||||
);
|
||||
// Auto-select the worktree when creating/using it for bulk update
|
||||
addAndSelectWorktree(result.worktree);
|
||||
// Refresh worktree list in UI
|
||||
setWorktreeRefreshKey((k) => k + 1);
|
||||
} else if (!result.success) {
|
||||
logger.error(
|
||||
`Failed to create worktree for branch "${finalBranchName}":`,
|
||||
result.error
|
||||
);
|
||||
toast.error('Failed to create worktree', {
|
||||
description: result.error || 'An error occurred',
|
||||
});
|
||||
return; // Don't proceed with update if worktree creation failed
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error creating worktree:', error);
|
||||
toast.error('Failed to create worktree', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
return; // Don't proceed with update if worktree creation failed
|
||||
}
|
||||
}
|
||||
|
||||
// Use the final branch name in updates
|
||||
const finalUpdates = {
|
||||
...updates,
|
||||
branchName: finalBranchName,
|
||||
};
|
||||
|
||||
const api = getHttpApiClient();
|
||||
const featureIds = Array.from(selectedFeatureIds);
|
||||
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates);
|
||||
const result = await api.features.bulkUpdate(currentProject.path, featureIds, finalUpdates);
|
||||
|
||||
if (result.success) {
|
||||
// Update local state
|
||||
featureIds.forEach((featureId) => {
|
||||
updateFeature(featureId, updates);
|
||||
updateFeature(featureId, finalUpdates);
|
||||
});
|
||||
toast.success(`Updated ${result.updatedCount} features`);
|
||||
exitSelectionMode();
|
||||
@@ -517,7 +589,16 @@ export function BoardView() {
|
||||
toast.error('Failed to update features');
|
||||
}
|
||||
},
|
||||
[currentProject, selectedFeatureIds, updateFeature, exitSelectionMode]
|
||||
[
|
||||
currentProject,
|
||||
selectedFeatureIds,
|
||||
updateFeature,
|
||||
exitSelectionMode,
|
||||
currentWorktreeBranch,
|
||||
getPrimaryWorktreeBranch,
|
||||
addAndSelectWorktree,
|
||||
setWorktreeRefreshKey,
|
||||
]
|
||||
);
|
||||
|
||||
// Handler for bulk deleting multiple features
|
||||
@@ -1325,6 +1406,9 @@ export function BoardView() {
|
||||
onClose={() => setShowMassEditDialog(false)}
|
||||
selectedFeatures={selectedFeatures}
|
||||
onApply={handleBulkUpdate}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentWorktreeBranch || undefined}
|
||||
/>
|
||||
|
||||
{/* Board Background Modal */}
|
||||
|
||||
@@ -138,7 +138,7 @@ export function BoardHeader({
|
||||
<div className={controlContainerClass} data-testid="worktrees-toggle-container">
|
||||
<GitBranch className="w-4 h-4 text-muted-foreground" />
|
||||
<Label htmlFor="worktrees-toggle" className="text-sm font-medium cursor-pointer">
|
||||
Worktrees
|
||||
Worktree Bar
|
||||
</Label>
|
||||
<Switch
|
||||
id="worktrees-toggle"
|
||||
|
||||
@@ -123,7 +123,7 @@ interface AddFeatureDialogProps {
|
||||
selectedNonMainWorktreeBranch?: string;
|
||||
/**
|
||||
* When true, forces the dialog to default to 'current' work mode (work on current branch).
|
||||
* This is used when the "Use selected worktree branch" setting is disabled.
|
||||
* This is used when the "Default to worktree mode" setting is disabled.
|
||||
*/
|
||||
forceCurrentBranchMode?: boolean;
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ export function BacklogPlanDialog({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4">{renderContent()}</div>
|
||||
<div className="py-4 overflow-y-auto">{renderContent()}</div>
|
||||
|
||||
<DialogFooter>
|
||||
{mode === 'input' && (
|
||||
|
||||
@@ -117,7 +117,7 @@ export function CreatePRDialog({
|
||||
description: `PR already exists for ${result.result.branch}`,
|
||||
action: {
|
||||
label: 'View PR',
|
||||
onClick: () => window.open(result.result!.prUrl!, '_blank'),
|
||||
onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -125,7 +125,7 @@ export function CreatePRDialog({
|
||||
description: `PR created from ${result.result.branch}`,
|
||||
action: {
|
||||
label: 'View PR',
|
||||
onClick: () => window.open(result.result!.prUrl!, '_blank'),
|
||||
onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -251,7 +251,10 @@ export function CreatePRDialog({
|
||||
<p className="text-sm text-muted-foreground mt-1">Your PR is ready for review</p>
|
||||
</div>
|
||||
<div className="flex gap-2 justify-center">
|
||||
<Button onClick={() => window.open(prUrl, '_blank')} className="gap-2">
|
||||
<Button
|
||||
onClick={() => window.open(prUrl, '_blank', 'noopener,noreferrer')}
|
||||
className="gap-2"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
View Pull Request
|
||||
</Button>
|
||||
@@ -277,7 +280,7 @@ export function CreatePRDialog({
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (browserUrl) {
|
||||
window.open(browserUrl, '_blank');
|
||||
window.open(browserUrl, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
}}
|
||||
className="gap-2 w-full"
|
||||
|
||||
@@ -13,7 +13,8 @@ import { Label } from '@/components/ui/label';
|
||||
import { AlertCircle } from 'lucide-react';
|
||||
import { modelSupportsThinking } from '@/lib/utils';
|
||||
import { Feature, ModelAlias, ThinkingLevel, PlanningMode } from '@/store/app-store';
|
||||
import { TestingTabContent, PrioritySelect, PlanningModeSelect } from '../shared';
|
||||
import { TestingTabContent, PrioritySelect, PlanningModeSelect, WorkModeSelector } from '../shared';
|
||||
import type { WorkMode } from '../shared';
|
||||
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
|
||||
import { isCursorModel, isClaudeModel, type PhaseModelEntry } from '@automaker/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -23,7 +24,10 @@ interface MassEditDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
selectedFeatures: Feature[];
|
||||
onApply: (updates: Partial<Feature>) => Promise<void>;
|
||||
onApply: (updates: Partial<Feature>, workMode: WorkMode) => Promise<void>;
|
||||
branchSuggestions: string[];
|
||||
branchCardCounts?: Record<string, number>;
|
||||
currentBranch?: string;
|
||||
}
|
||||
|
||||
interface ApplyState {
|
||||
@@ -33,6 +37,7 @@ interface ApplyState {
|
||||
requirePlanApproval: boolean;
|
||||
priority: boolean;
|
||||
skipTests: boolean;
|
||||
branchName: boolean;
|
||||
}
|
||||
|
||||
function getMixedValues(features: Feature[]): Record<string, boolean> {
|
||||
@@ -47,6 +52,7 @@ function getMixedValues(features: Feature[]): Record<string, boolean> {
|
||||
),
|
||||
priority: !features.every((f) => f.priority === first.priority),
|
||||
skipTests: !features.every((f) => f.skipTests === first.skipTests),
|
||||
branchName: !features.every((f) => f.branchName === first.branchName),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,7 +103,15 @@ function FieldWrapper({ label, isMixed, willApply, onApplyChange, children }: Fi
|
||||
);
|
||||
}
|
||||
|
||||
export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: MassEditDialogProps) {
|
||||
export function MassEditDialog({
|
||||
open,
|
||||
onClose,
|
||||
selectedFeatures,
|
||||
onApply,
|
||||
branchSuggestions,
|
||||
branchCardCounts,
|
||||
currentBranch,
|
||||
}: MassEditDialogProps) {
|
||||
const [isApplying, setIsApplying] = useState(false);
|
||||
|
||||
// Track which fields to apply
|
||||
@@ -108,6 +122,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
requirePlanApproval: false,
|
||||
priority: false,
|
||||
skipTests: false,
|
||||
branchName: false,
|
||||
});
|
||||
|
||||
// Field values
|
||||
@@ -118,6 +133,18 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
const [priority, setPriority] = useState(2);
|
||||
const [skipTests, setSkipTests] = useState(false);
|
||||
|
||||
// Work mode and branch name state
|
||||
const [workMode, setWorkMode] = useState<WorkMode>(() => {
|
||||
// Derive initial work mode from first selected feature's branchName
|
||||
if (selectedFeatures.length > 0 && selectedFeatures[0].branchName) {
|
||||
return 'custom';
|
||||
}
|
||||
return 'current';
|
||||
});
|
||||
const [branchName, setBranchName] = useState(() => {
|
||||
return getInitialValue(selectedFeatures, 'branchName', '') as string;
|
||||
});
|
||||
|
||||
// Calculate mixed values
|
||||
const mixedValues = useMemo(() => getMixedValues(selectedFeatures), [selectedFeatures]);
|
||||
|
||||
@@ -131,6 +158,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
requirePlanApproval: false,
|
||||
priority: false,
|
||||
skipTests: false,
|
||||
branchName: false,
|
||||
});
|
||||
setModel(getInitialValue(selectedFeatures, 'model', 'sonnet') as ModelAlias);
|
||||
setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel);
|
||||
@@ -138,6 +166,10 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false));
|
||||
setPriority(getInitialValue(selectedFeatures, 'priority', 2));
|
||||
setSkipTests(getInitialValue(selectedFeatures, 'skipTests', false));
|
||||
// Reset work mode and branch name
|
||||
const initialBranchName = getInitialValue(selectedFeatures, 'branchName', '') as string;
|
||||
setBranchName(initialBranchName);
|
||||
setWorkMode(initialBranchName ? 'custom' : 'current');
|
||||
}
|
||||
}, [open, selectedFeatures]);
|
||||
|
||||
@@ -150,6 +182,12 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
if (applyState.requirePlanApproval) updates.requirePlanApproval = requirePlanApproval;
|
||||
if (applyState.priority) updates.priority = priority;
|
||||
if (applyState.skipTests) updates.skipTests = skipTests;
|
||||
if (applyState.branchName) {
|
||||
// For 'current' mode, use empty string (work on current branch)
|
||||
// For 'auto' mode, use empty string (will be auto-generated)
|
||||
// For 'custom' mode, use the specified branch name
|
||||
updates.branchName = workMode === 'custom' ? branchName : '';
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
onClose();
|
||||
@@ -158,7 +196,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
|
||||
setIsApplying(true);
|
||||
try {
|
||||
await onApply(updates);
|
||||
await onApply(updates, workMode);
|
||||
onClose();
|
||||
} finally {
|
||||
setIsApplying(false);
|
||||
@@ -293,6 +331,25 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
|
||||
testIdPrefix="mass-edit"
|
||||
/>
|
||||
</FieldWrapper>
|
||||
|
||||
{/* Branch / Work Mode */}
|
||||
<FieldWrapper
|
||||
label="Branch / Work Mode"
|
||||
isMixed={mixedValues.branchName}
|
||||
willApply={applyState.branchName}
|
||||
onApplyChange={(apply) => setApplyState((prev) => ({ ...prev, branchName: apply }))}
|
||||
>
|
||||
<WorkModeSelector
|
||||
workMode={workMode}
|
||||
onWorkModeChange={setWorkMode}
|
||||
branchName={branchName}
|
||||
onBranchNameChange={setBranchName}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentBranch}
|
||||
testIdPrefix="mass-edit-work-mode"
|
||||
/>
|
||||
</FieldWrapper>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function PlanSettingsDialog({
|
||||
className="text-sm font-medium cursor-pointer flex items-center gap-2"
|
||||
>
|
||||
<GitBranch className="w-4 h-4 text-brand-500" />
|
||||
Use selected worktree branch
|
||||
Default to worktree mode
|
||||
</Label>
|
||||
<Switch
|
||||
id="plan-worktree-branch-toggle"
|
||||
@@ -55,8 +55,8 @@ export function PlanSettingsDialog({
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||
When enabled, features created via the Plan dialog will be assigned to the currently
|
||||
selected worktree branch. When disabled, features will be added to the main branch.
|
||||
Planned features will automatically use isolated worktrees, keeping changes separate
|
||||
from your main branch until you're ready to merge.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function WorktreeSettingsDialog({
|
||||
className="text-sm font-medium cursor-pointer flex items-center gap-2"
|
||||
>
|
||||
<GitBranch className="w-4 h-4 text-brand-500" />
|
||||
Use selected worktree branch
|
||||
Default to worktree mode
|
||||
</Label>
|
||||
<Switch
|
||||
id="worktree-branch-toggle"
|
||||
@@ -55,8 +55,8 @@ export function WorktreeSettingsDialog({
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||
When enabled, the Add Feature dialog will default to custom branch mode with the
|
||||
currently selected worktree branch pre-filled.
|
||||
New features will automatically use isolated worktrees, keeping changes separate
|
||||
from your main branch until you're ready to merge.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Brain, AlertTriangle } from 'lucide-react';
|
||||
import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { ModelAlias } from '@/store/app-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types';
|
||||
@@ -18,10 +19,6 @@ interface ModelSelectorProps {
|
||||
testIdPrefix?: string;
|
||||
}
|
||||
|
||||
const CODEX_EMPTY_AVAILABLE_MESSAGE = 'No Codex models available';
|
||||
const CODEX_EMPTY_ENABLED_MESSAGE =
|
||||
'No Codex models enabled. Enable models in Settings → AI Providers.';
|
||||
|
||||
export function ModelSelector({
|
||||
selectedModel,
|
||||
onModelSelect,
|
||||
@@ -30,8 +27,6 @@ export function ModelSelector({
|
||||
const {
|
||||
enabledCursorModels,
|
||||
cursorDefaultModel,
|
||||
enabledCodexModels,
|
||||
codexDefaultModel,
|
||||
codexModels,
|
||||
codexModelsLoading,
|
||||
codexModelsError,
|
||||
@@ -54,10 +49,8 @@ export function ModelSelector({
|
||||
}
|
||||
}, [isCodexAvailable, codexModels.length, codexModelsLoading, fetchCodexModels]);
|
||||
|
||||
const enabledCodexModelIds = new Set(enabledCodexModels);
|
||||
|
||||
// Transform codex models from store to ModelOption format
|
||||
const codexModelOptions: ModelOption[] = codexModels.map((model) => {
|
||||
const dynamicCodexModels: ModelOption[] = codexModels.map((model) => {
|
||||
// Infer badge based on tier
|
||||
let badge: string | undefined;
|
||||
if (model.tier === 'premium') badge = 'Premium';
|
||||
@@ -74,10 +67,6 @@ export function ModelSelector({
|
||||
};
|
||||
});
|
||||
|
||||
const enabledCodexModelOptions = codexModelOptions.filter((model) =>
|
||||
enabledCodexModelIds.has(model.id)
|
||||
);
|
||||
|
||||
// Filter Cursor models based on enabled models from global settings
|
||||
const filteredCursorModels = CURSOR_MODELS.filter((model) => {
|
||||
// Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto")
|
||||
@@ -85,36 +74,21 @@ export function ModelSelector({
|
||||
return enabledCursorModels.includes(cursorModelId as any);
|
||||
});
|
||||
|
||||
const hasEnabledCodexModels = enabledCodexModelOptions.length > 0;
|
||||
const codexDefaultSelection =
|
||||
codexModelOptions.find((model) => model.id === codexDefaultModel)?.id ||
|
||||
enabledCodexModelOptions[0]?.id ||
|
||||
codexModelOptions[0]?.id;
|
||||
|
||||
const handleProviderChange = (provider: ModelProvider) => {
|
||||
if (provider === 'cursor' && selectedProvider !== 'cursor') {
|
||||
// Switch to Cursor's default model (from global settings)
|
||||
onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`);
|
||||
} else if (provider === 'codex' && selectedProvider !== 'codex') {
|
||||
// Switch to Codex's default model (from global settings)
|
||||
if (codexDefaultSelection) {
|
||||
onModelSelect(codexDefaultSelection);
|
||||
}
|
||||
// Switch to Codex's default model (use isDefault flag from dynamic models)
|
||||
const defaultModel = codexModels.find((m) => m.isDefault);
|
||||
const defaultModelId = defaultModel?.id || codexModels[0]?.id || 'codex-gpt-5.2-codex';
|
||||
onModelSelect(defaultModelId);
|
||||
} else if (provider === 'claude' && selectedProvider !== 'claude') {
|
||||
// Switch to Claude's default model
|
||||
onModelSelect('sonnet');
|
||||
}
|
||||
};
|
||||
|
||||
const showCodexAvailableEmpty =
|
||||
!codexModelsLoading && !codexModelsError && codexModelOptions.length === 0;
|
||||
const showCodexEnabledEmpty =
|
||||
!codexModelsLoading &&
|
||||
!codexModelsError &&
|
||||
codexModelOptions.length > 0 &&
|
||||
!hasEnabledCodexModels;
|
||||
const showCodexList = !codexModelsLoading && !codexModelsError && hasEnabledCodexModels;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Provider Selection */}
|
||||
@@ -298,7 +272,7 @@ export function ModelSelector({
|
||||
</div>
|
||||
|
||||
{/* Loading state */}
|
||||
{codexModelsLoading && codexModelOptions.length === 0 && (
|
||||
{codexModelsLoading && dynamicCodexModels.length === 0 && (
|
||||
<div className="flex items-center justify-center gap-2 p-6 text-sm text-muted-foreground">
|
||||
<RefreshCw className="w-4 h-4 animate-spin" />
|
||||
Loading models...
|
||||
@@ -323,21 +297,15 @@ export function ModelSelector({
|
||||
)}
|
||||
|
||||
{/* Model list */}
|
||||
{showCodexAvailableEmpty && (
|
||||
{!codexModelsLoading && !codexModelsError && dynamicCodexModels.length === 0 && (
|
||||
<div className="text-sm text-muted-foreground p-3 border border-dashed rounded-md text-center">
|
||||
{CODEX_EMPTY_AVAILABLE_MESSAGE}
|
||||
No Codex models available
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showCodexEnabledEmpty && (
|
||||
<div className="text-sm text-muted-foreground p-3 border border-dashed rounded-md text-center">
|
||||
{CODEX_EMPTY_ENABLED_MESSAGE}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showCodexList && (
|
||||
{!codexModelsLoading && dynamicCodexModels.length > 0 && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{enabledCodexModelOptions.map((option) => {
|
||||
{dynamicCodexModels.map((option) => {
|
||||
const isSelected = selectedModel === option.id;
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -143,8 +143,12 @@ export function WorktreeActionsDropdown({
|
||||
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||
Dev Server Running (:{devServerInfo?.port})
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => onOpenDevServerUrl(worktree)} className="text-xs">
|
||||
<Globe className="w-3.5 h-3.5 mr-2" />
|
||||
<DropdownMenuItem
|
||||
onClick={() => onOpenDevServerUrl(worktree)}
|
||||
className="text-xs"
|
||||
aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
|
||||
>
|
||||
<Globe className="w-3.5 h-3.5 mr-2" aria-hidden="true" />
|
||||
Open in Browser
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
@@ -320,7 +324,7 @@ export function WorktreeActionsDropdown({
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
window.open(worktree.pr!.url, '_blank');
|
||||
window.open(worktree.pr!.url, '_blank', 'noopener,noreferrer');
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
|
||||
@@ -298,20 +298,29 @@ export function WorktreeTab({
|
||||
)}
|
||||
|
||||
{isDevServerRunning && (
|
||||
<Button
|
||||
variant={isSelected ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className={cn(
|
||||
'h-7 w-7 p-0 rounded-none border-r-0',
|
||||
isSelected && 'bg-primary text-primary-foreground',
|
||||
!isSelected && 'bg-secondary/50 hover:bg-secondary',
|
||||
'text-green-500'
|
||||
)}
|
||||
onClick={() => onOpenDevServerUrl(worktree)}
|
||||
title={`Open dev server (port ${devServerInfo?.port})`}
|
||||
>
|
||||
<Globe className="w-3 h-3" />
|
||||
</Button>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={isSelected ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className={cn(
|
||||
'h-7 w-7 p-0 rounded-none border-r-0',
|
||||
isSelected && 'bg-primary text-primary-foreground',
|
||||
!isSelected && 'bg-secondary/50 hover:bg-secondary',
|
||||
'text-green-500'
|
||||
)}
|
||||
onClick={() => onOpenDevServerUrl(worktree)}
|
||||
aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
|
||||
>
|
||||
<Globe className="w-3 h-3" aria-hidden="true" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Open dev server (:{devServerInfo?.port})</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<WorktreeActionsDropdown
|
||||
|
||||
@@ -118,8 +118,37 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
||||
const handleOpenDevServerUrl = useCallback(
|
||||
(worktree: WorktreeInfo) => {
|
||||
const serverInfo = runningDevServers.get(getWorktreeKey(worktree));
|
||||
if (serverInfo) {
|
||||
window.open(serverInfo.url, '_blank');
|
||||
if (!serverInfo) {
|
||||
logger.warn('No dev server info found for worktree:', getWorktreeKey(worktree));
|
||||
toast.error('Dev server not found', {
|
||||
description: 'The dev server may have stopped. Try starting it again.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Rewrite URL hostname to match the current browser's hostname.
|
||||
// This ensures dev server URLs work when accessing Automaker from
|
||||
// remote machines (e.g., 192.168.x.x or hostname.local instead of localhost).
|
||||
const devServerUrl = new URL(serverInfo.url);
|
||||
|
||||
// Security: Only allow http/https protocols to prevent potential attacks
|
||||
// via data:, javascript:, file:, or other dangerous URL schemes
|
||||
if (devServerUrl.protocol !== 'http:' && devServerUrl.protocol !== 'https:') {
|
||||
logger.error('Invalid dev server URL protocol:', devServerUrl.protocol);
|
||||
toast.error('Invalid dev server URL', {
|
||||
description: 'The server returned an unsupported URL protocol.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
devServerUrl.hostname = window.location.hostname;
|
||||
window.open(devServerUrl.toString(), '_blank', 'noopener,noreferrer');
|
||||
} catch (error) {
|
||||
logger.error('Failed to parse dev server URL:', error);
|
||||
toast.error('Failed to open dev server', {
|
||||
description: 'The server URL could not be processed. Please try again.',
|
||||
});
|
||||
}
|
||||
},
|
||||
[runningDevServers, getWorktreeKey]
|
||||
|
||||
@@ -48,7 +48,7 @@ export function AddEditServerDialog({
|
||||
Configure an MCP server to extend agent capabilities with custom tools.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-4 py-4 overflow-y-auto">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="server-name">Name</Label>
|
||||
<Input
|
||||
|
||||
@@ -159,9 +159,6 @@ export function PhaseModelSelector({
|
||||
const expandedCodexTriggerRef = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
enabledCursorModels,
|
||||
enabledCodexModels,
|
||||
enabledOpencodeModels,
|
||||
enabledDynamicModelIds,
|
||||
favoriteModels,
|
||||
toggleFavoriteModel,
|
||||
codexModels,
|
||||
@@ -264,14 +261,6 @@ export function PhaseModelSelector({
|
||||
}));
|
||||
}, [codexModels]);
|
||||
|
||||
const availableCodexModels = useMemo(
|
||||
() =>
|
||||
transformedCodexModels.filter((model) =>
|
||||
enabledCodexModels.includes(model.id as CodexModelId)
|
||||
),
|
||||
[transformedCodexModels, enabledCodexModels]
|
||||
);
|
||||
|
||||
// Filter Cursor models to only show enabled ones
|
||||
const availableCursorModels = CURSOR_MODELS.filter((model) => {
|
||||
const cursorId = stripProviderPrefix(model.id) as CursorModelId;
|
||||
@@ -377,20 +366,16 @@ export function PhaseModelSelector({
|
||||
// Combine static and dynamic OpenCode models
|
||||
const allOpencodeModels: ModelOption[] = useMemo(() => {
|
||||
// Start with static models
|
||||
const staticModels = OPENCODE_MODELS.filter((model) =>
|
||||
enabledOpencodeModels.includes(model.id)
|
||||
);
|
||||
const staticModels = [...OPENCODE_MODELS];
|
||||
|
||||
// Add dynamic models (convert ModelDefinition to ModelOption)
|
||||
const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels
|
||||
.filter((model) => enabledDynamicModelIds.includes(model.id))
|
||||
.map((model) => ({
|
||||
id: model.id,
|
||||
label: model.name,
|
||||
description: model.description,
|
||||
badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined,
|
||||
provider: 'opencode' as const,
|
||||
}));
|
||||
const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels.map((model) => ({
|
||||
id: model.id,
|
||||
label: model.name,
|
||||
description: model.description,
|
||||
badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined,
|
||||
provider: 'opencode' as const,
|
||||
}));
|
||||
|
||||
// Merge, avoiding duplicates (static models take precedence for same ID)
|
||||
// In practice, static and dynamic IDs don't overlap
|
||||
@@ -398,14 +383,14 @@ export function PhaseModelSelector({
|
||||
const uniqueDynamic = dynamicModelOptions.filter((m) => !staticIds.has(m.id));
|
||||
|
||||
return [...staticModels, ...uniqueDynamic];
|
||||
}, [dynamicOpencodeModels, enabledOpencodeModels, enabledDynamicModelIds]);
|
||||
}, [dynamicOpencodeModels]);
|
||||
|
||||
// Group models
|
||||
const { favorites, claude, cursor, codex, opencode } = useMemo(() => {
|
||||
const favs: typeof CLAUDE_MODELS = [];
|
||||
const cModels: typeof CLAUDE_MODELS = [];
|
||||
const curModels: typeof CURSOR_MODELS = [];
|
||||
const codModels: typeof availableCodexModels = [];
|
||||
const codModels: typeof transformedCodexModels = [];
|
||||
const ocModels: ModelOption[] = [];
|
||||
|
||||
// Process Claude Models
|
||||
@@ -427,7 +412,7 @@ export function PhaseModelSelector({
|
||||
});
|
||||
|
||||
// Process Codex Models
|
||||
availableCodexModels.forEach((model) => {
|
||||
transformedCodexModels.forEach((model) => {
|
||||
if (favoriteModels.includes(model.id)) {
|
||||
favs.push(model);
|
||||
} else {
|
||||
@@ -451,7 +436,7 @@ export function PhaseModelSelector({
|
||||
codex: codModels,
|
||||
opencode: ocModels,
|
||||
};
|
||||
}, [favoriteModels, availableCursorModels, availableCodexModels, allOpencodeModels]);
|
||||
}, [favoriteModels, availableCursorModels, transformedCodexModels, allOpencodeModels]);
|
||||
|
||||
// Group OpenCode models by model type for better organization
|
||||
const opencodeSections = useMemo(() => {
|
||||
@@ -468,11 +453,8 @@ export function PhaseModelSelector({
|
||||
free: {},
|
||||
dynamic: {},
|
||||
};
|
||||
const enabledDynamicProviders = dynamicOpencodeModels.filter((model) =>
|
||||
enabledDynamicModelIds.includes(model.id)
|
||||
);
|
||||
const dynamicProviderById = new Map(
|
||||
enabledDynamicProviders.map((model) => [model.id, model.provider])
|
||||
dynamicOpencodeModels.map((model) => [model.id, model.provider])
|
||||
);
|
||||
|
||||
const resolveProviderKey = (modelId: string): string => {
|
||||
@@ -542,10 +524,10 @@ export function PhaseModelSelector({
|
||||
}).filter(Boolean) as OpencodeSection[];
|
||||
|
||||
return builtSections;
|
||||
}, [opencode, dynamicOpencodeModels, enabledDynamicModelIds]);
|
||||
}, [opencode, dynamicOpencodeModels]);
|
||||
|
||||
// Render Codex model item with secondary popover for reasoning effort (only for models that support it)
|
||||
const renderCodexModelItem = (model: (typeof availableCodexModels)[0]) => {
|
||||
const renderCodexModelItem = (model: (typeof transformedCodexModels)[0]) => {
|
||||
const isSelected = selectedModel === model.id;
|
||||
const isFavorite = favoriteModels.includes(model.id);
|
||||
const hasReasoning = codexModelHasThinking(model.id as CodexModelId);
|
||||
@@ -726,7 +708,7 @@ export function PhaseModelSelector({
|
||||
};
|
||||
|
||||
// Render OpenCode model item (simple selector, no thinking/reasoning options)
|
||||
const renderOpencodeModelItem = (model: ModelOption) => {
|
||||
const renderOpencodeModelItem = (model: (typeof OPENCODE_MODELS)[0]) => {
|
||||
const isSelected = selectedModel === model.id;
|
||||
const isFavorite = favoriteModels.includes(model.id);
|
||||
|
||||
@@ -1172,7 +1154,7 @@ export function PhaseModelSelector({
|
||||
}
|
||||
// Codex model
|
||||
if (model.provider === 'codex') {
|
||||
return renderCodexModelItem(model as (typeof availableCodexModels)[0]);
|
||||
return renderCodexModelItem(model as (typeof transformedCodexModels)[0]);
|
||||
}
|
||||
// OpenCode model
|
||||
if (model.provider === 'opencode') {
|
||||
|
||||
@@ -50,7 +50,7 @@ export function CreateSpecDialog({
|
||||
<DialogDescription className="text-muted-foreground">{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-4 py-4 overflow-y-auto">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Project Overview</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -51,7 +51,7 @@ export function RegenerateSpecDialog({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-4 py-4 overflow-y-auto">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Describe your project</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -136,12 +136,11 @@ volumes:
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Container won't start | Check `.env` has `ANTHROPIC_API_KEY` set. Run `docker-compose logs` for errors. |
|
||||
| Can't access web UI | Verify container is running with `docker ps \| grep automaker` |
|
||||
| Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` |
|
||||
| Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. |
|
||||
| OpenCode not detected | Mount `~/.local/share/opencode` to `/home/automaker/.local/share/opencode`. Make sure you've run `opencode auth login` on your host first. |
|
||||
| Problem | Solution |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Container won't start | Check `.env` has `ANTHROPIC_API_KEY` set. Run `docker-compose logs` for errors. |
|
||||
| Can't access web UI | Verify container is running with `docker ps \| grep automaker` |
|
||||
| Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` |
|
||||
| Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. |
|
||||
| OpenCode not detected | Mount `~/.local/share/opencode` to `/home/automaker/.local/share/opencode`. Make sure you've run `opencode auth login` on your host first. |
|
||||
| File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). |
|
||||
|
||||
|
||||
91
package-lock.json
generated
91
package-lock.json
generated
@@ -679,6 +679,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -1271,6 +1272,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
|
||||
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
@@ -1313,6 +1315,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -2133,7 +2136,6 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
@@ -2155,7 +2157,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
@@ -2172,7 +2173,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
@@ -2187,7 +2187,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
@@ -2955,7 +2954,6 @@
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -3080,7 +3078,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3097,7 +3094,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3114,7 +3110,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3223,7 +3218,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3246,7 +3240,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3269,7 +3262,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3355,7 +3347,6 @@
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
@@ -3378,7 +3369,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3398,7 +3388,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3798,8 +3787,7 @@
|
||||
"version": "16.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
|
||||
"integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.0.10",
|
||||
@@ -3813,7 +3801,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3830,7 +3817,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3847,7 +3833,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3864,7 +3849,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3881,7 +3865,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3898,7 +3881,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3915,7 +3897,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3932,7 +3913,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -4032,6 +4012,7 @@
|
||||
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.57.0"
|
||||
},
|
||||
@@ -5472,7 +5453,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -5806,6 +5786,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz",
|
||||
"integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.141.0",
|
||||
"@tanstack/react-store": "^0.8.0",
|
||||
@@ -6232,6 +6213,7 @@
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
@@ -6374,6 +6356,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -6384,6 +6367,7 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -6489,6 +6473,7 @@
|
||||
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.50.0",
|
||||
"@typescript-eslint/types": "8.50.0",
|
||||
@@ -6982,7 +6967,8 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@xyflow/react": {
|
||||
"version": "12.10.0",
|
||||
@@ -7080,6 +7066,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7140,6 +7127,7 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -7738,6 +7726,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -8269,8 +8258,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
@@ -8575,8 +8563,7 @@
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "10.1.0",
|
||||
@@ -8673,6 +8660,7 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -8974,6 +8962,7 @@
|
||||
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.0.12",
|
||||
"builder-util": "26.0.11",
|
||||
@@ -9300,7 +9289,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
@@ -9321,7 +9309,6 @@
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -9572,6 +9559,7 @@
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -9886,6 +9874,7 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -11553,7 +11542,6 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11619,7 +11607,6 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -14061,7 +14048,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
@@ -14078,7 +14064,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
@@ -14096,7 +14081,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
@@ -14285,6 +14269,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -14294,6 +14279,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -14652,7 +14638,6 @@
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -14841,6 +14826,7 @@
|
||||
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
|
||||
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -14889,7 +14875,6 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
@@ -14940,7 +14925,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -14963,7 +14947,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -14986,7 +14969,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15003,7 +14985,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15020,7 +15001,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15037,7 +15017,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15054,7 +15033,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15071,7 +15049,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15088,7 +15065,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15105,7 +15081,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15128,7 +15103,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15151,7 +15125,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15174,7 +15147,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15197,7 +15169,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15220,7 +15191,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15689,7 +15659,6 @@
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
||||
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"client-only": "0.0.1"
|
||||
},
|
||||
@@ -15859,7 +15828,6 @@
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
@@ -15923,7 +15891,6 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -16021,6 +15988,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16225,6 +16193,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16596,6 +16565,7 @@
|
||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -16685,7 +16655,8 @@
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
|
||||
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
@@ -16711,6 +16682,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16753,6 +16725,7 @@
|
||||
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.0.16",
|
||||
"@vitest/mocker": "4.0.16",
|
||||
@@ -17010,6 +16983,7 @@
|
||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
@@ -17078,6 +17052,7 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
|
||||
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user