magmascript 2.0.1__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. {magmascript-2.0.1 → magmascript-2.2.0}/PKG-INFO +49 -5
  2. {magmascript-2.0.1 → magmascript-2.2.0}/README.md +48 -4
  3. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/__init__.py +4 -2
  4. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/cli.py +7 -0
  5. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/config.py +14 -0
  6. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/__init__.py +2 -1
  7. magmascript-2.2.0/magmascript/domains/mc1/__init__.py +14 -0
  8. magmascript-2.2.0/magmascript/domains/mc1/client.py +122 -0
  9. magmascript-2.2.0/magmascript/domains/mc1/tools.py +82 -0
  10. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/PKG-INFO +49 -5
  11. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/SOURCES.txt +3 -0
  12. {magmascript-2.0.1 → magmascript-2.2.0}/pyproject.toml +1 -1
  13. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_cli.py +14 -0
  14. {magmascript-2.0.1 → magmascript-2.2.0}/LICENSE +0 -0
  15. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/__init__.py +0 -0
  16. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/cache.py +0 -0
  17. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/commands.py +0 -0
  18. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/exceptions.py +0 -0
  19. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/github.py +0 -0
  20. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/output.py +0 -0
  21. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/registry.py +0 -0
  22. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/rpc.py +0 -0
  23. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/core/runner.py +0 -0
  24. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/archive/__init__.py +0 -0
  25. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/archive/client.py +0 -0
  26. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/gh/__init__.py +0 -0
  27. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/gh/client.py +0 -0
  28. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/gh/tools.py +0 -0
  29. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/lastfm/__init__.py +0 -0
  30. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/lastfm/client.py +0 -0
  31. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/mb/__init__.py +0 -0
  32. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/mb/client.py +0 -0
  33. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/mcp/__init__.py +0 -0
  34. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/mcp/client.py +0 -0
  35. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/mcp/tools.py +0 -0
  36. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/__init__.py +0 -0
  37. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/client.py +0 -0
  38. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/__init__.py +0 -0
  39. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/archive.py +0 -0
  40. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/met_museum.py +0 -0
  41. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/openverse.py +0 -0
  42. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/pexels.py +0 -0
  43. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/pixabay.py +0 -0
  44. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/providers/smithsonian.py +0 -0
  45. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/media/tools.py +0 -0
  46. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/pi/__init__.py +0 -0
  47. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/pi/client.py +0 -0
  48. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/pi/tools.py +0 -0
  49. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/rights/__init__.py +0 -0
  50. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/rights/client.py +0 -0
  51. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/rights/tools.py +0 -0
  52. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/scores/__init__.py +0 -0
  53. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/scores/client.py +0 -0
  54. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/scores/tools.py +0 -0
  55. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/search/__init__.py +0 -0
  56. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/domains/search/client.py +0 -0
  57. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/__init__.py +0 -0
  58. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/ast_nodes.py +0 -0
  59. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/builtins.py +0 -0
  60. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/domain_bridge.py +0 -0
  61. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/environment.py +0 -0
  62. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/interpreter.py +0 -0
  63. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/lexer.py +0 -0
  64. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/parser.py +0 -0
  65. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/pygments_mgs.py +0 -0
  66. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/tokens.py +0 -0
  67. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/lang/util.py +0 -0
  68. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript/repl.py +0 -0
  69. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/dependency_links.txt +0 -0
  70. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/entry_points.txt +0 -0
  71. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/requires.txt +0 -0
  72. {magmascript-2.0.1 → magmascript-2.2.0}/magmascript.egg-info/top_level.txt +0 -0
  73. {magmascript-2.0.1 → magmascript-2.2.0}/setup.cfg +0 -0
  74. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_cache.py +0 -0
  75. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_classes.py +0 -0
  76. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_commands.py +0 -0
  77. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_domain_bridge.py +0 -0
  78. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_import.py +0 -0
  79. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_lang.py +0 -0
  80. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_mcp_client.py +0 -0
  81. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_rpc.py +0 -0
  82. {magmascript-2.0.1 → magmascript-2.2.0}/tests/test_try_catch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magmascript
