(
skill_name,
version,
zip_bytes,
checksum,
agents,
stream=None,
action='Installed',
overwrite_existing=False,
)
| 136 | |
| 137 | |
| 138 | def install_skill( |
| 139 | skill_name, |
| 140 | version, |
| 141 | zip_bytes, |
| 142 | checksum, |
| 143 | agents, |
| 144 | stream=None, |
| 145 | action='Installed', |
| 146 | overwrite_existing=False, |
| 147 | ): |
| 148 | expected = checksum.strip().lower().split()[0] |
| 149 | actual = hashlib.sha256(zip_bytes).hexdigest() |
| 150 | if len(expected) != 64 or actual != expected: |
| 151 | raise AgentToolkitServiceError( |
| 152 | f'Checksum verification failed for {skill_name} ' |
| 153 | f'(expected {expected!r}, got {actual!r}).' |
| 154 | ) |
| 155 | |
| 156 | with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf: |
| 157 | total_size = sum(i.file_size for i in zf.infolist()) |
| 158 | if total_size > MAX_UNCOMPRESSED_SIZE: |
| 159 | raise AgentToolkitServiceError( |
| 160 | f'Refusing to extract: uncompressed size ' |
| 161 | f'({total_size} bytes) exceeds limit.' |
| 162 | ) |
| 163 | for member in zf.namelist(): |
| 164 | normalized = os.path.normpath(member) |
| 165 | if normalized.startswith('..') or os.path.isabs(normalized): |
| 166 | raise AgentToolkitServiceError( |
| 167 | f'Refusing to extract {member!r}' |
| 168 | ' (path escapes skill directory).' |
| 169 | ) |
| 170 | |
| 171 | extracted_paths = set() |
| 172 | for agent in universal_first(agents): |
| 173 | skill_dir = os.path.join(agent.skills_path, skill_name) |
| 174 | real_dir = os.path.realpath(skill_dir) |
| 175 | if real_dir in extracted_paths: |
| 176 | continue |
| 177 | extracted_paths.add(real_dir) |
| 178 | if os.path.isdir(real_dir): |
| 179 | marker = os.path.join(real_dir, SKILL_METADATA_FILENAME) |
| 180 | if not os.path.exists(marker): |
| 181 | if stream: |
| 182 | stream.write( |
| 183 | f' Skipped {skill_name} at {real_dir}: ' |
| 184 | f'directory was not installed by the AWS CLI.\n' |
| 185 | ) |
| 186 | continue |
| 187 | # Marker is present, but if SKILL.md is missing the |
| 188 | # previous install is corrupted (e.g. user deleted files |
| 189 | # by hand). |
| 190 | skill_file = os.path.join(real_dir, SKILL_FILENAME) |
| 191 | is_corrupted = not os.path.exists(skill_file) |
| 192 | if not overwrite_existing and not is_corrupted: |
| 193 | if stream: |
| 194 | installed = read_installed_version(real_dir) |
| 195 | version_note = f' ({installed})' if installed else '' |
no test coverage detected