primitive 0.2.61__py3-none-any.whl → 0.2.64__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.
@@ -0,0 +1,40 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ import click
4
+ from primitive.utils.printer import print_result
5
+ from primitive.network.ui import render_ports_table
6
+
7
+ if TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def cli(context):
14
+ """Network"""
15
+ pass
16
+
17
+
18
+ @cli.command("switch")
19
+ @click.pass_context
20
+ def switch(context):
21
+ """Switch"""
22
+ primitive: Primitive = context.obj.get("PRIMITIVE")
23
+ switch_info = primitive.network.get_switch_info()
24
+ if context.obj["JSON"]:
25
+ message = switch_info
26
+ else:
27
+ message = f"Vendor: {switch_info.get('vendor')}. Model: {switch_info.get('model')}. IP: {switch_info.get('ip_address')}"
28
+ print_result(message=message, context=context)
29
+
30
+
31
+ @cli.command("ports")
32
+ @click.pass_context
33
+ def ports(context):
34
+ """Ports"""
35
+ primitive: Primitive = context.obj.get("PRIMITIVE")
36
+ ports_info = primitive.network.get_interfaces_info()
37
+ if context.obj["JSON"]:
38
+ print_result(message=ports_info, context=context)
39
+ else:
40
+ render_ports_table(ports_info.get("interfaces"))
@@ -0,0 +1,121 @@
1
+ import requests
2
+ from typing import Literal
3
+
4
+
5
+ class RedfishClient:
6
+ def __init__(self, host: str, username: str, password: str):
7
+ self.host = host
8
+ self.username = username
9
+ self.password = password
10
+
11
+ def create_session_token(self):
12
+ response = requests.post(
13
+ f"https://{self.host}/redfish/v1/SessionService/Sessions",
14
+ json={"UserName": self.username, "Password": self.password},
15
+ verify=False,
16
+ )
17
+ response.raise_for_status()
18
+ return response.headers["X-Auth-Token"]
19
+
20
+ def get_systems(self):
21
+ token = self.create_session_token()
22
+ headers = {"X-Auth-Token": token}
23
+ response = requests.get(
24
+ f"https://{self.host}/redfish/v1/Systems",
25
+ headers=headers,
26
+ verify=False,
27
+ )
28
+ response.raise_for_status()
29
+ return response.json()
30
+
31
+ def get_system(self, system_id: str):
32
+ token = self.create_session_token()
33
+ headers = {"X-Auth-Token": token}
34
+ response = requests.get(
35
+ f"https://{self.host}/redfish/v1/Systems/{system_id}",
36
+ headers=headers,
37
+ verify=False,
38
+ )
39
+ response.raise_for_status()
40
+ return response.json()
41
+
42
+ def update_boot_options(
43
+ self,
44
+ system_id: str,
45
+ boot_source_override_enabled: Literal["Once", "Continuous", "Disabled"],
46
+ boot_source_override_target: Literal[
47
+ "None", "Pxe", "Cd", "Usb", "Hdd", "BiosSetup", "Diags", "UefiTarget"
48
+ ],
49
+ boot_source_override_mode: Literal["UEFI", "Legacy"] = "UEFI",
50
+ ):
51
+ token = self.create_session_token()
52
+ headers = {"X-Auth-Token": token}
53
+ body = {
54
+ "Boot": {
55
+ "BootSourceOverrideTarget": boot_source_override_target,
56
+ "BootSourceOverrideEnabled": boot_source_override_enabled,
57
+ "BootSourceOverrideMode": boot_source_override_mode,
58
+ }
59
+ }
60
+ response = requests.patch(
61
+ f"https://{self.host}/redfish/v1/Systems/{system_id}",
62
+ headers=headers,
63
+ verify=False,
64
+ json=body,
65
+ )
66
+ response.raise_for_status()
67
+ return response.json()
68
+
69
+ def get_boot_options(self, system_id: str):
70
+ token = self.create_session_token()
71
+ headers = {"X-Auth-Token": token}
72
+ response = requests.get(
73
+ f"https://{self.host}/redfish/v1/Systems/{system_id}/BootOptions",
74
+ headers=headers,
75
+ verify=False,
76
+ )
77
+ response.raise_for_status()
78
+ return response.json()
79
+
80
+ def get_boot_option(self, system_id: str, option_id: str):
81
+ # option_id typically looks like Boot000X
82
+ token = self.create_session_token()
83
+ headers = {"X-Auth-Token": token}
84
+ response = requests.get(
85
+ f"https://{self.host}/redfish/v1/Systems/{system_id}/BootOptions/{option_id}",
86
+ headers=headers,
87
+ verify=False,
88
+ )
89
+ response.raise_for_status()
90
+ return response.json()
91
+
92
+ def get_pxe_boot_option(self, system_id: str):
93
+ boot_options = self.get_boot_options(system_id)
94
+ for option in boot_options.get("Members", []):
95
+ option_data = self.get_boot_option(
96
+ system_id, option["@odata.id"].split("/")[-1]
97
+ )
98
+ boot_option_enabled = option_data.get("BootOptionEnabled", False)
99
+ display_name = option_data.get("DisplayName", "")
100
+ if "PXE" in display_name and "IPv4" in display_name and boot_option_enabled:
101
+ return option_data
102
+
103
+ return None
104
+
105
+ def compute_system_reset(
106
+ self,
107
+ system_id: str,
108
+ reset_type: Literal[
109
+ "ForceRestart", "GracefulRestart", "ForceOff", "PowerCycle"
110
+ ],
111
+ ) -> bool:
112
+ token = self.create_session_token()
113
+ headers = {"X-Auth-Token": token}
114
+ response = requests.post(
115
+ f"https://{self.host}/redfish/v1/Systems/{system_id}/Actions/ComputerSystem.Reset",
116
+ json={"ResetType": reset_type},
117
+ headers=headers,
118
+ verify=False,
119
+ )
120
+ response.raise_for_status()
121
+ return response.status_code == 200
@@ -0,0 +1,19 @@
1
+ from rich.console import Console
2
+ from rich.table import Table
3
+
4
+
5
+ def render_ports_table(ports_dict) -> None:
6
+ console = Console()
7
+
8
+ table = Table(show_header=True, header_style="bold #FFA800")
9
+ table.add_column("Port")
10
+ table.add_column("Status")
11
+ table.add_column("MAC Address")
12
+ table.add_column("IP Address")
13
+
14
+ for k, v in ports_dict.items():
15
+ table.add_row(
16
+ k, v.get("link_status"), v.get("mac_address"), v.get("ip_address")
17
+ )
18
+
19
+ console.print(table)
@@ -25,7 +25,14 @@ class Provisioning(BaseAction):
25
25
  return result.data["authorizedKeys"]
