archastro-sdk 0.1.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.
- archastro_sdk-0.1.1/.github/workflows/ci.yml +78 -0
- archastro_sdk-0.1.1/.github/workflows/publish.yml +97 -0
- archastro_sdk-0.1.1/.github/workflows/regenerate-sdk.yml +138 -0
- archastro_sdk-0.1.1/.github/workflows/release.yml +157 -0
- archastro_sdk-0.1.1/.gitignore +18 -0
- archastro_sdk-0.1.1/LICENSE +21 -0
- archastro_sdk-0.1.1/PKG-INFO +139 -0
- archastro_sdk-0.1.1/README.md +88 -0
- archastro_sdk-0.1.1/package-lock.json +2341 -0
- archastro_sdk-0.1.1/package.json +28 -0
- archastro_sdk-0.1.1/pyproject.toml +70 -0
- archastro_sdk-0.1.1/scripts/regenerate_sdk.sh +84 -0
- archastro_sdk-0.1.1/scripts/sdk-generator-config.json +8 -0
- archastro_sdk-0.1.1/specs/platform-openapi.json +31645 -0
- archastro_sdk-0.1.1/src/archastro/__init__.py +4 -0
- archastro_sdk-0.1.1/src/archastro/phx_channel/__init__.py +11 -0
- archastro_sdk-0.1.1/src/archastro/phx_channel/channel.py +290 -0
- archastro_sdk-0.1.1/src/archastro/phx_channel/harness.py +120 -0
- archastro_sdk-0.1.1/src/archastro/phx_channel/socket.py +280 -0
- archastro_sdk-0.1.1/src/archastro/phx_channel/tests/test_unit.py +457 -0
- archastro_sdk-0.1.1/src/archastro/platform/__init__.py +11 -0
- archastro_sdk-0.1.1/src/archastro/platform/auth.py +187 -0
- archastro_sdk-0.1.1/src/archastro/platform/channels/__init__.py +7 -0
- archastro_sdk-0.1.1/src/archastro/platform/channels/api_activity_feed_channel.py +71 -0
- archastro_sdk-0.1.1/src/archastro/platform/channels/api_chat_channel.py +573 -0
- archastro_sdk-0.1.1/src/archastro/platform/channels/api_object_channel.py +76 -0
- archastro_sdk-0.1.1/src/archastro/platform/client.py +135 -0
- archastro_sdk-0.1.1/src/archastro/platform/runtime/__init__.py +0 -0
- archastro_sdk-0.1.1/src/archastro/platform/runtime/http_client.py +210 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/__init__.py +16 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/ai.py +56 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/artifacts.py +31 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/automations.py +23 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/chat.py +77 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/common.py +1179 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/config.py +77 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/image.py +17 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/invites.py +19 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/notifications.py +37 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/teams.py +60 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/threads.py +64 -0
- archastro_sdk-0.1.1/src/archastro/platform/types/users.py +32 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/__init__.py +79 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/__init__.py +39 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/activity_feed.py +83 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_computers.py +39 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_env_vars.py +33 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_health_actions.py +22 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_installations.py +82 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_routine_runs.py +35 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_routines.py +176 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_sessions.py +89 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_skills.py +62 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agent_tools.py +63 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/agents.py +565 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/ai.py +149 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/artifacts.py +42 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/automation_runs.py +22 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/automations.py +27 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/bug_reports.py +25 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/config.py +427 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/custom_objects.py +116 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/files.py +17 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/installation_sources.py +15 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/invites.py +22 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/knowledge_documents.py +93 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/knowledge_sources.py +143 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/kv.py +63 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/notification_preferences.py +35 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/notifications.py +80 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/orgs.py +42 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/solution_categories.py +41 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/solution_tags.py +38 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/solutions.py +242 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/team_memberships.py +38 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/teams.py +719 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/thread_messages.py +86 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/threads.py +337 -0
- archastro_sdk-0.1.1/src/archastro/platform/v1/resources/users.py +405 -0
- archastro_sdk-0.1.1/tests/contract/channels/test_api_activity_feed_channel.py +145 -0
- archastro_sdk-0.1.1/tests/contract/channels/test_api_chat_channel.py +894 -0
- archastro_sdk-0.1.1/tests/contract/channels/test_api_object_channel.py +191 -0
- archastro_sdk-0.1.1/tests/contract/conftest.py +162 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_activity_feed.py +55 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_computers.py +141 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_env_vars.py +114 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_health_actions.py +87 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_installations.py +273 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_routine_runs.py +60 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_routines.py +324 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_sessions.py +238 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_skills.py +229 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agent_tools.py +222 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_agents.py +766 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_ai.py +174 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_artifacts.py +183 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_automation_runs.py +53 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_automations.py +60 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_bug_reports.py +86 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_config.py +627 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_custom_objects.py +147 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_files.py +40 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_installation_sources.py +53 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_invites.py +74 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_knowledge_documents.py +136 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_knowledge_sources.py +224 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_kv.py +203 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_notification_preferences.py +108 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_notifications.py +217 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_orgs.py +48 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_solution_categories.py +46 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_solution_tags.py +46 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_solutions.py +278 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_team_memberships.py +66 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_teams.py +592 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_thread_messages.py +196 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_threads.py +505 -0
- archastro_sdk-0.1.1/tests/contract/v1/test_users.py +233 -0
- archastro_sdk-0.1.1/tests/harness/conftest.py +137 -0
- archastro_sdk-0.1.1/tests/harness/fixtures/channel-harness-spec.json +131 -0
- archastro_sdk-0.1.1/tests/harness/test_harness.py +152 -0
- archastro_sdk-0.1.1/tests/test_http_client.py +255 -0
- archastro_sdk-0.1.1/uv.lock +455 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint-and-test:
|
|
18
|
+
name: Lint + Test (Python ${{ matrix.python }})
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python: ["3.11", "3.12", "3.13"]
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
26
|
+
|
|
27
|
+
- name: Setup Node (for @archastro/channel-harness + prism)
|
|
28
|
+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
29
|
+
with:
|
|
30
|
+
node-version: 20
|
|
31
|
+
cache: npm
|
|
32
|
+
|
|
33
|
+
- run: npm ci --ignore-scripts
|
|
34
|
+
|
|
35
|
+
- name: Audit JS tooling dependencies
|
|
36
|
+
run: npm audit --audit-level=moderate
|
|
37
|
+
|
|
38
|
+
- name: Setup Python ${{ matrix.python }}
|
|
39
|
+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
|
40
|
+
with:
|
|
41
|
+
python-version: ${{ matrix.python }}
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
|
45
|
+
with:
|
|
46
|
+
version: "0.11.3"
|
|
47
|
+
checksum: c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce
|
|
48
|
+
enable-cache: true
|
|
49
|
+
|
|
50
|
+
- name: Install project + dev deps
|
|
51
|
+
run: uv sync --locked --all-extras
|
|
52
|
+
|
|
53
|
+
- name: Ruff lint
|
|
54
|
+
run: uv run ruff check
|
|
55
|
+
|
|
56
|
+
- name: Ruff format check
|
|
57
|
+
run: uv run ruff format --check
|
|
58
|
+
|
|
59
|
+
- name: Unit tests
|
|
60
|
+
run: uv run pytest tests/test_http_client.py src/archastro/phx_channel/tests/test_unit.py
|
|
61
|
+
|
|
62
|
+
- name: Harness-client integration tests
|
|
63
|
+
run: uv run pytest tests/harness
|
|
64
|
+
|
|
65
|
+
- name: Contract tests (REST + channels over real harness subprocess)
|
|
66
|
+
run: uv run pytest tests/contract
|
|
67
|
+
env:
|
|
68
|
+
ARCHASTRO_RUN_CHANNEL_CONTRACT_TESTS: "1"
|
|
69
|
+
|
|
70
|
+
- name: Build and import wheel
|
|
71
|
+
run: |
|
|
72
|
+
rm -rf dist
|
|
73
|
+
uv build --no-build-isolation
|
|
74
|
+
uv export --locked --no-dev --no-emit-project --format requirements.txt --output-file dist/runtime-requirements.txt >/dev/null
|
|
75
|
+
uv venv --clear .wheel-smoke
|
|
76
|
+
uv pip install --python .wheel-smoke --require-hashes -r dist/runtime-requirements.txt
|
|
77
|
+
uv pip install --python .wheel-smoke --no-deps dist/*.whl
|
|
78
|
+
.wheel-smoke/bin/python -c "import archastro.platform; import archastro.phx_channel"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Triggered by the tags that release.yml pushes (v*), and reachable via
|
|
4
|
+
# workflow_dispatch so release.yml can re-trigger after pushing the tag —
|
|
5
|
+
# tags pushed by GITHUB_TOKEN don't themselves trigger workflows.
|
|
6
|
+
#
|
|
7
|
+
# Authenticates to PyPI via OIDC (Trusted Publishing) — no PYPI_API_TOKEN.
|
|
8
|
+
#
|
|
9
|
+
# Prereqs (one-time):
|
|
10
|
+
# 1. First publish done manually from a laptop (uv publish or twine upload)
|
|
11
|
+
# 2. Trusted publisher configured at pypi.org/manage/project/archastro-sdk/
|
|
12
|
+
# pointing at this workflow filename (publish.yml) and the `pypi`
|
|
13
|
+
# environment used below.
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
push:
|
|
17
|
+
tags:
|
|
18
|
+
- 'v*'
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write # create GitHub Release
|
|
23
|
+
id-token: write # OIDC token for PyPI Trusted Publishing
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
publish:
|
|
27
|
+
name: PyPI publish
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment:
|
|
30
|
+
name: pypi
|
|
31
|
+
url: https://pypi.org/p/archastro-sdk
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
34
|
+
|
|
35
|
+
- name: Setup Python
|
|
36
|
+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
|
37
|
+
with:
|
|
38
|
+
python-version: '3.12'
|
|
39
|
+
|
|
40
|
+
- name: Install uv
|
|
41
|
+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
|
42
|
+
with:
|
|
43
|
+
version: "0.11.3"
|
|
44
|
+
checksum: c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce
|
|
45
|
+
enable-cache: true
|
|
46
|
+
|
|
47
|
+
- name: Install project + dev deps
|
|
48
|
+
run: uv sync --locked --all-extras
|
|
49
|
+
|
|
50
|
+
- name: Resolve version from tag
|
|
51
|
+
id: pkg
|
|
52
|
+
env:
|
|
53
|
+
TAG: ${{ github.ref_name }}
|
|
54
|
+
run: |
|
|
55
|
+
set -euo pipefail
|
|
56
|
+
case "$TAG" in
|
|
57
|
+
v*)
|
|
58
|
+
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
|
|
59
|
+
;;
|
|
60
|
+
*)
|
|
61
|
+
echo "Unrecognized tag: $TAG (expected v*)" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
- name: Verify pyproject.toml version matches tag
|
|
67
|
+
env:
|
|
68
|
+
EXPECTED: ${{ steps.pkg.outputs.version }}
|
|
69
|
+
run: |
|
|
70
|
+
set -euo pipefail
|
|
71
|
+
actual=$(uv run python -c "import tomllib, sys; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
72
|
+
if [ "$actual" != "$EXPECTED" ]; then
|
|
73
|
+
echo "Version mismatch: tag says $EXPECTED, pyproject.toml says $actual" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
- name: Build sdist + wheel
|
|
78
|
+
run: |
|
|
79
|
+
rm -rf dist
|
|
80
|
+
uv build --no-build-isolation
|
|
81
|
+
|
|
82
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # release/v1
|
|
84
|
+
# Defaults: reads dist/, uses OIDC when id-token: write is set + a
|
|
85
|
+
# trusted publisher is configured for this repo + workflow + env.
|
|
86
|
+
|
|
87
|
+
- name: Create GitHub Release
|
|
88
|
+
env:
|
|
89
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
90
|
+
TAG: ${{ github.ref_name }}
|
|
91
|
+
VERSION: ${{ steps.pkg.outputs.version }}
|
|
92
|
+
run: |
|
|
93
|
+
set -euo pipefail
|
|
94
|
+
gh release create "$TAG" \
|
|
95
|
+
--title "archastro-sdk v$VERSION" \
|
|
96
|
+
--notes "Published to PyPI: https://pypi.org/project/archastro-sdk/$VERSION/" \
|
|
97
|
+
--generate-notes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
name: Regenerate SDK
|
|
2
|
+
|
|
3
|
+
# Manually trigger from the Actions tab to pull the latest spec from
|
|
4
|
+
# archastro-openapi and re-run @archastro/sdk-generator. If anything
|
|
5
|
+
# changes, the workflow force-pushes to a stable branch and opens (or
|
|
6
|
+
# updates) a PR to main for review.
|
|
7
|
+
#
|
|
8
|
+
# No build/test step here — the PR's own CI runs them.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
openapi_ref:
|
|
14
|
+
description: archastro-openapi git ref to pull spec from
|
|
15
|
+
required: false
|
|
16
|
+
default: main
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
pull-requests: write
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
AUTO_BRANCH: auto/regenerate-sdk
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
regenerate:
|
|
26
|
+
name: Regenerate + open PR
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 0
|
|
32
|
+
|
|
33
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
34
|
+
with:
|
|
35
|
+
node-version: 20
|
|
36
|
+
cache: npm
|
|
37
|
+
|
|
38
|
+
- name: Install latest SDK tooling
|
|
39
|
+
run: |
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
npm install --ignore-scripts --save-dev \
|
|
42
|
+
@archastro/sdk-generator@latest \
|
|
43
|
+
@archastro/channel-harness@latest
|
|
44
|
+
npm pkg set \
|
|
45
|
+
devDependencies.@archastro/sdk-generator=latest \
|
|
46
|
+
devDependencies.@archastro/channel-harness=latest
|
|
47
|
+
npm install --package-lock-only --ignore-scripts
|
|
48
|
+
|
|
49
|
+
- name: Audit JS tooling dependencies
|
|
50
|
+
run: npm audit --audit-level=moderate
|
|
51
|
+
|
|
52
|
+
- name: Setup Python
|
|
53
|
+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
|
54
|
+
with:
|
|
55
|
+
python-version: '3.12'
|
|
56
|
+
|
|
57
|
+
- name: Install uv
|
|
58
|
+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
|
59
|
+
with:
|
|
60
|
+
version: "0.11.3"
|
|
61
|
+
checksum: c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce
|
|
62
|
+
enable-cache: true
|
|
63
|
+
|
|
64
|
+
- name: Install project + dev deps
|
|
65
|
+
# Required because the regen script invokes `uv run ruff ...` to
|
|
66
|
+
# normalize formatting on the generated tree.
|
|
67
|
+
run: uv sync --locked --all-extras
|
|
68
|
+
|
|
69
|
+
- name: Regenerate SDK
|
|
70
|
+
env:
|
|
71
|
+
ARCHASTRO_OPENAPI_REF: ${{ inputs.openapi_ref }}
|
|
72
|
+
run: ./scripts/regenerate_sdk.sh
|
|
73
|
+
|
|
74
|
+
- name: Detect changes
|
|
75
|
+
id: diff
|
|
76
|
+
run: |
|
|
77
|
+
if [ -z "$(git status --porcelain package.json package-lock.json src tests specs)" ]; then
|
|
78
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
79
|
+
echo "No changes from regeneration; nothing to do."
|
|
80
|
+
else
|
|
81
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
82
|
+
git status --short package.json package-lock.json src tests specs
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
- name: Resolve generator version
|
|
86
|
+
if: steps.diff.outputs.changed == 'true'
|
|
87
|
+
id: gen
|
|
88
|
+
run: |
|
|
89
|
+
set -euo pipefail
|
|
90
|
+
resolved=$(node -p "require('./node_modules/@archastro/sdk-generator/package.json').version")
|
|
91
|
+
echo "version=$resolved" >> "$GITHUB_OUTPUT"
|
|
92
|
+
|
|
93
|
+
- name: Commit + force-push to ${{ env.AUTO_BRANCH }}
|
|
94
|
+
if: steps.diff.outputs.changed == 'true'
|
|
95
|
+
env:
|
|
96
|
+
REF: ${{ inputs.openapi_ref }}
|
|
97
|
+
GEN_VERSION: ${{ steps.gen.outputs.version }}
|
|
98
|
+
run: |
|
|
99
|
+
set -euo pipefail
|
|
100
|
+
git config user.name "github-actions[bot]"
|
|
101
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
102
|
+
git checkout -B "$AUTO_BRANCH"
|
|
103
|
+
git add package.json package-lock.json src tests specs
|
|
104
|
+
git commit -m "chore(sdk): regenerate from archastro-openapi@$REF (generator $GEN_VERSION)"
|
|
105
|
+
git push --force-with-lease origin "$AUTO_BRANCH"
|
|
106
|
+
|
|
107
|
+
- name: Open or update PR
|
|
108
|
+
if: steps.diff.outputs.changed == 'true'
|
|
109
|
+
env:
|
|
110
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
111
|
+
REF: ${{ inputs.openapi_ref }}
|
|
112
|
+
GEN_VERSION: ${{ steps.gen.outputs.version }}
|
|
113
|
+
run: |
|
|
114
|
+
set -euo pipefail
|
|
115
|
+
file_count=$(git diff --name-only HEAD~1 HEAD -- package.json package-lock.json src tests specs | wc -l | tr -d ' ')
|
|
116
|
+
body=$(cat <<EOF
|
|
117
|
+
Automated SDK regeneration.
|
|
118
|
+
|
|
119
|
+
- **archastro-openapi ref**: \`$REF\`
|
|
120
|
+
- **generator version**: \`$GEN_VERSION\` resolved from npm latest and locked in package-lock.json
|
|
121
|
+
- **changed files**: $file_count
|
|
122
|
+
|
|
123
|
+
Review the diff carefully — generator output changes can be subtle. The PR's CI will run lint + tests.
|
|
124
|
+
EOF
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
existing=$(gh pr list --head "$AUTO_BRANCH" --state open --json number --jq '.[0].number // empty')
|
|
128
|
+
if [ -n "$existing" ]; then
|
|
129
|
+
echo "Updating PR #$existing"
|
|
130
|
+
gh pr edit "$existing" --body "$body"
|
|
131
|
+
else
|
|
132
|
+
echo "Opening new PR"
|
|
133
|
+
gh pr create \
|
|
134
|
+
--base main \
|
|
135
|
+
--head "$AUTO_BRANCH" \
|
|
136
|
+
--title "chore(sdk): regenerate from openapi@$REF" \
|
|
137
|
+
--body "$body"
|
|
138
|
+
fi
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Manually trigger from the Actions tab to:
|
|
4
|
+
# 1. Bump pyproject.toml + uv.lock versions on a release branch
|
|
5
|
+
# 2. Push a tag (vX.Y.Z) which triggers publish.yml
|
|
6
|
+
# 3. Open a PR back to main and merge it so main tracks the bump
|
|
7
|
+
#
|
|
8
|
+
# Publishing uses PyPI Trusted Publishing (OIDC) — no PYPI_API_TOKEN.
|
|
9
|
+
# A package's first publish must be done manually; after that, configure a
|
|
10
|
+
# trusted publisher on pypi.org pointing at publish.yml.
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
inputs:
|
|
15
|
+
bump:
|
|
16
|
+
description: Version bump
|
|
17
|
+
required: true
|
|
18
|
+
type: choice
|
|
19
|
+
options:
|
|
20
|
+
- patch
|
|
21
|
+
- minor
|
|
22
|
+
- major
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: write
|
|
26
|
+
pull-requests: write
|
|
27
|
+
actions: write # to dispatch publish.yml via `gh workflow run`
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
release:
|
|
31
|
+
name: Bump + branch + tag
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0
|
|
37
|
+
|
|
38
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
39
|
+
with:
|
|
40
|
+
node-version: 20
|
|
41
|
+
cache: npm
|
|
42
|
+
|
|
43
|
+
- run: npm ci --ignore-scripts
|
|
44
|
+
|
|
45
|
+
- name: Audit JS tooling dependencies
|
|
46
|
+
run: npm audit --audit-level=moderate
|
|
47
|
+
|
|
48
|
+
- name: Setup Python
|
|
49
|
+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
|
50
|
+
with:
|
|
51
|
+
python-version: '3.12'
|
|
52
|
+
|
|
53
|
+
- name: Install uv
|
|
54
|
+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
|
55
|
+
with:
|
|
56
|
+
version: "0.11.3"
|
|
57
|
+
checksum: c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce
|
|
58
|
+
enable-cache: true
|
|
59
|
+
|
|
60
|
+
- name: Install project + dev deps
|
|
61
|
+
run: uv sync --locked --all-extras
|
|
62
|
+
|
|
63
|
+
- name: Test (sanity gate before bumping)
|
|
64
|
+
# Core CI gates on Python 3.12 only — keeps the release gate honest
|
|
65
|
+
# without spending time on the full ci.yml matrix.
|
|
66
|
+
run: |
|
|
67
|
+
uv run ruff check
|
|
68
|
+
uv run ruff format --check
|
|
69
|
+
uv run pytest tests/test_http_client.py src/archastro/phx_channel/tests/test_unit.py
|
|
70
|
+
uv run pytest tests/harness
|
|
71
|
+
uv run pytest tests/contract
|
|
72
|
+
rm -rf dist
|
|
73
|
+
uv build --no-build-isolation
|
|
74
|
+
uv export --locked --no-dev --no-emit-project --format requirements.txt --output-file dist/runtime-requirements.txt >/dev/null
|
|
75
|
+
uv venv --clear .wheel-smoke
|
|
76
|
+
uv pip install --python .wheel-smoke --require-hashes -r dist/runtime-requirements.txt
|
|
77
|
+
uv pip install --python .wheel-smoke --no-deps dist/*.whl
|
|
78
|
+
.wheel-smoke/bin/python -c "import archastro.platform; import archastro.phx_channel"
|
|
79
|
+
env:
|
|
80
|
+
ARCHASTRO_RUN_CHANNEL_CONTRACT_TESTS: "1"
|
|
81
|
+
|
|
82
|
+
- name: Configure git identity
|
|
83
|
+
run: |
|
|
84
|
+
git config user.name "github-actions[bot]"
|
|
85
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
86
|
+
|
|
87
|
+
- name: Bump version
|
|
88
|
+
id: bump
|
|
89
|
+
env:
|
|
90
|
+
BUMP: ${{ inputs.bump }}
|
|
91
|
+
run: |
|
|
92
|
+
set -euo pipefail
|
|
93
|
+
# `uv version --bump` updates pyproject.toml + uv.lock atomically.
|
|
94
|
+
uv version --bump "$BUMP"
|
|
95
|
+
version=$(uv run python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
96
|
+
tag="v$version"
|
|
97
|
+
branch="release/$tag"
|
|
98
|
+
{
|
|
99
|
+
echo "version=$version"
|
|
100
|
+
echo "tag=$tag"
|
|
101
|
+
echo "branch=$branch"
|
|
102
|
+
} >> "$GITHUB_OUTPUT"
|
|
103
|
+
|
|
104
|
+
- name: Commit + push release branch
|
|
105
|
+
env:
|
|
106
|
+
BRANCH: ${{ steps.bump.outputs.branch }}
|
|
107
|
+
VERSION: ${{ steps.bump.outputs.version }}
|
|
108
|
+
run: |
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
git checkout -b "$BRANCH"
|
|
111
|
+
git add pyproject.toml uv.lock
|
|
112
|
+
git commit -m "release: archastro-sdk v$VERSION"
|
|
113
|
+
git push origin "$BRANCH"
|
|
114
|
+
|
|
115
|
+
- name: Create + push tag
|
|
116
|
+
env:
|
|
117
|
+
TAG: ${{ steps.bump.outputs.tag }}
|
|
118
|
+
run: |
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
git tag -a "$TAG" -m "$TAG"
|
|
121
|
+
git push origin "$TAG"
|
|
122
|
+
|
|
123
|
+
- name: Dispatch publish.yml for the tag
|
|
124
|
+
env:
|
|
125
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
126
|
+
TAG: ${{ steps.bump.outputs.tag }}
|
|
127
|
+
run: |
|
|
128
|
+
set -euo pipefail
|
|
129
|
+
echo "Dispatching publish.yml --ref $TAG"
|
|
130
|
+
gh workflow run publish.yml --ref "$TAG"
|
|
131
|
+
|
|
132
|
+
- name: Open PR + merge
|
|
133
|
+
env:
|
|
134
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
135
|
+
BRANCH: ${{ steps.bump.outputs.branch }}
|
|
136
|
+
VERSION: ${{ steps.bump.outputs.version }}
|
|
137
|
+
TAG: ${{ steps.bump.outputs.tag }}
|
|
138
|
+
run: |
|
|
139
|
+
set -euo pipefail
|
|
140
|
+
body=$(cat <<EOF
|
|
141
|
+
Automated release — version bump opened by \`release.yml\`.
|
|
142
|
+
|
|
143
|
+
**Tag pushed** (publish.yml is publishing right now):
|
|
144
|
+
- \`$TAG\`
|
|
145
|
+
|
|
146
|
+
This PR merges immediately so \`main\` stays in sync with what
|
|
147
|
+
was published. Tests already ran above; the bump is the only
|
|
148
|
+
change in this PR.
|
|
149
|
+
EOF
|
|
150
|
+
)
|
|
151
|
+
pr_url=$(gh pr create \
|
|
152
|
+
--base main \
|
|
153
|
+
--head "$BRANCH" \
|
|
154
|
+
--title "release: archastro-sdk v$VERSION" \
|
|
155
|
+
--body "$body")
|
|
156
|
+
gh pr merge "$pr_url" --squash --delete-branch
|
|
157
|
+
echo "Merged $pr_url"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.venv/
|
|
11
|
+
|
|
12
|
+
# Node tooling (for @archastro/channel-harness + prism)
|
|
13
|
+
node_modules/
|
|
14
|
+
|
|
15
|
+
# OS / editor
|
|
16
|
+
.DS_Store
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ArchAstro Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: archastro-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python SDK for the ArchAstro Platform API
|
|
5
|
+
Project-URL: Homepage, https://github.com/ArchAstro/archastro-python
|
|
6
|
+
Project-URL: Repository, https://github.com/ArchAstro/archastro-python
|
|
7
|
+
Project-URL: Issues, https://github.com/ArchAstro/archastro-python/issues
|
|
8
|
+
Author-email: ArchAstro <support@archastro.ai>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 ArchAstro Inc.
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: api,archastro,phoenix-channels,sdk
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Operating System :: OS Independent
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
40
|
+
Classifier: Typing :: Typed
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Requires-Dist: httpx>=0.27
|
|
43
|
+
Requires-Dist: pydantic>=2.0
|
|
44
|
+
Requires-Dist: websockets>=13.0
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: hatchling>=1.27; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff>=0.11; extra == 'dev'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# archastro-python
|
|
53
|
+
|
|
54
|
+
Python SDK for the ArchAstro Platform API.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv add archastro-sdk # or: pip install archastro-sdk
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from archastro import ArchAstro
|
|
62
|
+
|
|
63
|
+
client = ArchAstro(api_key="pk_...")
|
|
64
|
+
teams = client.v1.teams.list()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Packages
|
|
68
|
+
|
|
69
|
+
All public code lives under the single top-level `archastro` package:
|
|
70
|
+
|
|
71
|
+
- **`archastro.platform`** — typed REST + channel SDK generated from
|
|
72
|
+
the canonical OpenAPI spec at
|
|
73
|
+
[`ArchAstro/archastro-openapi`](https://github.com/ArchAstro/archastro-openapi).
|
|
74
|
+
Pydantic models, async channel classes, auth helpers.
|
|
75
|
+
- **`archastro.phx_channel`** — the hand-written Phoenix Channels
|
|
76
|
+
client the generated channel classes run on top of. WebSocket
|
|
77
|
+
transport, join / reply / push / leave, heartbeat, reconnect, and a
|
|
78
|
+
`HarnessServiceClient` for driving the
|
|
79
|
+
[`@archastro/channel-harness`](https://www.npmjs.com/package/@archastro/channel-harness)
|
|
80
|
+
service from Python tests.
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
This repo contains:
|
|
85
|
+
|
|
86
|
+
- Python SDK (`src/archastro/`) installed via `uv`
|
|
87
|
+
- JS tooling (`package.json`) — the channel-harness subprocess that
|
|
88
|
+
powers the channel contract tests, plus the Prism mock server that
|
|
89
|
+
backs the REST contract tests. Installed via `npm ci`.
|
|
90
|
+
|
|
91
|
+
### Setup
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm ci --ignore-scripts # channel-harness + prism (for contract tests)
|
|
95
|
+
uv sync --locked --all-extras
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Running tests
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Unit tests only (no external services needed)
|
|
102
|
+
uv run pytest tests/test_http_client.py src/archastro/phx_channel/tests/test_unit.py
|
|
103
|
+
|
|
104
|
+
# REST contract tests (spawns Prism mock server)
|
|
105
|
+
uv run pytest tests/contract
|
|
106
|
+
|
|
107
|
+
# REST + channel contract tests (also spawns channel-harness subprocess)
|
|
108
|
+
ARCHASTRO_RUN_CHANNEL_CONTRACT_TESTS=1 uv run pytest tests/contract
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Regenerating the SDK
|
|
112
|
+
|
|
113
|
+
The typed SDK — `src/archastro/platform/` and `tests/contract/` — is
|
|
114
|
+
regenerated from the canonical OpenAPI spec by
|
|
115
|
+
[`@archastro/sdk-generator`](https://www.npmjs.com/package/@archastro/sdk-generator).
|
|
116
|
+
Don't hand-edit files with the `auto-generated by @archastro/sdk-generator`
|
|
117
|
+
header; they'll be overwritten.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
./scripts/regenerate_sdk.sh
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The script fetches the spec from `ArchAstro/archastro-openapi@main` and
|
|
124
|
+
runs the generator locked in `package-lock.json`. Knobs:
|
|
125
|
+
|
|
126
|
+
- `ARCHASTRO_OPENAPI_REF=some-branch ./scripts/regenerate_sdk.sh` — pull
|
|
127
|
+
the spec from a non-default ref (useful when a spec change is on a
|
|
128
|
+
branch awaiting merge).
|
|
129
|
+
|
|
130
|
+
After regenerating, review the diff, run the full test suite, and commit.
|
|
131
|
+
|
|
132
|
+
## Release
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# bump version in pyproject.toml, then:
|
|
136
|
+
uv sync --locked --all-extras
|
|
137
|
+
uv build --no-build-isolation
|
|
138
|
+
uv publish
|
|
139
|
+
```
|