diff --git a/Data/Agent/Roles/Borealis.WinRM.Localhost.psm1 b/Data/Agent/Roles/Borealis.WinRM.Localhost.psm1
new file mode 100644
index 0000000..4e29833
--- /dev/null
+++ b/Data/Agent/Roles/Borealis.WinRM.Localhost.psm1
@@ -0,0 +1,99 @@
+function Ensure-LocalhostWinRMHttps {
+ [CmdletBinding()]
+ param(
+ [string]$DnsName = $env:COMPUTERNAME
+ )
+
+ try {
+ Set-Service WinRM -StartupType Automatic -ErrorAction Stop
+ Start-Service WinRM -ErrorAction Stop
+ } catch {}
+
+ # Find or create a cert
+ try {
+ $cert = Get-ChildItem Cert:\LocalMachine\My |
+ Where-Object { $_.Subject -match "CN=$DnsName" -and $_.NotAfter -gt (Get-Date).AddMonths(1) } |
+ Sort-Object NotAfter -Descending | Select-Object -First 1
+ } catch { $cert = $null }
+ if (-not $cert) {
+ try {
+ $cert = New-SelfSignedCertificate -DnsName $DnsName -CertStoreLocation Cert:\LocalMachine\My -KeyLength 2048 -HashAlgorithm SHA256 -NotAfter (Get-Date).AddYears(3)
+ } catch { $cert = $null }
+ }
+ $thumb = if ($cert) { $cert.Thumbprint } else { '' }
+
+ # Create listener only if not present
+ try {
+ $listener = Get-WSManInstance -ResourceURI winrm/config/listener -Enumerate -ErrorAction SilentlyContinue |
+ Where-Object { $_.Transport -eq 'HTTPS' -and $_.Address -eq '127.0.0.1' -and $_.Port -eq '5986' }
+ } catch { $listener = $null }
+ if (-not $listener -and $thumb) {
+ $cmd = "winrm create winrm/config/Listener?Address=127.0.0.1+Transport=HTTPS @{Hostname=`"$DnsName`"; CertificateThumbprint=`"$thumb`"; Port=`"5986`"}"
+ cmd /c $cmd | Out-Null
+ }
+
+ # Harden auth and encryption
+ try { winrm set winrm/config/service/auth @{Basic="false"; Kerberos="true"; Negotiate="true"; CredSSP="false"} | Out-Null } catch {}
+ try { winrm set winrm/config/service @{AllowUnencrypted="false"} | Out-Null } catch {}
+}
+
+function Ensure-BorealisServiceUser {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory)][string]$UserName,
+ [Parameter(Mandatory)][string]$PlaintextPassword
+ )
+ $localName = $UserName -replace '^\.\\',''
+ $secure = ConvertTo-SecureString $PlaintextPassword -AsPlainText -Force
+ $u = Get-LocalUser -Name $localName -ErrorAction SilentlyContinue
+ if (-not $u) {
+ try {
+ New-LocalUser -Name $localName -Password $secure -PasswordNeverExpires:$true -AccountNeverExpires:$true | Out-Null
+ } catch {}
+ } else {
+ try { Set-LocalUser -Name $localName -Password $secure } catch {}
+ try { Enable-LocalUser -Name $localName } catch {}
+ }
+ try {
+ $admins = Get-LocalGroupMember -Group "Administrators" -ErrorAction SilentlyContinue
+ if (-not ($admins | Where-Object { $_.Name -match "\\$localName$" })) {
+ Add-LocalGroupMember -Group "Administrators" -Member $localName -ErrorAction SilentlyContinue
+ }
+ } catch {}
+}
+
+function Write-LocalInventory {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory)][string]$Path,
+ [Parameter(Mandatory)][string]$UserName,
+ [Parameter(Mandatory)][string]$Password,
+ [ValidateSet('ntlm','negotiate','basic')][string]$Transport = 'ntlm'
+ )
+@"
+[local]
+localhost
+
+[local:vars]
+ansible_connection=winrm
+ansible_host=127.0.0.1
+ansible_port=5986
+ansible_winrm_scheme=https
+ansible_winrm_transport=$Transport
+ansible_user=$UserName
+ansible_password=$Password
+ansible_winrm_server_cert_validation=ignore
+"@ | Set-Content -Path $Path -Encoding UTF8
+
+ # Lock down ACL to SYSTEM when running as SYSTEM
+ try {
+ $acl = New-Object System.Security.AccessControl.FileSecurity
+ $sid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-18")
+ $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($sid,"FullControl","ContainerInherit,ObjectInherit","None","Allow")
+ $acl.SetOwner($sid); $acl.SetAccessRuleProtection($true,$false); $acl.AddAccessRule($rule)
+ Set-Acl -Path $Path -AclObject $acl
+ } catch {}
+}
+
+Export-ModuleMember -Function Ensure-LocalhostWinRMHttps,Ensure-BorealisServiceUser,Write-LocalInventory
+
diff --git a/Data/Agent/Roles/role_PlaybookExec_SYSTEM.py b/Data/Agent/Roles/role_PlaybookExec_SYSTEM.py
index 5d141b4..8cd6ce5 100644
--- a/Data/Agent/Roles/role_PlaybookExec_SYSTEM.py
+++ b/Data/Agent/Roles/role_PlaybookExec_SYSTEM.py
@@ -8,6 +8,11 @@ import json
import socket
import subprocess
+try:
+ import winrm # type: ignore
+except Exception:
+ winrm = None
+
ROLE_NAME = 'playbook_exec_system'
ROLE_CONTEXTS = ['system']
@@ -85,6 +90,7 @@ class Role:
def __init__(self, ctx):
self.ctx = ctx
self._runs = {} # run_id -> { proc, task, cancel }
+ self._svc_creds = None # cache per-process: {username, password}
def _log_local(self, msg: str, error: bool = False):
try:
@@ -106,6 +112,131 @@ class Role:
pass
return 'http://localhost:5000'
+ async def _fetch_service_creds(self) -> dict:
+ if self._svc_creds and isinstance(self._svc_creds, dict):
+ return self._svc_creds
+ try:
+ import aiohttp
+ url = self._server_base().rstrip('/') + '/api/agent/checkin'
+ payload = {
+ 'agent_id': self.ctx.agent_id,
+ 'hostname': socket.gethostname(),
+ 'username': '.\\svcBorealisAnsibleRunner',
+ }
+ timeout = aiohttp.ClientTimeout(total=15)
+ async with aiohttp.ClientSession(timeout=timeout) as sess:
+ async with sess.post(url, json=payload) as resp:
+ js = await resp.json()
+ u = (js or {}).get('username') or '.\\svcBorealisAnsibleRunner'
+ p = (js or {}).get('password') or ''
+ self._svc_creds = {'username': u, 'password': p}
+ return self._svc_creds
+ except Exception:
+ return {'username': '.\\svcBorealisAnsibleRunner', 'password': ''}
+
+ def _normalize_playbook_content(self, content: str) -> str:
+ try:
+ # Heuristic fixes to honor our WinRM localhost inventory:
+ # - Replace hosts: localhost -> hosts: local (group name used by inventory)
+ # - Remove explicit "connection: local" if present
+ lines = (content or '').splitlines()
+ out = []
+ for ln in lines:
+ s = ln.strip().lower()
+ if s.startswith('connection:') and 'local' in s:
+ continue
+ if s.startswith('hosts:') and ('localhost' in s or '127.0.0.1' in s):
+ indent = ln.split('h')[0]
+ out.append(f"{indent}hosts: local")
+ continue
+ out.append(ln)
+ return '\n'.join(out) + ('\n' if not content.endswith('\n') else '')
+ except Exception:
+ return content
+
+ async def _rotate_service_creds(self) -> dict:
+ try:
+ import aiohttp
+ url = self._server_base().rstrip('/') + '/api/agent/service-account/rotate'
+ payload = {
+ 'agent_id': self.ctx.agent_id,
+ 'reason': 'bad_credentials',
+ }
+ timeout = aiohttp.ClientTimeout(total=15)
+ async with aiohttp.ClientSession(timeout=timeout) as sess:
+ async with sess.post(url, json=payload) as resp:
+ js = await resp.json()
+ u = (js or {}).get('username') or '.\\svcBorealisAnsibleRunner'
+ p = (js or {}).get('password') or ''
+ self._svc_creds = {'username': u, 'password': p}
+ return self._svc_creds
+ except Exception:
+ return await self._fetch_service_creds()
+
+ def _ps_module_path(self) -> str:
+ # Place PS module under Roles so it's deployed with the agent
+ try:
+ here = os.path.abspath(os.path.dirname(__file__))
+ p = os.path.join(here, 'Borealis.WinRM.Localhost.psm1')
+ return p
+ except Exception:
+ return ''
+
+ def _ensure_winrm_and_user(self, username: str, password: str):
+ if os.name != 'nt':
+ return
+ mod = self._ps_module_path()
+ if not os.path.isfile(mod):
+ # best effort with inline commands
+ try:
+ subprocess.run(['powershell', '-NoProfile', '-Command', 'Set-Service WinRM -StartupType Automatic; Start-Service WinRM'], timeout=30)
+ except Exception:
+ pass
+ return
+ ps = f"""
+Import-Module -Name '{mod}' -Force
+Ensure-LocalhostWinRMHttps
+Ensure-BorealisServiceUser -UserName '{username}' -PlaintextPassword '{password}'
+"""
+ try:
+ subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', ps], timeout=90)
+ except Exception:
+ pass
+
+ def _write_winrm_inventory(self, base_dir: str, username: str, password: str) -> str:
+ inv_dir = os.path.join(base_dir, 'inventory')
+ os.makedirs(inv_dir, exist_ok=True)
+ hosts = os.path.join(inv_dir, 'hosts')
+ try:
+ content = (
+ "[local]\n" \
+ "localhost\n\n" \
+ "[local:vars]\n" \
+ "ansible_connection=winrm\n" \
+ "ansible_host=127.0.0.1\n" \
+ "ansible_port=5986\n" \
+ "ansible_winrm_scheme=https\n" \
+ "ansible_winrm_transport=ntlm\n" \
+ f"ansible_user={username}\n" \
+ f"ansible_password={password}\n" \
+ "ansible_winrm_server_cert_validation=ignore\n"
+ )
+ with open(hosts, 'w', encoding='utf-8', newline='\n') as fh:
+ fh.write(content)
+ except Exception:
+ pass
+ return hosts
+
+ def _winrm_preflight(self, username: str, password: str) -> bool:
+ if os.name != 'nt' or winrm is None:
+ return True
+ try:
+ s = winrm.Session('https://127.0.0.1:5986', auth=(username, password), transport='ntlm', server_cert_validation='ignore')
+ r = s.run_cmd('whoami')
+ return r.status_code == 0
+ except Exception:
+ return False
+
async def _post_recap(self, payload: dict):
try:
import aiohttp
@@ -138,9 +269,24 @@ class Role:
play_rel = 'playbook.yml'
play_abs = os.path.join(project, play_rel)
with open(play_abs, 'w', encoding='utf-8', newline='\n') as fh:
- fh.write(playbook_content or '')
- with open(os.path.join(inventory_dir, 'hosts'), 'w', encoding='utf-8', newline='\n') as fh:
- fh.write('localhost,\n')
+ fh.write(self._normalize_playbook_content(playbook_content or ''))
+ # WinRM service account credentials
+ creds = await self._fetch_service_creds()
+ user = creds.get('username') or '.\\svcBorealisAnsibleRunner'
+ pwd = creds.get('password') or ''
+ # Converge endpoint state (listener + user)
+ self._ensure_winrm_and_user(user, pwd)
+ # Preflight auth and auto-rotate if needed
+ pre_ok = self._winrm_preflight(user, pwd)
+ if not pre_ok:
+ # rotate and retry once
+ creds = await self._rotate_service_creds()
+ user = creds.get('username') or user
+ pwd = creds.get('password') or ''
+ self._ensure_winrm_and_user(user, pwd)
+ # Write inventory for winrm localhost
+ inv_file = self._write_winrm_inventory(pd, user, pwd)
+
# Set connection via envvars
with open(os.path.join(env_dir, 'envvars'), 'w', encoding='utf-8', newline='\n') as fh:
json.dump({ 'ANSIBLE_FORCE_COLOR': '0', 'ANSIBLE_STDOUT_CALLBACK': 'default' }, fh)
@@ -188,17 +334,25 @@ class Role:
except Exception:
return False
+ auth_failed = False
try:
- ansible_runner.interface.run(
+ r = ansible_runner.interface.run(
private_data_dir=pd,
playbook=play_rel,
- inventory=os.path.join(inventory_dir, 'hosts'),
+ inventory=inv_file,
quiet=True,
event_handler=_on_event,
cancel_callback=_cancel_cb,
extravars={}
)
status = 'Cancelled' if _cancel_cb() else 'Success'
+ try:
+ # Some auth failures bubble up in events only; inspect last few lines
+ tail = '\n'.join(lines[-50:]).lower()
+ if ('access is denied' in tail) or ('unauthorized' in tail) or ('cannot process the request' in tail):
+ auth_failed = True
+ except Exception:
+ pass
except Exception:
status = 'Failed'
@@ -231,6 +385,18 @@ class Role:
'recap_json': recap_json,
'finished_ts': int(time.time()),
})
+ # If authentication failed on first pass, rotate password and try once more
+ if auth_failed:
+ try:
+ newc = await self._rotate_service_creds()
+ user2 = newc.get('username') or user
+ pwd2 = newc.get('password') or ''
+ self._ensure_winrm_and_user(user2, pwd2)
+ # Recurse once with updated creds
+ await self._run_playbook_runner(run_id, playbook_content, playbook_name=playbook_name, activity_job_id=activity_job_id, connection=connection)
+ return True
+ except Exception:
+ pass
return True
async def _run_playbook(self, run_id: str, playbook_content: str, playbook_name: str = '', activity_job_id=None, connection: str = 'local'):
@@ -239,7 +405,7 @@ class Role:
os.makedirs(tmp_dir, exist_ok=True)
fd, path = tempfile.mkstemp(prefix='pb_', suffix='.yml', dir=tmp_dir, text=True)
with os.fdopen(fd, 'w', encoding='utf-8', newline='\n') as fh:
- fh.write(playbook_content or '')
+ fh.write(self._normalize_playbook_content(playbook_content or ''))
hostname = socket.gethostname()
agent_id = self.ctx.agent_id
@@ -256,19 +422,33 @@ class Role:
'started_ts': started,
})
+ # Prefer WinRM localhost via inventory when on Windows; otherwise fallback to provided connection
+ inv_file_cli = None
conn = (connection or 'local').strip().lower()
- if conn not in ('local', 'winrm', 'psrp'):
- conn = 'local'
- # Best-effort: if playbook uses ansible.windows, prefer psrp when connection left as local
- if conn == 'local':
+ if os.name == 'nt':
try:
- if 'ansible.windows' in (playbook_content or ''):
- conn = 'psrp'
+ creds = await self._fetch_service_creds()
+ user = creds.get('username') or '.\\svcBorealisAnsibleRunner'
+ pwd = creds.get('password') or ''
+ self._ensure_winrm_and_user(user, pwd)
+ if not self._winrm_preflight(user, pwd):
+ creds = await self._rotate_service_creds()
+ user = creds.get('username') or user
+ pwd = creds.get('password') or ''
+ self._ensure_winrm_and_user(user, pwd)
+ # Create temp inventory adjacent to playbook
+ inv_file_cli = self._write_winrm_inventory(os.path.dirname(path), user, pwd)
except Exception:
- pass
-
- cmd = [_ansible_playbook_cmd(), path, '-i', 'localhost,', '-c', conn]
- self._log_local(f"Launching ansible-playbook: conn={conn} cmd={' '.join(cmd)}")
+ inv_file_cli = None
+ # Build CLI; if inv_file_cli present, omit -c and use '-i invfile'
+ if inv_file_cli and os.path.isfile(inv_file_cli):
+ cmd = [_ansible_playbook_cmd(), path, '-i', inv_file_cli]
+ self._log_local(f"Launching ansible-playbook with WinRM inventory: {' '.join(cmd)}")
+ else:
+ if conn not in ('local', 'winrm', 'psrp'):
+ conn = 'local'
+ cmd = [_ansible_playbook_cmd(), path, '-i', 'localhost,', '-c', conn]
+ self._log_local(f"Launching ansible-playbook: conn={conn} cmd={' '.join(cmd)}")
# Ensure clean, plain output and correct interpreter for localhost
env = os.environ.copy()
env.setdefault('ANSIBLE_FORCE_COLOR', '0')
@@ -301,9 +481,9 @@ class Role:
except Exception:
self._log_local("Collection install failed (continuing)")
- # Prefer ansible-runner when available and enabled
+ # Prefer ansible-runner when available (default on). Set BOREALIS_USE_ANSIBLE_RUNNER=0 to disable.
try:
- if os.environ.get('BOREALIS_USE_ANSIBLE_RUNNER', '0').lower() not in ('0', 'false', 'no'):
+ if os.environ.get('BOREALIS_USE_ANSIBLE_RUNNER', '1').lower() not in ('0', 'false', 'no'):
used = await self._run_playbook_runner(run_id, playbook_content, playbook_name=playbook_name, activity_job_id=activity_job_id, connection=connection)
if used:
return
diff --git a/Data/Agent/agent.py b/Data/Agent/agent.py
index 4abd3db..3226a04 100644
--- a/Data/Agent/agent.py
+++ b/Data/Agent/agent.py
@@ -1142,6 +1142,20 @@ async def connect():
await sio.emit('request_config', {"agent_id": AGENT_ID})
# Inventory details posting is managed by the DeviceAudit role (SYSTEM). No one-shot post here.
+ # Fire-and-forget service account check-in so server can provision WinRM credentials
+ try:
+ async def _svc_checkin_once():
+ try:
+ url = get_server_url().rstrip('/') + "/api/agent/checkin"
+ payload = {"agent_id": AGENT_ID, "hostname": socket.gethostname(), "username": ".\\svcBorealisAnsibleRunner"}
+ timeout = aiohttp.ClientTimeout(total=10)
+ async with aiohttp.ClientSession(timeout=timeout) as session:
+ await session.post(url, json=payload)
+ except Exception:
+ pass
+ asyncio.create_task(_svc_checkin_once())
+ except Exception:
+ pass
@sio.event
async def disconnect():
diff --git a/Data/Server/job_scheduler.py b/Data/Server/job_scheduler.py
index eaf9a6c..1518687 100644
--- a/Data/Server/job_scheduler.py
+++ b/Data/Server/job_scheduler.py
@@ -216,7 +216,7 @@ class JobScheduler:
"activity_job_id": act_id,
"scheduled_job_id": int(scheduled_job_id),
"scheduled_run_id": int(scheduled_run_id),
- "connection": "local",
+ "connection": "winrm",
}
try:
self.socketio.emit("ansible_playbook_run", payload)
diff --git a/Data/Server/server-requirements.txt b/Data/Server/server-requirements.txt
index a27f6d7..5e3dcc7 100644
--- a/Data/Server/server-requirements.txt
+++ b/Data/Server/server-requirements.txt
@@ -10,6 +10,7 @@ requests
flask_socketio
flask-cors
eventlet
+cryptography
# GUI-related dependencies (Qt for GUI components)
Qt.py
@@ -26,4 +27,4 @@ Pillow # Image processing (Windows)
# WebRTC Video Libraries
###aiortc # Python library for WebRTC in async environments
-###av # Required by aiortc for video/audio codecs
\ No newline at end of file
+###av # Required by aiortc for video/audio codecs
diff --git a/qj_old.txt b/qj_old.txt
deleted file mode 100644
index b8f8160..0000000
--- a/qj_old.txt
+++ /dev/null
@@ -1,226 +0,0 @@
-import React, { useEffect, useMemo, useState, useCallback } from "react";
-import {
- Dialog,
- DialogTitle,
- DialogContent,
- DialogActions,
- Button,
- Box,
- Typography,
- Paper,
- FormControlLabel,
- Checkbox
-} from "@mui/material";
-import { Folder as FolderIcon, Description as DescriptionIcon } from "@mui/icons-material";
-import { SimpleTreeView, TreeItem } from "@mui/x-tree-view";
-
-function buildTree(items, folders, rootLabel = "Scripts") {
- const map = {};
- const rootNode = {
- id: "root",
- label: rootLabel,
- path: "",
- isFolder: true,
- children: []
- };
- map[rootNode.id] = rootNode;
-
- (folders || []).forEach((f) => {
- const parts = (f || "").split("/");
- let children = rootNode.children;
- let parentPath = "";
- parts.forEach((part) => {
- const path = parentPath ? `${parentPath}/${part}` : part;
- let node = children.find((n) => n.id === path);
- if (!node) {
- node = { id: path, label: part, path, isFolder: true, children: [] };
- children.push(node);
- map[path] = node;
- }
- children = node.children;
- parentPath = path;
- });
- });
-
- (items || []).forEach((s) => {
- const parts = (s.rel_path || "").split("/");
- let children = rootNode.children;
- let parentPath = "";
- parts.forEach((part, idx) => {
- const path = parentPath ? `${parentPath}/${part}` : part;
- const isFile = idx === parts.length - 1;
- let node = children.find((n) => n.id === path);
- if (!node) {
- node = {
- id: path,
- label: isFile ? s.file_name : part,
- path,
- isFolder: !isFile,
- fileName: s.file_name,
- script: isFile ? s : null,
- children: []
- };
- children.push(node);
- map[path] = node;
- }
- if (!isFile) {
- children = node.children;
- parentPath = path;
- }
- });
- });
-
- return { root: [rootNode], map };
-}
-
-export default function QuickJob({ open, onClose, hostnames = [] }) {
- const [tree, setTree] = useState([]);
- const [nodeMap, setNodeMap] = useState({});
- const [selectedPath, setSelectedPath] = useState("");
- const [running, setRunning] = useState(false);
- const [error, setError] = useState("");
- const [runAsCurrentUser, setRunAsCurrentUser] = useState(false);
- const [mode, setMode] = useState("scripts"); // 'scripts' | 'ansible'
-
- const loadTree = useCallback(async () => {
- try {
- const island = mode === 'ansible' ? 'ansible' : 'scripts';
- const resp = await fetch(`/api/assembly/list?island=${island}`);
- if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
- const data = await resp.json();
- const { root, map } = buildTree(data.items || [], data.folders || [], mode === 'ansible' ? 'Ansible Playbooks' : 'Scripts');
- setTree(root);
- setNodeMap(map);
- } catch (err) {
- console.error("Failed to load scripts:", err);
- setTree([]);
- setNodeMap({});
- }
- }, [mode]);
-
- useEffect(() => {
- if (open) {
- setSelectedPath("");
- setError("");
- loadTree();
- }
- }, [open, loadTree]);
-
- const renderNodes = (nodes = []) =>
- nodes.map((n) => (
-
- {n.isFolder ? (
-
- ) : (
-
- )}
- {n.label}
-
- }
- >
- {n.children && n.children.length ? renderNodes(n.children) : null}
-
- ));
-
- const onItemSelect = (_e, itemId) => {
- const node = nodeMap[itemId];
- if (node && !node.isFolder) {
- setSelectedPath(node.path);
- setError("");
- }
- };
-
- const onRun = async () => {
- if (!selectedPath) {
- setError(mode === 'ansible' ? "Please choose a playbook to run." : "Please choose a script to run.");
- return;
- }
- setRunning(true);
- setError("");
- try {
- let resp;
- if (mode === 'ansible') {
- const playbook_path = selectedPath; // relative to ansible island
- resp = await fetch("/api/ansible/quick_run", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ playbook_path, hostnames })
- });
- } else {
- // quick_run expects a path relative to Assemblies root with 'Scripts/' prefix
- const script_path = selectedPath.startsWith('Scripts/') ? selectedPath : `Scripts/${selectedPath}`;
- resp = await fetch("/api/scripts/quick_run", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ script_path, hostnames, run_mode: runAsCurrentUser ? "current_user" : "system" })
- });
- }
- const data = await resp.json();
- if (!resp.ok) throw new Error(data.error || `HTTP ${resp.status}`);
- onClose && onClose();
- } catch (err) {
- setError(String(err.message || err));
- } finally {
- setRunning(false);
- }
- };
-
- return (
-
- );
-}
-
diff --git a/tmp_parse.py b/tmp_parse.py
deleted file mode 100644
index 88d0a26..0000000
--- a/tmp_parse.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import ast, sys
-with open('Data/Server/server.py','r',encoding='utf-8') as f:
- ast.parse(f.read())
-print('OK')