dulus 0.2.0__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.
Files changed (101) hide show
  1. agent.py +363 -0
  2. backend/__init__.py +63 -0
  3. backend/compressor.py +261 -0
  4. backend/context.py +329 -0
  5. backend/githook.py +166 -0
  6. backend/marketplace.py +141 -0
  7. backend/mempalace_bridge.py +182 -0
  8. backend/personas.py +297 -0
  9. backend/plugins.py +222 -0
  10. backend/server.py +411 -0
  11. backend/tasks.py +213 -0
  12. batch_api.py +307 -0
  13. checkpoint/__init__.py +27 -0
  14. checkpoint/hooks.py +90 -0
  15. checkpoint/store.py +314 -0
  16. checkpoint/types.py +80 -0
  17. claude_code_watcher.py +214 -0
  18. clipboard_utils.py +246 -0
  19. cloudsave.py +159 -0
  20. common.py +177 -0
  21. compaction.py +378 -0
  22. config.py +180 -0
  23. context.py +241 -0
  24. dulus-0.2.0.dist-info/METADATA +600 -0
  25. dulus-0.2.0.dist-info/RECORD +101 -0
  26. dulus-0.2.0.dist-info/WHEEL +5 -0
  27. dulus-0.2.0.dist-info/entry_points.txt +2 -0
  28. dulus-0.2.0.dist-info/licenses/LICENSE +674 -0
  29. dulus-0.2.0.dist-info/licenses/license_manager.py +187 -0
  30. dulus-0.2.0.dist-info/top_level.txt +36 -0
  31. dulus.py +8455 -0
  32. dulus_gui.py +331 -0
  33. dulus_mcp/__init__.py +43 -0
  34. dulus_mcp/client.py +546 -0
  35. dulus_mcp/config.py +133 -0
  36. dulus_mcp/tools.py +131 -0
  37. dulus_mcp/types.py +124 -0
  38. gui/__init__.py +18 -0
  39. gui/agent_bridge.py +283 -0
  40. gui/chat_widget.py +448 -0
  41. gui/main_window.py +485 -0
  42. gui/personas.py +230 -0
  43. gui/session_utils.py +189 -0
  44. gui/settings_dialog.py +146 -0
  45. gui/sidebar.py +515 -0
  46. gui/tasks_view.py +499 -0
  47. gui/themes.py +256 -0
  48. gui/tool_panel.py +94 -0
  49. input.py +1030 -0
  50. license_manager.py +187 -0
  51. memory/__init__.py +93 -0
  52. memory/audit.py +51 -0
  53. memory/consolidator.py +312 -0
  54. memory/context.py +270 -0
  55. memory/offload.py +148 -0
  56. memory/palace.py +127 -0
  57. memory/scan.py +146 -0
  58. memory/sessions.py +100 -0
  59. memory/store.py +395 -0
  60. memory/tools.py +408 -0
  61. memory/types.py +114 -0
  62. memory/vector_search.py +92 -0
  63. multi_agent/__init__.py +23 -0
  64. multi_agent/subagent.py +501 -0
  65. multi_agent/tools.py +393 -0
  66. offload_helper.py +183 -0
  67. plugin/__init__.py +22 -0
  68. plugin/autoadapter.py +1641 -0
  69. plugin/loader.py +156 -0
  70. plugin/recommend.py +211 -0
  71. plugin/store.py +387 -0
  72. plugin/types.py +147 -0
  73. providers.py +3750 -0
  74. skill/__init__.py +14 -0
  75. skill/builtin.py +100 -0
  76. skill/clawhub.py +270 -0
  77. skill/executor.py +66 -0
  78. skill/loader.py +199 -0
  79. skill/tools.py +110 -0
  80. skills.py +14 -0
  81. spinner.py +42 -0
  82. string_utils.py +42 -0
  83. subagent.py +11 -0
  84. task/__init__.py +12 -0
  85. task/store.py +199 -0
  86. task/tools.py +265 -0
  87. task/types.py +92 -0
  88. tmux_offloader.py +177 -0
  89. tmux_tools.py +410 -0
  90. tool_registry.py +214 -0
  91. tools.py +2694 -0
  92. ui/__init__.py +1 -0
  93. ui/input.py +464 -0
  94. ui/render.py +272 -0
  95. voice/__init__.py +56 -0
  96. voice/keyterms.py +179 -0
  97. voice/recorder.py +263 -0
  98. voice/stt.py +408 -0
  99. voice/tts.py +570 -0
  100. webchat.py +432 -0
  101. webchat_server.py +1761 -0