3
- Version: 2.0.1
3
+ Version: 2.2.0
4
4
  Summary: Scripting toolkit with domain-first subcommands
5
5
  Author: Jake McCoy
6
6
  License-Expression: MIT
@@ -85,11 +85,14 @@ Write `.mgs` scripts using a Python-inspired mini language with direct access to
85
85
  ### Quick Start
86
86
 
87
87
  ```bash
88
- # Run a script
89
- magmascript run scripts/examples/hello.mgs
88
+ # Run a script (shorthand)
89
+ magmascript scripts/examples/hello.mgs
90
90
 
91
91
  # Run with arguments
92
- magmascript run scripts/examples/top-scores.mgs tetris
92
+ magmascript scripts/examples/top-scores.mgs tetris
93
+
94
+ # Or use the explicit run subcommand
95
+ magmascript run scripts/examples/hello.mgs
93
96
 
94
97
  # Start interactive REPL
95
98
  magmascript repl
@@ -289,6 +292,7 @@ if "xyz" not in "hello" { ... }
289
292
  | Function | Description |
290
293
  |----------|-------------|
291
294
  | `print(...)` | Print to stdout |
295
+ | `echo(...)` | Print to stdout (alias for print) |
292
296
  | `len(x)` | Length of string, list, or dict |
293
297
  | `type(x)` | Type name as string |
294
298
  | `range(n)`, `range(start, stop)`, `range(start, stop, step)` | Generate integer ranges |
@@ -338,6 +342,32 @@ See `scripts/examples/` for working examples:
338
342
  | `real-domains.mgs` | Test real domain connections (MCP search, scoreboards, games) |
339
343
  | `domain-example.mgs` | Domain object overview and usage patterns |
340
344
 
345
+ ### REPL
346
+
347
+ Start an interactive MagmaScript session:
348
+
349
+ ```bash
350
+ magmascript repl
351
+ ```
352
+
353
+ **Features:**
354
+ - Syntax highlighting via Pygments
355
+ - Tab completion for keywords, builtins, domain methods, and user-defined variables
356
+ - Persistent history across sessions (`~/.magmascript_history`)
357
+ - Multiline editing with proper continuation prompts
358
+ - Dot-commands:
359
+
360
+ | Command | Description |
361
+ |---------|-------------|
362
+ | `.help` | Show available commands |
363
+ | `.exit` | Exit the REPL |
364
+ | `.clear` | Clear the screen |
365
+ | `.ast` | Show AST for last expression |
366
+ | `.magma` | System status dashboard |
367
+ | `.crunch <target>` | Run batch pipeline |
368
+ | `.texas <target>` | Full/heavy operation |
369
+ | `.toast <target>` | Burn/clear caches |
370
+
341
371
  ## Domains
342
372
 
343
373
  ### MCP Domain — MusicBrainz, scores, Discogs, write operations
@@ -363,6 +393,14 @@ magmascript pi backup musicbrainz # backup + commit to GitHub
363
393
  magmascript pi traffic # nginx traffic analysis
364
394
  ```
365
395
 
396
+ ### MC1 Domain — Windows PC management (via SSH)
397
+ ```bash
398
+ magmascript mc1 status # all running Windows services
399
+ magmascript mc1 info # uptime, memory, CPU, disk
400
+ magmascript mc1 restart OllamaSvc # restart a Windows service
401
+ magmascript mc1 processes # top processes by CPU
402
+ ```
403
+
366
404
  ### GitHub Domain — Direct API access
367
405
  ```bash
368
406
  magmascript gh workflows # all workflow statuses
@@ -385,6 +423,7 @@ magmascript scores reset tetris # reset one game (backup created)
385
423
  ```bash
386
424
  magmascript archive check-format # validate HTML formatting
387
425
  magmascript archive bake-cache # inline MusicBrainz cache into pages
