nullabot 1.0.1__py3-none-any.whl → 1.0.3__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.
- nullabot/agents/claude_agent.py +76 -12
- nullabot/bot/telegram.py +26 -4
- nullabot/core/memory.py +39 -5
- {nullabot-1.0.1.dist-info → nullabot-1.0.3.dist-info}/METADATA +1 -1
- {nullabot-1.0.1.dist-info → nullabot-1.0.3.dist-info}/RECORD +8 -8
- {nullabot-1.0.1.dist-info → nullabot-1.0.3.dist-info}/WHEEL +0 -0
- {nullabot-1.0.1.dist-info → nullabot-1.0.3.dist-info}/entry_points.txt +0 -0
- {nullabot-1.0.1.dist-info → nullabot-1.0.3.dist-info}/licenses/LICENSE +0 -0
nullabot/agents/claude_agent.py
CHANGED
|
@@ -300,12 +300,17 @@ IMPORTANT:
|
|
|
300
300
|
async def _run_claude_async(
|
|
301
301
|
self,
|
|
302
302
|
prompt: str,
|
|
303
|
-
) -> tuple[str, bool]:
|
|
304
|
-
"""
|
|
303
|
+
) -> tuple[str, bool, int, int]:
|
|
304
|
+
"""
|
|
305
|
+
Run Claude Code CLI asynchronously.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
tuple: (response_text, success, input_tokens, output_tokens)
|
|
309
|
+
"""
|
|
305
310
|
cmd = [
|
|
306
311
|
"claude",
|
|
307
312
|
"-p",
|
|
308
|
-
"--output-format", "
|
|
313
|
+
"--output-format", "json",
|
|
309
314
|
"--model", self.model,
|
|
310
315
|
"--max-turns", "50",
|
|
311
316
|
"--dangerously-skip-permissions",
|
|
@@ -332,15 +337,51 @@ IMPORTANT:
|
|
|
332
337
|
output = stdout.decode().strip()
|
|
333
338
|
if process.returncode != 0:
|
|
334
339
|
error = stderr.decode() or "Unknown error"
|
|
335
|
-
return f"Error: {error}", False
|
|
340
|
+
return f"Error: {error}", False, 0, 0
|
|
341
|
+
|
|
342
|
+
# Parse JSON response to extract tokens
|
|
343
|
+
try:
|
|
344
|
+
data = json.loads(output)
|
|
336
345
|
|
|
337
|
-
|
|
346
|
+
# Get result text - try multiple fields
|
|
347
|
+
result_text = data.get("result") or data.get("content") or ""
|
|
348
|
+
|
|
349
|
+
# If no result text but we have the raw output, use a summary
|
|
350
|
+
if not result_text and data.get("subtype") == "error_max_turns":
|
|
351
|
+
result_text = "Reached max turns limit. Work saved."
|
|
352
|
+
elif not result_text:
|
|
353
|
+
result_text = output # Fallback to raw output
|
|
354
|
+
|
|
355
|
+
# Extract token usage from nested usage object
|
|
356
|
+
usage = data.get("usage", {})
|
|
357
|
+
|
|
358
|
+
# Input includes regular + cache tokens
|
|
359
|
+
input_tokens = (
|
|
360
|
+
usage.get("input_tokens", 0) +
|
|
361
|
+
usage.get("cache_read_input_tokens", 0) +
|
|
362
|
+
usage.get("cache_creation_input_tokens", 0)
|
|
363
|
+
)
|
|
364
|
+
output_tokens = usage.get("output_tokens", 0)
|
|
365
|
+
|
|
366
|
+
# Also check modelUsage for more detailed breakdown
|
|
367
|
+
model_usage = data.get("modelUsage", {})
|
|
368
|
+
if model_usage and not input_tokens:
|
|
369
|
+
for model, mu in model_usage.items():
|
|
370
|
+
input_tokens += mu.get("inputTokens", 0)
|
|
371
|
+
input_tokens += mu.get("cacheReadInputTokens", 0)
|
|
372
|
+
input_tokens += mu.get("cacheCreationInputTokens", 0)
|
|
373
|
+
output_tokens += mu.get("outputTokens", 0)
|
|
374
|
+
|
|
375
|
+
return result_text, True, input_tokens, output_tokens
|
|
376
|
+
except json.JSONDecodeError:
|
|
377
|
+
# Fallback if not valid JSON
|
|
378
|
+
return output, True, 0, 0
|
|
338
379
|
|
|
339
380
|
except asyncio.TimeoutError:
|
|
340
381
|
timeout_min = self.timeout // 60
|
|
341
|
-
return f"Error: Claude timed out after {timeout_min} minutes", False
|
|
382
|
+
return f"Error: Claude timed out after {timeout_min} minutes", False, 0, 0
|
|
342
383
|
except Exception as e:
|
|
343
|
-
return f"Error: {str(e)}", False
|
|
384
|
+
return f"Error: {str(e)}", False, 0, 0
|
|
344
385
|
|
|
345
386
|
def _get_previous_work_hint(self) -> str:
|
|
346
387
|
"""Get hint about where to find previous agents' work."""
|
|
@@ -500,16 +541,16 @@ IMPORTANT:
|
|
|
500
541
|
)
|
|
501
542
|
|
|
502
543
|
# Run Claude
|
|
503
|
-
response, success = await self._run_claude_async(current_prompt)
|
|
544
|
+
response, success, input_tokens, output_tokens = await self._run_claude_async(current_prompt)
|
|
504
545
|
|
|
505
546
|
cycle_duration = (datetime.now() - cycle_start).total_seconds()
|
|
506
547
|
|
|
507
|
-
# Track usage
|
|
548
|
+
# Track usage with actual token counts
|
|
508
549
|
usage_info = self.usage.record_cycle(
|
|
509
550
|
model=self.model,
|
|
510
551
|
agent_type=self.agent_type,
|
|
511
|
-
input_tokens=
|
|
512
|
-
output_tokens=
|
|
552
|
+
input_tokens=input_tokens,
|
|
553
|
+
output_tokens=output_tokens,
|
|
513
554
|
duration_seconds=cycle_duration,
|
|
514
555
|
)
|
|
515
556
|
|
|
@@ -636,14 +677,25 @@ IMPORTANT:
|
|
|
636
677
|
state["total_time_seconds"] = (datetime.now() - start_time).total_seconds()
|
|
637
678
|
self._save_state(state)
|
|
638
679
|
|
|
639
|
-
# Send cycle complete notification with window usage
|
|
680
|
+
# Send cycle complete notification with window usage and tokens
|
|
640
681
|
window_pct = usage_info.get('window_usage_pct', 0)
|
|
641
682
|
window_bar = self._make_progress_bar(window_pct)
|
|
683
|
+
cycle_tokens = usage_info.get('cycle_tokens', 0)
|
|
684
|
+
total_tokens = usage_info.get('total_tokens', 0)
|
|
685
|
+
|
|
686
|
+
# Format token counts (K for thousands, M for millions)
|
|
687
|
+
def fmt_tokens(n):
|
|
688
|
+
if n >= 1_000_000:
|
|
689
|
+
return f"{n/1_000_000:.1f}M"
|
|
690
|
+
elif n >= 1_000:
|
|
691
|
+
return f"{n/1_000:.1f}K"
|
|
692
|
+
return str(n)
|
|
642
693
|
|
|
643
694
|
await self._notify(
|
|
644
695
|
"cycle_complete",
|
|
645
696
|
f"✅ *Cycle {cycle}* complete ({cycle_duration:.0f}s)\n\n"
|
|
646
697
|
f"📁 *Files:* {file_count}\n"
|
|
698
|
+
f"🔤 *Tokens:* {fmt_tokens(cycle_tokens)} (Total: {fmt_tokens(total_tokens)})\n"
|
|
647
699
|
f"⏱ *5hr window:* {window_bar} {window_pct:.0f}%\n"
|
|
648
700
|
f"💰 *Est. cost:* ${usage_info['cycle_cost']:.2f} (Total: ${usage_info['total_cost']:.2f})\n"
|
|
649
701
|
f"📊 {status_line}\n\n"
|
|
@@ -741,6 +793,17 @@ Remember to include STATUS and SUMMARY lines at the end."""
|
|
|
741
793
|
window_pct = usage_summary.get('window_usage_pct', 0)
|
|
742
794
|
window_hours = usage_summary.get('window_hours', 0)
|
|
743
795
|
window_bar = self._make_progress_bar(window_pct)
|
|
796
|
+
total_tokens = usage_summary.get('total_tokens', 0)
|
|
797
|
+
input_tokens = usage_summary.get('total_input_tokens', 0)
|
|
798
|
+
output_tokens = usage_summary.get('total_output_tokens', 0)
|
|
799
|
+
|
|
800
|
+
# Format token counts
|
|
801
|
+
def fmt_tokens(n):
|
|
802
|
+
if n >= 1_000_000:
|
|
803
|
+
return f"{n/1_000_000:.1f}M"
|
|
804
|
+
elif n >= 1_000:
|
|
805
|
+
return f"{n/1_000:.1f}K"
|
|
806
|
+
return str(n)
|
|
744
807
|
|
|
745
808
|
await self._notify(
|
|
746
809
|
"complete",
|
|
@@ -749,6 +812,7 @@ Remember to include STATUS and SUMMARY lines at the end."""
|
|
|
749
812
|
f"🔄 *Cycles:* {cycle}\n"
|
|
750
813
|
f"⏱ *Duration:* {time_str}\n"
|
|
751
814
|
f"📂 *Files created:* {file_count}\n"
|
|
815
|
+
f"🔤 *Tokens:* {fmt_tokens(total_tokens)} ({fmt_tokens(input_tokens)} in / {fmt_tokens(output_tokens)} out)\n"
|
|
752
816
|
f"⏱ *5hr window:* {window_bar} {window_pct:.0f}% ({window_hours:.1f}h used)\n"
|
|
753
817
|
f"💰 *Est. cost:* ${usage_summary['total_cost_usd']:.2f}\n\n"
|
|
754
818
|
f"*Files:*\n" + "\n".join(f"• `{f}`" for f in file_list[:10]),
|
nullabot/bot/telegram.py
CHANGED
|
@@ -1097,6 +1097,14 @@ class TelegramBot:
|
|
|
1097
1097
|
|
|
1098
1098
|
base_dir = self.base_projects_dir.parent
|
|
1099
1099
|
|
|
1100
|
+
# Format token counts
|
|
1101
|
+
def fmt_tokens(n):
|
|
1102
|
+
if n >= 1_000_000:
|
|
1103
|
+
return f"{n/1_000_000:.1f}M"
|
|
1104
|
+
elif n >= 1_000:
|
|
1105
|
+
return f"{n/1_000:.1f}K"
|
|
1106
|
+
return str(n)
|
|
1107
|
+
|
|
1100
1108
|
lines = [
|
|
1101
1109
|
"📈 *Nullabot Usage*\n",
|
|
1102
1110
|
]
|
|
@@ -1105,6 +1113,7 @@ class TelegramBot:
|
|
|
1105
1113
|
total_cost = 0
|
|
1106
1114
|
total_cycles = 0
|
|
1107
1115
|
total_hours = 0
|
|
1116
|
+
total_tokens = 0
|
|
1108
1117
|
|
|
1109
1118
|
for item in self.base_projects_dir.iterdir():
|
|
1110
1119
|
if item.is_dir() and (item / ".nullabot").exists():
|
|
@@ -1112,16 +1121,18 @@ class TelegramBot:
|
|
|
1112
1121
|
tracker = UsageTracker(item, base_dir)
|
|
1113
1122
|
summary = tracker.get_summary()
|
|
1114
1123
|
if summary["total_cycles"] > 0:
|
|
1124
|
+
tokens = summary.get("total_tokens", 0)
|
|
1115
1125
|
lines.append(f"📁 *{item.name}*")
|
|
1116
|
-
lines.append(f" {summary['total_cycles']} cycles · {
|
|
1126
|
+
lines.append(f" {summary['total_cycles']} cycles · {fmt_tokens(tokens)} tokens · ${summary['total_cost_usd']:.2f}")
|
|
1117
1127
|
total_cost += summary["total_cost_usd"]
|
|
1118
1128
|
total_cycles += summary["total_cycles"]
|
|
1119
1129
|
total_hours += summary["total_hours"]
|
|
1130
|
+
total_tokens += tokens
|
|
1120
1131
|
except:
|
|
1121
1132
|
pass
|
|
1122
1133
|
|
|
1123
1134
|
if total_cycles > 0:
|
|
1124
|
-
lines.append(f"\n💰 *Total:* {total_cycles} cycles · {
|
|
1135
|
+
lines.append(f"\n💰 *Total:* {total_cycles} cycles · {fmt_tokens(total_tokens)} tokens · ${total_cost:.2f}")
|
|
1125
1136
|
else:
|
|
1126
1137
|
lines.append("_No usage yet_")
|
|
1127
1138
|
|
|
@@ -1595,6 +1606,14 @@ class TelegramBot:
|
|
|
1595
1606
|
|
|
1596
1607
|
base_dir = self.base_projects_dir.parent
|
|
1597
1608
|
|
|
1609
|
+
# Format token counts
|
|
1610
|
+
def fmt_tokens(n):
|
|
1611
|
+
if n >= 1_000_000:
|
|
1612
|
+
return f"{n/1_000_000:.1f}M"
|
|
1613
|
+
elif n >= 1_000:
|
|
1614
|
+
return f"{n/1_000:.1f}K"
|
|
1615
|
+
return str(n)
|
|
1616
|
+
|
|
1598
1617
|
lines = [
|
|
1599
1618
|
"📈 *Nullabot Usage*\n",
|
|
1600
1619
|
]
|
|
@@ -1603,6 +1622,7 @@ class TelegramBot:
|
|
|
1603
1622
|
total_cost = 0
|
|
1604
1623
|
total_cycles = 0
|
|
1605
1624
|
total_hours = 0
|
|
1625
|
+
total_tokens = 0
|
|
1606
1626
|
|
|
1607
1627
|
for item in self.base_projects_dir.iterdir():
|
|
1608
1628
|
if item.is_dir() and (item / ".nullabot").exists():
|
|
@@ -1610,16 +1630,18 @@ class TelegramBot:
|
|
|
1610
1630
|
tracker = UsageTracker(item, base_dir)
|
|
1611
1631
|
summary = tracker.get_summary()
|
|
1612
1632
|
if summary["total_cycles"] > 0:
|
|
1633
|
+
tokens = summary.get("total_tokens", 0)
|
|
1613
1634
|
lines.append(f"📁 *{item.name}*")
|
|
1614
|
-
lines.append(f" {summary['total_cycles']} cycles · {
|
|
1635
|
+
lines.append(f" {summary['total_cycles']} cycles · {fmt_tokens(tokens)} tokens · ${summary['total_cost_usd']:.2f}")
|
|
1615
1636
|
total_cost += summary["total_cost_usd"]
|
|
1616
1637
|
total_cycles += summary["total_cycles"]
|
|
1617
1638
|
total_hours += summary["total_hours"]
|
|
1639
|
+
total_tokens += tokens
|
|
1618
1640
|
except:
|
|
1619
1641
|
pass
|
|
1620
1642
|
|
|
1621
1643
|
if total_cycles > 0:
|
|
1622
|
-
lines.append(f"\n💰 *Total:* {total_cycles} cycles · {
|
|
1644
|
+
lines.append(f"\n💰 *Total:* {total_cycles} cycles · {fmt_tokens(total_tokens)} tokens · ${total_cost:.2f}")
|
|
1623
1645
|
else:
|
|
1624
1646
|
lines.append("_No usage yet_")
|
|
1625
1647
|
|
nullabot/core/memory.py
CHANGED
|
@@ -776,33 +776,50 @@ class UsageTracker:
|
|
|
776
776
|
self,
|
|
777
777
|
model: str,
|
|
778
778
|
agent_type: str,
|
|
779
|
-
input_tokens: int = 0,
|
|
780
|
-
output_tokens: int = 0,
|
|
779
|
+
input_tokens: int = 0,
|
|
780
|
+
output_tokens: int = 0,
|
|
781
781
|
duration_seconds: float = 0,
|
|
782
782
|
) -> dict:
|
|
783
|
-
"""Record a cycle's usage based on actual duration."""
|
|
783
|
+
"""Record a cycle's usage based on actual duration and tokens."""
|
|
784
784
|
# Calculate cost based on time (more accurate for subscriptions)
|
|
785
785
|
hourly_rate = self.COST_PER_HOUR.get(model, self.COST_PER_HOUR["opus"])
|
|
786
786
|
cost = (duration_seconds / 3600) * hourly_rate
|
|
787
787
|
|
|
788
|
+
# Initialize token tracking if not present
|
|
789
|
+
if "total_input_tokens" not in self._usage:
|
|
790
|
+
self._usage["total_input_tokens"] = 0
|
|
791
|
+
self._usage["total_output_tokens"] = 0
|
|
792
|
+
|
|
788
793
|
# Update project totals
|
|
789
794
|
self._usage["total_cycles"] += 1
|
|
790
795
|
self._usage["total_duration_seconds"] += duration_seconds
|
|
791
796
|
self._usage["total_cost_usd"] += cost
|
|
797
|
+
self._usage["total_input_tokens"] += input_tokens
|
|
798
|
+
self._usage["total_output_tokens"] += output_tokens
|
|
792
799
|
|
|
793
800
|
# By model
|
|
794
801
|
if model not in self._usage["by_model"]:
|
|
795
|
-
self._usage["by_model"][model] = {
|
|
802
|
+
self._usage["by_model"][model] = {
|
|
803
|
+
"cycles": 0, "duration": 0.0, "cost": 0.0,
|
|
804
|
+
"input_tokens": 0, "output_tokens": 0
|
|
805
|
+
}
|
|
796
806
|
self._usage["by_model"][model]["cycles"] += 1
|
|
797
807
|
self._usage["by_model"][model]["duration"] += duration_seconds
|
|
798
808
|
self._usage["by_model"][model]["cost"] += cost
|
|
809
|
+
self._usage["by_model"][model]["input_tokens"] += input_tokens
|
|
810
|
+
self._usage["by_model"][model]["output_tokens"] += output_tokens
|
|
799
811
|
|
|
800
812
|
# By agent
|
|
801
813
|
if agent_type not in self._usage["by_agent"]:
|
|
802
|
-
self._usage["by_agent"][agent_type] = {
|
|
814
|
+
self._usage["by_agent"][agent_type] = {
|
|
815
|
+
"cycles": 0, "duration": 0.0, "cost": 0.0,
|
|
816
|
+
"input_tokens": 0, "output_tokens": 0
|
|
817
|
+
}
|
|
803
818
|
self._usage["by_agent"][agent_type]["cycles"] += 1
|
|
804
819
|
self._usage["by_agent"][agent_type]["duration"] += duration_seconds
|
|
805
820
|
self._usage["by_agent"][agent_type]["cost"] += cost
|
|
821
|
+
self._usage["by_agent"][agent_type]["input_tokens"] += input_tokens
|
|
822
|
+
self._usage["by_agent"][agent_type]["output_tokens"] += output_tokens
|
|
806
823
|
|
|
807
824
|
# Session log
|
|
808
825
|
self._usage["sessions"].append({
|
|
@@ -811,6 +828,8 @@ class UsageTracker:
|
|
|
811
828
|
"agent": agent_type,
|
|
812
829
|
"duration": round(duration_seconds, 1),
|
|
813
830
|
"cost": round(cost, 4),
|
|
831
|
+
"input_tokens": input_tokens,
|
|
832
|
+
"output_tokens": output_tokens,
|
|
814
833
|
})
|
|
815
834
|
# Keep last 500 sessions
|
|
816
835
|
self._usage["sessions"] = self._usage["sessions"][-500:]
|
|
@@ -820,12 +839,20 @@ class UsageTracker:
|
|
|
820
839
|
# Update GLOBAL 5-hour window (shared across all projects)
|
|
821
840
|
window_status = self._global_window.record_usage(duration_seconds)
|
|
822
841
|
|
|
842
|
+
total_tokens = self._usage["total_input_tokens"] + self._usage["total_output_tokens"]
|
|
843
|
+
|
|
823
844
|
return {
|
|
824
845
|
"cycle_cost": round(cost, 2),
|
|
825
846
|
"total_cost": round(self._usage["total_cost_usd"], 2),
|
|
826
847
|
"total_cycles": self._usage["total_cycles"],
|
|
827
848
|
"cycle_duration": round(duration_seconds, 1),
|
|
828
849
|
"total_duration": round(self._usage["total_duration_seconds"], 1),
|
|
850
|
+
"cycle_input_tokens": input_tokens,
|
|
851
|
+
"cycle_output_tokens": output_tokens,
|
|
852
|
+
"cycle_tokens": input_tokens + output_tokens,
|
|
853
|
+
"total_input_tokens": self._usage["total_input_tokens"],
|
|
854
|
+
"total_output_tokens": self._usage["total_output_tokens"],
|
|
855
|
+
"total_tokens": total_tokens,
|
|
829
856
|
"window_usage_pct": window_status["usage_pct"],
|
|
830
857
|
"window_duration": window_status["used_seconds"],
|
|
831
858
|
"window_hours": window_status["used_hours"],
|
|
@@ -839,10 +866,17 @@ class UsageTracker:
|
|
|
839
866
|
# Get global window status
|
|
840
867
|
window_status = self._global_window.get_status()
|
|
841
868
|
|
|
869
|
+
# Token totals (with backwards compatibility)
|
|
870
|
+
total_input = self._usage.get("total_input_tokens", 0)
|
|
871
|
+
total_output = self._usage.get("total_output_tokens", 0)
|
|
872
|
+
|
|
842
873
|
return {
|
|
843
874
|
"total_cycles": self._usage["total_cycles"],
|
|
844
875
|
"total_cost_usd": round(self._usage["total_cost_usd"], 2),
|
|
845
876
|
"total_hours": round(total_hours, 2),
|
|
877
|
+
"total_input_tokens": total_input,
|
|
878
|
+
"total_output_tokens": total_output,
|
|
879
|
+
"total_tokens": total_input + total_output,
|
|
846
880
|
"window_hours": window_status["used_hours"],
|
|
847
881
|
"window_usage_pct": window_status["usage_pct"],
|
|
848
882
|
"window_remaining_hours": window_status["remaining_hours"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nullabot
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: 24/7 AI agents that think, design, and code - controlled via Telegram
|
|
5
5
|
Project-URL: Homepage, https://github.com/ebokoo/nullabot
|
|
6
6
|
Project-URL: Repository, https://github.com/ebokoo/nullabot
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
nullabot/__init__.py,sha256=96923LhB3uU4TE33GdjyG8M2fRRcqeWvRWlqxt48OV4,58
|
|
2
2
|
nullabot/cli.py,sha256=zattwGYh4feT_sRjCxSOoG6WSToxc_F1Zp00T_zrAIU,25800
|
|
3
3
|
nullabot/agents/__init__.py,sha256=6mE-zjqFa99WfnE-khjDpCSKzaNlukKVP7tB5Q4o7Vk,133
|
|
4
|
-
nullabot/agents/claude_agent.py,sha256=
|
|
4
|
+
nullabot/agents/claude_agent.py,sha256=gCxu3jUAgJ2ZKyIkSXGRas9K8Oy-9h1ZxaCnWNefKY0,33507
|
|
5
5
|
nullabot/bot/__init__.py,sha256=9axCMJfCE09CxMGsxtx81QZcBEXfz6wruHbYEtQQAeY,102
|
|
6
|
-
nullabot/bot/telegram.py,sha256=
|
|
6
|
+
nullabot/bot/telegram.py,sha256=ttBv693dpkt327iCjdpvJEcEsAroaHZ1e8QWQZ--SJk,69104
|
|
7
7
|
nullabot/core/__init__.py,sha256=Yn9zEFXF_2-aXiiU_iftYuaXWrX1qC7ZaEd6i3sarIE,314
|
|
8
8
|
nullabot/core/claude_code.py,sha256=sIKINJzjolBBpd2B_XhM6Egwmxs8E_VvDV3AOcuJbkw,9306
|
|
9
|
-
nullabot/core/memory.py,sha256=
|
|
9
|
+
nullabot/core/memory.py,sha256=OVGCwa7z0NZtwtB0r-XttBQw66qw-z4vXFgnBA2zt1o,32252
|
|
10
10
|
nullabot/core/project.py,sha256=FZofBZzIhMQUCXxCI4jjJZjKRr5u4iJUbor70tvLh7Q,6222
|
|
11
11
|
nullabot/core/rate_limiter.py,sha256=41CF4cX_n5_PFc6Uk1vp6m4MERxXl25p1wyw2FVdgsM,16693
|
|
12
12
|
nullabot/core/reliability.py,sha256=nfMN29dCQqM_T_MSLe0z9Tk0N5CWYFtmNXVe9Lqmiyw,13985
|
|
13
13
|
nullabot/core/sandbox.py,sha256=fWgjhWglGzXsJ10qxiKwJEkQQv6tLVa-DsshO7qcQvQ,4562
|
|
14
14
|
nullabot/core/state.py,sha256=yeFF4r3h0nRd5LENiXE7SSqfWPuljGy7X0FgE_GuI5o,6307
|
|
15
|
-
nullabot-1.0.
|
|
16
|
-
nullabot-1.0.
|
|
17
|
-
nullabot-1.0.
|
|
18
|
-
nullabot-1.0.
|
|
19
|
-
nullabot-1.0.
|
|
15
|
+
nullabot-1.0.3.dist-info/METADATA,sha256=gfHS2303WEzHe8GeFJHZsIFmvrDYFGTF6dZ96R1mLC8,3360
|
|
16
|
+
nullabot-1.0.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
nullabot-1.0.3.dist-info/entry_points.txt,sha256=lHaTGUU4ku7Q6ZRZnlpp5LrHRyLxhaRB9HzXzsJwYY8,47
|
|
18
|
+
nullabot-1.0.3.dist-info/licenses/LICENSE,sha256=IppsK7DD3SKtJlMoquzr1-bGKs88x85956yPxCn8H-U,1063
|
|
19
|
+
nullabot-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|