@@ -0,0 +1,187 @@
1
+ """Dulus License Manager — Offline-first key validation + feature gating.
2
+
3
+ Tiers:
4
+ FREE No key required. Limited tool calls, local providers only.
5
+ PRO $15/mo. Full features, BYOK, priority support.
6
+ ENTERPRISE $50/mo. Team features + admin dashboard + SSO (future).
7
+
8
+ Key format (offline):
9
+ DULUS-<base64(json_payload + ":" + hmac_signature)>
10
+
11
+ The secret lives in ~/.dulus/.license_secret (never commit this file).
12
+ If the secret file is missing we fall back to a hardcoded dev-key so
13
+ Kev can develop without friction, but distribution builds MUST bundle
14
+ a real secret via CI env var or PyInstaller --add-data.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import base64
19
+ import hashlib
20
+ import hmac
21
+ import json
22
+ import os
23
+ import sys
24
+ import time
25
+ from pathlib import Path
26
+ from typing import Optional
27
+
28
+ # ── Secret resolution ───────────────────────────────────────────────────────
29
+ # 1. CI / build-time env var (safest for releases)
30
+ # 2. ~/.dulus/.license_secret (Kev's local dev key)
31
+ # 3. Fallback dev secret (NEVER use in production builds)
32
+ _LICENSE_SECRET = os.environ.get("DULUS_LICENSE_SECRET", "")
33
+ if not _LICENSE_SECRET:
34
+ _secret_path = Path.home() / ".dulus" / ".license_secret"
35
+ if _secret_path.exists():
36
+ _LICENSE_SECRET = _secret_path.read_text().strip()
37
+ else:
38
+ _LICENSE_SECRET = "dulus-dev-secret-do-not-distribute"
39
+ import warnings
40
+ warnings.warn(
41
+ "DULUS_LICENSE_SECRET not set — using hardcoded DEV secret. "
42
+ "Generated keys will be trivially forgeable in production!",
43
+ RuntimeWarning,
44
+ stacklevel=2,
45
+ )
46
+
47
+
48
+ class LicenseTier:
49
+ FREE = "free"
50
+ PRO = "pro"
51
+ ENTERPRISE = "enterprise"
52
+
53
+
54
+ class LicenseManager:
55
+ """Parse and validate a Dulus license key."""
56
+
57
+ def __init__(self, key: Optional[str] = None):
58
+ self.raw_key = key or ""
59
+ self.tier = LicenseTier.FREE
60
+ self.expiry: float = 0.0
61
+ self.features: list[str] = []
62
+ self.valid = False
63
+ self.error: Optional[str] = None
64
+
65
+ if self.raw_key:
66
+ self._validate()
67
+
68
+ # ── validation core ─────────────────────────────────────────────────────
69
+
70
+ def _validate(self) -> None:
71
+ if not self.raw_key.startswith("DULUS-"):
72
+ self.error = "Invalid key prefix"
73
+ return
74
+
75
+ try:
76
+ b64 = self.raw_key.split("-", 1)[1]
77
+ payload_sig = base64.urlsafe_b64decode(b64 + "==")
78
+ payload_json, sig_hex = payload_sig.rsplit(b":", 1)
79
+ data = json.loads(payload_json)
80
+ except Exception as exc:
81
+ self.error = f"Malformed key: {exc}"
82
+ return
83
+
84
+ # Verify HMAC-SHA256 signature
85
+ expected_sig = hmac.new(
86
+ _LICENSE_SECRET.encode(),
87
+ payload_json,
88
+ hashlib.sha256,
89
+ ).hexdigest()[:24]
90
+
91
+ if not hmac.compare_digest(sig_hex.decode(), expected_sig):
92
+ self.error = "Invalid signature (tampered or wrong secret)"
93
+ return
94
+
95
+ self.tier = data.get("tier", LicenseTier.FREE)
96
+ self.expiry = data.get("exp", 0)
97
+ self.features = data.get("features", [])
98
+
99
+ if time.time() > self.expiry:
100
+ self.error = "License expired"
101
+ return
102
+
103
+ self.valid = True
104
+
105
+ # ── feature gates ───────────────────────────────────────────────────────
106
+
107
+ def can_use(self, feature: str) -> bool:
108
+ """Check if a feature is allowed by current tier."""
109
+ if self.tier == LicenseTier.ENTERPRISE:
110
+ return True
111
+ if self.tier == LicenseTier.PRO:
112
+ return feature not in {"sso", "audit_logs", "admin_dashboard"}
113
+ # FREE
114
+ free_features = {"chat", "tools_basic", "local_providers"}
115
+ return feature in free_features
116
+
117
+ def max_tool_calls(self) -> int:
118
+ if self.tier == LicenseTier.ENTERPRISE:
119
+ return 999_999
120
+ if self.tier == LicenseTier.PRO:
121
+ return 10_000
122
+ return 25 # FREE daily limit
123
+
124
+ def max_providers(self) -> int:
125
+ if self.tier in (LicenseTier.PRO, LicenseTier.ENTERPRISE):
126
+ return 99
127
+ return 2 # FREE: e.g. ollama + 1 cloud
128
+
129
+ def max_subagents(self) -> int:
130
+ if self.tier == LicenseTier.ENTERPRISE:
131
+ return 50
132
+ if self.tier == LicenseTier.PRO:
133
+ return 10
134
+ return 0 # FREE: no subagents
135
+
136
+ def max_plugins(self) -> int:
137
+ if self.tier == LicenseTier.ENTERPRISE:
138
+ return 999
139
+ if self.tier == LicenseTier.PRO:
140
+ return 50
141
+ return 3 # FREE
142
+
143
+ def allow_cloudsave(self) -> bool:
144
+ return self.tier in (LicenseTier.PRO, LicenseTier.ENTERPRISE)
145
+
146
+ def allow_voice(self) -> bool:
147
+ return self.tier in (LicenseTier.PRO, LicenseTier.ENTERPRISE)
148
+
149
+ def allow_telegram(self) -> bool:
150
+ return self.tier in (LicenseTier.PRO, LicenseTier.ENTERPRISE)
151
+
152
+ def allow_mcp(self) -> bool:
153
+ return self.tier in (LicenseTier.PRO, LicenseTier.ENTERPRISE)
154
+
155
+ # ── UI helpers ──────────────────────────────────────────────────────────
156
+
157
+ def status_banner(self) -> str:
158
+ if self.error:
159
+ return f"[LICENSE EXPIRED / INVALID] {self.error} — running in FREE mode"
160
+ if self.tier == LicenseTier.FREE:
161
+ return "[FREE] Limited features. Upgrade: https://getdulus.dev/pro"
162
+ return f"[{self.tier.upper()}] Valid until {time.strftime('%Y-%m-%d', time.localtime(self.expiry))}"
163
+
164
+
165
+ # ── CLI helper for Kev ─────────────────────────────────────────────────────
166
+
167
+ def _generate_key(tier: str, days: int, secret: str) -> str:
168
+ """Generate a signed license key (Kev-only tool)."""
169
+ payload = json.dumps({
170
+ "tier": tier,
171
+ "exp": int(time.time() + days * 86400),
172
+ "features": [],
173
+ "iat": int(time.time()),
174
+ }, separators=(",", ":")).encode()
175
+ sig = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()[:24]
176
+ token = base64.urlsafe_b64encode(payload + b":" + sig.encode()).decode().rstrip("=")
177
+ return f"DULUS-{token}"
178
+
179
+
180
+ if __name__ == "__main__":
181
+ import argparse
182
+ ap = argparse.ArgumentParser(description="Dulus License Key Generator (Kev only)")
183
+ ap.add_argument("tier", choices=["free", "pro", "enterprise"])
184
+ ap.add_argument("--days", type=int, default=30)
185
+ ap.add_argument("--secret", default=_LICENSE_SECRET)
186
+ args = ap.parse_args()
187
+ print(_generate_key(args.tier, args.days, args.secret))
@@ -0,0 +1,36 @@
1
+ agent
2
+ backend
3
+ batch_api
4
+ checkpoint
5
+ claude_code_watcher
6
+ clipboard_utils
7
+ cloudsave
8
+ common
9
+ compaction
10
+ config
11
+ context
12
+ dulus
13
+ dulus_gui
14
+ dulus_mcp
15
+ gui
16
+ input
17
+ license_manager
18
+ memory
19
+ multi_agent
20
+ offload_helper
21
+ plugin
22
+ providers
23
+ skill
24
+ skills
25
+ spinner
26
+ string_utils
27
+ subagent
28
+ task
29
+ tmux_offloader
30
+ tmux_tools
31
+ tool_registry
32
+ tools
33
+ ui
34
+ voice
35
+ webchat
36
+ webchat_server