mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 596fcec56c |
@@ -84,10 +84,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m pip index versions openhands-ai > openhands_versions.txt
|
python -m pip index versions openhands-ai > openhands_versions.txt
|
||||||
OPENHANDS_VERSION=$(head -n 1 openhands_versions.txt | awk '{print $2}' | tr -d '()')
|
OPENHANDS_VERSION=$(head -n 1 openhands_versions.txt | awk '{print $2}' | tr -d '()')
|
||||||
# Ensure requirements.txt ends with newline before appending
|
|
||||||
if [ -f requirements.txt ] && [ -s requirements.txt ]; then
|
|
||||||
sed -i -e '$a\' requirements.txt
|
|
||||||
fi
|
|
||||||
echo "openhands-ai==${OPENHANDS_VERSION}" >> requirements.txt
|
echo "openhands-ai==${OPENHANDS_VERSION}" >> requirements.txt
|
||||||
cat requirements.txt
|
cat requirements.txt
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ evaluation/gorilla/data
|
|||||||
evaluation/toolqa/data
|
evaluation/toolqa/data
|
||||||
evaluation/scienceagentbench/benchmark
|
evaluation/scienceagentbench/benchmark
|
||||||
evaluation/commit0_bench/repos
|
evaluation/commit0_bench/repos
|
||||||
evaluation/visualcodebench/
|
|
||||||
|
|
||||||
# openhands resolver
|
# openhands resolver
|
||||||
output/
|
output/
|
||||||
|
|||||||
@@ -1,674 +0,0 @@
|
|||||||
from collections import Counter
|
|
||||||
from copy import deepcopy
|
|
||||||
from difflib import SequenceMatcher
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from bs4 import BeautifulSoup, Comment, NavigableString, Tag
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
import torch
|
|
||||||
from colormath.color_conversions import convert_color
|
|
||||||
from colormath.color_diff import delta_e_cie2000
|
|
||||||
from colormath.color_objects import LabColor, sRGBColor
|
|
||||||
from PIL import Image, ImageChops, ImageColor
|
|
||||||
from scipy.optimize import linear_sum_assignment
|
|
||||||
from transformers import CLIPModel, CLIPProcessor
|
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_similarity(block1, block2):
|
|
||||||
"""Calculate text similarity between two blocks using SequenceMatcher."""
|
|
||||||
text_similarity = SequenceMatcher(None, block1['text'], block2['text']).ratio()
|
|
||||||
return text_similarity
|
|
||||||
|
|
||||||
|
|
||||||
def adjust_cost_for_context(cost_matrix, consecutive_bonus=1.0, window_size=20):
|
|
||||||
"""Adjust cost matrix by considering context similarity."""
|
|
||||||
if window_size <= 0:
|
|
||||||
return cost_matrix
|
|
||||||
|
|
||||||
n, m = cost_matrix.shape
|
|
||||||
adjusted_cost_matrix = np.copy(cost_matrix)
|
|
||||||
|
|
||||||
for i in range(n):
|
|
||||||
for j in range(m):
|
|
||||||
if adjusted_cost_matrix[i][j] >= -0.5:
|
|
||||||
continue
|
|
||||||
nearby_matrix = cost_matrix[
|
|
||||||
max(0, i - window_size) : min(n, i + window_size + 1),
|
|
||||||
max(0, j - window_size) : min(m, j + window_size + 1),
|
|
||||||
]
|
|
||||||
flattened_array = nearby_matrix.flatten()
|
|
||||||
sorted_array = np.sort(flattened_array)[::-1]
|
|
||||||
sorted_array = np.delete(
|
|
||||||
sorted_array, np.where(sorted_array == cost_matrix[i, j])[0][0]
|
|
||||||
)
|
|
||||||
top_k_elements = sorted_array[-window_size * 2 :]
|
|
||||||
bonus = consecutive_bonus * np.sum(top_k_elements)
|
|
||||||
adjusted_cost_matrix[i][j] += bonus
|
|
||||||
return adjusted_cost_matrix
|
|
||||||
|
|
||||||
|
|
||||||
def create_cost_matrix(A, B):
|
|
||||||
"""Create cost matrix for block matching."""
|
|
||||||
n = len(A)
|
|
||||||
m = len(B)
|
|
||||||
cost_matrix = np.zeros((n, m))
|
|
||||||
for i in range(n):
|
|
||||||
for j in range(m):
|
|
||||||
cost_matrix[i, j] = -calculate_similarity(A[i], B[j])
|
|
||||||
return cost_matrix
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_distance_max_1d(x1, y1, x2, y2):
|
|
||||||
"""Calculate maximum 1D distance between points."""
|
|
||||||
return max(abs(x2 - x1), abs(y2 - y1))
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_ratio(h1, h2):
|
|
||||||
"""Calculate ratio between two heights."""
|
|
||||||
return max(h1, h2) / min(h1, h2)
|
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_lab(rgb):
|
|
||||||
"""Convert RGB color to Lab color space."""
|
|
||||||
rgb_color = sRGBColor(rgb[0], rgb[1], rgb[2], is_upscaled=True)
|
|
||||||
lab_color = convert_color(rgb_color, LabColor)
|
|
||||||
return lab_color
|
|
||||||
|
|
||||||
|
|
||||||
def color_similarity_ciede2000(rgb1, rgb2):
|
|
||||||
"""Calculate color similarity using CIEDE2000 formula."""
|
|
||||||
lab1 = rgb_to_lab(rgb1)
|
|
||||||
lab2 = rgb_to_lab(rgb2)
|
|
||||||
delta_e = delta_e_cie2000(lab1, lab2)
|
|
||||||
similarity = max(0, 1 - (delta_e / 100))
|
|
||||||
return similarity
|
|
||||||
|
|
||||||
|
|
||||||
def merge_blocks_wo_check(block1, block2):
|
|
||||||
"""Merge two blocks without additional checks."""
|
|
||||||
merged_text = block1['text'] + ' ' + block2['text']
|
|
||||||
x_min = min(block1['bbox'][0], block2['bbox'][0])
|
|
||||||
y_min = min(block1['bbox'][1], block2['bbox'][1])
|
|
||||||
x_max = max(
|
|
||||||
block1['bbox'][0] + block1['bbox'][2], block2['bbox'][0] + block2['bbox'][2]
|
|
||||||
)
|
|
||||||
y_max = max(
|
|
||||||
block1['bbox'][1] + block1['bbox'][3], block2['bbox'][1] + block2['bbox'][3]
|
|
||||||
)
|
|
||||||
merged_bbox = (x_min, y_min, x_max - x_min, y_max - y_min)
|
|
||||||
merged_color = tuple(
|
|
||||||
(color1 + color2) // 2
|
|
||||||
for color1, color2 in zip(block1['color'], block2['color'])
|
|
||||||
)
|
|
||||||
return {'text': merged_text, 'bbox': merged_bbox, 'color': merged_color}
|
|
||||||
|
|
||||||
|
|
||||||
def find_maximum_matching(A, B, consecutive_bonus, window_size):
|
|
||||||
"""Find maximum matching between two sets of blocks."""
|
|
||||||
cost_matrix = create_cost_matrix(A, B)
|
|
||||||
cost_matrix = adjust_cost_for_context(cost_matrix, consecutive_bonus, window_size)
|
|
||||||
row_ind, col_ind = linear_sum_assignment(cost_matrix)
|
|
||||||
current_cost = cost_matrix[row_ind, col_ind].tolist()
|
|
||||||
return list(zip(row_ind, col_ind)), current_cost, cost_matrix
|
|
||||||
|
|
||||||
|
|
||||||
def remove_indices(lst, indices):
|
|
||||||
"""Remove indices from list in reverse order."""
|
|
||||||
for index in sorted(indices, reverse=True):
|
|
||||||
if index < len(lst):
|
|
||||||
lst.pop(index)
|
|
||||||
return lst
|
|
||||||
|
|
||||||
|
|
||||||
def merge_blocks_by_list(blocks, merge_list):
|
|
||||||
"""Merge blocks according to merge list."""
|
|
||||||
pop_list = []
|
|
||||||
while merge_list:
|
|
||||||
i = merge_list[0][0]
|
|
||||||
j = merge_list[0][1]
|
|
||||||
blocks[i] = merge_blocks_wo_check(blocks[i], blocks[j])
|
|
||||||
pop_list.append(j)
|
|
||||||
merge_list.pop(0)
|
|
||||||
if merge_list:
|
|
||||||
new_merge_list = []
|
|
||||||
for k in range(len(merge_list)):
|
|
||||||
if (
|
|
||||||
merge_list[k][0] != i
|
|
||||||
and merge_list[k][1] != i
|
|
||||||
and merge_list[k][0] != j
|
|
||||||
and merge_list[k][1] != j
|
|
||||||
):
|
|
||||||
new_merge_list.append(merge_list[k])
|
|
||||||
merge_list = new_merge_list
|
|
||||||
remove_indices(blocks, pop_list)
|
|
||||||
return blocks
|
|
||||||
|
|
||||||
|
|
||||||
def difference_of_means(list1, list2):
|
|
||||||
"""Calculate difference of means between two lists."""
|
|
||||||
counter1 = Counter(list1)
|
|
||||||
counter2 = Counter(list2)
|
|
||||||
|
|
||||||
for element in set(list1) & set(list2):
|
|
||||||
common_count = min(counter1[element], counter2[element])
|
|
||||||
counter1[element] -= common_count
|
|
||||||
counter2[element] -= common_count
|
|
||||||
|
|
||||||
unique_list1 = [item for item in counter1.elements()]
|
|
||||||
unique_list2 = [item for item in counter2.elements()]
|
|
||||||
|
|
||||||
mean_list1 = sum(unique_list1) / len(unique_list1) if unique_list1 else 0
|
|
||||||
mean_list2 = sum(unique_list2) / len(unique_list2) if unique_list2 else 0
|
|
||||||
|
|
||||||
if mean_list1 - mean_list2 > 0:
|
|
||||||
if min(unique_list1) > min(unique_list2):
|
|
||||||
return mean_list1 - mean_list2
|
|
||||||
return 0.0
|
|
||||||
return mean_list1 - mean_list2
|
|
||||||
|
|
||||||
|
|
||||||
def find_possible_merge(A, B, consecutive_bonus, window_size, debug=False):
|
|
||||||
"""Find possible merges between blocks."""
|
|
||||||
merge_bonus = 0.0
|
|
||||||
merge_windows = 1
|
|
||||||
|
|
||||||
def sortFn(value):
|
|
||||||
return value[2]
|
|
||||||
|
|
||||||
while True:
|
|
||||||
A_changed = False
|
|
||||||
B_changed = False
|
|
||||||
|
|
||||||
matching, current_cost, cost_matrix = find_maximum_matching(
|
|
||||||
A, B, merge_bonus, merge_windows
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(A) >= 2:
|
|
||||||
merge_list = []
|
|
||||||
for i in range(len(A) - 1):
|
|
||||||
new_A = deepcopy(A)
|
|
||||||
new_A[i] = merge_blocks_wo_check(new_A[i], new_A[i + 1])
|
|
||||||
new_A.pop(i + 1)
|
|
||||||
updated_matching, updated_cost, _ = find_maximum_matching(
|
|
||||||
new_A, B, merge_bonus, merge_windows
|
|
||||||
)
|
|
||||||
diff = difference_of_means(current_cost, updated_cost)
|
|
||||||
if diff > 0.05:
|
|
||||||
merge_list.append([i, i + 1, diff])
|
|
||||||
|
|
||||||
merge_list.sort(key=sortFn, reverse=True)
|
|
||||||
if merge_list:
|
|
||||||
A_changed = True
|
|
||||||
A = merge_blocks_by_list(A, merge_list)
|
|
||||||
matching, current_cost, cost_matrix = find_maximum_matching(
|
|
||||||
A, B, merge_bonus, merge_windows
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(B) >= 2:
|
|
||||||
merge_list = []
|
|
||||||
for i in range(len(B) - 1):
|
|
||||||
new_B = deepcopy(B)
|
|
||||||
new_B[i] = merge_blocks_wo_check(new_B[i], new_B[i + 1])
|
|
||||||
new_B.pop(i + 1)
|
|
||||||
updated_matching, updated_cost, _ = find_maximum_matching(
|
|
||||||
A, new_B, merge_bonus, merge_windows
|
|
||||||
)
|
|
||||||
diff = difference_of_means(current_cost, updated_cost)
|
|
||||||
if diff > 0.05:
|
|
||||||
merge_list.append([i, i + 1, diff])
|
|
||||||
|
|
||||||
merge_list.sort(key=sortFn, reverse=True)
|
|
||||||
if merge_list:
|
|
||||||
B_changed = True
|
|
||||||
B = merge_blocks_by_list(B, merge_list)
|
|
||||||
matching, current_cost, cost_matrix = find_maximum_matching(
|
|
||||||
A, B, merge_bonus, merge_windows
|
|
||||||
)
|
|
||||||
|
|
||||||
if not A_changed and not B_changed:
|
|
||||||
break
|
|
||||||
|
|
||||||
matching, _, _ = find_maximum_matching(A, B, consecutive_bonus, window_size)
|
|
||||||
return A, B, matching
|
|
||||||
|
|
||||||
|
|
||||||
def merge_blocks_by_bbox(blocks):
|
|
||||||
"""Merge blocks with same bounding box."""
|
|
||||||
merged_blocks = {}
|
|
||||||
for block in blocks:
|
|
||||||
bbox = tuple(block['bbox'])
|
|
||||||
if bbox in merged_blocks:
|
|
||||||
existing_block = merged_blocks[bbox]
|
|
||||||
existing_block['text'] += ' ' + block['text']
|
|
||||||
existing_block['color'] = [
|
|
||||||
(ec + c) / 2 for ec, c in zip(existing_block['color'], block['color'])
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
merged_blocks[bbox] = block
|
|
||||||
return list(merged_blocks.values())
|
|
||||||
|
|
||||||
|
|
||||||
def mask_bounding_boxes_with_inpainting(image, bounding_boxes):
|
|
||||||
"""Mask bounding boxes in image using inpainting."""
|
|
||||||
image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
|
||||||
mask = np.zeros(image_cv.shape[:2], dtype=np.uint8)
|
|
||||||
height, width = image_cv.shape[:2]
|
|
||||||
|
|
||||||
for bbox in bounding_boxes:
|
|
||||||
x_ratio, y_ratio, w_ratio, h_ratio = bbox
|
|
||||||
x = int(x_ratio * width)
|
|
||||||
y = int(y_ratio * height)
|
|
||||||
w = int(w_ratio * width)
|
|
||||||
h = int(h_ratio * height)
|
|
||||||
mask[y : y + h, x : x + w] = 255
|
|
||||||
|
|
||||||
inpainted_image = cv2.inpaint(image_cv, mask, 3, cv2.INPAINT_TELEA)
|
|
||||||
return Image.fromarray(cv2.cvtColor(inpainted_image, cv2.COLOR_BGR2RGB))
|
|
||||||
|
|
||||||
|
|
||||||
def rescale_and_mask(image, blocks):
|
|
||||||
"""Rescale image and mask blocks."""
|
|
||||||
if blocks:
|
|
||||||
image = mask_bounding_boxes_with_inpainting(image, blocks)
|
|
||||||
|
|
||||||
width, height = image.size
|
|
||||||
if width < height:
|
|
||||||
new_size = (width, width)
|
|
||||||
else:
|
|
||||||
new_size = (height, height)
|
|
||||||
|
|
||||||
return image.resize(new_size, Image.LANCZOS)
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_clip_similarity(image1, image2, blocks1, blocks2):
|
|
||||||
"""Calculate CLIP similarity between two images."""
|
|
||||||
model = CLIPModel.from_pretrained('openai/clip-vit-base-patch32')
|
|
||||||
processor = CLIPProcessor.from_pretrained('openai/clip-vit-base-patch32')
|
|
||||||
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
||||||
model = model.to(device)
|
|
||||||
|
|
||||||
# Mask and preprocess images
|
|
||||||
image1_masked = rescale_and_mask(image1, [block['bbox'] for block in blocks1])
|
|
||||||
image2_masked = rescale_and_mask(image2, [block['bbox'] for block in blocks2])
|
|
||||||
inputs = processor(
|
|
||||||
images=[image1_masked, image2_masked], return_tensors='pt', padding=True
|
|
||||||
)
|
|
||||||
inputs = {k: v.to(device) for k, v in inputs.items()}
|
|
||||||
|
|
||||||
# Calculate features and similarity
|
|
||||||
with torch.no_grad():
|
|
||||||
image_features = model.get_image_features(**inputs)
|
|
||||||
image_features1 = image_features[0].unsqueeze(0)
|
|
||||||
image_features2 = image_features[1].unsqueeze(0)
|
|
||||||
image_features1 /= image_features1.norm(dim=-1, keepdim=True)
|
|
||||||
image_features2 /= image_features2.norm(dim=-1, keepdim=True)
|
|
||||||
similarity = (image_features1 @ image_features2.T).item()
|
|
||||||
|
|
||||||
return similarity
|
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_hex(rgb):
|
|
||||||
"""Convert an RGB tuple to hexadecimal format."""
|
|
||||||
return '{:02X}{:02X}{:02X}'.format(*rgb)
|
|
||||||
|
|
||||||
|
|
||||||
class ColorPool:
|
|
||||||
def __init__(self, offset=0):
|
|
||||||
color_values = list(range(10, 251, 16))
|
|
||||||
color_list = [((r + offset) % 256, (g + offset) % 256, (b + offset) % 256)
|
|
||||||
for r in color_values for g in color_values for b in color_values]
|
|
||||||
self.color_pool = [rgb_to_hex(color) for color in color_list]
|
|
||||||
|
|
||||||
def pop_color(self):
|
|
||||||
if self.color_pool:
|
|
||||||
return self.color_pool.pop()
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
def process_html_str(html_str, offset=0):
|
|
||||||
"""Process HTML string to assign unique colors to text elements."""
|
|
||||||
soup = BeautifulSoup(html_str, 'html.parser')
|
|
||||||
|
|
||||||
def update_style(element, property_name, value):
|
|
||||||
important_value = f"{value} !important"
|
|
||||||
styles = element.attrs.get('style', '').split(';')
|
|
||||||
updated_styles = [s for s in styles if not s.strip().startswith(property_name) and len(s.strip()) > 0]
|
|
||||||
updated_styles.append(f"{property_name}: {important_value}")
|
|
||||||
element['style'] = '; '.join(updated_styles).strip()
|
|
||||||
|
|
||||||
# Set background color of all elements to transparent white
|
|
||||||
for element in soup.find_all(True):
|
|
||||||
update_style(element, 'background-color', 'rgba(255, 255, 255, 0.0)')
|
|
||||||
|
|
||||||
color_pool = ColorPool(offset)
|
|
||||||
text_tags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span', 'a', 'b', 'li',
|
|
||||||
'table', 'td', 'th', 'button', 'footer', 'header', 'figcaption']
|
|
||||||
|
|
||||||
for tag in soup.find_all(text_tags):
|
|
||||||
color = f"#{color_pool.pop_color()}"
|
|
||||||
update_style(tag, 'color', color)
|
|
||||||
update_style(tag, 'opacity', '1.0')
|
|
||||||
|
|
||||||
return str(soup)
|
|
||||||
|
|
||||||
|
|
||||||
def similar(n1, n2):
|
|
||||||
"""Check if two numbers are similar within a threshold."""
|
|
||||||
return abs(n1 - n2) <= 8
|
|
||||||
|
|
||||||
|
|
||||||
def find_different_pixels(image1, image2):
|
|
||||||
"""Find pixels that differ between two images."""
|
|
||||||
if image1.size != image2.size:
|
|
||||||
logger.warning("Images are not the same size")
|
|
||||||
return None
|
|
||||||
|
|
||||||
image1 = image1.convert('RGB')
|
|
||||||
image2 = image2.convert('RGB')
|
|
||||||
pixels1 = image1.load()
|
|
||||||
pixels2 = image2.load()
|
|
||||||
different_pixels = []
|
|
||||||
|
|
||||||
for x in range(image1.size[0]):
|
|
||||||
for y in range(image1.size[1]):
|
|
||||||
r1, g1, b1 = pixels1[x, y]
|
|
||||||
r2, g2, b2 = pixels2[x, y]
|
|
||||||
if similar((r1 + 50) % 256, r2) and similar((g1 + 50) % 256, g2) and similar((b1 + 50) % 256, b2):
|
|
||||||
different_pixels.append((y, x))
|
|
||||||
|
|
||||||
return np.stack(different_pixels) if different_pixels else None
|
|
||||||
|
|
||||||
|
|
||||||
def extract_text_with_color(html_str):
|
|
||||||
"""Extract text and color information from HTML string."""
|
|
||||||
def get_color(tag):
|
|
||||||
if 'style' in tag.attrs:
|
|
||||||
styles = tag['style'].split(';')
|
|
||||||
color_style = [s for s in styles if 'color' in s and 'background-color' not in s]
|
|
||||||
if color_style:
|
|
||||||
color = color_style[-1].split(':')[1].strip().replace(" !important", "")
|
|
||||||
if color[0] == "#":
|
|
||||||
return color
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if color.startswith('rgb'):
|
|
||||||
color = tuple(map(int, color[4:-1].split(',')))
|
|
||||||
else:
|
|
||||||
color = ImageColor.getrgb(color)
|
|
||||||
return '#{:02x}{:02x}{:02x}'.format(*color)
|
|
||||||
except ValueError:
|
|
||||||
logger.warning(f"Unable to identify or convert color: {color}")
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def extract_text_recursive(element, parent_color='#000000'):
|
|
||||||
if isinstance(element, Comment):
|
|
||||||
return None
|
|
||||||
elif isinstance(element, NavigableString):
|
|
||||||
text = element.strip()
|
|
||||||
return (text, parent_color) if text else None
|
|
||||||
elif isinstance(element, Tag):
|
|
||||||
current_color = get_color(element) or parent_color
|
|
||||||
children_texts = filter(None, [extract_text_recursive(child, current_color)
|
|
||||||
for child in element.children])
|
|
||||||
return list(children_texts)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(html_str, 'html.parser')
|
|
||||||
body = soup.body
|
|
||||||
return extract_text_recursive(body) if body else []
|
|
||||||
|
|
||||||
|
|
||||||
def flatten_tree(tree):
|
|
||||||
"""Flatten a nested tree structure into a list."""
|
|
||||||
flat_list = []
|
|
||||||
def flatten(node):
|
|
||||||
if isinstance(node, list):
|
|
||||||
for item in node:
|
|
||||||
flatten(item)
|
|
||||||
else:
|
|
||||||
flat_list.append(node)
|
|
||||||
flatten(tree)
|
|
||||||
return flat_list
|
|
||||||
|
|
||||||
|
|
||||||
def get_blocks_from_image_diff_pixels(image, html_text_color_tree, different_pixels):
|
|
||||||
"""Extract text blocks from image using color differences."""
|
|
||||||
image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
|
||||||
x_w = image_cv.shape[0]
|
|
||||||
y_w = image_cv.shape[1]
|
|
||||||
|
|
||||||
def hex_to_bgr(hex_color):
|
|
||||||
hex_color = hex_color.lstrip('#')
|
|
||||||
rgb = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
|
||||||
return rgb[::-1]
|
|
||||||
|
|
||||||
def get_intersect(arr1, arr2):
|
|
||||||
arr1_reshaped = arr1.view([('', arr1.dtype)] * arr1.shape[1])
|
|
||||||
arr2_reshaped = arr2.view([('', arr2.dtype)] * arr2.shape[1])
|
|
||||||
common_rows = np.intersect1d(arr1_reshaped, arr2_reshaped)
|
|
||||||
return common_rows.view(arr1.dtype).reshape(-1, arr1.shape[1])
|
|
||||||
|
|
||||||
blocks = []
|
|
||||||
for item in html_text_color_tree:
|
|
||||||
try:
|
|
||||||
color = np.array(hex_to_bgr(item[1]), dtype="uint8")
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
|
|
||||||
lower = color - 4
|
|
||||||
upper = color + 4
|
|
||||||
mask = cv2.inRange(image_cv, lower, upper)
|
|
||||||
coords = np.column_stack(np.where(mask > 0))
|
|
||||||
coords = get_intersect(coords, different_pixels)
|
|
||||||
|
|
||||||
if coords.size == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
x_min, y_min = np.min(coords, axis=0)
|
|
||||||
x_max, y_max = np.max(coords, axis=0)
|
|
||||||
|
|
||||||
# Get average color from original image
|
|
||||||
color_coords = coords.copy()
|
|
||||||
color_coords = color_coords[color_coords[:, 0] <= x_max]
|
|
||||||
color_coords = color_coords[color_coords[:, 1] <= y_max]
|
|
||||||
colors = [image_cv[x, y] for x, y in color_coords]
|
|
||||||
avg_color = tuple(map(int, np.mean(colors, axis=0)))[::-1] # Convert BGR to RGB
|
|
||||||
|
|
||||||
blocks.append({
|
|
||||||
'text': item[0].lower(),
|
|
||||||
'bbox': (y_min / y_w, x_min / x_w, (y_max - y_min + 1) / y_w, (x_max - x_min + 1) / x_w),
|
|
||||||
'color': avg_color
|
|
||||||
})
|
|
||||||
|
|
||||||
return blocks
|
|
||||||
|
|
||||||
|
|
||||||
def get_blocks_from_html(html_str, image1):
|
|
||||||
"""Extract text blocks from HTML and image."""
|
|
||||||
# Process HTML with two different color offsets
|
|
||||||
html_str_1 = process_html_str(html_str, offset=0)
|
|
||||||
html_str_2 = process_html_str(html_str, offset=50)
|
|
||||||
|
|
||||||
# Render both HTML versions to images
|
|
||||||
# TODO: Screenshot html_str_2
|
|
||||||
filter_color = (255, 0, 0)
|
|
||||||
image2 = Image.new("RGB", image1.size, filter_color)
|
|
||||||
|
|
||||||
|
|
||||||
# Find pixels that differ between the two rendered images
|
|
||||||
different_pixels = find_different_pixels(image1, image2)
|
|
||||||
if different_pixels is None:
|
|
||||||
logger.warning("Unable to get pixels with different colors")
|
|
||||||
return []
|
|
||||||
|
|
||||||
# Extract text and color information from HTML
|
|
||||||
html_text_color_tree = flatten_tree(extract_text_with_color(html_str_1))
|
|
||||||
try:
|
|
||||||
blocks = get_blocks_from_image_diff_pixels(image1, html_text_color_tree, different_pixels)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Unable to get blocks: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
return blocks
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(task, generated_img):
|
|
||||||
"""Evaluate generated image against reference image using multiple metrics."""
|
|
||||||
# Load reference image
|
|
||||||
post_image = task['post_image']
|
|
||||||
|
|
||||||
# Extract blocks from HTML and images
|
|
||||||
post_blocks = get_blocks_from_html(task['post_html'], post_image)
|
|
||||||
gen_blocks = get_blocks_from_html(task['gen_html'], generated_img)
|
|
||||||
|
|
||||||
print("block details", post_blocks, gen_blocks)
|
|
||||||
if not post_blocks or not gen_blocks:
|
|
||||||
# Fallback to basic CLIP and pixel comparison if no blocks available
|
|
||||||
clip_score = calculate_clip_similarity(post_image, generated_img, [], [])
|
|
||||||
logger.info(f'CLIP similarity score: {clip_score}')
|
|
||||||
|
|
||||||
# Pixel comparison
|
|
||||||
diff = ImageChops.difference(generated_img, post_image)
|
|
||||||
pixel_match = not diff.getbbox()
|
|
||||||
logger.info(
|
|
||||||
f"Pixel difference analysis: {'No difference' if pixel_match else 'Differences found'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return clip_score > 0.95 or pixel_match
|
|
||||||
|
|
||||||
# Merge blocks with same bounding boxes
|
|
||||||
post_blocks = merge_blocks_by_bbox(post_blocks)
|
|
||||||
gen_blocks = merge_blocks_by_bbox(gen_blocks)
|
|
||||||
|
|
||||||
# Find optimal block matching
|
|
||||||
consecutive_bonus, window_size = 0.1, 1
|
|
||||||
gen_blocks_m, post_blocks_m, matching = find_possible_merge(
|
|
||||||
gen_blocks, deepcopy(post_blocks), consecutive_bonus, window_size
|
|
||||||
)
|
|
||||||
|
|
||||||
# Filter matches with low similarity
|
|
||||||
filtered_matching = []
|
|
||||||
for i, j in matching:
|
|
||||||
text_similarity = calculate_similarity(gen_blocks_m[i], post_blocks_m[j])
|
|
||||||
if text_similarity >= 0.5:
|
|
||||||
filtered_matching.append([i, j, text_similarity])
|
|
||||||
matching = filtered_matching
|
|
||||||
|
|
||||||
if not matching:
|
|
||||||
logger.warning('No matching blocks found')
|
|
||||||
clip_score = calculate_clip_similarity(
|
|
||||||
post_image, generated_img, gen_blocks, post_blocks
|
|
||||||
)
|
|
||||||
return clip_score > 0.95
|
|
||||||
|
|
||||||
# Calculate metrics for matched blocks
|
|
||||||
indices1 = [item[0] for item in matching]
|
|
||||||
indices2 = [item[1] for item in matching]
|
|
||||||
|
|
||||||
# Calculate unmatched areas
|
|
||||||
unmatched_area_1 = sum(
|
|
||||||
block['bbox'][2] * block['bbox'][3]
|
|
||||||
for i, block in enumerate(gen_blocks_m)
|
|
||||||
if i not in indices1
|
|
||||||
)
|
|
||||||
unmatched_area_2 = sum(
|
|
||||||
block['bbox'][2] * block['bbox'][3]
|
|
||||||
for j, block in enumerate(post_blocks_m)
|
|
||||||
if j not in indices2
|
|
||||||
)
|
|
||||||
total_unmatched_area = unmatched_area_1 + unmatched_area_2
|
|
||||||
|
|
||||||
# Calculate metrics for matched blocks
|
|
||||||
matched_areas = []
|
|
||||||
text_scores = []
|
|
||||||
position_scores = []
|
|
||||||
color_scores = []
|
|
||||||
|
|
||||||
for i, j, text_similarity in matching:
|
|
||||||
# Area
|
|
||||||
block_area = (
|
|
||||||
gen_blocks_m[i]['bbox'][2] * gen_blocks_m[i]['bbox'][3]
|
|
||||||
+ post_blocks_m[j]['bbox'][2] * post_blocks_m[j]['bbox'][3]
|
|
||||||
)
|
|
||||||
matched_areas.append(block_area)
|
|
||||||
|
|
||||||
# Position similarity
|
|
||||||
position_similarity = 1 - calculate_distance_max_1d(
|
|
||||||
gen_blocks_m[i]['bbox'][0] + gen_blocks_m[i]['bbox'][2] / 2,
|
|
||||||
gen_blocks_m[i]['bbox'][1] + gen_blocks_m[i]['bbox'][3] / 2,
|
|
||||||
post_blocks_m[j]['bbox'][0] + post_blocks_m[j]['bbox'][2] / 2,
|
|
||||||
post_blocks_m[j]['bbox'][1] + post_blocks_m[j]['bbox'][3] / 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Color similarity
|
|
||||||
color_similarity = color_similarity_ciede2000(
|
|
||||||
gen_blocks_m[i]['color'], post_blocks_m[j]['color']
|
|
||||||
)
|
|
||||||
|
|
||||||
text_scores.append(text_similarity)
|
|
||||||
position_scores.append(position_similarity)
|
|
||||||
color_scores.append(color_similarity)
|
|
||||||
|
|
||||||
# Calculate final scores
|
|
||||||
total_area = sum(matched_areas) + total_unmatched_area
|
|
||||||
size_score = sum(matched_areas) / total_area if total_area > 0 else 0
|
|
||||||
text_score = np.mean(text_scores) if text_scores else 0
|
|
||||||
position_score = np.mean(position_scores) if position_scores else 0
|
|
||||||
color_score = np.mean(color_scores) if color_scores else 0
|
|
||||||
clip_score = calculate_clip_similarity(
|
|
||||||
post_image, generated_img, gen_blocks, post_blocks
|
|
||||||
)
|
|
||||||
|
|
||||||
# Combine scores with equal weights
|
|
||||||
final_score = 0.2 * (
|
|
||||||
size_score + text_score + position_score + color_score + clip_score
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info('Evaluation scores:')
|
|
||||||
logger.info(f'- Size score: {size_score:.3f}')
|
|
||||||
logger.info(f'- Text score: {text_score:.3f}')
|
|
||||||
logger.info(f'- Position score: {position_score:.3f}')
|
|
||||||
logger.info(f'- Color score: {color_score:.3f}')
|
|
||||||
logger.info(f'- CLIP score: {clip_score:.3f}')
|
|
||||||
logger.info(f'- Final score: {final_score:.3f}')
|
|
||||||
|
|
||||||
return final_score > 0.8 # Consider it a match if final score > 80%
|
|
||||||
|
|
||||||
|
|
||||||
def png_to_bytes(png):
|
|
||||||
buffer = BytesIO()
|
|
||||||
png.save(buffer, format='PNG')
|
|
||||||
image_bytes = buffer.getvalue()
|
|
||||||
return image_bytes
|
|
||||||
|
|
||||||
|
|
||||||
def bytes_to_image(image_bytes):
|
|
||||||
"""Convert bytes to a Pillow Image object."""
|
|
||||||
return Image.open(BytesIO(image_bytes))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
first_image = Image.open('./evaluation/visualcodebench/data/1/post.png')
|
|
||||||
image = Image.open('./evaluation/visualcodebench/data/1/prev.png')
|
|
||||||
|
|
||||||
|
|
||||||
html_file = open('./evaluation/visualcodebench/data/1/post/index.html', 'r')
|
|
||||||
first_html = html_file.read()
|
|
||||||
html_file.close()
|
|
||||||
|
|
||||||
html_file = open('./evaluation/visualcodebench/data/1/prev/index.html', 'r')
|
|
||||||
gen_html = html_file.read()
|
|
||||||
html_file.close()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sample = {'post_image': first_image, "post_html": first_html, "gen_html": gen_html}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
evaluate(sample, image)
|
|
||||||
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import base64
|
|
||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
from huggingface_hub import snapshot_download
|
|
||||||
from PIL import PngImagePlugin
|
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
|
||||||
|
|
||||||
REPO_DOWNLOAD_DIR = (
|
|
||||||
'./evaluation/visualcodebench/' # Directory to store the downloaded repository
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def download_repository():
|
|
||||||
"""
|
|
||||||
Download the entire repository from Hugging Face Hub.
|
|
||||||
This function clones the repository into REPO_DOWNLOAD_DIR.
|
|
||||||
"""
|
|
||||||
repo_id = 'rvmalhot/VisualCodeBench'
|
|
||||||
try:
|
|
||||||
logger.info(f"Downloading repository '{repo_id}'...")
|
|
||||||
snapshot_download(
|
|
||||||
repo_id=repo_id,
|
|
||||||
local_dir=REPO_DOWNLOAD_DIR,
|
|
||||||
repo_type='dataset',
|
|
||||||
ignore_patterns=None, # Download all files
|
|
||||||
)
|
|
||||||
logger.info(f"Repository downloaded to '{REPO_DOWNLOAD_DIR}'.")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error downloading repository '{repo_id}': {e}")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
def format_task_dict(example):
|
|
||||||
instance_id = example['id']
|
|
||||||
prev_remote_path = os.path.join(REPO_DOWNLOAD_DIR, f'data/{instance_id}/prev')
|
|
||||||
post_remote_path = os.path.join(REPO_DOWNLOAD_DIR, f'data/{instance_id}/post')
|
|
||||||
|
|
||||||
# Check if 'prev' and 'post' directories exist
|
|
||||||
prev_exists = os.path.exists(prev_remote_path)
|
|
||||||
post_exists = os.path.exists(post_remote_path)
|
|
||||||
|
|
||||||
if prev_exists and post_exists:
|
|
||||||
skip = False
|
|
||||||
else:
|
|
||||||
skip = True
|
|
||||||
|
|
||||||
task = {
|
|
||||||
'instance_id': instance_id,
|
|
||||||
'prev_image': example['prev_image'],
|
|
||||||
'post_image': example['post_image'],
|
|
||||||
'changes': example['changes'],
|
|
||||||
'prev_code_files': example['prev_code_files'],
|
|
||||||
'post_code_files': example['post_code_files'],
|
|
||||||
'skip': skip,
|
|
||||||
}
|
|
||||||
|
|
||||||
return task
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_visualcodebench(dataset):
|
|
||||||
logger.info('Processing dataset')
|
|
||||||
dataset_processed = []
|
|
||||||
for example in tqdm(dataset['train']):
|
|
||||||
formatted_example = format_task_dict(example)
|
|
||||||
if formatted_example['skip']:
|
|
||||||
continue
|
|
||||||
del formatted_example['skip']
|
|
||||||
dataset_processed.append(formatted_example)
|
|
||||||
|
|
||||||
return pd.DataFrame(dataset_processed)
|
|
||||||
|
|
||||||
|
|
||||||
def pil_image_to_base64(image: PngImagePlugin.PngImageFile) -> str:
|
|
||||||
"""
|
|
||||||
Converts a PIL image to a Base64-encoded string.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- image (PngImagePlugin.PngImageFile): The PIL image to convert.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
- str: The Base64-encoded string of the image.
|
|
||||||
"""
|
|
||||||
if not isinstance(image, PngImagePlugin.PngImageFile):
|
|
||||||
raise ValueError(
|
|
||||||
'The provided image is not a PIL.PngImagePlugin.PngImageFile instance.'
|
|
||||||
)
|
|
||||||
|
|
||||||
buffered = BytesIO()
|
|
||||||
image.save(buffered, format='PNG')
|
|
||||||
img_bytes = buffered.getvalue()
|
|
||||||
img_base64 = base64.b64encode(img_bytes).decode('utf-8')
|
|
||||||
base64_with_prefix = f'data:image/png;base64,{img_base64}'
|
|
||||||
return [base64_with_prefix]
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
# FILE: run_infer.py
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
from datasets import load_dataset
|
|
||||||
|
|
||||||
# from evaluation.benchmarks.visualcodebench.eval import capture_screenshot
|
|
||||||
from evaluation.benchmarks.visualcodebench.prepare import (
|
|
||||||
REPO_DOWNLOAD_DIR,
|
|
||||||
download_repository,
|
|
||||||
pil_image_to_base64,
|
|
||||||
prepare_visualcodebench,
|
|
||||||
)
|
|
||||||
from evaluation.utils.shared import (
|
|
||||||
EvalMetadata,
|
|
||||||
assert_and_raise,
|
|
||||||
codeact_user_response,
|
|
||||||
make_metadata,
|
|
||||||
prepare_dataset,
|
|
||||||
reset_logger_for_multiprocessing,
|
|
||||||
run_evaluation,
|
|
||||||
)
|
|
||||||
from openhands.controller.state.state import State
|
|
||||||
from openhands.core.config import (
|
|
||||||
AppConfig,
|
|
||||||
SandboxConfig,
|
|
||||||
get_llm_config_arg,
|
|
||||||
)
|
|
||||||
from openhands.core.config.utils import parse_arguments
|
|
||||||
from openhands.core.logger import openhands_logger as logger # Import OpenHands logger
|
|
||||||
from openhands.core.main import create_runtime, run_controller
|
|
||||||
from openhands.events.action.commands import CmdRunAction
|
|
||||||
from openhands.events.action.message import MessageAction
|
|
||||||
from openhands.events.observation.commands import CmdOutputObservation
|
|
||||||
from openhands.runtime.base import Runtime
|
|
||||||
from openhands.utils.async_utils import call_async_from_sync
|
|
||||||
|
|
||||||
# Define workspace and output directories
|
|
||||||
WORKSPACE_DIR = './workspace'
|
|
||||||
|
|
||||||
FAKE_RESPONSES = {
|
|
||||||
'CodeActAgent': partial(codeact_user_response, encapsulate_solution=True),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(
|
|
||||||
metadata: EvalMetadata,
|
|
||||||
) -> AppConfig:
|
|
||||||
config = AppConfig(
|
|
||||||
default_agent=metadata.agent_class,
|
|
||||||
run_as_openhands=False,
|
|
||||||
runtime='eventstream',
|
|
||||||
max_iterations=metadata.max_iterations,
|
|
||||||
sandbox=SandboxConfig(
|
|
||||||
base_container_image='python:3.12-bookworm',
|
|
||||||
enable_auto_lint=True,
|
|
||||||
use_host_network=False,
|
|
||||||
),
|
|
||||||
# do not mount workspace
|
|
||||||
workspace_base=None,
|
|
||||||
workspace_mount_path=None,
|
|
||||||
)
|
|
||||||
config.set_llm_config(metadata.llm_config)
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_runtime(
|
|
||||||
runtime: Runtime,
|
|
||||||
instance: pd.Series, # this argument is not required
|
|
||||||
):
|
|
||||||
"""Initialize the runtime for the agent.
|
|
||||||
|
|
||||||
This function is called before the runtime is used to run the agent.
|
|
||||||
"""
|
|
||||||
logger.info('-' * 30)
|
|
||||||
logger.info('BEGIN Runtime Initialization Fn')
|
|
||||||
logger.info('-' * 30)
|
|
||||||
workspace_dir_name = instance['instance_id']
|
|
||||||
obs: CmdOutputObservation
|
|
||||||
|
|
||||||
action = CmdRunAction(command='mkdir -p /workspace/{workspace_dir_name}')
|
|
||||||
action.timeout = 600
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
|
|
||||||
assert_and_raise(
|
|
||||||
obs.exit_code == 0,
|
|
||||||
f'Failed to create /workspace/{workspace_dir_name}: {str(obs)}',
|
|
||||||
)
|
|
||||||
|
|
||||||
file_path = REPO_DOWNLOAD_DIR + f'data/{workspace_dir_name}/prev/index.html'
|
|
||||||
runtime.copy_to(file_path, f'/workspace/{workspace_dir_name}')
|
|
||||||
logger.info(f'Copied code file for instance {workspace_dir_name}')
|
|
||||||
|
|
||||||
action = CmdRunAction(command=f'cd /workspace/{workspace_dir_name}')
|
|
||||||
action.timeout = 600
|
|
||||||
logger.info(action, extra={'msg_type': 'ACTION'})
|
|
||||||
obs = runtime.run_action(action)
|
|
||||||
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
|
|
||||||
assert_and_raise(
|
|
||||||
obs.exit_code == 0,
|
|
||||||
f'Failed to cd to /workspace/{workspace_dir_name}: {str(obs)}',
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info('-' * 30)
|
|
||||||
logger.info('END Runtime Initialization Fn')
|
|
||||||
logger.info('-' * 30)
|
|
||||||
|
|
||||||
|
|
||||||
def complete_runtime(
|
|
||||||
runtime: Runtime,
|
|
||||||
instance: pd.Series, # this argument is not required, but it is used to get the workspace_dir_name
|
|
||||||
) -> str:
|
|
||||||
# TODO: extract edited HTML file from agent workspace
|
|
||||||
# temp_zip = runtime.copy_from(f'/workspace/{instance.instance_id}')
|
|
||||||
# file_name = f'/workspace/{instance.instance_id}/index.html'
|
|
||||||
# with zipfile.ZipFile(temp_zip, 'r') as zip_ref:
|
|
||||||
# if file_name in zip_ref.namelist():
|
|
||||||
# with zip_ref.open(file_name) as file:
|
|
||||||
# file_content = file.read().decode('utf-8') # Decode bytes to string
|
|
||||||
# else:
|
|
||||||
# raise FileNotFoundError(f"'{file_name}' not found in the ZIP archive.")
|
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
|
||||||
src_folder = REPO_DOWNLOAD_DIR + f'data/{instance.instance_id}/post/'
|
|
||||||
shutil.copytree(src_folder, tmpdir, dirs_exist_ok=True)
|
|
||||||
|
|
||||||
# image = capture_screenshot(tmpdir)
|
|
||||||
# if image is not None:
|
|
||||||
# shutil.copy(os.path.join(tmpdir, 'final_screenshot.png'), REPO_DOWNLOAD_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
def process_instance(
|
|
||||||
instance: pd.Series, metadata: EvalMetadata, reset_logger: bool = True
|
|
||||||
):
|
|
||||||
config = get_config(metadata)
|
|
||||||
|
|
||||||
# Setup the logger properly, so you can run multi-processing to parallelize the evaluation
|
|
||||||
if reset_logger:
|
|
||||||
log_dir = os.path.join(metadata.eval_output_dir, 'infer_logs')
|
|
||||||
reset_logger_for_multiprocessing(logger, instance.instance_id, log_dir)
|
|
||||||
else:
|
|
||||||
logger.info(f'Starting evaluation for instance {instance.instance_id}.')
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# build instruction
|
|
||||||
# =============================================
|
|
||||||
|
|
||||||
# Prepare instruction
|
|
||||||
instruction = (
|
|
||||||
f"Modify the HTML/CSS according to the following instruction:\n\n"
|
|
||||||
f"{instance['changes']}\n\n"
|
|
||||||
)
|
|
||||||
instruction += (
|
|
||||||
'IMPORTANT: You should ONLY interact with the environment provided '
|
|
||||||
'to you AND NEVER ASK FOR HUMAN HELP.\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# create sandbox and run the agent
|
|
||||||
# =============================================
|
|
||||||
|
|
||||||
runtime: Runtime = create_runtime(config)
|
|
||||||
call_async_from_sync(runtime.connect)
|
|
||||||
|
|
||||||
try:
|
|
||||||
initialize_runtime(runtime, instance=instance)
|
|
||||||
|
|
||||||
image_urls = pil_image_to_base64(instance['prev_image'])
|
|
||||||
|
|
||||||
action = MessageAction(content=instruction, image_urls=image_urls)
|
|
||||||
state: State | None = asyncio.run(
|
|
||||||
run_controller(
|
|
||||||
config=config,
|
|
||||||
initial_user_action=action,
|
|
||||||
runtime=runtime,
|
|
||||||
fake_user_response_fn=FAKE_RESPONSES[metadata.agent_class],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if state is None:
|
|
||||||
raise ValueError('State should not be None.')
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# result evaluation
|
|
||||||
# =============================================
|
|
||||||
|
|
||||||
return_val = complete_runtime(runtime, instance)
|
|
||||||
logger.info(f'Return value {return_val}')
|
|
||||||
finally:
|
|
||||||
runtime.close()
|
|
||||||
|
|
||||||
# TODO: return EVAL output
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main function to run the evaluation."""
|
|
||||||
# args = parse_args()
|
|
||||||
args = parse_arguments()
|
|
||||||
|
|
||||||
logger.info(f"\n{'='*80}\nStarting VisualCodeBench Evaluation\n{'='*80}")
|
|
||||||
logger.info(f'Agent: {args.agent_cls}')
|
|
||||||
logger.info(f'Model: {args.llm_config}')
|
|
||||||
logger.info(f'Max iterations: {args.max_iterations}')
|
|
||||||
logger.info(f'Eval limit: {args.eval_n_limit}')
|
|
||||||
logger.info(f'Num workers: {args.eval_num_workers}\n')
|
|
||||||
logger.info(f'Eval output: {args.eval_output_dir}\n')
|
|
||||||
|
|
||||||
# Step 1: Download the entire repository once
|
|
||||||
logger.info('Downloading repository...')
|
|
||||||
download_repository()
|
|
||||||
|
|
||||||
# Step 2: Load Dataset
|
|
||||||
logger.info('Loading dataset...')
|
|
||||||
dataset = load_dataset(REPO_DOWNLOAD_DIR)
|
|
||||||
|
|
||||||
# Step 3: Prepare dataset
|
|
||||||
llm_config = get_llm_config_arg(args.llm_config)
|
|
||||||
if llm_config is None:
|
|
||||||
logger.error(f'Could not find LLM config: {args.llm_config}')
|
|
||||||
raise ValueError(f'Could not find LLM config: {args.llm_config}')
|
|
||||||
|
|
||||||
metadata = make_metadata(
|
|
||||||
llm_config,
|
|
||||||
'VisualCodeBench',
|
|
||||||
args.agent_cls,
|
|
||||||
args.max_iterations,
|
|
||||||
args.eval_note,
|
|
||||||
'evaluation/output/',
|
|
||||||
)
|
|
||||||
|
|
||||||
output_file = os.path.join(metadata.eval_output_dir, 'output.jsonl')
|
|
||||||
dataset = prepare_visualcodebench(dataset)
|
|
||||||
instances = prepare_dataset(dataset, output_file, eval_n_limit=args.eval_n_limit)
|
|
||||||
|
|
||||||
# Step 4: Run eval
|
|
||||||
run_evaluation(
|
|
||||||
instances, metadata, output_file, args.eval_num_workers, process_instance
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
source "evaluation/utils/version_control.sh"
|
|
||||||
|
|
||||||
# Check if required arguments are provided
|
|
||||||
if [ "$#" -lt 4 ]; then
|
|
||||||
echo "Usage: $0 [model_config] [commit_hash] [agent_cls] [eval_limit] [num_workers]"
|
|
||||||
echo "Example: $0 llm.eval_gpt_4o_mini HEAD CodeActAgent 5 1"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
MODEL_CONFIG=$1
|
|
||||||
COMMIT_HASH=$2
|
|
||||||
AGENT_CLS=$3
|
|
||||||
EVAL_LIMIT=$4
|
|
||||||
NUM_WORKERS=${5:-1} # Default to 1 worker if not specified
|
|
||||||
|
|
||||||
# Checkout the specified commit
|
|
||||||
checkout_eval_branch
|
|
||||||
|
|
||||||
if [ -z "$AGENT" ]; then
|
|
||||||
echo "Agent not specified, use default CodeActAgent"
|
|
||||||
AGENT="CodeActAgent"
|
|
||||||
fi
|
|
||||||
|
|
||||||
get_openhands_version
|
|
||||||
|
|
||||||
echo "AGENT: $AGENT"
|
|
||||||
echo "OPENHANDS_VERSION: $OPENHANDS_VERSION"
|
|
||||||
echo "MODEL_CONFIG: $MODEL_CONFIG"
|
|
||||||
|
|
||||||
COMMAND="export PYTHONPATH=evaluation/benchmarks/visualcodebench:\$PYTHONPATH && poetry run python evaluation/benchmarks/visualcodebench/run_infer.py \
|
|
||||||
--agent-cls $AGENT \
|
|
||||||
--llm-config $MODEL_CONFIG \
|
|
||||||
--max-iterations 5 \
|
|
||||||
--eval-num-workers $NUM_WORKERS \
|
|
||||||
--eval-note $OPENHANDS_VERSION" \
|
|
||||||
|
|
||||||
if [ -n "$EVAL_LIMIT" ]; then
|
|
||||||
echo "EVAL_LIMIT: $EVAL_LIMIT"
|
|
||||||
COMMAND="$COMMAND --eval-n-limit $EVAL_LIMIT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
eval $COMMAND
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import http
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import socketserver
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from PIL import Image, ImageChops
|
|
||||||
from playwright.sync_api import sync_playwright
|
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
|
||||||
|
|
||||||
|
|
||||||
def get_free_port():
|
|
||||||
"""Find a free port to run the HTTP server."""
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
||||||
s.bind(('', 0))
|
|
||||||
return s.getsockname()[1]
|
|
||||||
|
|
||||||
|
|
||||||
def start_http_server(tmpdir):
|
|
||||||
port = get_free_port()
|
|
||||||
|
|
||||||
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
|
|
||||||
def translate_path(self, path):
|
|
||||||
# Serve files from the specified directory instead of the current working directory
|
|
||||||
path = super().translate_path(path)
|
|
||||||
relative_path = os.path.relpath(path, os.getcwd())
|
|
||||||
return os.path.join(tmpdir, relative_path)
|
|
||||||
|
|
||||||
handler = CustomHTTPRequestHandler
|
|
||||||
server = socketserver.TCPServer(('', port), handler)
|
|
||||||
return server, port
|
|
||||||
|
|
||||||
|
|
||||||
def capture_screenshot(tmpdir):
|
|
||||||
server, port = start_http_server(tmpdir)
|
|
||||||
server_thread = threading.Thread(target=server.serve_forever)
|
|
||||||
server_thread.daemon = True
|
|
||||||
server_thread.start()
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
image = None
|
|
||||||
try:
|
|
||||||
server_url = f'http://localhost:{port}/'
|
|
||||||
|
|
||||||
if not is_server_reachable(server_url):
|
|
||||||
raise RuntimeError(f'Server not reachable at {server_url}')
|
|
||||||
|
|
||||||
screenshot_path = os.path.join(tmpdir, 'final_screenshot.png')
|
|
||||||
capture_screenshot_playwright(server_url, screenshot_path)
|
|
||||||
image = Image.open(screenshot_path)
|
|
||||||
image.load()
|
|
||||||
finally:
|
|
||||||
# Shut down the server and clean up
|
|
||||||
server.shutdown()
|
|
||||||
server.server_close()
|
|
||||||
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
def is_server_reachable(url):
|
|
||||||
"""
|
|
||||||
Check if the local server is reachable.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = requests.get(url, timeout=5) # Set a 5-second timeout
|
|
||||||
if response.status_code == 200:
|
|
||||||
logger.info(f'Server is reachable at {url}')
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.warning(
|
|
||||||
f'Server responded with status code {response.status_code} at {url}'
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
except requests.ConnectionError as e:
|
|
||||||
logger.error(f'Failed to connect to server at {url}: {e}')
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def capture_screenshot_playwright(url, screenshot_path):
|
|
||||||
"""Capture a screenshot of the given URL using Playwright."""
|
|
||||||
try:
|
|
||||||
with sync_playwright() as p:
|
|
||||||
logger.info('Launching browser...')
|
|
||||||
browser = p.chromium.launch(timeout=10000) # 10 seconds for browser launch
|
|
||||||
|
|
||||||
logger.info('Creating a new page...')
|
|
||||||
page = browser.new_page()
|
|
||||||
|
|
||||||
logger.info(f'Navigating to URL: {url}')
|
|
||||||
try:
|
|
||||||
page.goto(url, timeout=60 * 1000) # Set timeout to 5 seconds
|
|
||||||
logger.info('Page navigation completed.')
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f'Page navigation timed out. {e}. Continuing...')
|
|
||||||
|
|
||||||
logger.info('Waiting for network to be idle...')
|
|
||||||
try:
|
|
||||||
page.wait_for_load_state(
|
|
||||||
'networkidle', timeout=60 * 1000
|
|
||||||
) # Set timeout to 5 seconds
|
|
||||||
logger.info('Page load state reached.')
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f'Page load state timed out. {e}. Continuing...')
|
|
||||||
|
|
||||||
logger.info('Capturing screenshot...')
|
|
||||||
page.screenshot(
|
|
||||||
path=screenshot_path, full_page=True
|
|
||||||
) # Capture full page screenshot
|
|
||||||
|
|
||||||
logger.info(f'Screenshot saved to {screenshot_path}')
|
|
||||||
browser.close()
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Error capturing screenshot with Playwright: {e}')
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(task, screenshot_path):
|
|
||||||
"""Compare generated screenshot with post_image using CLIP score."""
|
|
||||||
try:
|
|
||||||
import torch
|
|
||||||
from transformers import CLIPModel, CLIPProcessor
|
|
||||||
|
|
||||||
# Load CLIP model and processor
|
|
||||||
model = CLIPModel.from_pretrained('openai/clip-vit-base-patch32')
|
|
||||||
processor = CLIPProcessor.from_pretrained('openai/clip-vit-base-patch32')
|
|
||||||
|
|
||||||
# Load images
|
|
||||||
post_image = Image.open(BytesIO(task['post_image']))
|
|
||||||
generated_img = Image.open(screenshot_path)
|
|
||||||
|
|
||||||
# Process images
|
|
||||||
inputs = processor(
|
|
||||||
images=[post_image, generated_img], return_tensors='pt', padding=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get image features
|
|
||||||
image_features = model.get_image_features(**inputs)
|
|
||||||
|
|
||||||
# Calculate cosine similarity
|
|
||||||
similarity = torch.nn.functional.cosine_similarity(
|
|
||||||
image_features[0].unsqueeze(0), image_features[1].unsqueeze(0)
|
|
||||||
).item()
|
|
||||||
|
|
||||||
logger.info(f'CLIP similarity score: {similarity}')
|
|
||||||
|
|
||||||
return similarity > 0.95 # Consider it a match if similarity > 95%
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Error in CLIP evaluation: {e}')
|
|
||||||
# Fallback to pixel comparison if CLIP fails
|
|
||||||
try:
|
|
||||||
post_image = Image.open(BytesIO(task['post_image']))
|
|
||||||
generated_img = Image.open(screenshot_path)
|
|
||||||
|
|
||||||
# Compare images directly without converting to bytes
|
|
||||||
diff = ImageChops.difference(generated_img, post_image)
|
|
||||||
logger.info(
|
|
||||||
f"Pixel difference analysis: {'No difference' if not diff.getbbox() else 'Differences found'}"
|
|
||||||
)
|
|
||||||
return not diff.getbbox()
|
|
||||||
except Exception as ex:
|
|
||||||
logger.error(f'Error in fallback evaluation: {ex}')
|
|
||||||
return False
|
|
||||||
@@ -155,9 +155,7 @@ describe("Sidebar", () => {
|
|||||||
const settingsModal = screen.getByTestId("ai-config-modal");
|
const settingsModal = screen.getByTestId("ai-config-modal");
|
||||||
|
|
||||||
// Click the advanced options switch to show the API key input
|
// Click the advanced options switch to show the API key input
|
||||||
const advancedOptionsSwitch = within(settingsModal).getByTestId(
|
const advancedOptionsSwitch = within(settingsModal).getByTestId("advanced-option-switch");
|
||||||
"advanced-option-switch",
|
|
||||||
);
|
|
||||||
await user.click(advancedOptionsSwitch);
|
await user.click(advancedOptionsSwitch);
|
||||||
|
|
||||||
const apiKeyInput = within(settingsModal).getByLabelText(/API\$KEY/i);
|
const apiKeyInput = within(settingsModal).getByLabelText(/API\$KEY/i);
|
||||||
|
|||||||
@@ -2,6 +2,13 @@ import { render, screen } from "@testing-library/react";
|
|||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { UploadImageInput } from "#/components/features/images/upload-image-input";
|
import { UploadImageInput } from "#/components/features/images/upload-image-input";
|
||||||
|
import { toast } from "#/utils/toast";
|
||||||
|
|
||||||
|
vi.mock("#/utils/toast", () => ({
|
||||||
|
toast: {
|
||||||
|
error: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
describe("UploadImageInput", () => {
|
describe("UploadImageInput", () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
@@ -41,17 +48,37 @@ describe("UploadImageInput", () => {
|
|||||||
expect(onUploadMock).toHaveBeenNthCalledWith(1, files);
|
expect(onUploadMock).toHaveBeenNthCalledWith(1, files);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not upload any file that is not an image", async () => {
|
it("should show error and not upload unsupported image types", async () => {
|
||||||
render(<UploadImageInput onUpload={onUploadMock} />);
|
render(<UploadImageInput onUpload={onUploadMock} />);
|
||||||
|
|
||||||
const file = new File(["(⌐□_□)"], "chucknorris.txt", {
|
const file = new File(["(⌐□_□)"], "chucknorris.bmp", {
|
||||||
type: "text/plain",
|
type: "image/bmp",
|
||||||
});
|
});
|
||||||
const input = screen.getByTestId("upload-image-input");
|
const input = screen.getByTestId("upload-image-input");
|
||||||
|
|
||||||
await user.upload(input, file);
|
await user.upload(input, file);
|
||||||
|
|
||||||
expect(onUploadMock).not.toHaveBeenCalled();
|
expect(onUploadMock).not.toHaveBeenCalled();
|
||||||
|
expect(toast.error).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("Only JPEG, PNG, GIF, and WebP images are supported")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle mix of supported and unsupported image types", async () => {
|
||||||
|
render(<UploadImageInput onUpload={onUploadMock} />);
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
new File(["(⌐□_□)"], "valid.png", { type: "image/png" }),
|
||||||
|
new File(["(⌐□_□)"], "invalid.bmp", { type: "image/bmp" }),
|
||||||
|
];
|
||||||
|
const input = screen.getByTestId("upload-image-input");
|
||||||
|
|
||||||
|
await user.upload(input, files);
|
||||||
|
|
||||||
|
expect(onUploadMock).toHaveBeenCalledWith([files[0]]);
|
||||||
|
expect(toast.error).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("Only JPEG, PNG, GIF, and WebP images are supported")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render custom labels", () => {
|
it("should render custom labels", () => {
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import store from "../src/store";
|
import store from "../src/store";
|
||||||
import {
|
import {
|
||||||
setInitialPrompt,
|
setInitialQuery,
|
||||||
clearInitialPrompt,
|
clearInitialQuery,
|
||||||
} from "../src/state/initial-query-slice";
|
} from "../src/state/initial-query-slice";
|
||||||
|
|
||||||
describe("Initial Query Behavior", () => {
|
describe("Initial Query Behavior", () => {
|
||||||
it("should clear initial query when clearInitialPrompt is dispatched", () => {
|
it("should clear initial query when clearInitialQuery is dispatched", () => {
|
||||||
// Set up initial query in the store
|
// Set up initial query in the store
|
||||||
store.dispatch(setInitialPrompt("test query"));
|
store.dispatch(setInitialQuery("test query"));
|
||||||
expect(store.getState().initialQuery.initialPrompt).toBe("test query");
|
expect(store.getState().initialQuery.initialQuery).toBe("test query");
|
||||||
|
|
||||||
// Clear the initial query
|
// Clear the initial query
|
||||||
store.dispatch(clearInitialPrompt());
|
store.dispatch(clearInitialQuery());
|
||||||
|
|
||||||
// Verify initial query is cleared
|
// Verify initial query is cleared
|
||||||
expect(store.getState().initialQuery.initialPrompt).toBeNull();
|
expect(store.getState().initialQuery.initialQuery).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { validateImageType, getValidImageFiles } from '#/utils/validate-image-type';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('validateImageType', () => {
|
||||||
|
it('should accept supported image types', () => {
|
||||||
|
const supportedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||||
|
supportedTypes.forEach((type) => {
|
||||||
|
const file = new File([''], 'test.jpg', { type });
|
||||||
|
expect(validateImageType(file)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject unsupported image types', () => {
|
||||||
|
const unsupportedTypes = ['image/bmp', 'image/tiff', 'application/pdf', 'text/plain'];
|
||||||
|
unsupportedTypes.forEach((type) => {
|
||||||
|
const file = new File([''], 'test.jpg', { type });
|
||||||
|
expect(validateImageType(file)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getValidImageFiles', () => {
|
||||||
|
it('should separate valid and invalid files', () => {
|
||||||
|
const files = [
|
||||||
|
new File([''], 'test1.jpg', { type: 'image/jpeg' }),
|
||||||
|
new File([''], 'test2.bmp', { type: 'image/bmp' }),
|
||||||
|
new File([''], 'test3.png', { type: 'image/png' }),
|
||||||
|
new File([''], 'test4.pdf', { type: 'application/pdf' }),
|
||||||
|
];
|
||||||
|
|
||||||
|
const { validFiles, invalidFiles } = getValidImageFiles(files);
|
||||||
|
|
||||||
|
expect(validFiles).toHaveLength(2);
|
||||||
|
expect(invalidFiles).toHaveLength(2);
|
||||||
|
expect(validFiles[0].type).toBe('image/jpeg');
|
||||||
|
expect(validFiles[1].type).toBe('image/png');
|
||||||
|
expect(invalidFiles[0].type).toBe('image/bmp');
|
||||||
|
expect(invalidFiles[1].type).toBe('application/pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty array', () => {
|
||||||
|
const { validFiles, invalidFiles } = getValidImageFiles([]);
|
||||||
|
expect(validFiles).toHaveLength(0);
|
||||||
|
expect(invalidFiles).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Generated
+3
-106
@@ -105,7 +105,6 @@
|
|||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@@ -1594,7 +1593,6 @@
|
|||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^5.1.2",
|
"string-width": "^5.1.2",
|
||||||
@@ -1612,7 +1610,6 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -1625,7 +1622,6 @@
|
|||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
@@ -3408,7 +3404,6 @@
|
|||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "2.0.5",
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
@@ -3422,7 +3417,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
@@ -3432,7 +3426,6 @@
|
|||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.scandir": "2.1.5",
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
@@ -3533,7 +3526,6 @@
|
|||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5764,7 +5756,7 @@
|
|||||||
"version": "22.10.7",
|
"version": "22.10.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz",
|
||||||
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==",
|
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.20.0"
|
||||||
}
|
}
|
||||||
@@ -5773,7 +5765,6 @@
|
|||||||
"version": "19.0.7",
|
"version": "19.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz",
|
||||||
"integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==",
|
"integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -6462,7 +6453,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -6472,7 +6462,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
@@ -6488,14 +6477,12 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
@@ -6509,7 +6496,6 @@
|
|||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
@@ -6835,7 +6821,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/basic-auth": {
|
"node_modules/basic-auth": {
|
||||||
@@ -6860,7 +6845,6 @@
|
|||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -6909,7 +6893,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
@@ -6919,7 +6902,6 @@
|
|||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
@@ -7070,7 +7052,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -7646,7 +7627,6 @@
|
|||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
@@ -7661,7 +7641,6 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
@@ -7684,7 +7663,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"cssesc": "bin/cssesc"
|
"cssesc": "bin/cssesc"
|
||||||
@@ -7951,7 +7929,6 @@
|
|||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
@@ -7971,7 +7948,6 @@
|
|||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
@@ -8036,7 +8012,6 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
@@ -8055,7 +8030,6 @@
|
|||||||
"version": "9.2.2",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
@@ -9212,7 +9186,6 @@
|
|||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||||
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
@@ -9229,7 +9202,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
@@ -9256,7 +9228,6 @@
|
|||||||
"version": "1.18.0",
|
"version": "1.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
||||||
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
|
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
@@ -9298,7 +9269,6 @@
|
|||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
@@ -9418,7 +9388,6 @@
|
|||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||||
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
@@ -9689,7 +9658,6 @@
|
|||||||
"version": "10.4.5",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"foreground-child": "^3.1.0",
|
"foreground-child": "^3.1.0",
|
||||||
@@ -9710,7 +9678,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.3"
|
"is-glob": "^4.0.3"
|
||||||
@@ -10462,7 +10429,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
@@ -10505,7 +10471,6 @@
|
|||||||
"version": "2.16.1",
|
"version": "2.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hasown": "^2.0.2"
|
"hasown": "^2.0.2"
|
||||||
@@ -10573,7 +10538,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -10628,7 +10592,6 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
@@ -10681,7 +10644,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
@@ -10910,7 +10872,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/istanbul-lib-coverage": {
|
"node_modules/istanbul-lib-coverage": {
|
||||||
@@ -10989,7 +10950,6 @@
|
|||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/cliui": "^8.0.2"
|
"@isaacs/cliui": "^8.0.2"
|
||||||
@@ -11005,7 +10965,6 @@
|
|||||||
"version": "1.21.7",
|
"version": "1.21.7",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
@@ -11220,7 +11179,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
@@ -11233,7 +11191,6 @@
|
|||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lint-staged": {
|
"node_modules/lint-staged": {
|
||||||
@@ -12008,7 +11965,6 @@
|
|||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
@@ -12589,7 +12545,6 @@
|
|||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.3",
|
"braces": "^3.0.3",
|
||||||
@@ -12680,7 +12635,6 @@
|
|||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
@@ -12706,7 +12660,6 @@
|
|||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
@@ -12866,7 +12819,6 @@
|
|||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0",
|
"any-promise": "^1.0.0",
|
||||||
@@ -12987,7 +12939,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -13098,7 +13049,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -13108,7 +13058,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -13350,7 +13299,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
@@ -13469,7 +13417,6 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -13479,14 +13426,12 @@
|
|||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/path-scurry": {
|
"node_modules/path-scurry": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
@@ -13503,7 +13448,6 @@
|
|||||||
"version": "10.4.3",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
@@ -13559,7 +13503,6 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
@@ -13585,7 +13528,6 @@
|
|||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -13595,7 +13537,6 @@
|
|||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -13674,7 +13615,6 @@
|
|||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
@@ -13692,7 +13632,6 @@
|
|||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camelcase-css": "^2.0.1"
|
"camelcase-css": "^2.0.1"
|
||||||
@@ -13712,7 +13651,6 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
||||||
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -13748,7 +13686,6 @@
|
|||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -13774,7 +13711,6 @@
|
|||||||
"version": "6.1.2",
|
"version": "6.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
@@ -13802,7 +13738,6 @@
|
|||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/posthog-js": {
|
"node_modules/posthog-js": {
|
||||||
@@ -14064,7 +13999,6 @@
|
|||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -14313,7 +14247,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pify": "^2.3.0"
|
"pify": "^2.3.0"
|
||||||
@@ -14642,7 +14575,6 @@
|
|||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-core-module": "^2.16.0",
|
"is-core-module": "^2.16.0",
|
||||||
@@ -14716,7 +14648,6 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"iojs": ">=1.0.0",
|
"iojs": ">=1.0.0",
|
||||||
@@ -14841,7 +14772,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -15124,7 +15054,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
@@ -15137,7 +15066,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -15226,7 +15154,6 @@
|
|||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
@@ -15546,7 +15473,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eastasianwidth": "^0.2.0",
|
"eastasianwidth": "^0.2.0",
|
||||||
@@ -15565,7 +15491,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
@@ -15580,14 +15505,12 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
|
"node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -15597,7 +15520,6 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -15610,7 +15532,6 @@
|
|||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
@@ -15753,7 +15674,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
@@ -15767,7 +15687,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
@@ -15838,7 +15757,6 @@
|
|||||||
"version": "3.35.0",
|
"version": "3.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||||
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
@@ -15861,7 +15779,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -15884,7 +15801,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -15962,7 +15878,6 @@
|
|||||||
"version": "3.4.17",
|
"version": "3.4.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||||
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
@@ -16000,7 +15915,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
@@ -16025,7 +15939,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
@@ -16038,7 +15951,6 @@
|
|||||||
"version": "6.1.2",
|
"version": "6.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
@@ -16052,7 +15964,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
@@ -16086,7 +15997,6 @@
|
|||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0"
|
"any-promise": "^1.0.0"
|
||||||
@@ -16096,7 +16006,6 @@
|
|||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
@@ -16189,7 +16098,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
@@ -16278,7 +16186,6 @@
|
|||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/tsconfck": {
|
"node_modules/tsconfck": {
|
||||||
@@ -16460,7 +16367,7 @@
|
|||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -16501,7 +16408,7 @@
|
|||||||
"version": "6.20.0",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
@@ -16718,7 +16625,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
@@ -17256,7 +17162,6 @@
|
|||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^6.1.0",
|
"ansi-styles": "^6.1.0",
|
||||||
@@ -17275,7 +17180,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
@@ -17293,14 +17197,12 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
|
"node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -17310,7 +17212,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
@@ -17325,7 +17226,6 @@
|
|||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -17338,7 +17238,6 @@
|
|||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -17351,7 +17250,6 @@
|
|||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
@@ -17445,7 +17343,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
||||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
|
|||||||
@@ -244,14 +244,10 @@ class OpenHands {
|
|||||||
static async createConversation(
|
static async createConversation(
|
||||||
githubToken?: string,
|
githubToken?: string,
|
||||||
selectedRepository?: string,
|
selectedRepository?: string,
|
||||||
initialUserMsg?: string,
|
|
||||||
imageUrls?: string[],
|
|
||||||
): Promise<Conversation> {
|
): Promise<Conversation> {
|
||||||
const body = {
|
const body = {
|
||||||
github_token: githubToken,
|
github_token: githubToken,
|
||||||
selected_repository: selectedRepository,
|
selected_repository: selectedRepository,
|
||||||
initial_user_msg: initialUserMsg,
|
|
||||||
image_urls: imageUrls,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await openHands.post<Conversation>(
|
const { data } = await openHands.post<Conversation>(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const AGENT_STATUS_MAP: {
|
|||||||
},
|
},
|
||||||
[AgentState.AWAITING_USER_INPUT]: {
|
[AgentState.AWAITING_USER_INPUT]: {
|
||||||
message: I18nKey.CHAT_INTERFACE$AGENT_AWAITING_USER_INPUT_MESSAGE,
|
message: I18nKey.CHAT_INTERFACE$AGENT_AWAITING_USER_INPUT_MESSAGE,
|
||||||
indicator: IndicatorColor.BLUE,
|
indicator: IndicatorColor.ORANGE,
|
||||||
},
|
},
|
||||||
[AgentState.PAUSED]: {
|
[AgentState.PAUSED]: {
|
||||||
message: I18nKey.CHAT_INTERFACE$AGENT_PAUSED_MESSAGE,
|
message: I18nKey.CHAT_INTERFACE$AGENT_PAUSED_MESSAGE,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { I18nKey } from "#/i18n/declaration";
|
|||||||
import { cn } from "#/utils/utils";
|
import { cn } from "#/utils/utils";
|
||||||
import { SubmitButton } from "#/components/shared/buttons/submit-button";
|
import { SubmitButton } from "#/components/shared/buttons/submit-button";
|
||||||
import { StopButton } from "#/components/shared/buttons/stop-button";
|
import { StopButton } from "#/components/shared/buttons/stop-button";
|
||||||
|
import { getValidImageFiles } from "#/utils/validate-image-type";
|
||||||
|
import { toast } from "#/utils/toast";
|
||||||
|
|
||||||
interface ChatInputProps {
|
interface ChatInputProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -46,13 +48,22 @@ export function ChatInput({
|
|||||||
const handlePaste = (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
const handlePaste = (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
||||||
// Only handle paste if we have an image paste handler and there are files
|
// Only handle paste if we have an image paste handler and there are files
|
||||||
if (onImagePaste && event.clipboardData.files.length > 0) {
|
if (onImagePaste && event.clipboardData.files.length > 0) {
|
||||||
const files = Array.from(event.clipboardData.files).filter((file) =>
|
const files = Array.from(event.clipboardData.files);
|
||||||
file.type.startsWith("image/"),
|
const { validFiles, invalidFiles } = getValidImageFiles(files);
|
||||||
);
|
|
||||||
// Only prevent default if we found image files to handle
|
if (invalidFiles.length > 0) {
|
||||||
if (files.length > 0) {
|
toast.error(
|
||||||
|
t(I18nKey.UPLOAD$UNSUPPORTED_IMAGE_TYPE, {
|
||||||
|
count: invalidFiles.length,
|
||||||
|
files: invalidFiles.map((f) => f.name).join(", "),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only prevent default if we found valid image files to handle
|
||||||
|
if (validFiles.length > 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onImagePaste(files);
|
onImagePaste(validFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For text paste, let the default behavior handle it
|
// For text paste, let the default behavior handle it
|
||||||
@@ -74,11 +85,20 @@ export function ChatInput({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setIsDraggingOver(false);
|
setIsDraggingOver(false);
|
||||||
if (onImagePaste && event.dataTransfer.files.length > 0) {
|
if (onImagePaste && event.dataTransfer.files.length > 0) {
|
||||||
const files = Array.from(event.dataTransfer.files).filter((file) =>
|
const files = Array.from(event.dataTransfer.files);
|
||||||
file.type.startsWith("image/"),
|
const { validFiles, invalidFiles } = getValidImageFiles(files);
|
||||||
);
|
|
||||||
if (files.length > 0) {
|
if (invalidFiles.length > 0) {
|
||||||
onImagePaste(files);
|
toast.error(
|
||||||
|
t(I18nKey.UPLOAD$UNSUPPORTED_IMAGE_TYPE, {
|
||||||
|
count: invalidFiles.length,
|
||||||
|
files: invalidFiles.map((f) => f.name).join(", "),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validFiles.length > 0) {
|
||||||
|
onImagePaste(validFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import Clip from "#/icons/clip.svg?react";
|
import Clip from "#/icons/clip.svg?react";
|
||||||
|
import { getValidImageFiles } from "#/utils/validate-image-type";
|
||||||
|
import { toast } from "#/utils/toast";
|
||||||
|
import { I18nKey } from "#/i18n/declaration";
|
||||||
|
|
||||||
interface UploadImageInputProps {
|
interface UploadImageInputProps {
|
||||||
onUpload: (files: File[]) => void;
|
onUpload: (files: File[]) => void;
|
||||||
@@ -6,8 +10,26 @@ interface UploadImageInputProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function UploadImageInput({ onUpload, label }: UploadImageInputProps) {
|
export function UploadImageInput({ onUpload, label }: UploadImageInputProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.files) onUpload(Array.from(event.target.files));
|
if (!event.target.files) return;
|
||||||
|
|
||||||
|
const files = Array.from(event.target.files);
|
||||||
|
const { validFiles, invalidFiles } = getValidImageFiles(files);
|
||||||
|
|
||||||
|
if (invalidFiles.length > 0) {
|
||||||
|
toast.error(
|
||||||
|
t(I18nKey.UPLOAD$UNSUPPORTED_IMAGE_TYPE, {
|
||||||
|
count: invalidFiles.length,
|
||||||
|
files: invalidFiles.map((f) => f.name).join(", "),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validFiles.length > 0) {
|
||||||
|
onUpload(validFiles);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -16,7 +38,7 @@ export function UploadImageInput({ onUpload, label }: UploadImageInputProps) {
|
|||||||
<input
|
<input
|
||||||
data-testid="upload-image-input"
|
data-testid="upload-image-input"
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/jpeg,image/png,image/gif,image/webp"
|
||||||
multiple
|
multiple
|
||||||
hidden
|
hidden
|
||||||
onChange={handleUpload}
|
onChange={handleUpload}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useNavigate } from "react-router";
|
|||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import OpenHands from "#/api/open-hands";
|
import OpenHands from "#/api/open-hands";
|
||||||
import { setInitialPrompt } from "#/state/initial-query-slice";
|
import { setInitialQuery } from "#/state/initial-query-slice";
|
||||||
import { RootState } from "#/store";
|
import { RootState } from "#/store";
|
||||||
import { useAuth } from "#/context/auth-context";
|
import { useAuth } from "#/context/auth-context";
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export const useCreateConversation = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (variables: { q?: string }) => {
|
mutationFn: (variables: { q?: string }) => {
|
||||||
if (
|
if (
|
||||||
!variables.q?.trim() &&
|
!variables.q?.trim() &&
|
||||||
!selectedRepository &&
|
!selectedRepository &&
|
||||||
@@ -28,13 +28,10 @@ export const useCreateConversation = () => {
|
|||||||
throw new Error("No query provided");
|
throw new Error("No query provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variables.q) dispatch(setInitialPrompt(variables.q));
|
if (variables.q) dispatch(setInitialQuery(variables.q));
|
||||||
|
|
||||||
return OpenHands.createConversation(
|
return OpenHands.createConversation(
|
||||||
gitHubToken || undefined,
|
gitHubToken || undefined,
|
||||||
selectedRepository || undefined,
|
selectedRepository || undefined,
|
||||||
variables.q,
|
|
||||||
files,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onSuccess: async ({ conversation_id: conversationId }, { q }) => {
|
onSuccess: async ({ conversation_id: conversationId }, { q }) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useConfig } from "./use-config";
|
|||||||
import OpenHands from "#/api/open-hands";
|
import OpenHands from "#/api/open-hands";
|
||||||
|
|
||||||
export const useGitHubUser = () => {
|
export const useGitHubUser = () => {
|
||||||
const { gitHubToken, setUserId, logout } = useAuth();
|
const { gitHubToken, setUserId } = useAuth();
|
||||||
const { data: config } = useConfig();
|
const { data: config } = useConfig();
|
||||||
|
|
||||||
const user = useQuery({
|
const user = useQuery({
|
||||||
@@ -29,11 +29,5 @@ export const useGitHubUser = () => {
|
|||||||
}
|
}
|
||||||
}, [user.data]);
|
}, [user.data]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (user.isError) {
|
|
||||||
logout();
|
|
||||||
}
|
|
||||||
}, [user.isError]);
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -419,6 +419,22 @@
|
|||||||
"ja": "コードエディタ",
|
"ja": "コードエディタ",
|
||||||
"tr": "Kod Editörü"
|
"tr": "Kod Editörü"
|
||||||
},
|
},
|
||||||
|
"UPLOAD$UNSUPPORTED_IMAGE_TYPE": {
|
||||||
|
"en": "Only JPEG, PNG, GIF, and WebP images are supported",
|
||||||
|
"zh-CN": "仅支持 JPEG、PNG、GIF 和 WebP 图片",
|
||||||
|
"de": "Nur JPEG-, PNG-, GIF- und WebP-Bilder werden unterstützt",
|
||||||
|
"ko-KR": "JPEG, PNG, GIF 및 WebP 이미지만 지원됩니다",
|
||||||
|
"no": "Kun JPEG, PNG, GIF og WebP bilder støttes",
|
||||||
|
"zh-TW": "僅支援 JPEG、PNG、GIF 和 WebP 圖片",
|
||||||
|
"ar": "يتم دعم صور JPEG و PNG و GIF و WebP فقط",
|
||||||
|
"fr": "Seules les images JPEG, PNG, GIF et WebP sont prises en charge",
|
||||||
|
"it": "Sono supportate solo immagini JPEG, PNG, GIF e WebP",
|
||||||
|
"pt": "Apenas imagens JPEG, PNG, GIF e WebP são suportadas",
|
||||||
|
"es": "Solo se admiten imágenes JPEG, PNG, GIF y WebP",
|
||||||
|
"ja": "JPEG、PNG、GIF、WebP画像のみがサポートされています",
|
||||||
|
"tr": "Yalnızca JPEG, PNG, GIF ve WebP görüntüleri desteklenir"
|
||||||
|
},
|
||||||
|
|
||||||
"WORKSPACE$BROWSER_TAB_LABEL": {
|
"WORKSPACE$BROWSER_TAB_LABEL": {
|
||||||
"en": "Browser",
|
"en": "Browser",
|
||||||
"zh-CN": "浏览器",
|
"zh-CN": "浏览器",
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export const handlers = [
|
|||||||
{ id: 2, full_name: "octocat/earth" },
|
{ id: 2, full_name: "octocat/earth" },
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
http.get("/api/github/user", () => {
|
http.get("https://api.github.com/user", () => {
|
||||||
const user: GitHubUser = {
|
const user: GitHubUser = {
|
||||||
id: 1,
|
id: 1,
|
||||||
login: "octocat",
|
login: "octocat",
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useWSStatusChange } from "./hooks/use-ws-status-change";
|
||||||
import { useHandleWSEvents } from "./hooks/use-handle-ws-events";
|
import { useHandleWSEvents } from "./hooks/use-handle-ws-events";
|
||||||
import { useHandleRuntimeActive } from "./hooks/use-handle-runtime-active";
|
import { useHandleRuntimeActive } from "./hooks/use-handle-runtime-active";
|
||||||
|
|
||||||
export function EventHandler({ children }: React.PropsWithChildren) {
|
export function EventHandler({ children }: React.PropsWithChildren) {
|
||||||
|
useWSStatusChange();
|
||||||
useHandleWSEvents();
|
useHandleWSEvents();
|
||||||
useHandleRuntimeActive();
|
useHandleRuntimeActive();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import {
|
||||||
|
useWsClient,
|
||||||
|
WsClientProviderStatus,
|
||||||
|
} from "#/context/ws-client-provider";
|
||||||
|
import { createChatMessage } from "#/services/chat-service";
|
||||||
|
import { setCurrentAgentState } from "#/state/agent-slice";
|
||||||
|
import { addUserMessage } from "#/state/chat-slice";
|
||||||
|
import { clearFiles, clearInitialQuery } from "#/state/initial-query-slice";
|
||||||
|
import { RootState } from "#/store";
|
||||||
|
import { AgentState } from "#/types/agent-state";
|
||||||
|
|
||||||
|
export const useWSStatusChange = () => {
|
||||||
|
const { send, status } = useWsClient();
|
||||||
|
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const statusRef = React.useRef<WsClientProviderStatus | null>(null);
|
||||||
|
|
||||||
|
const { files, initialQuery } = useSelector(
|
||||||
|
(state: RootState) => state.initialQuery,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sendInitialQuery = (query: string, base64Files: string[]) => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
send(createChatMessage(query, base64Files, timestamp));
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatchInitialQuery = (query: string) => {
|
||||||
|
sendInitialQuery(query, files);
|
||||||
|
dispatch(clearFiles()); // reset selected files
|
||||||
|
dispatch(clearInitialQuery()); // reset initial query
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAgentInit = () => {
|
||||||
|
if (initialQuery) {
|
||||||
|
dispatchInitialQuery(initialQuery);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (curAgentState === AgentState.INIT) {
|
||||||
|
handleAgentInit();
|
||||||
|
}
|
||||||
|
}, [curAgentState]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (statusRef.current === status) {
|
||||||
|
return; // This is a check because of strict mode - if the status did not change, don't do anything
|
||||||
|
}
|
||||||
|
statusRef.current = status;
|
||||||
|
|
||||||
|
if (status !== WsClientProviderStatus.DISCONNECTED && initialQuery) {
|
||||||
|
dispatch(
|
||||||
|
addUserMessage({
|
||||||
|
content: initialQuery,
|
||||||
|
imageUrls: files,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
pending: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === WsClientProviderStatus.DISCONNECTED) {
|
||||||
|
dispatch(setCurrentAgentState(AgentState.STOPPED));
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useDisclosure } from "@nextui-org/react";
|
import { useDisclosure } from "@nextui-org/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Outlet } from "react-router";
|
import { Outlet } from "react-router";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { FaServer } from "react-icons/fa";
|
import { FaServer } from "react-icons/fa";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
useConversation,
|
useConversation,
|
||||||
} from "#/context/conversation-context";
|
} from "#/context/conversation-context";
|
||||||
import { Controls } from "#/components/features/controls/controls";
|
import { Controls } from "#/components/features/controls/controls";
|
||||||
import { clearMessages, addUserMessage } from "#/state/chat-slice";
|
import { clearMessages } from "#/state/chat-slice";
|
||||||
import { clearTerminal } from "#/state/command-slice";
|
import { clearTerminal } from "#/state/command-slice";
|
||||||
import { useEffectOnce } from "#/hooks/use-effect-once";
|
import { useEffectOnce } from "#/hooks/use-effect-once";
|
||||||
import CodeIcon from "#/icons/code.svg?react";
|
import CodeIcon from "#/icons/code.svg?react";
|
||||||
@@ -36,8 +36,6 @@ import { ServedAppLabel } from "#/components/layout/served-app-label";
|
|||||||
import { TerminalStatusLabel } from "#/components/features/terminal/terminal-status-label";
|
import { TerminalStatusLabel } from "#/components/features/terminal/terminal-status-label";
|
||||||
import { useSettings } from "#/hooks/query/use-settings";
|
import { useSettings } from "#/hooks/query/use-settings";
|
||||||
import { MULTI_CONVERSATION_UI } from "#/utils/feature-flags";
|
import { MULTI_CONVERSATION_UI } from "#/utils/feature-flags";
|
||||||
import { clearFiles, clearInitialPrompt } from "#/state/initial-query-slice";
|
|
||||||
import { RootState } from "#/store";
|
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
useConversationConfig();
|
useConversationConfig();
|
||||||
@@ -48,9 +46,6 @@ function AppContent() {
|
|||||||
const { data: conversation, isFetched } = useUserConversation(
|
const { data: conversation, isFetched } = useUserConversation(
|
||||||
conversationId || null,
|
conversationId || null,
|
||||||
);
|
);
|
||||||
const { initialPrompt, files } = useSelector(
|
|
||||||
(state: RootState) => state.initialQuery,
|
|
||||||
);
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const endSession = useEndSession();
|
const endSession = useEndSession();
|
||||||
|
|
||||||
@@ -79,18 +74,6 @@ function AppContent() {
|
|||||||
dispatch(clearMessages());
|
dispatch(clearMessages());
|
||||||
dispatch(clearTerminal());
|
dispatch(clearTerminal());
|
||||||
dispatch(clearJupyter());
|
dispatch(clearJupyter());
|
||||||
if (conversationId && (initialPrompt || files.length > 0)) {
|
|
||||||
dispatch(
|
|
||||||
addUserMessage({
|
|
||||||
content: initialPrompt || "",
|
|
||||||
imageUrls: files || [],
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
pending: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
dispatch(clearInitialPrompt());
|
|
||||||
dispatch(clearFiles());
|
|
||||||
}
|
|
||||||
}, [conversationId]);
|
}, [conversationId]);
|
||||||
|
|
||||||
useEffectOnce(() => {
|
useEffectOnce(() => {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
|
|
||||||
type SliceState = {
|
type SliceState = {
|
||||||
files: string[]; // base64 encoded images
|
files: string[]; // base64 encoded images
|
||||||
initialPrompt: string | null;
|
initialQuery: string | null;
|
||||||
selectedRepository: string | null;
|
selectedRepository: string | null;
|
||||||
importedProjectZip: string | null; // base64 encoded zip
|
importedProjectZip: string | null; // base64 encoded zip
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: SliceState = {
|
const initialState: SliceState = {
|
||||||
files: [],
|
files: [],
|
||||||
initialPrompt: null,
|
initialQuery: null,
|
||||||
selectedRepository: null,
|
selectedRepository: null,
|
||||||
importedProjectZip: null,
|
importedProjectZip: null,
|
||||||
};
|
};
|
||||||
@@ -27,11 +27,11 @@ export const selectedFilesSlice = createSlice({
|
|||||||
clearFiles(state) {
|
clearFiles(state) {
|
||||||
state.files = [];
|
state.files = [];
|
||||||
},
|
},
|
||||||
setInitialPrompt(state, action: PayloadAction<string>) {
|
setInitialQuery(state, action: PayloadAction<string>) {
|
||||||
state.initialPrompt = action.payload;
|
state.initialQuery = action.payload;
|
||||||
},
|
},
|
||||||
clearInitialPrompt(state) {
|
clearInitialQuery(state) {
|
||||||
state.initialPrompt = null;
|
state.initialQuery = null;
|
||||||
},
|
},
|
||||||
setSelectedRepository(state, action: PayloadAction<string | null>) {
|
setSelectedRepository(state, action: PayloadAction<string | null>) {
|
||||||
state.selectedRepository = action.payload;
|
state.selectedRepository = action.payload;
|
||||||
@@ -49,8 +49,8 @@ export const {
|
|||||||
addFile,
|
addFile,
|
||||||
removeFile,
|
removeFile,
|
||||||
clearFiles,
|
clearFiles,
|
||||||
setInitialPrompt,
|
setInitialQuery,
|
||||||
clearInitialPrompt,
|
clearInitialQuery,
|
||||||
setSelectedRepository,
|
setSelectedRepository,
|
||||||
clearSelectedRepository,
|
clearSelectedRepository,
|
||||||
setImportedProjectZip,
|
setImportedProjectZip,
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
const SUPPORTED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] as const;
|
||||||
|
|
||||||
|
export function validateImageType(file: File): boolean {
|
||||||
|
return SUPPORTED_IMAGE_TYPES.includes(file.type as typeof SUPPORTED_IMAGE_TYPES[number]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getValidImageFiles(files: File[]): { validFiles: File[]; invalidFiles: File[] } {
|
||||||
|
const validFiles: File[] = [];
|
||||||
|
const invalidFiles: File[] = [];
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (validateImageType(file)) {
|
||||||
|
validFiles.push(file);
|
||||||
|
} else {
|
||||||
|
invalidFiles.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { validFiles, invalidFiles };
|
||||||
|
}
|
||||||
@@ -32,7 +32,6 @@ from openhands.events.tool import ToolCallMetadata
|
|||||||
_BASH_DESCRIPTION = """Execute a bash command in the terminal.
|
_BASH_DESCRIPTION = """Execute a bash command in the terminal.
|
||||||
* Long running commands: For commands that may run indefinitely, it should be run in the background and the output should be redirected to a file, e.g. command = `python3 app.py > server.log 2>&1 &`.
|
* Long running commands: For commands that may run indefinitely, it should be run in the background and the output should be redirected to a file, e.g. command = `python3 app.py > server.log 2>&1 &`.
|
||||||
* Interact with running process: If a bash command returns exit code `-1`, this means the process is not yet finished. By setting `is_input` to `true`, the assistant can interact with the running process and send empty `command` to retrieve any additional logs, or send additional text (set `command` to the text) to STDIN of the running process, or send command like `C-c` (Ctrl+C), `C-d` (Ctrl+D), `C-z` (Ctrl+Z) to interrupt the process.
|
* Interact with running process: If a bash command returns exit code `-1`, this means the process is not yet finished. By setting `is_input` to `true`, the assistant can interact with the running process and send empty `command` to retrieve any additional logs, or send additional text (set `command` to the text) to STDIN of the running process, or send command like `C-c` (Ctrl+C), `C-d` (Ctrl+D), `C-z` (Ctrl+Z) to interrupt the process.
|
||||||
* One command at a time: You can only execute one bash command at a time. If you need to run multiple commands sequentially, you can use `&&` or `;` to chain them together.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CmdRunTool = ChatCompletionToolParam(
|
CmdRunTool = ChatCompletionToolParam(
|
||||||
@@ -45,7 +44,7 @@ CmdRunTool = ChatCompletionToolParam(
|
|||||||
'properties': {
|
'properties': {
|
||||||
'command': {
|
'command': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': 'The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process. Note: You can only execute one bash command at a time. If you need to run multiple commands sequentially, you can use `&&` or `;` to chain them together.',
|
'description': 'The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process.',
|
||||||
},
|
},
|
||||||
'is_input': {
|
'is_input': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
|
|||||||
@@ -501,6 +501,10 @@ class AgentController:
|
|||||||
EventSource.ENVIRONMENT,
|
EventSource.ENVIRONMENT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if new_state == AgentState.INIT and self.state.resume_state:
|
||||||
|
await self.set_agent_state_to(self.state.resume_state)
|
||||||
|
self.state.resume_state = None
|
||||||
|
|
||||||
def get_agent_state(self) -> AgentState:
|
def get_agent_state(self) -> AgentState:
|
||||||
"""Returns the current state of the agent.
|
"""Returns the current state of the agent.
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ __all__ = ['ActionType']
|
|||||||
|
|
||||||
|
|
||||||
class ActionTypeSchema(BaseModel):
|
class ActionTypeSchema(BaseModel):
|
||||||
|
INIT: str = Field(default='initialize')
|
||||||
|
"""Initializes the agent. Only sent by client.
|
||||||
|
"""
|
||||||
|
|
||||||
MESSAGE: str = Field(default='message')
|
MESSAGE: str = Field(default='message')
|
||||||
"""Represents a message.
|
"""Represents a message.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ class AgentState(str, Enum):
|
|||||||
"""The agent is loading.
|
"""The agent is loading.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
INIT = 'init'
|
||||||
|
"""The agent is initialized.
|
||||||
|
"""
|
||||||
|
|
||||||
RUNNING = 'running'
|
RUNNING = 'running'
|
||||||
"""The agent is running.
|
"""The agent is running.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -567,6 +567,12 @@ def main():
|
|||||||
choices=['issue', 'pr'],
|
choices=['issue', 'pr'],
|
||||||
help='Type of issue to resolve, either open issue or pr comments.',
|
help='Type of issue to resolve, either open issue or pr comments.',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--target-branch',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Target branch to pull and create PR against (for PRs). If not specified, uses the PR's base branch.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--is-experimental',
|
'--is-experimental',
|
||||||
type=lambda x: x.lower() == 'true',
|
type=lambda x: x.lower() == 'true',
|
||||||
|
|||||||
@@ -306,12 +306,6 @@ class RemoteRuntime(ActionExecutionClient):
|
|||||||
assert 'pod_status' in runtime_data
|
assert 'pod_status' in runtime_data
|
||||||
pod_status = runtime_data['pod_status'].lower()
|
pod_status = runtime_data['pod_status'].lower()
|
||||||
self.log('debug', f'Pod status: {pod_status}')
|
self.log('debug', f'Pod status: {pod_status}')
|
||||||
restart_count = runtime_data.get('restart_count', 0)
|
|
||||||
if restart_count != 0:
|
|
||||||
restart_reasons = runtime_data.get('restart_reasons')
|
|
||||||
self.log(
|
|
||||||
'debug', f'Pod restarts: {restart_count}, reasons: {restart_reasons}'
|
|
||||||
)
|
|
||||||
|
|
||||||
# FIXME: We should fix it at the backend of /start endpoint, make sure
|
# FIXME: We should fix it at the backend of /start endpoint, make sure
|
||||||
# the pod is created before returning the response.
|
# the pod is created before returning the response.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from abc import ABC, abstractmethod
|
|||||||
import socketio
|
import socketio
|
||||||
|
|
||||||
from openhands.core.config import AppConfig
|
from openhands.core.config import AppConfig
|
||||||
from openhands.events.action import MessageAction
|
|
||||||
from openhands.events.stream import EventStream
|
from openhands.events.stream import EventStream
|
||||||
from openhands.server.session.conversation import Conversation
|
from openhands.server.session.conversation import Conversation
|
||||||
from openhands.server.settings import Settings
|
from openhands.server.settings import Settings
|
||||||
@@ -69,7 +68,7 @@ class ConversationManager(ABC):
|
|||||||
sid: str,
|
sid: str,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
user_id: str | None,
|
user_id: str | None,
|
||||||
initial_user_msg: MessageAction | None = None,
|
initial_user_msg: str | None = None,
|
||||||
) -> EventStream:
|
) -> EventStream:
|
||||||
"""Start an event loop if one is not already running"""
|
"""Start an event loop if one is not already running"""
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from openhands.core.config.app_config import AppConfig
|
|||||||
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
from openhands.core.schema.agent import AgentState
|
from openhands.core.schema.agent import AgentState
|
||||||
from openhands.events.action import MessageAction
|
|
||||||
from openhands.events.stream import EventStream, session_exists
|
from openhands.events.stream import EventStream, session_exists
|
||||||
from openhands.server.session.conversation import Conversation
|
from openhands.server.session.conversation import Conversation
|
||||||
from openhands.server.session.session import ROOM_KEY, Session
|
from openhands.server.session.session import ROOM_KEY, Session
|
||||||
@@ -187,7 +186,7 @@ class StandaloneConversationManager(ConversationManager):
|
|||||||
sid: str,
|
sid: str,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
user_id: str | None,
|
user_id: str | None,
|
||||||
initial_user_msg: MessageAction | None = None,
|
initial_user_msg: str | None = None,
|
||||||
) -> EventStream:
|
) -> EventStream:
|
||||||
logger.info(f'maybe_start_agent_loop:{sid}')
|
logger.info(f'maybe_start_agent_loop:{sid}')
|
||||||
session: Session | None = None
|
session: Session | None = None
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from pydantic import SecretStr
|
|||||||
from socketio.exceptions import ConnectionRefusedError
|
from socketio.exceptions import ConnectionRefusedError
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
|
from openhands.core.schema.agent import AgentState
|
||||||
from openhands.events.action import (
|
from openhands.events.action import (
|
||||||
NullAction,
|
NullAction,
|
||||||
)
|
)
|
||||||
@@ -85,6 +86,8 @@ async def connect(connection_id: str, environ, auth):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
elif isinstance(event, AgentStateChangedObservation):
|
elif isinstance(event, AgentStateChangedObservation):
|
||||||
|
if event.agent_state == AgentState.INIT:
|
||||||
|
await sio.emit('oh_event', event_to_dict(event), to=connection_id)
|
||||||
agent_state_changed = event
|
agent_state_changed = event
|
||||||
else:
|
else:
|
||||||
await sio.emit('oh_event', event_to_dict(event), to=connection_id)
|
await sio.emit('oh_event', event_to_dict(event), to=connection_id)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import uvicorn
|
|||||||
from fastapi import FastAPI, WebSocket
|
from fastapi import FastAPI, WebSocket
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
|
from openhands.core.schema import ActionType
|
||||||
from openhands.utils.shutdown_listener import should_continue
|
from openhands.utils.shutdown_listener import should_continue
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -10,6 +11,10 @@ app = FastAPI()
|
|||||||
@app.websocket('/ws')
|
@app.websocket('/ws')
|
||||||
async def websocket_endpoint(websocket: WebSocket):
|
async def websocket_endpoint(websocket: WebSocket):
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
|
# send message to mock connection
|
||||||
|
await websocket.send_json(
|
||||||
|
{'action': ActionType.INIT, 'message': 'Control loop started.'}
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while should_continue():
|
while should_continue():
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from fastapi.responses import JSONResponse
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
from openhands.events.action.message import MessageAction
|
|
||||||
from openhands.events.stream import EventStreamSubscriber
|
from openhands.events.stream import EventStreamSubscriber
|
||||||
from openhands.runtime import get_runtime_cls
|
from openhands.runtime import get_runtime_cls
|
||||||
from openhands.server.auth import get_user_id
|
from openhands.server.auth import get_user_id
|
||||||
@@ -35,7 +34,6 @@ class InitSessionRequest(BaseModel):
|
|||||||
github_token: str | None = None
|
github_token: str | None = None
|
||||||
selected_repository: str | None = None
|
selected_repository: str | None = None
|
||||||
initial_user_msg: str | None = None
|
initial_user_msg: str | None = None
|
||||||
image_urls: list[str] | None = None
|
|
||||||
|
|
||||||
|
|
||||||
async def _create_new_conversation(
|
async def _create_new_conversation(
|
||||||
@@ -43,7 +41,6 @@ async def _create_new_conversation(
|
|||||||
token: str | None,
|
token: str | None,
|
||||||
selected_repository: str | None,
|
selected_repository: str | None,
|
||||||
initial_user_msg: str | None,
|
initial_user_msg: str | None,
|
||||||
image_urls: list[str] | None,
|
|
||||||
):
|
):
|
||||||
logger.info('Loading settings')
|
logger.info('Loading settings')
|
||||||
settings_store = await SettingsStoreImpl.get_instance(config, user_id)
|
settings_store = await SettingsStoreImpl.get_instance(config, user_id)
|
||||||
@@ -97,14 +94,8 @@ async def _create_new_conversation(
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f'Starting agent loop for conversation {conversation_id}')
|
logger.info(f'Starting agent loop for conversation {conversation_id}')
|
||||||
initial_message_action = None
|
|
||||||
if initial_user_msg or image_urls:
|
|
||||||
initial_message_action = MessageAction(
|
|
||||||
content=initial_user_msg or '',
|
|
||||||
image_urls=image_urls or [],
|
|
||||||
)
|
|
||||||
event_stream = await conversation_manager.maybe_start_agent_loop(
|
event_stream = await conversation_manager.maybe_start_agent_loop(
|
||||||
conversation_id, conversation_init_data, user_id, initial_message_action
|
conversation_id, conversation_init_data, user_id, initial_user_msg
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
event_stream.subscribe(
|
event_stream.subscribe(
|
||||||
@@ -130,16 +121,10 @@ async def new_conversation(request: Request, data: InitSessionRequest):
|
|||||||
github_token = getattr(request.state, 'github_token', '') or data.github_token
|
github_token = getattr(request.state, 'github_token', '') or data.github_token
|
||||||
selected_repository = data.selected_repository
|
selected_repository = data.selected_repository
|
||||||
initial_user_msg = data.initial_user_msg
|
initial_user_msg = data.initial_user_msg
|
||||||
image_urls = data.image_urls or []
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create conversation with initial message
|
|
||||||
conversation_id = await _create_new_conversation(
|
conversation_id = await _create_new_conversation(
|
||||||
user_id,
|
user_id, github_token, selected_repository, initial_user_msg
|
||||||
github_token,
|
|
||||||
selected_repository,
|
|
||||||
initial_user_msg,
|
|
||||||
image_urls,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
|||||||
@@ -8,12 +8,19 @@ interruptions are recoverable.
|
|||||||
There are 3 main server side event handlers:
|
There are 3 main server side event handlers:
|
||||||
|
|
||||||
* `connect` - Invoked when a new connection to the server is established. (This may be via http or WebSocket)
|
* `connect` - Invoked when a new connection to the server is established. (This may be via http or WebSocket)
|
||||||
* `oh_action` - Invoked when a connected client sends an event (such as a prompt for the Agent) -
|
* `oh_action` - Invoked when a connected client sends an event (Such as `INIT` or a prompt for the Agent) -
|
||||||
this is distinct from the `oh_event` sent from the server to the client.
|
this is distinct from the `oh_event` sent from the server to the client.
|
||||||
* `disconnect` - Invoked when a connected client disconnects from the server.
|
* `disconnect` - Invoked when a connected client disconnects from the server.
|
||||||
|
|
||||||
|
## Init
|
||||||
|
Each connection has a unique id, and when initially established, is not associated with any session. An
|
||||||
|
`INIT` event must be sent to the server in order to attach a connection to a session. The `INIT` event
|
||||||
|
may optionally include a GitHub token and a token to connect to an existing session. (Which may be running
|
||||||
|
locally or may need to be hydrated). If no token is received as part of the init event, it is assumed a
|
||||||
|
new session should be started.
|
||||||
|
|
||||||
## Disconnect
|
## Disconnect
|
||||||
The (manager)[manager.py] manages connections and sessions. Each session may have zero or more connections
|
The (manager)[manager.py] manages connections and sessions. Each session may have zero or more connections
|
||||||
associated with it. When a session no longer has any
|
associated with it, managed by invocations of `INIT` and disconnect. When a session no longer has any
|
||||||
connections associated with it, after a set amount of time (determined by `config.sandbox.close_delay`),
|
connections associated with it, after a set amount of time (determined by `config.sandbox.close_delay`),
|
||||||
the session and runtime are passivated (So will need to be rehydrated to continue.)
|
the session and runtime are passivated (So will need to be rehydrated to continue.)
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ from openhands.core.config import AgentConfig, AppConfig, LLMConfig
|
|||||||
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
from openhands.core.schema.agent import AgentState
|
from openhands.core.schema.agent import AgentState
|
||||||
from openhands.events.action import ChangeAgentStateAction, MessageAction
|
from openhands.events.action import ChangeAgentStateAction
|
||||||
|
from openhands.events.action.message import MessageAction
|
||||||
from openhands.events.event import EventSource
|
from openhands.events.event import EventSource
|
||||||
from openhands.events.stream import EventStream
|
from openhands.events.stream import EventStream
|
||||||
from openhands.microagent import BaseMicroAgent
|
from openhands.microagent import BaseMicroAgent
|
||||||
@@ -71,7 +72,7 @@ class AgentSession:
|
|||||||
agent_configs: dict[str, AgentConfig] | None = None,
|
agent_configs: dict[str, AgentConfig] | None = None,
|
||||||
github_token: str | None = None,
|
github_token: str | None = None,
|
||||||
selected_repository: str | None = None,
|
selected_repository: str | None = None,
|
||||||
initial_message: MessageAction | None = None,
|
initial_user_msg: str | None = None,
|
||||||
):
|
):
|
||||||
"""Starts the Agent session
|
"""Starts the Agent session
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -110,17 +111,15 @@ class AgentSession:
|
|||||||
agent_to_llm_config=agent_to_llm_config,
|
agent_to_llm_config=agent_to_llm_config,
|
||||||
agent_configs=agent_configs,
|
agent_configs=agent_configs,
|
||||||
)
|
)
|
||||||
if initial_message:
|
self.event_stream.add_event(
|
||||||
self.event_stream.add_event(initial_message, EventSource.USER)
|
ChangeAgentStateAction(AgentState.INIT), EventSource.ENVIRONMENT
|
||||||
self.event_stream.add_event(
|
)
|
||||||
ChangeAgentStateAction(AgentState.RUNNING), EventSource.ENVIRONMENT
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.event_stream.add_event(
|
|
||||||
ChangeAgentStateAction(AgentState.AWAITING_USER_INPUT),
|
|
||||||
EventSource.ENVIRONMENT,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
if initial_user_msg:
|
||||||
|
self.event_stream.add_event(
|
||||||
|
MessageAction(content=initial_user_msg), EventSource.USER
|
||||||
|
)
|
||||||
|
|
||||||
self._starting = False
|
self._starting = False
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from openhands.core.config import AppConfig
|
|||||||
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
from openhands.core.exceptions import AgentRuntimeUnavailableError
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
from openhands.core.schema.agent import AgentState
|
from openhands.core.schema.agent import AgentState
|
||||||
from openhands.events.action import MessageAction
|
|
||||||
from openhands.events.stream import EventStream, session_exists
|
from openhands.events.stream import EventStream, session_exists
|
||||||
from openhands.server.session.agent_session import WAIT_TIME_BEFORE_CLOSE
|
from openhands.server.session.agent_session import WAIT_TIME_BEFORE_CLOSE
|
||||||
from openhands.server.session.conversation import Conversation
|
from openhands.server.session.conversation import Conversation
|
||||||
@@ -447,7 +446,7 @@ class SessionManager:
|
|||||||
sid: str,
|
sid: str,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
user_id: str | None,
|
user_id: str | None,
|
||||||
initial_message: MessageAction | None = None,
|
initial_user_msg: str | None = None,
|
||||||
) -> EventStream:
|
) -> EventStream:
|
||||||
logger.info(f'maybe_start_agent_loop:{sid}')
|
logger.info(f'maybe_start_agent_loop:{sid}')
|
||||||
session: Session | None = None
|
session: Session | None = None
|
||||||
@@ -470,7 +469,7 @@ class SessionManager:
|
|||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
)
|
)
|
||||||
self._local_agent_loops_by_sid[sid] = session
|
self._local_agent_loops_by_sid[sid] = session
|
||||||
asyncio.create_task(session.initialize_agent(settings, initial_message))
|
asyncio.create_task(session.initialize_agent(settings, initial_user_msg))
|
||||||
|
|
||||||
event_stream = await self._get_event_stream(sid)
|
event_stream = await self._get_event_stream(sid)
|
||||||
if not event_stream:
|
if not event_stream:
|
||||||
|
|||||||
@@ -74,9 +74,7 @@ class Session:
|
|||||||
self.is_alive = False
|
self.is_alive = False
|
||||||
await self.agent_session.close()
|
await self.agent_session.close()
|
||||||
|
|
||||||
async def initialize_agent(
|
async def initialize_agent(self, settings: Settings, initial_user_msg: str | None):
|
||||||
self, settings: Settings, initial_message: MessageAction | None
|
|
||||||
):
|
|
||||||
self.agent_session.event_stream.add_event(
|
self.agent_session.event_stream.add_event(
|
||||||
AgentStateChangedObservation('', AgentState.LOADING),
|
AgentStateChangedObservation('', AgentState.LOADING),
|
||||||
EventSource.ENVIRONMENT,
|
EventSource.ENVIRONMENT,
|
||||||
@@ -124,7 +122,7 @@ class Session:
|
|||||||
agent_configs=self.config.get_agent_configs(),
|
agent_configs=self.config.get_agent_configs(),
|
||||||
github_token=github_token,
|
github_token=github_token,
|
||||||
selected_repository=selected_repository,
|
selected_repository=selected_repository,
|
||||||
initial_message=initial_message,
|
initial_user_msg=initial_user_msg,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f'Error creating agent_session: {e}')
|
logger.exception(f'Error creating agent_session: {e}')
|
||||||
|
|||||||
Generated
+21
-32
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohappyeyeballs"
|
name = "aiohappyeyeballs"
|
||||||
@@ -1108,20 +1108,6 @@ humanfriendly = ">=9.1"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
cron = ["capturer (>=2.4)"]
|
cron = ["capturer (>=2.4)"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colormath"
|
|
||||||
version = "3.0.0"
|
|
||||||
description = "Color math and conversion library."
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "colormath-3.0.0.tar.gz", hash = "sha256:3d4605af344527da0e4f9f504fad7ddbebda35322c566a6c72e28edb1ff31217"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
networkx = ">=2.0"
|
|
||||||
numpy = "*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "comm"
|
name = "comm"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -3958,19 +3944,19 @@ pydantic = ">=1.10"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llama-index"
|
name = "llama-index"
|
||||||
version = "0.12.13"
|
version = "0.12.12"
|
||||||
description = "Interface between LLMs and your data"
|
description = "Interface between LLMs and your data"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.9"
|
python-versions = "<4.0,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "llama_index-0.12.13-py3-none-any.whl", hash = "sha256:0b285aa451ced6bd8da40df99068ac96badf8b5725c4edc29f2bce4da2ffd8bc"},
|
{file = "llama_index-0.12.12-py3-none-any.whl", hash = "sha256:208f77dba5fd8268cacd3d56ec3ee33b0001d5b6ec623c5b91c755af7b08cfae"},
|
||||||
{file = "llama_index-0.12.13.tar.gz", hash = "sha256:1e39a397dcc51dabe280c121fd8d5451a6a84595233a8b26caa54d9b7ecf9ffc"},
|
{file = "llama_index-0.12.12.tar.gz", hash = "sha256:d4e475726e342b1178736ae3ed93336fe114605e86431b6dfcb454a9e1f26e72"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
llama-index-agent-openai = ">=0.4.0,<0.5.0"
|
llama-index-agent-openai = ">=0.4.0,<0.5.0"
|
||||||
llama-index-cli = ">=0.4.0,<0.5.0"
|
llama-index-cli = ">=0.4.0,<0.5.0"
|
||||||
llama-index-core = ">=0.12.13,<0.13.0"
|
llama-index-core = ">=0.12.12,<0.13.0"
|
||||||
llama-index-embeddings-openai = ">=0.3.0,<0.4.0"
|
llama-index-embeddings-openai = ">=0.3.0,<0.4.0"
|
||||||
llama-index-indices-managed-llama-cloud = ">=0.4.0"
|
llama-index-indices-managed-llama-cloud = ">=0.4.0"
|
||||||
llama-index-llms-openai = ">=0.3.0,<0.4.0"
|
llama-index-llms-openai = ">=0.3.0,<0.4.0"
|
||||||
@@ -4015,13 +4001,13 @@ llama-index-llms-openai = ">=0.3.0,<0.4.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llama-index-core"
|
name = "llama-index-core"
|
||||||
version = "0.12.13"
|
version = "0.12.12"
|
||||||
description = "Interface between LLMs and your data"
|
description = "Interface between LLMs and your data"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.9"
|
python-versions = "<4.0,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "llama_index_core-0.12.13-py3-none-any.whl", hash = "sha256:9708bb594bbddffd6ff0767242e49d8978d1ba60a2e62e071d9d123ad2f17e6f"},
|
{file = "llama_index_core-0.12.12-py3-none-any.whl", hash = "sha256:cea491e87f65e6b775b5aef95720de302b85af1bdc67d779c4b09170a30e5b98"},
|
||||||
{file = "llama_index_core-0.12.13.tar.gz", hash = "sha256:77af0161246ce1de38efc17cb6438dfff9e9558af00bcfac7dd4d0b7325efa4b"},
|
{file = "llama_index_core-0.12.12.tar.gz", hash = "sha256:068b755bbc681731336e822f5977d7608585e8f759c6293ebd812e2659316a37"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -5483,7 +5469,6 @@ description = "Nvidia JIT LTO Library"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3"
|
python-versions = ">=3"
|
||||||
files = [
|
files = [
|
||||||
{file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"},
|
|
||||||
{file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"},
|
{file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"},
|
||||||
{file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"},
|
{file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"},
|
||||||
]
|
]
|
||||||
@@ -5596,17 +5581,15 @@ realtime = ["websockets (>=13,<15)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openhands-aci"
|
name = "openhands-aci"
|
||||||
version = "0.1.9"
|
version = "0.1.8"
|
||||||
description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands."
|
description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.12"
|
python-versions = "^3.12"
|
||||||
files = [
|
files = []
|
||||||
{file = "openhands_aci-0.1.9-py3-none-any.whl", hash = "sha256:62af189878db046aa98475a41fa01200efd5ddf1db8a435c38da3d4ad32cb11a"},
|
develop = false
|
||||||
{file = "openhands_aci-0.1.9.tar.gz", hash = "sha256:690d33d355a3e4111f52861dbb96ff766b5a268202324a87c94ba67b628a63b1"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
diskcache = ">=5.6.3,<6.0.0"
|
diskcache = "^5.6.3"
|
||||||
flake8 = "*"
|
flake8 = "*"
|
||||||
gitpython = "*"
|
gitpython = "*"
|
||||||
grep-ast = "0.3.3"
|
grep-ast = "0.3.3"
|
||||||
@@ -5616,7 +5599,13 @@ numpy = "*"
|
|||||||
pandas = "*"
|
pandas = "*"
|
||||||
scipy = "*"
|
scipy = "*"
|
||||||
tree-sitter = "0.21.3"
|
tree-sitter = "0.21.3"
|
||||||
whatthepatch = ">=1.0.6,<2.0.0"
|
whatthepatch = "^1.0.6"
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "git"
|
||||||
|
url = "https://github.com/All-Hands-AI/openhands-aci.git"
|
||||||
|
reference = "fix-find-show-only-hidden-subpaths"
|
||||||
|
resolved_reference = "910e8c470aff0e496bf262bc673c7ee7b4531159"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-api"
|
name = "opentelemetry-api"
|
||||||
@@ -10130,4 +10119,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "fbca4b2ca0fe2d1d3cac46164c0c1eb9e468dc6f6bc7165e9a3d62ea9f25d801"
|
content-hash = "6b74056694bdc84a4583c2f93a5b218f15688827cb59e289eb83331045a1582e"
|
||||||
|
|||||||
+1
-3
@@ -65,7 +65,7 @@ runloop-api-client = "0.13.0"
|
|||||||
libtmux = ">=0.37,<0.40"
|
libtmux = ">=0.37,<0.40"
|
||||||
pygithub = "^2.5.0"
|
pygithub = "^2.5.0"
|
||||||
joblib = "*"
|
joblib = "*"
|
||||||
openhands-aci = "0.1.9"
|
openhands-aci = "0.1.8"
|
||||||
python-socketio = "^5.11.4"
|
python-socketio = "^5.11.4"
|
||||||
redis = "^5.2.0"
|
redis = "^5.2.0"
|
||||||
sse-starlette = "^2.1.3"
|
sse-starlette = "^2.1.3"
|
||||||
@@ -101,7 +101,6 @@ reportlab = "*"
|
|||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
concurrency = ["gevent"]
|
concurrency = ["gevent"]
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.runtime.dependencies]
|
[tool.poetry.group.runtime.dependencies]
|
||||||
jupyterlab = "*"
|
jupyterlab = "*"
|
||||||
notebook = "*"
|
notebook = "*"
|
||||||
@@ -130,7 +129,6 @@ ignore = ["D1"]
|
|||||||
[tool.ruff.lint.pydocstyle]
|
[tool.ruff.lint.pydocstyle]
|
||||||
convention = "google"
|
convention = "google"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.evaluation.dependencies]
|
[tool.poetry.group.evaluation.dependencies]
|
||||||
streamlit = "*"
|
streamlit = "*"
|
||||||
whatthepatch = "*"
|
whatthepatch = "*"
|
||||||
|
|||||||
Reference in New Issue
Block a user