aisoc-cli 0.1.0__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.
- aisoc_cli-0.1.0/.gitignore +162 -0
- aisoc_cli-0.1.0/PKG-INFO +90 -0
- aisoc_cli-0.1.0/README.md +58 -0
- aisoc_cli-0.1.0/pyproject.toml +88 -0
- aisoc_cli-0.1.0/src/aisoc_cli/__init__.py +3 -0
- aisoc_cli-0.1.0/src/aisoc_cli/main.py +1372 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/connector/README.md.tmpl +37 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/connector/connector.py.tmpl +106 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/connector/plugin.yaml.tmpl +42 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/detection/README.md.tmpl +30 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/detection/plugin.yaml.tmpl +17 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/detection/rules/example.yaml.tmpl +28 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/enricher/README.md.tmpl +29 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/enricher/plugin.py.tmpl +42 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/enricher/plugin.yaml.tmpl +29 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/responder/README.md.tmpl +25 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/responder/plugin.py.tmpl +66 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/responder/plugin.yaml.tmpl +32 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/widget/README.md.tmpl +40 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/widget/plugin.yaml.tmpl +29 -0
- aisoc_cli-0.1.0/src/aisoc_cli/templates/widget/widget.py.tmpl +50 -0
- aisoc_cli-0.1.0/tests/__init__.py +0 -0
- aisoc_cli-0.1.0/tests/test_cli.py +136 -0
- aisoc_cli-0.1.0/tests/test_ops_commands.py +282 -0
- aisoc_cli-0.1.0/tests/test_packaging.py +84 -0
- aisoc_cli-0.1.0/tests/test_replay_command.py +346 -0
- aisoc_cli-0.1.0/tests/test_submit_command.py +424 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Environment
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
|
|
6
|
+
# Dependencies
|
|
7
|
+
node_modules/
|
|
8
|
+
.pnpm-store/
|
|
9
|
+
|
|
10
|
+
# Build outputs
|
|
11
|
+
dist/
|
|
12
|
+
build/
|
|
13
|
+
.next/
|
|
14
|
+
out/
|
|
15
|
+
.turbo/
|
|
16
|
+
# T3.8 — generated Storybook static bundle. The source lives in
|
|
17
|
+
# apps/web/.storybook + apps/web/stories; the built bundle is uploaded
|
|
18
|
+
# as a CI artifact rather than committed.
|
|
19
|
+
apps/web/storybook-static/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
__pycache__/
|
|
22
|
+
*.pyc
|
|
23
|
+
*.pyo
|
|
24
|
+
|
|
25
|
+
# Go
|
|
26
|
+
*.exe
|
|
27
|
+
*.test
|
|
28
|
+
vendor/
|
|
29
|
+
bin/
|
|
30
|
+
|
|
31
|
+
# Terraform
|
|
32
|
+
.terraform/
|
|
33
|
+
*.tfstate
|
|
34
|
+
*.tfstate.backup
|
|
35
|
+
.terraform.lock.hcl
|
|
36
|
+
*.tfvars
|
|
37
|
+
!terraform.tfvars.example
|
|
38
|
+
|
|
39
|
+
# Python
|
|
40
|
+
.venv/
|
|
41
|
+
venv/
|
|
42
|
+
.eval-test-venv/
|
|
43
|
+
.mypy_cache/
|
|
44
|
+
.pytest_cache/
|
|
45
|
+
htmlcov/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
coverage.xml
|
|
49
|
+
# Phase 2.2 — vitest v8 coverage outputs (apps/web/coverage/) and
|
|
50
|
+
# any other per-service coverage dump. Generated on every CI run,
|
|
51
|
+
# uploaded as artefacts — never committed.
|
|
52
|
+
coverage/
|
|
53
|
+
**/coverage/
|
|
54
|
+
# BUT keep app-source directories literally named `coverage` (the
|
|
55
|
+
# /tools/coverage route, and `components/coverage/` behind /coverage-advisor).
|
|
56
|
+
# The rules above are for generated test-coverage output; without these
|
|
57
|
+
# negations the coverage tool page silently drops from the build.
|
|
58
|
+
#
|
|
59
|
+
# `components/` was missing here, which is worse than it sounds: the component
|
|
60
|
+
# was tracked because it predates the rule, so nothing looked wrong, but every
|
|
61
|
+
# *new* file beside it — a test especially — was ignored on `git add -A` and
|
|
62
|
+
# never reached CI. A test that cannot be committed is indistinguishable from
|
|
63
|
+
# a test that passes.
|
|
64
|
+
!apps/web/src/app/**/coverage/
|
|
65
|
+
!apps/web/src/app/**/coverage/**
|
|
66
|
+
!apps/web/src/components/coverage/
|
|
67
|
+
!apps/web/src/components/coverage/**
|
|
68
|
+
*.lcov
|
|
69
|
+
|
|
70
|
+
# IDE
|
|
71
|
+
.idea/
|
|
72
|
+
.vscode/
|
|
73
|
+
.cursor/
|
|
74
|
+
.claude/
|
|
75
|
+
*.swp
|
|
76
|
+
*.swo
|
|
77
|
+
|
|
78
|
+
# Go test/build binaries inside plugins
|
|
79
|
+
plugins/**/*-build-test
|
|
80
|
+
plugins/**/*-build
|
|
81
|
+
|
|
82
|
+
# OS
|
|
83
|
+
.DS_Store
|
|
84
|
+
Thumbs.db
|
|
85
|
+
|
|
86
|
+
# Logs
|
|
87
|
+
logs/
|
|
88
|
+
*.log
|
|
89
|
+
# But: the AIT-LDS fidelity-benchmark fixture ships an Apache CLF
|
|
90
|
+
# access.log on purpose (T5.3 in v8.0). It is committed test data,
|
|
91
|
+
# not a runtime log, so unblock it explicitly.
|
|
92
|
+
!services/agents/tests/eval_data/**/*.log
|
|
93
|
+
|
|
94
|
+
# Docker
|
|
95
|
+
.docker/
|
|
96
|
+
|
|
97
|
+
# Secrets
|
|
98
|
+
*.pem
|
|
99
|
+
*.key
|
|
100
|
+
*.crt
|
|
101
|
+
secrets/
|
|
102
|
+
.gstack/
|
|
103
|
+
|
|
104
|
+
# Local eval / build artifacts
|
|
105
|
+
eval_report.json
|
|
106
|
+
eval_mitre_accuracy_report.json
|
|
107
|
+
.gocache/
|
|
108
|
+
*.tsbuildinfo
|
|
109
|
+
|
|
110
|
+
# PR body scratch files (local working copy, never committed)
|
|
111
|
+
.pr-body-*.md
|
|
112
|
+
|
|
113
|
+
# Local progress tracking (per AGENTS.md preference, not committed)
|
|
114
|
+
PROGRESS.md
|
|
115
|
+
PR_TRIAGE.md
|
|
116
|
+
|
|
117
|
+
# Acceptance harness ledger (.aisoc/acceptance-history.jsonl is per-machine and
|
|
118
|
+
# accumulates across runs — useful for "is this getting slower?" but not for
|
|
119
|
+
# version control. Same applies to any other harness state we drop in here.)
|
|
120
|
+
.aisoc/
|
|
121
|
+
|
|
122
|
+
# Detection-import upstream clones (populated by tools.detection_import)
|
|
123
|
+
.import-cache/
|
|
124
|
+
|
|
125
|
+
# Docusaurus generated cache (apps/docs)
|
|
126
|
+
apps/docs/.docusaurus/
|
|
127
|
+
apps/docs/build/
|
|
128
|
+
|
|
129
|
+
# Local QA artifacts (screenshots from manual UI checks)
|
|
130
|
+
apps/web/.qa-screenshots/
|
|
131
|
+
|
|
132
|
+
# Runtime playbook store. `PlaybookStore` keeps the bundled corpus read-only and
|
|
133
|
+
# writes every mutation to `index.json` in the same directory, so anything that
|
|
134
|
+
# exercises `POST /api/v1/playbooks` — a test, a local run, a reproduction of a
|
|
135
|
+
# bug — leaves a several-thousand-line file behind. Committing it ships whatever
|
|
136
|
+
# the last run happened to hold as if it were curated content, and the playbook
|
|
137
|
+
# schema lint counts it as a 65th file.
|
|
138
|
+
services/agents/data/playbooks/index.json
|
|
139
|
+
|
|
140
|
+
# Marketplace index staged into the API service build context at deploy time
|
|
141
|
+
# (see infra/fly/fly-demo-deploy.sh). The canonical source is marketplace/index.json
|
|
142
|
+
# at the repo root; the API Dockerfile only sees its own dir as build context, so
|
|
143
|
+
# the deploy script copies the index in just-in-time. We never commit the copy.
|
|
144
|
+
services/api/marketplace/
|
|
145
|
+
|
|
146
|
+
# Live-dumped graph schema produced by `scripts/export_graph_schema.py`
|
|
147
|
+
# (default mode). The source of truth is `schemas/graph-schema.yaml`; the
|
|
148
|
+
# `-current.yaml` file is a runtime artefact that varies by environment.
|
|
149
|
+
schemas/graph-schema-current.yaml
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Compiled Go binaries. `services/demo-producer/demo-producer` was committed as
|
|
153
|
+
# a 6.9 MB macOS arm64 executable in a repository whose Dockerfile builds a
|
|
154
|
+
# GOOS=linux one — nothing consumed it, nothing rebuilt it, and running the
|
|
155
|
+
# documented `go build ./...` silently overwrote it and dirtied the tree.
|
|
156
|
+
/services/demo-producer/demo-producer
|
|
157
|
+
/services/enrichment/enrichment
|
|
158
|
+
/services/ingest/ingest
|
|
159
|
+
|
|
160
|
+
# Generated by scripts/resolve_port_conflicts.py when a host port AiSOC
|
|
161
|
+
# publishes is already in use. Machine-specific by definition.
|
|
162
|
+
docker-compose.ports.yml
|
aisoc_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: aisoc-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AiSOC Developer CLI — scaffold, validate, and publish plugins and detections
|
|
5
|
+
Project-URL: Homepage, https://github.com/beenuar/AiSOC
|
|
6
|
+
Project-URL: Documentation, https://github.com/beenuar/AiSOC#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/beenuar/AiSOC
|
|
8
|
+
Author-email: Beenu Arora <beenu@cyble.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: aisoc,cli,plugin,security,soc
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: click>=8.1
|
|
20
|
+
Requires-Dist: cryptography<51,>=46
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: jsonschema>=4.21
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: rich>=13.7
|
|
25
|
+
Requires-Dist: structlog>=24.1
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy<3,>=2.3.1; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff<0.17,>=0.16.8; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# aisoc-cli
|
|
34
|
+
|
|
35
|
+
Developer CLI for building, validating, and publishing AiSOC plugins and detection rules.
|
|
36
|
+
|
|
37
|
+
> **Status — monorepo today, not yet on PyPI.** The CLI ships from this monorepo and is fully functional today. `pip install aisoc-cli` does not resolve: the upload is blocked on registry credentials, which is an account action rather than a code change. The CLI name (`aisoc`) and command surface stay identical once it ships.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Today (from this monorepo):
|
|
43
|
+
git clone https://github.com/beenuar/AiSOC.git
|
|
44
|
+
cd AiSOC && pip install -e packages/aisoc-cli
|
|
45
|
+
|
|
46
|
+
# Not yet on PyPI — the upload is blocked on registry credentials,
|
|
47
|
+
# which is an account action rather than a code change. Until then, install
|
|
48
|
+
# from source with the command above.
|
|
49
|
+
# pipx install aisoc-cli # recommended (isolated venv)
|
|
50
|
+
# pip install aisoc-cli
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Commands
|
|
54
|
+
|
|
55
|
+
### Plugin Scaffold
|
|
56
|
+
```bash
|
|
57
|
+
aisoc plugin scaffold my-enricher
|
|
58
|
+
aisoc plugin scaffold my-connector --type connector
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Plugin Validate
|
|
62
|
+
```bash
|
|
63
|
+
aisoc plugin validate ./my-enricher
|
|
64
|
+
aisoc plugin validate ./my-enricher/plugin.yaml
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Plugin Publish
|
|
68
|
+
```bash
|
|
69
|
+
export AISOC_API_URL=https://api.example.com
|
|
70
|
+
export AISOC_API_KEY=sk-...
|
|
71
|
+
aisoc plugin publish ./my-enricher
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Detection Validate
|
|
75
|
+
```bash
|
|
76
|
+
aisoc detection validate ./detections/brute-force.yaml
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Key Generation
|
|
80
|
+
```bash
|
|
81
|
+
aisoc keygen # generates ~/.aisoc/signing.key + signing.pub
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Environment Variables
|
|
85
|
+
|
|
86
|
+
| Variable | Description |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `AISOC_API_URL` | AiSOC API base URL (default: `http://localhost:8000`) |
|
|
89
|
+
| `AISOC_API_KEY` | API key for authentication |
|
|
90
|
+
| `AISOC_SIGNING_KEY` | Path to Ed25519 private key (default: `~/.aisoc/signing.key`) |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# aisoc-cli
|
|
2
|
+
|
|
3
|
+
Developer CLI for building, validating, and publishing AiSOC plugins and detection rules.
|
|
4
|
+
|
|
5
|
+
> **Status — monorepo today, not yet on PyPI.** The CLI ships from this monorepo and is fully functional today. `pip install aisoc-cli` does not resolve: the upload is blocked on registry credentials, which is an account action rather than a code change. The CLI name (`aisoc`) and command surface stay identical once it ships.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Today (from this monorepo):
|
|
11
|
+
git clone https://github.com/beenuar/AiSOC.git
|
|
12
|
+
cd AiSOC && pip install -e packages/aisoc-cli
|
|
13
|
+
|
|
14
|
+
# Not yet on PyPI — the upload is blocked on registry credentials,
|
|
15
|
+
# which is an account action rather than a code change. Until then, install
|
|
16
|
+
# from source with the command above.
|
|
17
|
+
# pipx install aisoc-cli # recommended (isolated venv)
|
|
18
|
+
# pip install aisoc-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
### Plugin Scaffold
|
|
24
|
+
```bash
|
|
25
|
+
aisoc plugin scaffold my-enricher
|
|
26
|
+
aisoc plugin scaffold my-connector --type connector
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Plugin Validate
|
|
30
|
+
```bash
|
|
31
|
+
aisoc plugin validate ./my-enricher
|
|
32
|
+
aisoc plugin validate ./my-enricher/plugin.yaml
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Plugin Publish
|
|
36
|
+
```bash
|
|
37
|
+
export AISOC_API_URL=https://api.example.com
|
|
38
|
+
export AISOC_API_KEY=sk-...
|
|
39
|
+
aisoc plugin publish ./my-enricher
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Detection Validate
|
|
43
|
+
```bash
|
|
44
|
+
aisoc detection validate ./detections/brute-force.yaml
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Key Generation
|
|
48
|
+
```bash
|
|
49
|
+
aisoc keygen # generates ~/.aisoc/signing.key + signing.pub
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Environment Variables
|
|
53
|
+
|
|
54
|
+
| Variable | Description |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `AISOC_API_URL` | AiSOC API base URL (default: `http://localhost:8000`) |
|
|
57
|
+
| `AISOC_API_KEY` | API key for authentication |
|
|
58
|
+
| `AISOC_SIGNING_KEY` | Path to Ed25519 private key (default: `~/.aisoc/signing.key`) |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aisoc-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "AiSOC Developer CLI — scaffold, validate, and publish plugins and detections"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Beenu Arora", email = "beenu@cyble.com" },
|
|
13
|
+
]
|
|
14
|
+
keywords = ["aisoc", "soc", "security", "cli", "plugin"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Security",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.11"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"click>=8.1",
|
|
27
|
+
"httpx>=0.27",
|
|
28
|
+
"pyyaml>=6.0",
|
|
29
|
+
"jsonschema>=4.21",
|
|
30
|
+
# Ed25519 publisher signing. The verifier is services/api, so this is one
|
|
31
|
+
# half of a contract and carries the same range as every other install
|
|
32
|
+
# path — see scripts/check_dependency_pins.py.
|
|
33
|
+
"cryptography>=46,<51",
|
|
34
|
+
"rich>=13.7",
|
|
35
|
+
"structlog>=24.1",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
aisoc = "aisoc_cli.main:cli"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev = [
|
|
43
|
+
"pytest>=8.0",
|
|
44
|
+
"pytest-asyncio>=0.23",
|
|
45
|
+
"mypy>=2.3.1,<3",
|
|
46
|
+
"ruff>=0.16.8,<0.17",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://github.com/beenuar/AiSOC"
|
|
51
|
+
Documentation = "https://github.com/beenuar/AiSOC#readme"
|
|
52
|
+
Repository = "https://github.com/beenuar/AiSOC"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.wheel]
|
|
55
|
+
# `packages` already carries everything under src/aisoc_cli, templates
|
|
56
|
+
# included, so they reach the wheel at aisoc_cli/templates/... where
|
|
57
|
+
# `importlib.resources` expects them.
|
|
58
|
+
#
|
|
59
|
+
# There used to be a `force-include` mapping that same directory to that same
|
|
60
|
+
# destination. Hatchling then tried to add each template twice and refused:
|
|
61
|
+
# ValueError: A second file is being added to the wheel archive at the same
|
|
62
|
+
# path: aisoc_cli/templates/connector/README.md.tmpl
|
|
63
|
+
# Nothing built this wheel except a tag, so the failure was invisible until
|
|
64
|
+
# the release ran — and it would have blocked the first real publish.
|
|
65
|
+
# `test_templates_reach_the_wheel` in tests/ now asserts the templates are
|
|
66
|
+
# present, so removing the redundant mapping cannot quietly drop them.
|
|
67
|
+
packages = ["src/aisoc_cli"]
|
|
68
|
+
|
|
69
|
+
[tool.hatch.build.targets.sdist]
|
|
70
|
+
include = [
|
|
71
|
+
"src/aisoc_cli",
|
|
72
|
+
"tests",
|
|
73
|
+
"README.md",
|
|
74
|
+
"LICENSE",
|
|
75
|
+
"pyproject.toml",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[tool.ruff]
|
|
79
|
+
line-length = 100
|
|
80
|
+
target-version = "py311"
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
strict = true
|
|
84
|
+
python_version = "3.11"
|
|
85
|
+
|
|
86
|
+
[tool.pytest.ini_options]
|
|
87
|
+
asyncio_mode = "auto"
|
|
88
|
+
testpaths = ["tests"]
|