xiaoshiai-hub 1.1.1__py3-none-any.whl → 1.1.2__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.
xiaoshiai_hub/__init__.py CHANGED
@@ -28,6 +28,10 @@ from .auth import (
28
28
  load_token,
29
29
  delete_token,
30
30
  )
31
+ from .envelope_crypto import (
32
+ Algorithm,
33
+ envelope_enc_file,
34
+ )
31
35
 
32
36
  # Upload functionality (requires GitPython)
33
37
  try:
@@ -58,6 +62,9 @@ __all__ = [
58
62
  "save_token",
59
63
  "load_token",
60
64
  "delete_token",
65
+ # Encryption
66
+ "Algorithm",
67
+ "envelope_enc_file",
61
68
  # Exceptions
62
69
  "HubException",
63
70
  "RepositoryNotFoundError",
xiaoshiai_hub/cli.py CHANGED
@@ -62,6 +62,9 @@ def cmd_upload_folder(args):
62
62
  "请设置 MOHA_ENCRYPTION_PASSWORD 环境变量或使用 --encryption-password 参数", file=sys.stderr)
63
63
  return 1
64
64
 
65
+ # 获取加密算法
66
+ algorithm = args.algorithm if encryption_password else None
67
+
65
68
  try:
66
69
  upload_folder(
67
70
  folder_path=args.folder,
@@ -76,6 +79,7 @@ def cmd_upload_folder(args):
76
79
  encryption_password=encryption_password,
77
80
  ignore_patterns=ignore_patterns,
78
81
  temp_dir=args.temp_dir,
82
+ algorithm=algorithm,
79
83
  )
80
84
  print("上传成功!")
81
85
  return 0
@@ -102,6 +106,9 @@ def cmd_upload_file(args):
102
106
  # 确定仓库中的路径
103
107
  path_in_repo = args.path_in_repo if args.path_in_repo else os.path.basename(args.file)
104
108
 
109
+ # 获取加密算法
110
+ algorithm = args.algorithm if encryption_password else None
111
+
105
112
  try:
106
113
  upload_file(
107
114
  path_file=args.file,
@@ -115,6 +122,7 @@ def cmd_upload_file(args):
115
122
  password=password,
116
123
  token=token,
117
124
  encryption_password=encryption_password,
125
+ algorithm=algorithm,
118
126
  )
119
127
  print(f"上传成功: {path_in_repo}")
120
128
  return 0
@@ -286,6 +294,12 @@ def _add_encryption_args(parser):
286
294
  "--encryption-password",
287
295
  help="加密密码(或设置 MOHA_ENCRYPTION_PASSWORD 环境变量)",
288
296
  )
297
+ parser.add_argument(
298
+ "--algorithm", "-a",
299
+ choices=["AES", "SM4"],
300
+ default="AES",
301
+ help="加密算法(默认: AES)",
302
+ )
289
303
 
290
304
 
291
305
  def create_parser():
xiaoshiai_hub/client.py CHANGED
@@ -8,7 +8,7 @@ from typing import Dict, List, Optional
8
8
 
9
9
 
10
10
  import requests
11
- from xiaoshiai_hub.envelope_crypto import DataKey
11
+ from xiaoshiai_hub.envelope_crypto import DEFAULT_ALGORITHM, Algorithm, DataKey
12
12
 
13
13
  try:
14
14
  from tqdm.auto import tqdm
@@ -338,12 +338,18 @@ class HubClient:
338
338
  progress_bar.close()
339
339
 
340
340
 
341
- def generate_data_key(self, password: Optional[str] = None) -> DataKey:
341
+ def generate_data_key(
342
+ self,
343
+ algorithm : Optional[str] = DEFAULT_ALGORITHM,
344
+ password: Optional[str] = None) -> DataKey:
342
345
  url = f"{self.base_url}/api/kms/generate-data-key"
343
346
  try:
344
347
  resp = requests.post(
345
348
  url,
346
- json={"password": password},
349
+ json={
350
+ "algorithm": algorithm,
351
+ "password": password,
352
+ },
347
353
  timeout=30
348
354
  )
349
355
  resp.raise_for_status()
