intuned-runtime 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/__init__.py +45 -0
- cli/commands/__init__.py +25 -0
- cli/commands/ai_source/__init__.py +4 -0
- cli/commands/ai_source/ai_source.py +10 -0
- cli/commands/ai_source/deploy.py +64 -0
- cli/commands/browser/__init__.py +3 -0
- cli/commands/browser/save_state.py +32 -0
- cli/commands/init.py +127 -0
- cli/commands/project/__init__.py +20 -0
- cli/commands/project/auth_session/__init__.py +5 -0
- cli/commands/project/auth_session/check.py +118 -0
- cli/commands/project/auth_session/create.py +96 -0
- cli/commands/project/auth_session/load.py +39 -0
- cli/commands/project/project.py +10 -0
- cli/commands/project/run.py +340 -0
- cli/commands/project/run_interface.py +265 -0
- cli/commands/project/type_check.py +86 -0
- cli/commands/project/upgrade.py +92 -0
- cli/commands/publish_packages.py +264 -0
- cli/logger.py +19 -0
- cli/utils/ai_source_project.py +31 -0
- cli/utils/code_tree.py +83 -0
- cli/utils/run_apis.py +147 -0
- cli/utils/unix_socket.py +55 -0
- intuned_runtime-1.0.0.dist-info/LICENSE +42 -0
- intuned_runtime-1.0.0.dist-info/METADATA +113 -0
- intuned_runtime-1.0.0.dist-info/RECORD +58 -0
- intuned_runtime-1.0.0.dist-info/WHEEL +4 -0
- intuned_runtime-1.0.0.dist-info/entry_points.txt +3 -0
- runtime/__init__.py +3 -0
- runtime/backend_functions/__init__.py +5 -0
- runtime/backend_functions/_call_backend_function.py +86 -0
- runtime/backend_functions/get_auth_session_parameters.py +30 -0
- runtime/browser/__init__.py +3 -0
- runtime/browser/launch_chromium.py +212 -0
- runtime/browser/storage_state.py +106 -0
- runtime/context/__init__.py +5 -0
- runtime/context/context.py +51 -0
- runtime/env.py +13 -0
- runtime/errors/__init__.py +21 -0
- runtime/errors/auth_session_errors.py +9 -0
- runtime/errors/run_api_errors.py +120 -0
- runtime/errors/trace_errors.py +3 -0
- runtime/helpers/__init__.py +5 -0
- runtime/helpers/extend_payload.py +9 -0
- runtime/helpers/extend_timeout.py +13 -0
- runtime/helpers/get_auth_session_parameters.py +14 -0
- runtime/py.typed +0 -0
- runtime/run/__init__.py +3 -0
- runtime/run/intuned_settings.py +38 -0
- runtime/run/playwright_constructs.py +19 -0
- runtime/run/run_api.py +233 -0
- runtime/run/traces.py +36 -0
- runtime/types/__init__.py +15 -0
- runtime/types/payload.py +7 -0
- runtime/types/run_types.py +177 -0
- runtime_helpers/__init__.py +5 -0
- runtime_helpers/py.typed +0 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
Acceptance
|
2
|
+
By using the software, you agree to all of the terms and conditions below.
|
3
|
+
|
4
|
+
Copyright License
|
5
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below.
|
6
|
+
|
7
|
+
Limitations
|
8
|
+
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
|
9
|
+
|
10
|
+
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
|
11
|
+
|
12
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
13
|
+
|
14
|
+
Patents
|
15
|
+
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
16
|
+
|
17
|
+
Notices
|
18
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
19
|
+
|
20
|
+
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
|
21
|
+
|
22
|
+
No Other Rights
|
23
|
+
These terms do not imply any licenses other than those expressly granted in these terms.
|
24
|
+
|
25
|
+
Termination
|
26
|
+
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
|
27
|
+
|
28
|
+
No Liability
|
29
|
+
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
|
30
|
+
|
31
|
+
Definitions
|
32
|
+
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
|
33
|
+
|
34
|
+
you refers to the individual or entity agreeing to these terms.
|
35
|
+
|
36
|
+
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
37
|
+
|
38
|
+
your licenses are all the licenses granted to you for the software under these terms.
|
39
|
+
|
40
|
+
use means anything you do with the software requiring one of your licenses.
|
41
|
+
|
42
|
+
trademark means trademarks, service marks, and similar rights.
|
@@ -0,0 +1,113 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: intuned-runtime
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Runtime commands for Intuned platform Python scrapers
|
5
|
+
License: Elastic-2.0
|
6
|
+
Keywords: runtime,intuned
|
7
|
+
Author: Intuned Developers
|
8
|
+
Author-email: engineering@intunedhq.com
|
9
|
+
Requires-Python: >=3.9,<4.0
|
10
|
+
Classifier: License :: Other/Proprietary License
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
18
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
19
|
+
Requires-Dist: arguably (>=1.3.0,<2.0.0)
|
20
|
+
Requires-Dist: gitpython (>=3.1.43,<4.0.0)
|
21
|
+
Requires-Dist: httpx (>=0.23.0,<1)
|
22
|
+
Requires-Dist: more-termcolor (>=1.1.3,<2.0.0)
|
23
|
+
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
24
|
+
Requires-Dist: pydantic (>=2.10.6,<3.0.0)
|
25
|
+
Requires-Dist: pyright (>=1.1.387,<2.0.0)
|
26
|
+
Requires-Dist: python-dotenv (==1.0.1)
|
27
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
28
|
+
Requires-Dist: ruff (>=0.7.2,<0.8.0)
|
29
|
+
Requires-Dist: semver (>=3.0.4,<4.0.0)
|
30
|
+
Requires-Dist: tenacity (>=8.5.0,<9.0.0)
|
31
|
+
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
32
|
+
Requires-Dist: waitress (>=3.0.1,<4.0.0)
|
33
|
+
Description-Content-Type: text/markdown
|
34
|
+
|
35
|
+
# Intuned Python Runtime
|
36
|
+
|
37
|
+
Runtime commands for Intuned platform Python automations.
|
38
|
+
|
39
|
+
## Dependencies
|
40
|
+
|
41
|
+
- Requires Python 3.12 or higher.
|
42
|
+
- Install poetry: `pip install poetry`
|
43
|
+
- Install dependencies: `poetry install`
|
44
|
+
- Activate virtual environment: `poetry shell`
|
45
|
+
- Now you have access to `intuned` cli from within project.
|
46
|
+
|
47
|
+
## Install globally
|
48
|
+
|
49
|
+
- This project can be installed globally on the system to use `intuned` cli anywhere
|
50
|
+
- Make sure you are not in a virtual environment. `which python` should point to system python.
|
51
|
+
- If you are, `deactivate` to exit virtual environment. Or open in an external terminal (from outside vscode if it doesn't work)
|
52
|
+
- Run `pip install -e .` from the root of the project.
|
53
|
+
|
54
|
+
## Commands
|
55
|
+
|
56
|
+
All commands have `-h` flag to show help.
|
57
|
+
|
58
|
+
### `intuned init`
|
59
|
+
|
60
|
+
- Initializes a project. Creates `pyproject.toml`, `Intuned.json` and `README.md` files.
|
61
|
+
- Prompts for confirmation for each file that already exists.
|
62
|
+
- Options:
|
63
|
+
- `--yes/-y` flag to overwrite all files.
|
64
|
+
- `--no/-n` flag to not overwrite any files.
|
65
|
+
|
66
|
+
### `intuned publish-packages`
|
67
|
+
|
68
|
+
- Publishes packages to `python-packages` repository.
|
69
|
+
- Options:
|
70
|
+
- `--sdk` flag to publish SDK package. Creates `sdk-<version>` and `sdk-latest` tags for the published version.
|
71
|
+
- `--runtime` flag to publish runtime package. Creates `runtime-<version>` and `runtime-latest` tags for the published version.
|
72
|
+
- `--overwrite` flag to overwrite the existing version if it exists.
|
73
|
+
- `--show-diff` flag to show the diff of the package before publishing. You need to configure a diff tool to be used for `git difftool` command in your git config. [How to configure VS Code as a diff tool](https://www.roboleary.net/vscode/2020/09/15/vscode-git.html#tldr).
|
74
|
+
- `--no-latest` flag to not release `latest` tag for the published version.
|
75
|
+
- Uses the version specified in `pyproject.toml` of each package respectively.
|
76
|
+
- Uses WebApp directory specified in `WEBAPP_REPO` environment variable or tries to resolve it (only works if installed globally with `-e` flag).
|
77
|
+
- Uses `python-packages` directory to be sister to WebApp directory `<webapp path>/../python-packages`.
|
78
|
+
- These packages are used on deployed apps.
|
79
|
+
|
80
|
+
### `intuned project run`
|
81
|
+
|
82
|
+
- Runs the project.
|
83
|
+
- `--mode` to specify the mode to run. Default is `sample`.
|
84
|
+
- `--mode sample` extends a sample of payloads to run.
|
85
|
+
- `--mode full` runs all extended payloads.
|
86
|
+
- `--mode single` runs the initial API only.
|
87
|
+
- `--api-name <name>` to specify the initial API to run. Defaults to `default`
|
88
|
+
- `--params <params json>` to specify the parameters to the initial API.
|
89
|
+
- `--sample-config-str '{<api name>: <sample size>, ...}` to specify the sample config. Only used with `--mode sample`.
|
90
|
+
- `--no-headless` to disable headless mode.
|
91
|
+
|
92
|
+
### `intuned project deploy`
|
93
|
+
|
94
|
+
- Deploys a project and starts a default job.
|
95
|
+
- Options:
|
96
|
+
- `--workspace-info '{"environment_url": <>, "workspace_id": <>, "api_key": <>}'` to specify the workspace info.
|
97
|
+
- `--workspace-info-path` to specify the path to a JSON file containing workspace info.
|
98
|
+
- `-y/--yes` to skip confirmation.
|
99
|
+
- `--project-name` to specify the project name. Resolves the name if not provided.
|
100
|
+
- Resolves `.gitignore` from current/parent directories to decide what to deploy.
|
101
|
+
- Resolves `.env` from current/parent directories to get environment variables to deploy.
|
102
|
+
- Resolves project name from the current/parent directory name if not provided.
|
103
|
+
|
104
|
+
### `intuned project serve`
|
105
|
+
|
106
|
+
- Serves the project as an HTTP server.
|
107
|
+
- Options:
|
108
|
+
- `--env development/production` to specify the environment to run the server.
|
109
|
+
- Development runs using Flask's development server.
|
110
|
+
- Production runs using Waitress.
|
111
|
+
- `--debug` to run the development server in debug mode. Not supported in production.
|
112
|
+
- This is used on deployed apps.
|
113
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
cli/__init__.py,sha256=QvTM82L3VLVOFGpu_bziWupgwjsAgv07u6o_gUwO3z0,1314
|
2
|
+
cli/commands/__init__.py,sha256=QF-zy-iOZXY-9m-1O-JjWnugjulgJtsODtnkhvlNKq8,944
|
3
|
+
cli/commands/ai_source/__init__.py,sha256=lg7owgcK8owNn2a4VBUP9RKxzFsLruhtnnQV0F_z6pc,149
|
4
|
+
cli/commands/ai_source/ai_source.py,sha256=2woQtCmhxKvLfEz832eUoCT9gMsuSvEE6rMnHSYXC7w,138
|
5
|
+
cli/commands/ai_source/deploy.py,sha256=XVN_alilwIaoVniuSpE8RkAveuaB3MaUtsRjIcpQTXo,2440
|
6
|
+
cli/commands/browser/__init__.py,sha256=AuVbvh7aSBTFKYvewXZyPoIvfBTe2uHiPcnaAkzapas,95
|
7
|
+
cli/commands/browser/save_state.py,sha256=eHKfvBfeFR_U9VQbsjOnIZjWepyDjNn9NL3naDYWP2s,841
|
8
|
+
cli/commands/init.py,sha256=8rWBenWZfwNtLxOBqhEMbOATyQNEnmDUmrFJ1xBGyxI,4384
|
9
|
+
cli/commands/project/__init__.py,sha256=t97wvhSenerYRdbSeCKXqHASA6EWA3lc1hnRhF9baOE,734
|
10
|
+
cli/commands/project/auth_session/__init__.py,sha256=gt7mlaW6xmqAc_4-pfF_FiecsR51C6fqCaq_NFbcbwA,300
|
11
|
+
cli/commands/project/auth_session/check.py,sha256=AFILp7m34nAO_RD3IfRpuJm5Zh0wnCRtBXqIrerdbwo,4565
|
12
|
+
cli/commands/project/auth_session/create.py,sha256=r-eYu3uLUo2mzF836CbVgu4oBzcIIDekzzFwwekxmg0,3374
|
13
|
+
cli/commands/project/auth_session/load.py,sha256=UUvg9Vyj15xiR44XlJzojLoFm5esv-o4E3qA3JqnBsE,1201
|
14
|
+
cli/commands/project/project.py,sha256=_MSh6Xor2Cbh-ItifwgOPq_BP8UDuKB7S6w796FULKQ,137
|
15
|
+
cli/commands/project/run.py,sha256=FDYYkU24aURYbljyYLFo8wLF-nvb86EVr9gMEjAfeE0,12274
|
16
|
+
cli/commands/project/run_interface.py,sha256=4RyR8WZriIF7Va4z1wt-q6zZDQOI31n62Ho2dyimzUY,8717
|
17
|
+
cli/commands/project/type_check.py,sha256=3BKMqTzJ73_J-RYrjba7Yv2mM_VzHR0gnXQEg_Us7JA,3384
|
18
|
+
cli/commands/project/upgrade.py,sha256=XmkZLflM4O-mwvhwcswlZpazRotwi3xesLgE0Zz8fTI,3061
|
19
|
+
cli/commands/publish_packages.py,sha256=sijkaG7_s0I1EWgLekGy1qm8Aqi_gYY8poXbMX0B6Yw,10505
|
20
|
+
cli/logger.py,sha256=bZK3q-KUdGxk_qzDb6pn-n0LOhKJvi6a9p8oSwZtq3s,594
|
21
|
+
cli/utils/ai_source_project.py,sha256=xUCM6p3i1XN4bJbuQz8LCzeI4BwqAdSvCl_vwDAEi0k,831
|
22
|
+
cli/utils/code_tree.py,sha256=1wfxZoQ5kRCfqs2SEPAicbAIPTiD6P1LxSuwYu_eeaI,2790
|
23
|
+
cli/utils/run_apis.py,sha256=Zee4zkgt9R8XY1XCGzj2Nc4zJ3jlRz1xnO493wotuWw,4690
|
24
|
+
cli/utils/unix_socket.py,sha256=UISmkJMHrir5iBLUm6vxC3uzTGIFyOk_wa0C9LUw4Cc,1889
|
25
|
+
runtime/__init__.py,sha256=wgS7k3f10LBbjSYJdjKeKUptizNw6OrBgNx-ECT5Ox8,124
|
26
|
+
runtime/backend_functions/__init__.py,sha256=j2EaK4FK8bmdFtqc5FxtFwx1KhIn_7qKPChrrAhJI3s,119
|
27
|
+
runtime/backend_functions/_call_backend_function.py,sha256=sER0op3vE9EVEB5-ZNwpGraxrqkFh5ZgOIV2B5fgmg4,3028
|
28
|
+
runtime/backend_functions/get_auth_session_parameters.py,sha256=pOvB7XiWpphEuBpazdKALw9EWgBU1PeY3gkzBfVLpkc,869
|
29
|
+
runtime/browser/__init__.py,sha256=9Nqf-80l1F7KGjy2pYBC5S5SeEXJ4YJ4Dl71hi_GWdI,132
|
30
|
+
runtime/browser/launch_chromium.py,sha256=sML_XpjmlMf5QpuqMGeaS42D43pAXWKswm2q-3a2jAM,7459
|
31
|
+
runtime/browser/storage_state.py,sha256=AmiHMfWbFVr7XUEYXoaWWZH4pXGdPv4wTNPXunm19rA,3591
|
32
|
+
runtime/context/__init__.py,sha256=hg8ejm4bJy4tNkwmZ9lKgYJx6bU7OgOdBS684Uv5XGg,73
|
33
|
+
runtime/context/context.py,sha256=pl_0x77_d5CiAznz1qGSk6o9cW-msNvlCt-2eFoMKlA,1739
|
34
|
+
runtime/env.py,sha256=h4BJI-XVSZKgtTxjkzj2HsyN3DlY5Ml9GZqeH4CKDE8,238
|
35
|
+
runtime/errors/__init__.py,sha256=oqiBSvT_yFLQ3hG0AbCUA3WYFaxkTDVkDMSy59xvBCo,688
|
36
|
+
runtime/errors/auth_session_errors.py,sha256=6b4XTI8UCDHDPX4jEA8_HyrNUp4VZ1TrEA8DRh6Z3rM,228
|
37
|
+
runtime/errors/run_api_errors.py,sha256=etqS18ZxDzr30qUezG6D8gcH0Yc1XNtQ8GMkUZx3Rmw,3400
|
38
|
+
runtime/errors/trace_errors.py,sha256=Lzfo0sH3zGaWz1kn5DHcAXQMn3aR2y2bnauj6xP1LYE,110
|
39
|
+
runtime/helpers/__init__.py,sha256=jozYPKHgZJ7Na5U1Wjt83egzjPATMZ_OMInEI6swSbY,234
|
40
|
+
runtime/helpers/extend_payload.py,sha256=towZF08WTpTTDBL4AV1bUU3XpKAQHEB66kGUfTICDe0,246
|
41
|
+
runtime/helpers/extend_timeout.py,sha256=KjfSLEUrqoz7v00rhnPAKq2OmUzEzcv-eQ3M8c2U46s,348
|
42
|
+
runtime/helpers/get_auth_session_parameters.py,sha256=7bopGhJ7vjKAn_UxnHSAah-k2rVOPbq0zi9FQOOCFds,472
|
43
|
+
runtime/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
44
|
+
runtime/run/__init__.py,sha256=zxMYVb7hn147YTrhMLsrcX6-KTd71HLrYHstJOWeWXQ,52
|
45
|
+
runtime/run/intuned_settings.py,sha256=vy2-ktEzUfUp5Z90dp3l7jPKHNjgB-8GSMDgAY-rYaU,1074
|
46
|
+
runtime/run/playwright_constructs.py,sha256=kOa95d4m4DTcIDKq_QajBs-9FkPz5yZFcsafLtx5288,519
|
47
|
+
runtime/run/run_api.py,sha256=88u91YS5-m_aGwQnNfeN7Mszm1MRfOmciCpzuAXjdoI,8853
|
48
|
+
runtime/run/traces.py,sha256=fKzh11LqV47ujgq_9I2tdp-dgld566wffWaHwU_4gis,1123
|
49
|
+
runtime/types/__init__.py,sha256=IJkDfqsau8F8R_j8TO6j-JwW4ElQr6aU6LNaWRehg5U,401
|
50
|
+
runtime/types/payload.py,sha256=sty8HgDEn3nJbZrwEOMCXyuG7_ICGDwlBIIWSON5ABY,124
|
51
|
+
runtime/types/run_types.py,sha256=_ZoW0rv4VnjqVwVxJBWsdh0S5toppNL3AfL456rn5yM,4076
|
52
|
+
runtime_helpers/__init__.py,sha256=XBrEiE9yNC8Lgn8NgIkqNXbI6e4ap237E83Zj_nlhCQ,249
|
53
|
+
runtime_helpers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
+
intuned_runtime-1.0.0.dist-info/LICENSE,sha256=9LIjQdgyU_ptzNIfItNCR7VmEHqYnrY1f1XwOreKFI0,3714
|
55
|
+
intuned_runtime-1.0.0.dist-info/METADATA,sha256=i6-dbJpG78FttN_tw7HgA3qd_fE99cHW7bkASh2z16o,5134
|
56
|
+
intuned_runtime-1.0.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
57
|
+
intuned_runtime-1.0.0.dist-info/entry_points.txt,sha256=4VXmTtVfaolAgt2owC9QF1bQpzCVv6Qh_cqW6ZGpftw,35
|
58
|
+
intuned_runtime-1.0.0.dist-info/RECORD,,
|
runtime/__init__.py
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Any
|
3
|
+
from typing import Literal
|
4
|
+
|
5
|
+
from httpx import AsyncClient
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
from runtime.context.context import IntunedContext
|
9
|
+
from runtime.env import get_functions_domain
|
10
|
+
from runtime.env import get_project_id
|
11
|
+
from runtime.env import get_workspace_id
|
12
|
+
|
13
|
+
|
14
|
+
async def call_backend_function[T: BaseModel](
|
15
|
+
name: str,
|
16
|
+
validation_model: type[T],
|
17
|
+
*,
|
18
|
+
method: Literal["GET", "POST"] = "GET",
|
19
|
+
params: BaseModel | None = None,
|
20
|
+
) -> T:
|
21
|
+
"""
|
22
|
+
Get the auth session parameters from the IntunedContext.
|
23
|
+
"""
|
24
|
+
functions_domain, workspace_id, project_id = get_functions_domain(), get_workspace_id(), get_project_id()
|
25
|
+
|
26
|
+
if functions_domain is None or workspace_id is None or project_id is None:
|
27
|
+
# todo
|
28
|
+
raise Exception("No workspace ID or project ID found.")
|
29
|
+
|
30
|
+
context = IntunedContext.current()
|
31
|
+
if context.run_context is None:
|
32
|
+
# todo
|
33
|
+
raise Exception("No run context found.")
|
34
|
+
|
35
|
+
auth_session_id = context.run_context.auth_session_id
|
36
|
+
if auth_session_id is None:
|
37
|
+
# todo
|
38
|
+
raise Exception("No auth session ID found.")
|
39
|
+
|
40
|
+
async with AsyncClient() as client:
|
41
|
+
if context.functions_token:
|
42
|
+
client.headers["Authorization"] = f"Bearer {context.functions_token}"
|
43
|
+
if params:
|
44
|
+
client.headers["Content-Type"] = "application/json"
|
45
|
+
path = f"{functions_domain}/api/{workspace_id}/functions/{project_id}/{name}"
|
46
|
+
body = params.model_dump() if params else None
|
47
|
+
print("Calling backend function", method, path, context.functions_token, json.dumps(body, indent=2))
|
48
|
+
response = await client.request(
|
49
|
+
method,
|
50
|
+
path,
|
51
|
+
json=body,
|
52
|
+
)
|
53
|
+
try:
|
54
|
+
response_json = response.json()
|
55
|
+
except json.JSONDecodeError as e:
|
56
|
+
raise CallBackendException(
|
57
|
+
response.status_code,
|
58
|
+
f"Expected JSON response, but got: {response.text}",
|
59
|
+
) from e
|
60
|
+
if not isinstance(response_json, dict):
|
61
|
+
raise CallBackendException(
|
62
|
+
response.status_code,
|
63
|
+
f"Expected JSON object, but got: {response_json}",
|
64
|
+
)
|
65
|
+
if 200 <= response.status_code < 300:
|
66
|
+
return validation_model.model_validate(response_json)
|
67
|
+
raise CallBackendException(
|
68
|
+
response.status_code,
|
69
|
+
f"Calling backend function errored with status {response.status_code}: {response_json}",
|
70
|
+
)
|
71
|
+
|
72
|
+
|
73
|
+
class CallBackendException(Exception):
|
74
|
+
def __init__(self, status_code: int, body: str | dict[str, Any]):
|
75
|
+
message = "Unknown error"
|
76
|
+
if isinstance(body, str):
|
77
|
+
message = body
|
78
|
+
else:
|
79
|
+
body_message = body.get("message") or body.get("error")
|
80
|
+
if body_message:
|
81
|
+
message = str(body_message)
|
82
|
+
else:
|
83
|
+
message = json.dumps(body)
|
84
|
+
super().__init__(message)
|
85
|
+
self.status_code = status_code
|
86
|
+
self.body = body
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
from runtime.context.context import IntunedContext
|
6
|
+
|
7
|
+
from ._call_backend_function import call_backend_function
|
8
|
+
|
9
|
+
|
10
|
+
class AuthSessionParameters(BaseModel):
|
11
|
+
parameters: dict[str, Any]
|
12
|
+
|
13
|
+
|
14
|
+
async def get_auth_session_parameters() -> dict[str, Any]:
|
15
|
+
"""
|
16
|
+
Get the auth session parameters from backend.
|
17
|
+
"""
|
18
|
+
|
19
|
+
context = IntunedContext.current()
|
20
|
+
if context.run_context is None:
|
21
|
+
raise Exception("get_auth_session_parameters failed due to an internal error (context was not found).")
|
22
|
+
if context.run_context.auth_session_id is None:
|
23
|
+
raise Exception("Auth sessions are not enabled")
|
24
|
+
|
25
|
+
result = await call_backend_function(
|
26
|
+
name=f"auth-session/{context.run_context.auth_session_id}/parameters",
|
27
|
+
validation_model=AuthSessionParameters,
|
28
|
+
)
|
29
|
+
|
30
|
+
return result.parameters
|
@@ -0,0 +1,212 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import tempfile
|
4
|
+
from contextlib import asynccontextmanager
|
5
|
+
from os.path import join
|
6
|
+
from typing import Any
|
7
|
+
from typing import Optional
|
8
|
+
from typing import Literal
|
9
|
+
import logging
|
10
|
+
import aiofiles
|
11
|
+
from playwright.async_api import async_playwright
|
12
|
+
from playwright.async_api import Browser
|
13
|
+
from playwright.async_api import ProxySettings
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
def get_proxy_env() -> Optional[ProxySettings]:
|
19
|
+
server = os.getenv("PROXY_SERVER")
|
20
|
+
username = os.getenv("PROXY_USERNAME")
|
21
|
+
password = os.getenv("PROXY_PASSWORD")
|
22
|
+
if server is None or username is None or password is None:
|
23
|
+
return None
|
24
|
+
return {
|
25
|
+
"server": server,
|
26
|
+
"username": username,
|
27
|
+
"password": password,
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
chromium_launch_args_to_ignore = [
|
32
|
+
"--disable-field-trial-config",
|
33
|
+
"--disable-background-networking",
|
34
|
+
"--enable-features=NetworkService,NetworkServiceInProcess",
|
35
|
+
"--disable-background-timer-throttling",
|
36
|
+
"--disable-backgrounding-occluded-windows",
|
37
|
+
"--disable-back-forward-cache",
|
38
|
+
"--disable-breakpad",
|
39
|
+
"--disable-client-side-phishing-detection",
|
40
|
+
"--disable-component-extensions-with-background-pages",
|
41
|
+
"--disable-component-update",
|
42
|
+
"--no-default-browser-check",
|
43
|
+
"--disable-default-apps",
|
44
|
+
"--disable-dev-shm-usage",
|
45
|
+
"--disable-extensions",
|
46
|
+
"--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,TranslateUI",
|
47
|
+
"--allow-pre-commit-input",
|
48
|
+
"--disable-hang-monitor",
|
49
|
+
"--disable-ipc-flooding-protection",
|
50
|
+
"--disable-prompt-on-repost",
|
51
|
+
"--disable-renderer-backgrounding",
|
52
|
+
"--force-color-profile=srgb",
|
53
|
+
"--metrics-recording-only",
|
54
|
+
"--no-first-run",
|
55
|
+
"--enable-automation",
|
56
|
+
"--password-store=basic",
|
57
|
+
"--use-mock-keychain",
|
58
|
+
"--no-service-autorun",
|
59
|
+
"--export-tagged-pdf",
|
60
|
+
"--enable-use-zoom-for-dsf=false",
|
61
|
+
"--disable-popup-blocking",
|
62
|
+
]
|
63
|
+
|
64
|
+
|
65
|
+
async def create_user_dir_with_preferences():
|
66
|
+
# Create a temporary directory
|
67
|
+
playwright_temp_dir = tempfile.mkdtemp(prefix="pw-")
|
68
|
+
user_dir = join(playwright_temp_dir, "userdir")
|
69
|
+
default_dir = join(user_dir, "Default")
|
70
|
+
|
71
|
+
# Create the default directory recursively
|
72
|
+
os.makedirs(default_dir, exist_ok=True)
|
73
|
+
|
74
|
+
# Preferences data
|
75
|
+
preferences = {
|
76
|
+
"plugins": {
|
77
|
+
"always_open_pdf_externally": True,
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
# Write preferences to file
|
82
|
+
async with aiofiles.open(join(default_dir, "Preferences"), mode="w") as f:
|
83
|
+
await f.write(json.dumps(preferences))
|
84
|
+
|
85
|
+
return os.path.abspath(user_dir)
|
86
|
+
|
87
|
+
|
88
|
+
extra_args = [
|
89
|
+
"--no-first-run",
|
90
|
+
"--disable-sync",
|
91
|
+
"--disable-translate",
|
92
|
+
"--disable-features=TranslateUI",
|
93
|
+
"--disable-features=NetworkService",
|
94
|
+
"--lang=en",
|
95
|
+
"--disable-blink-features=AutomationControlled",
|
96
|
+
]
|
97
|
+
|
98
|
+
default_user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
|
99
|
+
|
100
|
+
|
101
|
+
@asynccontextmanager
|
102
|
+
async def launch_chromium(
|
103
|
+
headless: bool = True,
|
104
|
+
timeout: int = 10,
|
105
|
+
cdp_address: str | None = None,
|
106
|
+
**kwargs: Any,
|
107
|
+
):
|
108
|
+
async with async_playwright() as playwright:
|
109
|
+
if cdp_address is not None:
|
110
|
+
browser: Browser = await playwright.chromium.connect_over_cdp(cdp_address)
|
111
|
+
context = browser.contexts[0]
|
112
|
+
dir = None
|
113
|
+
else:
|
114
|
+
dir = await create_user_dir_with_preferences()
|
115
|
+
if kwargs.get("proxy") is None:
|
116
|
+
proxy_env = get_proxy_env()
|
117
|
+
else:
|
118
|
+
proxy_env = kwargs.get("proxy")
|
119
|
+
# Remove proxy from kwargs if it exists
|
120
|
+
kwargs.pop("proxy", None)
|
121
|
+
viewport = kwargs.get("viewport", {"width": 1280, "height": 800})
|
122
|
+
kwargs.pop("viewport", None)
|
123
|
+
|
124
|
+
if headless:
|
125
|
+
chromium_launch_args_to_ignore.append("--headless")
|
126
|
+
extra_args.append("--headless=new")
|
127
|
+
|
128
|
+
context = await playwright.chromium.launch_persistent_context(
|
129
|
+
dir,
|
130
|
+
headless=headless,
|
131
|
+
viewport=viewport,
|
132
|
+
proxy=proxy_env,
|
133
|
+
# ignore_default_args=chromium_launch_args_to_ignore,
|
134
|
+
user_agent=os.environ.get("USER_AGENT", default_user_agent),
|
135
|
+
# args=extra_args,
|
136
|
+
**kwargs,
|
137
|
+
)
|
138
|
+
context.set_default_timeout(timeout * 1000)
|
139
|
+
|
140
|
+
async def remove_dir_after_close(*_: Any, **__: Any) -> None:
|
141
|
+
if not dir:
|
142
|
+
return
|
143
|
+
os.system(f"rm -rf {os.path.realpath(dir)}")
|
144
|
+
|
145
|
+
context.once("close", remove_dir_after_close)
|
146
|
+
yield context, context.pages[0]
|
147
|
+
|
148
|
+
|
149
|
+
async def dangerous_launch_chromium(
|
150
|
+
headless: bool = True,
|
151
|
+
timeout: int = 10,
|
152
|
+
web_socket: str | None = None,
|
153
|
+
cdp_url: str | None = None,
|
154
|
+
connection_method: Literal["ws", "cdp"] | None = None,
|
155
|
+
**kwargs: Any,
|
156
|
+
):
|
157
|
+
playwright = await async_playwright().start()
|
158
|
+
if web_socket is not None and connection_method == "ws":
|
159
|
+
logging.info(f"Connecting to ws: {web_socket}")
|
160
|
+
browser: Browser = await playwright.chromium.connect(web_socket)
|
161
|
+
browser.on("disconnected", lambda: logging.info("Browser Session disconnected"))
|
162
|
+
await browser.new_context(
|
163
|
+
viewport={"width": 1280, "height": 800}, user_agent=default_user_agent
|
164
|
+
)
|
165
|
+
context = browser.contexts[0]
|
166
|
+
dir = None
|
167
|
+
elif cdp_url is not None and connection_method == "cdp":
|
168
|
+
logging.info(f"Connecting to cdp: {cdp_url}")
|
169
|
+
browser: Browser = await playwright.chromium.connect_over_cdp(cdp_url)
|
170
|
+
browser.on("disconnected", lambda: logging.info("Browser Session disconnected"))
|
171
|
+
context = browser.contexts[0]
|
172
|
+
dir = None
|
173
|
+
elif web_socket is None and cdp_url is None and connection_method is None:
|
174
|
+
logging.info("Launching local browser")
|
175
|
+
dir = await create_user_dir_with_preferences()
|
176
|
+
logging.info(f"Using user data directory: {dir}")
|
177
|
+
if kwargs.get("proxy") is None:
|
178
|
+
proxy_env = get_proxy_env()
|
179
|
+
else:
|
180
|
+
proxy_env = kwargs.get("proxy")
|
181
|
+
# Remove proxy from kwargs if it exists
|
182
|
+
kwargs.pop("proxy", None)
|
183
|
+
viewport = kwargs.get("viewport", {"width": 1280, "height": 800})
|
184
|
+
kwargs.pop("viewport", None)
|
185
|
+
|
186
|
+
if headless:
|
187
|
+
chromium_launch_args_to_ignore.append("--headless")
|
188
|
+
extra_args.append("--headless=new")
|
189
|
+
|
190
|
+
context = await playwright.chromium.launch_persistent_context(
|
191
|
+
dir,
|
192
|
+
headless=headless,
|
193
|
+
viewport=viewport,
|
194
|
+
proxy=proxy_env,
|
195
|
+
ignore_default_args=chromium_launch_args_to_ignore,
|
196
|
+
user_agent=os.environ.get("USER_AGENT", default_user_agent),
|
197
|
+
args=extra_args,
|
198
|
+
**kwargs,
|
199
|
+
)
|
200
|
+
else:
|
201
|
+
raise ValueError(
|
202
|
+
"You have to provide method if you are launching a remote browser with ws or cdp"
|
203
|
+
)
|
204
|
+
context.set_default_timeout(timeout * 1000)
|
205
|
+
|
206
|
+
async def remove_dir_after_close(*_: Any, **__: Any) -> None:
|
207
|
+
if not dir:
|
208
|
+
return
|
209
|
+
os.system(f"rm -rf {os.path.realpath(dir)}")
|
210
|
+
|
211
|
+
context.once("close", remove_dir_after_close)
|
212
|
+
return playwright, context
|