@introspection-ai/operator 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -20,20 +20,28 @@ npx @introspection-ai/operator ~/work/my-agent # start or resume a buil
20
20
  npx @introspection-ai/operator ~/work/my-agent --model <model> # flags after the folder go to Pi
21
21
  ```
22
22
 
23
- `npx @introspection-ai/operator` runs the package's launcher, `bin/introspection-operator.js`. It starts the build agent (`pi --recipe <the package> --agent build --no-context-files -e bin/local-workspace.mjs`) in a folder of its own, because build treats the folder it starts in as the owner's workspace: it creates the agent's repository there and resumes from what it finds there.
23
+ `npx @introspection-ai/operator` runs the package's launcher, `bin/introspection-operator.js`. It starts the build agent (`pi --recipe <the package> --agent build --no-context-files`) in a folder of its own, because build treats the folder it starts in as the owner's workspace: it creates the agent's repository there and resumes from what it finds there.
24
24
  - **No AGENTS.md or CLAUDE.md:** it turns off Pi's loading of those files from that folder and the folders above it. They're written for coding agents and can describe other agents, which build must not copy.
25
25
  - **Folder safety:** it refuses a folder inside the package or nested in another git repository; the agent's own repository is allowed, to resume.
26
- - **Python:** it builds the Recipe's Python environment (`package.json#pi.runtime.python`: paper-docx, pypdf, pypdfium2 and pillow, locked in `python/uv.lock`) with `uv sync --frozen` into `~/.introspection/operator-python/<version>-<lockfile hash>` (outside the package, which a global install may not let it write to; rebuilt only when the pins change), checks the declared imports, and starts Pi with it as `VIRTUAL_ENV`, so the `documents` tool can read and mark up Word and PDF files. Pi doesn't build `pi.runtime.python` itself.
27
- - **Shared documents:** it creates `files/` and `outputs/` in the build folder, beside the agent's repository, and loads `bin/local-workspace.mjs`, which tells the agent where they are, as the cloud does with `/workspace/files` and `/workspace/outputs`. Put the documents you'll share in `files/`: the documents tool opens files only inside the build folder. Given a path elsewhere, the agent copies the file into `files/` first.
26
+ - **Python:** it checks that `uv` is installed. The Operator has no Python of its own: each documents package brings its own, and Pi builds it when the session starts (Recipes 0.39.0 and later).
27
+ - **Shared documents:** it creates `files/` and `outputs/` in the build folder, beside the agent's repository. The Recipe's workspace extension (`extensions/workspace`) tells the agent where they are, as the cloud does with `/workspace/files` and `/workspace/outputs`. Put the documents you'll share in `files/`: the documents tool opens files only inside the build folder. Given a path elsewhere, the agent copies the file into `files/` first.
28
28
  - **Versions:** it checks that Pi's `@introspection-ai/recipes` satisfies the `peerDependencies` range, and says to run `introspection upgrade` when it doesn't.
29
29
 
30
30
  Set `INTROSPECTION_AGENTS_DIR` to put new builds somewhere other than `~/.introspection/agents`.
31
31
 
32
32
  In a checkout of this repository, `pnpm build` runs the same launcher, and installs the dependencies with pnpm once.
33
33
 
34
+ The launcher is being retired. `introspection local`, in a folder with no Recipe, starts the same session from the copy `pi install npm:@introspection-ai/operator` puts in Pi's package folder, which `introspection setup` installs.
35
+
36
+ ### Installed with `pi install`
37
+
38
+ `package.json#pi` is read twice: by Recipes when the package runs as a Recipe, and by Pi when it's installed as a Pi package, which loads its extensions and skills in every session.
39
+ - **Extensions** (`extensions/case-runs`, `extensions/workspace`) act only where Recipes loads them: each asks Recipes for its session (`getRecipeSessionContext`), and an instance Pi loaded as a plain package has none, so it registers nothing. The workspace extension acts only in the Operator's own `build` session.
40
+ - **Skills** are listed in `pi.skills`, because an explicit `pi` block turns off Pi's discovery of `skills/`. Pi then offers them in every session, including an owner's own Recipe runs. Pass `--no-skills` to a Recipe launch to keep them out: Recipes still adds the Recipe's own skills.
41
+
34
42
  ## Package
35
43
 
36
- `@introspection-ai/operator` is the npm package published from this repository's root. It contains the Recipe a person runs (`SYSTEM.md`, `agents/`, `skills/`, and `python/pyproject.toml` and `uv.lock` for its documents tool) and its launcher (`bin/`), so `npx @introspection-ai/operator` starts a build, and `npx skills sync` finds the skills in it. `docs/`, `scripts/` and the evals are never published.
44
+ `@introspection-ai/operator` is the npm package published from this repository's root. It contains the Recipe a person runs (`SYSTEM.md`, `agents/`, `skills/` and `extensions/`) and its launcher (`bin/`), so `npx @introspection-ai/operator` starts a build, and `npx skills sync` finds the skills in it. `docs/`, `scripts/` and the evals are never published.
37
45
 
38
46
  Skills are grouped by the stage of an agent's life (see [`docs/DESIGN.md`](docs/DESIGN.md)), and the group is the prefix of each skill's name, because `npx skills sync` finds skills only at `skills/<name>/SKILL.md` and not in nested folders:
39
47
 
@@ -45,7 +53,7 @@ Skills are grouped by the stage of an agent's life (see [`docs/DESIGN.md`](docs/
45
53
  | `optimize` | `optimize-quality`, `optimize-cost`, `optimize-latency` | Raise quality, then cut cost, then latency, each holding the gains of the one before | Designed; replaces today's `optimize` |
46
54
  | `harden` | `harden-scan`, `harden-spar`, `harden-stress` | Last, and optional apart from the scan: check the Recipe against the OWASP Agentic Skills Top 10, spar with it as a red team, and test it under load; failures become permanent cases | Designed; new, with no workflow behind it today |
47
55
 
48
- Releases are cut by release-please (`.github/workflows/release.yml`): Conventional Commit titles on `main` update a release pull request, and merging it tags `operator-vX.Y.Z` and publishes to npm through trusted publishing, starting at 0.1.0. CI runs `node scripts/check-skills.mjs` (frontmatter, folder names, and that exactly the Recipe and its launcher are packed: `skills/`, `agents/`, `SYSTEM.md`, `python/pyproject.toml`, `python/uv.lock` and `bin/introspection-operator.js`) and `npm pack --dry-run`; publishing also requires at least one skill.
56
+ Releases are cut by release-please (`.github/workflows/release.yml`): Conventional Commit titles on `main` update a release pull request, and merging it tags `operator-vX.Y.Z` and publishes to npm through trusted publishing, starting at 0.1.0. CI runs `node scripts/check-skills.mjs` (frontmatter, folder names, and that exactly the Recipe and its launcher are packed: `skills/`, `agents/`, `extensions/`, `SYSTEM.md` and `bin/introspection-operator.js`; that `pi.skills` lists `skills`; and that there's no `pi.runtime.python`) and `npm pack --dry-run`; publishing also requires at least one skill.
49
57
 
50
58
  ## License
51
59
 
package/SYSTEM.md CHANGED
@@ -6,6 +6,6 @@ Owners are usually business owners, not engineers. Speak plainly, ask only what
6
6
 
7
7
  When you need a document from the owner, ask them to share it: as an upload, unless the workspace says how this owner shares files. Documents the owner gives you are of two kinds. The owner's own standing material (their playbook, guidelines, standard terms, templates) is meant to be built into the agent: read it when they give it to you and commit it with the Recipe. Material that arrives with a request (a customer's contract, an inbox) is a case input: read it where it was shared, refer to it by content hash, and never commit it or copy it into the agent's repository.
8
8
 
9
- To read or show a Word or PDF document, use the `documents` tool: it reads text, tables and comments, renders scanned pages as images, and for Word makes tracked changes and comments, so you can show the owner a real example markup on their own contract. Without it, note that Pi's `read` tool handles text and images but returns Word and PDF files as raw bytes. Extract their text instead, preferring what's installed: `pdftotext` for PDF (the platform image always has it) and an installed `python-docx` for Word. Only when developing locally without them, use `uv run --with pypdf python` or `uv run --with python-docx python`; on the platform nothing is downloaded while you run. Never call a document unreadable or image-based without trying text extraction first; if it truly has no text, say so plainly and ask for another copy.
9
+ To read or show a Word or PDF document, use the `documents` tool: it reads text, tables and comments, renders scanned pages as images, and for Word makes tracked changes and comments, so you can show the owner a real example markup on their own contract. Don't use Pi's `read` tool, shell PDF tools or Python scripts for Word or PDF files. Never call a document unreadable or image-based without reading it with the tool, and rendering its pages when it has no text; if it truly has no text, say so plainly and ask for another copy.
10
10
 
11
11
  Never enter or ask to see credentials, and never push, deploy or connect real data unless the current step says to.
@@ -7,10 +7,12 @@
7
7
  //
8
8
  // In a checkout of this repository, `pnpm build` runs this same script.
9
9
  //
10
+ // Being retired: `introspection local`, in a folder with no Recipe, starts the Operator the same way, from the
11
+ // package `pi install` put in Pi's package folder. This launcher goes once that CLI release is out.
12
+ //
10
13
  // Build treats the folder it starts in as the owner's workspace: it creates the agent's repository
11
14
  // there and resumes from what it finds, so it must not start inside this package or another repository.
12
15
  import { execFileSync, spawnSync } from "node:child_process";
13
- import { createHash } from "node:crypto";
14
16
  import { existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
15
17
  import { homedir } from "node:os";
16
18
  import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -71,30 +73,10 @@ if (min) {
71
73
  }
72
74
  }
73
75
 