@@ -1,20 +1,25 @@
1
1
  """
2
2
  信封加密核心模块
3
3
 
4
- 实现基于 AES-256-CTR 的信封加密/解密逻辑。
4
+ 实现基于 AES-256-CTR 和 SM4-CTR 的信封加密/解密逻辑。
5
5
  支持随机访问和流式解密,适合大文件部分读取场景。
6
6
 
7
7
  信封加密文件格式:
8
8
  - 前 4 字节: 元数据长度 (big-endian)
9
- - 元数据: JSON 格式,包含 encryptedKey 和 password
10
- - 16 字节: IV (用于 AES-256-CTR)
9
+ - 元数据: JSON 格式,包含 encryptedKey 和 algorithm
10
+ - 16 字节: IV (用于 CTR 模式)
11
11
  - 剩余部分: 加密后的文件内容
12
+
13
+ 支持的算法:
14
+ - AES: 使用 32 字节密钥
15
+ - SM4: 使用 16 字节密钥
12
16
  """
13
17
 
14
18
  import io
15
19
  import os
16
20
  import json
17
21
  from dataclasses import dataclass
22
+ from enum import Enum
18
23
  from pathlib import Path
19
24
  import shutil
20
25
  import tempfile
@@ -24,17 +29,29 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
24
29
  from cryptography.hazmat.backends import default_backend
25
30
 
26
31
 
27
- # IV 长度 (AES-256-CTR)
32
+ # IV 长度 (AES-256-CTR 和 SM4-CTR 都使用 16 字节 IV)
28
33
  IV_SIZE = 16
29
34
  # 元数据长度字段大小
30
35
  METADATA_LENGTH_SIZE = 4
31
36
  CHUNK_SIZE = 64 * 1024 # 64KB chunks
32
37
 
38
+
39
+ class Algorithm(str, Enum):
40
+ """支持的加密算法"""
41
+ AES = "AES"
42
+ SM4 = "SM4"
43
+
44
+
45
+ # 默认算法
46
+ DEFAULT_ALGORITHM = Algorithm.AES
47
+
48
+
33
49
  @dataclass
34
50
  class DataKey:
35
51
  """数据密钥对象,包含明文密钥和加密后的密钥"""
36
- plaintext_key: bytes # 明文密钥(32字节,用于 AES-256
37
- encrypted_key: str
52
+ plaintext_key: bytes # 明文密钥(AES-256: 32字节,SM4: 16字节)
53
+ encrypted_key: str
54
+
38
55
 
39
56
  def _raw_open(path, mode="rb"):
40
57
  """
@@ -49,26 +66,51 @@ def _raw_open(path, mode="rb"):
49
66
  return file_io
50
67
 
51
68
 
52
- def create_aes_ctr_cipher(key: bytes, iv: bytes):
53
- """创建 AES-256-CTR cipher"""
54
- return Cipher(
55
- algorithms.AES(key),
56
- modes.CTR(iv),
57
- backend=default_backend()
58
- )
69
+ def create_cipher(key: bytes, iv: bytes, algorithm: Algorithm = DEFAULT_ALGORITHM):
70
+ """
71
+ 创建加密 cipher
72
+
73
+ Args:
74
+ key: 密钥(AES-256: 32字节,SM4: 16字节)
75
+ iv: 初始化向量(16字节)
76
+ algorithm: 加密算法
77
+
78
+ Returns:
79
+ Cipher 对象
80
+ """
81
+ if algorithm == Algorithm.AES:
82
+ if len(key) != 32:
83
+ raise ValueError(f"AES requires 32-byte key, got {len(key)} bytes")
84
+ return Cipher(
85
+ algorithms.AES(key),
86
+ modes.CTR(iv),
87
+ backend=default_backend()
88
+ )
89
+ elif algorithm == Algorithm.SM4:
90
+ if len(key) != 16:
91
+ raise ValueError(f"SM4 requires 16-byte key, got {len(key)} bytes")
92
+ return Cipher(
93
+ algorithms.SM4(key),
94
+ modes.CTR(iv),
95
+ backend=default_backend()
96
+ )
97
+ else:
98
+ raise ValueError(f"Unsupported algorithm: {algorithm}")
99
+
100
+
59
101
 
60
102
 
61
103
  @dataclass
62
104
  class EnvelopeMetadata:
63
105
  """加密文件元数据"""
64
106
  encrypted_key: str # 加密后的数据密钥(Base64)
65
- password: str # 用于解密 DEK 的密码
107
+ algorithm: str # 加密算法名称
66
108
 
67
109
  def to_bytes(self) -> bytes:
68
110
  """序列化为 JSON 字节"""
69
111
  return json.dumps({
70
112
  "encryptedKey": self.encrypted_key,
71
- "password": self.password
113
+ "algorithm": self.algorithm
72
114
  }).encode("utf-8")
73
115
 
74
116
  @classmethod
@@ -77,30 +119,30 @@ class EnvelopeMetadata:
77
119
  obj = json.loads(data.decode("utf-8"))
78
120
  return cls(
79
121
  encrypted_key=obj["encryptedKey"],
80
- password=obj["password"]
122
+ algorithm=obj.get("algorithm", Algorithm.AES.value) # 兼容旧格式
81
123
  )
82
124
 
83
125
 
84
126
  def _envelope_encrypt_file(
85
127
  input_path: Union[str, Path],
86
128
  output_path: Union[str, Path],
87
- password: str,
88
129
  data_key: DataKey,
130
+ algorithm: Algorithm = DEFAULT_ALGORITHM,
89
131
  ) -> None:
90
132
  """
