bizydraft 0.2.68.dev20250930064514__py3-none-any.whl → 0.2.68.dev20250930074209__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bizydraft might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  from aiohttp import web
2
2
  from loguru import logger
3
3
 
4
- from bizydraft.oss_utils import upload_image
4
+ from bizydraft.oss_utils import upload_image, upload_mask
5
5
  from bizydraft.patch_handlers import post_prompt, view_image
6
6
 
7
7
  try:
@@ -23,10 +23,12 @@ def hijack_routes_pre_add_routes():
23
23
  ("/view", "GET"): view_image,
24
24
  ("/prompt", "POST"): post_prompt,
25
25
  ("/upload/image", "POST"): upload_image,
26
+ ("/upload/mask", "POST"): upload_mask,
26
27
  # /api alias
27
28
  ("/api/view", "GET"): view_image,
28
29
  ("/api/prompt", "POST"): post_prompt,
29
30
  ("/api/upload/image", "POST"): upload_image,
31
+ ("/api/upload/mask", "POST"): upload_mask,
30
32
  }
31
33
 
32
34
  async def middleware_handler(request):
@@ -52,6 +54,7 @@ def hijack_routes_pre_add_routes():
52
54
  "/prompt",
53
55
  "/view",
54
56
  "/upload/image",
57
+ "/upload/mask",
55
58
  "/",
56
59
  "/ws",
57
60
  "/extensions",
bizydraft/oss_utils.py CHANGED
@@ -19,6 +19,8 @@ from werkzeug.utils import secure_filename
19
19
 
20
20
  from bizydraft.env import BIZYAIR_API_KEY, BIZYDRAFT_SERVER
21
21
 