74
- // The Recipe's Python (package.json#pi.runtime.python): the documents tool runs its Word and PDF helpers with
75
- // VIRTUAL_ENV's python. Pi doesn't build that environment, so build it here from the committed lockfile, never
76
- // into system Python, and refuse to start without it. It goes under ~/.introspection, not into the package:
77
- // a global install can sit in a folder that needs sudo. Keyed by version and lockfile, so it's rebuilt only
78
- // when the pins change and versions sit side by side.
79
- const python = pkg.pi?.runtime?.python;
80
- if (python) {
81
- if (!has("uv")) fail("uv is not installed; the documents tool needs it. Install it: https://docs.astral.sh/uv/getting-started/installation/");
82
- const project = join(root, python.project);
83
- const lock = readFileSync(join(root, python.lockfile));
84
- const key = createHash("sha256").update(lock).digest("hex").slice(0, 12);
85
- const venv = join(homedir(), ".introspection", "operator-python", `${pkg.version}-${key}`);
86
- const uvEnv = { ...env, UV_PROJECT_ENVIRONMENT: venv };
87
- const sync = spawnSync("uv", ["sync", "--frozen", "--no-install-project", "--quiet", "--project", project], { env: uvEnv, stdio: "inherit", shell: process.platform === "win32" });
88
- if (sync.status !== 0) fail(`couldn't build the Recipe's Python environment from ${join(root, python.lockfile)}`);
89
- const bin = join(venv, process.platform === "win32" ? "Scripts" : "bin");
90
- env.VIRTUAL_ENV = venv;
91
- env.PATH = [bin, env.PATH].join(delimiter);
92
- delete env.PYTHONHOME;
93
- const imports = python.imports ?? [];
94
- if (imports.length > 0) {
95
- const check = spawnSync(join(bin, "python"), ["-c", imports.map((name) => `import ${name}`).join("; ")], { env, stdio: "inherit" });
96
- if (check.status !== 0) fail(`the Recipe's Python environment is missing one of: ${imports.join(", ")}`);
97
- }
76
+ // The documents packages (package.json#pi.documents) bring their own Python: Pi builds it with uv when the
77
+ // session starts (Recipes 0.39.0 and later; the peer check above requires 0.40.0), so only uv has to be here.
78
+ if (pkg.pi?.documents?.length && !has("uv")) {
79
+ fail("uv is not installed; the documents tool needs it. Install it: https://docs.astral.sh/uv/getting-started/installation/");
98
80
  }
99
81
 
100
82
  // The folder: the first argument when it isn't a flag, else the next new-<n>. A relative folder resolves
@@ -135,13 +117,13 @@ mkdirSync(target, { recursive: true });
135
117
  target = realpathSync(target);
136
118
  // The local equivalent of the cloud's workspace: files/ for the documents the owner shares (the documents tool
137
119
  // opens files only inside the build folder) and outputs/ for what the agent produces, both beside the agent's
138
- // repository, not in it. local-workspace.mjs tells the agent where they are.
120
+ // repository, not in it. The Recipe's workspace extension (extensions/workspace) tells the agent where they are.
139
121
  for (const dir of ["files", "outputs"]) mkdirSync(join(target, dir), { recursive: true });
140
122
  console.error(`introspection-operator: put the documents you'll share in ${join(target, "files")}`);
141
123
  console.error(`introspection-operator: starting in ${target}`);
142
124
  // Pi would load AGENTS.md and CLAUDE.md from this folder and every folder above it. Those are written for
143
125
  // coding agents and can describe other agents, which build must not copy; the Recipe brings its own.
144
- const result = spawnSync("pi", ["--recipe", root, "--agent", "build", "--no-context-files", "-e", join(root, "bin", "local-workspace.mjs"), ...args], {
126
+ const result = spawnSync("pi", ["--recipe", root, "--agent", "build", "--no-context-files", ...args], {
145
127
  cwd: target,
146
128
  env,
147
129
  stdio: "inherit",
@@ -1,16 +1,21 @@
1
1
  // Tools the build agent runs an agent's cases with: run_case starts one run of the Recipe being built in the
2
2
  // background, case_status says what it's doing, stop_case ends it. The runs themselves are in runs.mjs, and
3
3
  // what their events mean in events.mjs.
4
+ //
5
+ // They load only where Recipes loads them, as one of the Recipe's extensions. Installed with `pi install`, Pi
6
+ // also loads them in every session, where they register nothing (recipe-session.mjs).
4
7
  import { relative } from "node:path";
5
8
  import { Type } from "typebox";
6
9
  import { minutes } from "./events.mjs";
7
10
  import { createCaseRuns, DEFAULT_BUDGET_MINUTES, MAX_WAIT_SECONDS } from "./runs.mjs";
11
+ import { recipeSession } from "../shared/recipe-session.mjs";
8
12
 
9
13
  function text(value) {
10
14
  return { content: [{ type: "text", text: value }], details: {} };
11
15
  }
12
16
 
13
- export default function caseRuns(pi) {
17
+ export default async function caseRuns(pi) {
18
+ if (!(await recipeSession(pi))) return;
14
19
  const caseRuns = createCaseRuns();
15
20
 
16
21
  function find(id) {
@@ -0,0 +1,29 @@
1
+ // Which Recipe session, if any, loaded an extension instance.
2
+ //
3
+ // The Operator runs as a Recipe (`introspection init --operator` starts `pi --recipe <operator>`), and only then
4
+ // should its extensions act. Recipes binds a session context to every extension instance it loads
5
+ // (`getRecipeSessionContext`); an instance without one, or from another Recipe, does nothing.
6
+ //
7
+ // The import is static on purpose: Recipes' loader points a static import of `@introspection-ai/recipes` at the
8
+ // host's copy, which a dynamic `import()` from the Operator's own folder can't reach once the Operator is installed
9
+ // without its peers (`~/.introspection/operator`).
10
+ import { readFileSync } from "node:fs";
11
+
12
+ import { getRecipeSessionContext } from "@introspection-ai/recipes/extensions";
13
+
14
+ /** The name the package runs under as a Recipe: its package.json name. */
15
+ export const OPERATOR = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).name;
16
+
17
+ /** The Recipe session context bound to this instance, or undefined when Recipes didn't load it. */
18
+ export async function recipeSession(pi) {
19
+ try {
20
+ return getRecipeSessionContext(pi);
21
+ } catch {
22
+ return undefined;
23
+ }
24
+ }
25
+
26
+ /** True for the Operator's own build session: this Recipe, its build agent, and the root session. */
27
+ export function isOperatorBuild(session) {
28
+ return session?.recipe?.name === OPERATOR && session?.agent?.name === "build" && session?.session?.role === "root";
29
+ }
@@ -0,0 +1,38 @@
1
+ // Tells the Operator's build agent where the local workspace's folders are, as the cloud host does for every
2
+ // session, so the Recipe itself says the same thing locally and in the cloud and holds no local paths. It also
3
+ // creates files/ and outputs/ when they're missing, and keeps the build agent's launch out of the commands it runs
4
+ // (workspace.mjs).
5
+ //
6
+ // It acts only in the Operator's own build session (recipe-session.mjs). Installed with `pi install`, Pi loads it
7
+ // in every session, where it does nothing.
8
+ //
9
+ // It adds a message to the conversation rather than changing the system prompt: with a root SYSTEM.md, the
10
+ // Recipes extension replaces Pi's whole prompt, so appended text (`--append-system-prompt`, or a
11
+ // before_agent_start handler that runs first) never reaches the model. The message is added once per session,
12
+ // so continuing a session with `-c` doesn't repeat it.
13
+ import { isOperatorBuild, recipeSession } from "../shared/recipe-session.mjs";
14
+ import { ensureFolders, MESSAGE_TYPE, withoutOperatorEnv, workspaceMessage } from "./workspace.mjs";
15
+
16
+ export default async function workspace(pi) {
17
+ registerWorkspace(pi, await recipeSession(pi));
18
+ }
19
+
20
+ /** Register the workspace handlers when the session is the Operator's build session; else do nothing. */
21
+ export function registerWorkspace(pi, session) {
22
+ if (!isOperatorBuild(session)) return false;
23
+ // Recipes loads its extensions as the session starts, in the build folder: create the folders now, so the
24
+ // owner can put documents in files/ before the first message, and again before each turn in case they went.
25
+ ensureFolders(process.cwd());
26
+ pi.on("tool_call", (event) => {
27
+ if (event.toolName === "bash" && typeof event.input?.command === "string") {
28
+ event.input.command = withoutOperatorEnv(event.input.command);
29
+ }
30
+ });
31
+ pi.on("before_agent_start", (_event, ctx) => {
32
+ const root = ctx.cwd ?? process.cwd();
33
+ ensureFolders(root);
34
+ if (ctx.sessionManager.getEntries().some((entry) => entry.type === "custom_message" && entry.customType === MESSAGE_TYPE)) return;
35
+ return { message: { customType: MESSAGE_TYPE, content: workspaceMessage(root), display: false } };
36
+ });
37
+ return true;
38
+ }
@@ -0,0 +1,55 @@
1
+ // The local workspace for a build on the owner's computer: the part of the workspace extension that doesn't need
2
+ // Pi, so it can be tested on its own.
3
+ import { mkdirSync } from "node:fs";
4
+ import { delimiter, join } from "node:path";
5
+
6
+ export const MESSAGE_TYPE = "operator-workspace";
7
+
8
+ // Commands the agent runs must not inherit the build agent's own launch. The Recipes extension exports
9
+ // PI_AGENT_NAME (`build`) for shell tools, and a Recipe launch reads it as its default agent, so
10
+ // `introspection local` in the agent's repository would look for an agent named `build` there and not start.
11
+ //
12
+ // The same goes for an activated Python: a Recipe run with `introspection local` that inherits one uses it, so it
13
+ // passes without an environment of its own and fails for its owner and in the cloud. The Operator declares no
14
+ // Python of its own (each documents package brings its own, which is never activated), but the shell Pi started
15
+ // in may have one.
16
+ const UNSET = ["PI_AGENT_NAME", "VIRTUAL_ENV"];
17
+
18
+ /** The command with the Operator's launch removed from its environment. Windows shells are left as they are. */
19
+ export function withoutOperatorEnv(command, env = process.env, platform = process.platform) {
20
+ if (platform === "win32") return command;
21
+ const unset = `unset ${UNSET.join(" ")}`;
22
+ if (!env.VIRTUAL_ENV) return `${unset}\n${command}`;
23
+ const bin = join(env.VIRTUAL_ENV, "bin");
24
+ const path = (env.PATH ?? "").split(delimiter).filter((dir) => dir !== bin).join(delimiter);
25
+ return `${unset}; export PATH='${path.replaceAll("'", `'\\''`)}'\n${command}`;
26
+ }
27
+
28
+ /** Create files/ and outputs/ beside the agent's repository, if they're missing. */
29
+ export function ensureFolders(root) {
30
+ for (const dir of ["files", "outputs"]) mkdirSync(join(root, dir), { recursive: true });
31
+ }
32
+
33
+ /**
34
+ * What the build agent is told about the workspace, as the cloud host tells every session (introspection-cloud
35
+ * apps/runtime-agent/runtime/resources/context.ts, "Managed workspace").
36
+ */
37
+ export function workspaceMessage(root) {
38
+ const files = join(root, "files");
39
+ const outputs = join(root, "outputs");
40
+ return [
41
+ "## Managed workspace",
42
+ "",
43
+ "<workspace>",
44
+ ` <inputs path="${files}" access="read_write" persistence="local" />`,
45
+ ` <outputs path="${outputs}" persistence="local" />`,
46
+ " Create the agent's repository in this folder, beside files/ and outputs/.",
47
+ "</workspace>",
48
+ `This build runs on the owner's computer, and the documents tool opens files only inside ${root}. ` +
49
+ `The owner shares a document by giving you its path, or by putting it in ${files}: ask for it that way ` +
50
+ `("send me the path to it, or put it in ${files}"), not as an upload. ` +
51
+ "When the owner gives you a path to a file outside files/, copy it into files/ first and read the copy; " +
52
+ "don't try the original path, which the tool refuses. Never copy a file straight into the agent's repository. " +
53
+ "What belongs in the Recipe is added from files/.",
54
+ ].join("\n");
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@introspection-ai/operator",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "The Introspection Operator: a Recipe and skills that build an agent with its owner (brainstorm, draft and review).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,16 +20,18 @@
20
20
  "agents",
21
21
  "SYSTEM.md",
22
22
  "bin",
23
- "extensions",
24
- "python/pyproject.toml",
25
- "python/uv.lock"
23
+ "extensions"
26
24
  ],
27
25
  "publishConfig": {
28
26
  "access": "public"
29
27
  },
30
28
  "pi": {
31
29
  "extensions": [
32
- "extensions/case-runs"
30
+ "extensions/case-runs",
31
+ "extensions/workspace"
32
+ ],
33
+ "skills": [
34
+ "skills"
33
35
  ],
34
36
  "documents": [
35
37
  {
@@ -38,31 +40,16 @@
38
40
  {
39
41
  "format": "pdf"
40
42
  }
41
- ],
42
- "runtime": {
43
- "python": {
44
- "project": "python",
45
- "lockfile": "python/uv.lock",
46
- "version": ">=3.12,<3.15",
47
- "imports": [
48
- "docx",
49
- "pypdf",
50
- "pypdfium2",
51
- "PIL"
52
- ]
53
- }
54
- }
43
+ ]
55
44
  },
56
45
  "type": "module",
57
46
  "scripts": {
58
47
  "build": "node bin/introspection-operator.js"
59
48
  },
60
49
  "peerDependencies": {
61
- "@introspection-ai/recipes": ">=0.37.0"
62
- },
63
- "dependencies": {
64
- "@introspection-ai/recipe-documents-docx": ">=0.1.0",
65
- "@introspection-ai/recipe-documents-pdf": ">=0.1.0"
50
+ "@introspection-ai/recipes": ">=0.40.0",
51
+ "@introspection-ai/recipe-documents-docx": ">=0.3.0",
52
+ "@introspection-ai/recipe-documents-pdf": ">=0.2.0"
66
53
  },
67
54
  "packageManager": "pnpm@12.9.1",
68
55
  "bin": {
@@ -4,7 +4,7 @@ Check this list before writing code. Each entry says when a case needs it.
4
4
 
5
5
  | Capability | Use when a case needs to | How a Recipe gets it |
6
6
  | --- | --- | --- |
7
- | `documents` | read, edit with tracked changes, comment on or compare Word or PDF files | `pi.documents` with a `format` (`docx`, `pdf`) per entry, plus the format's package `@introspection-ai/recipe-documents-<format>` as a dependency; for contract redlines add `"profile": "legal-redlining-en-US"` to the docx entry. Needs `@introspection-ai/recipes` 0.37.0 or later and a CLI that accepts `pi.documents`; with an older install, use the fallback in [documents](documents.md) and say so in Part B. For PDFs with tables, add the pdf package's `tables` extra |
7
+ | `documents` | read, edit with tracked changes, comment on or compare Word or PDF files | `pi.documents` with a `format` (`docx`, `pdf`) per entry, and the format's package `@introspection-ai/recipe-documents-<format>` as a peer (Recipes 0.40.0 or later installs it alongside itself); for contract redlines add `"profile": "legal-redlining-en-US"` to the docx entry. If the CLI or Recipes is too old, run `introspection upgrade`; see [documents](documents.md). For PDFs with tables, add the pdf package's `tables` extra |
8
8
  | Channels | receive or answer messages in Slack or another chat surface | a channel adapter package (e.g. `@introspection-ai/recipe-channel-slack`) as a dependency, and the channel's declaration in `package.json#pi` |
9
9
  | MCP connectors | read or write in a service (email, Drive, a CRM, an internal API) | an MCP declaration in the Recipe; locally a fake, on the platform a connection the owner makes in deploy |
10
10
  | Scheduling | run on a timetable or once at a set time | a platform automation created at deploy, not code in the Recipe |
@@ -2,84 +2,23 @@
2
2
 
3
3
  Plain text loses what a reviewer needs: a normal paragraph read drops tracked insertions and deletions without saying so. Change a document with tracked changes, never a clean retype, and never overwrite the original.
4
4
 
5
- ## First choice: the `documents` tool
5
+ ## The `documents` tool
6
6
 
7
- Declare each format and add its package (`@introspection-ai/recipes` 0.37.0 or later):
7
+ Declare each format, and its package as a peer: Recipes installs the docx and pdf packages alongside itself, so the platform provides them (`@introspection-ai/recipes` 0.40.0 or later):
8
8
 
9
9
  ```json
10
- "dependencies": {
11
- "@introspection-ai/recipe-documents-docx": ">=0.1.0 <1.0.0",
12
- "@introspection-ai/recipe-documents-pdf": ">=0.1.0 <1.0.0"
10
+ "peerDependencies": {
11
+ "@introspection-ai/recipe-documents-docx": ">=0.4.0",
12
+ "@introspection-ai/recipe-documents-pdf": ">=0.3.0"
13
13
  },
14
14
  "pi": { "documents": [{ "format": "docx" }, { "format": "pdf" }] }
15
15
  ```
16
16
 
17
- and list `documents` in the agent's `tools`.
17
+ and list `documents` in the agent's `tools`. Pin a package (in `dependencies`, with an exact version) only when the agent must stay on the version it was tested with. The packages bring their own Python, so the Recipe needs no `pi.runtime.python` for documents.
18
18
 
19
- For a kind of work a format ships a profile for, add it to that format's entry: `{ "format": "docx", "profile": "legal-redlining-en-US" }` for contract redlines and responses (Recipes 0.38.0 or later). The tool then offers the profile's skill beside its core one; don't list either in the agent's `skills` or name them in the instructions. Put the owner's own positions and voice in the Recipe's skills, not in a copy of the profile. For other kinds of work, leave `profile` out. A `commands` list on a format narrows what it may do, for example `["read", "comment"]` for an agent that only reviews. Then run `introspection check` and `introspection local -p "hello"`. If both pass, use the tool's commands for reading, redlining, commenting and comparing, and write no scripts for them.
19
+ For a kind of work a format ships a profile for, add it to that format's entry: `{ "format": "docx", "profile": "legal-redlining-en-US" }` for contract redlines and responses. The tool then offers the profile's skill beside its core one; don't list either in the agent's `skills` or name them in the instructions. Put the owner's own positions and voice in the Recipe's skills, not in a copy of the profile. For other kinds of work, leave `profile` out. A `commands` list on a format narrows what it may do, for example `["read", "comment"]` for an agent that only reviews. Then run `introspection check` and `introspection local -p "hello"`. If both pass, use the tool's commands for reading, redlining, commenting and comparing, and write no scripts for them.
20
20
 
21
- If `check` reports `package.json#pi contains unknown key 'documents'`, the installed CLI predates the tool: update it (`npm i -g @introspection-ai/cli`). Use the fallback below only if no release supports it, and say so in Part B: "`documents` tool not available in CLI <version>; used paper-docx in the Recipe's Python environment instead." Record each case's result as it actually ran; don't carry over numbers measured another way.
22
-
23
- ## Fallback: paper-docx in the Recipe's Python
24
-
25
- `paper-docx` is a fork of python-docx that owns the `docx` import. Never install upstream `python-docx` beside it. Ask the library for its API: `help(docx.search.Span.replace)`.
26
-
27
- 1. Declare the project and lock it (installed when the runtime is baked, never at run time):
28
-
29
- ```toml
30
- # python/pyproject.toml
31
- [project]
32
- name = "recipe-documents"
33
- version = "0.1.0"
34
- requires-python = ">=3.12,<3.15"
35
- dependencies = ["paper-docx==0.2.0"]
36
-
37
- [tool.uv]
38
- constraint-dependencies = ["python-docx<0"]
39
- ```
40
-
41
- ```sh
42
- uv lock --project python # commit python/pyproject.toml and python/uv.lock
43
- ```
44
-
45
- ```json
46
- "runtime": { "python": { "project": "python", "lockfile": "python/uv.lock", "version": ">=3.12,<3.15", "imports": ["docx"] } }
47
- ```
48
-
49
- (in `package.json#pi`). For PDFs add `pypdf[crypto]==6.21.4` and `pypdfium2==5.14.0`, with their imports `pypdf` and `pypdfium2`.
50
-
51
- 2. **Reading:** copy [view_docx.py](view_docx.py) into the Recipe as `lib/view_docx.py`. It prints the text with tracked changes as `[+new+]` / `[-old-]` and comments inline, each with author and date, and starts with a count of changes and comments to check against.
52
-
53
- 3. **Editing:** give the agent a `documents` skill with this pattern, and `bash` in its tools:
54
-
55
- ```python
56
- # python edit_docx.py <original> <new file> <author>
57
- import sys
58
- import docx
59
- from docx.package import pending_changes, text_diff
60
- from docx.search import find_text
61
-
62
- original, marked, author = sys.argv[1:4]
63
- document = docx.Document(original)
64
- spans = find_text(document, "thirty days")
65
- if len(spans) != 1: # pass nth= or near= when the text appears more than once
66
- sys.exit(f"Expected one match, found {len(spans)}")
67
- spans[0].replace("sixty days", tracked=True, author=author)
68
- document.save(marked)
69
- print(pending_changes(marked)) # what the changes do if accepted
70
- print(text_diff(original, marked))
71
- ```
72
-
73
- Skill rules: save results as new files in an `outputs/` folder of the workspace (`/workspace/outputs/` on the platform); read every result back with `view_docx.py` before saying it's done; sign changes with the name the owner gave for the markup (ask if none); comments are `document.add_comment(runs, text=..., author=..., initials=...)`; a refusal from the library means unsupported, so say so and never edit raw XML; never accept or reject someone else's changes unless asked; run scripts with `python`, not a login shell (`bash -lc` loses the environment).
74
-
75
- 4. **Running locally:** `introspection local` validates the Python declaration but doesn't build it. Build it and start with it active:
76
-
77
- ```sh
78
- uv sync --project python
79
- VIRTUAL_ENV="$PWD/python/.venv" PATH="$PWD/python/.venv/bin:$PATH" introspection local
80
- ```
81
-
82
- Add `python/.venv/` and `__pycache__/` to `.gitignore`.
21
+ If either fails, find the cause before changing the Recipe: an unknown `pi.documents` key or an unknown profile means the installed CLI or Recipes is older than this needs, so run `introspection upgrade`. Never remove a profile, or replace the tool with scripts, to make the agent start; record the failure in Part B and ask the owner to upgrade if you can't.
83
22
 
84
23
  ## Inputs
85
24
 
@@ -6,7 +6,7 @@ What actually goes wrong when building and proving an agent, and what to do.
6
6
 
7
7
  - **A clean `introspection check` doesn't mean it runs.** It checks the package against itself: an MCP server with no endpoint anywhere, or a capability that can't exist, still passes. Only a run proves it.
8
8
  - **The check's errors are precise; act on them.** A missing field, an undeclared skill or bad YAML comes with the name and, for YAML, the line and column; an unknown field lists the valid ones. Fix from the message, not by copying another Recipe.
9
- - **`unknown key` under `package.json#pi`** means the installed CLI doesn't know that capability yet (for example `documents` in CLI 0.63). Use the fallback and say so in Part B; see [documents.md](documents.md).
9
+ - **`unknown key` under `package.json#pi`** means the installed CLI doesn't know that capability yet (for example `documents` in CLI 0.63). Run `introspection upgrade`; don't work around it. See [documents.md](documents.md).
10
10
  - **Model names don't transliterate.** A Recipe names a model as `provider/model`, not a provider's raw API id. Pick it from the documented list, and treat the template's model as a placeholder to decide.
11
11
  - **Extension code isn't checked when you write it.** A guessed registration API passes every check and fails at run time. Read the extension contract before writing one, and run it.
12
12
  - **"Pi is not installed" can mean Node is too old.** Run `introspection setup` and read what it reports before reinstalling Pi.
@@ -1,62 +0,0 @@
1
- // A Pi extension the launcher loads (`pi -e`) for a local build. It tells the build agent where the
2
- // workspace's folders are, as the cloud host does for every session (introspection-cloud
3
- // apps/runtime-agent/runtime/resources/context.ts, "Managed workspace"), so the Recipe itself says the same
4
- // thing locally and in the cloud and holds no local paths.
5
- //
6
- // It adds a message to the conversation rather than changing the system prompt: with a root SYSTEM.md, the
7
- // Recipes extension replaces Pi's whole prompt, so appended text (`--append-system-prompt`, or a
8
- // before_agent_start handler that runs first) never reaches the model. The message is added once per session,
9
- // so continuing a session with `-c` doesn't repeat it.
10
- import { delimiter, join } from "node:path";
11
-
12
- const TYPE = "operator-workspace";
13
-
14
- // Commands the agent runs must not inherit the build agent's own launch. The Recipes extension exports
15
- // PI_AGENT_NAME (`build`) for shell tools, and a Recipe launch reads it as its default agent, so
16
- // `introspection local` in the agent's repository looks for an agent named `build` there and doesn't start.
17
- // So every bash command starts by unsetting it. Windows shells are left as they are.
18
- //
19
- // The same goes for the Operator's own Python: the launcher starts Pi with it (VIRTUAL_ENV, its bin first on
20
- // PATH) for the Operator's documents tool. A Recipe run with `introspection local` that inherits it uses the
21
- // Operator's environment, so it passes without one of its own and fails for its owner and in the cloud.
22
- const operatorPython = process.env.VIRTUAL_ENV;
23
- const UNSET = ["PI_AGENT_NAME", "VIRTUAL_ENV"];
24
-
25
- function withoutOperatorEnv(command) {
26
- if (process.platform === "win32") return command;
27
- const unset = `unset ${UNSET.join(" ")}`;
28
- if (!operatorPython) return `${unset}\n${command}`;
29
- const bin = join(operatorPython, "bin");
30
- const path = (process.env.PATH ?? "").split(delimiter).filter((dir) => dir !== bin).join(delimiter);
31
- return `${unset}; export PATH='${path.replaceAll("'", `'\\''`)}'\n${command}`;
32
- }
33
-
34
- export default function localWorkspace(pi) {
35
- pi.on("tool_call", (event) => {
36
- if (event.toolName === "bash" && typeof event.input?.command === "string") {
37
- event.input.command = withoutOperatorEnv(event.input.command);
38
- }
39
- });
40
- pi.on("before_agent_start", (_event, ctx) => {
41
- if (ctx.sessionManager.getEntries().some((entry) => entry.type === "custom_message" && entry.customType === TYPE)) return;
42
- const root = ctx.cwd ?? process.cwd();
43
- const files = join(root, "files");
44
- const outputs = join(root, "outputs");
45
- const content = [
46
- "## Managed workspace",
47
- "",
48
- "<workspace>",
49
- ` <inputs path="${files}" access="read_write" persistence="local" />`,
50
- ` <outputs path="${outputs}" persistence="local" />`,
51
- " Create the agent's repository in this folder, beside files/ and outputs/.",
52
- "</workspace>",
53
- `This build runs on the owner's computer, and the documents tool opens files only inside ${root}. ` +
54
- `The owner shares a document by giving you its path, or by putting it in ${files}: ask for it that way ` +
55
- `("send me the path to it, or put it in ${files}"), not as an upload. ` +
56
- "When the owner gives you a path to a file outside files/, copy it into files/ first and read the copy; " +
57
- "don't try the original path, which the tool refuses. Never copy a file straight into the agent's repository. " +
58
- "What belongs in the Recipe is added from files/.",
59
- ].join("\n");
60
- return { message: { customType: TYPE, content, display: false } };
61
- });
62
- }
@@ -1,17 +0,0 @@
1
- # The Python the Recipe's `documents` tool runs on: paper-docx for Word, and pypdf, pypdfium2 and pillow for
2
- # PDF. Declared in package.json#pi.runtime.python; the launcher builds it with `uv sync --frozen`.
3
- [project]
4
- name = "introspection-operator-runtime"
5
- version = "0.0.0"
6
- requires-python = ">=3.12,<3.15"
7
- dependencies = [
8
- "paper-docx==0.2.0",
9
- "pypdf[crypto]==6.21.4",
10
- "pypdfium2==5.14.0",
11
- "pillow==13.0.0",
12
- ]
13
-
14
- [tool.uv]
15
- package = false
16
- # paper-docx provides the `docx` module; python-docx would shadow it.
17
- constraint-dependencies = ["python-docx<0"]
package/python/uv.lock DELETED
@@ -1,369 +0,0 @@
1
- version = 1
2
- revision = 5
3
- requires-python = ">=3.12, <3.15"
4
-
5
- [options]
6
- exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
7
- exclude-newer-span = "P3D"
8
-
9
- [options.exclude-newer-package]
10
- introspection-harbor = false
11
- introspection-recipe-check = false
12
- introspection-remote-proto = false
13
- introspection-sdk = false
14
-
15
- [manifest]
16
- constraints = [{ name = "python-docx", specifier = "<0" }]
17
-
18
- [[package]]
19
- name = "cffi"
20
- version = "2.1.1"
21
- source = { registry = "https://pypi.org/simple" }
22
- dependencies = [
23
- { name = "pycparser", marker = "implementation_name != 'PyPy'" },
24
- ]
25
- sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
26
- wheels = [
27
- { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" },
28
- { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" },
29
- { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" },
30
- { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" },
31
- { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" },
32
- { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" },
33
- { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" },
34
- { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" },
35
- { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" },
36
- { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" },
37
- { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" },
38
- { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" },
39
- { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
40
- { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
41
- { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
42
- { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
43
- { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
44
- { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
45
- { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
46
- { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
47
- { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
48
- { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
49
- { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
50
- { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
51
- { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
52
- { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
53
- { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" },
54
- { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" },
55
- { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" },
56
- { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" },
57
- { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" },
58
- { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" },
59
- { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" },
60
- { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" },
61
- { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" },
62
- { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" },
63
- { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" },
64
- { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" },
65
- { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" },
66
- { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" },
67
- { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" },
68
- { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" },
69
- { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" },
70
- { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" },
71
- { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" },
72
- { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" },
73
- { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" },
74
- { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" },
75
- { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" },
76
- { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" },
77
- ]
78
-
79
- [[package]]
80
- name = "cryptography"
81
- version = "50.0.2"
82
- source = { registry = "https://pypi.org/simple" }
83
- dependencies = [
84
- { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
85
- ]
86
- sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
87
- wheels = [
88
- { url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
89
- { url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
90
- { url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" },
91
- { url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" },
92
- { url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" },
93
- { url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" },
94
- { url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" },
95
- { url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" },
96
- { url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" },
97
- { url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" },
98
- { url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" },
99
- { url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" },
100
- { url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" },
101
- { url = "https://files.pythonhosted.org/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" },
102
- { url = "https://files.pythonhosted.org/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" },
103
- { url = "https://files.pythonhosted.org/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" },
104
- { url = "https://files.pythonhosted.org/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" },
105
- { url = "https://files.pythonhosted.org/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" },
106
- { url = "https://files.pythonhosted.org/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" },
107
- { url = "https://files.pythonhosted.org/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" },
108
- { url = "https://files.pythonhosted.org/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" },
109
- { url = "https://files.pythonhosted.org/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" },
110
- { url = "https://files.pythonhosted.org/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" },
111
- { url = "https://files.pythonhosted.org/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" },
112
- { url = "https://files.pythonhosted.org/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" },
113
- { url = "https://files.pythonhosted.org/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" },
114
- { url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" },
115
- { url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" },
116
- { url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" },
117
- { url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" },
118
- { url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" },
119
- { url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" },
120
- { url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" },
121
- { url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" },
122
- { url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" },
123
- { url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" },
124
- { url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" },
125
- { url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" },
126
- { url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" },
127
- { url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" },
128
- { url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" },
129
- { url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" },
130
- { url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" },
131
- { url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" },
132
- { url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" },
133
- { url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" },
134
- { url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" },
135
- { url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" },
136
- { url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
137
- { url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
138
- { url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
139
- { url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
140
- ]
141
-
142
- [[package]]
143
- name = "introspection-operator-runtime"
144
- version = "0.0.0"
145
- source = { virtual = "." }
146
- dependencies = [
147
- { name = "paper-docx" },
148
- { name = "pillow" },
149
- { name = "pypdf", extra = ["crypto"] },
150
- { name = "pypdfium2" },
151
- ]
152
-
153
- [package.metadata]
154
- requires-dist = [
155
- { name = "paper-docx", specifier = "==0.2.0" },
156
- { name = "pillow", specifier = "==13.0.0" },
157
- { name = "pypdf", extras = ["crypto"], specifier = "==6.21.4" },
158
- { name = "pypdfium2", specifier = "==5.14.0" },
159
- ]
160
-
161
- [[package]]
162
- name = "lxml"
163
- version = "6.1.3"
164
- source = { registry = "https://pypi.org/simple" }
165
- sdist = { url = "https://files.pythonhosted.org/packages/23/ad/28ecd7cb894d172f3c9c80a075eeeb2017ac62e3632cee05a5f9493547eb/lxml-6.1.3.tar.gz", hash = "sha256:45222d94ddd511536f3b2f7d9deae3b2339b4ce0f075f1ca25703b07cad9dd21", size = 4211198, upload-time = "2026-09-02T14:48:02.287Z" }
166
- wheels = [
167
- { url = "https://files.pythonhosted.org/packages/dd/1f/a180b57d9eeabaab77f9d5aa30356898ea749c4795596a8f66d1eb6bef2e/lxml-6.1.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c0710ac085a157b593c38fbcacd950f15c4afa8e2057527185875ab302752bc", size = 8602094, upload-time = "2026-09-02T14:47:26.054Z" },
168
- { url = "https://files.pythonhosted.org/packages/a8/25/070c92013a1c029a602b03560d68772313d918268667fa993da7961759c9/lxml-6.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:623c8799c17128753c65699f1c3aa32402657393a9ad6db09ed8b98ddf76611d", size = 4638308, upload-time = "2026-09-02T14:47:29.587Z" },
169
- { url = "https://files.pythonhosted.org/packages/1e/1c/722e88883173097a1a375153e3c2447eba3060d0231522cf6596e99f4195/lxml-6.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f683dc6300317700025e41d89a43e0276692ded16113a3c43eab704d605c58e5", size = 4939696, upload-time = "2026-09-02T14:47:32.997Z" },
170
- { url = "https://files.pythonhosted.org/packages/db/36/aa413bc214dc4f785ad2b2ddd8cc99aae7062d49ab155e91e6011af00daf/lxml-6.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11", size = 5105247, upload-time = "2026-09-02T14:47:36.734Z" },
171
- { url = "https://files.pythonhosted.org/packages/a3/a0/a1f7f1313795bfec67b77f01ef3b1128d49f2d7f66a8413fa55d47f4e25f/lxml-6.1.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b37772102d44bb6628186accca3a121b1fa3a6b3d97518a8c29a5229ca4c0d0a", size = 5011915, upload-time = "2026-09-02T14:47:39.846Z" },
172
- { url = "https://files.pythonhosted.org/packages/b9/78/840e7e3f1d0cc7a5cfac5d8505b97e25b6427fd774ac4bae672aaebfb4b5/lxml-6.1.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ddcf547bea2aee967d6a77779376a45e77e610e8465147a1f3d7e20d539d6e32", size = 5638175, upload-time = "2026-09-02T14:47:43.644Z" },
173
- { url = "https://files.pythonhosted.org/packages/0a/20/e022dbc6b4753a9bc9fc5fb28a27163430c1731b9913997f6544c1b2518c/lxml-6.1.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c", size = 5244675, upload-time = "2026-09-02T14:47:47.635Z" },
174
- { url = "https://files.pythonhosted.org/packages/99/83/82cde81d2b5eb38d1539fdfdf318abdd014a7e604f4df01c9cd3deb18f2a/lxml-6.1.3-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a5c18810318303ce9afb3f95e2ddb54834f96fa699a8600433fd5a93dcf44c56", size = 5358205, upload-time = "2026-09-02T14:47:50.306Z" },
175
- { url = "https://files.pythonhosted.org/packages/d2/a1/f3b057371c8cb29f2a9c9c44ea320592446e40b74a4b0af68c3d8e65bc73/lxml-6.1.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:3e42265103fb385d8642a78672edf376c6f7e1d3598a7a4f9cb1278f2f6b5f6f", size = 4704495, upload-time = "2026-09-02T14:47:53.251Z" },
176
- { url = "https://files.pythonhosted.org/packages/1a/a4/230eb28be5d412152ffc3c679b51fe1aeede5a53f3a8eb6e9748f2f4754f/lxml-6.1.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:21402998e4b78e7cce237d2788841aaa21ac9a4d1574d04dc2d12ee41ae807b5", size = 5255117, upload-time = "2026-09-02T14:47:55.963Z" },
177
- { url = "https://files.pythonhosted.org/packages/a3/18/1969f56763af24ce42ea156007b0b2d73fddea552e283b2010416394f0f4/lxml-6.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:38fc4e4e4e084e0bd491949482527d406788045c546d4f8789e93fc527b91385", size = 5054424, upload-time = "2026-09-02T14:47:58.131Z" },
178
- { url = "https://files.pythonhosted.org/packages/f4/d4/2a90acc1f6fabaa3a8db9340437822bd8d041b205d626a4b3e8621aaa390/lxml-6.1.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5609efdb0d3c95499c00046bc53648b3482ec2175b5503d6e611b3f0555dc71d", size = 4785572, upload-time = "2026-09-02T14:48:01.029Z" },
179
- { url = "https://files.pythonhosted.org/packages/a5/1e/b90e845b1dcd0f2f3f26b98283d857f25909223aacd265eee032c34ab8b1/lxml-6.1.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:97ce49699d87ebf8aad631b55d65b33219a4f1bfefbbf5bff19dc9af160aeaf9", size = 5656516, upload-time = "2026-09-02T14:48:03.419Z" },
180
- { url = "https://files.pythonhosted.org/packages/eb/ab/0a1b802c57f3fba5c4efd77d5c6b78adaa8f7b681f0c90456b140fe8bf6c/lxml-6.1.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:48542c9acba9ff9450bd18d871d2c2c8787fdb283572b623d206f1b927cd7d9e", size = 5245982, upload-time = "2026-09-02T14:48:06.109Z" },
181
- { url = "https://files.pythonhosted.org/packages/da/ee/2c016fbceb3778137459292538d9dfa7e3ad9070fe409c15254ddd90d2cc/lxml-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c55e71a9b1db1f107efb60da49c093689b74c5c31a708e5379e2fd9439d4fbb5", size = 5267340, upload-time = "2026-09-02T14:48:08.374Z" },
182
- { url = "https://files.pythonhosted.org/packages/9c/b1/736d18fd6f0835761923b7bac1f0c27d60c1200384e9093f05d8c5100525/lxml-6.1.3-cp312-cp312-win32.whl", hash = "sha256:b3ff39654f0ce6ebd4db154211136dbe7e8157bcc3bed2344c87f32c7c6ecb6c", size = 3602606, upload-time = "2026-09-02T14:48:10.384Z" },
183
- { url = "https://files.pythonhosted.org/packages/3a/5b/6ed903e4e6278a020c8a6f0dbbe78030d041840a6b4a64ea441a1e414077/lxml-6.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:3e9a00d1c2c30936f7add097c41afc5da6556c580909104aafd382cac92a855c", size = 4005999, upload-time = "2026-09-02T14:48:12.51Z" },
184
- { url = "https://files.pythonhosted.org/packages/e4/1b/7bcebb7b6332cb3ae85e9c13b139adb6f23f75c71d84041c56a5005d9a29/lxml-6.1.3-cp312-cp312-win_arm64.whl", hash = "sha256:1aeca87830c4fe649dcf93fe2b059525b71c72587f21be4ae4af7103082a79fa", size = 3666631, upload-time = "2026-09-02T14:48:14.567Z" },
185
- { url = "https://files.pythonhosted.org/packages/52/05/3ef45db776baea068044c799bbba68f3ca00a440c0e930a17c572f3d9639/lxml-6.1.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3a48093cdb058a93af842ede9703520e810b05dcd0fc6d7190a06376c3bfb6bd", size = 8590357, upload-time = "2026-09-02T14:48:17.413Z" },
186
- { url = "https://files.pythonhosted.org/packages/8c/a5/eee2fc77eee5ea68e4a4334b1def1781a3beaeefd3d98e81b4a38dc447b7/lxml-6.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:887c021d9a977cff89cb273047c1352997b772a8908a25c21836861f69b92be1", size = 4632616, upload-time = "2026-09-02T14:48:20.745Z" },
187
- { url = "https://files.pythonhosted.org/packages/35/42/df27b56848acd29d8a720acc28977911aab36f2a09df4208d5502e887415/lxml-6.1.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:611a51e61c92f62345a50b0035df6fc0d678f9299f33728826d831598862f59d", size = 4936186, upload-time = "2026-09-02T14:48:22.94Z" },
188
- { url = "https://files.pythonhosted.org/packages/ab/8d/8a7b91df0b54d09d25f5f44885d6b3e0a6d6643a8c070191580318d20c42/lxml-6.1.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b477912f42c5c33405a10c759d22f80cf5af043ae02d95b9d8e5e5bc555739ed", size = 5093324, upload-time = "2026-09-02T14:48:25.132Z" },
189
- { url = "https://files.pythonhosted.org/packages/c6/7e/8f340ddcd43790332fb0de8a26628d571a492da3300cd191821698407c96/lxml-6.1.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cffe18571ccc51d742cd08cbb3f8b756de9311d18c7ea98f5d92f37b8fb60c2", size = 4998850, upload-time = "2026-09-02T14:48:27.394Z" },
190
- { url = "https://files.pythonhosted.org/packages/c5/c1/9c5bb572f1f09ec9e4322bd4a4e9f4ad48347fc56ef94cf4df58a5279dc8/lxml-6.1.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75cc6569e86be5785b6188ef1642670c6adbc984e81ec35e224842ecd9eefcc8", size = 5626813, upload-time = "2026-09-02T14:48:29.61Z" },
191
- { url = "https://files.pythonhosted.org/packages/ac/7d/8bf1fd8bae8247743968bb76d027a1ac5bd2c4b44495fba6a71b30d10706/lxml-6.1.3-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d85dfab42dd672f87a7f76e9de7172962aee69fa12044f0d6e1a23cbd53fb80e", size = 5232385, upload-time = "2026-09-02T14:48:31.969Z" },
192
- { url = "https://files.pythonhosted.org/packages/7b/2e/6cef69ed81cb7df0d03b0dd09d08e6e2cf5061a743ff6f42f0b741548e9b/lxml-6.1.3-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:42632b4024ab24a6b488f559ac851312509888b6b80ae2aa11cf29a646a0d245", size = 5347088, upload-time = "2026-09-02T14:48:34.13Z" },
193
- { url = "https://files.pythonhosted.org/packages/5f/e1/8e5fd8ddc8c7d685badb0f2db149e3c9da84eefc2827c01c658df2c4e3cb/lxml-6.1.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:febd35ef45f603c2d74b74655efdbf45e14f55fc0aef4ac82b663ca829b283e0", size = 4707227, upload-time = "2026-09-02T14:48:36.62Z" },
194
- { url = "https://files.pythonhosted.org/packages/7a/7e/00041382a11be40a88bf405ebff11c8efabd3de79f2691e1638b1c47a8a0/lxml-6.1.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a43b3bdf11e477dc7770609d3477316f974354dfc8425d596f64f471cc8daf6e", size = 5240208, upload-time = "2026-09-02T14:48:38.893Z" },
195
- { url = "https://files.pythonhosted.org/packages/fd/fe/316538b5cff0936fa63d45d421c655730fcbb5a28dcac728c175083002bc/lxml-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d582042c69857c364e8153de6e18e0da9b7b515a6a8113caf69a6ec8e0520f2", size = 5050271, upload-time = "2026-09-02T14:48:41.213Z" },
196
- { url = "https://files.pythonhosted.org/packages/c9/91/455bcccb3ac725373007344d351151810cd19762d1673b64b811f4359a42/lxml-6.1.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e49a646acfab83c68974f4aa1d0a2acca9e88d7d627ae0fc13201b14b76d310", size = 4780433, upload-time = "2026-09-02T14:48:43.779Z" },
197
- { url = "https://files.pythonhosted.org/packages/cb/f6/580440e2f52cf00bba5c5e1080bfa88cdfcde73be71a11d95170ddbb663f/lxml-6.1.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0dee106e9aa97fb00541b1ed7827070564d0549c3d3fba8920e6b20fd980f748", size = 5645928, upload-time = "2026-09-02T14:48:46.187Z" },
198
- { url = "https://files.pythonhosted.org/packages/f6/dc/d123c1f244306543d545f62443f794959e4f1ea709fe100f8740d514e74a/lxml-6.1.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:dd5e90f34cffcfed97f36cf066325773d2b6021c60c29942e53a18b028501b1d", size = 5231184, upload-time = "2026-09-02T14:48:48.691Z" },
199
- { url = "https://files.pythonhosted.org/packages/c3/3c/fe55b2bd5c6113c906511cd88f6a470195c5fbff1124f19970ab706c3477/lxml-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9b3e7d71bf6acff341233417abbdface29c647e3113892d9aaedc02eb4aa2bc", size = 5255814, upload-time = "2026-09-02T14:48:50.948Z" },
200
- { url = "https://files.pythonhosted.org/packages/e7/a7/485df55acf55dc35e4ca89d2f48f03889e5a3241826b18b85102b32ce9d8/lxml-6.1.3-cp313-cp313-win32.whl", hash = "sha256:160fcf381f76c3aeac28a756bec44f48942a8f7245a87aa28e3a523b4d90cd87", size = 3602214, upload-time = "2026-09-02T14:48:53.236Z" },
201
- { url = "https://files.pythonhosted.org/packages/c0/28/e46a7702bd95e9043291f7c3539b6184cba66f96cea9936f20939b284eeb/lxml-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:e477aca0bc0d19f3b4ae9e4f2a1cfd687c31bf772d78734910658186b40b2477", size = 4004091, upload-time = "2026-09-02T14:48:55.699Z" },
202
- { url = "https://files.pythonhosted.org/packages/8a/1d/154c78e20479a43916e63f19cb720d83f44f024b03228be44c92d9a97b24/lxml-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b1cc980905221a5d8b3c476330730b3adb40ff80add71ffbdb6215ba055656f1", size = 3665468, upload-time = "2026-09-02T14:48:57.703Z" },
203
- { url = "https://files.pythonhosted.org/packages/0c/15/fc75a70b0af6021d0ea16811f1fc71cc42cd06ce90fe10f007a69b2eed84/lxml-6.1.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2bec13085dc8ef48a3fe62f7dfcacfeda2c785cdf19cc8eeda2bb9ed081da165", size = 8609725, upload-time = "2026-09-02T14:49:00.156Z" },
204
- { url = "https://files.pythonhosted.org/packages/84/ef/398fcf9018f881ec9aeaafae1ddd6586dfb13314a35d35e899de373dcae0/lxml-6.1.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4f4db7c7e954d289d71878938348b3d91b904a3e8210a11939359fb758a58e7d", size = 4639629, upload-time = "2026-09-02T14:49:02.81Z" },
205
- { url = "https://files.pythonhosted.org/packages/a7/2d/49b6a6ad7ce8f64b07b9fe852ff0c6d3fcbb26db61bee4f63d4120180a1c/lxml-6.1.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2cae5d5c90a62d9139c512a0cb1aad1d182b022b5740daea2617eb5bf7fc658e", size = 4965074, upload-time = "2026-09-02T14:49:05.133Z" },
206
- { url = "https://files.pythonhosted.org/packages/66/bc/6230cf80e4331c33383b0b6b73dc31a393dd76edd4cb73d761de5123034d/lxml-6.1.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c6c0c13128a32eb04a51357e56a094e13aa8e6d3d1884de2e9ae923f6915e1a8", size = 5099355, upload-time = "2026-09-02T14:49:07.343Z" },
207
- { url = "https://files.pythonhosted.org/packages/ac/cf/d1143d9b7717e07a82f158a1fc9ce6e581fdad1226734950af869e3ffde4/lxml-6.1.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2221e88679d1351e9a40aaee54bc65679b9795bbd0160bc3d5e36b163344eb75", size = 5036795, upload-time = "2026-09-02T14:49:09.65Z" },
208
- { url = "https://files.pythonhosted.org/packages/31/6f/194bb00ffb89712c30f5a7e1b8e685590e140fad6c8261fec172c09a3dc0/lxml-6.1.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfb398886a7eb4c719161c3efcff2a1248febc53a4d8e5072d2d8a87fed84ac9", size = 5658740, upload-time = "2026-09-02T14:49:11.9Z" },
209
- { url = "https://files.pythonhosted.org/packages/e9/44/27e3cee3dcdb3b7bc09727b642bdbfcd098490ea77df04611db9060d7722/lxml-6.1.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7eb78ba28b187e1e9203a55c60fcf70df2d22cb205fe6d51b9383d6097419f0", size = 5245991, upload-time = "2026-09-02T14:49:14.154Z" },
210
- { url = "https://files.pythonhosted.org/packages/ca/e9/8312560579fc980bbd2233a8a673cc46f7d613d3633f2bf08a21e8f4ad13/lxml-6.1.3-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:ea6b1e9105b4b24a34c722432d9fb578f9ed83af21fa1abda639011e0f22bbb6", size = 5354136, upload-time = "2026-09-02T14:49:16.459Z" },
211
- { url = "https://files.pythonhosted.org/packages/74/d8/eda60f4f73a9c780b5d6e1175484f66e6c81a2c93346e2906a1fec9c7a02/lxml-6.1.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:e8b17e23df3e827a69d25af70990ca2420e92668aaffaeeb3cd2351d7916a023", size = 4704379, upload-time = "2026-09-02T14:49:19.032Z" },
212
- { url = "https://files.pythonhosted.org/packages/ba/c8/c9cc60057be78ac34bd2b842e45e6e88edbfe5e532e82c3b82381b7aab49/lxml-6.1.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b7c37339d7e75cab9a123a04248e243cefefb302ad6db566ea0c77cbcde421e", size = 5258676, upload-time = "2026-09-02T14:49:21.306Z" },
213
- { url = "https://files.pythonhosted.org/packages/41/7b/66894008fee8d1785b8db129747ae963fd427b68f456918df7f2f24a8b98/lxml-6.1.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:83e3a51e7933db700a0da0db31849db3a24022d9970da9bb73001e1d0326fd92", size = 5090069, upload-time = "2026-09-02T14:49:23.562Z" },
214
- { url = "https://files.pythonhosted.org/packages/8b/31/c1b60404859f4c3cd1f41f29c65a24e25cea78fde822d9574a21f66810be/lxml-6.1.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bde9ae026a55b9a192078dfa6e27dd0ca4a050171ab6272e92f97b757dfdf48", size = 4741958, upload-time = "2026-09-02T14:49:26.037Z" },
215
- { url = "https://files.pythonhosted.org/packages/23/b8/6285f0cf546f14da2554cabdeaf7c2c2ff3190c74807f0de2e8810a786f9/lxml-6.1.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1a635e837b50a1819bebfedaac5916498ea024120969da8790500148fb0a894d", size = 5683245, upload-time = "2026-09-02T14:49:28.438Z" },
216
- { url = "https://files.pythonhosted.org/packages/d3/f6/2168cab44336dcb15fed0f0b78577225b83297cdf0dee349c95420c3dcb0/lxml-6.1.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d0c5c362bc94f1929dc7e96e715bbe7bd17037f802e6d8f0d1545df9133c0559", size = 5246087, upload-time = "2026-09-02T14:49:30.955Z" },
217
- { url = "https://files.pythonhosted.org/packages/f5/89/32f5de69a0a31f30e6164981851f87b37ecb2c4ee838e504b88d49d4818e/lxml-6.1.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c59e4265608da6a041f54646ecc0c9ecdbb19aaf14c4c684bb6c2114998cc415", size = 5269352, upload-time = "2026-09-02T14:49:33.502Z" },
218
- { url = "https://files.pythonhosted.org/packages/a2/a1/741d952ed3a7ef7a50055c6415aec3f067015e97f72f4389ce77b09657ba/lxml-6.1.3-cp314-cp314-win32.whl", hash = "sha256:2e62c569ec7531b679b184cbfe335c501c1d13c4b363560013019962eb630e6d", size = 3662783, upload-time = "2026-09-02T14:50:23.751Z" },
219
- { url = "https://files.pythonhosted.org/packages/0f/bc/5811cc73cac05e324e05ba9b0924e1a163a317a167ede8a9c748b11db30a/lxml-6.1.3-cp314-cp314-win_amd64.whl", hash = "sha256:66299564c046bc7e0cc5de5106601eae907e9fa5904cd68a323380a8502f7861", size = 4073951, upload-time = "2026-09-02T14:50:26.348Z" },
220
- { url = "https://files.pythonhosted.org/packages/92/18/3768c8b01ac3a9bed1914715e6011711b00e2a11628ffa6f7fa37f8e0269/lxml-6.1.3-cp314-cp314-win_arm64.whl", hash = "sha256:ebd054ad1737a68fb7c5c073d405cef2b88bb824e294de3b4a4e995b47f0e376", size = 3749279, upload-time = "2026-09-02T14:50:28.749Z" },
221
- { url = "https://files.pythonhosted.org/packages/72/38/84684784738d9451db2b330de2483f496690c3a5c642071df24135739b37/lxml-6.1.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5a143e6207579de8baeded4eaac9134413200359f1969d636f0bfb98ee8c3c8f", size = 8860296, upload-time = "2026-09-02T14:49:36.346Z" },
222
- { url = "https://files.pythonhosted.org/packages/24/b7/fc4c50bb1b38e864010ea396046cabe85129bf9e65b11edcfbc37d356241/lxml-6.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a1cec0f99b9b914d39176347a93b7610dc09324491aee1cbc57cd291a41a1d55", size = 4755190, upload-time = "2026-09-02T14:49:39.872Z" },
223
- { url = "https://files.pythonhosted.org/packages/94/e2/ee9aa6ed2b666b2db1f6f7fd48964ff9da39ebe827ef5eac0ab881f639d9/lxml-6.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f6b9d2aad499c769ee8287609ab0e6de99d8bcea99c6e6c2e64945259fd52fb2", size = 4979517, upload-time = "2026-09-02T14:49:42.153Z" },
224
- { url = "https://files.pythonhosted.org/packages/29/e3/e7763d1661b283ddd4fa36f91b9a497db6b8d2aff55028b16c7f642e0755/lxml-6.1.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a23fefdb345b2d4d0ff2860571b5ff9a89a28b6a120f720e8fb0324d346626", size = 5115270, upload-time = "2026-09-02T14:49:44.493Z" },
225
- { url = "https://files.pythonhosted.org/packages/2d/cd/22205d5b4d177e3f4156f780412426ee7c7f8107809f119f0dcc40fa51e3/lxml-6.1.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:545ccc14fb05485f48b4439ec35beb16d5b5280eb6c81c658bd4707a2a119414", size = 5032449, upload-time = "2026-09-02T14:49:46.841Z" },
226
- { url = "https://files.pythonhosted.org/packages/da/43/06a4626c3bb79ef8c501b674afab8100d64e798665bb2a97d1c960636a49/lxml-6.1.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:93476b6514b373fc6ca67d26c442784f7807c86f00635bfe79f935c3eab2af17", size = 5603325, upload-time = "2026-09-02T14:49:49.664Z" },
227
- { url = "https://files.pythonhosted.org/packages/d0/9c/733682a0c2de9f5779ba207bbb3f3f6be8c6bda863fc01739b186b38783a/lxml-6.1.3-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8db38ff3fb7aee7d6a82ae4da2eef1178656fe1216841fbd24870062a9d60473", size = 5229023, upload-time = "2026-09-02T14:49:52.447Z" },
228
- { url = "https://files.pythonhosted.org/packages/c6/8a/e69cdaca3fd33a647942925664f01b20908d41a6968c182305be9c38fb11/lxml-6.1.3-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:25f4118c438f96bb466e83108506d03d5c31b1bd2387e83e5b070bda6ded9c37", size = 5317811, upload-time = "2026-09-02T14:49:55.25Z" },
229
- { url = "https://files.pythonhosted.org/packages/2e/b2/0c397588174403c2ab68fc464abf97e03e7324f9c6cb6a99023104707195/lxml-6.1.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:1beb0f9909b26cee938df9ba56b15252a84429b1fc30ce6fca161390b9789a70", size = 4646516, upload-time = "2026-09-02T14:49:57.761Z" },
230
- { url = "https://files.pythonhosted.org/packages/56/7e/cfea25afafbe49db8b225764f7f74bb37c2a7f5e717d917d3d4a5e098ed4/lxml-6.1.3-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3a27ac6c780c8b8a1cd231b58407634cafc1c4cc28cd6c7141362df0f36351e7", size = 5240626, upload-time = "2026-09-02T14:50:00.279Z" },
231
- { url = "https://files.pythonhosted.org/packages/a1/75/7a587771bb52ebb0e2c57b6dbe9fd96a70fbb54d72ddd97d54c5f8ec18d5/lxml-6.1.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a1932d7ce78a561367512c594fe66eac2b2ec9b9264cfd9b5f950622f4a116e2", size = 5086619, upload-time = "2026-09-02T14:50:03.245Z" },
232
- { url = "https://files.pythonhosted.org/packages/1e/01/94c0ebe6d831861542d251e038052e52bf6d33f1d18f1cfffdc82851065a/lxml-6.1.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:7d0f5976aa2701996f759b30172925829867547bb073af0ae67d1307a0f0262c", size = 4758828, upload-time = "2026-09-02T14:50:05.873Z" },
233
- { url = "https://files.pythonhosted.org/packages/1f/f1/938d67bd0e5b1fdfa52be28aefdffbad57e1f6b8e921c2aab88542c75f40/lxml-6.1.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e7ce578aa8a80910a72a8ca0bbea3baae10100827249001999726a788456d8", size = 5627083, upload-time = "2026-09-02T14:50:08.555Z" },
234
- { url = "https://files.pythonhosted.org/packages/d8/65/4e51522f6c214650db0abb7b16ccd11b1238b8a05a8d59aa4ebed59c9f67/lxml-6.1.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d97c5227621af74b111882a290b10f371780a38eef9d9e730408fba2259b52fb", size = 5235170, upload-time = "2026-09-02T14:50:11.255Z" },
235
- { url = "https://files.pythonhosted.org/packages/92/c2/e73d19365665f6b16ef84df21199befc3b06e4c539046ad2d9595f6fb9ea/lxml-6.1.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:da707f14ea3c35ee463d50acd596d6488e4b2b4ae7cf77a5bf93f55c023d63e8", size = 5252273, upload-time = "2026-09-02T14:50:13.782Z" },
236
- { url = "https://files.pythonhosted.org/packages/48/a9/7f386c84c9fe2854e1ca6e231c285e1c8f392971ac353c6865e6ec49faff/lxml-6.1.3-cp314-cp314t-win32.whl", hash = "sha256:9efe56a68179f3adc4de41861c9358931db03837c48dd5e1c78077b84dd07f3a", size = 3902712, upload-time = "2026-09-02T14:50:16.171Z" },
237
- { url = "https://files.pythonhosted.org/packages/82/a6/8a3eb793f7900ef01c7f99e6f5fcbcfbdff35251cfaef66b32a4c16352d6/lxml-6.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c9389b3784b56c58d933b5e0aecdf28f901b073ff385358d8a7d40907f6e14b2", size = 4400979, upload-time = "2026-09-02T14:50:18.621Z" },
238
- { url = "https://files.pythonhosted.org/packages/cc/c4/3807bea283b4fe9e9d9f5dde46a73df91178472b335d2778e10b2a37aa22/lxml-6.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32a409be3190b088f960ac92bfedfbef2f86c49ff940765e1548177592d20026", size = 3823401, upload-time = "2026-09-02T14:50:21.119Z" },
239
- ]
240
-
241
- [[package]]
242
- name = "paper-docx"
243
- version = "0.2.0"
244
- source = { registry = "https://pypi.org/simple" }
245
- dependencies = [
246
- { name = "lxml" },
247
- { name = "typing-extensions" },
248
- ]
249
- sdist = { url = "https://files.pythonhosted.org/packages/70/dc/bdc50405502a1a92707284005c1fab3e9e61fd3a7894a18bd80e24ac1920/paper_docx-0.2.0.tar.gz", hash = "sha256:034a961dc42eb44d7676e3a5af7ded485e89eb97851f8ccb24c481355b2c9d7d", size = 7291150, upload-time = "2026-08-24T23:37:59.805Z" }
250
- wheels = [
251
- { url = "https://files.pythonhosted.org/packages/f8/c6/b54eb65833f5c59e10be4d8f3f422eefba440aa805cca345f80410383975/paper_docx-0.2.0-py3-none-any.whl", hash = "sha256:d61b6c1e23c2e50364ca9ebf913b08121bba12dc9119ffd0047ad927ff29ace0", size = 439210, upload-time = "2026-08-24T23:37:58.225Z" },
252
- ]
253
-
254
- [[package]]
255
- name = "pillow"
256
- version = "13.0.0"
257
- source = { registry = "https://pypi.org/simple" }
258
- sdist = { url = "https://files.pythonhosted.org/packages/ce/a4/9cc30690a394a8305e8be4c9036aa16d0dfddadd09079860b582f562496d/pillow-13.0.0.tar.gz", hash = "sha256:cdcd1ba258178f7356d74170c9a81ba4eab6a5d55748bc402a5a2a6fe6be3510", size = 47058631, upload-time = "2026-10-16T22:18:09.718Z" }
259
- wheels = [
260
- { url = "https://files.pythonhosted.org/packages/7a/b8/58d79332229482eb0d27c82973850808c19d37e5bdc14443d3417bd373f5/pillow-13.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:67c8d44ad89fa7eb88de5d6ef6199b1e03dd24974f85a67639fc8badb801fe79", size = 5568643, upload-time = "2026-10-16T22:15:44.474Z" },
261
- { url = "https://files.pythonhosted.org/packages/39/4b/f2ed53d83aa44ac19599ac0a54897f867fafb95f406980d1e24083f8cbe3/pillow-13.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc481f742ccda20e9ed760b8f978dff50f2a4e9bc253ac167fb0a05b0783ef52", size = 4852329, upload-time = "2026-10-16T22:15:46.284Z" },
262
- { url = "https://files.pythonhosted.org/packages/3b/bb/9e30e8ed18c20800ee327a12107d5249ad8dc444867b3adfcc21654e3c71/pillow-13.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8bf295fe459343bc6093c8e5e0df60bfa189e0fcb775a22e2c2488fdeccc6c0d", size = 6519309, upload-time = "2026-10-16T22:15:48.14Z" },
263
- { url = "https://files.pythonhosted.org/packages/e6/99/fdf65cc539e266ab93546c261bdf98411fe165c6e4e13f9dbf597097192b/pillow-13.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42d63be3af4f9b951b199b0a9d23365d37ab14687e6d77b74940432c8515c4ca", size = 7144542, upload-time = "2026-10-16T22:15:50.093Z" },
264
- { url = "https://files.pythonhosted.org/packages/26/9b/f38b7c4d8f599537e2d1a6d546cec9beef7536ff8f5c49a3f9c78179d8ac/pillow-13.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4bd024d567eca931b668978d672dba914d4b8e2628e2c3073a2ad178de8a2620", size = 6618042, upload-time = "2026-10-16T22:15:52.054Z" },
265
- { url = "https://files.pythonhosted.org/packages/a8/7c/45ee28531f89ee4582967d8a17f7a26d71ca64cd902b93c269590cd60a00/pillow-13.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23edf4ec4b6f4f240e8df6e4985e9ee2ced86474632433e55fb1e2548744bf40", size = 7228137, upload-time = "2026-10-16T22:15:54.009Z" },
266
- { url = "https://files.pythonhosted.org/packages/01/bc/9c2a969cb1b70201d04fa17fd5aedf02a732870b8da86bceaaa30b0ca385/pillow-13.0.0-cp312-cp312-win32.whl", hash = "sha256:02464da86f561f410e13b061ec37c3068a9cb557a095494157742d07b3c24a27", size = 6515423, upload-time = "2026-10-16T22:15:56.25Z" },
267
- { url = "https://files.pythonhosted.org/packages/ff/cd/73a79ecdc822076002eb24b5e1e4a530b0bb5310fcb6d239abbec877110b/pillow-13.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ff3fe9eb621fbb9db4c4be33ab946f6bac5f98ab50ca8d3260503653656c961b", size = 7272166, upload-time = "2026-10-16T22:15:58.597Z" },
268
- { url = "https://files.pythonhosted.org/packages/4a/a3/be5a1236efd085b3984e4305da8f179ae9dab80c991d2ad78f2bd5c4c843/pillow-13.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3bc8ee0271387e0b1e3ad0ff893645990a656e79bf9c42f4f734a3642b11d51", size = 2698943, upload-time = "2026-10-16T22:16:00.637Z" },
269
- { url = "https://files.pythonhosted.org/packages/82/e2/9132d38df320e8099e2642d9fa538f18a7dfebf68a875a31725dc2a115aa/pillow-13.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e98b4f91f99b24b1da72e10621ba181f7281922384a7403094debb4199de6c00", size = 4190620, upload-time = "2026-10-16T22:16:02.527Z" },
270
- { url = "https://files.pythonhosted.org/packages/cb/be/436dac0996876f65ef1916ffb711c5ef4af2ebc57414b6f16642e07f6b47/pillow-13.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:34fb3db1149c879343861831728c243c432d4a5afc8f1878ca7612ef8de2a033", size = 4290286, upload-time = "2026-10-16T22:16:04.861Z" },
271
- { url = "https://files.pythonhosted.org/packages/60/16/7c1007162e4169ea0d5bec47e2e0b2174f56c50cf5b8c02a1dc23e41037e/pillow-13.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:2d50500a4a3bc6469adcdb319cf05d688b08ca3bf0f22a05339f3b25b433a599", size = 3723169, upload-time = "2026-10-16T22:16:06.956Z" },
272
- { url = "https://files.pythonhosted.org/packages/08/bc/578ab2eaa04705a8978bd9be0fe15c3d43a8fc95097c4f7e39540e79e4fa/pillow-13.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef5fe6b7db3f501c56000ba8fe7c7e1a35ca1d45824fbeb95a67daa85cff4dbe", size = 5568469, upload-time = "2026-10-16T22:16:08.664Z" },
273
- { url = "https://files.pythonhosted.org/packages/98/c8/ccede3dcc4f274b80fb49199eed1ef34ba05566c8b71976b9d1f111da2e3/pillow-13.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f11a3c1358bb3c68012a7b1f41b223c3db9f7bfde90b481df028cfe1c3363313", size = 4852187, upload-time = "2026-10-16T22:16:10.693Z" },
274
- { url = "https://files.pythonhosted.org/packages/fb/3c/cd0770e96d20bfd990db024bcbd43897ff3e88da1f4782cf90ea231b1d4f/pillow-13.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19b66ca0a4d25cc0200b381ce824c7ea090db61427315e73cfb05dbf1b35c369", size = 6515908, upload-time = "2026-10-16T22:16:12.896Z" },
275
- { url = "https://files.pythonhosted.org/packages/ef/fe/f5cdaa2d1c4086e757430f368c271a58ee29cfe7378e7ae7b9d964015c25/pillow-13.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5f56499c1a74d2b862beb0c33b8016f99d095858455df9d21583bbbc1c15b582", size = 7142544, upload-time = "2026-10-16T22:16:14.844Z" },
276
- { url = "https://files.pythonhosted.org/packages/fb/b5/a54f6d44adbcd23737c8c12953f42be107e4cca64e247c0ca3a5b563061c/pillow-13.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2a14cd73d5e6c927f644b0110ee81f59c4ef506259ec9183904747f2caacc81", size = 6613097, upload-time = "2026-10-16T22:16:16.893Z" },
277
- { url = "https://files.pythonhosted.org/packages/e6/b4/9e6c1207c21b2e91db0d15c16d30f3701ce5add1bd4da0e787761e66cb6e/pillow-13.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08b826a9bf1265c76de04a42061d61ea6b109418b7826f28ff1ecdfaa26c5502", size = 7224846, upload-time = "2026-10-16T22:16:19.195Z" },
278
- { url = "https://files.pythonhosted.org/packages/78/9f/d3c3cb0518f3dd1eee0ef501535f53d15597a53c5c4118d178086143397a/pillow-13.0.0-cp313-cp313-win32.whl", hash = "sha256:12b0fa15879fe5a9d9eebe66e4e2d74128a773b1a4448010c9c714f45e04b9d8", size = 6516013, upload-time = "2026-10-16T22:16:21.389Z" },
279
- { url = "https://files.pythonhosted.org/packages/d7/ed/1b16fb66a37e1b9e41370b93b2c614c7da761a5b2a41dc333f7af4639962/pillow-13.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:9dda0788a9932776a75d8bcc020076edc308ce2508b51f6739a45e3158b3fc42", size = 7284777, upload-time = "2026-10-16T22:16:23.393Z" },
280
- { url = "https://files.pythonhosted.org/packages/87/7d/8d02eba9f7ecc87f8e90f731e032cc4773802f2a843763afd0c66a535ff4/pillow-13.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:40a266371ece581e5de13b6635ef78d7487824af80561b6ca27694a88c19bc0f", size = 2698826, upload-time = "2026-10-16T22:16:25.382Z" },
281
- { url = "https://files.pythonhosted.org/packages/8e/45/fbad9c7f5e2f1745c373a434d46072bc84eddfefaa28b852425ac957fda6/pillow-13.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:44629d4498c9cb6b2051806155f22141df0d8a929c56134b166655232dfb2f8c", size = 4190721, upload-time = "2026-10-16T22:16:27.524Z" },
282
- { url = "https://files.pythonhosted.org/packages/a2/bf/2074902a259a90692c5dbf132885d21671644cbbd3f690047516243a55db/pillow-13.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:bf97ba1b5542050e9b0c74e632323b6d8136cc99d64e193abebee22fe6013c76", size = 4290236, upload-time = "2026-10-16T22:16:29.562Z" },
283
- { url = "https://files.pythonhosted.org/packages/ff/a5/3d1bb93b8ea38d15d21356c1b8e9f50971ac592aaa248f7fb71739094c0e/pillow-13.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:aa694c49787040943570d61c9b8fc01271bd2a2cfdd55f0d896f8aecdce1a981", size = 3723093, upload-time = "2026-10-16T22:16:31.249Z" },
284
- { url = "https://files.pythonhosted.org/packages/84/a1/be060135d85187b25fa8b2573786c0e45c62ae27527fff5257675eb97f4a/pillow-13.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:49c12af515073e9bf511e4ef736743bf70d531a643668722279314b937742ba4", size = 5572253, upload-time = "2026-10-16T22:16:33.103Z" },
285
- { url = "https://files.pythonhosted.org/packages/b5/f4/0bf6f1ac9ba7ee2ffe9914aeb410a1035a0c1a3c42f122388d7fc950bc4b/pillow-13.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d9b0a2bee3f3e03e9f29057aedb807dbf1e1d96ee1b9d0f422d9db6c8583271d", size = 4852269, upload-time = "2026-10-16T22:16:35.102Z" },
286
- { url = "https://files.pythonhosted.org/packages/83/16/b603274617b3e6728da3a0133d4fba1ea8d7fbe0c4ea5fef9cbffa398b8b/pillow-13.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5933fcea5a5ba64c686f55c886a115cbfd92f2900460784a1e79909fdbafd7b6", size = 6516058, upload-time = "2026-10-16T22:16:37.228Z" },
287
- { url = "https://files.pythonhosted.org/packages/1d/f2/1371707ce438ba9a43f9d24089828494ec6f9396e28cd7903aa704dd6306/pillow-13.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e91c2b1cbeae768c98752e67e717e283d68dae45717b41f2d91eaee648e37b7d", size = 7142574, upload-time = "2026-10-16T22:16:39.172Z" },
288
- { url = "https://files.pythonhosted.org/packages/b9/a9/62cf68587f910bc87c5951bcb6e9430c7643a65076adf5f18b4bf350e2bd/pillow-13.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f6299e494e46253d40c34653cac369412f44ca53b5f85d848d3f95b1ed0546c", size = 6614295, upload-time = "2026-10-16T22:16:41.117Z" },
289
- { url = "https://files.pythonhosted.org/packages/6c/c9/9e9b48178649730299be0bafbf5f810a5b6977538f5a92d47e4ba5f5d608/pillow-13.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f9bd7d260d986f3553518c5d723544346fa48031f8aef3265d786aeb59845bc0", size = 7224841, upload-time = "2026-10-16T22:16:43.118Z" },
290
- { url = "https://files.pythonhosted.org/packages/8f/48/c44e91384b1b6c072a714a63d00608acc16a6a968735e478e4be4384e5ac/pillow-13.0.0-cp314-cp314-win32.whl", hash = "sha256:2e31c4e68cffd7b1f116127626813b82fcba19edf6b940a7b0c266d6484d0055", size = 6508694, upload-time = "2026-10-16T22:16:45.883Z" },
291
- { url = "https://files.pythonhosted.org/packages/50/e2/7ad17747517e4c813dca78257d6bb593466f8231baf1384bc98d92745edb/pillow-13.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:f7cbb7a64e7ec664775195e4f81923bc8a93d9451b963f844e59118f63147a7a", size = 7281830, upload-time = "2026-10-16T22:16:47.725Z" },
292
- { url = "https://files.pythonhosted.org/packages/41/9f/e5b51f2ce8ef4563215334035403ce61a244f93a2e65c24a940131618f87/pillow-13.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6aaa7871aebca081eb5c45f83e845bbd9712ae99fa9286dbfdcee835e63f4b4", size = 2695468, upload-time = "2026-10-16T22:16:49.515Z" },
293
- { url = "https://files.pythonhosted.org/packages/97/73/a53502f731f6c84805a4ce87a8881e86d07a65aee64728b900d112dc703e/pillow-13.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b999816b5850a35ada08443e6e03945f1f6488144a83afe2d37b240a552aafa", size = 5578196, upload-time = "2026-10-16T22:16:51.291Z" },
294
- { url = "https://files.pythonhosted.org/packages/21/50/7e949959a56b5559c78ef172b70fc6025706acb289f3f648a15329e4fa6f/pillow-13.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:67871a13cf47275201553b536c025da777f997fa562a19eb798af4ad4b97c18f", size = 4857276, upload-time = "2026-10-16T22:16:53.399Z" },
295
- { url = "https://files.pythonhosted.org/packages/c9/52/43ded0ac08e77e39d17bd6d894162572201b5e8fdc4a022b14c55a1960a4/pillow-13.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8ca8f69b7c77fa96dad99f8638c1fc4f1c0bed4acaa1baf7a3c31bdb80a0ab1", size = 6607008, upload-time = "2026-10-16T22:16:55.525Z" },
296
- { url = "https://files.pythonhosted.org/packages/0d/2e/4a57ed216d5bf877387cba4d44c02aa87abe51ad2e06404aa6b597c4da39/pillow-13.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bce6a29c3898191f4c9998e4d4e8f69790caeabde46e54da4bc235ce5bde683", size = 7224281, upload-time = "2026-10-16T22:16:57.738Z" },
297
- { url = "https://files.pythonhosted.org/packages/5c/3d/2929b248e135752d63aaeabf53f6de797e905b7b6263f9c1e2adade20aba/pillow-13.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:816752a5ef76a80484c8c441c85593b7ea9304f6231fc5776ad1fbff7e773058", size = 6695600, upload-time = "2026-10-16T22:16:59.842Z" },
298
- { url = "https://files.pythonhosted.org/packages/bc/33/fb0da992cf084ed1ee6ee05e96b6191e35c351eb707ca2df874feb24ce47/pillow-13.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5bdb38ba220b61b4ec31dd1046dea6b016d3c6850a23f43ed8b58332b2fc668e", size = 7300589, upload-time = "2026-10-16T22:17:01.686Z" },
299
- { url = "https://files.pythonhosted.org/packages/bb/af/c30dd9ef6a3dce84ecb1b2393cac629047a32e66279056c6cb46c3ffd65c/pillow-13.0.0-cp314-cp314t-win32.whl", hash = "sha256:06903aac074d00c988e77a1fc435286dc498506d4ca8d6f69df9babddf4ecc15", size = 6517204, upload-time = "2026-10-16T22:17:03.722Z" },
300
- { url = "https://files.pythonhosted.org/packages/85/d5/ef3a8ae7609c8ab26570abf18194882534b480d245a9148b895a2262c004/pillow-13.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9a635068d9793c7f0d929f39f18b58f1bd86c236e8e7b64167b86af9232ebca0", size = 7286695, upload-time = "2026-10-16T22:17:05.543Z" },
301
- { url = "https://files.pythonhosted.org/packages/84/3c/0861db0c69a03689ebe3e7599d35668a414deb5aaafd8255215e4e62e3d3/pillow-13.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0d1a9cf7bc6917538b20f8d06b9ab2147e542f68849e96fc58eb66bdf6acc692", size = 2698098, upload-time = "2026-10-16T22:17:07.674Z" },
302
- { url = "https://files.pythonhosted.org/packages/60/da/f6d644e631c5b370c5d0b899f3ccadd2780aab070b29a110527cf4e1fd2c/pillow-13.0.0-pp312-pypy312_pp80-macosx_10_15_x86_64.whl", hash = "sha256:8619685cad8efc4c38c66d3b36aad854523297c43d748b3b6f0cc03c90ba3463", size = 5523188, upload-time = "2026-10-16T22:17:59.334Z" },
303
- { url = "https://files.pythonhosted.org/packages/e4/b0/2594f202d743a35d4fe80682bfd96f277e5ca7aecb27cf64ef5bfc1d000b/pillow-13.0.0-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:7e91e97a929232cdb828889ee6ad0e48d7f40f4ec38818f4e4a8968b2b1af22e", size = 4806808, upload-time = "2026-10-16T22:18:01.188Z" },
304
- { url = "https://files.pythonhosted.org/packages/8e/ed/d504165ef79af6fafe25c76726cf30f544cc236002f7ed054f1d51adc4ff/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93ff9c9c3663ce23aaa5d0b6b6abac55359e2a0c06c7e30f3771829af816be63", size = 5464358, upload-time = "2026-10-16T22:18:02.779Z" },
305
- { url = "https://files.pythonhosted.org/packages/7b/fe/a10aba5503ca13e9e009b8bd0a1a4ce5584bdce246245fd6f3a83701369c/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e65dd6e75de67b2362a4fa3e16aa627595f65054dc4ec73d64680c84d592fa61", size = 6043514, upload-time = "2026-10-16T22:18:04.538Z" },
306
- { url = "https://files.pythonhosted.org/packages/c0/37/66b12dc476041fe523bca29c9d58fbf1ea92a4eaf73acfb9e993948f1191/pillow-13.0.0-pp312-pypy312_pp80-win_amd64.whl", hash = "sha256:567f20f1c75897d7663830da6ea8af719e03d712b1d9ee7f813319f9ece42e5a", size = 7366030, upload-time = "2026-10-16T22:18:06.638Z" },
307
- ]
308
-
309
- [[package]]
310
- name = "pycparser"
311
- version = "3.12"
312
- source = { registry = "https://pypi.org/simple" }
313
- sdist = { url = "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz", hash = "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6", size = 158785, upload-time = "2026-10-22T13:40:03.56Z" }
314
- wheels = [
315
- { url = "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl", hash = "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", size = 62600, upload-time = "2026-10-22T13:40:02.021Z" },
316
- ]
317
-
318
- [[package]]
319
- name = "pypdf"
320
- version = "6.21.4"
321
- source = { registry = "https://pypi.org/simple" }
322
- sdist = { url = "https://files.pythonhosted.org/packages/1e/e1/ec8f4d155c997c45917153a5521e60d6ee6389d68abdcbc8e44e564caffc/pypdf-6.21.4.tar.gz", hash = "sha256:c8817e3f888d47a087872ded69812c1ef30cacb871d6e053e0c00bbaf0bc2163", size = 7109255, upload-time = "2026-10-28T17:00:54.81Z" }
323
- wheels = [
324
- { url = "https://files.pythonhosted.org/packages/3f/4b/77e8ad4ac42c9d93600f40541a50e9feb3a61d2bbee2d76f659346072051/pypdf-6.21.4-py3-none-any.whl", hash = "sha256:33e4fb41ca352810eb4e9838aa0d82d6888a5f8bf682f43e99cce5faa666255c", size = 410430, upload-time = "2026-10-28T17:00:53.146Z" },
325
- ]
326
-
327
- [package.optional-dependencies]
328
- crypto = [
329
- { name = "cryptography" },
330
- ]
331
-
332
- [[package]]
333
- name = "pypdfium2"
334
- version = "5.14.0"
335
- source = { registry = "https://pypi.org/simple" }
336
- sdist = { url = "https://files.pythonhosted.org/packages/95/d0/c81d3a7c2a9af37b817ace1de0acd40cf44d15f12407c5e86b3668364a5c/pypdfium2-5.14.0.tar.gz", hash = "sha256:c5f009b3157f10e97dceb55963f5910eff92feb00587ba10a76f12b87ce1a4b6", size = 376498, upload-time = "2026-10-04T15:19:19.835Z" }
337
- wheels = [
338
- { url = "https://files.pythonhosted.org/packages/91/03/79e89eac9d811e83d606342e129f5f39e168442ddf23b024fea4a7ee4762/pypdfium2-5.14.0-py3-none-android_23_arm64_v8a.whl", hash = "sha256:bed597b2cea3990164e43f9003f71db18959d0abd5d73adc9c176e7be2d84b98", size = 3453370, upload-time = "2026-10-04T15:18:40.79Z" },
339
- { url = "https://files.pythonhosted.org/packages/cc/68/369b80e408017b18eaecaa3c730bded07d90bfb65562215df200b56fb8e2/pypdfium2-5.14.0-py3-none-android_23_armeabi_v7a.whl", hash = "sha256:1951f0aed469150b13c62eabd501a9839e608ab9983ca8579be9eb73213b72b6", size = 2889924, upload-time = "2026-10-04T15:18:42.825Z" },
340
- { url = "https://files.pythonhosted.org/packages/d1/ea/14673bc9d8b7beeaa1eb46e9951b22543edaf2a4676c586e3b1e032ff6ee/pypdfium2-5.14.0-py3-none-macosx_13_0_arm64.whl", hash = "sha256:2de384df66ba55fcaab0775f30f28ec1090af3dfa60276a07821efc96d993118", size = 3542294, upload-time = "2026-10-04T15:18:44.345Z" },
341
- { url = "https://files.pythonhosted.org/packages/a6/11/b720097b01fa0874854f2f6669cbea4e4ea4e075769687714fac64d68964/pypdfium2-5.14.0-py3-none-macosx_13_0_x86_64.whl", hash = "sha256:e4e203ea9710fd00e5448edb6f1615dc8587035357f75f40b432dde0c33e8da1", size = 3735845, upload-time = "2026-10-04T15:18:45.975Z" },
342
- { url = "https://files.pythonhosted.org/packages/92/b4/0c31aa51887cd6cd032191dfe010a6d01ed43cf03204cfbd2184ebe4b715/pypdfium2-5.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1b696e6901e16f114a2ec6332e5e3f8f5033a901614ead28499ab18ca6024f5", size = 3719672, upload-time = "2026-10-04T15:18:47.455Z" },
343
- { url = "https://files.pythonhosted.org/packages/93/a8/ae6ef96bf66559328d07b9e402ea704352ea00c49b6a73573da57e1fb378/pypdfium2-5.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:593f2c952ae3ffdca0efcbb3d9464fbccb876254386114ff900cabef21157c3f", size = 3435593, upload-time = "2026-10-04T15:18:49.131Z" },
344
- { url = "https://files.pythonhosted.org/packages/59/ff/a78405fab4c8bad0ec25b49c5efba2c85ed14609ec73645f95220560bd81/pypdfium2-5.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d436ee9e024f981e68f5775f5a9d115f93ea14ee6c2c6efd35dd17d83edf4942", size = 3868604, upload-time = "2026-10-04T15:18:51.304Z" },
345
- { url = "https://files.pythonhosted.org/packages/5d/6e/09e9b62ab66c9acef5ad14f8a8c0d7b4d8d6ea6492e4e65b612ef146d373/pypdfium2-5.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6f13bbcc5f4adabc2676e52f662c6cb375de86b314790b0ae08f3ab62eb116a", size = 4279333, upload-time = "2026-10-04T15:18:52.948Z" },
346
- { url = "https://files.pythonhosted.org/packages/4f/a3/c9cc797fc8bdfb8f37b9b0f8b9d02a5fc196b2015f408d53624cab5b0519/pypdfium2-5.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f281613fa22313d9c7ab89947665e84eccf8ebe40e1198a84a88352305648d", size = 3799581, upload-time = "2026-10-04T15:18:54.913Z" },
347
- { url = "https://files.pythonhosted.org/packages/b9/76/54355a4bbd88bdd5ed3f4405bdc345eb593df9995daf90d285cbdf5c1410/pypdfium2-5.14.0-py3-none-manylinux_2_27_s390x.manylinux_2_28_s390x.whl", hash = "sha256:51d9e9b64ebc34effaf57f9b6d4511b3f66ad3744bd1690d2cc6700853173dcf", size = 4113022, upload-time = "2026-10-04T15:18:56.774Z" },
348
- { url = "https://files.pythonhosted.org/packages/7d/bc/ea461961ed0e0c4866df7a5610e76f769ef468bff28cd007e2aeecc8b882/pypdfium2-5.14.0-py3-none-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:605ab9d0d4c5e223599c9065b88d16b2c1f131c807c80dea8adbb16f1433e95b", size = 4062832, upload-time = "2026-10-04T15:18:58.471Z" },
349
- { url = "https://files.pythonhosted.org/packages/32/30/dde99bc8cb3f8ace1d856095c2b4a29c80eecf9089b186a3b0845d0abc69/pypdfium2-5.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:382de7fe20d32c42993a274d7b6c555a5623a97570dfc1d2f5e0a16fe0d5d482", size = 5058436, upload-time = "2026-10-04T15:18:59.993Z" },
350
- { url = "https://files.pythonhosted.org/packages/ec/16/5314182dda2695fdf5bd414a450ee866087068cca4725703932770d4be04/pypdfium2-5.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dbfd6deff68cc46b134acd6be380d98d694a9f018fbb622c07229225c85db389", size = 4595505, upload-time = "2026-10-04T15:19:01.835Z" },
351
- { url = "https://files.pythonhosted.org/packages/63/3f/474c42e726f0020095c7d5f3fb88cfd4e5d39c1361105a72899ada0ecd1b/pypdfium2-5.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9f4d77db5232826dd03a63481f32164331b96c21fd68f0667b2e43dbae141a93", size = 5309775, upload-time = "2026-10-04T15:19:03.564Z" },
352
- { url = "https://files.pythonhosted.org/packages/6b/0c/723a6cf11cff00f125310d8c2c08362dc6c100d05fff8f92285a4df1bd41/pypdfium2-5.14.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:b40a0913196a1483f0fdc22a53f8719c3aef87f1c4d8d9c38d2ad4e207500fdf", size = 5224565, upload-time = "2026-10-04T15:19:05.264Z" },
353
- { url = "https://files.pythonhosted.org/packages/5c/c5/86ab02a41e77a7aa962af6545a406815aeb9abaecd9f25dec34dbc336b72/pypdfium2-5.14.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:790e2cac1641a65912b73bd7243f45195d36f1663c85a3e1a126a8f5867c82a3", size = 4704416, upload-time = "2026-10-04T15:19:07.05Z" },
354
- { url = "https://files.pythonhosted.org/packages/ac/de/fb75013f924c5a4dde4a4a41ec13e7495f9b80022bf35dd51baa54e05910/pypdfium2-5.14.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:09b99c8f0cb427eb17fec13c0862ed598bba34b4843df153f70fff806a2820bc", size = 5163621, upload-time = "2026-10-04T15:19:09.021Z" },
355
- { url = "https://files.pythonhosted.org/packages/cd/77/e59c814f10b533bc4565abe90ccef888ba29be45ada4627ebbf710961f0d/pypdfium2-5.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e70d87cb0577eab38f2106f9c9606b458930beef612a1b5f298772ed259f5ec0", size = 5121606, upload-time = "2026-10-04T15:19:10.609Z" },
356
- { url = "https://files.pythonhosted.org/packages/21/25/e067396b4bdd26c19f0997bfa3422d3975a49ceec2c59668e7599f2adcba/pypdfium2-5.14.0-py3-none-pyemscripten_2026_0_wasm32.whl", hash = "sha256:c73be14076bedebd9bcaf9b062579c95c668580043bccd29eb0db502101d5716", size = 2675501, upload-time = "2026-10-04T15:19:12.588Z" },
357
- { url = "https://files.pythonhosted.org/packages/7f/0c/6c21f68a57d0c4c506b9e5f72506ba91d8dde47eef699f3fd9561f7bff0e/pypdfium2-5.14.0-py3-none-win32.whl", hash = "sha256:9fd5cc94a389d50298e4d8cb79af6b9b8e0d785606e2a937725dc6e271c9c6e6", size = 3805374, upload-time = "2026-10-04T15:19:14.357Z" },
358
- { url = "https://files.pythonhosted.org/packages/00/dc/ca7874924c9cfd701ad53f89529968523790e70473e0b71e834668316148/pypdfium2-5.14.0-py3-none-win_amd64.whl", hash = "sha256:149fd5c6397b8df8bf7911a93506eff0be874f877afe7ac936cf5d37d21a6a06", size = 3947280, upload-time = "2026-10-04T15:19:16.302Z" },
359
- { url = "https://files.pythonhosted.org/packages/46/ab/35f2276deeeebb781925e2647dd88a39f8ea1a910104a0dbb28218473502/pypdfium2-5.14.0-py3-none-win_arm64.whl", hash = "sha256:eb8aeca157808f323e39ea298cc6d6c8e080c192ea2efb1ca81daa0f0ff4d095", size = 3745021, upload-time = "2026-10-04T15:19:18.276Z" },
360
- ]
361
-
362
- [[package]]
363
- name = "typing-extensions"
364
- version = "4.16.0"
365
- source = { registry = "https://pypi.org/simple" }
366
- sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
367
- wheels = [
368
- { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
369
- ]
@@ -1,138 +0,0 @@
1
- """Word text with tracked changes and comments inline, as a reviewer sees them.
2
-
3
- python view_docx.py <file>
4
-
5
- [+new+] and [-old-] carry author and date; a space at the edge of a change stays
6
- inside the marks. «text»[comment ...] marks what a comment is anchored to.
7
- """
8
-
9
- import sys
10
-
11
- import docx
12
- from docx.table import Table
13
-
14
- W = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
15
- ADDED = {f"{W}ins": "+", f"{W}moveTo": "+"}
16
- REMOVED = {f"{W}del": "-", f"{W}moveFrom": "-"}
17
-
18
-
19
- def signed(author, date) -> str:
20
- return f"{author or 'unknown author'}, {str(date or 'no date')[:10]}"
21
-
22
-
23
- def run_text(run) -> str:
24
- parts = []
25
- for node in run:
26
- if node.tag in (f"{W}t", f"{W}delText"):
27
- parts.append(node.text or "")
28
- elif node.tag == f"{W}tab":
29
- parts.append("\t")
30
- elif node.tag in (f"{W}br", f"{W}cr"):
31
- parts.append("\n")
32
- return "".join(parts)
33
-
34
-
35
- def walk(element, comments: dict, seen: set) -> str:
36
- """Read only: the order of runs and changes in a paragraph. Never edit through it."""
37
- parts = []
38
- for node in element:
39
- if node.tag == f"{W}r":
40
- parts.append(run_text(node))
41
- elif node.tag in ADDED or node.tag in REMOVED:
42
- mark = ADDED.get(node.tag) or REMOVED[node.tag]
43
- who = signed(node.get(f"{W}author"), node.get(f"{W}date"))
44
- parts.append(f"[{mark}{walk(node, comments, seen)}{mark}]({who})")
45
- elif node.tag == f"{W}commentRangeStart":
46
- parts.append("«")
47
- elif node.tag == f"{W}commentRangeEnd":
48
- number = int(node.get(f"{W}id"))
49
- seen.add(number)
50
- comment = comments.get(number)
51
- who = signed(comment.author, comment.timestamp) if comment else "unknown author"
52
- body = comment.text if comment else ""
53
- parts.append(f"»[comment {number}, {who}: {body}]")
54
- elif node.tag in (f"{W}hyperlink", f"{W}smartTag", f"{W}sdt", f"{W}sdtContent"):
55
- parts.append(walk(node, comments, seen))
56
- return "".join(parts)
57
-
58
-
59
- def line(paragraph, comments: dict, seen: set) -> str:
60
- text = walk(paragraph._p, comments, seen)
61
- style = paragraph.style.name if paragraph.style is not None else ""
62
- return f"# {text}" if text and style.startswith("Heading") else text
63
-
64
-
65
- def table_lines(table, comments: dict, seen: set) -> list[str]:
66
- lines = []
67
- for row in table.rows:
68
- cells = (" ".join(walk(p, comments, seen) for p in cell._tc.iter(f"{W}p")) for cell in row.cells)
69
- lines.append(" | ".join(cells))
70
- return lines
71
-
72
-
73
- def body_lines(document, comments: dict, seen: set) -> list[str]:
74
- lines = []
75
- for block in document.iter_inner_content():
76
- if isinstance(block, Table):
77
- lines += table_lines(block, comments, seen)
78
- else:
79
- lines.append(line(block, comments, seen))
80
- return [text for text in lines if text]
81
-
82
-
83
- def margin_lines(document, kind: str, comments: dict, seen: set) -> list[str]:
84
- """Each section's headers or footers that apply. A linked one resolves to a part already shown."""
85
- even_pages = document.settings.odd_and_even_pages_header_footer
86
- lines = []
87
- shown = set()
88
- for number, section in enumerate(document.sections, 1):
89
- where = f", section {number}" if number > 1 else ""
90
- variants = [("", getattr(section, kind))]
91
- if section.different_first_page_header_footer:
92
- variants.append(("first-page ", getattr(section, f"first_page_{kind}")))
93
- if even_pages:
94
- variants.append(("even-page ", getattr(section, f"even_page_{kind}")))
95
- for variant, margin in variants:
96
- if margin.part.partname not in shown:
97
- shown.add(margin.part.partname)
98
- for block in margin.iter_inner_content():
99
- if isinstance(block, Table):
100
- rows = [row for row in table_lines(block, comments, seen) if row.strip(" |")]
101
- else:
102
- rows = [block.text] if block.text.strip() else []
103
- lines += [f"[{variant}{kind}{where}] {row}" for row in rows]
104
- return lines
105
-
106
-
107
- def view(path, out=None, pages=None) -> dict:
108
- document = docx.Document(str(path))
109
- comments = {comment.comment_id: comment for comment in document.comments}
110
- seen: set[int] = set()
111
- lines = margin_lines(document, "header", comments, seen)
112
- lines += body_lines(document, comments, seen)
113
- lines += margin_lines(document, "footer", comments, seen)
114
- revisions = list(document.revisions)
115
- # What the walk above cannot place: changes in headers and notes, formatting, paragraph marks.
116
- elsewhere = [
117
- r for r in revisions
118
- if r.story != "word/document.xml"
119
- or r.is_paragraph_mark
120
- or r.revision_type not in ("insertion", "deletion", "move_from", "move_to")
121
- ]
122
- if elsewhere:
123
- lines.append("--- other tracked changes")
124
- lines += [f"{r.story} {r.revision_type} ({signed(r.author, r.date)}): {r.text}" for r in elsewhere]
125
- unplaced = [c for number, c in comments.items() if number not in seen]
126
- if unplaced:
127
- lines.append("--- comments with no anchor")
128
- lines += [f"comment {c.comment_id}, {signed(c.author, c.timestamp)}: {c.text}" for c in unplaced]
129
- lines.insert(0, f"{len(revisions)} tracked change(s), {len(comments)} comment(s).")
130
- return {
131
- "text": "\n".join(lines),
132
- "images": [],
133
- "note": "Text only: Word files are not rendered here. List numbers and footnote bodies are not shown.",
134
- }
135
-
136
-
137
- if __name__ == "__main__":
138
- print(view(sys.argv[1])["text"])