91
- 使用信封加密对文件进行加密 (AES-256-CTR)
133
+ 使用信封加密对文件进行加密
92
134
 
93
135
  Args:
94
136
  input_path: 明文文件路径
95
137
  output_path: 加密文件输出路径
96
- password: 用于 KMS 密钥派生的密码
97
- kms_client: KMS 客户端实例(可选,不提供则使用默认客户端)
138
+ data_key: 数据密钥对象
139
+ algorithm: 加密算法(默认 AES)
98
140
  """
99
141
  input_path = Path(input_path)
100
142
  output_path = Path(output_path)
101
143
 
102
144
  iv = os.urandom(IV_SIZE)
103
- cipher = create_aes_ctr_cipher(data_key.plaintext_key, iv)
145
+ cipher = create_cipher(data_key.plaintext_key, iv, algorithm)
104
146
  encryptor = cipher.encryptor()
105
147
  with _raw_open(input_path, "rb") as f:
106
148
  plaintext = f.read()
@@ -108,7 +150,7 @@ def _envelope_encrypt_file(
108
150
  ciphertext = encryptor.update(plaintext) + encryptor.finalize()
109
151
  metadata = EnvelopeMetadata(
110
152
  encrypted_key=data_key.encrypted_key,
111
- password=password
153
+ algorithm=algorithm.value
112
154
  )
113
155
  metadata_bytes = metadata.to_bytes()
114
156
  output_path.parent.mkdir(parents=True, exist_ok=True)
@@ -123,25 +165,26 @@ def _envelope_encrypt_file(
123
165
  def envelope_enc_file(
124
166
  source: Union[Path, str],
125
167
  *,
126
- password: str,
127
168
  data_key: DataKey,
128
169
  dest: Optional[Union[Path, str]] = None,
129
170
  replace: bool = False,
130
171
  chunked: bool = True,
131
- chunk_size: int = CHUNK_SIZE, # 默认 64KB,与 envelope_crypto 一致
172
+ chunk_size: int = CHUNK_SIZE,
173
+ algorithm: Algorithm = DEFAULT_ALGORITHM,
132
174
  ) -> Path:
133
- """使用信封加密模式加密单个文件 (AES-256-CTR)。
175
+ """使用信封加密模式加密单个文件。
134
176
 
135
177
  信封加密使用 KMS 服务生成数据密钥,数据密钥用于加密文件内容,
136
178
  加密后的数据密钥存储在文件头部。
137
179
 
138
180
  Args:
139
181
  source: 源文件路径
140
- password: 用于 KMS 密钥派生的密码
182
+ data_key: 数据密钥对象
141
183
  dest: 目标文件路径(默认添加 .encrypted 后缀)
142
184
  replace: 是否原地加密(替换原文件)
143
185
  chunked: 是否使用流式加密(适用于大文件,减少内存占用)
144
186
  chunk_size: 流式加密时每次处理的块大小
187
+ algorithm: 加密算法(默认 AES,可选 SM4)
145
188
 
146
189
  Returns:
147
190
  加密后的文件路径
@@ -161,9 +204,9 @@ def envelope_enc_file(
161
204
  dst.parent.mkdir(parents=True, exist_ok=True)
162
205
 
163
206
  if chunked:
164
- _envelope_encrypt_file_streaming(src, dst, password, data_key, chunk_size)
207
+ _envelope_encrypt_file_streaming(src, dst, data_key, chunk_size, algorithm)
165
208
  else:
166
- _envelope_encrypt_file(src, dst, password, data_key)
209
+ _envelope_encrypt_file(src, dst, data_key, algorithm)
167
210
 
168
211
  # 替换模式:移动加密文件到原位置
169
212
  if replace:
@@ -185,32 +228,32 @@ def _make_temp_path(parent: Path, suffix: str) -> Path:
185
228
  def _envelope_encrypt_file_streaming(
186
229
  input_path: Union[str, Path],
187
230
  output_path: Union[str, Path],
188
- password: str,
189
231
  data_key: DataKey,
190
232
  chunk_size: int = CHUNK_SIZE,
233
+ algorithm: Algorithm = DEFAULT_ALGORITHM,
191
234
  ) -> None:
192
235
  """
