@leejungkiin/awkit 1.7.1 → 1.7.4
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.
- package/bin/awk.js +576 -84
- package/core/CLAUDE.md +1 -1
- package/core/GEMINI.md +148 -167
- package/core/GEMINI.md.bak +149 -116
- package/core/skill-runtime-manifest.json +3 -0
- package/docs/Claude Fable 5.md +3826 -0
- package/docs/android_kotlin_system_instruction.md +210 -0
- package/docs/brainstorm_ponytail_integration.md +146 -0
- package/docs/brainstorm_smart_setup.md +113 -0
- package/docs/deep-research-report (1).md +293 -0
- package/docs/history/GEMINI.v1.md +135 -0
- package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
- package/docs/history/implementation_plan.v1.md +58 -0
- package/package.json +4 -1
- package/scripts/artifact-storage.js +130 -0
- package/scripts/automation-gate.js +35 -2
- package/scripts/claude-plan.js +76 -0
- package/scripts/dependency-manager.js +210 -0
- package/scripts/exec-rtk.js +11 -5
- package/scripts/i18n-helper.js +381 -0
- package/scripts/multi-model-pipeline.js +144 -0
- package/skill-packs/mobile-ios/pack.json +4 -2
- package/skill-packs/reverse-engineering/pack.json +1 -0
- package/skills/CATALOG.md +20 -0
- package/skills/GEMINI.md +9 -1
- package/skills/TRIGGER_INDEX.md +10 -0
- package/skills/ai-music/SKILL.md +275 -0
- package/skills/android-re-analyzer/SKILL.md +238 -0
- package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
- package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
- package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
- package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
- package/skills/android-re-analyzer/references/setup-guide.md +221 -0
- package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
- package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
- package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
- package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
- package/skills/animal-island-ui-style/SKILL.md +1450 -0
- package/skills/app-store-review-agent/SKILL.md +164 -0
- package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
- package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
- package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
- package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
- package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
- package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
- package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
- package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
- package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
- package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
- package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
- package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
- package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
- package/skills/awf-ponytail/SKILL.md +91 -0
- package/skills/awf-ponytail-review/SKILL.md +67 -0
- package/skills/awf-session-restore/SKILL.md +3 -3
- package/skills/brainstorm-agent/SKILL.md +11 -2
- package/skills/brainstorm-agent/templates/brief-template.md +8 -0
- package/skills/claude-planner/SKILL.md +47 -0
- package/skills/code-review/SKILL.md +87 -0
- package/skills/expo-game-development/SKILL.md +163 -0
- package/skills/flutter/LICENSE.txt +202 -0
- package/skills/flutter/SKILL.md +127 -0
- package/skills/flutter-project-creater/LICENSE.txt +202 -0
- package/skills/flutter-project-creater/SKILL.md +106 -0
- package/skills/game-developer/SKILL.md +163 -0
- package/skills/game-developer/references/ecs-patterns.md +501 -0
- package/skills/game-developer/references/multiplayer-networking.md +475 -0
- package/skills/game-developer/references/performance-optimization.md +422 -0
- package/skills/game-developer/references/unity-patterns.md +271 -0
- package/skills/game-developer/references/unreal-cpp.md +352 -0
- package/skills/generate-gui-assets/SKILL.md +305 -0
- package/skills/generate-gui-assets/agents/openai.yaml +4 -0
- package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
- package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
- package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
- package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
- package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
- package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
- package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
- package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
- package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
- package/skills/godot-game-development/SKILL.md +142 -0
- package/skills/hatch-pet/LICENSE.txt +201 -0
- package/skills/hatch-pet/SKILL.md +420 -0
- package/skills/hatch-pet/agents/openai.yaml +4 -0
- package/skills/hatch-pet/references/animation-rows.md +29 -0
- package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
- package/skills/hatch-pet/references/qa-rubric.md +60 -0
- package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
- package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
- package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
- package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
- package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
- package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
- package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
- package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
- package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
- package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
- package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
- package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
- package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
- package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
- package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
- package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
- package/skills/i18n-orchestrator/SKILL.md +37 -0
- package/skills/ios-simulator-skill/SKILL.md +390 -0
- package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
- package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
- package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
- package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
- package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
- package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
- package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
- package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
- package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
- package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
- package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
- package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
- package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
- package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
- package/skills/ios-simulator-skill/scripts/container.py +668 -0
- package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
- package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
- package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
- package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
- package/skills/ios-simulator-skill/scripts/location.py +467 -0
- package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
- package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
- package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
- package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
- package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
- package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
- package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
- package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
- package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
- package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
- package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
- package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
- package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
- package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
- package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
- package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
- package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
- package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
- package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
- package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
- package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
- package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
- package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
- package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
- package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
- package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
- package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
- package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
- package/skills/orchestrator/SKILL.md +0 -20
- package/skills/persistent-storage/SKILL.md +55 -0
- package/skills/short-maker/SKILL.md +23 -0
- package/skills/short-maker/scripts/effects.js +56 -0
- package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
- package/skills/short-maker/scripts/videomix.js +601 -0
- package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
- package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
- package/skills/smali-to-kotlin/SKILL.md +128 -0
- package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
- package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
- package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
- package/skills/smali-to-kotlin/library-patterns.md +189 -0
- package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
- package/skills/smali-to-kotlin/phase-3-build.md +248 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
- package/skills/smali-to-kotlin/templates/app-map.md +101 -0
- package/skills/smali-to-kotlin/templates/architecture.md +142 -0
- package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
- package/skills/spec-gate/SKILL.md +6 -2
- package/skills/symphony-enforcer/SKILL.md +8 -0
- package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
- package/skills/symphony-enforcer/examples/three-phase.md +16 -0
- package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
- package/skills/unity-game-development/SKILL.md +231 -0
- package/skills/video-edit/SKILL.md +36 -0
- package/skills/video-edit/scripts/video_edit.py +324 -0
- package/templates/project-identity/android.json +2 -2
- package/templates/project-identity/backend-nestjs.json +2 -2
- package/templates/project-identity/expo.json +2 -2
- package/templates/project-identity/ios.json +2 -2
- package/templates/project-identity/web-nextjs.json +2 -2
- package/templates/setup-mapping.json +48 -0
- package/templates/specs/design-template.md +161 -71
- package/templates/specs/requirements-template.md +65 -133
- package/templates/specs/task-spec-template.xml +3 -0
- package/workflows/_uncategorized/critic.md +40 -0
- package/workflows/_uncategorized/git-rebase-flow.md +81 -0
- package/workflows/_uncategorized/image-gen.md +118 -0
- package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
- package/workflows/_uncategorized/pixel-gen.md +86 -0
- package/workflows/_uncategorized/pixel-setup.md +90 -0
- package/workflows/_uncategorized/ponytail-review.md +59 -0
- package/workflows/_uncategorized/reverse-android-build.md +222 -0
- package/workflows/_uncategorized/reverse-android-design.md +139 -0
- package/workflows/_uncategorized/reverse-android-discover.md +150 -0
- package/workflows/_uncategorized/reverse-android-scan.md +158 -0
- package/workflows/_uncategorized/reverse-android.md +143 -0
- package/workflows/_uncategorized/reverse-ios-build.md +240 -0
- package/workflows/_uncategorized/reverse-ios-design.md +112 -0
- package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
- package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
- package/workflows/_uncategorized/reverse-ios.md +152 -0
- package/workflows/_uncategorized/safety-router.md +34 -0
- package/workflows/_uncategorized/teach.md +89 -0
- package/workflows/_uncategorized/verify-ui.md +53 -0
- package/workflows/_uncategorized/visualize-screenshots.md +34 -0
- package/workflows/ads/ads-analyst.md +201 -0
- package/workflows/ads/ads-audit.md +106 -0
- package/workflows/ads/ads-optimize.md +97 -0
- package/workflows/ads/ads-targeting.md +241 -0
- package/workflows/ads/adsExpert.md +160 -0
- package/workflows/ads/smali-ads-config.md +400 -0
- package/workflows/ads/smali-ads-flow.md +331 -0
- package/workflows/ads/smali-ads-interstitial.md +377 -0
- package/workflows/ads/smali-ads-native.md +382 -0
- package/workflows/context/teach.md +89 -0
- package/workflows/gitnexus.md +8 -8
- package/workflows/lifecycle/brainstorm.md +43 -0
- package/workflows/lifecycle/code.md +5 -0
- package/workflows/lifecycle/init.md +23 -5
- package/workflows/lifecycle/multi-model-pipeline.md +60 -0
- package/workflows/quality/ponytail-review.md +59 -0
- package/workflows/roles/critic.md +40 -0
- package/workflows/roles/safety-router.md +34 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Progressive disclosure cache for large outputs.
|
|
4
|
+
|
|
5
|
+
Implements cache system to support progressive disclosure pattern:
|
|
6
|
+
- Return concise summary with cache_id for large outputs
|
|
7
|
+
- User retrieves full details on demand via cache_id
|
|
8
|
+
- Reduces token usage by 96% for common queries
|
|
9
|
+
|
|
10
|
+
Cache directory: ~/.ios-simulator-skill/cache/
|
|
11
|
+
Cache expiration: Configurable per cache type (default 1 hour)
|
|
12
|
+
|
|
13
|
+
Used by:
|
|
14
|
+
- sim_list.py - Simulator listing progressive disclosure
|
|
15
|
+
- Future: build logs, UI trees, etc.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import contextlib
|
|
19
|
+
import json
|
|
20
|
+
import time
|
|
21
|
+
from datetime import datetime, timedelta
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from common.env_config import env_int
|
|
26
|
+
|
|
27
|
+
DEFAULT_MAX_AGE_HOURS = env_int("IOS_SIM_CACHE_TTL_HOURS", 1)
|
|
28
|
+
DEFAULT_MAX_ENTRIES = env_int("IOS_SIM_CACHE_MAX_ENTRIES", 500)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ProgressiveCache:
|
|
32
|
+
"""Cache for progressive disclosure pattern.
|
|
33
|
+
|
|
34
|
+
Stores large outputs with timestamped IDs for on-demand retrieval.
|
|
35
|
+
Automatically cleans up expired entries.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
cache_dir: str | None = None,
|
|
41
|
+
max_age_hours: int | None = None,
|
|
42
|
+
max_entries: int | None = None,
|
|
43
|
+
):
|
|
44
|
+
"""Initialize cache system.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
cache_dir: Cache directory path (default: ~/.ios-simulator-skill/cache/)
|
|
48
|
+
max_age_hours: Max age for cache entries before expiration. Defaults to
|
|
49
|
+
``IOS_SIM_CACHE_TTL_HOURS`` env var, or 1 hour.
|
|
50
|
+
max_entries: Maximum entries retained; oldest are evicted (LRU by mtime).
|
|
51
|
+
Defaults to ``IOS_SIM_CACHE_MAX_ENTRIES`` env var, or 500.
|
|
52
|
+
"""
|
|
53
|
+
if cache_dir is None:
|
|
54
|
+
cache_dir = str(Path("~/.ios-simulator-skill/cache").expanduser())
|
|
55
|
+
|
|
56
|
+
self.cache_dir = Path(cache_dir)
|
|
57
|
+
self.max_age_hours = max_age_hours if max_age_hours is not None else DEFAULT_MAX_AGE_HOURS
|
|
58
|
+
self.max_entries = max_entries if max_entries is not None else DEFAULT_MAX_ENTRIES
|
|
59
|
+
|
|
60
|
+
# Create cache directory if needed
|
|
61
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
|
|
63
|
+
def _evict_overflow(self) -> int:
|
|
64
|
+
"""Evict oldest entries when count exceeds ``max_entries``. Returns count evicted."""
|
|
65
|
+
files = list(self.cache_dir.glob("*.json"))
|
|
66
|
+
if len(files) <= self.max_entries:
|
|
67
|
+
return 0
|
|
68
|
+
# Sort oldest-first by mtime; delete the head of the overflow
|
|
69
|
+
files.sort(key=lambda p: p.stat().st_mtime)
|
|
70
|
+
to_remove = len(files) - self.max_entries
|
|
71
|
+
for path in files[:to_remove]:
|
|
72
|
+
with contextlib.suppress(OSError):
|
|
73
|
+
path.unlink()
|
|
74
|
+
return to_remove
|
|
75
|
+
|
|
76
|
+
def save(self, data: dict[str, Any], cache_type: str) -> str:
|
|
77
|
+
"""Save data to cache and return cache_id.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
data: Dictionary data to cache
|
|
81
|
+
cache_type: Type of cache ('simulator-list', 'build-log', 'ui-tree', etc.)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Cache ID like 'sim-20251028-143052' for use in progressive disclosure
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
cache_id = cache.save({'devices': [...]}, 'simulator-list')
|
|
88
|
+
# Returns: 'sim-20251028-143052'
|
|
89
|
+
"""
|
|
90
|
+
# Generate cache_id with timestamp
|
|
91
|
+
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
92
|
+
cache_prefix = cache_type.split("-", maxsplit=1)[0] # e.g., 'sim' from 'simulator-list'
|
|
93
|
+
cache_id = f"{cache_prefix}-{timestamp}"
|
|
94
|
+
|
|
95
|
+
# Save to file
|
|
96
|
+
cache_file = self.cache_dir / f"{cache_id}.json"
|
|
97
|
+
with open(cache_file, "w") as f:
|
|
98
|
+
json.dump(
|
|
99
|
+
{
|
|
100
|
+
"cache_id": cache_id,
|
|
101
|
+
"cache_type": cache_type,
|
|
102
|
+
"created_at": datetime.now().isoformat(),
|
|
103
|
+
"data": data,
|
|
104
|
+
},
|
|
105
|
+
f,
|
|
106
|
+
indent=2,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
self._evict_overflow()
|
|
110
|
+
return cache_id
|
|
111
|
+
|
|
112
|
+
def get(self, cache_id: str) -> dict[str, Any] | None:
|
|
113
|
+
"""Retrieve data from cache by cache_id.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
cache_id: Cache ID from save() or list_entries()
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Cached data dictionary, or None if not found/expired
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
data = cache.get('sim-20251028-143052')
|
|
123
|
+
if data:
|
|
124
|
+
print(f"Found {len(data)} devices")
|
|
125
|
+
"""
|
|
126
|
+
cache_file = self.cache_dir / f"{cache_id}.json"
|
|
127
|
+
|
|
128
|
+
if not cache_file.exists():
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
# Check if expired
|
|
132
|
+
if self._is_expired(cache_file):
|
|
133
|
+
cache_file.unlink() # Delete expired file
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
with open(cache_file) as f:
|
|
138
|
+
entry = json.load(f)
|
|
139
|
+
return entry.get("data")
|
|
140
|
+
except (OSError, json.JSONDecodeError):
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def list_entries(self, cache_type: str | None = None) -> list[dict[str, Any]]:
|
|
144
|
+
"""List available cache entries with metadata.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
cache_type: Filter by type (e.g., 'simulator-list'), or None for all
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
List of cache entries with id, type, created_at, age_seconds
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
entries = cache.list_entries('simulator-list')
|
|
154
|
+
for entry in entries:
|
|
155
|
+
print(f"{entry['id']} - {entry['age_seconds']}s old")
|
|
156
|
+
"""
|
|
157
|
+
entries = []
|
|
158
|
+
|
|
159
|
+
for cache_file in sorted(self.cache_dir.glob("*.json"), reverse=True):
|
|
160
|
+
# Check if expired
|
|
161
|
+
if self._is_expired(cache_file):
|
|
162
|
+
cache_file.unlink()
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
with open(cache_file) as f:
|
|
167
|
+
entry = json.load(f)
|
|
168
|
+
|
|
169
|
+
# Filter by type if specified
|
|
170
|
+
if cache_type and entry.get("cache_type") != cache_type:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
created_at = datetime.fromisoformat(entry.get("created_at", ""))
|
|
174
|
+
age_seconds = (datetime.now() - created_at).total_seconds()
|
|
175
|
+
|
|
176
|
+
entries.append(
|
|
177
|
+
{
|
|
178
|
+
"id": entry.get("cache_id"),
|
|
179
|
+
"type": entry.get("cache_type"),
|
|
180
|
+
"created_at": entry.get("created_at"),
|
|
181
|
+
"age_seconds": int(age_seconds),
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
except (OSError, json.JSONDecodeError, ValueError):
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
return entries
|
|
188
|
+
|
|
189
|
+
def cleanup(self, max_age_hours: int | None = None) -> int:
|
|
190
|
+
"""Remove expired cache entries.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
max_age_hours: Age threshold (default: uses instance max_age_hours)
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Number of entries deleted
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
deleted = cache.cleanup()
|
|
200
|
+
print(f"Deleted {deleted} expired cache entries")
|
|
201
|
+
"""
|
|
202
|
+
if max_age_hours is None:
|
|
203
|
+
max_age_hours = self.max_age_hours
|
|
204
|
+
|
|
205
|
+
deleted = 0
|
|
206
|
+
|
|
207
|
+
for cache_file in self.cache_dir.glob("*.json"):
|
|
208
|
+
if self._is_expired(cache_file, max_age_hours):
|
|
209
|
+
cache_file.unlink()
|
|
210
|
+
deleted += 1
|
|
211
|
+
|
|
212
|
+
return deleted
|
|
213
|
+
|
|
214
|
+
def clear(self, cache_type: str | None = None) -> int:
|
|
215
|
+
"""Clear all cache entries of a type.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
cache_type: Type to clear (e.g., 'simulator-list'), or None to clear all
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Number of entries deleted
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
cleared = cache.clear('simulator-list')
|
|
225
|
+
print(f"Cleared {cleared} simulator list entries")
|
|
226
|
+
"""
|
|
227
|
+
deleted = 0
|
|
228
|
+
|
|
229
|
+
for cache_file in self.cache_dir.glob("*.json"):
|
|
230
|
+
if cache_type is None:
|
|
231
|
+
# Clear all
|
|
232
|
+
cache_file.unlink()
|
|
233
|
+
deleted += 1
|
|
234
|
+
else:
|
|
235
|
+
# Clear by type
|
|
236
|
+
try:
|
|
237
|
+
with open(cache_file) as f:
|
|
238
|
+
entry = json.load(f)
|
|
239
|
+
if entry.get("cache_type") == cache_type:
|
|
240
|
+
cache_file.unlink()
|
|
241
|
+
deleted += 1
|
|
242
|
+
except (OSError, json.JSONDecodeError):
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
return deleted
|
|
246
|
+
|
|
247
|
+
def _is_expired(self, cache_file: Path, max_age_hours: int | None = None) -> bool:
|
|
248
|
+
"""Check if cache file is expired.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
cache_file: Path to cache file
|
|
252
|
+
max_age_hours: Age threshold (default: uses instance max_age_hours)
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
True if file is older than max_age_hours
|
|
256
|
+
"""
|
|
257
|
+
if max_age_hours is None:
|
|
258
|
+
max_age_hours = self.max_age_hours
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
with open(cache_file) as f:
|
|
262
|
+
entry = json.load(f)
|
|
263
|
+
created_at = datetime.fromisoformat(entry.get("created_at", ""))
|
|
264
|
+
age = datetime.now() - created_at
|
|
265
|
+
return age > timedelta(hours=max_age_hours)
|
|
266
|
+
except (OSError, json.JSONDecodeError, ValueError):
|
|
267
|
+
return True
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# Module-level cache instances (lazy-loaded)
|
|
271
|
+
_cache_instances: dict[str, ProgressiveCache] = {}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_cache(cache_dir: str | None = None) -> ProgressiveCache:
|
|
275
|
+
"""Get or create global cache instance.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
cache_dir: Custom cache directory (uses default if None)
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
ProgressiveCache instance
|
|
282
|
+
"""
|
|
283
|
+
# Use cache_dir as key, or 'default' if None
|
|
284
|
+
key = cache_dir or "default"
|
|
285
|
+
|
|
286
|
+
if key not in _cache_instances:
|
|
287
|
+
_cache_instances[key] = ProgressiveCache(cache_dir)
|
|
288
|
+
|
|
289
|
+
return _cache_instances[key]
|