@mriekki/lean 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +71 -67
  2. package/assets/components.json +33 -33
  3. package/assets/core/distribution.json +52 -51
  4. package/assets/deployment/distribution.json +34 -34
  5. package/assets/generated/ASSEMBLY.json +38 -33
  6. package/assets/generated/core/core/skills/core-sync/SKILL.md +78 -120
  7. package/assets/generated/core/core/skills/deploy/SKILL.md +74 -91
  8. package/assets/generated/core/core/skills/lean-update/SKILL.md +189 -0
  9. package/assets/generated/core/core/skills/upstream-fix/SKILL.md +91 -76
  10. package/assets/generated/deployment/distribution/lean-poc-deploy.py +8 -8
  11. package/assets/generated/deployment/src/lean_poc_deployment/__init__.py +3 -3
  12. package/assets/generated/deployment/src/lean_poc_deployment/__main__.py +3 -3
  13. package/assets/generated/deployment/src/lean_poc_deployment/app_cli.py +140 -140
  14. package/assets/generated/deployment/src/lean_poc_deployment/app_runtime.py +455 -455
  15. package/assets/generated/deployment/src/lean_poc_deployment/app_runtime_remote.py +415 -415
  16. package/assets/generated/deployment/src/lean_poc_deployment/box_platform.py +208 -208
  17. package/assets/generated/deployment/src/lean_poc_deployment/box_platform_assets.py +100 -100
  18. package/assets/generated/deployment/src/lean_poc_deployment/box_platform_remote.py +271 -271
  19. package/assets/generated/deployment/src/lean_poc_deployment/cli.py +437 -437
  20. package/assets/generated/deployment/src/lean_poc_deployment/doctor.py +121 -121
  21. package/assets/generated/deployment/src/lean_poc_deployment/entrypoint.py +25 -25
  22. package/assets/generated/deployment/src/lean_poc_deployment/errors.py +5 -5
  23. package/assets/generated/deployment/src/lean_poc_deployment/keys.py +171 -171
  24. package/assets/generated/deployment/src/lean_poc_deployment/profiles.py +204 -204
  25. package/assets/generated/deployment/src/lean_poc_deployment/route_registry.py +367 -367
  26. package/assets/generated/deployment/src/lean_poc_deployment/route_registry_remote.py +443 -443
  27. package/assets/generated/deployment/src/lean_poc_deployment/security.py +290 -290
  28. package/assets/generated/deployment/src/lean_poc_deployment/sqlite_adapter.py +334 -334
  29. package/assets/generated/deployment/src/lean_poc_deployment/sqlite_adapter_remote.py +339 -339
  30. package/assets/generated/deployment/src/lean_poc_deployment/sqlite_cli.py +85 -85
  31. package/assets/generated/deployment/src/lean_poc_deployment/sqlite_import_recovery.py +437 -437
  32. package/assets/generated/deployment/src/lean_poc_deployment/ssh.py +118 -118
  33. package/assets/generated/deployment/src/lean_poc_deployment/teardown.py +286 -286
  34. package/assets/generated/deployment/src/lean_poc_deployment/teardown_cli.py +120 -120
  35. package/assets/generated/deployment/src/lean_poc_deployment/teardown_remote.py +100 -100
  36. package/assets/generated/deployment/src/lean_poc_deployment/trust.py +278 -278
  37. package/bin/lean.mjs +9 -9
  38. package/package.json +33 -33
  39. package/src/cli.mjs +131 -131
  40. package/src/engine.mjs +243 -243
  41. package/src/package-data.mjs +18 -18