426
+ magmascript archive generate-stubs # generate stub HTML for new entities
388
427
  ```
389
428
 
390
429
  ### MusicBrainz Domain — MusicBrainz API client
@@ -500,13 +539,18 @@ gh_workflows # GitHub commands
500
539
  ## Documentation
501
540
 
502
541
  Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
503
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
504
542
  - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
543
+ - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
505
544
  - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
506
545
  - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
507
546
  - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
508
547
  - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
509
548
  - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
549
+ - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
550
+ - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
551
+ - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
552
+ - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
553
+ - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
510
554
  - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
511
555
  - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
512
556
 
@@ -50,11 +50,14 @@ Write `.mgs` scripts using a Python-inspired mini language with direct access to
50
50
  ### Quick Start
51
51
 
52
52
  ```bash
53
- # Run a script
54
- magmascript run scripts/examples/hello.mgs
53
+ # Run a script (shorthand)
54
+ magmascript scripts/examples/hello.mgs
55
55
 
56
56
  # Run with arguments
57
- magmascript run scripts/examples/top-scores.mgs tetris
57
+ magmascript scripts/examples/top-scores.mgs tetris
58
+
59
+ # Or use the explicit run subcommand
60
+ magmascript run scripts/examples/hello.mgs
58
61
 
59
62
  # Start interactive REPL
60
63
  magmascript repl
@@ -254,6 +257,7 @@ if "xyz" not in "hello" { ... }
254
257
  | Function | Description |
255
258
  |----------|-------------|
256
259
  | `print(...)` | Print to stdout |
260
+ | `echo(...)` | Print to stdout (alias for print) |
257
261
  | `len(x)` | Length of string, list, or dict |
258
262
  | `type(x)` | Type name as string |
259
263
  | `range(n)`, `range(start, stop)`, `range(start, stop, step)` | Generate integer ranges |
@@ -303,6 +307,32 @@ See `scripts/examples/` for working examples:
303
307
  | `real-domains.mgs` | Test real domain connections (MCP search, scoreboards, games) |
304
308
  | `domain-example.mgs` | Domain object overview and usage patterns |
305
309
 
310
+ ### REPL
311
+
312
+ Start an interactive MagmaScript session:
313
+
314
+ ```bash
315
+ magmascript repl
316
+ ```
317
+
318
+ **Features:**
319
+ - Syntax highlighting via Pygments
320
+ - Tab completion for keywords, builtins, domain methods, and user-defined variables
321
+ - Persistent history across sessions (`~/.magmascript_history`)
322
+ - Multiline editing with proper continuation prompts
323
+ - Dot-commands:
324
+
325
+ | Command | Description |
326
+ |---------|-------------|
327
+ | `.help` | Show available commands |
328
+ | `.exit` | Exit the REPL |
329
+ | `.clear` | Clear the screen |
330
+ | `.ast` | Show AST for last expression |
331
+ | `.magma` | System status dashboard |
332
+ | `.crunch <target>` | Run batch pipeline |
333
+ | `.texas <target>` | Full/heavy operation |
334
+ | `.toast <target>` | Burn/clear caches |
335
+
306
336
  ## Domains
307
337
 
308
338
  ### MCP Domain — MusicBrainz, scores, Discogs, write operations
@@ -328,6 +358,14 @@ magmascript pi backup musicbrainz # backup + commit to GitHub
328
358
  magmascript pi traffic # nginx traffic analysis
329
359
  ```
330
360
 
361
+ ### MC1 Domain — Windows PC management (via SSH)
362
+ ```bash
363
+ magmascript mc1 status # all running Windows services
364
+ magmascript mc1 info # uptime, memory, CPU, disk
365
+ magmascript mc1 restart OllamaSvc # restart a Windows service
366
+ magmascript mc1 processes # top processes by CPU
367
+ ```
368
+
331
369
  ### GitHub Domain — Direct API access
332
370
  ```bash
333
371
  magmascript gh workflows # all workflow statuses
@@ -350,6 +388,7 @@ magmascript scores reset tetris # reset one game (backup created)
350
388
  ```bash
351
389
  magmascript archive check-format # validate HTML formatting
352
390
  magmascript archive bake-cache # inline MusicBrainz cache into pages
391
+ magmascript archive generate-stubs # generate stub HTML for new entities
353
392
  ```
