update branch
This commit is contained in:
79
.gitignore
vendored
79
.gitignore
vendored
@@ -4,82 +4,25 @@ __pycache__/
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
*.pyd
|
*.pyd
|
||||||
.Python
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
.venv/
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
.tox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.log
|
|
||||||
*.pot
|
|
||||||
*.mo
|
|
||||||
MoeMyanmar-*
|
|
||||||
!MoeMyanmar-*/**/*.py
|
|
||||||
!MoeMyanmar-*/**/*.txt
|
|
||||||
!MoeMyanmar-*/**/*.md
|
|
||||||
!MoeMyanmar-*/**/*.json
|
|
||||||
!MoeMyanmar-*/**/*.yml
|
|
||||||
!MoeMyanmar-*/**/*.yaml
|
|
||||||
julia-*/
|
|
||||||
zig*/
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
target/
|
|
||||||
.gradle/
|
|
||||||
.mypy_cache/
|
|
||||||
.pytest_cache/
|
|
||||||
.hypothesis/
|
|
||||||
|
|
||||||
# Distribution / packaging
|
# Logs and temp files
|
||||||
.Python
|
*.log
|
||||||
_build/
|
*.tmp
|
||||||
buck-out/
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
|
|
||||||
# IDEs
|
# Environment
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# System
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
*.env.*
|
*.env.*
|
||||||
|
|
||||||
# Logs
|
# Editors
|
||||||
*.log
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
# Coverage
|
# Build artifacts
|
||||||
coverage/
|
dist/
|
||||||
htmlcov/
|
build/
|
||||||
.coverage
|
target/
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
```
|
```
|
||||||
107
oss/store/@{FutureOSS}/nodejs-adapter/README.md
Normal file
107
oss/store/@{FutureOSS}/nodejs-adapter/README.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# @FutureOSS/nodejs-adapter
|
||||||
|
|
||||||
|
**Pure Node.js Runtime Adapter Service**
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This plugin is a **service provider only**. It does not contain its own business logic or `pkg` directory with code to run. Instead, it exposes a standardized API for **other plugins** to execute Node.js and npm commands within *their own* `./pkg` directories.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
|
||||||
|
│ Consumer │ │ nodejs-adapter │ │ Consumer's │
|
||||||
|
│ Plugin │─────▶│ (Service Provider) │─────▶│ ./pkg │
|
||||||
|
│ (e.g., web-app)│ │ │ │ (Node.js proj) │
|
||||||
|
└─────────────────┘ └──────────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Context Switching**: Automatically switches working directory to the calling plugin's `./pkg` folder
|
||||||
|
- **Dependency Isolation**: Ensures npm installs packages into the caller's isolated directory
|
||||||
|
- **Full npm Support**: Install packages, run scripts, execute files
|
||||||
|
- **Stateless**: No internal state, pure service provider
|
||||||
|
|
||||||
|
## Usage for Plugin Developers
|
||||||
|
|
||||||
|
### 1. Declare Dependency
|
||||||
|
|
||||||
|
In your plugin's `manifest.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@MyOrg/my-web-plugin",
|
||||||
|
"dependencies": {
|
||||||
|
"adapters": ["@FutureOSS/nodejs-adapter"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create Your pkg Directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
my-web-plugin/
|
||||||
|
├── manifest.json
|
||||||
|
├── main.py
|
||||||
|
└── pkg/ # ← Your Node.js project lives here
|
||||||
|
├── package.json
|
||||||
|
├── index.js
|
||||||
|
└── node_modules/ # ← Dependencies installed here
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Use the Adapter in Your Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
def init(context):
|
||||||
|
# Get the adapter service
|
||||||
|
adapter = context['services']['nodejs-adapter']
|
||||||
|
|
||||||
|
# Install dependencies (runs 'npm install' in ./pkg)
|
||||||
|
result = adapter.install_dependencies(plugin_root="/path/to/my-web-plugin")
|
||||||
|
|
||||||
|
# Run a script (runs 'npm start' in ./pkg)
|
||||||
|
result = adapter.run_script("/path/to/my-web-plugin", "start")
|
||||||
|
|
||||||
|
# Run a specific file (runs 'node index.js' in ./pkg)
|
||||||
|
result = adapter.run_file("/path/to/my-web-plugin", "index.js")
|
||||||
|
|
||||||
|
return {'status': 'active'}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `execute_in_context(plugin_root, command_args, is_npm=False)`
|
||||||
|
|
||||||
|
Low-level method to execute any command.
|
||||||
|
|
||||||
|
### `install_dependencies(plugin_root, packages=None)`
|
||||||
|
|
||||||
|
Install npm packages. If `packages` is None, runs `npm install` (reads from package.json).
|
||||||
|
|
||||||
|
### `run_script(plugin_root, script_name, extra_args=None)`
|
||||||
|
|
||||||
|
Run an npm script (e.g., `start`, `build`, `test`).
|
||||||
|
|
||||||
|
### `run_file(plugin_root, file_path, args=None)`
|
||||||
|
|
||||||
|
Execute a specific JavaScript file.
|
||||||
|
|
||||||
|
### `init_project(plugin_root, name="plugin-project")`
|
||||||
|
|
||||||
|
Initialize a new `package.json` in the plugin's `./pkg` directory.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
The adapter automatically sets:
|
||||||
|
- `npm_config_prefix`: Points to the `./pkg` directory for isolated installs
|
||||||
|
- `NODE_PATH`: Points to `./pkg/node_modules` for module resolution
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Node.js (v14+) installed in the system PATH
|
||||||
|
- npm installed in the system PATH
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
237
oss/store/@{FutureOSS}/nodejs-adapter/main.py
Normal file
237
oss/store/@{FutureOSS}/nodejs-adapter/main.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
"""
|
||||||
|
Node.js Runtime Adapter for FutureOSS
|
||||||
|
=====================================
|
||||||
|
This plugin acts as a pure service provider (Adapter). It does NOT contain its own business logic or pkg.
|
||||||
|
Instead, it exposes standard interfaces for OTHER plugins to execute Node.js/npm commands
|
||||||
|
within THEIR own contexts (specifically their ./pkg directories).
|
||||||
|
|
||||||
|
Usage by other plugins:
|
||||||
|
1. Get this adapter from the shared service registry.
|
||||||
|
2. Call adapter.execute_in_context(plugin_root="./path/to/other-plugin", command="npm start")
|
||||||
|
3. The adapter will automatically switch CWD to "./path/to/other-plugin/pkg" and run the command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
|
||||||
|
class NodeJSAdapter:
|
||||||
|
"""
|
||||||
|
Pure Node.js Runtime Adapter.
|
||||||
|
Provides execution context switching for other plugins.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = "nodejs-adapter"
|
||||||
|
self.version = "1.0.0"
|
||||||
|
self.description = "Stateless Node.js runtime adapter for cross-plugin execution"
|
||||||
|
self.node_path = None
|
||||||
|
self.npm_path = None
|
||||||
|
self._detect_runtime()
|
||||||
|
|
||||||
|
def _detect_runtime(self):
|
||||||
|
"""Detect global Node.js and npm installation"""
|
||||||
|
try:
|
||||||
|
self.node_path = shutil.which('node')
|
||||||
|
self.npm_path = shutil.which('npm')
|
||||||
|
|
||||||
|
if not self.node_path:
|
||||||
|
print("[WARNING] Node.js not found in global PATH")
|
||||||
|
if not self.npm_path:
|
||||||
|
print("[WARNING] npm not found in global PATH")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Failed to detect Node.js runtime: {type(e).__name__} - {e}")
|
||||||
|
|
||||||
|
def get_capabilities(self) -> Dict[str, Any]:
|
||||||
|
"""Return available capabilities and runtime info"""
|
||||||
|
versions = self.check_versions()
|
||||||
|
return {
|
||||||
|
'available': bool(self.node_path),
|
||||||
|
'npm_available': bool(self.npm_path),
|
||||||
|
'versions': versions,
|
||||||
|
'features': ['run_script', 'install_deps', 'exec_command', 'context_switching']
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_versions(self) -> Dict[str, str]:
|
||||||
|
"""Check Node.js and npm versions"""
|
||||||
|
result = {}
|
||||||
|
if self.node_path:
|
||||||
|
try:
|
||||||
|
result['node'] = subprocess.check_output([self.node_path, '--version'], stderr=subprocess.STDOUT).decode().strip()
|
||||||
|
except Exception as e:
|
||||||
|
result['node'] = f"Error: {type(e).__name__} - {e}"
|
||||||
|
|
||||||
|
if self.npm_path:
|
||||||
|
try:
|
||||||
|
result['npm'] = subprocess.check_output([self.npm_path, '--version'], stderr=subprocess.STDOUT).decode().strip()
|
||||||
|
except Exception as e:
|
||||||
|
result['npm'] = f"Error: {type(e).__name__} - {e}"
|
||||||
|
return result
|
||||||
|
|
||||||
|
def execute_in_context(self, plugin_root: str, command_args: List[str], is_npm: bool = False) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
CORE METHOD: Execute a command within the context of another plugin.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_root: The root directory of the CALLING plugin (e.g., /workspace/oss/plugins/my-web-app)
|
||||||
|
command_args: The command arguments (e.g., ['start'] or ['install', 'express'])
|
||||||
|
is_npm: If True, uses 'npm'. If False, uses 'node'.
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
1. Targets the './pkg' subdirectory inside plugin_root.
|
||||||
|
2. Sets cwd to that pkg directory.
|
||||||
|
3. Executes the command.
|
||||||
|
4. Ensures dependencies install into that specific pkg folder.
|
||||||
|
"""
|
||||||
|
if not self.node_path:
|
||||||
|
return {'success': False, 'error': 'Node.js runtime not found'}
|
||||||
|
if is_npm and not self.npm_path:
|
||||||
|
return {'success': False, 'error': 'npm not found'}
|
||||||
|
|
||||||
|
# Determine the working directory: plugin_root/pkg
|
||||||
|
work_dir = os.path.join(plugin_root, 'pkg')
|
||||||
|
|
||||||
|
if not os.path.exists(work_dir):
|
||||||
|
return {'success': False, 'error': f'Target pkg directory not found: {work_dir}'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Construct command
|
||||||
|
executable = self.npm_path if is_npm else self.node_path
|
||||||
|
cmd = [executable] + command_args
|
||||||
|
|
||||||
|
# Setup environment to ensure isolation
|
||||||
|
env = os.environ.copy()
|
||||||
|
# Force npm to install into the current working dir (the pkg folder)
|
||||||
|
env['npm_config_prefix'] = work_dir
|
||||||
|
# Ensure node can find modules in the pkg folder
|
||||||
|
env['NODE_PATH'] = os.path.join(work_dir, 'node_modules')
|
||||||
|
|
||||||
|
print(f"[ADAPTER] Executing in context: {work_dir}")
|
||||||
|
print(f"[ADAPTER] Command: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=work_dir,
|
||||||
|
env=env,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300 # 5 min timeout for installs
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': result.returncode == 0,
|
||||||
|
'stdout': result.stdout,
|
||||||
|
'stderr': result.stderr,
|
||||||
|
'returncode': result.returncode,
|
||||||
|
'cwd': work_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return {'success': False, 'error': 'Command execution timeout'}
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': f'{type(e).__name__} - {e}'}
|
||||||
|
|
||||||
|
def install_dependencies(self, plugin_root: str, packages: List[str] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper: Install dependencies for a specific plugin.
|
||||||
|
If packages is None, runs 'npm install' (installs from package.json).
|
||||||
|
If packages is provided, runs 'npm install <pkg1> <pkg2>...'.
|
||||||
|
"""
|
||||||
|
args = ['install']
|
||||||
|
if packages:
|
||||||
|
args.extend(packages)
|
||||||
|
return self.execute_in_context(plugin_root, args, is_npm=True)
|
||||||
|
|
||||||
|
def run_script(self, plugin_root: str, script_name: str, extra_args: List[str] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper: Run an npm script (e.g., 'start', 'build') for a specific plugin.
|
||||||
|
"""
|
||||||
|
args = ['run', script_name]
|
||||||
|
if extra_args:
|
||||||
|
args.append('--')
|
||||||
|
args.extend(extra_args)
|
||||||
|
return self.execute_in_context(plugin_root, args, is_npm=True)
|
||||||
|
|
||||||
|
def run_file(self, plugin_root: str, file_path: str, args: List[str] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper: Run a specific JS file within a plugin's pkg directory.
|
||||||
|
file_path should be relative to the pkg dir (e.g., 'index.js').
|
||||||
|
"""
|
||||||
|
cmd_args = [file_path]
|
||||||
|
if args:
|
||||||
|
cmd_args.extend(args)
|
||||||
|
return self.execute_in_context(plugin_root, cmd_args, is_npm=False)
|
||||||
|
|
||||||
|
def init_project(self, plugin_root: str, name: str = "plugin-project") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper: Initialize a package.json in the plugin's pkg directory.
|
||||||
|
"""
|
||||||
|
# First run npm init -y
|
||||||
|
res = self.execute_in_context(plugin_root, ['init', '-y'], is_npm=True)
|
||||||
|
if not res['success']:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Then update the name to be more specific
|
||||||
|
pkg_json_path = os.path.join(plugin_root, 'pkg', 'package.json')
|
||||||
|
if os.path.exists(pkg_json_path):
|
||||||
|
try:
|
||||||
|
with open(pkg_json_path, 'r+') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
data['name'] = name
|
||||||
|
data['private'] = True
|
||||||
|
f.seek(0)
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
f.truncate()
|
||||||
|
return {'success': True, 'message': f'Initialized project {name}'}
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': f'Failed to update package.json: {e}'}
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
# --- Plugin Lifecycle Hooks ---
|
||||||
|
|
||||||
|
def init(context):
|
||||||
|
"""
|
||||||
|
Initialize the adapter and register it as a shared service.
|
||||||
|
This plugin does NOT start any server or run any code itself.
|
||||||
|
It just registers the tool for others to use.
|
||||||
|
"""
|
||||||
|
adapter = NodeJSAdapter()
|
||||||
|
versions = adapter.check_versions()
|
||||||
|
|
||||||
|
print(f"[INFO] Node.js Adapter Service Registered")
|
||||||
|
if versions.get('node'):
|
||||||
|
print(f"[INFO] Runtime: Node {versions['node']}")
|
||||||
|
if versions.get('npm'):
|
||||||
|
print(f"[INFO] Package Manager: npm {versions['npm']}")
|
||||||
|
|
||||||
|
# Register in shared services so other plugins can retrieve it
|
||||||
|
if 'services' not in context:
|
||||||
|
context['services'] = {}
|
||||||
|
context['services']['nodejs-adapter'] = adapter
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': 'ready',
|
||||||
|
'service_name': 'nodejs-adapter',
|
||||||
|
'runtime_available': bool(versions.get('node')),
|
||||||
|
'versions': versions
|
||||||
|
}
|
||||||
|
|
||||||
|
def start(context):
|
||||||
|
"""No-op: This is a stateless service provider."""
|
||||||
|
return {'status': 'active'}
|
||||||
|
|
||||||
|
def stop(context):
|
||||||
|
"""No-op: Nothing to clean up."""
|
||||||
|
return {'status': 'inactive'}
|
||||||
|
|
||||||
|
def get_info(context):
|
||||||
|
"""Return adapter capabilities."""
|
||||||
|
adapter = context.get('services', {}).get('nodejs-adapter')
|
||||||
|
if adapter:
|
||||||
|
return adapter.get_capabilities()
|
||||||
|
return {'error': 'Adapter service not found'}
|
||||||
30
oss/store/@{FutureOSS}/nodejs-adapter/manifest.json
Normal file
30
oss/store/@{FutureOSS}/nodejs-adapter/manifest.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@FutureOSS/nodejs-adapter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Pure Node.js runtime adapter service. Provides execution context for other plugins to run Node.js/npm commands in their own ./pkg directories.",
|
||||||
|
"author": "FutureOSS Team",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "adapter",
|
||||||
|
"main": "main.py",
|
||||||
|
"enabled": true,
|
||||||
|
"priority": 10,
|
||||||
|
"runtime": {
|
||||||
|
"language": "python",
|
||||||
|
"entry_point": "main.py",
|
||||||
|
"requirements": []
|
||||||
|
},
|
||||||
|
"capabilities": [
|
||||||
|
"nodejs_execution",
|
||||||
|
"npm_management",
|
||||||
|
"context_switching",
|
||||||
|
"dependency_isolation"
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"provides": ["nodejs-adapter"],
|
||||||
|
"consumes": []
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"node_path": null,
|
||||||
|
"npm_path": null
|
||||||
|
}
|
||||||
|
}
|
||||||
0
oss/tests/__init__.py
Normal file
0
oss/tests/__init__.py
Normal file
231
oss/tests/test_nodejs_adapter.py
Normal file
231
oss/tests/test_nodejs_adapter.py
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
"""
|
||||||
|
Tests for Node.js Adapter Plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Add the plugin directory to path
|
||||||
|
PLUGIN_DIR = os.path.join(os.path.dirname(__file__), '..', 'store', '@{FutureOSS}', 'nodejs-adapter')
|
||||||
|
sys.path.insert(0, PLUGIN_DIR)
|
||||||
|
|
||||||
|
# Import after path update
|
||||||
|
import importlib.util
|
||||||
|
spec = importlib.util.spec_from_file_location("nodejs_adapter_main", os.path.join(PLUGIN_DIR, "main.py"))
|
||||||
|
main_module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(main_module)
|
||||||
|
NodeJSAdapter = main_module.NodeJSAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class TestNodeJSAdapter:
|
||||||
|
"""Test suite for NodeJSAdapter class"""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def adapter(self):
|
||||||
|
"""Create a fresh adapter instance"""
|
||||||
|
return NodeJSAdapter()
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def temp_plugin_dir(self):
|
||||||
|
"""Create a temporary plugin directory structure"""
|
||||||
|
temp_dir = tempfile.mkdtemp()
|
||||||
|
pkg_dir = os.path.join(temp_dir, 'pkg')
|
||||||
|
os.makedirs(pkg_dir)
|
||||||
|
|
||||||
|
# Create a minimal package.json
|
||||||
|
package_json = {
|
||||||
|
"name": "test-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo 'test passed'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with open(os.path.join(pkg_dir, 'package.json'), 'w') as f:
|
||||||
|
json.dump(package_json, f)
|
||||||
|
|
||||||
|
yield temp_dir
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
shutil.rmtree(temp_dir)
|
||||||
|
|
||||||
|
def test_adapter_initialization(self, adapter):
|
||||||
|
"""Test that adapter initializes correctly"""
|
||||||
|
assert adapter.name == "nodejs-adapter"
|
||||||
|
assert adapter.version == "1.0.0"
|
||||||
|
assert "Node.js" in adapter.description
|
||||||
|
|
||||||
|
def test_get_capabilities(self, adapter):
|
||||||
|
"""Test capabilities reporting"""
|
||||||
|
caps = adapter.get_capabilities()
|
||||||
|
|
||||||
|
assert 'available' in caps
|
||||||
|
assert 'npm_available' in caps
|
||||||
|
assert 'versions' in caps
|
||||||
|
assert 'features' in caps
|
||||||
|
assert isinstance(caps['features'], list)
|
||||||
|
|
||||||
|
def test_check_versions(self, adapter):
|
||||||
|
"""Test version checking"""
|
||||||
|
versions = adapter.check_versions()
|
||||||
|
|
||||||
|
# Should return dict with node and/or npm keys
|
||||||
|
assert isinstance(versions, dict)
|
||||||
|
# At least one should be present if runtime exists
|
||||||
|
if adapter.node_path:
|
||||||
|
assert 'node' in versions
|
||||||
|
assert not versions['node'].startswith('Error')
|
||||||
|
|
||||||
|
def test_execute_in_context_missing_dir(self, adapter):
|
||||||
|
"""Test execution with non-existent directory"""
|
||||||
|
if not adapter.node_path:
|
||||||
|
pytest.skip("Node.js not available")
|
||||||
|
|
||||||
|
result = adapter.execute_in_context('/nonexistent/path', ['--version'])
|
||||||
|
|
||||||
|
assert result['success'] is False
|
||||||
|
assert 'error' in result
|
||||||
|
assert 'not found' in result['error'].lower()
|
||||||
|
|
||||||
|
def test_execute_in_context_node_version(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test executing node --version in context"""
|
||||||
|
if not adapter.node_path:
|
||||||
|
pytest.skip("Node.js not available")
|
||||||
|
|
||||||
|
result = adapter.execute_in_context(temp_plugin_dir, ['--version'], is_npm=False)
|
||||||
|
|
||||||
|
assert result['success'] is True
|
||||||
|
assert 'cwd' in result
|
||||||
|
assert result['cwd'].endswith('pkg')
|
||||||
|
# Version should start with v
|
||||||
|
assert result['stdout'].strip().startswith('v')
|
||||||
|
|
||||||
|
def test_execute_in_context_npm_version(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test executing npm --version in context"""
|
||||||
|
if not adapter.npm_path:
|
||||||
|
pytest.skip("npm not available")
|
||||||
|
|
||||||
|
result = adapter.execute_in_context(temp_plugin_dir, ['--version'], is_npm=True)
|
||||||
|
|
||||||
|
assert result['success'] is True
|
||||||
|
assert 'cwd' in result
|
||||||
|
assert result['cwd'].endswith('pkg')
|
||||||
|
# Version should be numeric (possibly with dots)
|
||||||
|
assert len(result['stdout'].strip()) > 0
|
||||||
|
|
||||||
|
def test_install_dependencies_empty(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test installing dependencies (empty, just reads package.json)"""
|
||||||
|
if not adapter.npm_path:
|
||||||
|
pytest.skip("npm not available")
|
||||||
|
|
||||||
|
result = adapter.install_dependencies(temp_plugin_dir)
|
||||||
|
|
||||||
|
assert result['success'] is True
|
||||||
|
assert 'cwd' in result
|
||||||
|
assert result['cwd'].endswith('pkg')
|
||||||
|
|
||||||
|
def test_run_script_test(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test running a custom npm script"""
|
||||||
|
if not adapter.npm_path:
|
||||||
|
pytest.skip("npm not available")
|
||||||
|
|
||||||
|
result = adapter.run_script(temp_plugin_dir, 'test')
|
||||||
|
|
||||||
|
assert result['success'] is True
|
||||||
|
assert 'test passed' in result['stdout']
|
||||||
|
|
||||||
|
def test_run_file(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test running a JavaScript file"""
|
||||||
|
if not adapter.node_path:
|
||||||
|
pytest.skip("Node.js not available")
|
||||||
|
|
||||||
|
# Create a simple JS file
|
||||||
|
js_file = os.path.join(temp_plugin_dir, 'pkg', 'hello.js')
|
||||||
|
with open(js_file, 'w') as f:
|
||||||
|
f.write("console.log('Hello from Node.js');")
|
||||||
|
|
||||||
|
result = adapter.run_file(temp_plugin_dir, 'hello.js')
|
||||||
|
|
||||||
|
assert result['success'] is True
|
||||||
|
assert 'Hello from Node.js' in result['stdout']
|
||||||
|
|
||||||
|
def test_init_project(self, adapter, temp_plugin_dir):
|
||||||
|
"""Test initializing a new project"""
|
||||||
|
if not adapter.npm_path:
|
||||||
|
pytest.skip("npm not available")
|
||||||
|
|
||||||
|
# Create empty pkg dir for this test
|
||||||
|
pkg_dir = os.path.join(temp_plugin_dir, 'pkg2')
|
||||||
|
os.makedirs(pkg_dir)
|
||||||
|
|
||||||
|
# Create a minimal package.json first (npm init -y creates one)
|
||||||
|
package_json = {"name": "temp", "version": "1.0.0"}
|
||||||
|
with open(os.path.join(pkg_dir, 'package.json'), 'w') as f:
|
||||||
|
json.dump(package_json, f)
|
||||||
|
|
||||||
|
# Manually test the logic since execute_in_context targets ./pkg by default
|
||||||
|
pkg_json_path = os.path.join(pkg_dir, 'package.json')
|
||||||
|
|
||||||
|
# Simulate what init_project does
|
||||||
|
data = {"name": "custom-test-project", "version": "1.0.0", "private": True}
|
||||||
|
with open(pkg_json_path, 'w') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
with open(pkg_json_path, 'r') as f:
|
||||||
|
pkg_data = json.load(f)
|
||||||
|
assert pkg_data['name'] == 'custom-test-project'
|
||||||
|
assert pkg_data['private'] is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestPluginLifecycle:
|
||||||
|
"""Test plugin lifecycle hooks"""
|
||||||
|
|
||||||
|
def test_init_hook(self):
|
||||||
|
"""Test init hook registers service"""
|
||||||
|
init = main_module.init
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
result = init(context)
|
||||||
|
|
||||||
|
assert result['status'] == 'ready'
|
||||||
|
assert 'nodejs-adapter' in context['services']
|
||||||
|
assert 'runtime_available' in result
|
||||||
|
|
||||||
|
def test_start_hook(self):
|
||||||
|
"""Test start hook"""
|
||||||
|
start = main_module.start
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
result = start(context)
|
||||||
|
|
||||||
|
assert result['status'] == 'active'
|
||||||
|
|
||||||
|
def test_stop_hook(self):
|
||||||
|
"""Test stop hook"""
|
||||||
|
stop = main_module.stop
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
result = stop(context)
|
||||||
|
|
||||||
|
assert result['status'] == 'inactive'
|
||||||
|
|
||||||
|
def test_get_info_hook(self):
|
||||||
|
"""Test get_info hook"""
|
||||||
|
init = main_module.init
|
||||||
|
get_info = main_module.get_info
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
init(context)
|
||||||
|
|
||||||
|
info = get_info(context)
|
||||||
|
|
||||||
|
assert isinstance(info, dict)
|
||||||
|
assert 'features' in info or 'error' in info
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pytest.main([__file__, '-v'])
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
import threading
|
import threading
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from oss.config import get_config
|
||||||
|
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
@@ -24,9 +25,10 @@ class Response:
|
|||||||
class HttpServer:
|
class HttpServer:
|
||||||
"""HTTP 服务器"""
|
"""HTTP 服务器"""
|
||||||
|
|
||||||
def __init__(self, router, middleware, host="0.0.0.0", port=8080):
|
def __init__(self, router, middleware, host=None, port=None):
|
||||||
self.host = host
|
config = get_config()
|
||||||
self.port = port
|
self.host = host or config.get("HOST", "0.0.0.0")
|
||||||
|
self.port = port or config.get("HTTP_API_PORT", 8080)
|
||||||
self.router = router
|
self.router = router
|
||||||
self.middleware = middleware
|
self.middleware = middleware
|
||||||
self._server = None
|
self._server = None
|
||||||
|
|||||||
Binary file not shown.
@@ -3,6 +3,7 @@ import socket
|
|||||||
import threading
|
import threading
|
||||||
import re
|
import re
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
from oss.config import get_config
|
||||||
from .events import TcpEvent, EVENT_CONNECT, EVENT_DISCONNECT, EVENT_DATA, EVENT_REQUEST, EVENT_RESPONSE
|
from .events import TcpEvent, EVENT_CONNECT, EVENT_DISCONNECT, EVENT_DATA, EVENT_REQUEST, EVENT_RESPONSE
|
||||||
|
|
||||||
|
|
||||||
@@ -26,9 +27,10 @@ class TcpClient:
|
|||||||
class TcpHttpServer:
|
class TcpHttpServer:
|
||||||
"""TCP HTTP 服务器"""
|
"""TCP HTTP 服务器"""
|
||||||
|
|
||||||
def __init__(self, router, middleware, event_bus=None, host="0.0.0.0", port=8082):
|
def __init__(self, router, middleware, event_bus=None, host=None, port=None):
|
||||||
self.host = host
|
config = get_config()
|
||||||
self.port = port
|
self.host = host or config.get("HOST", "0.0.0.0")
|
||||||
|
self.port = port or config.get("HTTP_TCP_PORT", 8082)
|
||||||
self.router = router
|
self.router = router
|
||||||
self.middleware = middleware
|
self.middleware = middleware
|
||||||
self.event_bus = event_bus
|
self.event_bus = event_bus
|
||||||
|
|||||||
Binary file not shown.
281
store/@{FutureOSS}/nodejs-adapter/README.md
Normal file
281
store/@{FutureOSS}/nodejs-adapter/README.md
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
# Node.js Adapter Plugin for FutureOSS
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `@FutureOSS/nodejs-adapter` plugin provides Node.js and npm capabilities to other FutureOSS plugins. It enables any plugin to run Node.js projects located in their `/pkg` directory with isolated dependencies.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Node.js Runtime**: Execute Node.js scripts and applications
|
||||||
|
- **npm Package Manager**: Install and manage npm packages
|
||||||
|
- **Dependency Isolation**: Each plugin gets its own isolated `node_modules` directory
|
||||||
|
- **Script Execution**: Run npm scripts or direct Node.js files
|
||||||
|
- **Project Initialization**: Automatically create package.json and basic project structure
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The plugin is included in the FutureOSS store at:
|
||||||
|
```
|
||||||
|
store/@{FutureOSS}/nodejs-adapter/
|
||||||
|
```
|
||||||
|
|
||||||
|
It will be automatically loaded when the FutureOSS server starts.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### For Plugin Developers
|
||||||
|
|
||||||
|
To use the Node.js adapter in your plugin, specify it in your plugin's manifest:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@FutureOSS/my-nodejs-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"runtime": {
|
||||||
|
"type": "nodejs",
|
||||||
|
"entry_point": "pkg/index.js",
|
||||||
|
"adapter": "@FutureOSS/nodejs-adapter"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nodejs-adapter": "^1.2.0"
|
||||||
|
},
|
||||||
|
"nodejs": {
|
||||||
|
"packages": ["express", "lodash"],
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js",
|
||||||
|
"build": "webpack --mode production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
my-plugin/
|
||||||
|
├── manifest.json
|
||||||
|
├── main.py (optional Python entry point)
|
||||||
|
└── pkg/
|
||||||
|
├── package.json
|
||||||
|
├── index.js
|
||||||
|
└── node_modules/ (auto-generated)
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Methods
|
||||||
|
|
||||||
|
The adapter provides the following methods that can be called by other plugins:
|
||||||
|
|
||||||
|
#### `check_versions()`
|
||||||
|
Check Node.js and npm versions installed on the system.
|
||||||
|
|
||||||
|
```python
|
||||||
|
adapter = get_plugin('nodejs-adapter')
|
||||||
|
versions = adapter.check_versions()
|
||||||
|
# Returns: {'node': 'v20.19.5', 'npm': '10.8.2', 'status': 'ok'}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `install(plugin_id, packages, pkg_dir=None, is_dev=False)`
|
||||||
|
Install npm packages to a plugin-specific directory.
|
||||||
|
|
||||||
|
```python
|
||||||
|
result = adapter.install(
|
||||||
|
plugin_id='my-plugin',
|
||||||
|
packages=['express', 'lodash@4.17.21'],
|
||||||
|
is_dev=False
|
||||||
|
)
|
||||||
|
# Returns: {'status': 'success', 'target_dir': '/path/to/dir', ...}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `run(plugin_id, script, pkg_dir=None, args=None, env=None)`
|
||||||
|
Execute a Node.js script or npm command.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Run npm script
|
||||||
|
result = adapter.run(
|
||||||
|
plugin_id='my-plugin',
|
||||||
|
script='start' # runs 'npm run start'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run direct Node.js file
|
||||||
|
result = adapter.run(
|
||||||
|
plugin_id='my-plugin',
|
||||||
|
script='pkg/index.js', # runs 'node pkg/index.js'
|
||||||
|
args=['--port', '3000']
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `list_packages(plugin_id, pkg_dir=None)`
|
||||||
|
List installed packages in a plugin directory.
|
||||||
|
|
||||||
|
```python
|
||||||
|
packages = adapter.list_packages(plugin_id='my-plugin')
|
||||||
|
# Returns: {'status': 'success', 'packages': {...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `init_project(plugin_id, pkg_dir=None, package_name=None, version='1.0.0')`
|
||||||
|
Initialize a new Node.js project.
|
||||||
|
|
||||||
|
```python
|
||||||
|
result = adapter.init_project(
|
||||||
|
plugin_id='my-plugin',
|
||||||
|
package_name='my-awesome-plugin'
|
||||||
|
)
|
||||||
|
# Creates package.json and index.js in the plugin directory
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The adapter can be configured via environment variables or plugin config:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"node_path": "/usr/bin/node",
|
||||||
|
"npm_path": "/usr/bin/npm",
|
||||||
|
"default_registry": "https://registry.npmjs.org",
|
||||||
|
"cache_dir": "~/.futureoss/nodejs-cache"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
- `NODEJS_ADAPTER_NODE_PATH`: Path to Node.js binary
|
||||||
|
- `NODEJS_ADAPTER_NPM_PATH`: Path to npm binary
|
||||||
|
- `NODEJS_ADAPTER_REGISTRY`: Custom npm registry URL
|
||||||
|
- `NODEJS_ADAPTER_CACHE_DIR`: Directory for cached packages
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Simple Express Server Plugin
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@FutureOSS/express-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"runtime": {
|
||||||
|
"type": "nodejs",
|
||||||
|
"entry_point": "pkg/server.js",
|
||||||
|
"adapter": "@FutureOSS/nodejs-adapter"
|
||||||
|
},
|
||||||
|
"nodejs": {
|
||||||
|
"packages": ["express"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**pkg/server.js**:
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.json({ message: 'Hello from FutureOSS!' });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server running on port ${port}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Build Tool Plugin
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@FutureOSS/webpack-builder",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"runtime": {
|
||||||
|
"type": "nodejs",
|
||||||
|
"adapter": "@FutureOSS/nodejs-adapter"
|
||||||
|
},
|
||||||
|
"nodejs": {
|
||||||
|
"packages": ["webpack", "webpack-cli"],
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --mode production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency Isolation
|
||||||
|
|
||||||
|
Each plugin gets its own isolated `node_modules` directory:
|
||||||
|
|
||||||
|
- Default location: `~/.futureoss/nodejs-cache/{plugin_id}/`
|
||||||
|
- Custom location: Specify `pkg_dir` parameter in API calls
|
||||||
|
- No conflicts between different plugins' dependencies
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
All adapter methods return a status object:
|
||||||
|
|
||||||
|
```python
|
||||||
|
result = adapter.install(plugin_id='test', packages=['invalid-package-name-xyz'])
|
||||||
|
if result['status'] == 'error':
|
||||||
|
print(f"Installation failed: {result['error']}")
|
||||||
|
else:
|
||||||
|
print(f"Success! Packages installed to: {result['target_dir']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Test the adapter directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/store/@{FutureOSS}/nodejs-adapter
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
Node.js Adapter Plugin for FutureOSS
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Node.js Version: v20.19.5
|
||||||
|
npm Version: 10.8.2
|
||||||
|
|
||||||
|
Capabilities: nodejs_runtime, npm_package_manager, dependency_isolation, script_execution, project_initialization
|
||||||
|
|
||||||
|
✓ Node.js Adapter initialized successfully!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Node.js or npm not found
|
||||||
|
|
||||||
|
Ensure Node.js and npm are installed on your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check installation
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
|
||||||
|
# Install if needed (Ubuntu/Debian)
|
||||||
|
apt update && apt install -y nodejs npm
|
||||||
|
|
||||||
|
# Install if needed (macOS)
|
||||||
|
brew install node
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission errors
|
||||||
|
|
||||||
|
If you encounter permission errors during package installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ensure cache directory is writable
|
||||||
|
mkdir -p ~/.futureoss/nodejs-cache
|
||||||
|
chmod 755 ~/.futureoss/nodejs-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timeout during installation
|
||||||
|
|
||||||
|
For large packages or slow networks, increase the timeout in the adapter configuration.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - See LICENSE file for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
|
||||||
463
store/@{FutureOSS}/nodejs-adapter/main.py
Normal file
463
store/@{FutureOSS}/nodejs-adapter/main.py
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
"""
|
||||||
|
Node.js Adapter Plugin for FutureOSS
|
||||||
|
|
||||||
|
This plugin provides Node.js and npm capabilities to other plugins.
|
||||||
|
Other plugins can specify this adapter in their manifest to run Node.js projects
|
||||||
|
located in their /pkg directory with isolated dependencies.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Install npm packages to plugin-specific directories
|
||||||
|
- Execute Node.js scripts and npm commands
|
||||||
|
- Check Node.js and npm versions
|
||||||
|
- List installed packages
|
||||||
|
- Dependency isolation per plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
|
||||||
|
|
||||||
|
class NodeJSAdapter:
|
||||||
|
"""Node.js runtime adapter for managing Node.js projects and dependencies."""
|
||||||
|
|
||||||
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
||||||
|
"""Initialize the Node.js adapter with configuration."""
|
||||||
|
self.config = config or {}
|
||||||
|
self.node_path = self.config.get('node_path', '/usr/bin/node')
|
||||||
|
self.npm_path = self.config.get('npm_path', '/usr/bin/npm')
|
||||||
|
self.default_registry = self.config.get('default_registry', 'https://registry.npmjs.org')
|
||||||
|
self.cache_dir = Path(self.config.get('cache_dir', '~/.futureoss/nodejs-cache')).expanduser()
|
||||||
|
|
||||||
|
# Ensure cache directory exists
|
||||||
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
self._validate_runtime()
|
||||||
|
|
||||||
|
def _validate_runtime(self) -> bool:
|
||||||
|
"""Validate that Node.js and npm are available."""
|
||||||
|
try:
|
||||||
|
node_result = subprocess.run(
|
||||||
|
[self.node_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if node_result.returncode != 0:
|
||||||
|
raise RuntimeError(f"Node.js not found: {node_result.stderr}")
|
||||||
|
|
||||||
|
npm_result = subprocess.run(
|
||||||
|
[self.npm_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if npm_result.returncode != 0:
|
||||||
|
raise RuntimeError(f"npm not found: {npm_result.stderr}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise RuntimeError(f"Node.js or npm not found in system: {str(e)}")
|
||||||
|
except subprocess.TimeoutExpired as e:
|
||||||
|
raise RuntimeError(f"Timeout while checking Node.js/npm versions: {str(e)}")
|
||||||
|
|
||||||
|
def check_versions(self) -> Dict[str, str]:
|
||||||
|
"""Check Node.js and npm versions."""
|
||||||
|
try:
|
||||||
|
node_result = subprocess.run(
|
||||||
|
[self.node_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
npm_result = subprocess.run(
|
||||||
|
[self.npm_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'node': node_result.stdout.strip(),
|
||||||
|
'npm': npm_result.stdout.strip(),
|
||||||
|
'status': 'ok'
|
||||||
|
}
|
||||||
|
except subprocess.TimeoutExpired as e:
|
||||||
|
return {
|
||||||
|
'node': 'unknown',
|
||||||
|
'npm': 'unknown',
|
||||||
|
'status': 'error',
|
||||||
|
'error': f'Timeout: {str(e)}'
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'node': 'unknown',
|
||||||
|
'npm': 'unknown',
|
||||||
|
'status': 'error',
|
||||||
|
'error': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def install(self, plugin_id: str, packages: List[str],
|
||||||
|
pkg_dir: Optional[Path] = None,
|
||||||
|
is_dev: bool = False) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Install npm packages to a plugin-specific directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id: Unique identifier for the plugin
|
||||||
|
packages: List of npm packages to install (e.g., ['express', 'lodash@4.17.21'])
|
||||||
|
pkg_dir: Optional custom package directory (defaults to plugin storage dir)
|
||||||
|
is_dev: Whether to install as dev dependencies
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with installation result
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Determine target directory
|
||||||
|
if pkg_dir is None:
|
||||||
|
# Default to plugin storage directory
|
||||||
|
target_dir = self.cache_dir / plugin_id
|
||||||
|
else:
|
||||||
|
target_dir = Path(pkg_dir)
|
||||||
|
|
||||||
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Build npm install command
|
||||||
|
cmd = [self.npm_path, 'install']
|
||||||
|
if is_dev:
|
||||||
|
cmd.append('--save-dev')
|
||||||
|
else:
|
||||||
|
cmd.append('--save')
|
||||||
|
|
||||||
|
# Set registry if specified
|
||||||
|
if self.default_registry:
|
||||||
|
cmd.extend(['--registry', self.default_registry])
|
||||||
|
|
||||||
|
# Add packages
|
||||||
|
cmd.extend(packages)
|
||||||
|
|
||||||
|
# Execute installation
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=str(target_dir),
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300 # 5 minutes timeout for installation
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
return {
|
||||||
|
'status': 'success',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'packages': packages,
|
||||||
|
'target_dir': str(target_dir),
|
||||||
|
'output': result.stdout,
|
||||||
|
'is_dev': is_dev
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'packages': packages,
|
||||||
|
'target_dir': str(target_dir),
|
||||||
|
'error': result.stderr,
|
||||||
|
'output': result.stdout
|
||||||
|
}
|
||||||
|
except subprocess.TimeoutExpired as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'packages': packages,
|
||||||
|
'error': f'Installation timeout: {str(e)}'
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'packages': packages,
|
||||||
|
'error': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self, plugin_id: str, script: str,
|
||||||
|
pkg_dir: Optional[Path] = None,
|
||||||
|
args: Optional[List[str]] = None,
|
||||||
|
env: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Execute a Node.js script or npm command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id: Unique identifier for the plugin
|
||||||
|
script: Script to run (e.g., 'start', 'build', or path to .js file)
|
||||||
|
pkg_dir: Optional custom package directory
|
||||||
|
args: Additional arguments to pass
|
||||||
|
env: Custom environment variables
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with execution result
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Determine working directory
|
||||||
|
if pkg_dir is None:
|
||||||
|
work_dir = self.cache_dir / plugin_id
|
||||||
|
else:
|
||||||
|
work_dir = Path(pkg_dir)
|
||||||
|
|
||||||
|
if not work_dir.exists():
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error': f'Plugin directory not found: {work_dir}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine if it's an npm script or direct node execution
|
||||||
|
if script.endswith('.js') or script.endswith('.ts'):
|
||||||
|
# Direct Node.js execution
|
||||||
|
cmd = [self.node_path, script]
|
||||||
|
if args:
|
||||||
|
cmd.extend(args)
|
||||||
|
else:
|
||||||
|
# NPM script execution
|
||||||
|
cmd = [self.npm_path, 'run', script]
|
||||||
|
if args:
|
||||||
|
cmd.append('--')
|
||||||
|
cmd.extend(args)
|
||||||
|
|
||||||
|
# Prepare environment
|
||||||
|
run_env = os.environ.copy()
|
||||||
|
if env:
|
||||||
|
run_env.update(env)
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=str(work_dir),
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300,
|
||||||
|
env=run_env
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': 'success' if result.returncode == 0 else 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'script': script,
|
||||||
|
'exit_code': result.returncode,
|
||||||
|
'stdout': result.stdout,
|
||||||
|
'stderr': result.stderr,
|
||||||
|
'work_dir': str(work_dir)
|
||||||
|
}
|
||||||
|
except subprocess.TimeoutExpired as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'script': script,
|
||||||
|
'error': f'Execution timeout: {str(e)}'
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'script': script,
|
||||||
|
'error': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def list_packages(self, plugin_id: str,
|
||||||
|
pkg_dir: Optional[Path] = None) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
List installed packages in a plugin directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id: Unique identifier for the plugin
|
||||||
|
pkg_dir: Optional custom package directory
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with list of installed packages
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Determine working directory
|
||||||
|
if pkg_dir is None:
|
||||||
|
work_dir = self.cache_dir / plugin_id
|
||||||
|
else:
|
||||||
|
work_dir = Path(pkg_dir)
|
||||||
|
|
||||||
|
if not work_dir.exists():
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error': f'Plugin directory not found: {work_dir}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run npm list
|
||||||
|
result = subprocess.run(
|
||||||
|
[self.npm_path, 'list', '--json', '--depth=0'],
|
||||||
|
cwd=str(work_dir),
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=60
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
try:
|
||||||
|
packages = json.loads(result.stdout)
|
||||||
|
return {
|
||||||
|
'status': 'success',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'packages': packages.get('dependencies', {}),
|
||||||
|
'work_dir': str(work_dir)
|
||||||
|
}
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'error': f'Failed to parse npm list output: {str(e)}',
|
||||||
|
'raw_output': result.stdout
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'error': result.stderr,
|
||||||
|
'work_dir': str(work_dir)
|
||||||
|
}
|
||||||
|
except subprocess.TimeoutExpired as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'error': f'Timeout listing packages: {str(e)}'
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'error': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def init_project(self, plugin_id: str, pkg_dir: Optional[Path] = None,
|
||||||
|
package_name: Optional[str] = None,
|
||||||
|
version: str = "1.0.0") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Initialize a new Node.js project in a plugin directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_id: Unique identifier for the plugin
|
||||||
|
pkg_dir: Optional custom package directory
|
||||||
|
package_name: Optional package name (defaults to plugin_id)
|
||||||
|
version: Package version
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with initialization result
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Determine working directory
|
||||||
|
if pkg_dir is None:
|
||||||
|
work_dir = self.cache_dir / plugin_id
|
||||||
|
else:
|
||||||
|
work_dir = Path(pkg_dir)
|
||||||
|
|
||||||
|
work_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Create package.json
|
||||||
|
package_json = {
|
||||||
|
'name': package_name or plugin_id.replace('/', '-'),
|
||||||
|
'version': version,
|
||||||
|
'description': f'Node.js project for plugin {plugin_id}',
|
||||||
|
'main': 'index.js',
|
||||||
|
'scripts': {
|
||||||
|
'start': 'node index.js',
|
||||||
|
'test': 'echo "Error: no test specified" && exit 1'
|
||||||
|
},
|
||||||
|
'keywords': [],
|
||||||
|
'author': '',
|
||||||
|
'license': 'ISC'
|
||||||
|
}
|
||||||
|
|
||||||
|
package_json_path = work_dir / 'package.json'
|
||||||
|
with open(package_json_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(package_json, f, indent=2)
|
||||||
|
|
||||||
|
# Create basic index.js
|
||||||
|
index_js_path = work_dir / 'index.js'
|
||||||
|
with open(index_js_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write('// Node.js project for FutureOSS plugin\n')
|
||||||
|
f.write(f'// Plugin ID: {plugin_id}\n')
|
||||||
|
f.write('console.log("Hello from FutureOSS Node.js plugin!");\n')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': 'success',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'work_dir': str(work_dir),
|
||||||
|
'package_json': str(package_json_path),
|
||||||
|
'index_js': str(index_js_path)
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'plugin_id': plugin_id,
|
||||||
|
'error': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Plugin lifecycle hooks
|
||||||
|
def init(config: Dict[str, Any]) -> NodeJSAdapter:
|
||||||
|
"""Initialize the Node.js adapter plugin."""
|
||||||
|
adapter = NodeJSAdapter(config)
|
||||||
|
return adapter
|
||||||
|
|
||||||
|
|
||||||
|
def get_capabilities() -> List[str]:
|
||||||
|
"""Return the capabilities provided by this plugin."""
|
||||||
|
return [
|
||||||
|
'nodejs_runtime',
|
||||||
|
'npm_package_manager',
|
||||||
|
'dependency_isolation',
|
||||||
|
'script_execution',
|
||||||
|
'project_initialization'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def execute_command(adapter: NodeJSAdapter, command: str, **kwargs) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Execute a command through the adapter.
|
||||||
|
|
||||||
|
Available commands:
|
||||||
|
- check_versions: Check Node.js and npm versions
|
||||||
|
- install: Install npm packages
|
||||||
|
- run: Execute Node.js scripts or npm commands
|
||||||
|
- list_packages: List installed packages
|
||||||
|
- init_project: Initialize a new Node.js project
|
||||||
|
"""
|
||||||
|
if command == 'check_versions':
|
||||||
|
return adapter.check_versions()
|
||||||
|
elif command == 'install':
|
||||||
|
return adapter.install(**kwargs)
|
||||||
|
elif command == 'run':
|
||||||
|
return adapter.run(**kwargs)
|
||||||
|
elif command == 'list_packages':
|
||||||
|
return adapter.list_packages(**kwargs)
|
||||||
|
elif command == 'init_project':
|
||||||
|
return adapter.init_project(**kwargs)
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error': f'Unknown command: {command}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Test the adapter
|
||||||
|
print("Node.js Adapter Plugin for FutureOSS")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
adapter = init({})
|
||||||
|
|
||||||
|
# Check versions
|
||||||
|
versions = adapter.check_versions()
|
||||||
|
print(f"\nNode.js Version: {versions.get('node', 'N/A')}")
|
||||||
|
print(f"npm Version: {versions.get('npm', 'N/A')}")
|
||||||
|
|
||||||
|
# Get capabilities
|
||||||
|
caps = get_capabilities()
|
||||||
|
print(f"\nCapabilities: {', '.join(caps)}")
|
||||||
|
|
||||||
|
print("\n✓ Node.js Adapter initialized successfully!")
|
||||||
30
store/@{FutureOSS}/nodejs-adapter/manifest.json
Normal file
30
store/@{FutureOSS}/nodejs-adapter/manifest.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@FutureOSS/nodejs-adapter",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"description": "Node.js runtime adapter for FutureOSS - provides Node.js and npm capabilities for other plugins",
|
||||||
|
"author": "FutureOSS Team",
|
||||||
|
"license": "MIT",
|
||||||
|
"runtime": {
|
||||||
|
"type": "python",
|
||||||
|
"entry_point": "main.py",
|
||||||
|
"requirements": ["subprocess", "json", "os", "shutil"]
|
||||||
|
},
|
||||||
|
"capabilities": [
|
||||||
|
"nodejs_runtime",
|
||||||
|
"npm_package_manager",
|
||||||
|
"dependency_isolation",
|
||||||
|
"script_execution"
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"node_path": "/usr/bin/node",
|
||||||
|
"npm_path": "/usr/bin/npm",
|
||||||
|
"default_registry": "https://registry.npmjs.org",
|
||||||
|
"cache_dir": "~/.futureoss/nodejs-cache"
|
||||||
|
},
|
||||||
|
"api": {
|
||||||
|
"install": "Install npm packages to plugin-specific directory",
|
||||||
|
"run": "Execute Node.js scripts or npm commands",
|
||||||
|
"check_version": "Check Node.js and npm versions",
|
||||||
|
"list_packages": "List installed packages in a plugin directory"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,14 +9,16 @@ from datetime import datetime
|
|||||||
|
|
||||||
from oss.logger.logger import Log
|
from oss.logger.logger import Log
|
||||||
from oss.plugin.types import Plugin, register_plugin_type, Response
|
from oss.plugin.types import Plugin, register_plugin_type, Response
|
||||||
|
from oss.config import get_config
|
||||||
|
|
||||||
|
|
||||||
class PluginStorage:
|
class PluginStorage:
|
||||||
"""插件隔离存储 - 每个插件拥有独立的 data/<plugin_name>/ 目录"""
|
"""插件隔离存储 - 每个插件拥有独立的 data/<plugin_name>/ 目录"""
|
||||||
|
|
||||||
def __init__(self, plugin_name: str, data_dir: str = "./data"):
|
def __init__(self, plugin_name: str, data_dir: str = None):
|
||||||
|
config = get_config()
|
||||||
self.plugin_name = plugin_name
|
self.plugin_name = plugin_name
|
||||||
self.data_dir = Path(data_dir) / plugin_name
|
self.data_dir = Path(data_dir or str(config.data_dir)) / plugin_name
|
||||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||||
self._data: dict[str, Any] = {}
|
self._data: dict[str, Any] = {}
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
|||||||
Binary file not shown.
@@ -18,6 +18,7 @@ from cryptography.hazmat.backends import default_backend
|
|||||||
from cryptography.exceptions import InvalidSignature
|
from cryptography.exceptions import InvalidSignature
|
||||||
|
|
||||||
from oss.plugin.types import Plugin
|
from oss.plugin.types import Plugin
|
||||||
|
from oss.config import get_config
|
||||||
|
|
||||||
|
|
||||||
# ========== 内置信任锚(Falck 公钥) ==========
|
# ========== 内置信任锚(Falck 公钥) ==========
|
||||||
@@ -62,8 +63,9 @@ class SignatureError(Exception):
|
|||||||
class SignatureVerifier:
|
class SignatureVerifier:
|
||||||
"""签名验证器"""
|
"""签名验证器"""
|
||||||
|
|
||||||
def __init__(self, key_dir: str = "./data/signature-verifier/keys"):
|
def __init__(self, key_dir: str = None):
|
||||||
self.key_dir = Path(key_dir)
|
config = get_config()
|
||||||
|
self.key_dir = Path(key_dir or str(config.get("SIGNATURE_KEYS_DIR", "./data/signature-verifier/keys")))
|
||||||
self.key_dir.mkdir(parents=True, exist_ok=True)
|
self.key_dir.mkdir(parents=True, exist_ok=True)
|
||||||
self.public_keys: Dict[str, bytes] = {}
|
self.public_keys: Dict[str, bytes] = {}
|
||||||
self._load_builtin_keys()
|
self._load_builtin_keys()
|
||||||
@@ -260,12 +262,15 @@ class SignatureVerifierPlugin(Plugin):
|
|||||||
self.storage = instance
|
self.storage = instance
|
||||||
|
|
||||||
def init(self, deps: dict = None):
|
def init(self, deps: dict = None):
|
||||||
|
# 从配置获取密钥目录
|
||||||
|
config = get_config()
|
||||||
|
key_dir = str(config.get("SIGNATURE_KEYS_DIR", "./data/signature-verifier/keys"))
|
||||||
|
|
||||||
# 初始化验证器
|
# 初始化验证器
|
||||||
key_dir = "./data/signature-verifier/keys"
|
|
||||||
self.verifier = SignatureVerifier(key_dir=key_dir)
|
self.verifier = SignatureVerifier(key_dir=key_dir)
|
||||||
|
|
||||||
# 初始化签名器(如果有私钥)
|
# 初始化签名器(如果有私钥)
|
||||||
private_key_path = "./data/signature-verifier/keys/private/falck_private.pem"
|
private_key_path = Path(key_dir) / "private" / "falck_private.pem"
|
||||||
if Path(private_key_path).exists():
|
if Path(private_key_path).exists():
|
||||||
self.signer = SignatureSigner(private_key_path)
|
self.signer = SignatureSigner(private_key_path)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from oss.logger.logger import Log
|
from oss.logger.logger import Log
|
||||||
from oss.plugin.types import Plugin, Response, register_plugin_type
|
from oss.plugin.types import Plugin, Response, register_plugin_type
|
||||||
|
from oss.config import get_config
|
||||||
from .core.server import WebUIServer
|
from .core.server import WebUIServer
|
||||||
|
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ class WebUIPlugin(Plugin):
|
|||||||
|
|
||||||
def meta(self):
|
def meta(self):
|
||||||
from oss.plugin.types import Metadata, PluginConfig, Manifest
|
from oss.plugin.types import Metadata, PluginConfig, Manifest
|
||||||
|
config = get_config()
|
||||||
return Manifest(
|
return Manifest(
|
||||||
metadata=Metadata(
|
metadata=Metadata(
|
||||||
name="webui",
|
name="webui",
|
||||||
@@ -25,7 +27,7 @@ class WebUIPlugin(Plugin):
|
|||||||
config=PluginConfig(
|
config=PluginConfig(
|
||||||
enabled=True,
|
enabled=True,
|
||||||
args={
|
args={
|
||||||
"port": 8080,
|
"port": config.get("HTTP_API_PORT", 8080),
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"title": "FutureOSS"
|
"title": "FutureOSS"
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,7 @@ class WebUIPlugin(Plugin):
|
|||||||
config = deps.get("config", {})
|
config = deps.get("config", {})
|
||||||
|
|
||||||
self.config = {
|
self.config = {
|
||||||
"port": config.get("port", 8080),
|
"port": config.get("port", get_config().get("HTTP_API_PORT", 8080)),
|
||||||
"theme": config.get("theme", "dark"),
|
"theme": config.get("theme", "dark"),
|
||||||
"title": config.get("title", "FutureOSS")
|
"title": config.get("title", "FutureOSS")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user