pybiolib 0.2.951__py3-none-any.whl → 1.2.1890__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.
Files changed (262) hide show
  1. biolib/__init__.py +357 -11
  2. biolib/_data_record/data_record.py +380 -0
  3. biolib/_index/__init__.py +0 -0
  4. biolib/_index/index.py +55 -0
  5. biolib/_index/query_result.py +103 -0
  6. biolib/_internal/__init__.py +0 -0
  7. biolib/_internal/add_copilot_prompts.py +58 -0
  8. biolib/_internal/add_gui_files.py +81 -0
  9. biolib/_internal/data_record/__init__.py +1 -0
  10. biolib/_internal/data_record/data_record.py +85 -0
  11. biolib/_internal/data_record/push_data.py +116 -0
  12. biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
  13. biolib/_internal/errors.py +5 -0
  14. biolib/_internal/file_utils.py +125 -0
  15. biolib/_internal/fuse_mount/__init__.py +1 -0
  16. biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
  17. biolib/_internal/http_client.py +159 -0
  18. biolib/_internal/lfs/__init__.py +1 -0
  19. biolib/_internal/lfs/cache.py +51 -0
  20. biolib/_internal/libs/__init__.py +1 -0
  21. biolib/_internal/libs/fusepy/__init__.py +1257 -0
  22. biolib/_internal/push_application.py +488 -0
  23. biolib/_internal/runtime.py +22 -0
  24. biolib/_internal/string_utils.py +13 -0
  25. biolib/_internal/templates/__init__.py +1 -0
  26. biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
  27. biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
  28. biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
  29. biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
  30. biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
  31. biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
  32. biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
  33. biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
  34. biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
  35. biolib/_internal/templates/gitignore_template/.gitignore +10 -0
  36. biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
  37. biolib/_internal/templates/gui_template/App.tsx +53 -0
  38. biolib/_internal/templates/gui_template/Dockerfile +27 -0
  39. biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
  40. biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
  41. biolib/_internal/templates/gui_template/index.css +5 -0
  42. biolib/_internal/templates/gui_template/index.html +13 -0
  43. biolib/_internal/templates/gui_template/index.tsx +10 -0
  44. biolib/_internal/templates/gui_template/package.json +27 -0
  45. biolib/_internal/templates/gui_template/tsconfig.json +24 -0
  46. biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
  47. biolib/_internal/templates/gui_template/vite.config.mts +10 -0
  48. biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
  49. biolib/_internal/templates/init_template/Dockerfile +14 -0
  50. biolib/_internal/templates/init_template/requirements.txt +1 -0
  51. biolib/_internal/templates/init_template/run.py +12 -0
  52. biolib/_internal/templates/init_template/run.sh +4 -0
  53. biolib/_internal/templates/templates.py +25 -0
  54. biolib/_internal/tree_utils.py +106 -0
  55. biolib/_internal/utils/__init__.py +65 -0
  56. biolib/_internal/utils/auth.py +46 -0
  57. biolib/_internal/utils/job_url.py +33 -0
  58. biolib/_internal/utils/multinode.py +263 -0
  59. biolib/_runtime/runtime.py +157 -0
  60. biolib/_session/session.py +44 -0
  61. biolib/_shared/__init__.py +0 -0
  62. biolib/_shared/types/__init__.py +74 -0
  63. biolib/_shared/types/account.py +12 -0
  64. biolib/_shared/types/account_member.py +8 -0
  65. biolib/_shared/types/app.py +9 -0
  66. biolib/_shared/types/data_record.py +40 -0
  67. biolib/_shared/types/experiment.py +32 -0
  68. biolib/_shared/types/file_node.py +17 -0
  69. biolib/_shared/types/push.py +6 -0
  70. biolib/_shared/types/resource.py +37 -0
  71. biolib/_shared/types/resource_deploy_key.py +11 -0
  72. biolib/_shared/types/resource_permission.py +14 -0
  73. biolib/_shared/types/resource_version.py +19 -0
  74. biolib/_shared/types/result.py +14 -0
  75. biolib/_shared/types/typing.py +10 -0
  76. biolib/_shared/types/user.py +19 -0
  77. biolib/_shared/utils/__init__.py +7 -0
  78. biolib/_shared/utils/resource_uri.py +75 -0
  79. biolib/api/__init__.py +6 -0
  80. biolib/api/client.py +168 -0
  81. biolib/app/app.py +252 -49
  82. biolib/app/search_apps.py +45 -0
  83. biolib/biolib_api_client/api_client.py +126 -31
  84. biolib/biolib_api_client/app_types.py +24 -4
  85. biolib/biolib_api_client/auth.py +31 -8
  86. biolib/biolib_api_client/biolib_app_api.py +147 -52
  87. biolib/biolib_api_client/biolib_job_api.py +161 -141
  88. biolib/biolib_api_client/job_types.py +21 -5
  89. biolib/biolib_api_client/lfs_types.py +7 -23
  90. biolib/biolib_api_client/user_state.py +56 -0
  91. biolib/biolib_binary_format/__init__.py +1 -4
  92. biolib/biolib_binary_format/file_in_container.py +105 -0
  93. biolib/biolib_binary_format/module_input.py +24 -7
  94. biolib/biolib_binary_format/module_output_v2.py +149 -0
  95. biolib/biolib_binary_format/remote_endpoints.py +34 -0
  96. biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
  97. biolib/biolib_binary_format/saved_job.py +3 -2
  98. biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
  99. biolib/biolib_binary_format/system_status_update.py +3 -2
  100. biolib/biolib_binary_format/utils.py +175 -0
  101. biolib/biolib_docker_client/__init__.py +11 -2
  102. biolib/biolib_errors.py +36 -0
  103. biolib/biolib_logging.py +27 -10
  104. biolib/cli/__init__.py +38 -0
  105. biolib/cli/auth.py +46 -0
  106. biolib/cli/data_record.py +164 -0
  107. biolib/cli/index.py +32 -0
  108. biolib/cli/init.py +421 -0
  109. biolib/cli/lfs.py +101 -0
  110. biolib/cli/push.py +50 -0
  111. biolib/cli/run.py +63 -0
  112. biolib/cli/runtime.py +14 -0
  113. biolib/cli/sdk.py +16 -0
  114. biolib/cli/start.py +56 -0
  115. biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
  116. biolib/compute_node/job_worker/cache_state.py +66 -88
  117. biolib/compute_node/job_worker/cache_types.py +1 -6
  118. biolib/compute_node/job_worker/docker_image_cache.py +112 -37
  119. biolib/compute_node/job_worker/executors/__init__.py +0 -3
  120. biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
  121. biolib/compute_node/job_worker/executors/docker_types.py +9 -1
  122. biolib/compute_node/job_worker/executors/types.py +19 -9
  123. biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
  124. biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
  125. biolib/compute_node/job_worker/job_storage.py +108 -0
  126. biolib/compute_node/job_worker/job_worker.py +397 -212
  127. biolib/compute_node/job_worker/large_file_system.py +87 -38
  128. biolib/compute_node/job_worker/network_alloc.py +99 -0
  129. biolib/compute_node/job_worker/network_buffer.py +240 -0
  130. biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
  131. biolib/compute_node/job_worker/utils.py +9 -24
  132. biolib/compute_node/remote_host_proxy.py +400 -98
  133. biolib/compute_node/utils.py +31 -9
  134. biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
  135. biolib/compute_node/webserver/proxy_utils.py +28 -0
  136. biolib/compute_node/webserver/webserver.py +130 -44
  137. biolib/compute_node/webserver/webserver_types.py +2 -6
  138. biolib/compute_node/webserver/webserver_utils.py +77 -12
  139. biolib/compute_node/webserver/worker_thread.py +183 -42
  140. biolib/experiments/__init__.py +0 -0
  141. biolib/experiments/experiment.py +356 -0
  142. biolib/jobs/__init__.py +1 -0
  143. biolib/jobs/job.py +741 -0
  144. biolib/jobs/job_result.py +185 -0
  145. biolib/jobs/types.py +50 -0
  146. biolib/py.typed +0 -0
  147. biolib/runtime/__init__.py +14 -0
  148. biolib/sdk/__init__.py +91 -0
  149. biolib/tables.py +34 -0
  150. biolib/typing_utils.py +2 -7
  151. biolib/user/__init__.py +1 -0
  152. biolib/user/sign_in.py +54 -0
  153. biolib/utils/__init__.py +162 -0
  154. biolib/utils/cache_state.py +94 -0
  155. biolib/utils/multipart_uploader.py +194 -0
  156. biolib/utils/seq_util.py +150 -0
  157. biolib/utils/zip/remote_zip.py +640 -0
  158. pybiolib-1.2.1890.dist-info/METADATA +41 -0
  159. pybiolib-1.2.1890.dist-info/RECORD +177 -0
  160. {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
  161. pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
  162. README.md +0 -17
  163. biolib/app/app_result.py +0 -68
  164. biolib/app/utils.py +0 -62
  165. biolib/biolib-js/0-biolib.worker.js +0 -1
  166. biolib/biolib-js/1-biolib.worker.js +0 -1
  167. biolib/biolib-js/2-biolib.worker.js +0 -1
  168. biolib/biolib-js/3-biolib.worker.js +0 -1
  169. biolib/biolib-js/4-biolib.worker.js +0 -1
  170. biolib/biolib-js/5-biolib.worker.js +0 -1
  171. biolib/biolib-js/6-biolib.worker.js +0 -1
  172. biolib/biolib-js/index.html +0 -10
  173. biolib/biolib-js/main-biolib.js +0 -1
  174. biolib/biolib_api_client/biolib_account_api.py +0 -21
  175. biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
  176. biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
  177. biolib/biolib_binary_format/module_output.py +0 -58
  178. biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
  179. biolib/biolib_push.py +0 -114
  180. biolib/cli.py +0 -203
  181. biolib/cli_utils.py +0 -273
  182. biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
  183. biolib/compute_node/enclave/__init__.py +0 -2
  184. biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
  185. biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
  186. biolib/compute_node/job_worker/executors/base_executor.py +0 -18
  187. biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
  188. biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
  189. biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
  190. biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
  191. biolib/lfs.py +0 -196
  192. biolib/pyppeteer/.circleci/config.yml +0 -100
  193. biolib/pyppeteer/.coveragerc +0 -3
  194. biolib/pyppeteer/.gitignore +0 -89
  195. biolib/pyppeteer/.pre-commit-config.yaml +0 -28
  196. biolib/pyppeteer/CHANGES.md +0 -253
  197. biolib/pyppeteer/CONTRIBUTING.md +0 -26
  198. biolib/pyppeteer/LICENSE +0 -12
  199. biolib/pyppeteer/README.md +0 -137
  200. biolib/pyppeteer/docs/Makefile +0 -177
  201. biolib/pyppeteer/docs/_static/custom.css +0 -28
  202. biolib/pyppeteer/docs/_templates/layout.html +0 -10
  203. biolib/pyppeteer/docs/changes.md +0 -1
  204. biolib/pyppeteer/docs/conf.py +0 -299
  205. biolib/pyppeteer/docs/index.md +0 -21
  206. biolib/pyppeteer/docs/make.bat +0 -242
  207. biolib/pyppeteer/docs/reference.md +0 -211
  208. biolib/pyppeteer/docs/server.py +0 -60
  209. biolib/pyppeteer/poetry.lock +0 -1699
  210. biolib/pyppeteer/pyppeteer/__init__.py +0 -135
  211. biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
  212. biolib/pyppeteer/pyppeteer/browser.py +0 -401
  213. biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
  214. biolib/pyppeteer/pyppeteer/command.py +0 -22
  215. biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
  216. biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
  217. biolib/pyppeteer/pyppeteer/coverage.py +0 -346
  218. biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
  219. biolib/pyppeteer/pyppeteer/dialog.py +0 -79
  220. biolib/pyppeteer/pyppeteer/domworld.py +0 -597
  221. biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
  222. biolib/pyppeteer/pyppeteer/errors.py +0 -48
  223. biolib/pyppeteer/pyppeteer/events.py +0 -63
  224. biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
  225. biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
  226. biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
  227. biolib/pyppeteer/pyppeteer/helpers.py +0 -245
  228. biolib/pyppeteer/pyppeteer/input.py +0 -371
  229. biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
  230. biolib/pyppeteer/pyppeteer/launcher.py +0 -683
  231. biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
  232. biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
  233. biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
  234. biolib/pyppeteer/pyppeteer/multimap.py +0 -82
  235. biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
  236. biolib/pyppeteer/pyppeteer/options.py +0 -8
  237. biolib/pyppeteer/pyppeteer/page.py +0 -1728
  238. biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
  239. biolib/pyppeteer/pyppeteer/target.py +0 -147
  240. biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
  241. biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
  242. biolib/pyppeteer/pyppeteer/tracing.py +0 -93
  243. biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
  244. biolib/pyppeteer/pyppeteer/util.py +0 -18
  245. biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
  246. biolib/pyppeteer/pyppeteer/worker.py +0 -101
  247. biolib/pyppeteer/pyproject.toml +0 -97
  248. biolib/pyppeteer/spell.txt +0 -137
  249. biolib/pyppeteer/tox.ini +0 -72
  250. biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
  251. biolib/start_cli.py +0 -7
  252. biolib/utils.py +0 -47
  253. biolib/validators/validate_app_version.py +0 -183
  254. biolib/validators/validate_argument.py +0 -134
  255. biolib/validators/validate_module.py +0 -323
  256. biolib/validators/validate_zip_file.py +0 -40
  257. biolib/validators/validator_utils.py +0 -103
  258. pybiolib-0.2.951.dist-info/LICENSE +0 -21
  259. pybiolib-0.2.951.dist-info/METADATA +0 -61
  260. pybiolib-0.2.951.dist-info/RECORD +0 -153
  261. pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
  262. /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,5 @@
1
+ biolib_version: 2
2
+ assets: dist/
3
+ modules:
4
+ main:
5
+ image: "assets://index.html"
@@ -0,0 +1,21 @@
1
+ name: BioLib Build & Push
2
+ on: push
3
+ concurrency:
4
+ group: ${{ github.repository }}
5
+ cancel-in-progress: false
6
+ jobs:
7
+ build-and-push:
8
+ runs-on: biolib-github-runner
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - name: Build
12
+ run: BIOLIB_REPLACE_BUILD_COMMAND
13
+ - name: Push
14
+ run: |
15
+ if [ "$GITHUB_REF_NAME" == "main" ]; then
16
+ biolib push BIOLIB_REPLACE_APP_URI
17
+ else
18
+ biolib push --dev BIOLIB_REPLACE_APP_URI:latest-dev
19
+ fi
20
+ env:
21
+ BIOLIB_TOKEN: ${{ secrets.BIOLIB_TOKEN }}
@@ -0,0 +1,10 @@
1
+ # Python
2
+ *__pycache__*
3
+ *.pyc
4
+
5
+ # GUI
6
+ .yarn
7
+ dist
8
+ yarn.lock
9
+ tsconfig.tsbuildinfo
10
+ node_modules
@@ -0,0 +1 @@
1
+ nodeLinker: node-modules
@@ -0,0 +1,53 @@
1
+ import { useState, useEffect } from "react";
2
+ import biolib from "./biolib-sdk";
3
+
4
+ export default function App() {
5
+ const [outputFileData, setOutputFileData] = useState<Uint8Array | null>(null);
6
+ const [loading, setLoading] = useState(true);
7
+
8
+ const loadOutputData = async () => {
9
+ setLoading(true);
10
+ try {
11
+ const data = await biolib.getOutputFileData("output.json");
12
+ setOutputFileData(data);
13
+ } catch (error) {
14
+ console.error("Error loading output data:", error);
15
+ setOutputFileData(null);
16
+ } finally {
17
+ setLoading(false);
18
+ }
19
+ };
20
+
21
+ useEffect(() => {
22
+ loadOutputData();
23
+ }, []);
24
+
25
+ return (
26
+ <div className="min-h-screen bg-gray-100 flex items-center justify-center">
27
+ <div className="text-center max-w-2xl mx-auto p-8">
28
+ <h1 className="text-4xl font-bold mb-4">
29
+ Hello, BioLib!
30
+ </h1>
31
+ <p className="text-lg mb-2">
32
+ You have successfully set up your BioLib GUI application.
33
+ </p>
34
+ <p className="italic mb-6">
35
+ This is a simple React template with Tailwind CSS styling.
36
+ </p>
37
+
38
+ <div className="mt-8 p-4 bg-white rounded-lg shadow">
39
+ <h2 className="text-xl font-semibold mb-4">Example: Reading Output Files</h2>
40
+ {loading ? (
41
+ <p className="text-gray-500">Loading output.json...</p>
42
+ ) : outputFileData ? (
43
+ <div className="p-3 bg-gray-50 rounded text-left">
44
+ <pre className="text-sm">{new TextDecoder().decode(outputFileData)}</pre>
45
+ </div>
46
+ ) : (
47
+ <p className="text-red-500">Failed to load output.json</p>
48
+ )}
49
+ </div>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
@@ -0,0 +1,27 @@
1
+ # syntax=docker/dockerfile:1
2
+
3
+ FROM node:24.4.1-alpine3.21 AS gui_builder
4
+
5
+ WORKDIR /home/biolib/
6
+
7
+ RUN corepack enable
8
+ COPY .yarnrc.yml .
9
+ COPY package.json .
10
+ COPY vite.config.mts .
11
+ RUN yarn install
12
+
13
+ COPY gui gui
14
+ RUN yarn build
15
+
16
+ FROM python:3.13.5-slim
17
+ WORKDIR /home/biolib/
18
+
19
+ COPY requirements.txt .
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ COPY run.sh .
23
+ COPY run.py .
24
+
25
+ RUN mkdir output
26
+
27
+ COPY --from=gui_builder /home/biolib/gui/dist/index.html result.html
@@ -0,0 +1,82 @@
1
+ interface IGetFileFromDataRecordOptions {
2
+ uri: string;
3
+ path: string;
4
+ }
5
+
6
+ interface IRunJobPayload {
7
+ appUri: string;
8
+ arguments: string[];
9
+ files: Record<string, { data: Uint8Array }>;
10
+ temporaryClientSecrets?: Record<string, string>;
11
+ openResult?: boolean | 'currentWindow' | 'newTab';
12
+ blocking?: boolean;
13
+ }
14
+
15
+ interface IJobProgress {
16
+ logMessage?: string;
17
+ systemLogMessage?: string;
18
+ jobId: string;
19
+ }
20
+
21
+ interface IJobResult {
22
+ exit_code: number;
23
+ stderr: Uint8Array;
24
+ stdout: Uint8Array;
25
+ files: Record<string, { data: Uint8Array }>;
26
+ jobId: string;
27
+ }
28
+
29
+ interface IPendingJobResult {
30
+ jobId: string;
31
+ authToken: string;
32
+ }
33
+
34
+ interface IBioLibGlobals {
35
+ getFileDataFromDataRecord: (options: IGetFileFromDataRecordOptions) => Promise<Uint8Array>;
36
+ getOutputFileData: (path: string) => Promise<Uint8Array>;
37
+ listOutputFilePaths: () => Promise<string[]>;
38
+ runJob: (payload: IRunJobPayload, jobProgressHandler?: (jobProgress: IJobProgress) => void) => Promise<IJobResult | IPendingJobResult>;
39
+ }
40
+
41
+ declare global {
42
+ const biolib: IBioLibGlobals;
43
+ }
44
+
45
+ // DO NOT MODIFY: Development data files are injected at build time from gui/dev-data/ folder
46
+ const DEV_DATA_FILES: Record<string, string> = {};
47
+
48
+ const devSdkBioLib: IBioLibGlobals = {
49
+ getFileDataFromDataRecord: async (_options: IGetFileFromDataRecordOptions): Promise<Uint8Array> => {
50
+ throw new Error('getFileDataFromDataRecord is not available in local development mode.');
51
+ },
52
+ getOutputFileData: async (path: string): Promise<Uint8Array> => {
53
+ console.log(`[SDK] getOutputFileData called with path: ${path}`);
54
+
55
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
56
+
57
+ if (typeof DEV_DATA_FILES !== 'undefined' && normalizedPath in DEV_DATA_FILES) {
58
+ const base64Data = DEV_DATA_FILES[normalizedPath];
59
+ const binaryString = atob(base64Data);
60
+ const bytes = new Uint8Array(binaryString.length);
61
+ for (let i = 0; i < binaryString.length; i++) {
62
+ bytes[i] = binaryString.charCodeAt(i);
63
+ }
64
+ return bytes;
65
+ }
66
+
67
+ throw new Error(`File not found: ${path}. Add this file to the dev-data/ folder for local development.`);
68
+ },
69
+ listOutputFilePaths: async (): Promise<string[]> => {
70
+ throw new Error('listOutputFilePaths is not available in local development mode.');
71
+ },
72
+ runJob: async (_payload: IRunJobPayload, _jobProgressHandler?: (jobProgress: IJobProgress) => void): Promise<IJobResult | IPendingJobResult> => {
73
+ throw new Error('runJob is not available in local development mode.');
74
+ },
75
+ };
76
+
77
+ const biolib: IBioLibGlobals =
78
+ process.env.NODE_ENV === "development"
79
+ ? devSdkBioLib
80
+ : (window as any).biolib;
81
+
82
+ export default biolib;
@@ -0,0 +1,7 @@
1
+ {
2
+ "message": "Example JSON data for development",
3
+ "results": [
4
+ { "id": 1, "value": "Sample result 1" },
5
+ { "id": 2, "value": "Sample result 2" }
6
+ ]
7
+ }
@@ -0,0 +1,5 @@
1
+ @import "tailwindcss";
2
+
3
+ * {
4
+ font-family: "Noto Sans", sans-serif;
5
+ }
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link href="index.css" rel="stylesheet">
7
+ <title>BIOLIB_REPLACE_APP_NAME</title>
8
+ </head>
9
+ <body style="overflow: hidden; background: white">
10
+ <div id="root"></div>
11
+ <script type="module" src="index.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import App from "./App.tsx";
4
+ import "./index.css";
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "biolib_replace_app_name",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc --noEmit -p gui/tsconfig.json && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "18.3.1",
13
+ "react-dom": "18.3.1"
14
+ },
15
+ "devDependencies": {
16
+ "@tailwindcss/vite": "4.0.14",
17
+ "@types/node": "20.17.10",
18
+ "@types/react": "18.3.3",
19
+ "@types/react-dom": "18.3.0",
20
+ "@vitejs/plugin-react": "4.2.1",
21
+ "tailwindcss": "4.0.14",
22
+ "typescript": "5.2.2",
23
+ "vite": "5.3.4",
24
+ "vite-plugin-singlefile": "2.0.2"
25
+ },
26
+ "packageManager": "yarn@4.6.0"
27
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "target": "ES2020",
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["."]
24
+ }
@@ -0,0 +1,50 @@
1
+ import type { Plugin } from 'vite';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ export function devDataPlugin(): Plugin {
6
+ let isDev = false;
7
+
8
+ return {
9
+ name: 'dev-data-plugin',
10
+ configResolved(config) {
11
+ isDev = config.mode === 'development';
12
+ },
13
+ transform(code: string, id: string) {
14
+ if (id.endsWith('biolib-sdk.ts')) {
15
+ let injectedCode: string;
16
+
17
+ if (isDev) {
18
+ const devDataDir = path.join(__dirname, 'dev-data');
19
+ const devDataMap: Record<string, string> = {};
20
+
21
+ if (fs.existsSync(devDataDir)) {
22
+ const entries = fs.readdirSync(devDataDir, { recursive: true });
23
+ for (const entry of entries) {
24
+ const relativePath = entry.toString();
25
+ const fullPath = path.join(devDataDir, relativePath);
26
+ if (fs.statSync(fullPath).isFile()) {
27
+ const content = fs.readFileSync(fullPath);
28
+ const base64Content = content.toString('base64');
29
+ devDataMap[relativePath] = base64Content;
30
+ }
31
+ }
32
+ }
33
+
34
+ const devDataJson = JSON.stringify(devDataMap);
35
+ injectedCode = code.replace(
36
+ "const DEV_DATA_FILES = {};",
37
+ `const DEV_DATA_FILES = ${devDataJson};`
38
+ );
39
+ } else {
40
+ injectedCode = code;
41
+ }
42
+
43
+ return {
44
+ code: injectedCode,
45
+ map: null
46
+ };
47
+ }
48
+ }
49
+ };
50
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import { viteSingleFile } from "vite-plugin-singlefile";
5
+ import { devDataPlugin } from "./gui/vite-plugin-dev-data";
6
+
7
+ export default defineConfig({
8
+ root: "gui",
9
+ plugins: [react(), tailwindcss(), devDataPlugin(), viteSingleFile()],
10
+ });
@@ -0,0 +1,19 @@
1
+ biolib_version: 2
2
+
3
+ modules:
4
+ main:
5
+ image: 'local-docker://BIOLIB_REPLACE_DOCKER_TAG:latest'
6
+ command: "bash run.sh"
7
+ working_directory: /home/biolib/
8
+ input_files:
9
+ - COPY / /home/biolib/
10
+ output_files:
11
+ - COPY /home/biolib/output/ /
12
+
13
+ BIOLIB_REPLACE_GUI_CONFIG
14
+ arguments:
15
+ -
16
+ key: --input
17
+ description: 'Input protein sequences'
18
+ type: sequence
19
+ required: true
@@ -0,0 +1,14 @@
1
+ # syntax=docker/dockerfile:1
2
+
3
+ FROM python:3.13.3-slim
4
+ WORKDIR /home/biolib/
5
+
6
+ RUN pip install --no-cache-dir uv
7
+
8
+ COPY requirements.txt .
9
+ RUN uv pip install --no-cache-dir --system -r requirements.txt
10
+
11
+ COPY run.sh .
12
+ COPY run.py .
13
+
14
+ RUN mkdir output
@@ -0,0 +1 @@
1
+ pybiolib==BIOLIB_REPLACE_PYBIOLIB_VERSION
@@ -0,0 +1,12 @@
1
+ import argparse
2
+
3
+ from biolib.sdk import Runtime
4
+
5
+ parser = argparse.ArgumentParser(description='Process some biological sequences.')
6
+ parser.add_argument('--input', type=str, required=True, help='Input protein sequences')
7
+ args = parser.parse_args()
8
+
9
+ # update the BioLib result name based on the provided file
10
+ Runtime.set_result_name_from_file(args.input)
11
+
12
+ print(f'Processing input file {args.input}...')
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ set -e
3
+ BIOLIB_REPLACE_GUI_MV_COMMAND
4
+ python3 run.py "$@"
@@ -0,0 +1,25 @@
1
+ import os
2
+
3
+
4
+ def init_template() -> str:
5
+ return os.path.join(os.path.dirname(__file__), 'init_template')
6
+
7
+
8
+ def copilot_template() -> str:
9
+ return os.path.join(os.path.dirname(__file__), 'copilot_template')
10
+
11
+
12
+ def gui_template() -> str:
13
+ return os.path.join(os.path.dirname(__file__), 'gui_template')
14
+
15
+
16
+ def dashboard_template() -> str:
17
+ return os.path.join(os.path.dirname(__file__), 'dashboard_template')
18
+
19
+
20
+ def gitignore_template() -> str:
21
+ return os.path.join(os.path.dirname(__file__), 'gitignore_template')
22
+
23
+
24
+ def github_workflow_template() -> str:
25
+ return os.path.join(os.path.dirname(__file__), 'github_workflow_template')
@@ -0,0 +1,106 @@
1
+ """Utilities for displaying file trees and formatting file sizes."""
2
+
3
+ from typing import Any, Dict, List, Optional, Union
4
+
5
+ from biolib.biolib_binary_format.utils import LazyLoadedFile
6
+
7
+
8
+ def format_size(size_bytes: int) -> str:
9
+ """Convert bytes to human-readable format (e.g., 2.5KB).
10
+
11
+ Args:
12
+ size_bytes: File size in bytes
13
+
14
+ Returns:
15
+ Human-readable string representation of the file size
16
+ """
17
+ if size_bytes < 1024:
18
+ return f'{size_bytes}B'
19
+ elif size_bytes < 1024 * 1024:
20
+ return f'{size_bytes/1024:.1f}KB'
21
+ elif size_bytes < 1024 * 1024 * 1024:
22
+ return f'{size_bytes/(1024*1024):.1f}MB'
23
+ else:
24
+ return f'{size_bytes/(1024*1024*1024):.1f}GB'
25
+
26
+
27
+ def build_tree_str(
28
+ data: Dict[str, Any],
29
+ prefix: str = '',
30
+ tree_lines: Optional[List[str]] = None,
31
+ blue: str = '\033[34m',
32
+ white: str = '\033[90m', # Changed from white (37m) to dark gray (90m) for better visibility on light backgrounds
33
+ reset: str = '\033[0m',
34
+ ) -> List[str]:
35
+ """Build a string representation of a file tree with color-coded directories and files.
36
+
37
+ Args:
38
+ data: Hierarchical tree structure of directories and files
39
+ prefix: Line prefix for indentation and tree structure characters
40
+ tree_lines: List to accumulate tree lines
41
+ blue: ANSI color code for directories
42
+ white: ANSI color code for files (defaults to dark gray for visibility on light backgrounds)
43
+ reset: ANSI color code to reset color
44
+
45
+ Returns:
46
+ List of tree lines for display
47
+ """
48
+ if tree_lines is None:
49
+ tree_lines = []
50
+
51
+ # Get sorted items, keeping directories first
52
+ items = sorted([(k, v) for k, v in data.items() if k != '__files__'])
53
+ files_list = sorted(data.get('__files__', []), key=lambda f: f['name'])
54
+
55
+ # Add directories
56
+ for i, (key, value) in enumerate(items):
57
+ is_last_dir = i == len(items) - 1 and not files_list
58
+
59
+ if is_last_dir:
60
+ tree_lines.append(f'{prefix}└── {blue}{key}{reset}')
61
+ build_tree_str(value, prefix + ' ', tree_lines, blue, white, reset)
62
+ else:
63
+ tree_lines.append(f'{prefix}├── {blue}{key}{reset}')
64
+ build_tree_str(value, prefix + '│ ', tree_lines, blue, white, reset)
65
+
66
+ # Add files with their sizes
67
+ for i, file in enumerate(files_list):
68
+ is_last = i == len(files_list) - 1
69
+ size_str = format_size(file['size'])
70
+ file_name = file['name']
71
+
72
+ if is_last:
73
+ tree_lines.append(f'{prefix}└── {white}{file_name} ({size_str}){reset}')
74
+ else:
75
+ tree_lines.append(f'{prefix}├── {white}{file_name} ({size_str}){reset}')
76
+
77
+ return tree_lines
78
+
79
+
80
+ def build_tree_from_files(files: List[LazyLoadedFile]) -> Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]]:
81
+ """Build a hierarchical tree structure from a list of files.
82
+
83
+ Args:
84
+ files: List of files to organize into a tree
85
+
86
+ Returns:
87
+ Hierarchical tree structure of directories and files
88
+ """
89
+ tree: Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]] = {}
90
+ for file in files:
91
+ parts = file.path.lstrip('/').split('/')
92
+ current: Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]] = tree
93
+ for i, part in enumerate(parts):
94
+ if i == len(parts) - 1: # This is a file
95
+ if '__files__' not in current:
96
+ current['__files__'] = []
97
+ files_list = current['__files__']
98
+ assert isinstance(files_list, list)
99
+ files_list.append({'name': part, 'size': file.length})
100
+ else: # This is a directory
101
+ if part not in current:
102
+ current[part] = {}
103
+ dir_dict = current[part]
104
+ assert isinstance(dir_dict, dict)
105
+ current = dir_dict
106
+ return tree
@@ -0,0 +1,65 @@
1
+ import base64
2
+ import shutil
3
+ import time
4
+ import uuid
5
+ from fnmatch import fnmatch
6
+
7
+ from biolib.biolib_binary_format.utils import LazyLoadedFile
8
+ from biolib.typing_utils import Callable, List, Union, cast
9
+
10
+ PathFilter = Union[str, Callable[[str], bool]]
11
+
12
+
13
+ def filter_lazy_loaded_files(files: List[LazyLoadedFile], path_filter: PathFilter) -> List[LazyLoadedFile]:
14
+ if not (isinstance(path_filter, str) or callable(path_filter)):
15
+ raise Exception('Expected path_filter to be a string or a function')
16
+
17
+ if callable(path_filter):
18
+ return list(filter(lambda x: path_filter(x.path), files)) # type: ignore
19
+
20
+ glob_filter = cast(str, path_filter)
21
+
22
+ # since all file paths start with /, make sure filter does too
23
+ if not glob_filter.startswith('/'):
24
+ glob_filter = '/' + glob_filter
25
+
26
+ def _filter_function(file: LazyLoadedFile) -> bool:
27
+ return fnmatch(file.path, glob_filter)
28
+
29
+ return list(filter(_filter_function, files))
30
+
31
+
32
+ def open_browser_window_from_notebook(url_to_open: str) -> None:
33
+ try:
34
+ from IPython.display import ( # type:ignore # pylint: disable=import-error, import-outside-toplevel
35
+ Javascript,
36
+ display,
37
+ update_display,
38
+ )
39
+ except ImportError as error:
40
+ raise Exception('Unexpected environment. This function can only be called from a notebook.') from error
41
+
42
+ display_id = str(uuid.uuid4())
43
+ display(Javascript(f'window.open("{url_to_open}");'), display_id=display_id)
44
+ time.sleep(1)
45
+ update_display(Javascript(''), display_id=display_id)
46
+
47
+
48
+ def base64_encode_string(input_str: str) -> str:
49
+ input_bytes = input_str.encode('utf-8')
50
+ base64_bytes = base64.b64encode(input_bytes)
51
+ base64_str = base64_bytes.decode('utf-8')
52
+ return base64_str
53
+
54
+
55
+ def decode_base64_string(base64_str: str) -> str:
56
+ base64_bytes = base64_str.encode('utf-8')
57
+ input_bytes = base64.b64decode(base64_bytes)
58
+ input_str = input_bytes.decode('utf-8')
59
+ return input_str
60
+
61
+
62
+ def get_pip_command() -> str:
63
+ if shutil.which('uv'):
64
+ return 'uv pip'
65
+ return 'pip'
@@ -0,0 +1,46 @@
1
+ import base64
2
+ import binascii
3
+ import json
4
+ from typing import Any, Dict
5
+
6
+
7
+ class JwtDecodeError(Exception):
8
+ pass
9
+
10
+
11
+ def decode_jwt_without_checking_signature(jwt: str) -> Dict[str, Any]:
12
+ jwt_bytes = jwt.encode('utf-8')
13
+
14
+ try:
15
+ signing_input, _ = jwt_bytes.rsplit(b'.', 1)
16
+ header_segment, payload_segment = signing_input.split(b'.', 1)
17
+ except ValueError as error:
18
+ raise JwtDecodeError('Not enough segments') from error
19
+
20
+ try:
21
+ header_data = base64.urlsafe_b64decode(header_segment)
22
+ except (TypeError, binascii.Error) as error:
23
+ raise JwtDecodeError('Invalid header padding') from error
24
+
25
+ try:
26
+ header = json.loads(header_data)
27
+ except ValueError as error:
28
+ raise JwtDecodeError(f'Invalid header string: {error}') from error
29
+
30
+ if not isinstance(header, dict):
31
+ raise JwtDecodeError('Invalid header string: must be a json object')
32
+
33
+ try:
34
+ payload_data = base64.urlsafe_b64decode(payload_segment)
35
+ except (TypeError, binascii.Error) as error:
36
+ raise JwtDecodeError('Invalid payload padding') from error
37
+
38
+ try:
39
+ payload = json.loads(payload_data)
40
+ except ValueError as error:
41
+ raise JwtDecodeError(f'Invalid payload string: {error}') from error
42
+
43
+ if not isinstance(payload, dict):
44
+ raise JwtDecodeError('Invalid payload string: must be a json object')
45
+
46
+ return dict(header=header, payload=payload)