354
393
 
355
394
  ### MusicBrainz Domain — MusicBrainz API client
@@ -465,13 +504,18 @@ gh_workflows # GitHub commands
465
504
  ## Documentation
466
505
 
467
506
  Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
468
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
469
507
  - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
508
+ - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
470
509
  - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
471
510
  - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
472
511
  - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
473
512
  - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
474
513
  - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
514
+ - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
515
+ - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
516
+ - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
517
+ - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
518
+ - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
475
519
  - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
476
520
  - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
477
521
 
@@ -1,8 +1,8 @@
1
1
  """magmascript — a scripting toolkit with domain-first subcommands."""
2
2
 
3
- __version__ = "2.0.1"
3
+ __version__ = "2.2.0"
4
4
 
5
- from magmascript.core.config import Config, GHConfig, MediaConfig, PIConfig, get_config, load_config, set_config
5
+ from magmascript.core.config import Config, GHConfig, MediaConfig, MC1Config, PIConfig, get_config, load_config, set_config
6
6
  from magmascript.core.registry import get_domain, list_domains, register_domain
7
7
  from magmascript.core.rpc import RPCClient, RPCError, RPCResponse
8
8
  from magmascript.core.runner import CommandRunner
@@ -14,6 +14,7 @@ from magmascript import domains # noqa: F401
14
14
  # Convenience: expose clients at top level
15
15
  from magmascript.domains.mcp import MCPClient
16
16
  from magmascript.domains.pi import PIClient
17
+ from magmascript.domains.mc1 import MC1Client
17
18
  from magmascript.domains.gh import GHClient
18
19
  from magmascript.domains.media import MediaClient
19
20
  from magmascript.domains.rights import RightsClient