26
26
 
27
27
  def add_reservation_authorized_keys(self, reservation_id: str) -> None:
28
- SSH_DIR.mkdir(parents=True, exist_ok=True)
28
+ if SSH_DIR.exists() is False:
29
+ SSH_DIR.mkdir(parents=True, exist_ok=True)
30
+ SSH_DIR.chmod(0o700)
31
+
32
+ if AUTHORIZED_KEYS_FILEPATH.exists() is False:
33
+ AUTHORIZED_KEYS_FILEPATH.touch()
34
+ AUTHORIZED_KEYS_FILEPATH.chmod(0o600)
35
+
29
36
  AUTHORIZED_KEYS_BACKUP_FILEPATH = SSH_DIR.joinpath(
30
37
  f"authorized_keys.bak-{reservation_id}"
31
38
  )
@@ -1,5 +1,5 @@
1
1
  authorized_keys_query = """
2
- query authorizedKeys($reservationId: GlobalID!) {
2
+ query authorizedKeys($reservationId: ID!) {
3
3
  authorizedKeys(reservationId: $reservationId)
4
4
  }
5
5
  """
@@ -38,7 +38,7 @@ query reservations(
38
38
  reservation_query = (
39
39
  reservation_fragment
40
40
  + """
41
- query reservation($id: GlobalID!) {
41
+ query reservation($id: ID!) {
42
42
  reservation(id: $id) {
43
43
  ...ReservationFragment
44
44
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: primitive
3
- Version: 0.2.61
3
+ Version: 0.2.64
4
4
  Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
5
5
  Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
6
6
  Project-URL: Source, https://github.com//primitivecorp/primitive-cli
@@ -24,6 +24,7 @@ Requires-Dist: loguru
24
24
  Requires-Dist: paramiko[invoke]
25
25
  Requires-Dist: pika>=1.3.2
26
26
  Requires-Dist: psutil>=7.0.0
27
+ Requires-Dist: pyserial>=3.5
27
28
  Requires-Dist: rich>=13.9.4
28
29
  Requires-Dist: speedtest-cli
29
30
  Description-Content-Type: text/markdown
@@ -1,11 +1,11 @@
1
- primitive/__about__.py,sha256=44bjP-EhQtVr_s6llvOiTQ56lIx7zqJ9wHAGO3s_eCw,130
1
+ primitive/__about__.py,sha256=hRAAesFWrmf9g-0XnQagAF61QBoVVA8Jtq5ovjMar4U,130
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
- primitive/cli.py,sha256=g7EtHI9MATAB0qQu5w-WzbXtxz_8zu8z5E7sETmMkKU,2509
4
- primitive/client.py,sha256=gyZIj61qMtOi_s8y0WG3gDehGT-Ms3BtbDP2J_2lajU,3753
3
+ primitive/cli.py,sha256=kLjqyNijkbElbH8XhEtR6vjPc5uwOoh9mKEf2RkcfKk,2608
4
+ primitive/client.py,sha256=n0eQAft3lQyTT_tVE1_1BWSsh8G7gJI2kHBuXLvZVEY,3836
5
5
  primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- primitive/agent/actions.py,sha256=f1sSDdFHOflfGQCwNJ5P51YWUTKs8EK5YXbMAvZ-KMM,8018
6
+ primitive/agent/actions.py,sha256=ohY81bflsYSTsNgo2o2WP_tZr66haX4z319zf5oib-4,9362
7
7
  primitive/agent/commands.py,sha256=o847pK7v7EWQGG67tky6a33qtwoutX6LZrP2FIS_NOk,388
8
- primitive/agent/runner.py,sha256=vymeuIDiah6J9SHjTbsNQGi74PpnJw1MxWPd3TWjc-w,11512
8
+ primitive/agent/runner.py,sha256=oKMCkZpeHGENLXgLIFMgOitrjIpUGYUQmcfTWwSsqSM,15598
9
9
  primitive/agent/uploader.py,sha256=DT_Nzt5eOTm_uRcYKm1sjBBaQZzp5iNZ_uN5XktfQ30,3382
10
10
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  primitive/auth/actions.py,sha256=9NIEXJ1BNJutJs6AMMSjMN_ziONUAUhY_xHwojYJCLA,942
@@ -19,7 +19,7 @@ primitive/daemons/launch_agents.py,sha256=cCxsvCBFmGlKOiBINsKi_NxcozLC8yPw3w6pxq
19
19
  primitive/daemons/launch_service.py,sha256=iuklHeuEqadlf8U1n9xFg4ZG1EKdK2jyaPI-VTlpJ4I,7907
20
20
  primitive/daemons/ui.py,sha256=Af3OJWJ0jdGlb1nfA5yaGYdhBEqqpM8zP2U2vUQdCbw,1236
21
21
  primitive/exec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- primitive/exec/actions.py,sha256=4d_TCjNDcVFoZ9Zw7ZuBa6hKMv2Xzm7_UX_8wcX1aSk,4124
22
+ primitive/exec/actions.py,sha256=rcAMP7hxJZhQUwx-orduCUratzMwVRknX14QSyQLdcA,4348
23
23
  primitive/exec/commands.py,sha256=66LO2kkJC-ynNZQpUCXv4Ol15QoacdSZAHblePDcmLo,510
24
24
  primitive/exec/interactive.py,sha256=TscY6s2ZysijidKPheq6y-fCErUVLS0zcdTW8XyFWGI,2435
25
25
  primitive/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -40,25 +40,30 @@ primitive/graphql/relay.py,sha256=bmij2AjdpURQ6GGVCxwWhauF-r_SxuAU2oJ4sDbLxpI,72
40
40
  primitive/graphql/sdk.py,sha256=dE4TD8KiTKw3Y0uiw5XrIcuZGqexE47eSlPaPD6jDGo,1545
41
41
  primitive/graphql/utility_fragments.py,sha256=uIjwILC4QtWNyO5vu77VjQf_p0jvP3A9q_6zRq91zqs,303
42
42
  primitive/hardware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- primitive/hardware/actions.py,sha256=4xswcLaHk4IhEwRK5Pw_rpEgn6XciT5K2apnHi8_cI4,29204
43
+ primitive/hardware/actions.py,sha256=kSQ1DufDCwxY5fs3zNMj7evAunEF8vt36a7rgtjI7Uw,37323
44
44
  primitive/hardware/android.py,sha256=tu7pBPxWFrIwb_mm5CEdFFf1_veNDOKjOCQg13i_Lh4,2758
45
- primitive/hardware/commands.py,sha256=lwO-cm3doGtuGvR30fib-nALJyNCScdCTXOVVbYVpkY,4604
45
+ primitive/hardware/commands.py,sha256=XU365RByl75ECAV_GdA4xxLrmZkwo1axlw9ZH0k3yRk,4751
46
46
  primitive/hardware/ui.py,sha256=12rucuZ2s-w5R4bKyxON5dEbrdDnVf5sbj3K_nbdo44,2473
47
47
  primitive/hardware/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- primitive/hardware/graphql/fragments.py,sha256=H315uv-ujOsZEbQR5tmstbRU_9OCa5SErZMwIckSiLc,383
49
- primitive/hardware/graphql/mutations.py,sha256=wOKNJN-1x_DGxvdHt2Bm3AUqyW8P1lNPkxLIQThbXUg,1874
50
- primitive/hardware/graphql/queries.py,sha256=I86uLuOSjHSph11Y5MVCYko5Js7hoiEZ-cEoPTc4J-k,1392
48
+ primitive/hardware/graphql/fragments.py,sha256=FRPAKB3KBfVxVm69HeYCxvvrdkW-O8CR46Inbk3wgc8,481
49
+ primitive/hardware/graphql/mutations.py,sha256=MqhjICTouKPgtxXy1CCyLZFa4SZbJQGk9jJMcIJDQW8,2251
50
+ primitive/hardware/graphql/queries.py,sha256=D7C32AKizZ_4KaqFCSffjMpnLBTnCgr5npvyrU_om-o,2099
51
51
  primitive/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  primitive/jobs/actions.py,sha256=-IRNO7KM63cPAbGRNQycyFsugW9hAyg-TDuK0g5AiSw,6542
53
53
  primitive/jobs/commands.py,sha256=MxPCkBEYW_eLNqgCRYeyj7ZcLOFAWfpVZlqDR2Y_S0o,830
54
54
  primitive/jobs/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- primitive/jobs/graphql/fragments.py,sha256=rWZWxZs9ZWjWal0eiY_XPWUS7i7f3fwSM_w8ZvDs2JQ,614
55
+ primitive/jobs/graphql/fragments.py,sha256=I-Ly0TGARt_TdenLQ7877pGLWBzono_IMj7NCzsRtYA,654
56
56
  primitive/jobs/graphql/mutations.py,sha256=8ASvCmwQh7cMeeiykOdYaYVryG8FRIuVF6v_J8JJZuw,219
57
- primitive/jobs/graphql/queries.py,sha256=ZxNmm-WovytbggNuKRnwa0kc26T34_0yhqkoqx-2uj0,1736
57
+ primitive/jobs/graphql/queries.py,sha256=57B5mSnAYjNEFFk1P69odON0fqkf7FyhsA316pcxb6g,1712
58
58
  primitive/messaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- primitive/messaging/provider.py,sha256=NCgl1KiV4Ow49Laz2grNGsP7Ij4vUUeorUjkhAKF1H4,4182
60
- primitive/monitor/actions.py,sha256=AZOoNzASorvye5309h6BJbsup_jR8YunQ19EiG5SAQg,9643
59
+ primitive/messaging/provider.py,sha256=NkR4hnQzEZ0SItno1TgYNvzTwgKZq03iNrnIpLK7MTg,4400
60
+ primitive/monitor/actions.py,sha256=GqwnTibVwAtiSU40TBexBruZI_V-7ZS8MoYZpFxq3mI,10833
61
61
  primitive/monitor/commands.py,sha256=VDlEL_Qpm_ysHxug7VpI0cVAZ0ny6AS91Y58D7F1zkU,409
62
+ primitive/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ primitive/network/actions.py,sha256=qpkn9U7LTDzdoOntiFP2sBPCQYEraRjAYrkcPzhmw78,17021
64
+ primitive/network/commands.py,sha256=F84KmwkG0DHNLIa7Em8LMm2XDOaKMCWRpcFej8olxM0,1071
65
+ primitive/network/redfish.py,sha256=uOtAS_Dwc4I4bnWKNSTCQ_xsj5LTtRzW5v2P_fWaSJM,4248
66
+ primitive/network/ui.py,sha256=_i4lJ3hhTrPc_KS5EjXPkqqKkzdLCOdxxtk5MiDWsfU,504
62
67
  primitive/organizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
68
  primitive/organizations/actions.py,sha256=kVHOhG1oS2sI5p8uldSo5L-RUZsnG36eaulVuKLyZ-M,1863
64
69
  primitive/organizations/commands.py,sha256=_dwgVEJCqMa5VgB_7P1wLPFc0AuT1p9dtyR9JRr4kpw,487
@@ -74,16 +79,16 @@ primitive/projects/graphql/fragments.py,sha256=02F5nyI8i-ML_bV5FFHUgFWM5bBBfjmz_
74
79
  primitive/projects/graphql/mutations.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
80
  primitive/projects/graphql/queries.py,sha256=nFaVf6YOHA2L_FTgIUdRK-80hYTmv1a1X5ac7QPMp1k,646
76
81
  primitive/provisioning/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- primitive/provisioning/actions.py,sha256=oM19p1JyqwPhfJtyy23RqhuIKinorTo78yOYWVLC98c,2165
82
+ primitive/provisioning/actions.py,sha256=o4JA-CNJPfMXwhqSLAQ5SI9o1TLNPKbn5GpJn4ML_ds,2392
78
83
  primitive/provisioning/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- primitive/provisioning/graphql/queries.py,sha256=cBtuKa6shoatYZfKSnQoPJP6B8g8y3QhFqJ_pkvMcG0,134
84
+ primitive/provisioning/graphql/queries.py,sha256=QxXkn9jEzfpevXu9iYCPFEJLs8SXgS0mABwJM98dEGg,128
80
85
  primitive/reservations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
86
  primitive/reservations/actions.py,sha256=vxJ62oriy3Km1d_tiFGMbSclkMJf66eaUgVuhjGmuyA,7021
82
87
  primitive/reservations/commands.py,sha256=LFRoV59QGgWIjBdrGjJdffHugg8TLe0Fwlcyu_JaTkk,2369
83
88
  primitive/reservations/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
89
  primitive/reservations/graphql/fragments.py,sha256=h3edd0Es38SNldhHiRU3hAJNYwrUIJMWxiP8av3BfrI,431
85
90
  primitive/reservations/graphql/mutations.py,sha256=IqzwQL7OclN7RpIcidrTQo9cGYofY7wqoBOdnY0pwN8,651
86
- primitive/reservations/graphql/queries.py,sha256=x31wTRelskX2fc0fx2qrY7XT1q74nvzLv_Xef3o9weg,746
91
+ primitive/reservations/graphql/queries.py,sha256=BP7ON6QfChZRlY_YTyUbxM_tEPSfJqc8DknfpTvQ7hk,740
87
92
  primitive/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
93
  primitive/utils/actions.py,sha256=Gm84YgXFSpilpYqjG5nxcQTdRSp3waN7ZorzIzMBLtc,208
89
94
  primitive/utils/auth.py,sha256=uBIZNPF2CpbaPV2UMi6eWVUKghV6WIm-pG3-UM29bNs,1465
@@ -99,8 +104,8 @@ primitive/utils/psutil.py,sha256=xa7ef435UL37jyjmUPbEqCO2ayQMpCs0HCrxVEvLcuM,763
99
104
  primitive/utils/shell.py,sha256=Z4zxmOaSyGCrS0D6I436iQci-ewHLt4UxVg1CD9Serc,2171
100
105
  primitive/utils/text.py,sha256=XiESMnlhjQ534xE2hMNf08WehE1SKaYFRNih0MmnK0k,829
101
106
  primitive/utils/x509.py,sha256=HwHRPqakTHWd40ny-9O_yNknSL1Cxo50O0UCjXHFq04,3796
102
- primitive-0.2.61.dist-info/METADATA,sha256=VMscEolFKYI028E5OFCQY8wouk_Zw0eBzU1vXSCjwBA,3540
103
- primitive-0.2.61.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
104
- primitive-0.2.61.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
105
- primitive-0.2.61.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
106
- primitive-0.2.61.dist-info/RECORD,,
107
+ primitive-0.2.64.dist-info/METADATA,sha256=BJiBuQqlDVUUeO7_00Gq0IvKM1983vxjyp2w7x_dP7k,3569
108
+ primitive-0.2.64.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
109
+ primitive-0.2.64.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
110
+ primitive-0.2.64.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
111
+ primitive-0.2.64.dist-info/RECORD,,