agent-orchestration-process 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/PKG-INFO +1 -1
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/pyproject.toml +1 -1
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/pyproject.toml.orig +1 -1
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/cli.py +40 -8
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/isolation.py +1 -1
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/models.py +16 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/prompt_transport.py +25 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/runner.py +41 -2
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/LICENSE +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/README.md +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/__init__.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/__main__.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/batch.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/codex_auth.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/codex_routes.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/dsh_runner.mjs +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/egress.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/egress_bridge.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/integration.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/locks.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/migration.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/model_catalog.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/model_listing.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/pricing.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/provider_versions.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/worktrees.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/zcode.py +0 -0
- {agent_orchestration_process-0.2.0 → agent_orchestration_process-0.2.1}/src/agent_orchestration_process/zcode_protocol.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-orchestration-process
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Run bounded, non-interactive jobs across agent harnesses with isolation, exact resume, and provenance
|
|
5
5
|
Keywords: agent-orchestration,ai-agents,autoresearch,coding-agents,git-worktrees
|
|
6
6
|
Author: AOP contributors
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-orchestration-process"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Run bounded, non-interactive jobs across agent harnesses with isolation, exact resume, and provenance"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-orchestration-process"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Run bounded, non-interactive jobs across agent harnesses with isolation, exact resume, and provenance"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -22,7 +22,7 @@ from .isolation import PROFILES, explain_profile, resolve_policy
|
|
|
22
22
|
from .locks import exclusive_lock, task_lock_path
|
|
23
23
|
from .model_catalog import ModelCatalog, ensure_catalog_fresh
|
|
24
24
|
from .model_listing import AGENTS, AvailableModel, list_models
|
|
25
|
-
from .models import RunRequest, RunResult
|
|
25
|
+
from .models import PromptPart, RunRequest, RunResult
|
|
26
26
|
from .runner import AgentRunner, RunStore, adapter_for, provider_runtime_record
|
|
27
27
|
from .migration import migrate_runs, migration_roots, print_counts
|
|
28
28
|
from .worktrees import (
|
|
@@ -409,9 +409,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
409
409
|
prefix="aop: ",
|
|
410
410
|
stream=sys.stderr,
|
|
411
411
|
)
|
|
412
|
+
prompt, prompt_parts = _read_prompt(args)
|
|
412
413
|
result = AgentRunner(manager, adapter_for(args.agent)).run(
|
|
413
414
|
task=task,
|
|
414
|
-
prompt=
|
|
415
|
+
prompt=prompt,
|
|
416
|
+
prompt_parts=prompt_parts,
|
|
415
417
|
base=args.base,
|
|
416
418
|
model=args.model,
|
|
417
419
|
inference_provider=args.inference_provider,
|
|
@@ -432,9 +434,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
432
434
|
return _report_run(result, manager, json_output=args.json)
|
|
433
435
|
|
|
434
436
|
if args.command == "resume":
|
|
437
|
+
prompt, prompt_parts = _read_prompt(args)
|
|
435
438
|
result = AgentRunner(manager).resume(
|
|
436
439
|
run_id=args.run_id,
|
|
437
|
-
prompt=
|
|
440
|
+
prompt=prompt,
|
|
441
|
+
prompt_parts=prompt_parts,
|
|
438
442
|
timeout_seconds=args.timeout,
|
|
439
443
|
artifacts=args.artifact,
|
|
440
444
|
input_paths=args.input_paths,
|
|
@@ -472,6 +476,13 @@ def _add_prompt_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
472
476
|
prompts = parser.add_mutually_exclusive_group(required=True)
|
|
473
477
|
prompts.add_argument("--prompt")
|
|
474
478
|
prompts.add_argument("--prompt-file", type=Path)
|
|
479
|
+
prompts.add_argument(
|
|
480
|
+
"--prompt-parts",
|
|
481
|
+
type=Path,
|
|
482
|
+
metavar="PATH",
|
|
483
|
+
help='JSON list of prompt blocks, each {"text": ..., "cache": true|false}; '
|
|
484
|
+
"Claude caches the prompt through the one cached block (claude only)",
|
|
485
|
+
)
|
|
475
486
|
|
|
476
487
|
|
|
477
488
|
def _add_artifact_arguments(parser: argparse.ArgumentParser) -> None:
|
|
@@ -520,15 +531,36 @@ def _read_system_prompt(args: argparse.Namespace) -> str | None:
|
|
|
520
531
|
) from error
|
|
521
532
|
|
|
522
533
|
|
|
523
|
-
def _read_prompt(args: argparse.Namespace) -> str:
|
|
534
|
+
def _read_prompt(args: argparse.Namespace) -> tuple[str, tuple[PromptPart, ...] | None]:
|
|
524
535
|
if args.prompt is not None:
|
|
525
|
-
return args.prompt
|
|
536
|
+
return args.prompt, None
|
|
537
|
+
path = args.prompt_file or args.prompt_parts
|
|
526
538
|
try:
|
|
527
|
-
|
|
539
|
+
text = path.read_text()
|
|
528
540
|
except OSError as error:
|
|
541
|
+
raise AOPError(f"could not read prompt file {path}: {error}") from error
|
|
542
|
+
if args.prompt_file is not None:
|
|
543
|
+
return text, None
|
|
544
|
+
try:
|
|
545
|
+
blocks = json.loads(text)
|
|
546
|
+
except json.JSONDecodeError as error:
|
|
547
|
+
raise AOPError(f"prompt parts file {path} is not JSON: {error}") from error
|
|
548
|
+
if not isinstance(blocks, list) or not all(
|
|
549
|
+
isinstance(block, dict)
|
|
550
|
+
and set(block) <= {"text", "cache"}
|
|
551
|
+
and isinstance(block.get("text"), str)
|
|
552
|
+
and isinstance(block.get("cache", False), bool)
|
|
553
|
+
for block in blocks
|
|
554
|
+
):
|
|
529
555
|
raise AOPError(
|
|
530
|
-
f"
|
|
531
|
-
|
|
556
|
+
f"prompt parts file {path} must be a list of "
|
|
557
|
+
'{"text": string, "cache": boolean} objects'
|
|
558
|
+
)
|
|
559
|
+
parts = tuple(
|
|
560
|
+
PromptPart(chars=len(block["text"]), cache=block.get("cache", False))
|
|
561
|
+
for block in blocks
|
|
562
|
+
)
|
|
563
|
+
return "".join(block["text"] for block in blocks), parts
|
|
532
564
|
|
|
533
565
|
|
|
534
566
|
def _report_policy(
|
|
@@ -157,7 +157,7 @@ def resolve_policy(
|
|
|
157
157
|
host={
|
|
158
158
|
"access": selected.host_access,
|
|
159
159
|
"runtime_paths": (
|
|
160
|
-
["/usr", "/bin", "/lib", "/lib64", "/etc runtime files"]
|
|
160
|
+
["/usr", "/bin", "/sbin", "/lib", "/lib64", "/etc runtime files"]
|
|
161
161
|
if selected.host_access == "runtime-only"
|
|
162
162
|
else ["/"]
|
|
163
163
|
),
|
|
@@ -57,6 +57,15 @@ class InferenceRoute:
|
|
|
57
57
|
return cls(**fields)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class PromptPart:
|
|
62
|
+
"""One content block of a prompt; `cache` asks Claude to cache the prompt
|
|
63
|
+
up to and including this block. The text itself stays in RunRequest.prompt."""
|
|
64
|
+
|
|
65
|
+
chars: int
|
|
66
|
+
cache: bool = False
|
|
67
|
+
|
|
68
|
+
|
|
60
69
|
@dataclass(frozen=True)
|
|
61
70
|
class RunRequest:
|
|
62
71
|
run_id: str
|
|
@@ -90,6 +99,9 @@ class RunRequest:
|
|
|
90
99
|
allowed_urls: tuple[str, ...] = ()
|
|
91
100
|
# A sealed run's private, initially empty /workspace is writable.
|
|
92
101
|
writable_workspace: bool = False
|
|
102
|
+
# The prompt split into content blocks, in order; their lengths add up to
|
|
103
|
+
# len(prompt). None sends the prompt as one block.
|
|
104
|
+
prompt_parts: tuple[PromptPart, ...] | None = None
|
|
93
105
|
# How `aop migrate-results` converted an older record, if it did.
|
|
94
106
|
migration: dict[str, Any] | None = None
|
|
95
107
|
|
|
@@ -106,6 +118,10 @@ class RunRequest:
|
|
|
106
118
|
if fields.get("tools") is not None:
|
|
107
119
|
fields["tools"] = tuple(fields["tools"])
|
|
108
120
|
fields["allowed_urls"] = tuple(fields.get("allowed_urls", ()))
|
|
121
|
+
if fields.get("prompt_parts") is not None:
|
|
122
|
+
fields["prompt_parts"] = tuple(
|
|
123
|
+
PromptPart(**part) for part in fields["prompt_parts"]
|
|
124
|
+
)
|
|
109
125
|
fields["inputs"] = tuple(
|
|
110
126
|
InputSnapshot.from_dict(item) for item in fields["inputs"]
|
|
111
127
|
)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""Prompt transport independent of the OS argument and environment limits."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Sequence
|
|
3
4
|
from contextlib import contextmanager
|
|
4
5
|
import json
|
|
5
6
|
import os
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
import tempfile
|
|
8
9
|
|
|
10
|
+
from .models import PromptPart
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
# Native protocols remain in their adapters. Runtime-file is only for CLIs
|
|
11
14
|
# whose one-shot parser accepts text exclusively as an argument.
|
|
@@ -47,6 +50,28 @@ def stdin_prompt(provider: str, prompt: str) -> str | None:
|
|
|
47
50
|
return None
|
|
48
51
|
|
|
49
52
|
|
|
53
|
+
def claude_message(prompt: str, parts: Sequence[PromptPart]) -> str:
|
|
54
|
+
"""A stream-json user message whose content blocks follow `parts`.
|
|
55
|
+
|
|
56
|
+
Claude Code marks its own system blocks and the context it appends after
|
|
57
|
+
the prompt with one-hour cache marks, and a five-minute mark must not
|
|
58
|
+
precede a one-hour one, so a caller's mark uses one hour too.
|
|
59
|
+
"""
|
|
60
|
+
content = []
|
|
61
|
+
start = 0
|
|
62
|
+
for part in parts:
|
|
63
|
+
block: dict[str, object] = {
|
|
64
|
+
"type": "text",
|
|
65
|
+
"text": prompt[start : start + part.chars],
|
|
66
|
+
}
|
|
67
|
+
if part.cache:
|
|
68
|
+
block["cache_control"] = {"type": "ephemeral", "ttl": "1h"}
|
|
69
|
+
content.append(block)
|
|
70
|
+
start += part.chars
|
|
71
|
+
message = {"type": "user", "message": {"role": "user", "content": content}}
|
|
72
|
+
return json.dumps(message) + "\n"
|
|
73
|
+
|
|
74
|
+
|
|
50
75
|
@contextmanager
|
|
51
76
|
def prompt_stream(prompt: str | None):
|
|
52
77
|
"""A seekable stdin avoids blocking on pipe writes before the timeout starts."""
|
|
@@ -39,6 +39,7 @@ from .models import (
|
|
|
39
39
|
InputFile,
|
|
40
40
|
InputSnapshot,
|
|
41
41
|
InferenceRoute,
|
|
42
|
+
PromptPart,
|
|
42
43
|
RunArtifact,
|
|
43
44
|
RunRequest,
|
|
44
45
|
RunResult,
|
|
@@ -1221,7 +1222,11 @@ class ClaudeAdapter:
|
|
|
1221
1222
|
command,
|
|
1222
1223
|
cwd=worktree.path,
|
|
1223
1224
|
environment=environment,
|
|
1224
|
-
prompt=
|
|
1225
|
+
prompt=(
|
|
1226
|
+
prompt_transport.claude_message(request.prompt, request.prompt_parts)
|
|
1227
|
+
if request.prompt_parts is not None
|
|
1228
|
+
else prompt_transport.stdin_prompt(self.provider, request.prompt)
|
|
1229
|
+
),
|
|
1225
1230
|
timeout_seconds=request.timeout_seconds,
|
|
1226
1231
|
is_response=self._is_response,
|
|
1227
1232
|
)
|
|
@@ -1396,6 +1401,8 @@ class ClaudeAdapter:
|
|
|
1396
1401
|
command.extend(["--system-prompt", request.system_prompt])
|
|
1397
1402
|
if request.thinking_display is not None:
|
|
1398
1403
|
command.extend(["--thinking-display", request.thinking_display])
|
|
1404
|
+
if request.prompt_parts is not None:
|
|
1405
|
+
command.extend(["--input-format", "stream-json"])
|
|
1399
1406
|
if request.session_id:
|
|
1400
1407
|
command.extend(["--resume", request.session_id])
|
|
1401
1408
|
else:
|
|
@@ -4205,7 +4212,7 @@ def _isolated_root_command(bwrap: str) -> list[str]:
|
|
|
4205
4212
|
"--tmpfs",
|
|
4206
4213
|
"/tmp",
|
|
4207
4214
|
]
|
|
4208
|
-
for path in ("/usr", "/bin", "/lib", "/lib64"):
|
|
4215
|
+
for path in ("/usr", "/bin", "/sbin", "/lib", "/lib64"):
|
|
4209
4216
|
source = Path(path)
|
|
4210
4217
|
if source.exists():
|
|
4211
4218
|
command.extend(["--ro-bind", path, path])
|
|
@@ -4214,6 +4221,9 @@ def _isolated_root_command(bwrap: str) -> list[str]:
|
|
|
4214
4221
|
"/etc/ca-certificates",
|
|
4215
4222
|
"/etc/crypto-policies",
|
|
4216
4223
|
"/etc/hosts",
|
|
4224
|
+
# The dynamic linker finds libraries in folders registered under
|
|
4225
|
+
# /etc/ld.so.conf.d only through this cache.
|
|
4226
|
+
"/etc/ld.so.cache",
|
|
4217
4227
|
"/etc/localtime",
|
|
4218
4228
|
"/etc/nsswitch.conf",
|
|
4219
4229
|
"/etc/pki",
|
|
@@ -5775,8 +5785,10 @@ class AgentRunner:
|
|
|
5775
5785
|
allowed_urls: Sequence[str] = (),
|
|
5776
5786
|
writable_workspace: bool = False,
|
|
5777
5787
|
thinking_display: str | None = None,
|
|
5788
|
+
prompt_parts: Sequence[PromptPart] | None = None,
|
|
5778
5789
|
) -> RunResult:
|
|
5779
5790
|
self._validate_mode(mode)
|
|
5791
|
+
prompt_parts = self._validated_prompt_parts(prompt, prompt_parts)
|
|
5780
5792
|
if writable_workspace and profile != "sealed":
|
|
5781
5793
|
raise AOPError("--writable-workspace applies only to the sealed profile")
|
|
5782
5794
|
if thinking_display is not None:
|
|
@@ -5822,6 +5834,7 @@ class AgentRunner:
|
|
|
5822
5834
|
allowed_urls=allowed_urls,
|
|
5823
5835
|
writable_workspace=writable_workspace,
|
|
5824
5836
|
thinking_display=thinking_display,
|
|
5837
|
+
prompt_parts=prompt_parts,
|
|
5825
5838
|
)
|
|
5826
5839
|
worktree = (
|
|
5827
5840
|
self._create_sealed_workspace(request.run_id)
|
|
@@ -5854,6 +5867,7 @@ class AgentRunner:
|
|
|
5854
5867
|
timeout_seconds: float | None = None,
|
|
5855
5868
|
artifacts: Sequence[str] = (),
|
|
5856
5869
|
input_paths: Sequence[str | os.PathLike[str]] | None = None,
|
|
5870
|
+
prompt_parts: Sequence[PromptPart] | None = None,
|
|
5857
5871
|
_task_lock_held: bool = False,
|
|
5858
5872
|
) -> RunResult:
|
|
5859
5873
|
parent_request = self.store.load_request(run_id)
|
|
@@ -5865,6 +5879,7 @@ class AgentRunner:
|
|
|
5865
5879
|
)
|
|
5866
5880
|
self.adapter = adapter_for(parent_request.provider)
|
|
5867
5881
|
self._validate_mode(parent_request.mode)
|
|
5882
|
+
prompt_parts = self._validated_prompt_parts(prompt, prompt_parts)
|
|
5868
5883
|
self._validate_no_web(
|
|
5869
5884
|
parent_request.no_web, parent_request.mode, parent_request.profile
|
|
5870
5885
|
)
|
|
@@ -5952,6 +5967,7 @@ class AgentRunner:
|
|
|
5952
5967
|
allowed_urls=parent_request.allowed_urls,
|
|
5953
5968
|
writable_workspace=parent_request.writable_workspace,
|
|
5954
5969
|
thinking_display=parent_request.thinking_display,
|
|
5970
|
+
prompt_parts=prompt_parts,
|
|
5955
5971
|
)
|
|
5956
5972
|
return self._execute(
|
|
5957
5973
|
request,
|
|
@@ -6308,6 +6324,7 @@ class AgentRunner:
|
|
|
6308
6324
|
allowed_urls: tuple[str, ...] = (),
|
|
6309
6325
|
writable_workspace: bool = False,
|
|
6310
6326
|
thinking_display: str | None = None,
|
|
6327
|
+
prompt_parts: tuple[PromptPart, ...] | None = None,
|
|
6311
6328
|
) -> RunRequest:
|
|
6312
6329
|
return RunRequest(
|
|
6313
6330
|
run_id=str(uuid.uuid4()),
|
|
@@ -6335,6 +6352,7 @@ class AgentRunner:
|
|
|
6335
6352
|
allowed_urls=allowed_urls,
|
|
6336
6353
|
writable_workspace=writable_workspace,
|
|
6337
6354
|
thinking_display=thinking_display,
|
|
6355
|
+
prompt_parts=prompt_parts,
|
|
6338
6356
|
)
|
|
6339
6357
|
|
|
6340
6358
|
def _validate_provider_base_url(self, url: str | None, profile: str) -> None:
|
|
@@ -6356,6 +6374,27 @@ class AgentRunner:
|
|
|
6356
6374
|
if parsed.scheme not in {"http", "https"} or not parsed.hostname:
|
|
6357
6375
|
raise AOPError(f"--provider-base-url must be an http(s) URL: {url!r}")
|
|
6358
6376
|
|
|
6377
|
+
def _validated_prompt_parts(
|
|
6378
|
+
self, prompt: str, parts: Sequence[PromptPart] | None
|
|
6379
|
+
) -> tuple[PromptPart, ...] | None:
|
|
6380
|
+
if parts is None:
|
|
6381
|
+
return None
|
|
6382
|
+
if self.adapter.provider != "claude":
|
|
6383
|
+
raise AOPError(f"{self.adapter.provider} does not support --prompt-parts")
|
|
6384
|
+
parts = tuple(parts)
|
|
6385
|
+
if not parts or any(part.chars <= 0 for part in parts):
|
|
6386
|
+
raise AOPError("--prompt-parts needs at least one part, none of them empty")
|
|
6387
|
+
if sum(part.chars for part in parts) != len(prompt):
|
|
6388
|
+
raise AOPError("prompt parts do not add up to the prompt")
|
|
6389
|
+
# Anthropic accepts four cache marks per request, and Claude Code
|
|
6390
|
+
# places three: two on its system prompt and one on the context it
|
|
6391
|
+
# appends after the prompt.
|
|
6392
|
+
if sum(part.cache for part in parts) > 1:
|
|
6393
|
+
raise AOPError(
|
|
6394
|
+
"Claude allows one cached prompt part; Claude Code uses the other marks"
|
|
6395
|
+
)
|
|
6396
|
+
return parts
|
|
6397
|
+
|
|
6359
6398
|
def _validated_framing(
|
|
6360
6399
|
self, system_prompt: str | None, tools: Sequence[str] | None, no_web: bool
|
|
6361
6400
|
) -> tuple[str, ...] | None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|