Add author auth, forking, tags, and stats tracking
Introduce token-based author authentication (register/verify API), skill forking with EditGate protection, tag metadata on skills, and download/push stats. Enhanced push scripts with token auth and per-skill filtering. Updated UI with stats, tags, and author info on skill cards.
This commit is contained in:
309
dist/server/chunks/sync_BEq_wzpT.mjs
vendored
Normal file
309
dist/server/chunks/sync_BEq_wzpT.mjs
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
import { l as listSkills } from './skills_BacVQUiS.mjs';
|
||||
|
||||
function isPowerShell(request) {
|
||||
const ua = request.headers.get("user-agent") || "";
|
||||
return /PowerShell/i.test(ua);
|
||||
}
|
||||
async function buildPushScript(baseUrl, skillsDir) {
|
||||
const lines = [
|
||||
"#!/usr/bin/env bash",
|
||||
"set -euo pipefail",
|
||||
"",
|
||||
`SKILLS_DIR="${skillsDir}"`,
|
||||
`BASE_URL="${baseUrl}"`,
|
||||
'FILTER="${1:-}"',
|
||||
'TOKEN_FILE="$HOME/.claude/skills.here-token"',
|
||||
"",
|
||||
"# Get git author if available",
|
||||
'AUTHOR_NAME=$(git config user.name 2>/dev/null || echo "")',
|
||||
'AUTHOR_EMAIL=$(git config user.email 2>/dev/null || echo "")',
|
||||
"",
|
||||
"# Load or register token",
|
||||
'TOKEN=""',
|
||||
'if [ -f "$TOKEN_FILE" ]; then',
|
||||
' TOKEN=$(cat "$TOKEN_FILE")',
|
||||
'elif [ -n "$AUTHOR_EMAIL" ]; then',
|
||||
' echo "No token found. Registering with $AUTHOR_EMAIL..."',
|
||||
" REGISTER_RESPONSE=$(curl -sS -X POST \\",
|
||||
' -H "Content-Type: application/json" \\',
|
||||
' -d "{\\"email\\": \\"$AUTHOR_EMAIL\\", \\"name\\": \\"$AUTHOR_NAME\\"}" \\',
|
||||
' -w "\\n%{http_code}" \\',
|
||||
' "$BASE_URL/api/auth/register")',
|
||||
` REGISTER_BODY=$(echo "$REGISTER_RESPONSE" | sed '$d')`,
|
||||
' REGISTER_STATUS=$(echo "$REGISTER_RESPONSE" | tail -1)',
|
||||
' if [ "$REGISTER_STATUS" = "201" ]; then',
|
||||
' TOKEN=$(echo "$REGISTER_BODY" | jq -r .token)',
|
||||
' mkdir -p "$(dirname "$TOKEN_FILE")"',
|
||||
' echo "$TOKEN" > "$TOKEN_FILE"',
|
||||
' chmod 600 "$TOKEN_FILE"',
|
||||
' echo " Token saved to $TOKEN_FILE"',
|
||||
' elif [ "$REGISTER_STATUS" = "409" ]; then',
|
||||
' echo " Email already registered. Place your token in $TOKEN_FILE"',
|
||||
' echo " Continuing without token (unprotected skills only)..."',
|
||||
" else",
|
||||
' echo " Registration failed ($REGISTER_STATUS): $REGISTER_BODY"',
|
||||
' echo " Continuing without token (unprotected skills only)..."',
|
||||
" fi",
|
||||
"fi",
|
||||
"",
|
||||
'if [ ! -d "$SKILLS_DIR" ]; then',
|
||||
' echo "No skills directory found at $SKILLS_DIR"',
|
||||
" exit 1",
|
||||
"fi",
|
||||
"",
|
||||
'AUTH_HEADER=""',
|
||||
'if [ -n "$TOKEN" ]; then',
|
||||
' AUTH_HEADER="Authorization: Bearer $TOKEN"',
|
||||
"fi",
|
||||
"",
|
||||
"push_skill() {",
|
||||
' local file="$1"',
|
||||
' local slug=$(basename "$file" .md)',
|
||||
' local content=$(cat "$file")',
|
||||
"",
|
||||
" # Inject author into frontmatter if available and not already present",
|
||||
' if [ -n "$AUTHOR_EMAIL" ] && ! echo "$content" | grep -q "^author-email:"; then',
|
||||
` content=$(echo "$content" | awk -v name="$AUTHOR_NAME" -v email="$AUTHOR_EMAIL" 'NR==1 && /^---$/{print; if (name) print "author: " name; print "author-email: " email; next} {print}')`,
|
||||
" fi",
|
||||
"",
|
||||
" # Build curl auth args",
|
||||
' local auth_args=""',
|
||||
' if [ -n "$AUTH_HEADER" ]; then',
|
||||
' auth_args="-H \\"$AUTH_HEADER\\""',
|
||||
" fi",
|
||||
"",
|
||||
" # Try PUT (update), fallback to POST (create)",
|
||||
" local response",
|
||||
' if [ -n "$TOKEN" ]; then',
|
||||
' response=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \\',
|
||||
' -H "Content-Type: application/json" \\',
|
||||
' -H "Authorization: Bearer $TOKEN" \\',
|
||||
' -d "{\\"content\\": $(echo "$content" | jq -Rs .)}" \\',
|
||||
' "$BASE_URL/api/skills/$slug")',
|
||||
" else",
|
||||
' response=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \\',
|
||||
' -H "Content-Type: application/json" \\',
|
||||
' -d "{\\"content\\": $(echo "$content" | jq -Rs .)}" \\',
|
||||
' "$BASE_URL/api/skills/$slug")',
|
||||
" fi",
|
||||
"",
|
||||
' if [ "$response" = "403" ]; then',
|
||||
' echo " ✗ $slug (permission denied — token missing or invalid)"',
|
||||
" return 1",
|
||||
" fi",
|
||||
"",
|
||||
' if [ "$response" = "404" ]; then',
|
||||
' if [ -n "$TOKEN" ]; then',
|
||||
' local post_status=$(curl -sS -o /dev/null -w "%{http_code}" -X POST \\',
|
||||
' -H "Content-Type: application/json" \\',
|
||||
' -H "Authorization: Bearer $TOKEN" \\',
|
||||
' -d "{\\"slug\\": \\"$slug\\", \\"content\\": $(echo "$content" | jq -Rs .)}" \\',
|
||||
' "$BASE_URL/api/skills")',
|
||||
" else",
|
||||
' local post_status=$(curl -sS -o /dev/null -w "%{http_code}" -X POST \\',
|
||||
' -H "Content-Type: application/json" \\',
|
||||
' -d "{\\"slug\\": \\"$slug\\", \\"content\\": $(echo "$content" | jq -Rs .)}" \\',
|
||||
' "$BASE_URL/api/skills")',
|
||||
" fi",
|
||||
' if [ "$post_status" = "403" ]; then',
|
||||
' echo " ✗ $slug (permission denied — token missing or invalid)"',
|
||||
" return 1",
|
||||
" fi",
|
||||
" fi",
|
||||
"",
|
||||
' echo " ✓ $slug"',
|
||||
"}",
|
||||
"",
|
||||
"count=0",
|
||||
"failed=0",
|
||||
'if [ -n "$FILTER" ]; then',
|
||||
" # Push a specific skill",
|
||||
' file="$SKILLS_DIR/${FILTER%.md}.md"',
|
||||
' if [ ! -f "$file" ]; then',
|
||||
' echo "Skill not found: $file"',
|
||||
" exit 1",
|
||||
" fi",
|
||||
' if push_skill "$file"; then',
|
||||
" count=1",
|
||||
" else",
|
||||
" failed=1",
|
||||
" fi",
|
||||
"else",
|
||||
" # Push all skills",
|
||||
' for file in "$SKILLS_DIR"/*.md; do',
|
||||
' [ -f "$file" ] || continue',
|
||||
' if push_skill "$file"; then',
|
||||
" count=$((count + 1))",
|
||||
" else",
|
||||
" failed=$((failed + 1))",
|
||||
" fi",
|
||||
" done",
|
||||
"fi",
|
||||
"",
|
||||
'echo "Pushed $count skill(s) to $BASE_URL"',
|
||||
'if [ "$failed" -gt 0 ]; then',
|
||||
' echo "$failed skill(s) failed (permission denied)"',
|
||||
"fi",
|
||||
""
|
||||
];
|
||||
return lines.join("\n");
|
||||
}
|
||||
async function buildSyncScript(baseUrl, skillsDir) {
|
||||
const skills = await listSkills();
|
||||
const lines = [
|
||||
"#!/usr/bin/env bash",
|
||||
"set -euo pipefail",
|
||||
"",
|
||||
`SKILLS_DIR="${skillsDir}"`,
|
||||
'mkdir -p "$SKILLS_DIR"',
|
||||
""
|
||||
];
|
||||
if (skills.length === 0) {
|
||||
lines.push('echo "No skills available to sync."');
|
||||
} else {
|
||||
lines.push(`echo "Syncing ${skills.length} skill(s) from ${baseUrl}..."`);
|
||||
lines.push("");
|
||||
for (const skill of skills) {
|
||||
const skillUrl = `${baseUrl}/${skill.slug}`;
|
||||
lines.push(`curl -fsSL "${skillUrl}" -o "$SKILLS_DIR/${skill.slug}.md"`);
|
||||
lines.push(`echo " ✓ ${skill.name}"`);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push('echo "Done! Skills synced to $SKILLS_DIR"');
|
||||
}
|
||||
lines.push("");
|
||||
return lines.join("\n");
|
||||
}
|
||||
async function buildSyncScriptPS(baseUrl, skillsDir) {
|
||||
const skills = await listSkills();
|
||||
const lines = [
|
||||
'$ErrorActionPreference = "Stop"',
|
||||
"",
|
||||
`$SkillsDir = "${skillsDir}"`,
|
||||
"New-Item -ItemType Directory -Force -Path $SkillsDir | Out-Null",
|
||||
""
|
||||
];
|
||||
if (skills.length === 0) {
|
||||
lines.push('Write-Host "No skills available to sync."');
|
||||
} else {
|
||||
lines.push(`Write-Host "Syncing ${skills.length} skill(s) from ${baseUrl}..."`);
|
||||
lines.push("");
|
||||
for (const skill of skills) {
|
||||
const skillUrl = `${baseUrl}/${skill.slug}`;
|
||||
lines.push(`Invoke-WebRequest -Uri "${skillUrl}" -OutFile (Join-Path $SkillsDir "${skill.slug}.md")`);
|
||||
lines.push(`Write-Host " ✓ ${skill.name}"`);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push('Write-Host "Done! Skills synced to $SkillsDir"');
|
||||
}
|
||||
lines.push("");
|
||||
return lines.join("\n");
|
||||
}
|
||||
async function buildPushScriptPS(baseUrl, skillsDir) {
|
||||
const lines = [
|
||||
'$ErrorActionPreference = "Stop"',
|
||||
"",
|
||||
`$SkillsDir = "${skillsDir}"`,
|
||||
`$BaseUrl = "${baseUrl}"`,
|
||||
'$Filter = if ($args.Count -gt 0) { $args[0] } else { "" }',
|
||||
'$TokenFile = Join-Path $HOME ".claude\\skills.here-token"',
|
||||
"",
|
||||
"# Get git author if available",
|
||||
'$AuthorName = try { git config user.name 2>$null } catch { "" }',
|
||||
'$AuthorEmail = try { git config user.email 2>$null } catch { "" }',
|
||||
"",
|
||||
"# Load or register token",
|
||||
'$Token = ""',
|
||||
"if (Test-Path $TokenFile) {",
|
||||
" $Token = (Get-Content $TokenFile -Raw).Trim()",
|
||||
"} elseif ($AuthorEmail) {",
|
||||
' Write-Host "No token found. Registering with $AuthorEmail..."',
|
||||
" try {",
|
||||
" $body = @{ email = $AuthorEmail; name = $AuthorName } | ConvertTo-Json",
|
||||
' $resp = Invoke-WebRequest -Uri "$BaseUrl/api/auth/register" -Method POST -ContentType "application/json" -Body $body',
|
||||
" if ($resp.StatusCode -eq 201) {",
|
||||
" $Token = ($resp.Content | ConvertFrom-Json).token",
|
||||
" New-Item -ItemType Directory -Force -Path (Split-Path $TokenFile) | Out-Null",
|
||||
" Set-Content -Path $TokenFile -Value $Token",
|
||||
' Write-Host " Token saved to $TokenFile"',
|
||||
" }",
|
||||
" } catch {",
|
||||
" $code = $_.Exception.Response.StatusCode.value__",
|
||||
" if ($code -eq 409) {",
|
||||
' Write-Host " Email already registered. Place your token in $TokenFile"',
|
||||
" } else {",
|
||||
' Write-Host " Registration failed: $_"',
|
||||
" }",
|
||||
' Write-Host " Continuing without token (unprotected skills only)..."',
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"if (-not (Test-Path $SkillsDir)) {",
|
||||
' Write-Host "No skills directory found at $SkillsDir"',
|
||||
" exit 1",
|
||||
"}",
|
||||
"",
|
||||
'$headers = @{ "Content-Type" = "application/json" }',
|
||||
'if ($Token) { $headers["Authorization"] = "Bearer $Token" }',
|
||||
"",
|
||||
"function Push-Skill($file) {",
|
||||
" $slug = [IO.Path]::GetFileNameWithoutExtension($file)",
|
||||
" $content = Get-Content $file -Raw",
|
||||
"",
|
||||
" # Inject author if available and not present",
|
||||
' if ($AuthorEmail -and $content -notmatch "(?m)^author-email:") {',
|
||||
' $inject = ""',
|
||||
' if ($AuthorName) { $inject += "author: $AuthorName`n" }',
|
||||
' $inject += "author-email: $AuthorEmail"',
|
||||
' $content = $content -replace "^---$", "---`n$inject" -replace "^---`n", "---`n"',
|
||||
" }",
|
||||
"",
|
||||
" $body = @{ content = $content } | ConvertTo-Json",
|
||||
" try {",
|
||||
' Invoke-WebRequest -Uri "$BaseUrl/api/skills/$slug" -Method PUT -Headers $headers -Body $body | Out-Null',
|
||||
' Write-Host " ✓ $slug"',
|
||||
" return $true",
|
||||
" } catch {",
|
||||
" $code = $_.Exception.Response.StatusCode.value__",
|
||||
" if ($code -eq 403) {",
|
||||
' Write-Host " ✗ $slug (permission denied)"',
|
||||
" return $false",
|
||||
" }",
|
||||
" if ($code -eq 404) {",
|
||||
" $postBody = @{ slug = $slug; content = $content } | ConvertTo-Json",
|
||||
" try {",
|
||||
' Invoke-WebRequest -Uri "$BaseUrl/api/skills" -Method POST -Headers $headers -Body $postBody | Out-Null',
|
||||
' Write-Host " ✓ $slug"',
|
||||
" return $true",
|
||||
" } catch {",
|
||||
" $postCode = $_.Exception.Response.StatusCode.value__",
|
||||
" if ($postCode -eq 403) {",
|
||||
' Write-Host " ✗ $slug (permission denied)"',
|
||||
" return $false",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
' Write-Host " ✓ $slug"',
|
||||
" return $true",
|
||||
"}",
|
||||
"",
|
||||
"$count = 0; $failed = 0",
|
||||
"if ($Filter) {",
|
||||
` $file = Join-Path $SkillsDir "$($Filter -replace '\\.md$','').md"`,
|
||||
' if (-not (Test-Path $file)) { Write-Host "Skill not found: $file"; exit 1 }',
|
||||
" if (Push-Skill $file) { $count++ } else { $failed++ }",
|
||||
"} else {",
|
||||
' Get-ChildItem -Path $SkillsDir -Filter "*.md" | ForEach-Object {',
|
||||
" if (Push-Skill $_.FullName) { $count++ } else { $failed++ }",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
'Write-Host "Pushed $count skill(s) to $BaseUrl"',
|
||||
'if ($failed -gt 0) { Write-Host "$failed skill(s) failed (permission denied)" }',
|
||||
""
|
||||
];
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export { buildSyncScriptPS as a, buildSyncScript as b, buildPushScript as c, buildPushScriptPS as d, isPowerShell as i };
|
||||
Reference in New Issue
Block a user