claude-mpm 3.4.3__py3-none-any.whl → 3.4.6__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.
@@ -1,479 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Version management script for Claude MPM.
4
-
5
- This script implements a comprehensive version management system that:
6
- 1. Uses setuptools-scm for version detection from git tags
7
- 2. Updates VERSION file to maintain version synchronization
8
- 3. Generates/updates CHANGELOG.md from git commits automatically
9
- 4. Supports semantic versioning with conventional commits
10
-
11
- OPERATIONAL PURPOSE:
12
- Central version management for consistent releases across all distribution channels.
13
- Automates version bumping, changelog generation, and release preparation.
14
-
15
- VERSION MANAGEMENT STRATEGY:
16
- - Primary source of truth: Git tags (format: v1.0.0)
17
- - setuptools-scm derives version from git state
18
- - VERSION file kept in sync for quick access
19
- - package.json synchronized via release.py script
20
- - Conventional commits determine version bump type automatically
21
-
22
- SEMANTIC VERSIONING IMPLEMENTATION:
23
- - MAJOR (X.0.0): Breaking changes (BREAKING CHANGE: or feat!)
24
- - MINOR (0.X.0): New features (feat:)
25
- - PATCH (0.0.X): Bug fixes (fix:) or performance improvements (perf:)
26
- - Development versions between releases: X.Y.Z.postN+gHASH[.dirty]
27
-
28
- VERSION COMPATIBILITY:
29
- - Backward compatible with manual version management
30
- - Forward compatible with CI/CD automation
31
- - Supports both manual and automatic version bumping
32
- - Handles migration from old version formats gracefully
33
-
34
- DEPLOYMENT PIPELINE INTEGRATION:
35
- 1. Developer commits with conventional format
36
- 2. CI runs manage_version.py to determine bump
37
- 3. Version updated and changelog generated
38
- 4. Git tag created for release
39
- 5. PyPI and npm packages built and published
40
-
41
- MONITORING AND TROUBLESHOOTING:
42
- - Check git tags: git tag -l | sort -V
43
- - Verify VERSION file matches latest tag
44
- - Review CHANGELOG.md for missing commits
45
- - Ensure conventional commit format compliance
46
- - Monitor version sync across package files
47
-
48
- ROLLBACK PROCEDURES:
49
- - Delete incorrect git tag: git tag -d vX.Y.Z
50
- - Reset VERSION file to previous version
51
- - Revert CHANGELOG.md changes
52
- - Force push tag updates carefully
53
- - Coordinate with PyPI/npm for package yanking
54
- """
55
-
56
- import subprocess
57
- import re
58
- import sys
59
- from pathlib import Path
60
- from datetime import datetime
61
- from typing import List, Tuple, Optional
62
- import argparse
63
-
64
-
65
- # Conventional commit types and their changelog sections
66
- # These map commit types to human-readable changelog categories
67
- # Following the Conventional Commits specification v1.0.0
68
- COMMIT_TYPES = {
69
- "feat": "Features", # New features
70
- "fix": "Bug Fixes", # Bug fixes
71
- "docs": "Documentation", # Documentation only changes
72
- "style": "Code Style", # Code style changes (formatting, etc)
73
- "refactor": "Code Refactoring", # Code changes that neither fix bugs nor add features
74
- "perf": "Performance Improvements", # Performance improvements
75
- "test": "Tests", # Adding or updating tests
76
- "build": "Build System", # Build system or dependency changes
77
- "ci": "Continuous Integration", # CI configuration changes
78
- "chore": "Chores", # Other changes that don't modify src or test files
79
- "revert": "Reverts" # Reverting previous commits
80
- }
81
-
82
- # Types that trigger version bumps based on semantic versioning rules
83
- # These determine how the version number changes based on commit types
84
- MAJOR_TYPES = ["breaking", "major"] # Keywords in commit message that trigger major bump
85
- MINOR_TYPES = ["feat"] # Commit types that trigger minor version bump
86
- PATCH_TYPES = ["fix", "perf"] # Commit types that trigger patch version bump
87
-
88
-
89
- def run_command(cmd: List[str]) -> str:
90
- """Run a command and return its output.
91
-
92
- This is a utility function that executes shell commands safely and returns
93
- their output. Used throughout the script for git operations.
94
-
95
- Args:
96
- cmd: List of command arguments (e.g., ['git', 'tag', '-l'])
97
-
98
- Returns:
99
- String output from the command, stripped of whitespace
100
- Empty string if command fails
101
- """
102
- try:
103
- result = subprocess.run(cmd, capture_output=True, text=True, check=True)
104
- return result.stdout.strip()
105
- except subprocess.CalledProcessError as e:
106
- print(f"Error running command {' '.join(cmd)}: {e}")
107
- return ""
108
-
109
-
110
- def get_current_version() -> str:
111
- """Get current version from VERSION file.
112
-
113
- Version Detection:
114
- 1. VERSION file: Primary source of truth
115
- 2. Default: Returns 0.0.0 if VERSION file is missing
116
-
117
- The VERSION file is the single source of truth for version information.
118
- Git tags are used for releases, but VERSION file contains the current version.
119
-
120
- Returns:
121
- Current version string from VERSION file
122
- """
123
- # Read from VERSION file - single source of truth
124
- version_file = Path("VERSION")
125
- if version_file.exists():
126
- return version_file.read_text().strip()
127
-
128
- # Default version when VERSION file is missing
129
- print("WARNING: VERSION file not found, using default version 0.0.0", file=sys.stderr)
130
- return "0.0.0"
131
-
132
-
133
- def parse_conventional_commit(message: str) -> Tuple[Optional[str], Optional[str], str, bool]:
134
- """Parse a conventional commit message following the Conventional Commits spec.
135
-
136
- Conventional Commits Format:
137
- <type>[optional scope]: <description>
138
-
139
- [optional body]
140
-
141
- [optional footer(s)]
142
-
143
- Examples:
144
- - "feat: add new agent capabilities"
145
- - "fix(logging): correct session duration calculation"
146
- - "feat!: redesign agent API" (breaking change)
147
- - "fix: typo\n\nBREAKING CHANGE: API renamed" (breaking in footer)
148
-
149
- Breaking Changes Detection:
150
- 1. Exclamation mark after type/scope: "feat!:" or "feat(api)!:"
151
- 2. "BREAKING CHANGE:" in commit body/footer
152
- 3. "BREAKING:" as shorthand in body/footer
153
-
154
- Args:
155
- message: Full commit message including body
156
-
157
- Returns:
158
- Tuple of:
159
- - type: Commit type (feat, fix, etc.) or None
160
- - scope: Optional scope in parentheses or None
161
- - description: Commit description (subject line)
162
- - is_breaking: True if breaking change detected
163
- """
164
- # Check for breaking change indicators anywhere in the message
165
- # This includes both the footer format and inline indicators
166
- is_breaking = "BREAKING CHANGE:" in message or "BREAKING:" in message
167
-
168
- # Parse conventional commit format: type(scope): description
169
- # Also handles type!: for breaking changes
170
- pattern = r"^(\w+)(?:\(([^)]+)\))?: (.+)"
171
- match = re.match(pattern, message.split("\n")[0])
172
-
173
- if match:
174
- commit_type, scope, description = match.groups()
175
- return commit_type, scope, description, is_breaking
176
-
177
- # If not a conventional commit, return the first line as description
178
- return None, None, message.split("\n")[0], is_breaking
179
-
180
-
181
- def get_commits_since_tag(tag: Optional[str] = None) -> List[dict]:
182
- """Get all commits since the last tag."""
183
- if tag:
184
- cmd = ["git", "log", f"{tag}..HEAD", "--pretty=format:%H|%ai|%s|%b|%an"]
185
- else:
186
- cmd = ["git", "log", "--pretty=format:%H|%ai|%s|%b|%an"]
187
-
188
- output = run_command(cmd)
189
- if not output:
190
- return []
191
-
192
- commits = []
193
- for line in output.split("\n"):
194
- if line:
195
- parts = line.split("|", 4)
196
- if len(parts) >= 5:
197
- hash, date, subject, body, author = parts
198
- commit_type, scope, description, is_breaking = parse_conventional_commit(subject)
199
- commits.append({
200
- "hash": hash[:7],
201
- "date": date,
202
- "type": commit_type,
203
- "scope": scope,
204
- "description": description,
205
- "breaking": is_breaking,
206
- "author": author,
207
- "body": body
208
- })
209
-
210
- return commits
211
-
212
-
213
- def determine_version_bump(commits: List[dict]) -> str:
214
- """Determine version bump type based on commits using semantic versioning rules.
215
-
216
- Version Bump Logic (in priority order):
217
- 1. MAJOR: Any commit with breaking changes
218
- - BREAKING CHANGE: in footer
219
- - feat!: or fix!: syntax
220
- - Resets minor and patch to 0
221
-
222
- 2. MINOR: Any commit with new features (feat:)
223
- - Only if no breaking changes
224
- - Resets patch to 0
225
-
226
- 3. PATCH: Any commit with fixes (fix:) or performance improvements (perf:)
227
- - Only if no breaking changes or features
228
- - Increments patch version
229
-
230
- 4. DEFAULT: If no conventional commits found, defaults to patch
231
- - Ensures version always increments
232
- - Safe default for manual commits
233
-
234
- This implements the semantic versioning specification where:
235
- - MAJOR version for incompatible API changes
236
- - MINOR version for backwards-compatible functionality additions
237
- - PATCH version for backwards-compatible bug fixes
238
-
239
- Args:
240
- commits: List of parsed commit dictionaries
241
-
242
- Returns:
243
- Version bump type: "major", "minor", or "patch"
244
- """
245
- # Check for breaking changes first (highest priority)
246
- has_breaking = any(c["breaking"] for c in commits)
247
- # Check for new features
248
- has_minor = any(c["type"] in MINOR_TYPES for c in commits)
249
- # Check for fixes or performance improvements
250
- has_patch = any(c["type"] in PATCH_TYPES for c in commits)
251
-
252
- # Apply semantic versioning rules in priority order
253
- if has_breaking:
254
- return "major"
255
- elif has_minor:
256
- return "minor"
257
- elif has_patch:
258
- return "patch"
259
- return "patch" # Default to patch for safety
260
-
261
-
262
- def bump_version(current_version: str, bump_type: str) -> str:
263
- """Bump version according to semantic versioning specification.
264
-
265
- Semantic Versioning Rules:
266
- - MAJOR: Increment major, reset minor and patch to 0
267
- - MINOR: Increment minor, reset patch to 0
268
- - PATCH: Increment patch only
269
-
270
- Version Cleaning:
271
- - Removes development suffixes (.postN, .dirty, +gHASH)
272
- - Extracts base semantic version (X.Y.Z)
273
- - Handles various version formats from setuptools-scm
274
-
275
- Examples:
276
- - "1.2.3" + patch -> "1.2.4"
277
- - "1.2.3.post4+g1234567" + minor -> "1.3.0"
278
- - "1.2.3.dirty" + major -> "2.0.0"
279
-
280
- Args:
281
- current_version: Current version string (may include suffixes)
282
- bump_type: Type of bump ("major", "minor", or "patch")
283
-
284
- Returns:
285
- New clean semantic version string (X.Y.Z format)
286
- """
287
- # Clean version by extracting base semantic version
288
- # This removes any PEP 440 local version identifiers
289
- base_version = re.match(r"(\d+\.\d+\.\d+)", current_version)
290
- if base_version:
291
- current_version = base_version.group(1)
292
-
293
- # Parse version components
294
- major, minor, patch = map(int, current_version.split("."))
295
-
296
- # Apply semantic versioning rules
297
- if bump_type == "major":
298
- # Breaking change: increment major, reset others
299
- return f"{major + 1}.0.0"
300
- elif bump_type == "minor":
301
- # New feature: increment minor, reset patch
302
- return f"{major}.{minor + 1}.0"
303
- else: # patch
304
- # Bug fix: increment patch only
305
- return f"{major}.{minor}.{patch + 1}"
306
-
307
-
308
- def generate_changelog_entry(version: str, commits: List[dict], date: str) -> str:
309
- """Generate a changelog entry for a version."""
310
- lines = [f"## [{version}] - {date}\n"]
311
-
312
- # Group commits by type
313
- grouped = {}
314
- for commit in commits:
315
- commit_type = commit["type"] or "other"
316
- if commit_type not in grouped:
317
- grouped[commit_type] = []
318
- grouped[commit_type].append(commit)
319
-
320
- # Add sections
321
- for commit_type, section_name in COMMIT_TYPES.items():
322
- if commit_type in grouped:
323
- lines.append(f"\n### {section_name}\n")
324
- for commit in grouped[commit_type]:
325
- scope = f"**{commit['scope']}**: " if commit["scope"] else ""
326
- lines.append(f"- {scope}{commit['description']} ([{commit['hash']}])")
327
- if commit["breaking"]:
328
- lines.append(f" - **BREAKING CHANGE**")
329
-
330
- # Add uncategorized commits
331
- if "other" in grouped:
332
- lines.append(f"\n### Other Changes\n")
333
- for commit in grouped["other"]:
334
- lines.append(f"- {commit['description']} ([{commit['hash']}])")
335
-
336
- return "\n".join(lines)
337
-
338
-
339
- def update_changelog(new_entry: str):
340
- """Update CHANGELOG.md with new entry."""
341
- changelog_path = Path("CHANGELOG.md")
342
-
343
- if changelog_path.exists():
344
- content = changelog_path.read_text()
345
- # Insert after the header
346
- parts = content.split("\n## ", 1)
347
- if len(parts) == 2:
348
- new_content = parts[0] + "\n" + new_entry + "\n## " + parts[1]
349
- else:
350
- new_content = content + "\n" + new_entry
351
- else:
352
- # Create new changelog
353
- new_content = f"""# Changelog
354
-
355
- All notable changes to Claude MPM will be documented in this file.
356
-
357
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
358
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
359
-
360
- {new_entry}"""
361
-
362
- changelog_path.write_text(new_content)
363
- print(f"Updated CHANGELOG.md")
364
-
365
-
366
- def update_version_file(version: str):
367
- """Update VERSION file."""
368
- version_file = Path("VERSION")
369
- version_file.write_text(version + "\n")
370
- print(f"Updated VERSION file to {version}")
371
-
372
-
373
- def create_git_tag(version: str, message: str):
374
- """Create an annotated git tag for the version.
375
-
376
- Git Tag Strategy:
377
- - Uses 'v' prefix convention (v1.2.3)
378
- - Creates annotated tags (includes tagger info and message)
379
- - Annotated tags are preferred for releases (shown in git describe)
380
- - Tag message typically includes release title
381
-
382
- The 'v' prefix is a widely adopted convention that:
383
- - Distinguishes version tags from other tags
384
- - Works well with GitHub releases
385
- - Compatible with most CI/CD systems
386
- - Recognized by setuptools-scm
387
-
388
- Args:
389
- version: Semantic version string (without 'v' prefix)
390
- message: Tag annotation message
391
- """
392
- tag = f"v{version}"
393
- # Create annotated tag with message
394
- # -a: Create annotated tag (not lightweight)
395
- # -m: Provide message inline
396
- run_command(["git", "tag", "-a", tag, "-m", message])
397
- print(f"Created git tag: {tag}")
398
-
399
-
400
- def main():
401
- parser = argparse.ArgumentParser(description="Manage Claude MPM versioning")
402
- parser.add_argument("command", choices=["check", "bump", "changelog", "tag", "auto"],
403
- help="Command to run")
404
- parser.add_argument("--bump-type", choices=["major", "minor", "patch", "auto"],
405
- default="auto", help="Version bump type")
406
- parser.add_argument("--dry-run", action="store_true",
407
- help="Don't make any changes")
408
- parser.add_argument("--no-commit", action="store_true",
409
- help="Don't commit changes")
410
-
411
- args = parser.parse_args()
412
-
413
- # Get current version
414
- current_version = get_current_version()
415
- print(f"Current version: {current_version}")
416
-
417
- if args.command == "check":
418
- # Just display current version
419
- return
420
-
421
- # Get latest tag
422
- latest_tag = run_command(["git", "describe", "--tags", "--abbrev=0"])
423
- if not latest_tag or latest_tag.startswith("fatal"):
424
- latest_tag = None
425
-
426
- # Get commits since last tag
427
- commits = get_commits_since_tag(latest_tag)
428
- print(f"Found {len(commits)} commits since {latest_tag or 'beginning'}")
429
-
430
- if args.command in ["bump", "auto"]:
431
- # Determine version bump
432
- if args.bump_type == "auto":
433
- bump_type = determine_version_bump(commits)
434
- else:
435
- bump_type = args.bump_type
436
-
437
- new_version = bump_version(current_version, bump_type)
438
- print(f"New version: {new_version} ({bump_type} bump)")
439
-
440
- if not args.dry_run:
441
- # Update VERSION file
442
- update_version_file(new_version)
443
-
444
- # Generate changelog entry
445
- changelog_entry = generate_changelog_entry(
446
- new_version, commits, datetime.now().strftime("%Y-%m-%d")
447
- )
448
- update_changelog(changelog_entry)
449
-
450
- if not args.no_commit:
451
- # Commit changes
452
- run_command(["git", "add", "VERSION", "CHANGELOG.md"])
453
- run_command(["git", "commit", "-m", f"chore: bump version to {new_version}"])
454
-
455
- # Create tag
456
- create_git_tag(new_version, f"Release {new_version}")
457
- print(f"\nVersion bumped to {new_version}")
458
- print("Run 'git push && git push --tags' to publish")
459
-
460
- elif args.command == "changelog":
461
- # Just generate changelog
462
- if commits:
463
- changelog_entry = generate_changelog_entry(
464
- current_version, commits, datetime.now().strftime("%Y-%m-%d")
465
- )
466
- if args.dry_run:
467
- print("\nChangelog entry:")
468
- print(changelog_entry)
469
- else:
470
- update_changelog(changelog_entry)
471
-
472
- elif args.command == "tag":
473
- # Just create a tag for current version
474
- if not args.dry_run:
475
- create_git_tag(current_version, f"Release {current_version}")
476
-
477
-
478
- if __name__ == "__main__":
479
- main()
@@ -1,221 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Pure Python daemon management for Socket.IO server.
4
- No external dependencies required.
5
- """
6
-
7
- import os
8
- import sys
9
- import time
10
- import signal
11
- import subprocess
12
- import psutil
13
- import json
14
- from pathlib import Path
15
-
16
- # Add src to path
17
- sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
18
-
19
- from claude_mpm.services.socketio_server import SocketIOServer
20
-
21
- PID_FILE = Path.home() / ".claude-mpm" / "socketio-server.pid"
22
- LOG_FILE = Path.home() / ".claude-mpm" / "socketio-server.log"
23
-
24
- def ensure_dirs():
25
- """Ensure required directories exist."""
26
- PID_FILE.parent.mkdir(parents=True, exist_ok=True)
27
-
28
- def is_running():
29
- """Check if server is already running."""
30
- if not PID_FILE.exists():
31
- return False
32
-
33
- try:
34
- with open(PID_FILE) as f:
35
- pid = int(f.read().strip())
36
-
37
- # Check if process exists
38
- process = psutil.Process(pid)
39
- return process.is_running()
40
- except (ValueError, psutil.NoSuchProcess, psutil.AccessDenied):
41
- # Clean up stale PID file
42
- PID_FILE.unlink(missing_ok=True)
43
- return False
44
-
45
- def start_server():
46
- """Start the Socket.IO server as a daemon with conflict detection."""
47
- if is_running():
48
- print("Socket.IO daemon server is already running.")
49
- print(f"Use '{__file__} status' for details")
50
- return
51
-
52
- # Check for HTTP-managed server conflict
53
- try:
54
- import requests
55
- response = requests.get("http://localhost:8765/health", timeout=1.0)
56
- if response.status_code == 200:
57
- data = response.json()
58
- if 'server_id' in data:
59
- print(f"⚠️ HTTP-managed server already running: {data.get('server_id')}")
60
- print(f" Stop it first: socketio_server_manager.py stop --port 8765")
61
- print(f" Or diagnose: socketio_server_manager.py diagnose")
62
- return
63
- except:
64
- pass # No HTTP server, continue
65
-
66
- ensure_dirs()
67
-
68
- # Fork to create daemon
69
- pid = os.fork()
70
- if pid > 0:
71
- # Parent process
72
- print(f"Starting Socket.IO server (PID: {pid})...")
73
- with open(PID_FILE, 'w') as f:
74
- f.write(str(pid))
75
- print("Socket.IO server started successfully.")
76
- print(f"PID file: {PID_FILE}")
77
- print(f"Log file: {LOG_FILE}")
78
- sys.exit(0)
79
-
80
- # Child process - become daemon
81
- os.setsid()
82
- os.umask(0)
83
-
84
- # Redirect stdout/stderr to log file
85
- with open(LOG_FILE, 'a') as log:
86
- os.dup2(log.fileno(), sys.stdout.fileno())
87
- os.dup2(log.fileno(), sys.stderr.fileno())
88
-
89
- # Start server
90
- print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Starting Socket.IO server...")
91
- server = SocketIOServer(host="localhost", port=8765)
92
-
93
- # Handle signals
94
- def signal_handler(signum, frame):
95
- print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Received signal {signum}, shutting down...")
96
- server.stop()
97
- PID_FILE.unlink(missing_ok=True)
98
- sys.exit(0)
99
-
100
- signal.signal(signal.SIGTERM, signal_handler)
101
- signal.signal(signal.SIGINT, signal_handler)
102
-
103
- # Start server
104
- server.start()
105
-
106
- # Keep running
107
- try:
108
- while True:
109
- time.sleep(1)
110
- except KeyboardInterrupt:
111
- signal_handler(signal.SIGINT, None)
112
-
113
- def stop_server():
114
- """Stop the Socket.IO daemon server."""
115
- if not is_running():
116
- print("Socket.IO daemon server is not running.")
117
- print(f"Check for other servers: socketio_server_manager.py status")
118
- return
119
-
120
- try:
121
- with open(PID_FILE) as f:
122
- pid = int(f.read().strip())
123
-
124
- print(f"Stopping Socket.IO server (PID: {pid})...")
125
- os.kill(pid, signal.SIGTERM)
126
-
127
- # Wait for process to stop
128
- for _ in range(10):
129
- if not is_running():
130
- print("Socket.IO server stopped successfully.")
131
- PID_FILE.unlink(missing_ok=True)
132
- return
133
- time.sleep(0.5)
134
-
135
- # Force kill if still running
136
- print("Server didn't stop gracefully, forcing...")
137
- os.kill(pid, signal.SIGKILL)
138
- PID_FILE.unlink(missing_ok=True)
139
-
140
- except Exception as e:
141
- print(f"Error stopping server: {e}")
142
-
143
- def status_server():
144
- """Check server status with manager integration info."""
145
- if is_running():
146
- with open(PID_FILE) as f:
147
- pid = int(f.read().strip())
148
- print(f"Socket.IO daemon server is running (PID: {pid})")
149
- print(f"PID file: {PID_FILE}")
150
-
151
- # Check if port is listening
152
- try:
153
- import socket
154
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
155
- result = sock.connect_ex(('localhost', 8765))
156
- sock.close()
157
- if result == 0:
158
- print("✅ Server is listening on port 8765")
159
- print("🔧 Management style: daemon")
160
- else:
161
- print("⚠️ WARNING: Server process exists but port 8765 is not accessible")
162
- except:
163
- pass
164
-
165
- # Show management commands
166
- print("\n🔧 Management Commands:")
167
- print(f" • Stop: {__file__} stop")
168
- print(f" • Restart: {__file__} restart")
169
-
170
- # Check for manager conflicts
171
- try:
172
- import requests
173
- response = requests.get("http://localhost:8765/health", timeout=1.0)
174
- if response.status_code == 200:
175
- data = response.json()
176
- if 'server_id' in data and data.get('server_id') != 'daemon-socketio':
177
- print(f"\n⚠️ POTENTIAL CONFLICT: HTTP-managed server also detected")
178
- print(f" Server ID: {data.get('server_id')}")
179
- print(f" Use 'socketio_server_manager.py diagnose' to resolve")
180
- except:
181
- pass
182
-
183
- else:
184
- print("Socket.IO daemon server is not running")
185
- print(f"\n🔧 Start Commands:")
186
- print(f" • Daemon: {__file__} start")
187
- print(f" • HTTP-managed: socketio_server_manager.py start")
188
-
189
- def main():
190
- """Main entry point."""
191
- if len(sys.argv) < 2:
192
- print("Usage: socketio-daemon.py {start|stop|restart|status}")
193
- sys.exit(1)
194
-
195
- command = sys.argv[1]
196
-
197
- if command == "start":
198
- start_server()
199
- elif command == "stop":
200
- stop_server()
201
- elif command == "restart":
202
- stop_server()
203
- time.sleep(1)
204
- start_server()
205
- elif command == "status":
206
- status_server()
207
- else:
208
- print(f"Unknown command: {command}")
209
- print("Usage: socketio-daemon.py {start|stop|restart|status}")
210
- sys.exit(1)
211
-
212
- if __name__ == "__main__":
213
- # Install psutil if not available
214
- try:
215
- import psutil
216
- except ImportError:
217
- print("Installing psutil...")
218
- subprocess.check_call([sys.executable, "-m", "pip", "install", "psutil"])
219
- import psutil
220
-
221
- main()