IncludeCPP 3.3.20__py3-none-any.whl → 3.4.2__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.
@@ -0,0 +1,2803 @@
1
+ """
2
+ CSSL Standard Modules
3
+ Provides standard library modules accessible via service-include
4
+
5
+ Available Modules:
6
+ @Time - Date, time, and duration operations
7
+ @Secrets - Secure credential and secret management
8
+ @Math - Mathematical operations and constants
9
+ @Crypto - Cryptographic functions (hashing, encoding)
10
+ @Net - Network operations (HTTP, sockets)
11
+ @IO - File and stream I/O operations
12
+ @JSON - JSON parsing and serialization
13
+ @Regex - Regular expression operations
14
+ @System - System information and process control
15
+ @Log - Logging and diagnostics
16
+ @Cache - In-memory caching with TTL support
17
+ @Queue - Message queue and task scheduling
18
+ @Format - String formatting and templating
19
+ @Desktop - Desktop widget creation (registered separately)
20
+ """
21
+
22
+ import os
23
+ import time
24
+ import math
25
+ import json
26
+ import re
27
+ import hashlib
28
+ import base64
29
+ import secrets
30
+ import threading
31
+ import queue
32
+ from datetime import datetime, timedelta
33
+ from typing import Any, Dict, List, Optional, Callable, Union
34
+
35
+
36
+ class CSSLModuleBase:
37
+ """Base class for CSSL standard modules"""
38
+
39
+ def __init__(self, runtime=None):
40
+ self.runtime = runtime
41
+ self._methods: Dict[str, Callable] = {}
42
+ self._register_methods()
43
+
44
+ def _register_methods(self):
45
+ """Override to register module methods"""
46
+ pass
47
+
48
+ def __getattr__(self, name: str) -> Any:
49
+ if name.startswith('_'):
50
+ raise AttributeError(name)
51
+ if name in self._methods:
52
+ return self._methods[name]
53
+ raise AttributeError(f"Module has no method '{name}'")
54
+
55
+ def get_method(self, name: str) -> Optional[Callable]:
56
+ return self._methods.get(name)
57
+
58
+ def list_methods(self) -> List[str]:
59
+ return sorted(self._methods.keys())
60
+
61
+
62
+ # =============================================================================
63
+ # @Time Module - Date, time, and duration operations
64
+ # =============================================================================
65
+
66
+ class TimeModule(CSSLModuleBase):
67
+ """
68
+ @Time - Date, time, and duration operations
69
+
70
+ Methods:
71
+ now() - Current timestamp (float)
72
+ timestamp() - Current timestamp (int)
73
+ date(fmt) - Current date string
74
+ time(fmt) - Current time string
75
+ datetime(fmt) - Current datetime string
76
+ parse(str, fmt) - Parse string to timestamp
77
+ format(ts, fmt) - Format timestamp to string
78
+ add(ts, days, hours, minutes, seconds) - Add duration
79
+ diff(ts1, ts2) - Difference in seconds
80
+ sleep(seconds) - Sleep for duration
81
+ year(), month(), day() - Current date components
82
+ hour(), minute(), second() - Current time components
83
+ weekday() - Day of week (0=Monday)
84
+ iso() - ISO 8601 formatted string
85
+ utc() - Current UTC timestamp
86
+ """
87
+
88
+ def _register_methods(self):
89
+ self._methods['now'] = self.now
90
+ self._methods['timestamp'] = self.timestamp
91
+ self._methods['date'] = self.date
92
+ self._methods['time'] = self.time_str
93
+ self._methods['datetime'] = self.datetime_str
94
+ self._methods['parse'] = self.parse
95
+ self._methods['format'] = self.format
96
+ self._methods['add'] = self.add
97
+ self._methods['diff'] = self.diff
98
+ self._methods['sleep'] = self.sleep
99
+ self._methods['year'] = self.year
100
+ self._methods['month'] = self.month
101
+ self._methods['day'] = self.day
102
+ self._methods['hour'] = self.hour
103
+ self._methods['minute'] = self.minute
104
+ self._methods['second'] = self.second
105
+ self._methods['weekday'] = self.weekday
106
+ self._methods['iso'] = self.iso
107
+ self._methods['utc'] = self.utc
108
+
109
+ def now(self) -> float:
110
+ return time.time()
111
+
112
+ def timestamp(self) -> int:
113
+ return int(time.time())
114
+
115
+ def date(self, fmt: str = '%Y-%m-%d') -> str:
116
+ return datetime.now().strftime(fmt)
117
+
118
+ def time_str(self, fmt: str = '%H:%M:%S') -> str:
119
+ return datetime.now().strftime(fmt)
120
+
121
+ def datetime_str(self, fmt: str = '%Y-%m-%d %H:%M:%S') -> str:
122
+ return datetime.now().strftime(fmt)
123
+
124
+ def parse(self, date_str: str, fmt: str = '%Y-%m-%d %H:%M:%S') -> float:
125
+ return datetime.strptime(date_str, fmt).timestamp()
126
+
127
+ def format(self, ts: float, fmt: str = '%Y-%m-%d %H:%M:%S') -> str:
128
+ return datetime.fromtimestamp(ts).strftime(fmt)
129
+
130
+ def add(self, ts: float = None, days: int = 0, hours: int = 0,
131
+ minutes: int = 0, seconds: int = 0) -> float:
132
+ if ts is None:
133
+ ts = time.time()
134
+ delta = timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
135
+ return ts + delta.total_seconds()
136
+
137
+ def diff(self, ts1: float, ts2: float) -> float:
138
+ return abs(ts1 - ts2)
139
+
140
+ def sleep(self, seconds: float) -> None:
141
+ time.sleep(seconds)
142
+
143
+ def year(self) -> int:
144
+ return datetime.now().year
145
+
146
+ def month(self) -> int:
147
+ return datetime.now().month
148
+
149
+ def day(self) -> int:
150
+ return datetime.now().day
151
+
152
+ def hour(self) -> int:
153
+ return datetime.now().hour
154
+
155
+ def minute(self) -> int:
156
+ return datetime.now().minute
157
+
158
+ def second(self) -> int:
159
+ return datetime.now().second
160
+
161
+ def weekday(self) -> int:
162
+ return datetime.now().weekday()
163
+
164
+ def iso(self) -> str:
165
+ return datetime.now().isoformat()
166
+
167
+ def utc(self) -> float:
168
+ return datetime.utcnow().timestamp()
169
+
170
+
171
+ # =============================================================================
172
+ # @Secrets Module - Secure credential and secret management
173
+ # =============================================================================
174
+
175
+ class SecretsModule(CSSLModuleBase):
176
+ """
177
+ @Secrets - Secure credential and secret management
178
+
179
+ Methods:
180
+ generate(length) - Generate secure random string
181
+ token(nbytes) - Generate secure token (hex)
182
+ token_bytes(nbytes) - Generate secure random bytes
183
+ token_urlsafe(nbytes) - Generate URL-safe token
184
+ choice(seq) - Secure random choice
185
+ randint(a, b) - Secure random integer
186
+ compare(a, b) - Constant-time string comparison
187
+ store(key, value) - Store secret (memory only)
188
+ retrieve(key) - Retrieve stored secret
189
+ delete(key) - Delete stored secret
190
+ env(name, default) - Get environment variable
191
+ """
192
+
193
+ def __init__(self, runtime=None):
194
+ super().__init__(runtime)
195
+ self._store: Dict[str, str] = {}
196
+
197
+ def _register_methods(self):
198
+ self._methods['generate'] = self.generate
199
+ self._methods['token'] = self.token
200
+ self._methods['token_bytes'] = self.token_bytes
201
+ self._methods['token_urlsafe'] = self.token_urlsafe
202
+ self._methods['choice'] = self.choice
203
+ self._methods['randint'] = self.randint
204
+ self._methods['compare'] = self.compare
205
+ self._methods['store'] = self.store
206
+ self._methods['retrieve'] = self.retrieve
207
+ self._methods['delete'] = self.delete
208
+ self._methods['env'] = self.env
209
+
210
+ def generate(self, length: int = 32) -> str:
211
+ alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
212
+ return ''.join(secrets.choice(alphabet) for _ in range(length))
213
+
214
+ def token(self, nbytes: int = 32) -> str:
215
+ return secrets.token_hex(nbytes)
216
+
217
+ def token_bytes(self, nbytes: int = 32) -> bytes:
218
+ return secrets.token_bytes(nbytes)
219
+
220
+ def token_urlsafe(self, nbytes: int = 32) -> str:
221
+ return secrets.token_urlsafe(nbytes)
222
+
223
+ def choice(self, seq: list) -> Any:
224
+ return secrets.choice(seq)
225
+
226
+ def randint(self, a: int, b: int) -> int:
227
+ return secrets.randbelow(b - a + 1) + a
228
+
229
+ def compare(self, a: str, b: str) -> bool:
230
+ return secrets.compare_digest(a, b)
231
+
232
+ def store(self, key: str, value: str) -> bool:
233
+ self._store[key] = value
234
+ return True
235
+
236
+ def retrieve(self, key: str) -> Optional[str]:
237
+ return self._store.get(key)
238
+
239
+ def delete(self, key: str) -> bool:
240
+ if key in self._store:
241
+ del self._store[key]
242
+ return True
243
+ return False
244
+
245
+ def env(self, name: str, default: str = None) -> Optional[str]:
246
+ return os.environ.get(name, default)
247
+
248
+
249
+ # =============================================================================
250
+ # @Math Module - Mathematical operations and constants
251
+ # =============================================================================
252
+
253
+ class MathModule(CSSLModuleBase):
254
+ """
255
+ @Math - Mathematical operations and constants
256
+
257
+ Constants:
258
+ PI, E, TAU, INF, NAN
259
+
260
+ Methods:
261
+ abs(x) - Absolute value
262
+ ceil(x) - Ceiling
263
+ floor(x) - Floor
264
+ round(x, digits) - Round to digits
265
+ trunc(x) - Truncate
266
+ sqrt(x) - Square root
267
+ pow(x, y) - Power
268
+ exp(x) - Exponential
269
+ log(x, base) - Logarithm
270
+ log10(x) - Log base 10
271
+ log2(x) - Log base 2
272
+ sin(x), cos(x), tan(x) - Trigonometric
273
+ asin(x), acos(x), atan(x) - Inverse trig
274
+ sinh(x), cosh(x), tanh(x) - Hyperbolic
275
+ degrees(x) - Radians to degrees
276
+ radians(x) - Degrees to radians
277
+ min(args), max(args) - Min/max values
278
+ sum(list), avg(list) - Aggregations
279
+ factorial(n) - Factorial
280
+ gcd(a, b) - Greatest common divisor
281
+ lcm(a, b) - Least common multiple
282
+ clamp(x, lo, hi) - Clamp value to range
283
+ lerp(a, b, t) - Linear interpolation
284
+ random() - Random float [0, 1)
285
+ randint(a, b) - Random integer [a, b]
286
+ """
287
+
288
+ def _register_methods(self):
289
+ # Constants
290
+ self._methods['PI'] = lambda: math.pi
291
+ self._methods['E'] = lambda: math.e
292
+ self._methods['TAU'] = lambda: math.tau
293
+ self._methods['INF'] = lambda: math.inf
294
+ self._methods['NAN'] = lambda: math.nan
295
+
296
+ # Basic operations
297
+ self._methods['abs'] = abs
298
+ self._methods['ceil'] = math.ceil
299
+ self._methods['floor'] = math.floor
300
+ self._methods['round'] = round
301
+ self._methods['trunc'] = math.trunc
302
+ self._methods['sqrt'] = math.sqrt
303
+ self._methods['pow'] = pow
304
+ self._methods['exp'] = math.exp
305
+ self._methods['log'] = self.log
306
+ self._methods['log10'] = math.log10
307
+ self._methods['log2'] = math.log2
308
+
309
+ # Trigonometry
310
+ self._methods['sin'] = math.sin
311
+ self._methods['cos'] = math.cos
312
+ self._methods['tan'] = math.tan
313
+ self._methods['asin'] = math.asin
314
+ self._methods['acos'] = math.acos
315
+ self._methods['atan'] = math.atan
316
+ self._methods['atan2'] = math.atan2
317
+ self._methods['sinh'] = math.sinh
318
+ self._methods['cosh'] = math.cosh
319
+ self._methods['tanh'] = math.tanh
320
+ self._methods['degrees'] = math.degrees
321
+ self._methods['radians'] = math.radians
322
+
323
+ # Aggregations
324
+ self._methods['min'] = min
325
+ self._methods['max'] = max
326
+ self._methods['sum'] = sum
327
+ self._methods['avg'] = self.avg
328
+
329
+ # Number theory
330
+ self._methods['factorial'] = math.factorial
331
+ self._methods['gcd'] = math.gcd
332
+ self._methods['lcm'] = self.lcm
333
+ self._methods['isfinite'] = math.isfinite
334
+ self._methods['isinf'] = math.isinf
335
+ self._methods['isnan'] = math.isnan
336
+
337
+ # Utility
338
+ self._methods['clamp'] = self.clamp
339
+ self._methods['lerp'] = self.lerp
340
+ self._methods['random'] = self.random
341
+ self._methods['randint'] = self.randint
342
+
343
+ def log(self, x: float, base: float = math.e) -> float:
344
+ return math.log(x, base)
345
+
346
+ def avg(self, items: list) -> float:
347
+ if not items:
348
+ return 0.0
349
+ return sum(items) / len(items)
350
+
351
+ def lcm(self, a: int, b: int) -> int:
352
+ return abs(a * b) // math.gcd(a, b)
353
+
354
+ def clamp(self, x: float, lo: float, hi: float) -> float:
355
+ return max(lo, min(hi, x))
356
+
357
+ def lerp(self, a: float, b: float, t: float) -> float:
358
+ return a + (b - a) * t
359
+
360
+ def random(self) -> float:
361
+ import random
362
+ return random.random()
363
+
364
+ def randint(self, a: int, b: int) -> int:
365
+ import random
366
+ return random.randint(a, b)
367
+
368
+
369
+ # =============================================================================
370
+ # @Crypto Module - Cryptographic functions
371
+ # =============================================================================
372
+
373
+ class CryptoModule(CSSLModuleBase):
374
+ """
375
+ @Crypto - Cryptographic functions
376
+
377
+ Methods:
378
+ md5(data) - MD5 hash
379
+ sha1(data) - SHA-1 hash
380
+ sha256(data) - SHA-256 hash
381
+ sha384(data) - SHA-384 hash
382
+ sha512(data) - SHA-512 hash
383
+ hmac(key, data, algo) - HMAC signature
384
+ base64_encode(data) - Base64 encode
385
+ base64_decode(data) - Base64 decode
386
+ hex_encode(data) - Hex encode
387
+ hex_decode(data) - Hex decode
388
+ uuid() - Generate UUID4
389
+ uuid1() - Generate UUID1
390
+ """
391
+
392
+ def _register_methods(self):
393
+ self._methods['md5'] = self.md5
394
+ self._methods['sha1'] = self.sha1
395
+ self._methods['sha256'] = self.sha256
396
+ self._methods['sha384'] = self.sha384
397
+ self._methods['sha512'] = self.sha512
398
+ self._methods['hmac'] = self.hmac
399
+ self._methods['base64_encode'] = self.base64_encode
400
+ self._methods['base64_decode'] = self.base64_decode
401
+ self._methods['hex_encode'] = self.hex_encode
402
+ self._methods['hex_decode'] = self.hex_decode
403
+ self._methods['uuid'] = self.uuid
404
+ self._methods['uuid1'] = self.uuid1
405
+
406
+ def _to_bytes(self, data: Union[str, bytes]) -> bytes:
407
+ if isinstance(data, str):
408
+ return data.encode('utf-8')
409
+ return data
410
+
411
+ def md5(self, data: Union[str, bytes]) -> str:
412
+ return hashlib.md5(self._to_bytes(data)).hexdigest()
413
+
414
+ def sha1(self, data: Union[str, bytes]) -> str:
415
+ return hashlib.sha1(self._to_bytes(data)).hexdigest()
416
+
417
+ def sha256(self, data: Union[str, bytes]) -> str:
418
+ return hashlib.sha256(self._to_bytes(data)).hexdigest()
419
+
420
+ def sha384(self, data: Union[str, bytes]) -> str:
421
+ return hashlib.sha384(self._to_bytes(data)).hexdigest()
422
+
423
+ def sha512(self, data: Union[str, bytes]) -> str:
424
+ return hashlib.sha512(self._to_bytes(data)).hexdigest()
425
+
426
+ def hmac(self, key: Union[str, bytes], data: Union[str, bytes],
427
+ algo: str = 'sha256') -> str:
428
+ import hmac as hmac_lib
429
+ hash_func = getattr(hashlib, algo, hashlib.sha256)
430
+ return hmac_lib.new(
431
+ self._to_bytes(key),
432
+ self._to_bytes(data),
433
+ hash_func
434
+ ).hexdigest()
435
+
436
+ def base64_encode(self, data: Union[str, bytes]) -> str:
437
+ return base64.b64encode(self._to_bytes(data)).decode('utf-8')
438
+
439
+ def base64_decode(self, data: str) -> str:
440
+ return base64.b64decode(data).decode('utf-8')
441
+
442
+ def hex_encode(self, data: Union[str, bytes]) -> str:
443
+ return self._to_bytes(data).hex()
444
+
445
+ def hex_decode(self, data: str) -> str:
446
+ return bytes.fromhex(data).decode('utf-8')
447
+
448
+ def uuid(self) -> str:
449
+ import uuid
450
+ return str(uuid.uuid4())
451
+
452
+ def uuid1(self) -> str:
453
+ import uuid
454
+ return str(uuid.uuid1())
455
+
456
+
457
+ # =============================================================================
458
+ # @Net Module - Network operations
459
+ # =============================================================================
460
+
461
+ class NetModule(CSSLModuleBase):
462
+ """
463
+ @Net - Network operations
464
+
465
+ Methods:
466
+ get(url, headers) - HTTP GET request
467
+ post(url, data, headers) - HTTP POST request
468
+ put(url, data, headers) - HTTP PUT request
469
+ delete(url, headers) - HTTP DELETE request
470
+ download(url, path) - Download file
471
+ hostname() - Get local hostname
472
+ ip() - Get local IP address
473
+ ping(host) - Check if host is reachable
474
+ url_encode(data) - URL encode string/dict
475
+ url_decode(data) - URL decode string
476
+ parse_url(url) - Parse URL into components
477
+ """
478
+
479
+ def _register_methods(self):
480
+ self._methods['get'] = self.http_get
481
+ self._methods['post'] = self.http_post
482
+ self._methods['put'] = self.http_put
483
+ self._methods['delete'] = self.http_delete
484
+ self._methods['download'] = self.download
485
+ self._methods['hostname'] = self.hostname
486
+ self._methods['ip'] = self.ip
487
+ self._methods['ping'] = self.ping
488
+ self._methods['url_encode'] = self.url_encode
489
+ self._methods['url_decode'] = self.url_decode
490
+ self._methods['parse_url'] = self.parse_url
491
+
492
+ def http_get(self, url: str, headers: Dict = None) -> Dict:
493
+ try:
494
+ import urllib.request
495
+ import urllib.error
496
+ req = urllib.request.Request(url, headers=headers or {})
497
+ with urllib.request.urlopen(req, timeout=30) as resp:
498
+ return {
499
+ 'status': resp.status,
500
+ 'headers': dict(resp.headers),
501
+ 'body': resp.read().decode('utf-8')
502
+ }
503
+ except urllib.error.HTTPError as e:
504
+ return {'status': e.code, 'error': str(e), 'body': ''}
505
+ except Exception as e:
506
+ return {'status': 0, 'error': str(e), 'body': ''}
507
+
508
+ def http_post(self, url: str, data: Union[str, Dict] = None,
509
+ headers: Dict = None) -> Dict:
510
+ try:
511
+ import urllib.request
512
+ import urllib.error
513
+ if isinstance(data, dict):
514
+ data = json.dumps(data).encode('utf-8')
515
+ headers = headers or {}
516
+ headers['Content-Type'] = 'application/json'
517
+ elif isinstance(data, str):
518
+ data = data.encode('utf-8')
519
+ req = urllib.request.Request(url, data=data, headers=headers or {})
520
+ with urllib.request.urlopen(req, timeout=30) as resp:
521
+ return {
522
+ 'status': resp.status,
523
+ 'headers': dict(resp.headers),
524
+ 'body': resp.read().decode('utf-8')
525
+ }
526
+ except urllib.error.HTTPError as e:
527
+ return {'status': e.code, 'error': str(e), 'body': ''}
528
+ except Exception as e:
529
+ return {'status': 0, 'error': str(e), 'body': ''}
530
+
531
+ def http_put(self, url: str, data: Union[str, Dict] = None,
532
+ headers: Dict = None) -> Dict:
533
+ try:
534
+ import urllib.request
535
+ if isinstance(data, dict):
536
+ data = json.dumps(data).encode('utf-8')
537
+ headers = headers or {}
538
+ headers['Content-Type'] = 'application/json'
539
+ elif isinstance(data, str):
540
+ data = data.encode('utf-8')
541
+ req = urllib.request.Request(url, data=data, headers=headers or {},
542
+ method='PUT')
543
+ with urllib.request.urlopen(req, timeout=30) as resp:
544
+ return {
545
+ 'status': resp.status,
546
+ 'headers': dict(resp.headers),
547
+ 'body': resp.read().decode('utf-8')
548
+ }
549
+ except Exception as e:
550
+ return {'status': 0, 'error': str(e), 'body': ''}
551
+
552
+ def http_delete(self, url: str, headers: Dict = None) -> Dict:
553
+ try:
554
+ import urllib.request
555
+ req = urllib.request.Request(url, headers=headers or {},
556
+ method='DELETE')
557
+ with urllib.request.urlopen(req, timeout=30) as resp:
558
+ return {
559
+ 'status': resp.status,
560
+ 'headers': dict(resp.headers),
561
+ 'body': resp.read().decode('utf-8')
562
+ }
563
+ except Exception as e:
564
+ return {'status': 0, 'error': str(e), 'body': ''}
565
+
566
+ def download(self, url: str, path: str) -> bool:
567
+ try:
568
+ import urllib.request
569
+ urllib.request.urlretrieve(url, path)
570
+ return True
571
+ except Exception:
572
+ return False
573
+
574
+ def hostname(self) -> str:
575
+ import socket
576
+ return socket.gethostname()
577
+
578
+ def ip(self) -> str:
579
+ import socket
580
+ try:
581
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
582
+ s.connect(('8.8.8.8', 80))
583
+ ip = s.getsockname()[0]
584
+ s.close()
585
+ return ip
586
+ except Exception:
587
+ return '127.0.0.1'
588
+
589
+ def ping(self, host: str) -> bool:
590
+ import subprocess
591
+ import platform
592
+ param = '-n' if platform.system().lower() == 'windows' else '-c'
593
+ try:
594
+ result = subprocess.run(
595
+ ['ping', param, '1', host],
596
+ capture_output=True,
597
+ timeout=5
598
+ )
599
+ return result.returncode == 0
600
+ except Exception:
601
+ return False
602
+
603
+ def url_encode(self, data: Union[str, Dict]) -> str:
604
+ import urllib.parse
605
+ if isinstance(data, dict):
606
+ return urllib.parse.urlencode(data)
607
+ return urllib.parse.quote(str(data))
608
+
609
+ def url_decode(self, data: str) -> str:
610
+ import urllib.parse
611
+ return urllib.parse.unquote(data)
612
+
613
+ def parse_url(self, url: str) -> Dict:
614
+ import urllib.parse
615
+ parsed = urllib.parse.urlparse(url)
616
+ return {
617
+ 'scheme': parsed.scheme,
618
+ 'netloc': parsed.netloc,
619
+ 'path': parsed.path,
620
+ 'params': parsed.params,
621
+ 'query': parsed.query,
622
+ 'fragment': parsed.fragment,
623
+ 'hostname': parsed.hostname,
624
+ 'port': parsed.port
625
+ }
626
+
627
+
628
+ # =============================================================================
629
+ # @IO Module - File and stream I/O operations
630
+ # =============================================================================
631
+
632
+ class IOModule(CSSLModuleBase):
633
+ """
634
+ @IO - File and stream I/O operations
635
+
636
+ Methods:
637
+ read(path) - Read entire file
638
+ read_lines(path) - Read file as lines
639
+ read_bytes(path) - Read file as bytes
640
+ write(path, content) - Write content to file
641
+ write_lines(path, lines) - Write lines to file
642
+ write_bytes(path, data) - Write bytes to file
643
+ append(path, content) - Append to file
644
+ exists(path) - Check if path exists
645
+ isfile(path) - Check if path is file
646
+ isdir(path) - Check if path is directory
647
+ mkdir(path) - Create directory
648
+ mkdirs(path) - Create directory tree
649
+ remove(path) - Remove file
650
+ rmdir(path) - Remove directory
651
+ rename(old, new) - Rename file/directory
652
+ copy(src, dst) - Copy file
653
+ move(src, dst) - Move file
654
+ listdir(path) - List directory contents
655
+ glob(pattern) - Find files by pattern
656
+ size(path) - Get file size
657
+ mtime(path) - Get modification time
658
+ """
659
+
660
+ def _register_methods(self):
661
+ self._methods['read'] = self.read
662
+ self._methods['read_lines'] = self.read_lines
663
+ self._methods['read_bytes'] = self.read_bytes
664
+ self._methods['write'] = self.write
665
+ self._methods['write_lines'] = self.write_lines
666
+ self._methods['write_bytes'] = self.write_bytes
667
+ self._methods['append'] = self.append
668
+ self._methods['exists'] = os.path.exists
669
+ self._methods['isfile'] = os.path.isfile
670
+ self._methods['isdir'] = os.path.isdir
671
+ self._methods['mkdir'] = self.mkdir
672
+ self._methods['mkdirs'] = self.mkdirs
673
+ self._methods['remove'] = self.remove
674
+ self._methods['rmdir'] = self.rmdir
675
+ self._methods['rename'] = os.rename
676
+ self._methods['copy'] = self.copy
677
+ self._methods['move'] = self.move
678
+ self._methods['listdir'] = os.listdir
679
+ self._methods['glob'] = self.glob
680
+ self._methods['size'] = self.size
681
+ self._methods['mtime'] = self.mtime
682
+
683
+ def read(self, path: str, encoding: str = 'utf-8') -> str:
684
+ with open(path, 'r', encoding=encoding) as f:
685
+ return f.read()
686
+
687
+ def read_lines(self, path: str, encoding: str = 'utf-8') -> List[str]:
688
+ with open(path, 'r', encoding=encoding) as f:
689
+ return f.readlines()
690
+
691
+ def read_bytes(self, path: str) -> bytes:
692
+ with open(path, 'rb') as f:
693
+ return f.read()
694
+
695
+ def write(self, path: str, content: str, encoding: str = 'utf-8') -> bool:
696
+ with open(path, 'w', encoding=encoding) as f:
697
+ f.write(content)
698
+ return True
699
+
700
+ def write_lines(self, path: str, lines: List[str],
701
+ encoding: str = 'utf-8') -> bool:
702
+ with open(path, 'w', encoding=encoding) as f:
703
+ f.writelines(lines)
704
+ return True
705
+
706
+ def write_bytes(self, path: str, data: bytes) -> bool:
707
+ with open(path, 'wb') as f:
708
+ f.write(data)
709
+ return True
710
+
711
+ def append(self, path: str, content: str, encoding: str = 'utf-8') -> bool:
712
+ with open(path, 'a', encoding=encoding) as f:
713
+ f.write(content)
714
+ return True
715
+
716
+ def mkdir(self, path: str) -> bool:
717
+ os.makedirs(path, exist_ok=True)
718
+ return True
719
+
720
+ def mkdirs(self, path: str) -> bool:
721
+ os.makedirs(path, exist_ok=True)
722
+ return True
723
+
724
+ def remove(self, path: str) -> bool:
725
+ if os.path.isfile(path):
726
+ os.remove(path)
727
+ return True
728
+ return False
729
+
730
+ def rmdir(self, path: str) -> bool:
731
+ import shutil
732
+ if os.path.isdir(path):
733
+ shutil.rmtree(path)
734
+ return True
735
+ return False
736
+
737
+ def copy(self, src: str, dst: str) -> bool:
738
+ import shutil
739
+ shutil.copy2(src, dst)
740
+ return True
741
+
742
+ def move(self, src: str, dst: str) -> bool:
743
+ import shutil
744
+ shutil.move(src, dst)
745
+ return True
746
+
747
+ def glob(self, pattern: str) -> List[str]:
748
+ import glob as glob_module
749
+ return glob_module.glob(pattern, recursive=True)
750
+
751
+ def size(self, path: str) -> int:
752
+ return os.path.getsize(path)
753
+
754
+ def mtime(self, path: str) -> float:
755
+ return os.path.getmtime(path)
756
+
757
+
758
+ # =============================================================================
759
+ # @JSON Module - JSON parsing and serialization
760
+ # =============================================================================
761
+
762
+ class JSONModule(CSSLModuleBase):
763
+ """
764
+ @JSON - JSON parsing and serialization
765
+
766
+ Methods:
767
+ parse(string) - Parse JSON string to object
768
+ stringify(obj, indent) - Convert object to JSON string
769
+ read(path) - Read JSON file
770
+ write(path, obj, indent) - Write JSON file
771
+ valid(string) - Check if string is valid JSON
772
+ get(obj, path, default) - Get nested value by path
773
+ set(obj, path, value) - Set nested value by path
774
+ merge(obj1, obj2) - Deep merge two objects
775
+ """
776
+
777
+ def _register_methods(self):
778
+ self._methods['parse'] = self.parse
779
+ self._methods['stringify'] = self.stringify
780
+ self._methods['read'] = self.read_file
781
+ self._methods['write'] = self.write_file
782
+ self._methods['valid'] = self.valid
783
+ self._methods['get'] = self.get_path
784
+ self._methods['set'] = self.set_path
785
+ self._methods['merge'] = self.merge
786
+
787
+ def parse(self, string: str) -> Any:
788
+ return json.loads(string)
789
+
790
+ def stringify(self, obj: Any, indent: int = None) -> str:
791
+ return json.dumps(obj, indent=indent, ensure_ascii=False)
792
+
793
+ def read_file(self, path: str) -> Any:
794
+ with open(path, 'r', encoding='utf-8') as f:
795
+ return json.load(f)
796
+
797
+ def write_file(self, path: str, obj: Any, indent: int = 2) -> bool:
798
+ with open(path, 'w', encoding='utf-8') as f:
799
+ json.dump(obj, f, indent=indent, ensure_ascii=False)
800
+ return True
801
+
802
+ def valid(self, string: str) -> bool:
803
+ try:
804
+ json.loads(string)
805
+ return True
806
+ except Exception:
807
+ return False
808
+
809
+ def get_path(self, obj: Any, path: str, default: Any = None) -> Any:
810
+ keys = path.split('.')
811
+ current = obj
812
+ for key in keys:
813
+ if isinstance(current, dict) and key in current:
814
+ current = current[key]
815
+ elif isinstance(current, list):
816
+ try:
817
+ current = current[int(key)]
818
+ except (ValueError, IndexError):
819
+ return default
820
+ else:
821
+ return default
822
+ return current
823
+
824
+ def set_path(self, obj: Dict, path: str, value: Any) -> Dict:
825
+ keys = path.split('.')
826
+ current = obj
827
+ for key in keys[:-1]:
828
+ if key not in current:
829
+ current[key] = {}
830
+ current = current[key]
831
+ current[keys[-1]] = value
832
+ return obj
833
+
834
+ def merge(self, obj1: Dict, obj2: Dict) -> Dict:
835
+ result = obj1.copy()
836
+ for key, value in obj2.items():
837
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
838
+ result[key] = self.merge(result[key], value)
839
+ else:
840
+ result[key] = value
841
+ return result
842
+
843
+
844
+ # =============================================================================
845
+ # @Regex Module - Regular expression operations
846
+ # =============================================================================
847
+
848
+ class RegexModule(CSSLModuleBase):
849
+ """
850
+ @Regex - Regular expression operations
851
+
852
+ Methods:
853
+ match(pattern, string) - Match pattern at start
854
+ search(pattern, string) - Search for pattern anywhere
855
+ findall(pattern, string) - Find all matches
856
+ finditer(pattern, string)- Iterate over matches
857
+ sub(pattern, repl, string, count) - Substitute matches
858
+ split(pattern, string) - Split by pattern
859
+ escape(string) - Escape special characters
860
+ compile(pattern, flags) - Compile pattern
861
+ test(pattern, string) - Test if pattern matches
862
+ """
863
+
864
+ def _register_methods(self):
865
+ self._methods['match'] = self.match
866
+ self._methods['search'] = self.search
867
+ self._methods['findall'] = re.findall
868
+ self._methods['finditer'] = self.finditer
869
+ self._methods['sub'] = re.sub
870
+ self._methods['split'] = re.split
871
+ self._methods['escape'] = re.escape
872
+ self._methods['compile'] = re.compile
873
+ self._methods['test'] = self.test
874
+
875
+ def match(self, pattern: str, string: str) -> Optional[Dict]:
876
+ m = re.match(pattern, string)
877
+ if m:
878
+ return {
879
+ 'match': m.group(),
880
+ 'groups': m.groups(),
881
+ 'start': m.start(),
882
+ 'end': m.end(),
883
+ 'span': m.span()
884
+ }
885
+ return None
886
+
887
+ def search(self, pattern: str, string: str) -> Optional[Dict]:
888
+ m = re.search(pattern, string)
889
+ if m:
890
+ return {
891
+ 'match': m.group(),
892
+ 'groups': m.groups(),
893
+ 'start': m.start(),
894
+ 'end': m.end(),
895
+ 'span': m.span()
896
+ }
897
+ return None
898
+
899
+ def finditer(self, pattern: str, string: str) -> List[Dict]:
900
+ return [{
901
+ 'match': m.group(),
902
+ 'groups': m.groups(),
903
+ 'start': m.start(),
904
+ 'end': m.end()
905
+ } for m in re.finditer(pattern, string)]
906
+
907
+ def test(self, pattern: str, string: str) -> bool:
908
+ return re.search(pattern, string) is not None
909
+
910
+
911
+ # =============================================================================
912
+ # @System Module - System information and process control
913
+ # =============================================================================
914
+
915
+ class SystemModule(CSSLModuleBase):
916
+ """
917
+ @System - System information and process control
918
+
919
+ Methods:
920
+ platform() - Operating system name
921
+ version() - OS version
922
+ arch() - CPU architecture
923
+ hostname() - Computer hostname
924
+ user() - Current username
925
+ home() - User home directory
926
+ cwd() - Current working directory
927
+ chdir(path) - Change directory
928
+ env(name, default) - Get environment variable
929
+ setenv(name, value) - Set environment variable
930
+ exec(cmd) - Execute shell command
931
+ spawn(cmd) - Spawn background process
932
+ pid() - Current process ID
933
+ cpus() - Number of CPUs
934
+ memory() - Memory info
935
+ uptime() - System uptime
936
+ exit(code) - Exit with code
937
+ """
938
+
939
+ def _register_methods(self):
940
+ self._methods['platform'] = self.platform
941
+ self._methods['version'] = self.version
942
+ self._methods['arch'] = self.arch
943
+ self._methods['hostname'] = self.hostname
944
+ self._methods['user'] = self.user
945
+ self._methods['home'] = self.home
946
+ self._methods['cwd'] = os.getcwd
947
+ self._methods['chdir'] = os.chdir
948
+ self._methods['env'] = self.env
949
+ self._methods['setenv'] = self.setenv
950
+ self._methods['exec'] = self.exec_cmd
951
+ self._methods['spawn'] = self.spawn
952
+ self._methods['pid'] = os.getpid
953
+ self._methods['cpus'] = self.cpus
954
+ self._methods['memory'] = self.memory
955
+ self._methods['uptime'] = self.uptime
956
+ self._methods['exit'] = self.exit_sys
957
+
958
+ def platform(self) -> str:
959
+ import platform
960
+ return platform.system()
961
+
962
+ def version(self) -> str:
963
+ import platform
964
+ return platform.version()
965
+
966
+ def arch(self) -> str:
967
+ import platform
968
+ return platform.machine()
969
+
970
+ def hostname(self) -> str:
971
+ import socket
972
+ return socket.gethostname()
973
+
974
+ def user(self) -> str:
975
+ return os.environ.get('USER') or os.environ.get('USERNAME', '')
976
+
977
+ def home(self) -> str:
978
+ return os.path.expanduser('~')
979
+
980
+ def env(self, name: str, default: str = None) -> Optional[str]:
981
+ return os.environ.get(name, default)
982
+
983
+ def setenv(self, name: str, value: str) -> bool:
984
+ os.environ[name] = value
985
+ return True
986
+
987
+ def exec_cmd(self, cmd: str) -> Dict:
988
+ import subprocess
989
+ try:
990
+ result = subprocess.run(
991
+ cmd, shell=True, capture_output=True, text=True, timeout=300
992
+ )
993
+ return {
994
+ 'returncode': result.returncode,
995
+ 'stdout': result.stdout,
996
+ 'stderr': result.stderr
997
+ }
998
+ except subprocess.TimeoutExpired:
999
+ return {'returncode': -1, 'stdout': '', 'stderr': 'Command timed out'}
1000
+ except Exception as e:
1001
+ return {'returncode': -1, 'stdout': '', 'stderr': str(e)}
1002
+
1003
+ def spawn(self, cmd: str) -> int:
1004
+ import subprocess
1005
+ proc = subprocess.Popen(cmd, shell=True)
1006
+ return proc.pid
1007
+
1008
+ def cpus(self) -> int:
1009
+ return os.cpu_count() or 1
1010
+
1011
+ def memory(self) -> Dict:
1012
+ try:
1013
+ import psutil
1014
+ mem = psutil.virtual_memory()
1015
+ return {
1016
+ 'total': mem.total,
1017
+ 'available': mem.available,
1018
+ 'used': mem.used,
1019
+ 'percent': mem.percent
1020
+ }
1021
+ except ImportError:
1022
+ return {'total': 0, 'available': 0, 'used': 0, 'percent': 0}
1023
+
1024
+ def uptime(self) -> float:
1025
+ try:
1026
+ import psutil
1027
+ return time.time() - psutil.boot_time()
1028
+ except ImportError:
1029
+ return 0.0
1030
+
1031
+ def exit_sys(self, code: int = 0) -> None:
1032
+ raise SystemExit(code)
1033
+
1034
+
1035
+ # =============================================================================
1036
+ # @Log Module - Logging and diagnostics
1037
+ # =============================================================================
1038
+
1039
+ class LogModule(CSSLModuleBase):
1040
+ """
1041
+ @Log - Logging and diagnostics
1042
+
1043
+ Methods:
1044
+ debug(msg) - Debug level message
1045
+ info(msg) - Info level message
1046
+ warn(msg) - Warning level message
1047
+ error(msg) - Error level message
1048
+ fatal(msg) - Fatal level message
1049
+ log(level, msg) - Log with custom level
1050
+ setLevel(level) - Set minimum log level
1051
+ setOutput(path) - Set log file output
1052
+ setFormat(fmt) - Set log format
1053
+ clear() - Clear log buffer
1054
+ history(count) - Get recent log entries
1055
+ """
1056
+
1057
+ LEVELS = {'DEBUG': 0, 'INFO': 1, 'WARN': 2, 'ERROR': 3, 'FATAL': 4}
1058
+
1059
+ def __init__(self, runtime=None):
1060
+ super().__init__(runtime)
1061
+ self._level = 'DEBUG'
1062
+ self._output = None
1063
+ self._format = '[{timestamp}] [{level}] {message}'
1064
+ self._history: List[Dict] = []
1065
+ self._max_history = 1000
1066
+
1067
+ def _register_methods(self):
1068
+ self._methods['debug'] = self.debug
1069
+ self._methods['info'] = self.info
1070
+ self._methods['warn'] = self.warn
1071
+ self._methods['error'] = self.error
1072
+ self._methods['fatal'] = self.fatal
1073
+ self._methods['log'] = self.log
1074
+ self._methods['setLevel'] = self.setLevel
1075
+ self._methods['setOutput'] = self.setOutput
1076
+ self._methods['setFormat'] = self.setFormat
1077
+ self._methods['clear'] = self.clear
1078
+ self._methods['history'] = self.history
1079
+
1080
+ def _write(self, level: str, msg: str):
1081
+ if self.LEVELS.get(level, 0) < self.LEVELS.get(self._level, 0):
1082
+ return
1083
+
1084
+ entry = {
1085
+ 'timestamp': datetime.now().isoformat(),
1086
+ 'level': level,
1087
+ 'message': msg
1088
+ }
1089
+
1090
+ formatted = self._format.format(**entry)
1091
+ print(formatted)
1092
+
1093
+ self._history.append(entry)
1094
+ if len(self._history) > self._max_history:
1095
+ self._history.pop(0)
1096
+
1097
+ if self._output:
1098
+ try:
1099
+ with open(self._output, 'a', encoding='utf-8') as f:
1100
+ f.write(formatted + '\n')
1101
+ except Exception:
1102
+ pass
1103
+
1104
+ def debug(self, msg: str):
1105
+ self._write('DEBUG', msg)
1106
+
1107
+ def info(self, msg: str):
1108
+ self._write('INFO', msg)
1109
+
1110
+ def warn(self, msg: str):
1111
+ self._write('WARN', msg)
1112
+
1113
+ def error(self, msg: str):
1114
+ self._write('ERROR', msg)
1115
+
1116
+ def fatal(self, msg: str):
1117
+ self._write('FATAL', msg)
1118
+
1119
+ def log(self, level: str, msg: str):
1120
+ self._write(level.upper(), msg)
1121
+
1122
+ def setLevel(self, level: str) -> bool:
1123
+ if level.upper() in self.LEVELS:
1124
+ self._level = level.upper()
1125
+ return True
1126
+ return False
1127
+
1128
+ def setOutput(self, path: str) -> bool:
1129
+ self._output = path
1130
+ return True
1131
+
1132
+ def setFormat(self, fmt: str) -> bool:
1133
+ self._format = fmt
1134
+ return True
1135
+
1136
+ def clear(self):
1137
+ self._history.clear()
1138
+
1139
+ def history(self, count: int = 100) -> List[Dict]:
1140
+ return self._history[-count:]
1141
+
1142
+
1143
+ # =============================================================================
1144
+ # @Cache Module - In-memory caching with TTL support
1145
+ # =============================================================================
1146
+
1147
+ class CacheModule(CSSLModuleBase):
1148
+ """
1149
+ @Cache - In-memory caching with TTL support
1150
+
1151
+ Methods:
1152
+ get(key, default) - Get cached value
1153
+ set(key, value, ttl) - Set value with optional TTL (seconds)
1154
+ has(key) - Check if key exists and not expired
1155
+ delete(key) - Delete cached value
1156
+ clear() - Clear all cached values
1157
+ keys() - Get all cache keys
1158
+ size() - Get number of cached items
1159
+ stats() - Get cache statistics
1160
+ cleanup() - Remove expired entries
1161
+ """
1162
+
1163
+ def __init__(self, runtime=None):
1164
+ super().__init__(runtime)
1165
+ self._cache: Dict[str, Dict] = {}
1166
+ self._hits = 0
1167
+ self._misses = 0
1168
+
1169
+ def _register_methods(self):
1170
+ self._methods['get'] = self.get
1171
+ self._methods['set'] = self.set
1172
+ self._methods['has'] = self.has
1173
+ self._methods['delete'] = self.delete
1174
+ self._methods['clear'] = self.clear
1175
+ self._methods['keys'] = self.keys
1176
+ self._methods['size'] = self.size
1177
+ self._methods['stats'] = self.stats
1178
+ self._methods['cleanup'] = self.cleanup
1179
+
1180
+ def _is_expired(self, entry: Dict) -> bool:
1181
+ if entry.get('ttl') is None:
1182
+ return False
1183
+ return time.time() > entry['expires']
1184
+
1185
+ def get(self, key: str, default: Any = None) -> Any:
1186
+ entry = self._cache.get(key)
1187
+ if entry is None or self._is_expired(entry):
1188
+ self._misses += 1
1189
+ if entry:
1190
+ del self._cache[key]
1191
+ return default
1192
+ self._hits += 1
1193
+ return entry['value']
1194
+
1195
+ def set(self, key: str, value: Any, ttl: int = None) -> bool:
1196
+ entry = {'value': value, 'ttl': ttl}
1197
+ if ttl is not None:
1198
+ entry['expires'] = time.time() + ttl
1199
+ self._cache[key] = entry
1200
+ return True
1201
+
1202
+ def has(self, key: str) -> bool:
1203
+ entry = self._cache.get(key)
1204
+ if entry is None:
1205
+ return False
1206
+ if self._is_expired(entry):
1207
+ del self._cache[key]
1208
+ return False
1209
+ return True
1210
+
1211
+ def delete(self, key: str) -> bool:
1212
+ if key in self._cache:
1213
+ del self._cache[key]
1214
+ return True
1215
+ return False
1216
+
1217
+ def clear(self):
1218
+ self._cache.clear()
1219
+ self._hits = 0
1220
+ self._misses = 0
1221
+
1222
+ def keys(self) -> List[str]:
1223
+ self.cleanup()
1224
+ return list(self._cache.keys())
1225
+
1226
+ def size(self) -> int:
1227
+ self.cleanup()
1228
+ return len(self._cache)
1229
+
1230
+ def stats(self) -> Dict:
1231
+ return {
1232
+ 'size': len(self._cache),
1233
+ 'hits': self._hits,
1234
+ 'misses': self._misses,
1235
+ 'hit_rate': self._hits / (self._hits + self._misses) if (self._hits + self._misses) > 0 else 0
1236
+ }
1237
+
1238
+ def cleanup(self):
1239
+ expired = [k for k, v in self._cache.items() if self._is_expired(v)]
1240
+ for k in expired:
1241
+ del self._cache[k]
1242
+
1243
+
1244
+ # =============================================================================
1245
+ # @Queue Module - Message queue and task scheduling
1246
+ # =============================================================================
1247
+
1248
+ class QueueModule(CSSLModuleBase):
1249
+ """
1250
+ @Queue - Message queue and task scheduling
1251
+
1252
+ Methods:
1253
+ create(name) - Create a named queue
1254
+ push(name, item) - Push item to queue
1255
+ pop(name, timeout) - Pop item from queue (blocking)
1256
+ peek(name) - Peek at front item without removing
1257
+ size(name) - Get queue size
1258
+ empty(name) - Check if queue is empty
1259
+ clear(name) - Clear queue
1260
+ list() - List all queue names
1261
+ delete(name) - Delete a queue
1262
+ schedule(func, delay) - Schedule function execution
1263
+ interval(func, interval) - Repeat function at interval
1264
+ cancel(task_id) - Cancel scheduled task
1265
+ """
1266
+
1267
+ def __init__(self, runtime=None):
1268
+ super().__init__(runtime)
1269
+ self._queues: Dict[str, queue.Queue] = {}
1270
+ self._tasks: Dict[str, threading.Timer] = {}
1271
+ self._task_counter = 0
1272
+
1273
+ def _register_methods(self):
1274
+ self._methods['create'] = self.create
1275
+ self._methods['push'] = self.push
1276
+ self._methods['pop'] = self.pop
1277
+ self._methods['peek'] = self.peek
1278
+ self._methods['size'] = self.size
1279
+ self._methods['empty'] = self.empty
1280
+ self._methods['clear'] = self.clear
1281
+ self._methods['list'] = self.list_queues
1282
+ self._methods['delete'] = self.delete
1283
+ self._methods['schedule'] = self.schedule
1284
+ self._methods['interval'] = self.interval
1285
+ self._methods['cancel'] = self.cancel
1286
+
1287
+ def create(self, name: str) -> bool:
1288
+ if name not in self._queues:
1289
+ self._queues[name] = queue.Queue()
1290
+ return True
1291
+
1292
+ def push(self, name: str, item: Any) -> bool:
1293
+ self.create(name)
1294
+ self._queues[name].put(item)
1295
+ return True
1296
+
1297
+ def pop(self, name: str, timeout: float = None) -> Any:
1298
+ if name not in self._queues:
1299
+ return None
1300
+ try:
1301
+ return self._queues[name].get(timeout=timeout)
1302
+ except queue.Empty:
1303
+ return None
1304
+
1305
+ def peek(self, name: str) -> Any:
1306
+ if name not in self._queues:
1307
+ return None
1308
+ q = self._queues[name]
1309
+ if q.empty():
1310
+ return None
1311
+ # Peek without removing
1312
+ with q.mutex:
1313
+ if q.queue:
1314
+ return q.queue[0]
1315
+ return None
1316
+
1317
+ def size(self, name: str) -> int:
1318
+ if name not in self._queues:
1319
+ return 0
1320
+ return self._queues[name].qsize()
1321
+
1322
+ def empty(self, name: str) -> bool:
1323
+ if name not in self._queues:
1324
+ return True
1325
+ return self._queues[name].empty()
1326
+
1327
+ def clear(self, name: str) -> bool:
1328
+ if name in self._queues:
1329
+ with self._queues[name].mutex:
1330
+ self._queues[name].queue.clear()
1331
+ return True
1332
+
1333
+ def list_queues(self) -> List[str]:
1334
+ return list(self._queues.keys())
1335
+
1336
+ def delete(self, name: str) -> bool:
1337
+ if name in self._queues:
1338
+ del self._queues[name]
1339
+ return True
1340
+ return False
1341
+
1342
+ def schedule(self, func: Callable, delay: float) -> str:
1343
+ self._task_counter += 1
1344
+ task_id = f"task_{self._task_counter}"
1345
+ timer = threading.Timer(delay, func)
1346
+ self._tasks[task_id] = timer
1347
+ timer.start()
1348
+ return task_id
1349
+
1350
+ def interval(self, func: Callable, interval_secs: float) -> str:
1351
+ self._task_counter += 1
1352
+ task_id = f"interval_{self._task_counter}"
1353
+
1354
+ def repeat():
1355
+ if task_id in self._tasks:
1356
+ func()
1357
+ timer = threading.Timer(interval_secs, repeat)
1358
+ self._tasks[task_id] = timer
1359
+ timer.start()
1360
+
1361
+ timer = threading.Timer(interval_secs, repeat)
1362
+ self._tasks[task_id] = timer
1363
+ timer.start()
1364
+ return task_id
1365
+
1366
+ def cancel(self, task_id: str) -> bool:
1367
+ if task_id in self._tasks:
1368
+ self._tasks[task_id].cancel()
1369
+ del self._tasks[task_id]
1370
+ return True
1371
+ return False
1372
+
1373
+
1374
+ # =============================================================================
1375
+ # @Format Module - String formatting and templating
1376
+ # =============================================================================
1377
+
1378
+ class FormatModule(CSSLModuleBase):
1379
+ """
1380
+ @Format - String formatting and templating
1381
+
1382
+ Methods:
1383
+ sprintf(fmt, args) - C-style sprintf formatting
1384
+ format(template, kwargs) - Python format string
1385
+ template(tmpl, vars) - Simple ${var} template substitution
1386
+ pad(str, width, char, align) - Pad string to width
1387
+ truncate(str, maxlen, ellipsis) - Truncate with ellipsis
1388
+ wrap(str, width) - Word wrap text
1389
+ indent(str, spaces) - Indent each line
1390
+ dedent(str) - Remove common leading whitespace
1391
+ upper(str) - Convert to uppercase
1392
+ lower(str) - Convert to lowercase
1393
+ title(str) - Convert to title case
1394
+ camel(str) - Convert to camelCase
1395
+ snake(str) - Convert to snake_case
1396
+ kebab(str) - Convert to kebab-case
1397
+ bytes(n, decimals) - Format bytes as human readable
1398
+ number(n, decimals, sep) - Format number with separators
1399
+ currency(n, symbol) - Format as currency
1400
+ percent(n, decimals) - Format as percentage
1401
+ """
1402
+
1403
+ def _register_methods(self):
1404
+ self._methods['sprintf'] = self.sprintf
1405
+ self._methods['format'] = self.format_str
1406
+ self._methods['template'] = self.template
1407
+ self._methods['pad'] = self.pad
1408
+ self._methods['truncate'] = self.truncate
1409
+ self._methods['wrap'] = self.wrap
1410
+ self._methods['indent'] = self.indent
1411
+ self._methods['dedent'] = self.dedent
1412
+ self._methods['upper'] = str.upper
1413
+ self._methods['lower'] = str.lower
1414
+ self._methods['title'] = str.title
1415
+ self._methods['camel'] = self.camel
1416
+ self._methods['snake'] = self.snake
1417
+ self._methods['kebab'] = self.kebab
1418
+ self._methods['bytes'] = self.format_bytes
1419
+ self._methods['number'] = self.format_number
1420
+ self._methods['currency'] = self.currency
1421
+ self._methods['percent'] = self.percent
1422
+
1423
+ def sprintf(self, fmt: str, *args) -> str:
1424
+ return fmt % args
1425
+
1426
+ def format_str(self, template: str, **kwargs) -> str:
1427
+ return template.format(**kwargs)
1428
+
1429
+ def template(self, tmpl: str, vars: Dict) -> str:
1430
+ result = tmpl
1431
+ for key, value in vars.items():
1432
+ result = result.replace('${' + key + '}', str(value))
1433
+ return result
1434
+
1435
+ def pad(self, s: str, width: int, char: str = ' ', align: str = 'right') -> str:
1436
+ if align == 'left':
1437
+ return s.ljust(width, char)
1438
+ elif align == 'center':
1439
+ return s.center(width, char)
1440
+ return s.rjust(width, char)
1441
+
1442
+ def truncate(self, s: str, maxlen: int, ellipsis: str = '...') -> str:
1443
+ if len(s) <= maxlen:
1444
+ return s
1445
+ return s[:maxlen - len(ellipsis)] + ellipsis
1446
+
1447
+ def wrap(self, s: str, width: int = 80) -> str:
1448
+ import textwrap
1449
+ return '\n'.join(textwrap.wrap(s, width))
1450
+
1451
+ def indent(self, s: str, spaces: int = 4) -> str:
1452
+ prefix = ' ' * spaces
1453
+ return '\n'.join(prefix + line for line in s.split('\n'))
1454
+
1455
+ def dedent(self, s: str) -> str:
1456
+ import textwrap
1457
+ return textwrap.dedent(s)
1458
+
1459
+ def camel(self, s: str) -> str:
1460
+ words = re.split(r'[-_\s]+', s)
1461
+ return words[0].lower() + ''.join(w.capitalize() for w in words[1:])
1462
+
1463
+ def snake(self, s: str) -> str:
1464
+ s = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', s)
1465
+ s = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', s)
1466
+ return re.sub(r'[-\s]+', '_', s).lower()
1467
+
1468
+ def kebab(self, s: str) -> str:
1469
+ return self.snake(s).replace('_', '-')
1470
+
1471
+ def format_bytes(self, n: int, decimals: int = 2) -> str:
1472
+ for unit in ['B', 'KB', 'MB', 'GB', 'TB', 'PB']:
1473
+ if abs(n) < 1024:
1474
+ return f"{n:.{decimals}f} {unit}"
1475
+ n /= 1024
1476
+ return f"{n:.{decimals}f} EB"
1477
+
1478
+ def format_number(self, n: float, decimals: int = 2, sep: str = ',') -> str:
1479
+ parts = f"{n:.{decimals}f}".split('.')
1480
+ parts[0] = re.sub(r'(\d)(?=(\d{3})+$)', r'\1' + sep, parts[0])
1481
+ return '.'.join(parts)
1482
+
1483
+ def currency(self, n: float, symbol: str = '$') -> str:
1484
+ return f"{symbol}{self.format_number(abs(n), 2)}"
1485
+
1486
+ def percent(self, n: float, decimals: int = 1) -> str:
1487
+ return f"{n * 100:.{decimals}f}%"
1488
+
1489
+
1490
+ # =============================================================================
1491
+ # @Console Module - Terminal/Console operations
1492
+ # =============================================================================
1493
+
1494
+ class ConsoleModule(CSSLModuleBase):
1495
+ """
1496
+ @Console - Terminal/Console operations
1497
+
1498
+ Methods:
1499
+ clear() - Clear console screen
1500
+ print(text, color) - Print with optional color
1501
+ println(text, color) - Print line with optional color
1502
+ table(data, headers) - Print formatted table
1503
+ progress(current, total, width) - Show progress bar
1504
+ spinner(message) - Show loading spinner
1505
+ prompt(message, default) - Prompt for input
1506
+ confirm(message) - Yes/No confirmation
1507
+ select(message, options) - Select from options menu
1508
+ color(text, color) - Colorize text
1509
+ bold(text) - Bold text
1510
+ dim(text) - Dim text
1511
+ underline(text) - Underlined text
1512
+ cursor_hide() - Hide cursor
1513
+ cursor_show() - Show cursor
1514
+ cursor_move(x, y) - Move cursor to position
1515
+ beep() - Terminal beep
1516
+ """
1517
+
1518
+ COLORS = {
1519
+ 'black': '30', 'red': '31', 'green': '32', 'yellow': '33',
1520
+ 'blue': '34', 'magenta': '35', 'cyan': '36', 'white': '37',
1521
+ 'bright_black': '90', 'bright_red': '91', 'bright_green': '92',
1522
+ 'bright_yellow': '93', 'bright_blue': '94', 'bright_magenta': '95',
1523
+ 'bright_cyan': '96', 'bright_white': '97',
1524
+ 'reset': '0', 'bold': '1', 'dim': '2', 'italic': '3',
1525
+ 'underline': '4', 'blink': '5', 'reverse': '7'
1526
+ }
1527
+
1528
+ def __init__(self, runtime=None):
1529
+ super().__init__(runtime)
1530
+ self._spinner_running = False
1531
+ self._spinner_thread = None
1532
+
1533
+ def _register_methods(self):
1534
+ self._methods['clear'] = self.clear
1535
+ self._methods['print'] = self.print_styled
1536
+ self._methods['println'] = self.println_styled
1537
+ self._methods['table'] = self.print_table
1538
+ self._methods['progress'] = self.show_progress
1539
+ self._methods['spinner'] = self.show_spinner
1540
+ self._methods['spinner_stop'] = self.stop_spinner
1541
+ self._methods['prompt'] = self.prompt
1542
+ self._methods['confirm'] = self.confirm
1543
+ self._methods['select'] = self.select_menu
1544
+ self._methods['color'] = self.colorize
1545
+ self._methods['bold'] = self.bold
1546
+ self._methods['dim'] = self.dim
1547
+ self._methods['underline'] = self.underline_text
1548
+ self._methods['cursor_hide'] = self.cursor_hide
1549
+ self._methods['cursor_show'] = self.cursor_show
1550
+ self._methods['cursor_move'] = self.cursor_move
1551
+ self._methods['beep'] = self.beep
1552
+
1553
+ def clear(self):
1554
+ """Clear the console screen."""
1555
+ if os.name == 'nt':
1556
+ os.system('cls')
1557
+ else:
1558
+ print('\033[2J\033[H', end='')
1559
+
1560
+ def print_styled(self, text: str, color: str = None) -> None:
1561
+ """Print text with optional color."""
1562
+ if color:
1563
+ text = self.colorize(text, color)
1564
+ print(text, end='')
1565
+
1566
+ def println_styled(self, text: str, color: str = None) -> None:
1567
+ """Print line with optional color."""
1568
+ if color:
1569
+ text = self.colorize(text, color)
1570
+ print(text)
1571
+
1572
+ def print_table(self, data: List[Dict], headers: List[str] = None) -> None:
1573
+ """Print a formatted table."""
1574
+ if not data:
1575
+ print("(no data)")
1576
+ return
1577
+
1578
+ # Auto-detect headers from first row if not provided
1579
+ if headers is None:
1580
+ if isinstance(data[0], dict):
1581
+ headers = list(data[0].keys())
1582
+ else:
1583
+ headers = [f"Col {i+1}" for i in range(len(data[0]))]
1584
+
1585
+ # Calculate column widths
1586
+ widths = [len(h) for h in headers]
1587
+ for row in data:
1588
+ if isinstance(row, dict):
1589
+ values = [str(row.get(h, '')) for h in headers]
1590
+ else:
1591
+ values = [str(v) for v in row]
1592
+ for i, v in enumerate(values):
1593
+ if i < len(widths):
1594
+ widths[i] = max(widths[i], len(v))
1595
+
1596
+ # Print header
1597
+ sep = '+-' + '-+-'.join('-' * w for w in widths) + '-+'
1598
+ print(sep)
1599
+ header_line = '| ' + ' | '.join(h.ljust(widths[i]) for i, h in enumerate(headers)) + ' |'
1600
+ print(header_line)
1601
+ print(sep)
1602
+
1603
+ # Print rows
1604
+ for row in data:
1605
+ if isinstance(row, dict):
1606
+ values = [str(row.get(h, '')) for h in headers]
1607
+ else:
1608
+ values = [str(v) for v in row]
1609
+ row_line = '| ' + ' | '.join(v.ljust(widths[i]) for i, v in enumerate(values) if i < len(widths)) + ' |'
1610
+ print(row_line)
1611
+
1612
+ print(sep)
1613
+
1614
+ def show_progress(self, current: int, total: int, width: int = 40, show_text: bool = True) -> None:
1615
+ """Show a progress bar."""
1616
+ if total <= 0:
1617
+ return
1618
+ percent = min(100, current * 100 // total)
1619
+ filled = int(width * current // total)
1620
+ bar = '█' * filled + '░' * (width - filled)
1621
+ if show_text:
1622
+ print(f'\r[{bar}] {percent}% ({current}/{total})', end='', flush=True)
1623
+ else:
1624
+ print(f'\r[{bar}]', end='', flush=True)
1625
+ if current >= total:
1626
+ print()
1627
+
1628
+ def show_spinner(self, message: str = "Loading") -> None:
1629
+ """Show a loading spinner."""
1630
+ self._spinner_running = True
1631
+ spinners = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
1632
+
1633
+ def spin():
1634
+ i = 0
1635
+ while self._spinner_running:
1636
+ print(f'\r{spinners[i % len(spinners)]} {message}...', end='', flush=True)
1637
+ time.sleep(0.1)
1638
+ i += 1
1639
+ print('\r' + ' ' * (len(message) + 10) + '\r', end='')
1640
+
1641
+ self._spinner_thread = threading.Thread(target=spin, daemon=True)
1642
+ self._spinner_thread.start()
1643
+
1644
+ def stop_spinner(self) -> None:
1645
+ """Stop the loading spinner."""
1646
+ self._spinner_running = False
1647
+ if self._spinner_thread:
1648
+ self._spinner_thread.join(timeout=1)
1649
+ self._spinner_thread = None
1650
+
1651
+ def prompt(self, message: str, default: str = '') -> str:
1652
+ """Prompt for user input."""
1653
+ if default:
1654
+ result = input(f"{message} [{default}]: ")
1655
+ return result if result else default
1656
+ return input(f"{message}: ")
1657
+
1658
+ def confirm(self, message: str) -> bool:
1659
+ """Yes/No confirmation prompt."""
1660
+ result = input(f"{message} (y/n): ").lower().strip()
1661
+ return result in ('y', 'yes', 'ja', 'j', '1', 'true')
1662
+
1663
+ def select_menu(self, message: str, options: List[str]) -> int:
1664
+ """Select from options menu. Returns index of selected option."""
1665
+ print(f"\n{message}")
1666
+ for i, opt in enumerate(options):
1667
+ print(f" {i + 1}. {opt}")
1668
+ while True:
1669
+ try:
1670
+ choice = int(input("\nWahl: "))
1671
+ if 1 <= choice <= len(options):
1672
+ return choice - 1
1673
+ except ValueError:
1674
+ pass
1675
+ print("Ungültige Auswahl, bitte erneut versuchen.")
1676
+
1677
+ def colorize(self, text: str, color: str) -> str:
1678
+ """Apply ANSI color to text."""
1679
+ code = self.COLORS.get(color.lower(), color)
1680
+ return f'\033[{code}m{text}\033[0m'
1681
+
1682
+ def bold(self, text: str) -> str:
1683
+ """Make text bold."""
1684
+ return f'\033[1m{text}\033[0m'
1685
+
1686
+ def dim(self, text: str) -> str:
1687
+ """Make text dim."""
1688
+ return f'\033[2m{text}\033[0m'
1689
+
1690
+ def underline_text(self, text: str) -> str:
1691
+ """Underline text."""
1692
+ return f'\033[4m{text}\033[0m'
1693
+
1694
+ def cursor_hide(self) -> None:
1695
+ """Hide cursor."""
1696
+ print('\033[?25l', end='')
1697
+
1698
+ def cursor_show(self) -> None:
1699
+ """Show cursor."""
1700
+ print('\033[?25h', end='')
1701
+
1702
+ def cursor_move(self, x: int, y: int) -> None:
1703
+ """Move cursor to position."""
1704
+ print(f'\033[{y};{x}H', end='')
1705
+
1706
+ def beep(self) -> None:
1707
+ """Terminal beep."""
1708
+ print('\a', end='')
1709
+
1710
+
1711
+ # =============================================================================
1712
+ # @Process Module - Process and subprocess management
1713
+ # =============================================================================
1714
+
1715
+ class ProcessModule(CSSLModuleBase):
1716
+ """
1717
+ @Process - Process and subprocess management
1718
+
1719
+ Methods:
1720
+ run(cmd, timeout, cwd) - Run command and wait for result
1721
+ spawn(cmd, cwd) - Spawn background process
1722
+ kill(pid) - Kill process by PID
1723
+ list() - List running processes
1724
+ pid() - Get current process ID
1725
+ ppid() - Get parent process ID
1726
+ exists(pid) - Check if process exists
1727
+ wait(pid, timeout) - Wait for process to finish
1728
+ shell(cmd) - Run in system shell
1729
+ popen(cmd, cwd) - Open process for streaming
1730
+ read_stdout(handle) - Read from process stdout
1731
+ write_stdin(handle, data) - Write to process stdin
1732
+ close(handle) - Close process handle
1733
+ """
1734
+
1735
+ def __init__(self, runtime=None):
1736
+ super().__init__(runtime)
1737
+ self._handles: Dict[int, Any] = {}
1738
+ self._handle_counter = 0
1739
+
1740
+ def _register_methods(self):
1741
+ self._methods['run'] = self.run_command
1742
+ self._methods['spawn'] = self.spawn_process
1743
+ self._methods['kill'] = self.kill_process
1744
+ self._methods['list'] = self.list_processes
1745
+ self._methods['pid'] = lambda: os.getpid()
1746
+ self._methods['ppid'] = lambda: os.getppid()
1747
+ self._methods['exists'] = self.process_exists
1748
+ self._methods['wait'] = self.wait_for_process
1749
+ self._methods['shell'] = self.shell_command
1750
+ self._methods['popen'] = self.popen_process
1751
+ self._methods['read_stdout'] = self.read_stdout
1752
+ self._methods['write_stdin'] = self.write_stdin
1753
+ self._methods['close'] = self.close_handle
1754
+
1755
+ def run_command(self, cmd: Union[str, List[str]], timeout: float = 300, cwd: str = None) -> Dict:
1756
+ """Run command and wait for result."""
1757
+ import subprocess
1758
+ try:
1759
+ result = subprocess.run(
1760
+ cmd,
1761
+ shell=isinstance(cmd, str),
1762
+ capture_output=True,
1763
+ text=True,
1764
+ timeout=timeout,
1765
+ cwd=cwd
1766
+ )
1767
+ return {
1768
+ 'returncode': result.returncode,
1769
+ 'stdout': result.stdout,
1770
+ 'stderr': result.stderr,
1771
+ 'success': result.returncode == 0
1772
+ }
1773
+ except subprocess.TimeoutExpired:
1774
+ return {'returncode': -1, 'stdout': '', 'stderr': 'Command timed out', 'success': False}
1775
+ except Exception as e:
1776
+ return {'returncode': -1, 'stdout': '', 'stderr': str(e), 'success': False}
1777
+
1778
+ def spawn_process(self, cmd: Union[str, List[str]], cwd: str = None) -> int:
1779
+ """Spawn background process, returns PID."""
1780
+ import subprocess
1781
+ proc = subprocess.Popen(
1782
+ cmd,
1783
+ shell=isinstance(cmd, str),
1784
+ cwd=cwd,
1785
+ stdout=subprocess.DEVNULL,
1786
+ stderr=subprocess.DEVNULL
1787
+ )
1788
+ return proc.pid
1789
+
1790
+ def kill_process(self, pid: int) -> bool:
1791
+ """Kill process by PID."""
1792
+ try:
1793
+ os.kill(pid, 9) # SIGKILL
1794
+ return True
1795
+ except OSError:
1796
+ return False
1797
+
1798
+ def list_processes(self) -> List[Dict]:
1799
+ """List running processes."""
1800
+ try:
1801
+ import psutil
1802
+ processes = []
1803
+ for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent']):
1804
+ try:
1805
+ info = proc.info
1806
+ processes.append({
1807
+ 'pid': info['pid'],
1808
+ 'name': info['name'],
1809
+ 'cpu': info['cpu_percent'],
1810
+ 'memory': info['memory_percent']
1811
+ })
1812
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
1813
+ pass
1814
+ return processes
1815
+ except ImportError:
1816
+ # Fallback without psutil
1817
+ import subprocess
1818
+ if os.name == 'nt':
1819
+ result = subprocess.run(['tasklist', '/fo', 'csv'], capture_output=True, text=True)
1820
+ lines = result.stdout.strip().split('\n')[1:]
1821
+ processes = []
1822
+ for line in lines:
1823
+ parts = line.strip('"').split('","')
1824
+ if len(parts) >= 2:
1825
+ processes.append({'name': parts[0], 'pid': int(parts[1])})
1826
+ return processes
1827
+ else:
1828
+ result = subprocess.run(['ps', 'aux'], capture_output=True, text=True)
1829
+ lines = result.stdout.strip().split('\n')[1:]
1830
+ processes = []
1831
+ for line in lines:
1832
+ parts = line.split()
1833
+ if len(parts) >= 11:
1834
+ processes.append({
1835
+ 'user': parts[0],
1836
+ 'pid': int(parts[1]),
1837
+ 'cpu': float(parts[2]),
1838
+ 'memory': float(parts[3]),
1839
+ 'name': parts[10]
1840
+ })
1841
+ return processes
1842
+
1843
+ def process_exists(self, pid: int) -> bool:
1844
+ """Check if process exists."""
1845
+ try:
1846
+ os.kill(pid, 0)
1847
+ return True
1848
+ except OSError:
1849
+ return False
1850
+
1851
+ def wait_for_process(self, pid: int, timeout: float = None) -> int:
1852
+ """Wait for process to finish, returns exit code."""
1853
+ import subprocess
1854
+ try:
1855
+ if os.name == 'nt':
1856
+ # Windows
1857
+ import subprocess
1858
+ result = subprocess.run(['tasklist', '/fi', f'pid eq {pid}'], capture_output=True, text=True)
1859
+ start = time.time()
1860
+ while str(pid) in result.stdout:
1861
+ if timeout and (time.time() - start) > timeout:
1862
+ return -1
1863
+ time.sleep(0.5)
1864
+ result = subprocess.run(['tasklist', '/fi', f'pid eq {pid}'], capture_output=True, text=True)
1865
+ return 0
1866
+ else:
1867
+ # Unix
1868
+ os.waitpid(pid, 0)
1869
+ return 0
1870
+ except Exception:
1871
+ return -1
1872
+
1873
+ def shell_command(self, cmd: str) -> int:
1874
+ """Run command in system shell, returns exit code."""
1875
+ return os.system(cmd)
1876
+
1877
+ def popen_process(self, cmd: Union[str, List[str]], cwd: str = None) -> int:
1878
+ """Open process for streaming, returns handle ID."""
1879
+ import subprocess
1880
+ proc = subprocess.Popen(
1881
+ cmd,
1882
+ shell=isinstance(cmd, str),
1883
+ cwd=cwd,
1884
+ stdout=subprocess.PIPE,
1885
+ stderr=subprocess.PIPE,
1886
+ stdin=subprocess.PIPE,
1887
+ text=True
1888
+ )
1889
+ self._handle_counter += 1
1890
+ self._handles[self._handle_counter] = proc
1891
+ return self._handle_counter
1892
+
1893
+ def read_stdout(self, handle: int, timeout: float = None) -> Optional[str]:
1894
+ """Read from process stdout."""
1895
+ proc = self._handles.get(handle)
1896
+ if not proc:
1897
+ return None
1898
+ try:
1899
+ import select
1900
+ if hasattr(select, 'select'):
1901
+ # Unix
1902
+ ready, _, _ = select.select([proc.stdout], [], [], timeout or 0)
1903
+ if ready:
1904
+ return proc.stdout.readline()
1905
+ return proc.stdout.readline()
1906
+ except Exception:
1907
+ return None
1908
+
1909
+ def write_stdin(self, handle: int, data: str) -> bool:
1910
+ """Write to process stdin."""
1911
+ proc = self._handles.get(handle)
1912
+ if not proc:
1913
+ return False
1914
+ try:
1915
+ proc.stdin.write(data)
1916
+ proc.stdin.flush()
1917
+ return True
1918
+ except Exception:
1919
+ return False
1920
+
1921
+ def close_handle(self, handle: int) -> bool:
1922
+ """Close process handle."""
1923
+ proc = self._handles.get(handle)
1924
+ if not proc:
1925
+ return False
1926
+ try:
1927
+ proc.terminate()
1928
+ del self._handles[handle]
1929
+ return True
1930
+ except Exception:
1931
+ return False
1932
+
1933
+
1934
+ # =============================================================================
1935
+ # @Config Module - Configuration file management
1936
+ # =============================================================================
1937
+
1938
+ class ConfigModule(CSSLModuleBase):
1939
+ """
1940
+ @Config - Configuration file management (JSON, INI, ENV)
1941
+
1942
+ Methods:
1943
+ load(path) - Load config file (auto-detect format)
1944
+ save(path, data) - Save config file
1945
+ loadJSON(path) - Load JSON config
1946
+ saveJSON(path, data) - Save JSON config
1947
+ loadINI(path) - Load INI config
1948
+ saveINI(path, data) - Save INI config
1949
+ loadENV(path) - Load .env file
1950
+ saveENV(path, data) - Save .env file
1951
+ get(key, default) - Get config value by key path
1952
+ set(key, value) - Set config value by key path
1953
+ has(key) - Check if key exists
1954
+ delete(key) - Delete config key
1955
+ merge(config) - Merge config into current
1956
+ reload() - Reload from file
1957
+ env(name, default) - Get environment variable
1958
+ setenv(name, value) - Set environment variable
1959
+ """
1960
+
1961
+ def __init__(self, runtime=None):
1962
+ super().__init__(runtime)
1963
+ self._config: Dict[str, Any] = {}
1964
+ self._file_path: Optional[str] = None
1965
+
1966
+ def _register_methods(self):
1967
+ self._methods['load'] = self.load
1968
+ self._methods['save'] = self.save
1969
+ self._methods['loadJSON'] = self.load_json
1970
+ self._methods['saveJSON'] = self.save_json
1971
+ self._methods['loadINI'] = self.load_ini
1972
+ self._methods['saveINI'] = self.save_ini
1973
+ self._methods['loadENV'] = self.load_env
1974
+ self._methods['saveENV'] = self.save_env
1975
+ self._methods['get'] = self.get_value
1976
+ self._methods['set'] = self.set_value
1977
+ self._methods['has'] = self.has_key
1978
+ self._methods['delete'] = self.delete_key
1979
+ self._methods['merge'] = self.merge_config
1980
+ self._methods['reload'] = self.reload
1981
+ self._methods['env'] = self.get_env
1982
+ self._methods['setenv'] = self.set_env
1983
+ self._methods['all'] = lambda: dict(self._config)
1984
+
1985
+ def load(self, path: str) -> Dict:
1986
+ """Load config file, auto-detecting format."""
1987
+ self._file_path = path
1988
+ ext = os.path.splitext(path)[1].lower()
1989
+
1990
+ if ext == '.json':
1991
+ return self.load_json(path)
1992
+ elif ext in ('.ini', '.cfg', '.conf'):
1993
+ return self.load_ini(path)
1994
+ elif ext == '.env' or os.path.basename(path).startswith('.env'):
1995
+ return self.load_env(path)
1996
+ else:
1997
+ # Try JSON first, then INI
1998
+ try:
1999
+ return self.load_json(path)
2000
+ except:
2001
+ try:
2002
+ return self.load_ini(path)
2003
+ except:
2004
+ return {}
2005
+
2006
+ def save(self, path: str = None, data: Dict = None) -> bool:
2007
+ """Save config file."""
2008
+ path = path or self._file_path
2009
+ if not path:
2010
+ return False
2011
+
2012
+ data = data if data is not None else self._config
2013
+ ext = os.path.splitext(path)[1].lower()
2014
+
2015
+ if ext == '.json':
2016
+ return self.save_json(path, data)
2017
+ elif ext in ('.ini', '.cfg', '.conf'):
2018
+ return self.save_ini(path, data)
2019
+ elif ext == '.env' or os.path.basename(path).startswith('.env'):
2020
+ return self.save_env(path, data)
2021
+ else:
2022
+ return self.save_json(path, data)
2023
+
2024
+ def load_json(self, path: str) -> Dict:
2025
+ """Load JSON config."""
2026
+ with open(path, 'r', encoding='utf-8') as f:
2027
+ self._config = json.load(f)
2028
+ self._file_path = path
2029
+ return self._config
2030
+
2031
+ def save_json(self, path: str, data: Dict = None) -> bool:
2032
+ """Save JSON config."""
2033
+ data = data if data is not None else self._config
2034
+ with open(path, 'w', encoding='utf-8') as f:
2035
+ json.dump(data, f, indent=2, ensure_ascii=False)
2036
+ return True
2037
+
2038
+ def load_ini(self, path: str) -> Dict:
2039
+ """Load INI config."""
2040
+ import configparser
2041
+ parser = configparser.ConfigParser()
2042
+ parser.read(path, encoding='utf-8')
2043
+
2044
+ config = {}
2045
+ for section in parser.sections():
2046
+ config[section] = dict(parser.items(section))
2047
+
2048
+ # Handle DEFAULT section
2049
+ if parser.defaults():
2050
+ config['DEFAULT'] = dict(parser.defaults())
2051
+
2052
+ self._config = config
2053
+ self._file_path = path
2054
+ return config
2055
+
2056
+ def save_ini(self, path: str, data: Dict = None) -> bool:
2057
+ """Save INI config."""
2058
+ import configparser
2059
+ data = data if data is not None else self._config
2060
+ parser = configparser.ConfigParser()
2061
+
2062
+ for section, values in data.items():
2063
+ if section.upper() == 'DEFAULT':
2064
+ for key, value in values.items():
2065
+ parser['DEFAULT'][key] = str(value)
2066
+ else:
2067
+ parser[section] = {k: str(v) for k, v in values.items()}
2068
+
2069
+ with open(path, 'w', encoding='utf-8') as f:
2070
+ parser.write(f)
2071
+ return True
2072
+
2073
+ def load_env(self, path: str) -> Dict:
2074
+ """Load .env file."""
2075
+ config = {}
2076
+ with open(path, 'r', encoding='utf-8') as f:
2077
+ for line in f:
2078
+ line = line.strip()
2079
+ if line and not line.startswith('#') and '=' in line:
2080
+ key, value = line.split('=', 1)
2081
+ key = key.strip()
2082
+ value = value.strip().strip('"').strip("'")
2083
+ config[key] = value
2084
+ # Also set in environment
2085
+ os.environ[key] = value
2086
+
2087
+ self._config = config
2088
+ self._file_path = path
2089
+ return config
2090
+
2091
+ def save_env(self, path: str, data: Dict = None) -> bool:
2092
+ """Save .env file."""
2093
+ data = data if data is not None else self._config
2094
+ with open(path, 'w', encoding='utf-8') as f:
2095
+ for key, value in data.items():
2096
+ # Quote values with spaces
2097
+ if ' ' in str(value):
2098
+ value = f'"{value}"'
2099
+ f.write(f"{key}={value}\n")
2100
+ return True
2101
+
2102
+ def get_value(self, key: str, default: Any = None) -> Any:
2103
+ """Get config value by key path (e.g., 'section.key')."""
2104
+ keys = key.split('.')
2105
+ current = self._config
2106
+ for k in keys:
2107
+ if isinstance(current, dict) and k in current:
2108
+ current = current[k]
2109
+ else:
2110
+ return default
2111
+ return current
2112
+
2113
+ def set_value(self, key: str, value: Any) -> bool:
2114
+ """Set config value by key path."""
2115
+ keys = key.split('.')
2116
+ current = self._config
2117
+ for k in keys[:-1]:
2118
+ if k not in current:
2119
+ current[k] = {}
2120
+ current = current[k]
2121
+ current[keys[-1]] = value
2122
+ return True
2123
+
2124
+ def has_key(self, key: str) -> bool:
2125
+ """Check if key exists."""
2126
+ return self.get_value(key, _MISSING) is not _MISSING
2127
+
2128
+ def delete_key(self, key: str) -> bool:
2129
+ """Delete config key."""
2130
+ keys = key.split('.')
2131
+ current = self._config
2132
+ for k in keys[:-1]:
2133
+ if k not in current:
2134
+ return False
2135
+ current = current[k]
2136
+ if keys[-1] in current:
2137
+ del current[keys[-1]]
2138
+ return True
2139
+ return False
2140
+
2141
+ def merge_config(self, config: Dict) -> Dict:
2142
+ """Merge config into current."""
2143
+ def deep_merge(base, overlay):
2144
+ for key, value in overlay.items():
2145
+ if key in base and isinstance(base[key], dict) and isinstance(value, dict):
2146
+ deep_merge(base[key], value)
2147
+ else:
2148
+ base[key] = value
2149
+ deep_merge(self._config, config)
2150
+ return self._config
2151
+
2152
+ def reload(self) -> Dict:
2153
+ """Reload from file."""
2154
+ if self._file_path and os.path.exists(self._file_path):
2155
+ return self.load(self._file_path)
2156
+ return self._config
2157
+
2158
+ def get_env(self, name: str, default: str = None) -> Optional[str]:
2159
+ """Get environment variable."""
2160
+ return os.environ.get(name, default)
2161
+
2162
+ def set_env(self, name: str, value: str) -> bool:
2163
+ """Set environment variable."""
2164
+ os.environ[name] = value
2165
+ return True
2166
+
2167
+
2168
+ # Sentinel for missing values
2169
+ class _MissingSentinel:
2170
+ pass
2171
+ _MISSING = _MissingSentinel()
2172
+
2173
+
2174
+ # =============================================================================
2175
+ # @Server Module - HTTP Server for APIs and static files
2176
+ # =============================================================================
2177
+
2178
+ class ServerModule(CSSLModuleBase):
2179
+ """
2180
+ @Server - HTTP Server for web APIs and static files
2181
+
2182
+ Methods:
2183
+ run(port, host) - Start HTTP server
2184
+ stop() - Stop the server
2185
+ api(path, method) - Register API endpoint
2186
+ static(path, dir) - Serve static files
2187
+ showServer() - Display server information
2188
+ getConnections() - Get active connections count
2189
+ getRoutes() - List registered routes
2190
+ status() - Get server status
2191
+ setErrorHandler(func) - Set custom error handler
2192
+ setCORS(origins) - Configure CORS
2193
+
2194
+ Example:
2195
+ @Server <== get('Server');
2196
+ @Server.run(port=3030);
2197
+
2198
+ @Server.api('/status') <== {
2199
+ define handler {
2200
+ return { "status": "ok" };
2201
+ }
2202
+ };
2203
+ """
2204
+
2205
+ def __init__(self, runtime=None):
2206
+ super().__init__(runtime)
2207
+ self._server = None
2208
+ self._server_thread = None
2209
+ self._port = 8080
2210
+ self._host = '0.0.0.0'
2211
+ self._running = False
2212
+ self._routes: Dict[str, Dict] = {}
2213
+ self._static_dirs: Dict[str, str] = {}
2214
+ self._connections = 0
2215
+ self._cors_origins = ['*']
2216
+ self._error_handler = None
2217
+
2218
+ def _register_methods(self):
2219
+ self._methods['run'] = self.run
2220
+ self._methods['stop'] = self.stop
2221
+ self._methods['api'] = self.api
2222
+ self._methods['static'] = self.static
2223
+ self._methods['showServer'] = self.show_server
2224
+ self._methods['getConnections'] = self.get_connections
2225
+ self._methods['getRoutes'] = self.get_routes
2226
+ self._methods['status'] = self.status
2227
+ self._methods['setErrorHandler'] = self.set_error_handler
2228
+ self._methods['setCORS'] = self.set_cors
2229
+
2230
+ def run(self, port: int = 8080, host: str = '0.0.0.0') -> bool:
2231
+ """Start the HTTP server."""
2232
+ if self._running:
2233
+ print(f"[Server] Already running on {self._host}:{self._port}")
2234
+ return False
2235
+
2236
+ self._port = port
2237
+ self._host = host
2238
+
2239
+ try:
2240
+ from http.server import HTTPServer, BaseHTTPRequestHandler
2241
+ import socket
2242
+
2243
+ module_ref = self
2244
+
2245
+ class CSSLRequestHandler(BaseHTTPRequestHandler):
2246
+ def log_message(self, format, *args):
2247
+ # Custom logging
2248
+ print(f"[Server] {self.address_string()} - {format % args}")
2249
+
2250
+ def _send_response(self, status: int, body: Any, content_type: str = 'application/json'):
2251
+ self.send_response(status)
2252
+ self.send_header('Content-Type', content_type)
2253
+ # CORS headers
2254
+ origin = self.headers.get('Origin', '*')
2255
+ if '*' in module_ref._cors_origins or origin in module_ref._cors_origins:
2256
+ self.send_header('Access-Control-Allow-Origin', origin)
2257
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
2258
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
2259
+ self.end_headers()
2260
+
2261
+ if isinstance(body, dict) or isinstance(body, list):
2262
+ body = json.dumps(body, ensure_ascii=False)
2263
+ if isinstance(body, str):
2264
+ body = body.encode('utf-8')
2265
+ self.wfile.write(body)
2266
+
2267
+ def _handle_request(self, method: str):
2268
+ module_ref._connections += 1
2269
+ try:
2270
+ path = self.path.split('?')[0]
2271
+
2272
+ # Check API routes
2273
+ route_key = f"{method}:{path}"
2274
+ if route_key in module_ref._routes:
2275
+ route = module_ref._routes[route_key]
2276
+ handler = route.get('handler')
2277
+
2278
+ # Parse request body
2279
+ content_length = int(self.headers.get('Content-Length', 0))
2280
+ body = None
2281
+ if content_length > 0:
2282
+ raw_body = self.rfile.read(content_length)
2283
+ try:
2284
+ body = json.loads(raw_body.decode('utf-8'))
2285
+ except:
2286
+ body = raw_body.decode('utf-8')
2287
+
2288
+ # Build request object
2289
+ request = {
2290
+ 'method': method,
2291
+ 'path': path,
2292
+ 'headers': dict(self.headers),
2293
+ 'query': self._parse_query(),
2294
+ 'body': body
2295
+ }
2296
+
2297
+ # Call handler
2298
+ if callable(handler):
2299
+ try:
2300
+ result = handler(request)
2301
+ self._send_response(200, result)
2302
+ except Exception as e:
2303
+ if module_ref._error_handler:
2304
+ result = module_ref._error_handler(e)
2305
+ self._send_response(500, result)
2306
+ else:
2307
+ self._send_response(500, {'error': str(e)})
2308
+ else:
2309
+ self._send_response(200, handler)
2310
+ return
2311
+
2312
+ # Check static directories
2313
+ for url_path, dir_path in module_ref._static_dirs.items():
2314
+ if path.startswith(url_path):
2315
+ file_path = path[len(url_path):].lstrip('/')
2316
+ full_path = os.path.join(dir_path, file_path)
2317
+ if os.path.isfile(full_path):
2318
+ self._serve_file(full_path)
2319
+ return
2320
+
2321
+ # 404 Not Found
2322
+ self._send_response(404, {'error': 'Not Found', 'path': path})
2323
+
2324
+ finally:
2325
+ module_ref._connections -= 1
2326
+
2327
+ def _parse_query(self) -> Dict:
2328
+ from urllib.parse import urlparse, parse_qs
2329
+ query = urlparse(self.path).query
2330
+ return {k: v[0] if len(v) == 1 else v for k, v in parse_qs(query).items()}
2331
+
2332
+ def _serve_file(self, path: str):
2333
+ import mimetypes
2334
+ mime_type = mimetypes.guess_type(path)[0] or 'application/octet-stream'
2335
+ try:
2336
+ with open(path, 'rb') as f:
2337
+ content = f.read()
2338
+ self._send_response(200, content, mime_type)
2339
+ except Exception as e:
2340
+ self._send_response(500, {'error': str(e)})
2341
+
2342
+ def do_GET(self):
2343
+ self._handle_request('GET')
2344
+
2345
+ def do_POST(self):
2346
+ self._handle_request('POST')
2347
+
2348
+ def do_PUT(self):
2349
+ self._handle_request('PUT')
2350
+
2351
+ def do_DELETE(self):
2352
+ self._handle_request('DELETE')
2353
+
2354
+ def do_OPTIONS(self):
2355
+ # CORS preflight
2356
+ self.send_response(200)
2357
+ origin = self.headers.get('Origin', '*')
2358
+ if '*' in module_ref._cors_origins or origin in module_ref._cors_origins:
2359
+ self.send_header('Access-Control-Allow-Origin', origin)
2360
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
2361
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
2362
+ self.send_header('Access-Control-Max-Age', '86400')
2363
+ self.end_headers()
2364
+
2365
+ # Create server with SO_REUSEADDR
2366
+ class ReusableTCPServer(HTTPServer):
2367
+ allow_reuse_address = True
2368
+
2369
+ self._server = ReusableTCPServer((host, port), CSSLRequestHandler)
2370
+ self._running = True
2371
+
2372
+ # Start server in thread
2373
+ def serve():
2374
+ print(f"[Server] Started on http://{host}:{port}")
2375
+ self._server.serve_forever()
2376
+
2377
+ self._server_thread = threading.Thread(target=serve, daemon=True)
2378
+ self._server_thread.start()
2379
+
2380
+ return True
2381
+
2382
+ except Exception as e:
2383
+ print(f"[Server] Failed to start: {e}")
2384
+ return False
2385
+
2386
+ def stop(self) -> bool:
2387
+ """Stop the HTTP server."""
2388
+ if not self._running or not self._server:
2389
+ print("[Server] Not running")
2390
+ return False
2391
+
2392
+ try:
2393
+ self._server.shutdown()
2394
+ self._running = False
2395
+ print("[Server] Stopped")
2396
+ return True
2397
+ except Exception as e:
2398
+ print(f"[Server] Error stopping: {e}")
2399
+ return False
2400
+
2401
+ def api(self, path: str, method: str = 'GET', handler: Callable = None) -> 'ApiRouteBuilder':
2402
+ """
2403
+ Register an API endpoint.
2404
+
2405
+ Can be used as:
2406
+ @Server.api('/status', handler=myFunc)
2407
+ or:
2408
+ @Server.api('/status') <== { define handler { return {...}; } };
2409
+ """
2410
+ method = method.upper()
2411
+ route_key = f"{method}:{path}"
2412
+
2413
+ if handler:
2414
+ self._routes[route_key] = {
2415
+ 'path': path,
2416
+ 'method': method,
2417
+ 'handler': handler
2418
+ }
2419
+ print(f"[Server] Registered {method} {path}")
2420
+ return True
2421
+
2422
+ # Return builder for <== assignment
2423
+ return ApiRouteBuilder(self, path, method)
2424
+
2425
+ def static(self, url_path: str, directory: str) -> bool:
2426
+ """Serve static files from a directory."""
2427
+ if not os.path.isdir(directory):
2428
+ print(f"[Server] Directory not found: {directory}")
2429
+ return False
2430
+
2431
+ # Ensure url_path starts with /
2432
+ if not url_path.startswith('/'):
2433
+ url_path = '/' + url_path
2434
+
2435
+ self._static_dirs[url_path] = directory
2436
+ print(f"[Server] Static files: {url_path} -> {directory}")
2437
+ return True
2438
+
2439
+ def show_server(self) -> Dict:
2440
+ """Display server information."""
2441
+ info = {
2442
+ 'running': self._running,
2443
+ 'host': self._host,
2444
+ 'port': self._port,
2445
+ 'url': f"http://{self._host}:{self._port}" if self._running else None,
2446
+ 'routes': len(self._routes),
2447
+ 'static_dirs': len(self._static_dirs),
2448
+ 'connections': self._connections
2449
+ }
2450
+
2451
+ print(f"\n{'='*50}")
2452
+ print(f" CSSL Server Status")
2453
+ print(f"{'='*50}")
2454
+ print(f" Status: {'Running' if self._running else 'Stopped'}")
2455
+ if self._running:
2456
+ print(f" URL: http://{self._host}:{self._port}")
2457
+ print(f" Routes: {len(self._routes)}")
2458
+ print(f" Static Dirs: {len(self._static_dirs)}")
2459
+ print(f" Connections: {self._connections}")
2460
+ print(f"{'='*50}\n")
2461
+
2462
+ return info
2463
+
2464
+ def get_connections(self) -> int:
2465
+ """Get number of active connections."""
2466
+ return self._connections
2467
+
2468
+ def get_routes(self) -> List[Dict]:
2469
+ """Get list of registered routes."""
2470
+ return [
2471
+ {'method': r['method'], 'path': r['path']}
2472
+ for r in self._routes.values()
2473
+ ]
2474
+
2475
+ def status(self) -> Dict:
2476
+ """Get server status."""
2477
+ return {
2478
+ 'running': self._running,
2479
+ 'host': self._host,
2480
+ 'port': self._port,
2481
+ 'routes': len(self._routes),
2482
+ 'connections': self._connections
2483
+ }
2484
+
2485
+ def set_error_handler(self, handler: Callable) -> bool:
2486
+ """Set custom error handler."""
2487
+ self._error_handler = handler
2488
+ return True
2489
+
2490
+ def set_cors(self, origins: Union[str, List[str]]) -> bool:
2491
+ """Configure CORS allowed origins."""
2492
+ if isinstance(origins, str):
2493
+ origins = [origins]
2494
+ self._cors_origins = origins
2495
+ return True
2496
+
2497
+
2498
+ class ApiRouteBuilder:
2499
+ """Builder class for API route registration with <== syntax."""
2500
+
2501
+ def __init__(self, server: ServerModule, path: str, method: str):
2502
+ self._server = server
2503
+ self._path = path
2504
+ self._method = method
2505
+
2506
+ def __call__(self, handler: Callable) -> bool:
2507
+ """Called when used as @Server.api('/path')(handler)"""
2508
+ return self._server.api(self._path, self._method, handler)
2509
+
2510
+ def set_handler(self, handler: Any) -> bool:
2511
+ """Called by runtime for <== assignment"""
2512
+ route_key = f"{self._method}:{self._path}"
2513
+ self._server._routes[route_key] = {
2514
+ 'path': self._path,
2515
+ 'method': self._method,
2516
+ 'handler': handler
2517
+ }
2518
+ print(f"[Server] Registered {self._method} {self._path}")
2519
+ return True
2520
+
2521
+
2522
+ # =============================================================================
2523
+ # @APK Module - App Registration and Management
2524
+ # =============================================================================
2525
+
2526
+ class APKModule(CSSLModuleBase):
2527
+ """
2528
+ @APK - App package management and registration
2529
+
2530
+ Methods:
2531
+ createAppService(config) - Register an app from CSSL
2532
+ registerApp(app_info) - Register app directly
2533
+ getAppInfo(app_id) - Get app metadata
2534
+ listApps() - List all registered apps
2535
+ launchApp(app_id) - Launch an app
2536
+ isInstalled(app_id) - Check if app is installed
2537
+
2538
+ Usage in CSSL:
2539
+ @APK.createAppService() <== {
2540
+ script = "app.py";
2541
+ service = "myapp.cll";
2542
+ execute_on_boot = false;
2543
+ }
2544
+ """
2545
+
2546
+ def __init__(self, runtime=None):
2547
+ super().__init__(runtime)
2548
+ self._registry = None
2549
+ self._root_dir = None
2550
+
2551
+ def _register_methods(self):
2552
+ self._methods['createAppService'] = self.createAppService
2553
+ self._methods['registerApp'] = self.registerApp
2554
+ self._methods['getAppInfo'] = self.getAppInfo
2555
+ self._methods['listApps'] = self.listApps
2556
+ self._methods['launchApp'] = self.launchApp
2557
+ self._methods['isInstalled'] = self.isInstalled
2558
+
2559
+ def _get_registry(self):
2560
+ """Lazy load AppRegistry"""
2561
+ if self._registry is None:
2562
+ try:
2563
+ import sys
2564
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
2565
+ from common.clients.apk import AppRegistry
2566
+ self._registry = AppRegistry()
2567
+ self._registry.scan()
2568
+ except ImportError:
2569
+ pass
2570
+ return self._registry
2571
+
2572
+ def _get_root_dir(self) -> str:
2573
+ """Get root32 directory"""
2574
+ if self._root_dir is None:
2575
+ if self.runtime and hasattr(self.runtime, 'kernel') and hasattr(self.runtime.kernel, 'RootDirectory'):
2576
+ self._root_dir = self.runtime.kernel.RootDirectory
2577
+ else:
2578
+ self._root_dir = os.path.expanduser("~/.cso/root32")
2579
+ return self._root_dir
2580
+
2581
+ def createAppService(self, config: Dict = None) -> 'AppServiceBuilder':
2582
+ """
2583
+ Create an app service registration.
2584
+
2585
+ Returns a builder that can be used with <== assignment.
2586
+
2587
+ Usage:
2588
+ @APK.createAppService() <== { script="app.py", service="myapp.cll" }
2589
+ """
2590
+ return AppServiceBuilder(self, config or {})
2591
+
2592
+ def registerApp(self, app_info: Dict) -> bool:
2593
+ """
2594
+ Register an app directly.
2595
+
2596
+ Args:
2597
+ app_info: Dict with name, version, category, icon, module_path
2598
+ """
2599
+ registry = self._get_registry()
2600
+ if not registry:
2601
+ return False
2602
+
2603
+ try:
2604
+ from common.clients.apk import AppInfo
2605
+ info = AppInfo(
2606
+ app_id=app_info.get('id', app_info.get('name', '').lower()),
2607
+ name=app_info.get('name', ''),
2608
+ version=app_info.get('version', '1.0.0'),
2609
+ category=app_info.get('category', 'Apps'),
2610
+ description=app_info.get('description', ''),
2611
+ icon=app_info.get('icon'),
2612
+ module_path=app_info.get('module_path')
2613
+ )
2614
+ return registry.register_app(info)
2615
+ except Exception as e:
2616
+ print(f"[APK] Error registering app: {e}")
2617
+ return False
2618
+
2619
+ def getAppInfo(self, app_id: str) -> Optional[Dict]:
2620
+ """Get app info by ID"""
2621
+ registry = self._get_registry()
2622
+ if not registry:
2623
+ return None
2624
+
2625
+ info = registry.get_app(app_id)
2626
+ if info:
2627
+ return {
2628
+ 'id': info.app_id,
2629
+ 'name': info.name,
2630
+ 'version': info.version,
2631
+ 'category': info.category,
2632
+ 'description': info.description,
2633
+ 'icon': info.icon
2634
+ }
2635
+ return None
2636
+
2637
+ def listApps(self) -> List[Dict]:
2638
+ """List all registered apps"""
2639
+ registry = self._get_registry()
2640
+ if not registry:
2641
+ return []
2642
+
2643
+ return [
2644
+ {
2645
+ 'id': info.app_id,
2646
+ 'name': info.name,
2647
+ 'category': info.category
2648
+ }
2649
+ for info in registry.get_all_apps()
2650
+ ]
2651
+
2652
+ def launchApp(self, app_id: str) -> bool:
2653
+ """Launch an app by ID (placeholder - needs desktop integration)"""
2654
+ print(f"[APK] TODO: Launch app {app_id}")
2655
+ return False
2656
+
2657
+ def isInstalled(self, app_id: str) -> bool:
2658
+ """Check if app is installed"""
2659
+ registry = self._get_registry()
2660
+ if not registry:
2661
+ return False
2662
+ return app_id in registry
2663
+
2664
+
2665
+ class AppServiceBuilder:
2666
+ """Builder class for app service registration with <== syntax."""
2667
+
2668
+ def __init__(self, module: APKModule, config: Dict):
2669
+ self._module = module
2670
+ self._config = config
2671
+
2672
+ def set_handler(self, config: Dict) -> bool:
2673
+ """Called by runtime for <== assignment"""
2674
+ # Merge configs
2675
+ full_config = {**self._config, **config}
2676
+
2677
+ script = full_config.get('script', '')
2678
+ service = full_config.get('service', '')
2679
+ execute_on_boot = full_config.get('execute_on_boot', False)
2680
+
2681
+ # Derive service name from .cll filename
2682
+ service_name = service.replace('.cll', '') if service else ''
2683
+
2684
+ if not service_name:
2685
+ print("[APK] Error: service name is required")
2686
+ return False
2687
+
2688
+ # Look for .cll-meta file
2689
+ root_dir = self._module._get_root_dir()
2690
+ meta_path = os.path.join(root_dir, "sys", "intern", f"{service_name}.cll-meta")
2691
+
2692
+ if os.path.exists(meta_path):
2693
+ # Parse metadata and register
2694
+ app_info = self._parse_meta_file(meta_path)
2695
+ if app_info:
2696
+ app_info['script'] = script
2697
+ success = self._module.registerApp(app_info)
2698
+
2699
+ if success:
2700
+ print(f"[APK] Registered app: {app_info.get('name', service_name)}")
2701
+
2702
+ if execute_on_boot:
2703
+ print(f"[APK] TODO: Schedule {service_name} for boot execution")
2704
+
2705
+ return success
2706
+
2707
+ print(f"[APK] Meta file not found: {meta_path}")
2708
+ return False
2709
+
2710
+ def _parse_meta_file(self, path: str) -> Optional[Dict]:
2711
+ """Parse .cll-meta file"""
2712
+ try:
2713
+ data = {}
2714
+ with open(path, 'r', encoding='utf-8') as f:
2715
+ for line in f:
2716
+ line = line.strip()
2717
+ if '=' in line and not line.startswith('#'):
2718
+ key, value = line.split('=', 1)
2719
+ data[key.strip()] = value.strip()
2720
+
2721
+ return {
2722
+ 'id': data.get('Name', '').lower().replace(' ', '_'),
2723
+ 'name': data.get('Name', ''),
2724
+ 'version': data.get('Version', '1.0.0'),
2725
+ 'category': data.get('Category', 'Apps'),
2726
+ 'description': data.get('Description', ''),
2727
+ 'icon': data.get('Icon'),
2728
+ 'module_path': data.get('Module')
2729
+ }
2730
+ except Exception as e:
2731
+ print(f"[APK] Error parsing {path}: {e}")
2732
+ return None
2733
+
2734
+
2735
+ # =============================================================================
2736
+ # Module Registry
2737
+ # =============================================================================
2738
+
2739
+ class CSSLModuleRegistry:
2740
+ """Registry of all CSSL standard modules"""
2741
+
2742
+ _instance = None
2743
+ _modules: Dict[str, CSSLModuleBase] = {}
2744
+
2745
+ def __new__(cls):
2746
+ if cls._instance is None:
2747
+ cls._instance = super().__new__(cls)
2748
+ cls._instance._init_modules()
2749
+ return cls._instance
2750
+
2751
+ def _init_modules(self):
2752
+ """Initialize all standard modules"""
2753
+ self._modules = {
2754
+ 'Time': TimeModule(),
2755
+ 'Secrets': SecretsModule(),
2756
+ 'Math': MathModule(),
2757
+ 'Crypto': CryptoModule(),
2758
+ 'Net': NetModule(),
2759
+ 'IO': IOModule(),
2760
+ 'JSON': JSONModule(),
2761
+ 'Regex': RegexModule(),
2762
+ 'System': SystemModule(),
2763
+ 'Log': LogModule(),
2764
+ 'Cache': CacheModule(),
2765
+ 'Queue': QueueModule(),
2766
+ 'Format': FormatModule(),
2767
+ 'Console': ConsoleModule(),
2768
+ 'Process': ProcessModule(),
2769
+ 'Config': ConfigModule(),
2770
+ 'Server': ServerModule(),
2771
+ }
2772
+
2773
+ # Register Desktop module (lazy loaded)
2774
+ try:
2775
+ from .cssl_desktop import get_desktop_module
2776
+ self._modules['Desktop'] = get_desktop_module()
2777
+ except ImportError:
2778
+ pass
2779
+
2780
+ # Register APK module for app registration
2781
+ self._modules['APK'] = APKModule()
2782
+
2783
+ def get_module(self, name: str) -> Optional[CSSLModuleBase]:
2784
+ """Get a module by name"""
2785
+ return self._modules.get(name)
2786
+
2787
+ def list_modules(self) -> List[str]:
2788
+ """List all available module names"""
2789
+ return sorted(self._modules.keys())
2790
+
2791
+ def register_module(self, name: str, module: CSSLModuleBase):
2792
+ """Register a custom module"""
2793
+ self._modules[name] = module
2794
+
2795
+
2796
+ def get_module_registry() -> CSSLModuleRegistry:
2797
+ """Get the global module registry"""
2798
+ return CSSLModuleRegistry()
2799
+
2800
+
2801
+ def get_standard_module(name: str) -> Optional[CSSLModuleBase]:
2802
+ """Get a standard module by name"""
2803
+ return get_module_registry().get_module(name)