loopcut 0.1.0__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.
- loopcut/__init__.py +5 -0
- loopcut/__main__.py +8 -0
- loopcut/addon/VENDORED.md +21 -0
- loopcut/addon/__init__.py +8 -0
- loopcut/addon/blobs.py +444 -0
- loopcut/addon/bundles.py +569 -0
- loopcut/addon/cloud_http.py +256 -0
- loopcut/addon/config.py +5 -0
- loopcut/addon/credentials.py +6 -0
- loopcut/agent_tools.json +37 -0
- loopcut/catalog.json +955 -0
- loopcut/catalog.py +90 -0
- loopcut/cli.py +265 -0
- loopcut/mcp_server.py +340 -0
- loopcut/pack.py +154 -0
- loopcut/service.py +839 -0
- loopcut/state.py +81 -0
- loopcut-0.1.0.dist-info/METADATA +198 -0
- loopcut-0.1.0.dist-info/RECORD +23 -0
- loopcut-0.1.0.dist-info/WHEEL +5 -0
- loopcut-0.1.0.dist-info/entry_points.txt +2 -0
- loopcut-0.1.0.dist-info/licenses/LICENSE +674 -0
- loopcut-0.1.0.dist-info/top_level.txt +1 -0
loopcut/state.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Small JSON records on disk: the signed-in key, quotes awaiting a start, started jobs and the
|
|
2
|
+
last tool catalog read from the server.
|
|
3
|
+
|
|
4
|
+
Everything lives under LOOPCUT_CONNECTOR_HOME, else %APPDATA%/loopcut-connector on Windows and
|
|
5
|
+
$XDG_CONFIG_HOME/loopcut-connector (default ~/.config/loopcut-connector) elsewhere. Not
|
|
6
|
+
~/.config/loopcut: other Loopcut tools already keep files there, and the Blender add-on keeps its
|
|
7
|
+
own sign-in in Blender's config folder, so this folder belongs to the connector alone. Files are
|
|
8
|
+
written whole, owner-only, and replaced atomically, so the CLI and an MCP server can share them.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
_ID = re.compile(r"[A-Za-z0-9_-]{1,128}\Z")
|
|
19
|
+
FOLDER = "loopcut-connector"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def home():
|
|
23
|
+
explicit = os.environ.get("LOOPCUT_CONNECTOR_HOME")
|
|
24
|
+
if explicit:
|
|
25
|
+
return Path(explicit).expanduser()
|
|
26
|
+
if sys.platform == "win32" and os.environ.get("APPDATA"):
|
|
27
|
+
return Path(os.environ["APPDATA"]) / FOLDER
|
|
28
|
+
return Path(os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config") / FOLDER
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _write(path, value):
|
|
32
|
+
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
33
|
+
handle, temporary = tempfile.mkstemp(prefix=".tmp-", dir=path.parent)
|
|
34
|
+
try:
|
|
35
|
+
with os.fdopen(handle, "w", encoding="utf-8") as output:
|
|
36
|
+
json.dump(value, output, indent=2, ensure_ascii=False)
|
|
37
|
+
os.chmod(temporary, 0o600)
|
|
38
|
+
os.replace(temporary, path)
|
|
39
|
+
except BaseException:
|
|
40
|
+
Path(temporary).unlink(missing_ok=True)
|
|
41
|
+
raise
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _read(path):
|
|
45
|
+
try:
|
|
46
|
+
value = json.loads(path.read_text(encoding="utf-8"))
|
|
47
|
+
except FileNotFoundError:
|
|
48
|
+
return None
|
|
49
|
+
return value if isinstance(value, dict) else None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _record(kind, identifier):
|
|
53
|
+
if not isinstance(identifier, str) or not _ID.fullmatch(identifier):
|
|
54
|
+
raise ValueError(f"Invalid {kind[:-1]} id: {identifier!r}")
|
|
55
|
+
return home() / kind / f"{identifier}.json"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def credentials(base_url):
|
|
59
|
+
"""{api_key, email} stored for this service, or None."""
|
|
60
|
+
entry = (_read(home() / "credentials.json") or {}).get(base_url)
|
|
61
|
+
return entry if isinstance(entry, dict) and isinstance(entry.get("api_key"), str) else None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def save_credentials(base_url, api_key, email):
|
|
65
|
+
stored = _read(home() / "credentials.json") or {}
|
|
66
|
+
stored[base_url] = {"api_key": api_key, "email": email}
|
|
67
|
+
_write(home() / "credentials.json", stored)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def forget_credentials(base_url):
|
|
71
|
+
stored = _read(home() / "credentials.json") or {}
|
|
72
|
+
if stored.pop(base_url, None) is not None:
|
|
73
|
+
_write(home() / "credentials.json", stored)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load(kind, identifier):
|
|
77
|
+
return _read(_record(kind, identifier))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def save(kind, identifier, value):
|
|
81
|
+
_write(_record(kind, identifier), value)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loopcut
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Loopcut's cloud tools for Claude Code, Codex and other agents: render .blend files on cloud GPUs, search free assets, make 3D models, sound effects and voice lines, billed to your Loopcut credits.
|
|
5
|
+
License-Expression: GPL-3.0-or-later
|
|
6
|
+
Project-URL: Homepage, https://loopcut.org/render
|
|
7
|
+
Project-URL: Documentation, https://loopcut.org/docs/render
|
|
8
|
+
Project-URL: Source, https://github.com/loopcut-ai/loopcut
|
|
9
|
+
Keywords: blender,render,mcp,cloud,loopcut,3d,assets
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# loopcut
|
|
16
|
+
|
|
17
|
+
Loopcut's cloud tools for Claude Code, Codex, Cursor, Claude Desktop and the shell. Render `.blend` files on
|
|
18
|
+
cloud RTX 4090s, search free asset libraries, turn images into 3D models, remesh meshes, and make sound
|
|
19
|
+
effects and voice lines. Results come back as files the agent imports with Blender's Python. Everything is
|
|
20
|
+
paid from your [Loopcut](https://loopcut.org/render) account's credits. You don't need the Loopcut app;
|
|
21
|
+
any Blender install packs the scene.
|
|
22
|
+
|
|
23
|
+
<!-- mcp-name: io.github.loopcut-ai/loopcut -->
|
|
24
|
+
|
|
25
|
+
It's one MCP server plus a CLI with the same verbs. The tools come from Loopcut's catalog, so a cloud tool
|
|
26
|
+
that opens on Loopcut shows up without a new release.
|
|
27
|
+
|
|
28
|
+
| Tool | What it does | Result |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `loopcut_login` | Signs in or switches account. Any tool does it by itself the first time: a browser opens, you approve, and the call carries on | |
|
|
31
|
+
| `loopcut_account` | Plan, credit balance, Private mode and no-generation settings, and which tools you can use | |
|
|
32
|
+
| `render_quote` | Packs the saved scene with headless Blender, uploads what the cloud lacks and prices the render | PNG frames and a QA report |
|
|
33
|
+
| `asset_search` | Searches Poly Haven, ambientCG and Physically Based. Free, and it runs at once | Matches with licence and download details |
|
|
34
|
+
| `image_to_3d` | Makes a textured model from a prompt, an image or both | GLB |
|
|
35
|
+
| `remesh` | Rebuilds a GLB mesh with clean quads or triangles and bakes its textures onto it | FBX (quads) or GLB (triangles) |
|
|
36
|
+
| `sound_effect` | Makes a sound effect from a description | WAV |
|
|
37
|
+
| `voice_line` | Speaks a line with a chosen voice and direction | WAV |
|
|
38
|
+
| `job_start` | Starts a quoted job, which spends credits. Calling it again returns the same job | |
|
|
39
|
+
| `job_status` | Waits for a job and downloads its results, hash-checked and resumable | |
|
|
40
|
+
| `job_cancel` | Cancels a job and refunds unspent credits | |
|
|
41
|
+
|
|
42
|
+
Image generation and image editing appear as `image_generate` and `image_edit` once they open.
|
|
43
|
+
|
|
44
|
+
**Price.** 1 credit = $0.01. Every paid tool quotes first, for free, and the quote is the most a job can
|
|
45
|
+
cost. Rendering costs 5 credits per GPU-minute on an RTX 4090 with a one-minute minimum; the other prices
|
|
46
|
+
are on [loopcut.org/pricing](https://loopcut.org/pricing). Asset search, quotes, packing and the render QA
|
|
47
|
+
report are free. Failed jobs are refunded. New accounts start with 100 credits.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
You need [uv](https://docs.astral.sh/uv/) (it provides Python 3.10+), and Blender if you render.
|
|
52
|
+
|
|
53
|
+
**Claude Code** (plugin, which includes a skill that teaches the agent the tools and the habits that save
|
|
54
|
+
credits)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
/plugin install loopcut --marketplace loopcut-ai/loopcut
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
On Claude Code before 2.1.275, run `/plugin marketplace add loopcut-ai/loopcut` and then
|
|
61
|
+
`/plugin install loopcut@loopcut`. Or add only the MCP server:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
claude mcp add loopcut -- uvx loopcut mcp
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Codex** (plugin, with the same skill)
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
codex plugin marketplace add loopcut-ai/loopcut
|
|
71
|
+
codex plugin add loopcut@loopcut
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or add only the MCP server:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
codex mcp add loopcut -- uvx loopcut mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Every tool call returns within 50 s, under Codex's default 60 s tool timeout; long work carries on between
|
|
81
|
+
calls. Both plugins start the server from their own folder with `uv run --frozen --project <plugin folder>
|
|
82
|
+
loopcut mcp`; the package has no dependencies beyond Python.
|
|
83
|
+
|
|
84
|
+
**Cursor, VS Code, Claude Desktop and other MCP clients**
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"loopcut": {
|
|
90
|
+
"command": "uvx",
|
|
91
|
+
"args": ["loopcut", "mcp"]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Apps opened from the Dock or Start menu may not see your shell's `PATH`. If the server doesn't start, use
|
|
98
|
+
the full path from `which uvx` as `command`.
|
|
99
|
+
|
|
100
|
+
The first call opens a browser to sign in, and the key is kept where only you can read it. Without a
|
|
101
|
+
browser (over SSH, say), the call returns the link for you to open. `LOOPCUT_API_KEY` skips all of this.
|
|
102
|
+
|
|
103
|
+
## CLI
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
loopcut login # once per machine
|
|
107
|
+
loopcut account # plan, credits, settings
|
|
108
|
+
loopcut tools # the open tools and their inputs
|
|
109
|
+
loopcut render shot.blend --frames 1-120 --samples 256 --max-credits 300
|
|
110
|
+
loopcut run asset_search --input '{"query": "oak floor", "kind": "material"}'
|
|
111
|
+
loopcut run image_to_3d --input '{"prompt": "a brass desk lamp", "quality": "draft"}' --file image=lamp.png --max-credits 30
|
|
112
|
+
loopcut quote sound_effect --input '{"prompt": "a heavy wooden door slams", "duration_seconds": 2}'
|
|
113
|
+
loopcut start QUOTE_ID | status JOB_ID --wait | fetch JOB_ID --out DIR | cancel JOB_ID
|
|
114
|
+
loopcut settings --private-mode off # or on; also --no-generation on|off
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Install the command with `uv tool install loopcut`, or run any command without installing as `uvx loopcut …`.
|
|
118
|
+
`run` and `render` quote, then start only with `--yes`, under `--max-credits N`, or after you answer the
|
|
119
|
+
prompt. Results print as JSON on stdout; progress goes to stderr. In CI, create a key on
|
|
120
|
+
[loopcut.org/account](https://loopcut.org/account) and set `LOOPCUT_API_KEY`.
|
|
121
|
+
|
|
122
|
+
## Settings
|
|
123
|
+
|
|
124
|
+
| Variable | Meaning |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `LOOPCUT_API_KEY` | A Loopcut key (`lc_…`) to use instead of the stored sign-in, e.g. in CI |
|
|
127
|
+
| `LOOPCUT_BLENDER` | The Blender executable used for packing. Without it, the tool checks `BLENDER`, then `PATH`, then the usual install folders |
|
|
128
|
+
| `LOOPCUT_SERVICE_URL` | The service, `https://loopcut.org` by default |
|
|
129
|
+
| `LOOPCUT_CONNECTOR_HOME` | Where the sign-in, quotes, jobs and the last tool catalog are kept. Default: `~/.config/loopcut-connector`, or `%APPDATA%\loopcut-connector` on Windows. It is separate from the Loopcut app's own files |
|
|
130
|
+
|
|
131
|
+
**Private mode and no-generation.** These account settings apply here too. Private mode (on by default on
|
|
132
|
+
paid plans) refuses tools whose provider may train on what it receives; today that is sound effects.
|
|
133
|
+
No-generation hides and refuses the generative tools. Change them in the Loopcut app or with
|
|
134
|
+
`loopcut settings`.
|
|
135
|
+
|
|
136
|
+
## How it works
|
|
137
|
+
|
|
138
|
+
1. **Quote.** Every tool goes through the same job API the Loopcut add-on uses. A quote prices the exact
|
|
139
|
+
request and lasts ten minutes; an expired one is renewed at start, and the job goes ahead only if the
|
|
140
|
+
price didn't change. Starting the same quote twice returns the same job.
|
|
141
|
+
2. **Files in.** File inputs are local paths. They're checked (type and size), addressed by SHA-256, and
|
|
142
|
+
only uploaded if the cloud doesn't already have them, straight to storage with presigned URLs. Big
|
|
143
|
+
files go in parts, each retried on its own.
|
|
144
|
+
3. **Render packing.** A background Blender (`-b --factory-startup --disable-autoexec`) opens the saved
|
|
145
|
+
`.blend` and runs the Loopcut add-on's own collector (`addon/bundles.py`, vendored). It copies every
|
|
146
|
+
texture, library, cache and sequencer file into a bundle with relative paths. That Blender gets a
|
|
147
|
+
private config folder and none of your environment's keys. The cloud never runs Python from your file,
|
|
148
|
+
and the renderer has no internet access.
|
|
149
|
+
4. **Files out.** Results are checked against their hashes and saved into `loopcut_renders/<job_id>/`
|
|
150
|
+
next to the `.blend` for a render, and `loopcut_outputs/<job_id>/` in the working folder for everything
|
|
151
|
+
else (or `out_dir`). The working folder is the client's workspace root when it shares one; Codex starts
|
|
152
|
+
plugin servers in the plugin's own folder, so there results go under your home folder instead. A
|
|
153
|
+
render's QA report flags black, white, flat and transparent frames, fireflies, duplicate frames and
|
|
154
|
+
exposure jumps.
|
|
155
|
+
|
|
156
|
+
If the catalog can't be read at startup, the server uses the last catalog it read from that service, then
|
|
157
|
+
the snapshot in `src/loopcut/catalog.json`.
|
|
158
|
+
|
|
159
|
+
**What it runs and connects to.** `uv`, which provides Python (and downloads it if needed); your Blender,
|
|
160
|
+
headless, only to pack a scene for `render_quote`; and your browser, only to open the Loopcut sign-in
|
|
161
|
+
page. It connects to `loopcut.org` (the catalog, sign-in and job API) and to the presigned storage URLs
|
|
162
|
+
that API hands out for uploads and downloads. It sends no analytics.
|
|
163
|
+
|
|
164
|
+
## Privacy Policy
|
|
165
|
+
|
|
166
|
+
Loopcut's privacy policy is at [loopcut.org/privacy](https://loopcut.org/privacy). For this connector:
|
|
167
|
+
|
|
168
|
+
- **What it sends.** The Loopcut key on every request, with the client's name and version. For a render,
|
|
169
|
+
the packed scene: the saved `.blend` and every texture, library and cache it uses. For other tools, the
|
|
170
|
+
inputs you or the agent give (prompts, settings) and the files named as inputs (images, meshes). It reads
|
|
171
|
+
nothing else from your machine: no other files, no environment keys besides `LOOPCUT_API_KEY`, no open
|
|
172
|
+
Blender session.
|
|
173
|
+
- **Where it's stored.** Loopcut's database (Supabase, US) holds your account, quotes and jobs; uploaded
|
|
174
|
+
files and results are kept in Cloudflare R2.
|
|
175
|
+
- **Third parties.** Each job goes only to the provider that runs it: today OpenAI, fal and the model
|
|
176
|
+
developers it runs (Deemos for Rodin, Tripo), Google Cloud, ElevenLabs and RunPod. Providers' own
|
|
177
|
+
retention terms are on the privacy page. Private mode, on by default for paid plans, refuses tools whose
|
|
178
|
+
provider may train on what it receives (today, sound effects).
|
|
179
|
+
- **Retention.** Uploaded files are deleted 7 days after their last use and results 30 days after theirs.
|
|
180
|
+
Account and billing records are kept while the account exists. Results you download are ordinary files
|
|
181
|
+
on your machine.
|
|
182
|
+
- **On your machine.** The key, quotes, jobs and the last tool catalog are kept in
|
|
183
|
+
`~/.config/loopcut-connector`, readable only by you. `loopcut logout` removes the key.
|
|
184
|
+
- **Contact.** hello@loopcut.org, also for exporting or deleting your account data.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
PYTHONPATH=src python3 -m unittest discover -s tests # MCP protocol and unit tests
|
|
190
|
+
LOOPCUT_TEST_BLENDER=/path/to/blender PYTHONPATH=src python3 -m unittest tests.test_blender_pack
|
|
191
|
+
python3 scripts/snapshot_catalog.py # refresh the fallback catalog before a release
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The end-to-end tests run against Loopcut's contract mock, which lives in the service repository; they're
|
|
195
|
+
skipped here. `addon/VENDORED.md` explains how the add-on copies are kept in sync.
|
|
196
|
+
|
|
197
|
+
Released under the GPL-3.0-or-later. Loopcut is an independent project; Blender is a trademark of the
|
|
198
|
+
Blender Foundation.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
loopcut/__init__.py,sha256=Vfi9rWzegZCEgZRlyfRaelzzSiFLmJJiE6FirukULw4,272
|
|
2
|
+
loopcut/__main__.py,sha256=ifaxB2AjCol94-nP2LzRaVc7KSe5sQ9qyVPaDmSwq5c,295
|
|
3
|
+
loopcut/agent_tools.json,sha256=Sc3nuzHW7GiZ5T6x8QNzFZIOFvJAADMVBEJlw9xUBTU,2226
|
|
4
|
+
loopcut/catalog.json,sha256=lxxvcoarE_Uu5Zv22WB1GtEUgOKOGyKYxdKodiu3PjA,28387
|
|
5
|
+
loopcut/catalog.py,sha256=AK5rlSFvT1JugAyQHFZq4z4j-IXvQcOBZAeHnh_a1Eo,4079
|
|
6
|
+
loopcut/cli.py,sha256=2TC9x7mKK5-vgTWOLcN-BtwdTk7KIVtfw1aIzRGQExM,12130
|
|
7
|
+
loopcut/mcp_server.py,sha256=Z2Ug5RVbI-3mDSRlw6mzBQ6ri5PuKE7NmvOi-l4eD9A,18863
|
|
8
|
+
loopcut/pack.py,sha256=c9uSqtHpW4y7xzdAa6q92KuBW71wuDjilFXYF1ZQrNs,7892
|
|
9
|
+
loopcut/service.py,sha256=W5jh_A0OUK7ugPQEA19M_m7kYuoTCUFZ4matCIwzL2E,45870
|
|
10
|
+
loopcut/state.py,sha256=Su6m8k-zSBAw4fdKbM4l_QqIaW7ytMYZIqOpt07npiI,2797
|
|
11
|
+
loopcut/addon/VENDORED.md,sha256=gFi32OTZY0lOyBSOStHdVdHCLsDWA22nLj5gY4RYWMA,882
|
|
12
|
+
loopcut/addon/__init__.py,sha256=d9bmFgHVKIwSi4ahSjG8gnV7qPbUcKOxrqcC6OXZ2Ms,416
|
|
13
|
+
loopcut/addon/blobs.py,sha256=V_rizBviQiARoM7dniEc5wcrCFrCwhZ8dORv3ZYDMYw,21127
|
|
14
|
+
loopcut/addon/bundles.py,sha256=aqk5yXC2sgbxnYwB_KJoiWuhg12ePdswSnkPCf1ie_E,28953
|
|
15
|
+
loopcut/addon/cloud_http.py,sha256=omJUCsYm1D9nR4QwVV3s7qWkeNvDjaqYnm5TAvL-iEc,11569
|
|
16
|
+
loopcut/addon/config.py,sha256=WdAAeSHKb9vr1tg2OGXgfs9YZ26xq_Dbjg0xJCADCQ0,190
|
|
17
|
+
loopcut/addon/credentials.py,sha256=J6dPHG_zunc4K_gnPyEoq1c8at9YgocabqmT7zqRzKM,212
|
|
18
|
+
loopcut-0.1.0.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
19
|
+
loopcut-0.1.0.dist-info/METADATA,sha256=GyAY9ZYk5h7bapU7wWcgn0tXOzp21AwUpshvP2TwHUU,10958
|
|
20
|
+
loopcut-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
21
|
+
loopcut-0.1.0.dist-info/entry_points.txt,sha256=uZ6M5Aq3Eba9bsHcDb1T9wtke5rQF0pLFOdQhDSNsZs,45
|
|
22
|
+
loopcut-0.1.0.dist-info/top_level.txt,sha256=Ea17ZV-WdAtQTBeD13TOslzcQswftry0C2_UNpDVWHw,8
|
|
23
|
+
loopcut-0.1.0.dist-info/RECORD,,
|