22
+ CLIPSPACE_TO_OSS_MAPPING = {}
23
+
22
24
  private_key_pem = os.getenv(
23
25
  "RSA_PRIVATE_KEY",
24
26
  """-----BEGIN RSA PRIVATE KEY-----
@@ -176,6 +178,7 @@ async def upload_to_oss(post, api_key: str):
176
178
  if not (image and image.file):
177
179
  return web.Response(status=400)
178
180
 
181
+ original_frontend_filename = image.filename # 保存前端发送的原始文件名
179
182
  filename = image.filename
180
183
  if not filename:
181
184
  return web.Response(status=400)
@@ -196,8 +199,23 @@ async def upload_to_oss(post, api_key: str):
196
199
  except Exception as e:
197
200
  logger.error(f"Commit file failed: {e}")
198
201
  return web.Response(status=500, text=str(e))
202
+
203
+ # 将 OSS URL 拆分成 filename 和 subfolder,以便前端正确构建 /api/view 请求
204
+ # 例如: https://bizyair-prod.oss-cn-shanghai.aliyuncs.com/inputs/20250930/file.png
205
+ oss_url = result["url"]
206
+ oss_filename = oss_url.split("/")[-1] # 获取最后一部分作为文件名
207
+ oss_subfolder = "/".join(
208
+ oss_url.split("/")[:-1]
209
+ ) # 获取除文件名外的部分作为 subfolder
210
+
211
+ if original_frontend_filename:
212
+ CLIPSPACE_TO_OSS_MAPPING[original_frontend_filename] = oss_url
213
+ logger.info(
214
+ f"[OSS_MAPPING] Cached mapping: {original_frontend_filename} -> {oss_url}"
215
+ )
216
+
199
217
  return web.json_response(
200
- {"name": result["url"], "subfolder": subfolder, "type": image_upload_type}
218
+ {"name": oss_filename, "subfolder": oss_subfolder, "type": image_upload_type}
201
219
  )
202
220
 
203
221
 
@@ -234,6 +252,165 @@ async def upload_image(request):
234
252
  return await upload_to_oss(post, api_key)
235
253
 
236
254
 
255
+ async def upload_mask(request):
256
+ """
257
+ 处理 mask editor 上传,将带 alpha 通道的图片上传到 OSS
258
+ """
259
+ import io
260
+ import json
261
+ import tempfile
262
+
263
+ from PIL import Image
264
+ from PIL.PngImagePlugin import PngInfo
265
+
266
+ api_key = get_api_key(request)
267
+ if not api_key:
268
+ logger.error("[UPLOAD_MASK] No API key found")
269
+ return web.Response(status=403, text="No validated key found")
270
+
271
+ post = await request.post()
272
+
273
+ # 获取上传的 mask 图片
274
+ mask_image = post.get("image")
275
+ if not (mask_image and mask_image.file):
276
+ logger.error("[UPLOAD_MASK] No image provided in request")
277
+ return web.Response(status=400, text="No image provided")
278
+
279
+ # 保存前端发送的原始文件名,用于后续缓存映射
280
+ original_frontend_filename = mask_image.filename
281
+
282
+ # 获取原始图片引用
283
+ original_ref_str = post.get("original_ref")
284
+
285
+ if not original_ref_str:
286
+ # 如果没有 original_ref,直接上传 mask
287
+ return await upload_to_oss(post, api_key)
288
+
289
+ try:
290
+ from urllib.parse import unquote
291
+
292
+ original_ref = json.loads(original_ref_str)
293
+ original_filename = original_ref.get("filename")
294
+ original_subfolder = original_ref.get("subfolder", "")
295
+
296
+ if not original_filename:
297
+ logger.error("[UPLOAD_MASK] No filename in original_ref")
298
+ return web.Response(status=400, text="No filename in original_ref")
299
+
300
+ # 构建完整的 OSS URL(类似 view_image 的逻辑)
301
+ http_prefix_options = ("http:", "https:")
302
+
303
+ if "http" in original_subfolder:
304
+ # subfolder 中包含 URL 基础路径
305
+ original_subfolder = original_subfolder[original_subfolder.find("http") :]
306
+ original_subfolder = unquote(original_subfolder).replace(
307
+ "https:/", "https://"
308
+ )
309
+ original_url = f"{original_subfolder}/{original_filename}"
310
+ elif original_filename.startswith(http_prefix_options):
311
+ # filename 本身就是完整 URL
312
+ original_url = original_filename
313
+ elif (
314
+ original_subfolder == "clipspace"
315
+ and original_filename in CLIPSPACE_TO_OSS_MAPPING
316
+ ):
317
+ # 检查缓存:如果是 clipspace 文件且在缓存中,使用缓存的 OSS URL
318
+ original_url = CLIPSPACE_TO_OSS_MAPPING[original_filename]
319
+ else:
320
+ # 不是 OSS URL 格式且不在缓存中,直接上传 mask 图片
321
+ return await upload_to_oss(post, api_key)
322
+
323
+ async with aiohttp.ClientSession() as session:
324
+ async with session.get(original_url) as resp:
325
+ if resp.status != 200:
326
+ logger.error(
327
+ f"[UPLOAD_MASK] Failed to download original image: {resp.status}"
328
+ )
329
+ return web.Response(
330
+ status=502,
331
+ text=f"Failed to download original image: {resp.status}",
332
+ )
333
+ original_image_data = await resp.read()
334
+
335
+ # 处理图片:应用 alpha 通道
336
+ with Image.open(io.BytesIO(original_image_data)) as original_pil:
337
+ # 保存元数据
338
+ metadata = PngInfo()
339
+ if hasattr(original_pil, "text"):
340
+ for key in original_pil.text:
341
+ metadata.add_text(key, original_pil.text[key])
342
+
343
+ # 转换为 RGBA
344
+ original_pil = original_pil.convert("RGBA")
345
+
346
+ # 读取上传的 mask
347
+ mask_pil = Image.open(mask_image.file).convert("RGBA")
348
+
349
+ # alpha copy - 从 mask 提取 alpha 通道并应用到原图
350
+ new_alpha = mask_pil.getchannel("A")
351
+ original_pil.putalpha(new_alpha)
352
+
353
+ # 保存到临时文件
354
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
355
+ tmp_filepath = tmp_file.name
356
+ original_pil.save(tmp_filepath, compress_level=4, pnginfo=metadata)
357
+
358
+ # 准备上传到 OSS
359
+ filename = f"clipspace-mask-{uuid.uuid4().hex[:8]}.png"
360
+ # subfolder = post.get("subfolder", "clipspace")
361
+ image_upload_type = post.get("type", "input")
362
+
363
+ try:
364
+ # 获取上传 token
365
+ oss_token = await get_upload_token(filename, api_key)
366
+
367
+ # 读取临时文件并上传
368
+ with open(tmp_filepath, "rb") as f:
369
+ # 创建一个类似 FileField 的对象
370
+ class FileFieldLike:
371
+ def __init__(self, file_obj, filename, content_type):
372
+ self.file = file_obj
373
+ self.filename = filename
374
+ self.content_type = content_type
375
+
376
+ file_field = FileFieldLike(f, filename, "image/png")
377
+ result = await upload_filefield_to_oss(file_field, oss_token)
378
+
379
+ if result["status"] != 200:
380
+ logger.error(f"[UPLOAD_MASK] Upload failed: {result.get('reason', '')}")
381
+ return web.Response(
382
+ status=result["status"], text=result.get("reason", "")
383
+ )
384
+
385
+ # Commit file
386
+ object_key = oss_token["data"]["file"]["object_key"]
387
+ await commit_file(object_key, filename, api_key)
388
+
389
+ # 将 OSS URL 拆分成 filename 和 subfolder,以便前端正确构建 /api/view 请求
390
+ oss_url = result["url"]
391
+ oss_filename = oss_url.split("/")[-1]
392
+ oss_subfolder = "/".join(oss_url.split("/")[:-1])
393
+
394
+ if original_frontend_filename:
395
+ CLIPSPACE_TO_OSS_MAPPING[original_frontend_filename] = oss_url
396
+
397
+ response_data = {
398
+ "name": oss_filename,
399
+ "subfolder": oss_subfolder,
400
+ "type": image_upload_type,
401
+ }
402
+ return web.json_response(response_data)
403
+
404
+ finally:
405
+ # 清理临时文件
406
+ if os.path.exists(tmp_filepath):
407
+ os.remove(tmp_filepath)
408
+
409
+ except Exception as e:
410
+ logger.error(f"[UPLOAD_MASK] ERROR processing mask upload: {e}", exc_info=True)
411
+ return web.Response(status=500, text=f"Error processing mask: {str(e)}")
412
+
413
+
237
414
  def _should_clean(name: str) -> bool:
238
415
  """True -> 乱码;False -> 正常"""
239
416
  # 主名部分含 URL 参数符号且最后有扩展名
@@ -246,4 +423,4 @@ def clean_filename(bad: str) -> (bool, str):
246
423
  return False, bad
247
424
  # 提取最后扩展名(含点)
248
425
  ext = re.search(r"(\.[\w]+)$", bad)
249
- return True, ext.group(1) if ext else bad # 理论上不会没有扩展名
426
+ return True, ext.group(1) if ext else bad
@@ -29,6 +29,8 @@ BIZYDRAFT_CHUNK_SIZE = int(os.getenv("BIZYDRAFT_CHUNK_SIZE", 1024 * 16)) # 16KB
29
29
 
30
30
 
31
31
  async def view_image(request):
32
+ from bizydraft.oss_utils import CLIPSPACE_TO_OSS_MAPPING
33
+
32
34
  logger.debug(f"Received request for /view with query: {request.rel_url.query}")
33
35
  if "filename" not in request.rel_url.query:
34
36
  logger.warning("'filename' not provided in query string, returning 404")
@@ -37,6 +39,12 @@ async def view_image(request):
37
39
  filename = request.rel_url.query["filename"]
38
40
  subfolder = request.rel_url.query.get("subfolder", "")
39
41
 
42
+ if subfolder == "clipspace" and filename in CLIPSPACE_TO_OSS_MAPPING:
43
+ oss_url = CLIPSPACE_TO_OSS_MAPPING[filename]
44
+ logger.info(f"[OSS_MAPPING] Found clipspace mapping: {filename} -> {oss_url}")
45
+ filename = oss_url
46
+ subfolder = "" # Clear subfolder since filename is now the full URL
47
+
40
48
  http_prefix_options = ("http:", "https:")
41
49
 
42
50
  if not filename.startswith(http_prefix_options) and "http" not in subfolder:
@@ -0,0 +1,131 @@
1
+ import { app } from "../../scripts/app.js";
2
+ import { api } from "../../scripts/api.js";
3
+
4
+ window.CLIPSPACE_TO_OSS_MAP = window.CLIPSPACE_TO_OSS_MAP || {};
5
+
6
+
7
+ const originalFetchApi = api.fetchApi;
8
+ api.fetchApi = async function(url, options) {
9
+ const response = await originalFetchApi.call(this, url, options);
10
+ console.log(url,'url--------------------');
11
+ if ((url==='/upload/image' || url==='/upload/mask') && response.ok) {
12
+ try {
13
+ const clonedResponse = response.clone();
14
+ const data = await clonedResponse.json();
15
+
16
+ if (data && data.name && data.subfolder) {
17
+ if (options && options.body && options.body instanceof FormData) {
18
+ const imageFile = options.body.get('image');
19
+ if (imageFile && imageFile.name) {
20
+ const originalFilename = imageFile.name;
21
+
22
+ let ossUrl;
23
+ if (data.name.startsWith('http://') || data.name.startsWith('https://')) {
24
+ ossUrl = data.name;
25
+ } else if (data.subfolder && (data.subfolder.includes('http://') || data.subfolder.includes('https://'))) {
26
+ ossUrl = `${data.subfolder}/${data.name}`;
27
+ } else {
28
+ return response;
29
+ }
30
+
31
+ window.CLIPSPACE_TO_OSS_MAP[originalFilename] = ossUrl;
32
+
33
+ const filenameWithoutSuffix = originalFilename.replace(/ \[(input|output)\]$/, '');
34
+ if (filenameWithoutSuffix !== originalFilename) {
35
+ window.CLIPSPACE_TO_OSS_MAP[filenameWithoutSuffix] = ossUrl;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ } catch (e) {
41
+ console.warn('[BizyDraft ClipspaceToOss] Failed to parse upload response:', e);
42
+ }
43
+ }
44
+
45
+ return response;
46
+ };
47
+
48
+ /**
49
+ * Convert clipspace paths to OSS URLs in a prompt object
50
+ * @param {Object} prompt - The prompt object to process
51
+ * @returns {Object} The processed prompt object
52
+ */
53
+ function convertClipspacePathsInPrompt(prompt) {
54
+ if (!prompt || typeof prompt !== 'object') {
55
+ return prompt;
56
+ }
57
+
58
+ let conversionsCount = 0;
59
+
60
+ // Iterate through all nodes in the prompt
61
+ for (const [nodeId, node] of Object.entries(prompt)) {
62
+ if (!node || !node.inputs) {
63
+ continue;
64
+ }
65
+
66
+ // Check all input values
67
+ for (const [inputKey, inputValue] of Object.entries(node.inputs)) {
68
+ if (typeof inputValue === 'string' && inputValue.includes('clipspace')) {
69
+ // Extract the filename from paths like:
70
+ // "clipspace/clipspace-mask-12345.png [input]"
71
+ // "clipspace/clipspace-painted-masked-12345.png [input]"
72
+ const match = inputValue.match(/clipspace\/([\w-]+\.(?:png|jpg|jpeg|webp|gif))/i);
73
+ if (match) {
74
+ const filename = match[1]; // e.g., "clipspace-mask-12345.png"
75
+
76
+ // Look for this filename in our mapping (with or without [input]/[output] suffix)
77
+ const filenameWithInput = `${filename} [input]`;
78
+ const filenameWithOutput = `${filename} [output]`;
79
+
80
+ let ossUrl = window.CLIPSPACE_TO_OSS_MAP[filename]
81
+ || window.CLIPSPACE_TO_OSS_MAP[filenameWithInput]
82
+ || window.CLIPSPACE_TO_OSS_MAP[filenameWithOutput];
83
+
84
+ if (ossUrl) {
85
+ console.log(`[BizyDraft ClipspaceToOss] Converting: ${inputValue} -> ${ossUrl}`);
86
+ node.inputs[inputKey] = ossUrl;
87
+
88
+ // Also update image_name if it exists
89
+ if (inputKey === 'image' && node.inputs['image_name']) {
90
+ // Extract just the filename from the OSS URL
91
+ const ossFilename = ossUrl.split('/').pop();
92
+ node.inputs['image_name'] = ossFilename;
93
+ console.log(`[BizyDraft ClipspaceToOss] Also updated image_name to: ${ossFilename}`);
94
+ }
95
+
96
+ conversionsCount++;
97
+ } else {
98
+ console.warn(`[BizyDraft ClipspaceToOss] No OSS URL found for clipspace file: ${filename}`);
99
+ console.warn('[BizyDraft ClipspaceToOss] Available mappings:', Object.keys(window.CLIPSPACE_TO_OSS_MAP));
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ if (conversionsCount > 0) {
107
+ console.log(`[BizyDraft ClipspaceToOss] Converted ${conversionsCount} clipspace path(s) to OSS URLs`);
108
+ }
109
+
110
+ return prompt;
111
+ }
112
+
113
+ app.registerExtension({
114
+ name: "bizyair.clipspace.to.oss",
115
+
116
+ async setup() {
117
+ const originalGraphToPrompt = app.graphToPrompt;
118
+
119
+ app.graphToPrompt = async function(...args) {
120
+ console.log('[BizyDraft ClipspaceToOss] graphToPrompt called, intercepting...');
121
+
122
+ const result = await originalGraphToPrompt.apply(this, args);
123
+
124
+ if (result && result.output) {
125
+ result.output = convertClipspacePathsInPrompt(result.output);
126
+ }
127
+
128
+ return result;
129
+ };
130
+ }
131
+ });
@@ -2,3 +2,4 @@
2
2
  import "./hookLoadImage.js";
3
3
  import "./postEvent.js";
4
4
  import "./handleStyle.js";
5
+ import "./clipspaceToOss.js";
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizydraft
3
- Version: 0.2.68.dev20250930064514
3
+ Version: 0.2.68.dev20250930074209
4
4
  Summary: bizydraft
5
5
  Requires-Dist: loguru
6
6
  Requires-Dist: aiohttp
@@ -2,20 +2,21 @@ bizydraft/__init__.py,sha256=OM-sKCQrPh25nHVJIX-DgF1raMYyoWLSuyduIAHt0Gs,78
2
2
  bizydraft/block_nodes.py,sha256=Lqn3oSCaGDHR2OICc8a2iRoRCVVK9v1-9MM3r-qIZgA,1092
3
3
  bizydraft/env.py,sha256=VFmGopVL2TGWA6hwxyFhIglCEcQxy6iVvL_raMNd6u4,407
4
4
  bizydraft/hijack_nodes.py,sha256=GivcoUsYAOfMjoEMxeViEkSQlmYjMA0RORy04fCbG60,3652
5
- bizydraft/hijack_routes.py,sha256=lf6x3xDzbo9yQIRwfG_1oxcUNfrX_1ogbiff3WOV9gM,3268
6
- bizydraft/oss_utils.py,sha256=FxTe9TxA9Q13pxKaOpcN0Jui-BG1SBWySM71PqpIlsM,8601
7
- bizydraft/patch_handlers.py,sha256=341y4YpzXOngQk9vLO8KLr7MXqAgM-_DrGrtIVGbPWo,6193
5
+ bizydraft/hijack_routes.py,sha256=wLu_PWUbUzhN2uZeayTAj1ShdLXVuKsp85a_FX1UCYY,3415
6
+ bizydraft/oss_utils.py,sha256=JHpMA61NxFzA053y8IzBc01xxMJCF6G2PTHk-rXqIFo,15590
7
+ bizydraft/patch_handlers.py,sha256=1aKOAAI1cCLBuQoym6le7GbW0rs_9_m_h7pCCg3xIz8,6574
8
8
  bizydraft/postload.py,sha256=XFElKcmCajT_oO7SVJJBaN04XcWro54N5HB5cSCxfvI,1308
9
9
  bizydraft/prestartup_patch.py,sha256=4FGjmRcDHELjtlQOrfTfk2Un5OS89QIqfq-gEcB9WDs,998
10
10
  bizydraft/resp.py,sha256=8INvKOe5Dgai3peKfqKjrhUoYeuXWXn358w30-_cY-A,369
11
11
  bizydraft/server.py,sha256=L2zoJgOisr65IRphOyko74AdsLel59gh55peyMaUrO8,2102
12
12
  bizydraft/workflow_io.py,sha256=MYhJbpgkv8hrA5k_aolijOTrWpTtu62nzRznA4hv8JE,4298
13
13
  bizydraft/static/js/aiAppHandler.js,sha256=4PcrcD9gv-yqBelkIfuhoN0ZwuFv66f7-bmwqla_aPI,17428
14
+ bizydraft/static/js/clipspaceToOss.js,sha256=l0mUiiTsbSZx1mNgDfT9WJaM9Ee-ebP2UcAwrnAquMQ,5355
14
15
  bizydraft/static/js/freezeModeHandler.js,sha256=SjpHD2nYymR-E13B0YcqkA6e4WycZOVI3c48Ts9qvWE,18027
15
16
  bizydraft/static/js/handleStyle.js,sha256=liIzTu-wnV172g58gHWGLYTfd86xpJxL4A-HuHpFnq4,3616
16
17
  bizydraft/static/js/hookLoadImage.js,sha256=aFRWkgJW-Cp-YHjZh-3j-vsVcNaDZpBVoQqcFZ2Po0g,8186
17
18
  bizydraft/static/js/hookLoadModel.js,sha256=GIvew1mDcJhgyyaJif0tFGNdFQo_LzlTQfBZQ2lg2Tw,17032
18
- bizydraft/static/js/main.js,sha256=oEsVEUZSo8ipx93oqs2WFQSRgp46XQ_D-Xao-wen2S8,121
19
+ bizydraft/static/js/main.js,sha256=7ijEPNQu9WtZZFQVrxby-ThG3ZJQ53feEiPrQTXrZns,151
19
20
  bizydraft/static/js/nodeFocusHandler.js,sha256=24xXbS4Q-GjJdRqf11i-1pBo8MkOJ24F7MHFV44EG6Q,4683
20
21
  bizydraft/static/js/nodeParamsFilter.js,sha256=H7lBB0G8HNqoGhOCH1hNXqPU-rPlrFyTxg_f_JgLEMk,4168
21
22
  bizydraft/static/js/postEvent.js,sha256=En0SNrekq3uTWF36yNP9V6LnBoyXjqKC-OnufggpWLE,39839
@@ -23,7 +24,7 @@ bizydraft/static/js/socket.js,sha256=VE3fTAgEfM0FZhL526Skt7OCRokOa3mzTCAjAomI_tE
23
24
  bizydraft/static/js/tool.js,sha256=VupamUuh7tYiDnBTrL5Z_yLmhJinskhzRXwE3zfsKZM,2901
24
25
  bizydraft/static/js/uploadFile.js,sha256=WvglKzHMeOzDhOH3P-fLcPHxCLbKOJpo4DntoRxeJtI,4908
25
26
  bizydraft/static/js/workflow_io.js,sha256=FWAjncvWhvy-3nN_legD2fpRwgnIncpRLHU5X016a-U,5236
26
- bizydraft-0.2.68.dev20250930064514.dist-info/METADATA,sha256=ebcTaJ0WE5-IeUnXPlR-nTFYCp2PHBvxmvrgqtoZiMA,180
27
- bizydraft-0.2.68.dev20250930064514.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- bizydraft-0.2.68.dev20250930064514.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
29
- bizydraft-0.2.68.dev20250930064514.dist-info/RECORD,,
27
+ bizydraft-0.2.68.dev20250930074209.dist-info/METADATA,sha256=BhOLzTKeIJVH0bFHkw3d-oHMvc9wQR7K5fcyrm2H4-s,180
28
+ bizydraft-0.2.68.dev20250930074209.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ bizydraft-0.2.68.dev20250930074209.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
30
+ bizydraft-0.2.68.dev20250930074209.dist-info/RECORD,,