193
- 使用信封加密对大文件进行流式加密 (AES-256-CTR)
236
+ 使用信封加密对大文件进行流式加密
194
237
 
195
- AES-CTR 模式天然支持流式加密,不需要将整个文件加载到内存。
238
+ CTR 模式天然支持流式加密,不需要将整个文件加载到内存。
196
239
  加密后的文件格式与普通模式完全相同,可以用普通模式解密。
197
240
 
198
241
  Args:
199
242
  input_path: 明文文件路径
200
243
  output_path: 加密文件输出路径
201
- password: 用于 KMS 密钥派生的密码
202
- kms_client: KMS 客户端实例
244
+ data_key: 数据密钥对象
203
245
  chunk_size: 每次读取的块大小
246
+ algorithm: 加密算法(默认 AES)
204
247
  """
205
248
  input_path = Path(input_path)
206
249
  output_path = Path(output_path)
207
250
  iv = os.urandom(IV_SIZE)
208
251
  metadata = EnvelopeMetadata(
209
252
  encrypted_key=data_key.encrypted_key,
210
- password=password
253
+ algorithm=algorithm.value
211
254
  )
212
255
  metadata_bytes = metadata.to_bytes()
213
- cipher = create_aes_ctr_cipher(data_key.plaintext_key, iv)
256
+ cipher = create_cipher(data_key.plaintext_key, iv, algorithm)
214
257
  encryptor = cipher.encryptor()
215
258
  output_path.parent.mkdir(parents=True, exist_ok=True)
216
259
  with _raw_open(input_path, "rb") as fin, _raw_open(output_path, "wb") as fout:
xiaoshiai_hub/upload.py CHANGED
@@ -12,7 +12,7 @@ from typing import List, Optional, Union, Dict
12
12
  import requests
13
13
 
14
14
  from xiaoshiai_hub.client import DEFAULT_BASE_URL, HubClient
15
- from xiaoshiai_hub.envelope_crypto import DataKey, envelope_enc_file
15
+ from xiaoshiai_hub.envelope_crypto import Algorithm, DataKey, envelope_enc_file
16
16
  from .exceptions import HubException, AuthenticationError, RepositoryNotFoundError
17
17
 
18
18
 
@@ -125,6 +125,7 @@ def _encrypt_file_if_needed(
125
125
  file_path: Path,
126
126
  encryption_password: Optional[str] = None,
127
127
  data_key: Optional[DataKey] = None,
128
+ algorithm: Optional[Algorithm] = None,
128
129
  ) -> tuple[Optional[Path], Optional[Path]]:
129
130
  """
130
131
  Encrypt file if encryption_password is provided, file is large enough, and file extension is encryptable.