@@ -27,6 +28,7 @@ __all__ = [
27
28
  "Config",
28
29
  "GHClient",
29
30
  "GHConfig",
31
+ "MC1Client",
30
32
  "MCPClient",
31
33
  "MediaClient",
32
34
  "MediaConfig",
@@ -34,6 +34,7 @@ def usage():
34
34
  print("""magmascript — scripting toolkit with domain-first subcommands
35
35
 
36
36
  Usage:
37
+ magmascript <script.mgs> [args...] Run a MagmaScript file directly
37
38
  magmascript <domain> <action> [args...]
38
39
 
39
40
  Domains:
@@ -174,6 +175,12 @@ def main():
174
175
  action = args[1] if len(args) > 1 else ""
175
176
  rest = args[2:]
176
177
 
178
+ # Auto-detect .mgs files: magmascript hello.mgs
179
+ if domain.endswith(".mgs"):
180
+ script_args = [action] + rest if action else rest
181
+ _dispatch_run(domain, script_args)
182
+ return
183
+
177
184
  # Parse output format
178
185
  fmt = "table"
179
186
  if "--json" in rest:
@@ -31,6 +31,14 @@ class PIConfig:
31
31
  user: str = "jake"
32
32
 
33
33
 
34
+ @dataclass
35
+ class MC1Config:
36
+ """MC1 Windows PC SSH configuration."""
37
+
38
+ host: str = "100.75.220.87"
39
+ user: str = "magma"
40
+
41
+
34
42
  @dataclass
35
43
  class GHConfig:
36
44
  """GitHub API configuration."""
@@ -79,6 +87,7 @@ class Config:
79
87
 
80
88
  mcp: MCPConfig = field(default_factory=MCPConfig)
81
89
  pi: PIConfig = field(default_factory=PIConfig)
90
+ mc1: MC1Config = field(default_factory=MC1Config)
82
91
  gh: GHConfig = field(default_factory=GHConfig)
83
92
  media: MediaConfig = field(default_factory=MediaConfig)
84
93
  cache: CacheConfig = field(default_factory=CacheConfig)
@@ -112,6 +121,7 @@ def load_config(*, config_path: Path | None = None, env_prefix: str = "MAGMA_")
112
121
  toml = _load_toml(cfg_file)
113
122
  mcp_section = toml.get("mcp", {})
114
123
  pi_section = toml.get("pi", {})
124
+ mc1_section = toml.get("mc1", {})
115
125
  gh_section = toml.get("gh", {})
116
126
  media_section = toml.get("media", {})
117
127
  cache_section = toml.get("cache", {})
@@ -129,6 +139,10 @@ def load_config(*, config_path: Path | None = None, env_prefix: str = "MAGMA_")
129
139
  host=os.environ.get(f"{env_prefix}PI_HOST", pi_section.get("host", "192.168.1.16")),
130
140
  user=os.environ.get(f"{env_prefix}PI_USER", pi_section.get("user", "jake")),
131
141
  ),
142
+ mc1=MC1Config(
143
+ host=os.environ.get(f"{env_prefix}MC1_HOST", mc1_section.get("host", "100.75.220.87")),
144
+ user=os.environ.get(f"{env_prefix}MC1_USER", mc1_section.get("user", "magma")),
145
+ ),
132
146
  gh=GHConfig(
133
147
  token=os.environ.get(f"{env_prefix}GH_TOKEN")
134
148
  or os.environ.get("GITHUB_TOKEN", "")
@@ -3,6 +3,7 @@
3
3
  # Importing this package triggers domain registration
4
4
  from magmascript.domains import mcp # noqa: F401
5
5
  from magmascript.domains import pi # noqa: F401
6
+ from magmascript.domains import mc1 # noqa: F401
6
7
  from magmascript.domains import gh # noqa: F401
7
8
  from magmascript.domains import media # noqa: F401
8
9
  from magmascript.domains import scores # noqa: F401
@@ -12,4 +13,4 @@ from magmascript.domains import mb # noqa: F401
12
13
  from magmascript.domains import lastfm # noqa: F401
13
14
  from magmascript.domains import search # noqa: F401
14
15
 
15
- __all__ = ["mcp", "pi", "gh", "media", "scores", "rights", "archive", "mb", "lastfm", "search"]
16
+ __all__ = ["mcp", "pi", "mc1", "gh", "media", "scores", "rights", "archive", "mb", "lastfm", "search"]
@@ -0,0 +1,14 @@
1
+ """MC1 domain — exposes MC1Client and registers with the domain registry."""
2
+
3
+ from magmascript.domains.mc1.client import MC1Client
4
+ from magmascript.domains.mc1.tools import MC1ServiceStatus, MC1SystemInfo
5
+ from magmascript.core.registry import register_domain
6
+
7
+ # Register this domain
8
+ register_domain("mc1", MC1Client)
9
+
10
+ __all__ = [
11
+ "MC1Client",
12
+ "MC1ServiceStatus",
13
+ "MC1SystemInfo",
14
+ ]
@@ -0,0 +1,122 @@
1
+ """MC1 domain — direct client for Windows PC management.
2
+
3
+ Supports SSH (remote) execution mode for managing MC1.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from magmascript.core.config import Config, get_config
9
+ from magmascript.core.runner import CommandRunner
10
+ from magmascript.domains.mc1.tools import (
11
+ MC1ServiceStatus,
12
+ MC1SystemInfo,
13
+ parse_service_list,
14
+ parse_system_info,
15
+ )
16
+
17
+
18
+ class MC1Client:
19
+ """Direct client for MC1 Windows PC management.
20
+
21
+ Commands run via SSH using PowerShell.
22
+ Raises SSHError on connection or command failures.
23
+ """
24
+
25
+ def __init__(self, config: Config | None = None, *, local: bool = False):
26
+ cfg = config or get_config()
27
+ self._host = cfg.mc1.host
28
+ self._user = cfg.mc1.user
29
+ self._local = local
30
+ self._runner = CommandRunner(cfg.mc1.host, cfg.mc1.user, local=local)
31
+
32
+ # ------------------------------------------------------------------
33
+ # Service management
34
+ # ------------------------------------------------------------------
35
+
36
+ def services(self) -> list[MC1ServiceStatus]:
37
+ """Get status of all running Windows services.
38
+
39
+ Raises SSHError on connection failure.
40
+ """
41
+ stdout = self._runner.run(
42
+ 'powershell -Command "Get-Service | Where-Object {$_.Status -eq \\"Running\\"} | '
43
+ 'Select-Object -Property Name, Status | ForEach-Object { \\"$($_.Name): $($_.Status)\\" }"'
44
+ )
45
+ return parse_service_list(stdout)
46
+
47
+ def restart(self, service: str) -> str:
48
+ """Restart a Windows service.
49
+
50
+ Raises SSHError on connection failure.
51
+ """
52
+ self._runner.run(
53
+ f'powershell -Command "Restart-Service -Name \\"{service}\\" -Force"',
54
+ timeout=30,
55
+ )
56
+ return f"✓ {service} restarted"
57
+
58
+ # ------------------------------------------------------------------
59
+ # System info
60
+ # ------------------------------------------------------------------
61
+
62
+ def info(self) -> MC1SystemInfo:
63
+ """Get MC1 system info (uptime, memory, CPU, disk).
64
+
65
+ Raises SSHError on connection failure.
66
+ """
67
+ stdout = self._runner.run(
68
+ 'powershell -Command "'
69
+ '$os = Get-CimInstance Win32_OperatingSystem; '
70
+ '$cpu = Get-CimInstance Win32_Processor; '
71
+ '$disk = Get-CimInstance Win32_LogicalDisk -Filter \\"DriveType=3\\"; '
72
+ 'Write-Host \\"HOSTNAME:$(hostname)\\"; '
73
+ 'Write-Host \\"UPTIME:$((Get-Date) - $os.LastBootUpTime)\\"; '
74
+ 'Write-Host \\"MEMORY:$([math]::Round($os.TotalVisibleMemorySize/1MB, 1))GB/$([math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)/1MB, 1))GB\\"; '
75
+ 'Write-Host \\"CPU:$($cpu.LoadPercentage)%\\"; '
76
+ 'Write-Host \\"DISK:$([math]::Round($disk.FreeSpace/$disk.Size*100, 1))% free\\"'
77
+ '"'
78
+ )
79
+ return parse_system_info(stdout)
80
+
81
+ # ------------------------------------------------------------------
82
+ # Processes
83
+ # ------------------------------------------------------------------
84
+
85
+ def processes(self) -> str:
86
+ """Get top processes on MC1 by CPU usage.
87
+
88
+ Raises SSHError on connection failure.
89
+ """
90
+ return self._runner.run(
91
+ 'powershell -Command "Get-Process | Sort-Object CPU -Descending | '
92
+ 'Select-Object -First 15 Name, CPU, WorkingSet | Format-Table -AutoSize"'
93
+ )
94
+
95
+ # ------------------------------------------------------------------
96
+ # Power
97
+ # ------------------------------------------------------------------
98
+
99
+ def reboot(self) -> str:
100
+ """Reboot MC1.
101
+
102
+ Raises SSHError on connection failure.
103
+ """
104
+ self._runner.run(
105
+ 'powershell -Command "Restart-Computer -Force"',
106
+ timeout=10,
107
+ )
108
+ return "✓ Rebooting MC1..."
109
+
110
+ # ------------------------------------------------------------------
111
+ # Lifecycle
112
+ # ------------------------------------------------------------------
113
+
114
+ def close(self):
115
+ """No-op."""
116
+ pass
117
+
118
+ def __enter__(self):
119
+ return self
120
+
121
+ def __exit__(self, *args):
122
+ self.close()
@@ -0,0 +1,82 @@
1
+ """Typed result dataclasses for the MC1 domain."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass
9
+ class MC1ServiceStatus:
10
+ """Status of a Windows service."""
11
+
12
+ name: str
13
+ status: str
14
+ ok: bool
15
+
16
+
17
+ @dataclass
18
+ class MC1SystemInfo:
19
+ """MC1 system info."""
20
+
21
+ hostname: str
22
+ uptime: str
23
+ memory: str
24
+ cpu_load: str
25
+ disk_free: str
26
+
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Parsers
30
+ # ---------------------------------------------------------------------------
31
+
32
+
33
+ def parse_service_list(text: str) -> list[MC1ServiceStatus]:
34
+ """Parse PowerShell Get-Service output into MC1ServiceStatus list.
35
+
36
+ Expected format (from the PowerShell command):
37
+ AmdPpkgSvc: Running
38
+ AppIDSvc: Running
39
+ """
40
+ results = []
41
+ for line in text.splitlines():
42
+ line = line.strip()
43
+ if ":" in line:
44
+ name, status = line.split(":", 1)
45
+ name = name.strip()
46
+ status = status.strip()
47
+ results.append(MC1ServiceStatus(
48
+ name=name,
49
+ status=status,
50
+ ok=status.lower() == "running",
51
+ ))
52
+ return results
53
+
54
+
55
+ def parse_system_info(text: str) -> MC1SystemInfo:
56
+ """Parse the output of the combined system info command.
57
+
58
+ Expected format:
59
+ HOSTNAME:MC1
60
+ UPTIME:07:33:06.0140847
61
+ MEMORY:31.7GB/7.6GB
62
+ CPU:4%
63
+ DISK:93.2% free
64
+ """
65
+ info = {"hostname": "", "uptime": "", "memory": "", "cpu_load": "", "disk_free": ""}
66
+ for line in text.splitlines():
67
+ line = line.strip()
68
+ if ":" in line:
69
+ key, val = line.split(":", 1)
70
+ key = key.lower().strip()
71
+ val = val.strip()
72
+ if key == "hostname":
73
+ info["hostname"] = val
74
+ elif key == "uptime":
75
+ info["uptime"] = val
76
+ elif key == "memory":
77
+ info["memory"] = val
78
+ elif key == "cpu":
79
+ info["cpu_load"] = val
80
+ elif key == "disk":
81
+ info["disk_free"] = val
82
+ return MC1SystemInfo(**info)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magmascript
3
- Version: 2.0.1
3
+ Version: 2.2.0
4
4
  Summary: Scripting toolkit with domain-first subcommands
5
5
  Author: Jake McCoy
6
6
  License-Expression: MIT
@@ -85,11 +85,14 @@ Write `.mgs` scripts using a Python-inspired mini language with direct access to
85
85
  ### Quick Start
86
86
 
87
87
  ```bash
88
- # Run a script
89
- magmascript run scripts/examples/hello.mgs
88
+ # Run a script (shorthand)
89
+ magmascript scripts/examples/hello.mgs
90
90
 
91
91
  # Run with arguments
92
- magmascript run scripts/examples/top-scores.mgs tetris
92
+ magmascript scripts/examples/top-scores.mgs tetris
93
+
94
+ # Or use the explicit run subcommand
95
+ magmascript run scripts/examples/hello.mgs
93
96
 
94
97
  # Start interactive REPL
95
98
  magmascript repl
@@ -289,6 +292,7 @@ if "xyz" not in "hello" { ... }
289
292
  | Function | Description |
290
293
  |----------|-------------|
291
294
  | `print(...)` | Print to stdout |
295
+ | `echo(...)` | Print to stdout (alias for print) |
292
296
  | `len(x)` | Length of string, list, or dict |
293
297
  | `type(x)` | Type name as string |
294
298
  | `range(n)`, `range(start, stop)`, `range(start, stop, step)` | Generate integer ranges |
@@ -338,6 +342,32 @@ See `scripts/examples/` for working examples:
338
342
  | `real-domains.mgs` | Test real domain connections (MCP search, scoreboards, games) |
339
343
  | `domain-example.mgs` | Domain object overview and usage patterns |
340
344
 
345
+ ### REPL
346
+
347
+ Start an interactive MagmaScript session:
348
+
349
+ ```bash
350
+ magmascript repl
351
+ ```
352
+
353
+ **Features:**
354
+ - Syntax highlighting via Pygments
355
+ - Tab completion for keywords, builtins, domain methods, and user-defined variables
356
+ - Persistent history across sessions (`~/.magmascript_history`)
357
+ - Multiline editing with proper continuation prompts
358
+ - Dot-commands:
359
+
360
+ | Command | Description |
361
+ |---------|-------------|
362
+ | `.help` | Show available commands |
363
+ | `.exit` | Exit the REPL |
364
+ | `.clear` | Clear the screen |
365
+ | `.ast` | Show AST for last expression |
366
+ | `.magma` | System status dashboard |
367
+ | `.crunch <target>` | Run batch pipeline |
368
+ | `.texas <target>` | Full/heavy operation |
369
+ | `.toast <target>` | Burn/clear caches |
370
+
341
371
  ## Domains
342
372
 
343
373
  ### MCP Domain — MusicBrainz, scores, Discogs, write operations
@@ -363,6 +393,14 @@ magmascript pi backup musicbrainz # backup + commit to GitHub
363
393
  magmascript pi traffic # nginx traffic analysis
364
394
  ```
365
395
 
396
+ ### MC1 Domain — Windows PC management (via SSH)
397
+ ```bash
398
+ magmascript mc1 status # all running Windows services
399
+ magmascript mc1 info # uptime, memory, CPU, disk
400
+ magmascript mc1 restart OllamaSvc # restart a Windows service
401
+ magmascript mc1 processes # top processes by CPU
402
+ ```
403
+
366
404
  ### GitHub Domain — Direct API access
367
405
  ```bash
368
406
  magmascript gh workflows # all workflow statuses
@@ -385,6 +423,7 @@ magmascript scores reset tetris # reset one game (backup created)
385
423
  ```bash
386
424
  magmascript archive check-format # validate HTML formatting
387
425
  magmascript archive bake-cache # inline MusicBrainz cache into pages
426
+ magmascript archive generate-stubs # generate stub HTML for new entities
388
427
  ```
389
428
 
390
429
  ### MusicBrainz Domain — MusicBrainz API client
@@ -500,13 +539,18 @@ gh_workflows # GitHub commands
500
539
  ## Documentation
501
540
 
502
541
  Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
503
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
504
542
  - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
543
+ - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
505
544
  - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
506
545
  - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
507
546
  - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
508
547
  - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
509
548
  - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
549
+ - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
550
+ - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
551
+ - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
552
+ - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
553
+ - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
510
554
  - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
511
555
  - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
512
556
 
@@ -30,6 +30,9 @@ magmascript/domains/lastfm/__init__.py
30
30
  magmascript/domains/lastfm/client.py
31
31
  magmascript/domains/mb/__init__.py
32
32
  magmascript/domains/mb/client.py
33
+ magmascript/domains/mc1/__init__.py
34
+ magmascript/domains/mc1/client.py
35
+ magmascript/domains/mc1/tools.py
33
36
  magmascript/domains/mcp/__init__.py
34
37
  magmascript/domains/mcp/client.py
35
38
  magmascript/domains/mcp/tools.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "magmascript"
7
- version = "2.0.1"
7
+ version = "2.2.0"
8
8
  description = "Scripting toolkit with domain-first subcommands"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -61,6 +61,20 @@ class TestCLI:
61
61
  captured = capsys.readouterr()
62
62
  assert "[]" in captured.out
63
63
 
64
+ @patch("magmascript.cli._dispatch_run")
65
+ def test_mgs_shorthand(self, mock_dispatch):
66
+ with patch("sys.argv", ["magmascript", "hello.mgs"]):
67
+ main()
68
+
69
+ mock_dispatch.assert_called_once_with("hello.mgs", [])
70
+
71
+ @patch("magmascript.cli._dispatch_run")
72
+ def test_mgs_shorthand_with_args(self, mock_dispatch):
73
+ with patch("sys.argv", ["magmascript", "script.mgs", "arg1", "arg2"]):
74
+ main()
75
+
76
+ mock_dispatch.assert_called_once_with("script.mgs", ["arg1", "arg2"])
77
+
64
78
 
65
79
  class TestCLIBrandCommands:
66
80
  def test_magma_help(self, capsys):
File without changes
File without changes