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.
- biolib/__init__.py +357 -11
- biolib/_data_record/data_record.py +380 -0
- biolib/_index/__init__.py +0 -0
- biolib/_index/index.py +55 -0
- biolib/_index/query_result.py +103 -0
- biolib/_internal/__init__.py +0 -0
- biolib/_internal/add_copilot_prompts.py +58 -0
- biolib/_internal/add_gui_files.py +81 -0
- biolib/_internal/data_record/__init__.py +1 -0
- biolib/_internal/data_record/data_record.py +85 -0
- biolib/_internal/data_record/push_data.py +116 -0
- biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
- biolib/_internal/errors.py +5 -0
- biolib/_internal/file_utils.py +125 -0
- biolib/_internal/fuse_mount/__init__.py +1 -0
- biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
- biolib/_internal/http_client.py +159 -0
- biolib/_internal/lfs/__init__.py +1 -0
- biolib/_internal/lfs/cache.py +51 -0
- biolib/_internal/libs/__init__.py +1 -0
- biolib/_internal/libs/fusepy/__init__.py +1257 -0
- biolib/_internal/push_application.py +488 -0
- biolib/_internal/runtime.py +22 -0
- biolib/_internal/string_utils.py +13 -0
- biolib/_internal/templates/__init__.py +1 -0
- biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
- biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
- biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
- biolib/_internal/templates/gitignore_template/.gitignore +10 -0
- biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
- biolib/_internal/templates/gui_template/App.tsx +53 -0
- biolib/_internal/templates/gui_template/Dockerfile +27 -0
- biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
- biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
- biolib/_internal/templates/gui_template/index.css +5 -0
- biolib/_internal/templates/gui_template/index.html +13 -0
- biolib/_internal/templates/gui_template/index.tsx +10 -0
- biolib/_internal/templates/gui_template/package.json +27 -0
- biolib/_internal/templates/gui_template/tsconfig.json +24 -0
- biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
- biolib/_internal/templates/gui_template/vite.config.mts +10 -0
- biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
- biolib/_internal/templates/init_template/Dockerfile +14 -0
- biolib/_internal/templates/init_template/requirements.txt +1 -0
- biolib/_internal/templates/init_template/run.py +12 -0
- biolib/_internal/templates/init_template/run.sh +4 -0
- biolib/_internal/templates/templates.py +25 -0
- biolib/_internal/tree_utils.py +106 -0
- biolib/_internal/utils/__init__.py +65 -0
- biolib/_internal/utils/auth.py +46 -0
- biolib/_internal/utils/job_url.py +33 -0
- biolib/_internal/utils/multinode.py +263 -0
- biolib/_runtime/runtime.py +157 -0
- biolib/_session/session.py +44 -0
- biolib/_shared/__init__.py +0 -0
- biolib/_shared/types/__init__.py +74 -0
- biolib/_shared/types/account.py +12 -0
- biolib/_shared/types/account_member.py +8 -0
- biolib/_shared/types/app.py +9 -0
- biolib/_shared/types/data_record.py +40 -0
- biolib/_shared/types/experiment.py +32 -0
- biolib/_shared/types/file_node.py +17 -0
- biolib/_shared/types/push.py +6 -0
- biolib/_shared/types/resource.py +37 -0
- biolib/_shared/types/resource_deploy_key.py +11 -0
- biolib/_shared/types/resource_permission.py +14 -0
- biolib/_shared/types/resource_version.py +19 -0
- biolib/_shared/types/result.py +14 -0
- biolib/_shared/types/typing.py +10 -0
- biolib/_shared/types/user.py +19 -0
- biolib/_shared/utils/__init__.py +7 -0
- biolib/_shared/utils/resource_uri.py +75 -0
- biolib/api/__init__.py +6 -0
- biolib/api/client.py +168 -0
- biolib/app/app.py +252 -49
- biolib/app/search_apps.py +45 -0
- biolib/biolib_api_client/api_client.py +126 -31
- biolib/biolib_api_client/app_types.py +24 -4
- biolib/biolib_api_client/auth.py +31 -8
- biolib/biolib_api_client/biolib_app_api.py +147 -52
- biolib/biolib_api_client/biolib_job_api.py +161 -141
- biolib/biolib_api_client/job_types.py +21 -5
- biolib/biolib_api_client/lfs_types.py +7 -23
- biolib/biolib_api_client/user_state.py +56 -0
- biolib/biolib_binary_format/__init__.py +1 -4
- biolib/biolib_binary_format/file_in_container.py +105 -0
- biolib/biolib_binary_format/module_input.py +24 -7
- biolib/biolib_binary_format/module_output_v2.py +149 -0
- biolib/biolib_binary_format/remote_endpoints.py +34 -0
- biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
- biolib/biolib_binary_format/saved_job.py +3 -2
- biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
- biolib/biolib_binary_format/system_status_update.py +3 -2
- biolib/biolib_binary_format/utils.py +175 -0
- biolib/biolib_docker_client/__init__.py +11 -2
- biolib/biolib_errors.py +36 -0
- biolib/biolib_logging.py +27 -10
- biolib/cli/__init__.py +38 -0
- biolib/cli/auth.py +46 -0
- biolib/cli/data_record.py +164 -0
- biolib/cli/index.py +32 -0
- biolib/cli/init.py +421 -0
- biolib/cli/lfs.py +101 -0
- biolib/cli/push.py +50 -0
- biolib/cli/run.py +63 -0
- biolib/cli/runtime.py +14 -0
- biolib/cli/sdk.py +16 -0
- biolib/cli/start.py +56 -0
- biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
- biolib/compute_node/job_worker/cache_state.py +66 -88
- biolib/compute_node/job_worker/cache_types.py +1 -6
- biolib/compute_node/job_worker/docker_image_cache.py +112 -37
- biolib/compute_node/job_worker/executors/__init__.py +0 -3
- biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
- biolib/compute_node/job_worker/executors/docker_types.py +9 -1
- biolib/compute_node/job_worker/executors/types.py +19 -9
- biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
- biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
- biolib/compute_node/job_worker/job_storage.py +108 -0
- biolib/compute_node/job_worker/job_worker.py +397 -212
- biolib/compute_node/job_worker/large_file_system.py +87 -38
- biolib/compute_node/job_worker/network_alloc.py +99 -0
- biolib/compute_node/job_worker/network_buffer.py +240 -0
- biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
- biolib/compute_node/job_worker/utils.py +9 -24
- biolib/compute_node/remote_host_proxy.py +400 -98
- biolib/compute_node/utils.py +31 -9
- biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
- biolib/compute_node/webserver/proxy_utils.py +28 -0
- biolib/compute_node/webserver/webserver.py +130 -44
- biolib/compute_node/webserver/webserver_types.py +2 -6
- biolib/compute_node/webserver/webserver_utils.py +77 -12
- biolib/compute_node/webserver/worker_thread.py +183 -42
- biolib/experiments/__init__.py +0 -0
- biolib/experiments/experiment.py +356 -0
- biolib/jobs/__init__.py +1 -0
- biolib/jobs/job.py +741 -0
- biolib/jobs/job_result.py +185 -0
- biolib/jobs/types.py +50 -0
- biolib/py.typed +0 -0
- biolib/runtime/__init__.py +14 -0
- biolib/sdk/__init__.py +91 -0
- biolib/tables.py +34 -0
- biolib/typing_utils.py +2 -7
- biolib/user/__init__.py +1 -0
- biolib/user/sign_in.py +54 -0
- biolib/utils/__init__.py +162 -0
- biolib/utils/cache_state.py +94 -0
- biolib/utils/multipart_uploader.py +194 -0
- biolib/utils/seq_util.py +150 -0
- biolib/utils/zip/remote_zip.py +640 -0
- pybiolib-1.2.1890.dist-info/METADATA +41 -0
- pybiolib-1.2.1890.dist-info/RECORD +177 -0
- {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
- pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
- README.md +0 -17
- biolib/app/app_result.py +0 -68
- biolib/app/utils.py +0 -62
- biolib/biolib-js/0-biolib.worker.js +0 -1
- biolib/biolib-js/1-biolib.worker.js +0 -1
- biolib/biolib-js/2-biolib.worker.js +0 -1
- biolib/biolib-js/3-biolib.worker.js +0 -1
- biolib/biolib-js/4-biolib.worker.js +0 -1
- biolib/biolib-js/5-biolib.worker.js +0 -1
- biolib/biolib-js/6-biolib.worker.js +0 -1
- biolib/biolib-js/index.html +0 -10
- biolib/biolib-js/main-biolib.js +0 -1
- biolib/biolib_api_client/biolib_account_api.py +0 -21
- biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
- biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
- biolib/biolib_binary_format/module_output.py +0 -58
- biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
- biolib/biolib_push.py +0 -114
- biolib/cli.py +0 -203
- biolib/cli_utils.py +0 -273
- biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
- biolib/compute_node/enclave/__init__.py +0 -2
- biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
- biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
- biolib/compute_node/job_worker/executors/base_executor.py +0 -18
- biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
- biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
- biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
- biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
- biolib/lfs.py +0 -196
- biolib/pyppeteer/.circleci/config.yml +0 -100
- biolib/pyppeteer/.coveragerc +0 -3
- biolib/pyppeteer/.gitignore +0 -89
- biolib/pyppeteer/.pre-commit-config.yaml +0 -28
- biolib/pyppeteer/CHANGES.md +0 -253
- biolib/pyppeteer/CONTRIBUTING.md +0 -26
- biolib/pyppeteer/LICENSE +0 -12
- biolib/pyppeteer/README.md +0 -137
- biolib/pyppeteer/docs/Makefile +0 -177
- biolib/pyppeteer/docs/_static/custom.css +0 -28
- biolib/pyppeteer/docs/_templates/layout.html +0 -10
- biolib/pyppeteer/docs/changes.md +0 -1
- biolib/pyppeteer/docs/conf.py +0 -299
- biolib/pyppeteer/docs/index.md +0 -21
- biolib/pyppeteer/docs/make.bat +0 -242
- biolib/pyppeteer/docs/reference.md +0 -211
- biolib/pyppeteer/docs/server.py +0 -60
- biolib/pyppeteer/poetry.lock +0 -1699
- biolib/pyppeteer/pyppeteer/__init__.py +0 -135
- biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
- biolib/pyppeteer/pyppeteer/browser.py +0 -401
- biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
- biolib/pyppeteer/pyppeteer/command.py +0 -22
- biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
- biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
- biolib/pyppeteer/pyppeteer/coverage.py +0 -346
- biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
- biolib/pyppeteer/pyppeteer/dialog.py +0 -79
- biolib/pyppeteer/pyppeteer/domworld.py +0 -597
- biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
- biolib/pyppeteer/pyppeteer/errors.py +0 -48
- biolib/pyppeteer/pyppeteer/events.py +0 -63
- biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
- biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
- biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
- biolib/pyppeteer/pyppeteer/helpers.py +0 -245
- biolib/pyppeteer/pyppeteer/input.py +0 -371
- biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
- biolib/pyppeteer/pyppeteer/launcher.py +0 -683
- biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
- biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
- biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
- biolib/pyppeteer/pyppeteer/multimap.py +0 -82
- biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
- biolib/pyppeteer/pyppeteer/options.py +0 -8
- biolib/pyppeteer/pyppeteer/page.py +0 -1728
- biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
- biolib/pyppeteer/pyppeteer/target.py +0 -147
- biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
- biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
- biolib/pyppeteer/pyppeteer/tracing.py +0 -93
- biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
- biolib/pyppeteer/pyppeteer/util.py +0 -18
- biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
- biolib/pyppeteer/pyppeteer/worker.py +0 -101
- biolib/pyppeteer/pyproject.toml +0 -97
- biolib/pyppeteer/spell.txt +0 -137
- biolib/pyppeteer/tox.ini +0 -72
- biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
- biolib/start_cli.py +0 -7
- biolib/utils.py +0 -47
- biolib/validators/validate_app_version.py +0 -183
- biolib/validators/validate_argument.py +0 -134
- biolib/validators/validate_module.py +0 -323
- biolib/validators/validate_zip_file.py +0 -40
- biolib/validators/validator_utils.py +0 -103
- pybiolib-0.2.951.dist-info/LICENSE +0 -21
- pybiolib-0.2.951.dist-info/METADATA +0 -61
- pybiolib-0.2.951.dist-info/RECORD +0 -153
- pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
- /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
|
@@ -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 @@
|
|
|
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,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,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,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)
|