@@ -133,6 +134,7 @@ def _encrypt_file_if_needed(
133
134
  file_path: Path to the file to encrypt
134
135
  encryption_password: Password for encryption
135
136
  data_key: DataKey for encryption (required if encryption_password is provided)
137
+ algorithm: Encryption algorithm (default: AES-256-CTR)
136
138
 
137
139
  Returns:
138
140
  Tuple of (encrypted_file_path, temp_dir_path)
@@ -154,13 +156,15 @@ def _encrypt_file_if_needed(
154
156
  encrypted_file = temp_dir / file_path.name
155
157
 
156
158
  # Encrypt the file
157
- envelope_enc_file(
158
- source=file_path,
159
- password=encryption_password,
160
- data_key=data_key,
161
- dest=encrypted_file,
162
- chunked=True,
163
- )
159
+ enc_kwargs = {
160
+ "source": file_path,
161
+ "data_key": data_key,
162
+ "dest": encrypted_file,
163
+ "chunked": True,
164
+ }
165
+ if algorithm:
166
+ enc_kwargs["algorithm"] = algorithm
167
+ envelope_enc_file(**enc_kwargs)
164
168
  print(f"Encrypted file path: {encrypted_file}")
165
169
 
166
170
  return encrypted_file, temp_dir
@@ -295,6 +299,7 @@ def upload_folder(
295
299
  encryption_password: Optional[str] = None,
296
300
  ignore_patterns: Optional[List[str]] = None,
297
301
  temp_dir: Optional[Union[str, Path]] = None,
302
+ algorithm: Optional[str] = None,
298
303
  ) -> Dict:
299
304
  """
300
305
  Upload a folder to a repository using HTTP API.
@@ -313,6 +318,7 @@ def upload_folder(
313
318
  encryption_password: Password for file encryption (optional)
314
319
  ignore_patterns: List of patterns to ignore
315
320
  temp_dir: Temporary directory for encrypted files (optional, auto-created if not specified)
321
+ algorithm: Encryption algorithm ("AES-256-CTR" or "SM4-CTR", default: AES-256-CTR)
316
322
 
317
323
  Returns:
318
324
  Upload response
@@ -361,7 +367,7 @@ def upload_folder(
361
367
  # Auto-create temp directory
362
368
  temp_dir_path = Path(tempfile.mkdtemp())
363
369
  # Generate data key for encryption
364
- data_key = client.generate_data_key(encryption_password)
370
+ data_key = client.generate_data_key(algorithm,encryption_password)
365
371
 
366
372
  try:
367
373
  # Create session and API URL
@@ -409,13 +415,16 @@ def upload_folder(
409
415
  # Preserve directory structure in temp dir
410
416
  encrypted_file = temp_dir_path / rel_file_path
411
417
  encrypted_file.parent.mkdir(parents=True, exist_ok=True)
412
- envelope_enc_file(
413
- source=local_file,
414
- password=encryption_password,
415
- data_key=data_key,
416
- dest=encrypted_file,
417
- chunked=True,
418
- )
418
+ # Build encryption kwargs
419
+ enc_kwargs = {
420
+ "source": local_file,
421
+ "data_key": data_key,
422
+ "dest": encrypted_file,
423
+ "chunked": True,
424
+ }
425
+ if algorithm:
426
+ enc_kwargs["algorithm"] = Algorithm(algorithm)
427
+ envelope_enc_file(**enc_kwargs)
419
428
  print(f"Encrypted file path: {encrypted_file}")
420
429
  # 加密后重新计算大小,避免被huggingface下载的时候大小一致性检查不通过
421
430
  file_size = encrypted_file.stat().st_size
@@ -512,6 +521,7 @@ def upload_file(
512
521
  password: Optional[str] = None,
513
522
  token: Optional[str] = None,
514
523
  encryption_password: Optional[str] = None,
524
+ algorithm: Optional[str] = None,
515
525
  ) -> Dict:
516
526
  """
517
527
  Upload a single file to a repository using HTTP API.
@@ -529,6 +539,7 @@ def upload_file(
529
539
  password: Password for authentication
530
540
  token: Token for authentication (preferred)
531
541
  encryption_password: Password for file encryption (optional)
542
+ algorithm: Encryption algorithm ("AES" or "SM4", default: AES)
532
543
 
533
544
  Returns:
534
545
  Upload response
@@ -561,10 +572,11 @@ def upload_file(
561
572
  # Generate data key if encryption is needed
562
573
  data_key: Optional[DataKey] = None
563
574
  if encryption_password:
564
- data_key = client.generate_data_key(encryption_password)
575
+ data_key = client.generate_data_key(algorithm,encryption_password)
565
576
 
566
577
  # Encrypt file if needed
567
- encrypted_file, temp_dir = _encrypt_file_if_needed(path_file, encryption_password, data_key)
578
+ algo = Algorithm(algorithm) if algorithm else None
579
+ encrypted_file, temp_dir = _encrypt_file_if_needed(path_file, encryption_password, data_key, algo)
568
580
  actual_file = encrypted_file if encrypted_file else path_file
569
581
  file_was_encrypted = encrypted_file is not None
570
582
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xiaoshiai-hub
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: Python SDK for XiaoShi AI Hub - Upload, download, and manage AI models and datasets with xpai-enc encryption support
5
5
  Home-page: https://github.com/poxiaoyun/moha-sdk
6
6
  Author: XiaoShi AI
@@ -262,7 +262,14 @@ for entry in content.entries:
262
262
 
263
263
  ## 🔐 加密功能
264
264
 
265
- SDK 提供了智能加密功能,使用 AES-256-CTR 算法对大型模型文件进行加密。
265
+ SDK 提供了智能加密功能,支持 **AES** 和 **SM4** 两种加密算法对大型模型文件进行加密。
266
+
267
+ ### 支持的加密算法
268
+
269
+ | 算法 | 说明 |
270
+ |------|------|
271
+ | `AES` | AES-256-CTR 模式,国际通用标准(默认) |
272
+ | `SM4` | SM4-CTR 模式,国密标准 |
266
273
 
267
274
  ### 自动加密规则
268
275
 
@@ -278,11 +285,21 @@ SDK 提供了智能加密功能,使用 AES-256-CTR 算法对大型模型文件
278
285
  ```python
279
286
  from xiaoshiai_hub import upload_folder
280
287
 
281
- # 上传文件夹,自动加密大型模型文件
288
+ # 上传文件夹,使用 AES 加密(默认)
282
289
  result = upload_folder(
283
290
  folder_path="./llama-7b",
284
291
  repo_id="demo/llama-7b",
285
- encryption_password="my-secure-password-123", # 设置加密密码
292
+ encryption_password="my-secure-password-123",
293
+ username="your-username",
294
+ password="your-password",
295
+ )
296
+
297
+ # 使用 SM4 国密算法加密
298
+ result = upload_folder(
299
+ folder_path="./llama-7b",
300
+ repo_id="demo/llama-7b",
301
+ encryption_password="my-secure-password-123",
302
+ algorithm="SM4", # 使用 SM4 加密
286
303
  username="your-username",
287
304
  password="your-password",
288
305
  )
@@ -353,10 +370,18 @@ moha upload ./my_model org/my-model \
353
370
  --username your-username \
354
371
  --password your-password
355
372
 
356
- # 启用加密
373
+ # 启用加密(默认使用 AES)
374
+ moha upload ./my_model org/my-model \
375
+ --encrypt \
376
+ --encryption-password "your-secret" \
377
+ --username your-username \
378
+ --password your-password
379
+
380
+ # 使用 SM4 国密算法加密
357
381
  moha upload ./my_model org/my-model \
358
382
  --encrypt \
359
383
  --encryption-password "your-secret" \
384
+ --algorithm SM4 \
360
385
  --username your-username \
361
386
  --password your-password
362
387
  ```
@@ -434,6 +459,7 @@ moha download-file org/my-model model.safetensors \
434
459
  | `--include` | 包含模式(可多次使用) | download |
435
460
  | `--encrypt, -e` | 启用加密 | upload, upload-file |
436
461
  | `--encryption-password` | 加密密码 | upload, upload-file |
462
+ | `--algorithm, -a` | 加密算法:`AES` 或 `SM4`(默认:AES) | upload, upload-file |
437
463
  | `--path-in-repo, -p` | 仓库中的文件路径 | upload-file |
438
464
  | `--temp-dir` | 加密临时目录 | upload |
439
465
  | `--local-dir, -o` | 本地保存目录 | download, download-file |
@@ -0,0 +1,15 @@
1
+ xiaoshiai_hub/__init__.py,sha256=2hnhTGad1GPTzRstfwZhAF2204Pl2VF8YHD8HQkAYGY,1469
2
+ xiaoshiai_hub/auth.py,sha256=Pv0P6f76flOefYmALyehuMu2Klyc-u_gtXlJYJR4eMY,4105
3
+ xiaoshiai_hub/cli.py,sha256=lBmIKlxmDk74WYttDCqF5fAuIKjvcIlETipWLyBUP4U,15292
4
+ xiaoshiai_hub/client.py,sha256=Ikq-eJz1kA5KA0_PbzAKhmnPOpZT_AubRIefcCOZYBk,12113
5
+ xiaoshiai_hub/download.py,sha256=9Uido7cJjGVd6ERDKu_xNMPliarPdZVVb8hkLgYfFcU,13613
6
+ xiaoshiai_hub/envelope_crypto.py,sha256=zjrt5fc3ya86Y4N7_4OI5_NnmFpQ5HiHgoP31ioJRmw,8235
7
+ xiaoshiai_hub/exceptions.py,sha256=24QzgHWq_4bes07UkC3vGi2oT8SMH6Xu4FNlKt52QHo,672
8
+ xiaoshiai_hub/types.py,sha256=ZC5elxqles8_ODl-fCSssOzm9q8_KjA9mGpiGgObgls,1915
9
+ xiaoshiai_hub/upload.py,sha256=Hb9KX-87YP7HyskHbQCtfgn11ARzzKzPX_i-fhRtZ0E,21372
10
+ xiaoshiai_hub-1.1.2.dist-info/licenses/LICENSE,sha256=tS28u6VpvqNisRWGeufp-XYQc6p194vOGARl3OIjidA,9110
11
+ xiaoshiai_hub-1.1.2.dist-info/METADATA,sha256=QEUR27oyHBY2bm-CIseIRQZzYFzWgtoMORZExQQeAEo,17454
12
+ xiaoshiai_hub-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ xiaoshiai_hub-1.1.2.dist-info/entry_points.txt,sha256=fVh_IA1mbRWl7LEd4-RhENMaspSBa4Hxbsg8_HDWc6Y,48
14
+ xiaoshiai_hub-1.1.2.dist-info/top_level.txt,sha256=9AQDFb5Xn7RLQPdbk1aA0QpntbKhlhlT6Z_g-zUBtlM,14
15
+ xiaoshiai_hub-1.1.2.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- xiaoshiai_hub/__init__.py,sha256=ZDZEGKBbv8zEDO9GkKPy0uiTva9gPPEMIU9_A_MzCZA,1339
2
- xiaoshiai_hub/auth.py,sha256=Pv0P6f76flOefYmALyehuMu2Klyc-u_gtXlJYJR4eMY,4105
3
- xiaoshiai_hub/cli.py,sha256=IKuHYGyk-Kg57BUquWyZFMK6rPfLMmwEOWGZq8E7LJs,14885
4
- xiaoshiai_hub/client.py,sha256=OQyyKD5kyDaPlI81-js1Uj9gYjVPkS60UicZAtokDVw,11914
5
- xiaoshiai_hub/download.py,sha256=9Uido7cJjGVd6ERDKu_xNMPliarPdZVVb8hkLgYfFcU,13613
6
- xiaoshiai_hub/envelope_crypto.py,sha256=PWZrVCkgQckFQO6putRDlZXkQoEYOkq5O0PPltdBDE4,7161
7
- xiaoshiai_hub/exceptions.py,sha256=24QzgHWq_4bes07UkC3vGi2oT8SMH6Xu4FNlKt52QHo,672
8
- xiaoshiai_hub/types.py,sha256=ZC5elxqles8_ODl-fCSssOzm9q8_KjA9mGpiGgObgls,1915
9
- xiaoshiai_hub/upload.py,sha256=cYKCvaxfO5wOmjCmWp_rgbTOdEr73MN2EoBUCwyqqvE,20713
10
- xiaoshiai_hub-1.1.1.dist-info/licenses/LICENSE,sha256=tS28u6VpvqNisRWGeufp-XYQc6p194vOGARl3OIjidA,9110
11
- xiaoshiai_hub-1.1.1.dist-info/METADATA,sha256=0zbLRQQGJbyp5nAcmloTSvoRe9LEXuB8pKGBWNgUudo,16689
12
- xiaoshiai_hub-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- xiaoshiai_hub-1.1.1.dist-info/entry_points.txt,sha256=fVh_IA1mbRWl7LEd4-RhENMaspSBa4Hxbsg8_HDWc6Y,48
14
- xiaoshiai_hub-1.1.1.dist-info/top_level.txt,sha256=9AQDFb5Xn7RLQPdbk1aA0QpntbKhlhlT6Z_g-zUBtlM,14
15
- xiaoshiai_hub-1.1.1.dist-info/RECORD,,