finalize doc-translator skill
This commit is contained in:
10
AGENTS.md
10
AGENTS.md
@@ -1,5 +1,15 @@
|
|||||||
# Opencode Skills Repository
|
# Opencode Skills Repository
|
||||||
|
|
||||||
|
## MANDATORY: Use td for Task Management
|
||||||
|
|
||||||
|
Run td usage --new-session at conversation start (or after /clear). This tells you what to work on next.
|
||||||
|
|
||||||
|
Sessions are automatic (based on terminal/agent context). Optional:
|
||||||
|
- td session "name" to label the current session
|
||||||
|
- td session --new to force a new session in the same context
|
||||||
|
|
||||||
|
Use td usage -q after first read.
|
||||||
|
|
||||||
Configuration repository for Opencode Agent Skills, context files, and agent configurations. Deployed via Nix home-manager to `~/.config/opencode/`.
|
Configuration repository for Opencode Agent Skills, context files, and agent configurations. Deployed via Nix home-manager to `~/.config/opencode/`.
|
||||||
|
|
||||||
## Quick Commands
|
## Quick Commands
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# ./scripts/test-skill.sh --run # Launch interactive opencode session
|
# ./scripts/test-skill.sh --run # Launch interactive opencode session
|
||||||
#
|
#
|
||||||
# This script creates a temporary XDG_CONFIG_HOME with symlinks to this
|
# This script creates a temporary XDG_CONFIG_HOME with symlinks to this
|
||||||
# repository's skill/, context/, command/, and prompts/ directories,
|
# repository's skills/, context/, command/, and prompts/ directories,
|
||||||
# allowing you to test skill changes before deploying via home-manager.
|
# allowing you to test skill changes before deploying via home-manager.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -72,17 +72,17 @@ list_skills() {
|
|||||||
|
|
||||||
validate_skill() {
|
validate_skill() {
|
||||||
local skill_name="$1"
|
local skill_name="$1"
|
||||||
local skill_path="$REPO_ROOT/skill/$skill_name"
|
local skill_path="$REPO_ROOT/skills/$skill_name"
|
||||||
|
|
||||||
if [[ ! -d "$skill_path" ]]; then
|
if [[ ! -d "$skill_path" ]]; then
|
||||||
echo -e "${RED}❌ Skill not found: $skill_name${NC}"
|
echo -e "${RED}❌ Skill not found: $skill_name${NC}"
|
||||||
echo "Available skills:"
|
echo "Available skills:"
|
||||||
ls -1 "$REPO_ROOT/skill/"
|
ls -1 "$REPO_ROOT/skills/"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${YELLOW}Validating skill: $skill_name${NC}"
|
echo -e "${YELLOW}Validating skill: $skill_name${NC}"
|
||||||
if python3 "$REPO_ROOT/skill/skill-creator/scripts/quick_validate.py" "$skill_path"; then
|
if python3 "$REPO_ROOT/skills/skill-creator/scripts/quick_validate.py" "$skill_path"; then
|
||||||
echo -e "${GREEN}✅ Skill '$skill_name' is valid${NC}"
|
echo -e "${GREEN}✅ Skill '$skill_name' is valid${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❌ Skill '$skill_name' has validation errors${NC}"
|
echo -e "${RED}❌ Skill '$skill_name' has validation errors${NC}"
|
||||||
@@ -95,14 +95,14 @@ validate_all() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
local failed=0
|
local failed=0
|
||||||
for skill_dir in "$REPO_ROOT/skill/"*/; do
|
for skill_dir in "$REPO_ROOT/skills/"*/; do
|
||||||
local skill_name=$(basename "$skill_dir")
|
local skill_name=$(basename "$skill_dir")
|
||||||
echo -n " $skill_name: "
|
echo -n " $skill_name: "
|
||||||
if python3 "$REPO_ROOT/skill/skill-creator/scripts/quick_validate.py" "$skill_dir" > /dev/null 2>&1; then
|
if python3 "$REPO_ROOT/skills/skill-creator/scripts/quick_validate.py" "$skill_dir" > /dev/null 2>&1; then
|
||||||
echo -e "${GREEN}✅${NC}"
|
echo -e "${GREEN}✅${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❌${NC}"
|
echo -e "${RED}❌${NC}"
|
||||||
python3 "$REPO_ROOT/skill/skill-creator/scripts/quick_validate.py" "$skill_dir" 2>&1 | sed 's/^/ /'
|
python3 "$REPO_ROOT/skills/skill-creator/scripts/quick_validate.py" "$skill_dir" 2>&1 | sed 's/^/ /'
|
||||||
((failed++)) || true
|
((failed++)) || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -72,76 +72,72 @@ If an image download fails, log it and continue. Use a placeholder in the final
|
|||||||
|
|
||||||
### 4. Upload Images to Outline
|
### 4. Upload Images to Outline
|
||||||
|
|
||||||
**IMPORTANT:** Always use Outline MCP tools for all Outline operations. If Outline tools throw errors:
|
MCP-outline does not support attachment creation. Use the bundled script for image uploads:
|
||||||
1. Load the outline skill first: `skill name=outline`
|
|
||||||
2. Retry with `skill_mcp` tool for outline operations
|
|
||||||
3. Only fallback to direct API calls via `bash` after exhausting MCP options
|
|
||||||
|
|
||||||
mcp-outline cannot create attachments. Use direct API calls via `bash` for image uploads only.
|
|
||||||
|
|
||||||
**Required env:** `OUTLINE_API_KEY` (read from /run/agenix/outline-key)
|
|
||||||
|
|
||||||
For each downloaded image:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/usr/bin/env bash
|
# Upload with optional document association
|
||||||
set -euo pipefail
|
bash scripts/upload_image_to_outline.sh "/tmp/doc-images/screenshot.png" "$DOCUMENT_ID"
|
||||||
|
|
||||||
IMAGE_PATH="/tmp/doc-images/screenshot.png"
|
# Upload without document (attach later)
|
||||||
IMAGE_NAME="$(basename "$IMAGE_PATH")"
|
bash scripts/upload_image_to_outline.sh "/tmp/doc-images/screenshot.png"
|
||||||
CONTENT_TYPE="image/png" # Detect from extension: png->image/png, jpg/jpeg->image/jpeg, gif->image/gif, svg->image/svg+xml, webp->image/webp
|
|
||||||
|
|
||||||
# 1. Get file size (cross-platform)
|
|
||||||
FILESIZE=$(stat -f%z "$IMAGE_PATH" 2>/dev/null || stat -c%s "$IMAGE_PATH")
|
|
||||||
|
|
||||||
# 2. Create attachment record
|
|
||||||
RESPONSE=$(curl -s -X POST "https://wiki.az-gruppe.com/api/attachments.create" \
|
|
||||||
-H "Authorization: Bearer $OUTLINE_API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{
|
|
||||||
\"name\": \"$IMAGE_NAME\",
|
|
||||||
\"contentType\": \"$CONTENT_TYPE\",
|
|
||||||
\"size\": $FILESIZE
|
|
||||||
}")
|
|
||||||
|
|
||||||
# 3. Extract URLs from response
|
|
||||||
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.data.uploadUrl')
|
|
||||||
ATTACHMENT_URL=$(echo "$RESPONSE" | jq -r '.data.attachment.url')
|
|
||||||
|
|
||||||
# 4. Check for errors
|
|
||||||
if [ "$UPLOAD_URL" = "null" ] || [ -z "$UPLOAD_URL" ]; then
|
|
||||||
echo "ERROR: Failed to create attachment. Response: $RESPONSE" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5. Upload binary to signed URL
|
|
||||||
curl -s -X PUT "$UPLOAD_URL" \
|
|
||||||
-H "Content-Type: $CONTENT_TYPE" \
|
|
||||||
--data-binary "@$IMAGE_PATH"
|
|
||||||
|
|
||||||
# 6. Output the attachment URL for use in markdown
|
|
||||||
echo "$ATTACHMENT_URL"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace image references in the translated markdown:
|
The script handles API key loading from `/run/agenix/outline-key`, content-type detection, the two-step presigned POST flow, and retries. Output is JSON: `{"success": true, "attachment_url": "https://..."}`.
|
||||||
|
|
||||||
|
Replace image references in the translated markdown with the returned `attachment_url`:
|
||||||
```markdown
|
```markdown
|
||||||

|

|
||||||
```
|
```
|
||||||
|
|
||||||
**Content-Type detection by extension:**
|
For all other Outline operations (documents, collections, search), use MCP tools (`Outline_*`).
|
||||||
|
|
||||||
| Extension | Content-Type |
|
|
||||||
|-----------|-------------|
|
|
||||||
| `.png` | `image/png` |
|
|
||||||
| `.jpg`, `.jpeg` | `image/jpeg` |
|
|
||||||
| `.gif` | `image/gif` |
|
|
||||||
| `.svg` | `image/svg+xml` |
|
|
||||||
| `.webp` | `image/webp` |
|
|
||||||
|
|
||||||
### 5. Translate with TEEM Format
|
### 5. Translate with TEEM Format
|
||||||
|
|
||||||
Translate the entire document into each target language. Apply TEEM format to UI elements.
|
Translate the entire document into each target language. Apply TEEM format to UI elements.
|
||||||
|
|
||||||
|
#### Address Form (CRITICAL)
|
||||||
|
|
||||||
|
**Always use the informal "you" form** in ALL target languages:
|
||||||
|
- **German**: Use **"Du"** (informal), NEVER "Sie" (formal)
|
||||||
|
- **Czech**: Use **"ty"** (informal), NEVER "vy" (formal)
|
||||||
|
- This applies to all translations — documentation should feel approachable and direct
|
||||||
|
|
||||||
|
#### Infobox / Callout Formatting
|
||||||
|
|
||||||
|
Source documentation often uses admonitions, callouts, or info boxes (e.g., GitHub-style `> [!NOTE]`, Docusaurus `:::note`, or custom HTML boxes). **Convert ALL such elements** to Outline's callout syntax:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:::tip
|
||||||
|
Tip or best practice content here.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Informational content here.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
Warning or caution content here.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::success
|
||||||
|
Success message or positive outcome here.
|
||||||
|
|
||||||
|
:::
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mapping rules** (source → Outline):
|
||||||
|
| Source pattern | Outline syntax |
|
||||||
|
|---|---|
|
||||||
|
| Note, Info, Information | `:::info` |
|
||||||
|
| Tip, Hint, Best Practice | `:::tip` |
|
||||||
|
| Warning, Caution, Danger, Important | `:::warning` |
|
||||||
|
| Success, Done, Check | `:::success` |
|
||||||
|
|
||||||
|
**CRITICAL formatting**: The closing `:::` MUST be on its own line with an empty line before it. Content goes directly after the opening line.
|
||||||
|
|
||||||
#### TEEM Rules
|
#### TEEM Rules
|
||||||
|
|
||||||
**Format:** `**English UI Term** (Translation)`
|
**Format:** `**English UI Term** (Translation)`
|
||||||
@@ -217,7 +213,7 @@ Use mcp-outline tools to publish:
|
|||||||
|-------|--------|
|
|-------|--------|
|
||||||
| URL fetch fails | Use `question` to ask for alternative URL or manual paste |
|
| URL fetch fails | Use `question` to ask for alternative URL or manual paste |
|
||||||
| Image download fails | Continue with placeholder, note in completion report |
|
| Image download fails | Continue with placeholder, note in completion report |
|
||||||
| Outline API error (attachments) | Save markdown to `/tmp/doc-translator-backup-TIMESTAMP.md`, report error |
|
| Outline API error (attachments) | Script retries 3x with backoff; on final failure save markdown to `/tmp/doc-translator-backup-TIMESTAMP.md`, report error |
|
||||||
| Outline API error (document) | Save markdown to `/tmp/doc-translator-backup-TIMESTAMP.md`, report error |
|
| Outline API error (document) | Save markdown to `/tmp/doc-translator-backup-TIMESTAMP.md`, report error |
|
||||||
| Ambiguous UI term | Use `question` to ask user for correct translation |
|
| Ambiguous UI term | Use `question` to ask user for correct translation |
|
||||||
| Large document (>5000 words) | Ask user if splitting into multiple docs is preferred |
|
| Large document (>5000 words) | Ask user if splitting into multiple docs is preferred |
|
||||||
@@ -254,9 +250,9 @@ Items Needing Review:
|
|||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Purpose |
|
| Variable | Purpose | Source |
|
||||||
|----------|---------|
|
|----------|---------|--------|
|
||||||
| `OUTLINE_API_KEY` | Bearer token for wiki.az-gruppe.com API |
|
| `OUTLINE_API_KEY` | Bearer token for wiki.az-gruppe.com API | Auto-loaded from `/run/agenix/outline-key` by upload script |
|
||||||
|
|
||||||
## Integration with Other Skills
|
## Integration with Other Skills
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# List all collections available in Outline
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# list_outline_collections.sh
|
|
||||||
#
|
|
||||||
# Environment:
|
|
||||||
# OUTLINE_API_KEY - Bearer token for wiki.az-gruppe.com API
|
|
||||||
#
|
|
||||||
# Output (JSON):
|
|
||||||
# [{"id": "...", "name": "...", "url": "..."}, ...]
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# List collections
|
|
||||||
RESPONSE=$(curl -s -X POST "https://wiki.az-gruppe.com/api/collections.list" \
|
|
||||||
-H "Authorization: Bearer $OUTLINE_API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{}')
|
|
||||||
|
|
||||||
# Check for errors
|
|
||||||
if echo "$RESPONSE" | jq -e '.error' > /dev/null 2>&1; then
|
|
||||||
ERROR_MSG=$(echo "$RESPONSE" | jq -r '.message // "Failed to list collections"')
|
|
||||||
echo "{\"success\": false, \"error\": \"$ERROR_MSG\", \"response\": \"$RESPONSE\"}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract collection list
|
|
||||||
COLLECTIONS=$(echo "$RESPONSE" | jq -r '.data[] | {id: .id, name: .name, url: .url}')
|
|
||||||
|
|
||||||
# Output as JSON array
|
|
||||||
echo "$COLLECTIONS" | jq -s '.'
|
|
||||||
@@ -1,26 +1,40 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Upload an image to an Outline document via signed URL
|
# Upload an image to Outline via presigned POST (two-step flow)
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# upload_image_to_outline.sh <image_path> <document_id>
|
# upload_image_to_outline.sh <image_path> [document_id]
|
||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# OUTLINE_API_KEY - Bearer token for wiki.az-gruppe.com API
|
# OUTLINE_API_KEY - Bearer token for wiki.az-gruppe.com API
|
||||||
|
# Auto-loaded from /run/agenix/outline-key if not set
|
||||||
#
|
#
|
||||||
# Output (JSON):
|
# Output (JSON to stdout):
|
||||||
# {"success": true, "attachment_url": "https://...", "document_id": "..."}
|
# {"success": true, "attachment_url": "https://..."}
|
||||||
# OR
|
# Error (JSON to stderr):
|
||||||
# {"success": false, "error": "error message"}
|
# {"success": false, "error": "error message"}
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ $# -ne 2 ]; then
|
MAX_RETRIES=3
|
||||||
echo '{"success": false, "error": "Usage: upload_image_to_outline.sh <image_path> <document_id>"}' >&2
|
RETRY_DELAY=2
|
||||||
|
|
||||||
|
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
|
||||||
|
echo '{"success": false, "error": "Usage: upload_image_to_outline.sh <image_path> [document_id]"}' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IMAGE_PATH="$1"
|
IMAGE_PATH="$1"
|
||||||
DOCUMENT_ID="$2"
|
DOCUMENT_ID="${2:-}"
|
||||||
|
|
||||||
|
if [ -z "${OUTLINE_API_KEY:-}" ]; then
|
||||||
|
if [ -f /run/agenix/outline-key ]; then
|
||||||
|
OUTLINE_API_KEY=$(cat /run/agenix/outline-key)
|
||||||
|
export OUTLINE_API_KEY
|
||||||
|
else
|
||||||
|
echo '{"success": false, "error": "OUTLINE_API_KEY not set and /run/agenix/outline-key not found"}' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if file exists
|
# Check if file exists
|
||||||
if [ ! -f "$IMAGE_PATH" ]; then
|
if [ ! -f "$IMAGE_PATH" ]; then
|
||||||
@@ -31,10 +45,9 @@ fi
|
|||||||
# Extract image name and extension
|
# Extract image name and extension
|
||||||
IMAGE_NAME="$(basename "$IMAGE_PATH")"
|
IMAGE_NAME="$(basename "$IMAGE_PATH")"
|
||||||
EXTENSION="${IMAGE_NAME##*.}"
|
EXTENSION="${IMAGE_NAME##*.}"
|
||||||
IMAGE_NAME_BASE="${IMAGE_NAME%.*}"
|
|
||||||
|
|
||||||
# Detect content type by extension
|
# Detect content type by extension
|
||||||
case "$EXTENSION" in
|
case "${EXTENSION,,}" in
|
||||||
png) CONTENT_TYPE="image/png" ;;
|
png) CONTENT_TYPE="image/png" ;;
|
||||||
jpg|jpeg) CONTENT_TYPE="image/jpeg" ;;
|
jpg|jpeg) CONTENT_TYPE="image/jpeg" ;;
|
||||||
gif) CONTENT_TYPE="image/gif" ;;
|
gif) CONTENT_TYPE="image/gif" ;;
|
||||||
@@ -43,48 +56,61 @@ case "$EXTENSION" in
|
|||||||
*) CONTENT_TYPE="application/octet-stream" ;;
|
*) CONTENT_TYPE="application/octet-stream" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Get file size (cross-platform: macOS uses stat -f%z, Linux uses stat -c%s)
|
FILESIZE=$(stat -c%s "$IMAGE_PATH" 2>/dev/null || stat -f%z "$IMAGE_PATH" 2>/dev/null)
|
||||||
FILESIZE=$(stat -f%z "$IMAGE_PATH" 2>/dev/null || stat -c%s "$IMAGE_PATH" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$FILESIZE" ]; then
|
if [ -z "$FILESIZE" ]; then
|
||||||
echo "{\"success\": false, \"error\": \"Failed to get file size for: $IMAGE_PATH\"}" >&2
|
echo "{\"success\": false, \"error\": \"Failed to get file size for: $IMAGE_PATH\"}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create attachment record
|
REQUEST_BODY=$(jq -n \
|
||||||
|
--arg name "$IMAGE_NAME" \
|
||||||
|
--arg contentType "$CONTENT_TYPE" \
|
||||||
|
--argjson size "$FILESIZE" \
|
||||||
|
--arg documentId "$DOCUMENT_ID" \
|
||||||
|
'if $documentId == "" then
|
||||||
|
{name: $name, contentType: $contentType, size: $size}
|
||||||
|
else
|
||||||
|
{name: $name, contentType: $contentType, size: $size, documentId: $documentId}
|
||||||
|
end')
|
||||||
|
|
||||||
|
# Step 1: Create attachment record
|
||||||
RESPONSE=$(curl -s -X POST "https://wiki.az-gruppe.com/api/attachments.create" \
|
RESPONSE=$(curl -s -X POST "https://wiki.az-gruppe.com/api/attachments.create" \
|
||||||
-H "Authorization: Bearer $OUTLINE_API_KEY" \
|
-H "Authorization: Bearer $OUTLINE_API_KEY" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{
|
-d "$REQUEST_BODY")
|
||||||
\"name\": \"$IMAGE_NAME\",
|
|
||||||
\"contentType\": \"$CONTENT_TYPE\",
|
|
||||||
\"size\": $FILESIZE,
|
|
||||||
\"documentId\": \"$DOCUMENT_ID\"
|
|
||||||
}")
|
|
||||||
|
|
||||||
# Extract URLs from response
|
|
||||||
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.data.uploadUrl // empty')
|
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.data.uploadUrl // empty')
|
||||||
ATTACHMENT_URL=$(echo "$RESPONSE" | jq -r '.data.attachment.url // empty')
|
ATTACHMENT_URL=$(echo "$RESPONSE" | jq -r '.data.attachment.url // empty')
|
||||||
|
|
||||||
# Check for errors
|
if [ -z "$UPLOAD_URL" ]; then
|
||||||
if [ -z "$UPLOAD_URL" ] || [ "$UPLOAD_URL" = "null" ]; then
|
|
||||||
ERROR_MSG=$(echo "$RESPONSE" | jq -r '.message // "Failed to create attachment"')
|
ERROR_MSG=$(echo "$RESPONSE" | jq -r '.message // "Failed to create attachment"')
|
||||||
echo "{\"success\": false, \"error\": \"$ERROR_MSG\", \"response\": \"$RESPONSE\"}" >&2
|
echo "{\"success\": false, \"error\": \"$ERROR_MSG\", \"response\": $(echo "$RESPONSE" | jq -c .)}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Upload binary to signed URL
|
FORM_ARGS=()
|
||||||
UPLOAD_RESPONSE=$(curl -s -w "%{http_code}" -X POST "$UPLOAD_URL" \
|
while IFS= read -r line; do
|
||||||
-H "Content-Type: $CONTENT_TYPE" \
|
key=$(echo "$line" | jq -r '.key')
|
||||||
--data-binary "@$IMAGE_PATH")
|
value=$(echo "$line" | jq -r '.value')
|
||||||
|
FORM_ARGS+=(-F "$key=$value")
|
||||||
|
done < <(echo "$RESPONSE" | jq -c '.data.form | to_entries[]')
|
||||||
|
|
||||||
# Extract HTTP status code (last 3 characters)
|
# Step 2: Upload binary to presigned URL with retry
|
||||||
HTTP_CODE="${UPLOAD_RESPONSE: -3}"
|
for attempt in $(seq 1 "$MAX_RETRIES"); do
|
||||||
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$UPLOAD_URL" \
|
||||||
|
"${FORM_ARGS[@]}" \
|
||||||
|
-F "file=@$IMAGE_PATH")
|
||||||
|
|
||||||
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
|
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "204" ]; then
|
||||||
echo "{\"success\": false, \"error\": \"Upload failed with HTTP $HTTP_CODE\", \"upload_url\": \"$UPLOAD_URL\"}" >&2
|
echo "{\"success\": true, \"attachment_url\": \"$ATTACHMENT_URL\"}"
|
||||||
exit 1
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Output success result
|
if [ "$attempt" -lt "$MAX_RETRIES" ]; then
|
||||||
echo "{\"success\": true, \"attachment_url\": \"$ATTACHMENT_URL\", \"document_id\": \"$DOCUMENT_ID\"}"
|
sleep "$((RETRY_DELAY * attempt))"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "{\"success\": false, \"error\": \"Upload failed after $MAX_RETRIES attempts (last HTTP $HTTP_CODE)\"}" >&2
|
||||||
|
exit 1
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ Usage:
|
|||||||
init_skill.py <skill-name> --path <path>
|
init_skill.py <skill-name> --path <path>
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
init_skill.py my-new-skill --path ~/.config/opencode/skill
|
init_skill.py my-new-skill --path ~/.config/opencode/skills
|
||||||
init_skill.py my-api-helper --path .opencode/skill
|
init_skill.py my-api-helper --path .opencode/skills
|
||||||
init_skill.py custom-skill --path /custom/location
|
init_skill.py custom-skill --path /custom/location
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user