@@ -1,118 +1,118 @@
1
- """Strict SSH verification primitives for logical targets."""
2
-
3
- from __future__ import annotations
4
-
5
- import shutil
6
- import socket
7
- import subprocess
8
- from pathlib import Path
9
- from typing import Any
10
-
11
- from .errors import DeploymentKitError
12
- from .security import check_ssh_material_security, validate_target_trust_file
13
-
14
-
15
- def required_tool(name: str) -> str:
16
- path = shutil.which(name)
17
- if path is None:
18
- raise DeploymentKitError(f"Required OpenSSH tool is unavailable: {name}")
19
- return path
20
-
21
-
22
- def resolve_addresses(profile: dict[str, Any]) -> list[str]:
23
- override = profile["addressOverride"]
24
- if override:
25
- return [override]
26
- try:
27
- answers = socket.getaddrinfo(
28
- profile["hostName"],
29
- profile["port"],
30
- type=socket.SOCK_STREAM,
31
- )
32
- except OSError as error:
33
- raise DeploymentKitError(
34
- f"Cannot resolve target hostname {profile['hostName']}: {error}. "
35
- "Use only a separately verified address override; do not scan the network."
36
- ) from error
37
- addresses = sorted({answer[4][0] for answer in answers})
38
- if not addresses:
39
- raise DeploymentKitError(f"Target hostname {profile['hostName']} returned no addresses.")
40
- return addresses
41
-
42
-
43
- def ssh_args(profile: dict[str, Any], address: str, *, command: str = "true") -> list[str]:
44
- return [
45
- required_tool("ssh"),
46
- "-F",
47
- "none",
48
- "-o",
49
- "BatchMode=yes",
50
- "-o",
51
- "ConnectTimeout=8",
52
- "-o",
53
- "StrictHostKeyChecking=yes",
54
- "-o",
55
- "UpdateHostKeys=no",
56
- "-o",
57
- "CheckHostIP=no",
58
- "-o",
59
- "VerifyHostKeyDNS=no",
60
- "-o",
61
- "GlobalKnownHostsFile=none",
62
- "-o",
63
- f"UserKnownHostsFile={profile['knownHostsFile']}",
64
- "-o",
65
- f"HostKeyAlias={profile['sshAlias']}",
66
- "-o",
67
- f"IdentityFile={profile['identityFile']}",
68
- "-o",
69
- "IdentitiesOnly=yes",
70
- "-o",
71
- "PasswordAuthentication=no",
72
- "-o",
73
- "KbdInteractiveAuthentication=no",
74
- "-o",
75
- "PreferredAuthentications=publickey",
76
- "-o",
77
- "ForwardAgent=no",
78
- "-o",
79
- "ForwardX11=no",
80
- "-o",
81
- "ClearAllForwardings=yes",
82
- "-p",
83
- str(profile["port"]),
84
- f"{profile['user']}@{address}",
85
- command,
86
- ]
87
-
88
-
89
- def verify_access(profile: dict[str, Any]) -> dict[str, str]:
90
- identity = Path(profile["identityFile"])
91
- known_hosts = Path(profile["knownHostsFile"])
92
- if not identity.is_file():
93
- raise DeploymentKitError(f"Dedicated private key is missing: {identity}")
94
- if not known_hosts.is_file() or known_hosts.stat().st_size == 0:
95
- raise DeploymentKitError(
96
- f"Target-specific trust file is missing or empty: {known_hosts}. "
97
- "Enroll trust only after independently verifying the host fingerprint."
98
- )
99
- check_ssh_material_security(identity, known_hosts)
100
- validate_target_trust_file(profile)
101
-
102
- failures: list[str] = []
103
- for address in resolve_addresses(profile):
104
- result = subprocess.run(
105
- ssh_args(profile, address),
106
- check=False,
107
- capture_output=True,
108
- text=True,
109
- encoding="utf-8",
110
- errors="replace",
111
- )
112
- if result.returncode == 0:
113
- return {"targetName": profile["targetName"], "address": address, "status": "verified"}
114
- failures.append(f"{address}: exit {result.returncode}")
115
-
116
- raise DeploymentKitError(
117
- "Public-key SSH verification failed for all resolved addresses: " + "; ".join(failures)
118
- )
1
+ """Strict SSH verification primitives for logical targets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ import socket
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from .errors import DeploymentKitError
12
+ from .security import check_ssh_material_security, validate_target_trust_file
13
+
14
+
15
+ def required_tool(name: str) -> str:
16
+ path = shutil.which(name)
17
+ if path is None:
18
+ raise DeploymentKitError(f"Required OpenSSH tool is unavailable: {name}")
19
+ return path
20
+
21
+
22
+ def resolve_addresses(profile: dict[str, Any]) -> list[str]:
23
+ override = profile["addressOverride"]
24
+ if override:
25
+ return [override]
26
+ try:
27
+ answers = socket.getaddrinfo(
28
+ profile["hostName"],
29
+ profile["port"],
30
+ type=socket.SOCK_STREAM,
31
+ )
32
+ except OSError as error:
33
+ raise DeploymentKitError(
34
+ f"Cannot resolve target hostname {profile['hostName']}: {error}. "
35
+ "Use only a separately verified address override; do not scan the network."
36
+ ) from error
37
+ addresses = sorted({answer[4][0] for answer in answers})
38
+ if not addresses:
39
+ raise DeploymentKitError(f"Target hostname {profile['hostName']} returned no addresses.")
40
+ return addresses
41
+
42
+
43
+ def ssh_args(profile: dict[str, Any], address: str, *, command: str = "true") -> list[str]:
44
+ return [
45
+ required_tool("ssh"),
46
+ "-F",
47
+ "none",
48
+ "-o",
49
+ "BatchMode=yes",
50
+ "-o",
51
+ "ConnectTimeout=8",
52
+ "-o",
53
+ "StrictHostKeyChecking=yes",
54
+ "-o",
55
+ "UpdateHostKeys=no",
56
+ "-o",
57
+ "CheckHostIP=no",
58
+ "-o",
59
+ "VerifyHostKeyDNS=no",
60
+ "-o",
61
+ "GlobalKnownHostsFile=none",
62
+ "-o",
63
+ f"UserKnownHostsFile={profile['knownHostsFile']}",
64
+ "-o",
65
+ f"HostKeyAlias={profile['sshAlias']}",
66
+ "-o",
67
+ f"IdentityFile={profile['identityFile']}",
68
+ "-o",
69
+ "IdentitiesOnly=yes",
70
+ "-o",
71
+ "PasswordAuthentication=no",
72
+ "-o",
73
+ "KbdInteractiveAuthentication=no",
74
+ "-o",
75
+ "PreferredAuthentications=publickey",
76
+ "-o",
77
+ "ForwardAgent=no",
78
+ "-o",
79
+ "ForwardX11=no",
80
+ "-o",
81
+ "ClearAllForwardings=yes",
82
+ "-p",
83
+ str(profile["port"]),
84
+ f"{profile['user']}@{address}",
85
+ command,
86
+ ]
87
+
88
+
89
+ def verify_access(profile: dict[str, Any]) -> dict[str, str]:
90
+ identity = Path(profile["identityFile"])
91
+ known_hosts = Path(profile["knownHostsFile"])
92
+ if not identity.is_file():
93
+ raise DeploymentKitError(f"Dedicated private key is missing: {identity}")
94
+ if not known_hosts.is_file() or known_hosts.stat().st_size == 0:
95
+ raise DeploymentKitError(
96
+ f"Target-specific trust file is missing or empty: {known_hosts}. "
97
+ "Enroll trust only after independently verifying the host fingerprint."
98
+ )
99
+ check_ssh_material_security(identity, known_hosts)
100
+ validate_target_trust_file(profile)
101
+
102
+ failures: list[str] = []
103
+ for address in resolve_addresses(profile):
104
+ result = subprocess.run(
105
+ ssh_args(profile, address),
106
+ check=False,
107
+ capture_output=True,
108
+ text=True,
109
+ encoding="utf-8",
110
+ errors="replace",
111
+ )
112
+ if result.returncode == 0:
113
+ return {"targetName": profile["targetName"], "address": address, "status": "verified"}
114
+ failures.append(f"{address}: exit {result.returncode}")
115
+
116
+ raise DeploymentKitError(
117
+ "Public-key SSH verification failed for all resolved addresses: " + "; ".join(failures)
118
+ )