aidevops 2.52.1
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/.agent/AGENTS.md +614 -0
- package/.agent/accounts.md +65 -0
- package/.agent/aidevops/add-new-mcp-to-aidevops.md +456 -0
- package/.agent/aidevops/api-integrations.md +335 -0
- package/.agent/aidevops/architecture.md +510 -0
- package/.agent/aidevops/configs.md +274 -0
- package/.agent/aidevops/docs.md +244 -0
- package/.agent/aidevops/extension.md +311 -0
- package/.agent/aidevops/mcp-integrations.md +340 -0
- package/.agent/aidevops/mcp-troubleshooting.md +162 -0
- package/.agent/aidevops/memory-patterns.md +172 -0
- package/.agent/aidevops/providers.md +217 -0
- package/.agent/aidevops/recommendations.md +321 -0
- package/.agent/aidevops/requirements.md +301 -0
- package/.agent/aidevops/resources.md +214 -0
- package/.agent/aidevops/security-requirements.md +174 -0
- package/.agent/aidevops/security.md +350 -0
- package/.agent/aidevops/service-links.md +400 -0
- package/.agent/aidevops/services.md +357 -0
- package/.agent/aidevops/setup.md +153 -0
- package/.agent/aidevops/troubleshooting.md +389 -0
- package/.agent/aidevops.md +124 -0
- package/.agent/build-plus.md +244 -0
- package/.agent/content/guidelines.md +109 -0
- package/.agent/content.md +87 -0
- package/.agent/health.md +59 -0
- package/.agent/legal.md +59 -0
- package/.agent/loop-state/full-loop.local.md +16 -0
- package/.agent/loop-state/ralph-loop.local.md +10 -0
- package/.agent/marketing.md +440 -0
- package/.agent/memory/README.md +260 -0
- package/.agent/onboarding.md +796 -0
- package/.agent/plan-plus.md +245 -0
- package/.agent/research.md +100 -0
- package/.agent/sales.md +333 -0
- package/.agent/scripts/101domains-helper.sh +701 -0
- package/.agent/scripts/add-missing-returns.sh +140 -0
- package/.agent/scripts/agent-browser-helper.sh +311 -0
- package/.agent/scripts/agno-setup.sh +712 -0
- package/.agent/scripts/ahrefs-mcp-wrapper.js +168 -0
- package/.agent/scripts/aidevops-update-check.sh +71 -0
- package/.agent/scripts/ampcode-cli.sh +522 -0
- package/.agent/scripts/auto-version-bump.sh +156 -0
- package/.agent/scripts/autogen-helper.sh +512 -0
- package/.agent/scripts/beads-sync-helper.sh +596 -0
- package/.agent/scripts/closte-helper.sh +5 -0
- package/.agent/scripts/cloudron-helper.sh +321 -0
- package/.agent/scripts/codacy-cli-chunked.sh +581 -0
- package/.agent/scripts/codacy-cli.sh +442 -0
- package/.agent/scripts/code-audit-helper.sh +5 -0
- package/.agent/scripts/coderabbit-cli.sh +417 -0
- package/.agent/scripts/coderabbit-pro-analysis.sh +238 -0
- package/.agent/scripts/commands/code-simplifier.md +86 -0
- package/.agent/scripts/commands/full-loop.md +246 -0
- package/.agent/scripts/commands/postflight-loop.md +103 -0
- package/.agent/scripts/commands/recall.md +182 -0
- package/.agent/scripts/commands/remember.md +132 -0
- package/.agent/scripts/commands/save-todo.md +175 -0
- package/.agent/scripts/commands/session-review.md +154 -0
- package/.agent/scripts/comprehensive-quality-fix.sh +106 -0
- package/.agent/scripts/context-builder-helper.sh +522 -0
- package/.agent/scripts/coolify-cli-helper.sh +674 -0
- package/.agent/scripts/coolify-helper.sh +380 -0
- package/.agent/scripts/crawl4ai-examples.sh +401 -0
- package/.agent/scripts/crawl4ai-helper.sh +1078 -0
- package/.agent/scripts/crewai-helper.sh +681 -0
- package/.agent/scripts/dev-browser-helper.sh +513 -0
- package/.agent/scripts/dns-helper.sh +396 -0
- package/.agent/scripts/domain-research-helper.sh +917 -0
- package/.agent/scripts/dspy-helper.sh +285 -0
- package/.agent/scripts/dspyground-helper.sh +291 -0
- package/.agent/scripts/eeat-score-helper.sh +1242 -0
- package/.agent/scripts/efficient-return-fix.sh +92 -0
- package/.agent/scripts/extract-opencode-prompts.sh +128 -0
- package/.agent/scripts/find-missing-returns.sh +113 -0
- package/.agent/scripts/fix-auth-headers.sh +104 -0
- package/.agent/scripts/fix-common-strings.sh +254 -0
- package/.agent/scripts/fix-content-type.sh +100 -0
- package/.agent/scripts/fix-error-messages.sh +130 -0
- package/.agent/scripts/fix-misplaced-returns.sh +74 -0
- package/.agent/scripts/fix-remaining-literals.sh +152 -0
- package/.agent/scripts/fix-return-statements.sh +41 -0
- package/.agent/scripts/fix-s131-default-cases.sh +249 -0
- package/.agent/scripts/fix-sc2155-simple.sh +102 -0
- package/.agent/scripts/fix-shellcheck-critical.sh +187 -0
- package/.agent/scripts/fix-string-literals.sh +273 -0
- package/.agent/scripts/full-loop-helper.sh +773 -0
- package/.agent/scripts/generate-opencode-agents.sh +497 -0
- package/.agent/scripts/generate-opencode-commands.sh +1629 -0
- package/.agent/scripts/generate-skills.sh +366 -0
- package/.agent/scripts/git-platforms-helper.sh +640 -0
- package/.agent/scripts/gitea-cli-helper.sh +743 -0
- package/.agent/scripts/github-cli-helper.sh +702 -0
- package/.agent/scripts/gitlab-cli-helper.sh +682 -0
- package/.agent/scripts/gsc-add-user-helper.sh +325 -0
- package/.agent/scripts/gsc-sitemap-helper.sh +678 -0
- package/.agent/scripts/hetzner-helper.sh +485 -0
- package/.agent/scripts/hostinger-helper.sh +229 -0
- package/.agent/scripts/keyword-research-helper.sh +1815 -0
- package/.agent/scripts/langflow-helper.sh +544 -0
- package/.agent/scripts/linkedin-automation.py +241 -0
- package/.agent/scripts/linter-manager.sh +599 -0
- package/.agent/scripts/linters-local.sh +434 -0
- package/.agent/scripts/list-keys-helper.sh +488 -0
- package/.agent/scripts/local-browser-automation.py +339 -0
- package/.agent/scripts/localhost-helper.sh +744 -0
- package/.agent/scripts/loop-common.sh +806 -0
- package/.agent/scripts/mainwp-helper.sh +728 -0
- package/.agent/scripts/markdown-formatter.sh +338 -0
- package/.agent/scripts/markdown-lint-fix.sh +311 -0
- package/.agent/scripts/mass-fix-returns.sh +58 -0
- package/.agent/scripts/mcp-diagnose.sh +167 -0
- package/.agent/scripts/mcp-inspector-helper.sh +449 -0
- package/.agent/scripts/memory-helper.sh +650 -0
- package/.agent/scripts/monitor-code-review.sh +255 -0
- package/.agent/scripts/onboarding-helper.sh +706 -0
- package/.agent/scripts/opencode-github-setup-helper.sh +797 -0
- package/.agent/scripts/opencode-test-helper.sh +213 -0
- package/.agent/scripts/pagespeed-helper.sh +464 -0
- package/.agent/scripts/pandoc-helper.sh +362 -0
- package/.agent/scripts/postflight-check.sh +555 -0
- package/.agent/scripts/pre-commit-hook.sh +259 -0
- package/.agent/scripts/pre-edit-check.sh +169 -0
- package/.agent/scripts/qlty-cli.sh +356 -0
- package/.agent/scripts/quality-cli-manager.sh +525 -0
- package/.agent/scripts/quality-feedback-helper.sh +462 -0
- package/.agent/scripts/quality-fix.sh +263 -0
- package/.agent/scripts/quality-loop-helper.sh +1108 -0
- package/.agent/scripts/ralph-loop-helper.sh +836 -0
- package/.agent/scripts/ralph-upstream-check.sh +341 -0
- package/.agent/scripts/secretlint-helper.sh +847 -0
- package/.agent/scripts/servers-helper.sh +241 -0
- package/.agent/scripts/ses-helper.sh +619 -0
- package/.agent/scripts/session-review-helper.sh +404 -0
- package/.agent/scripts/setup-linters-wizard.sh +379 -0
- package/.agent/scripts/setup-local-api-keys.sh +330 -0
- package/.agent/scripts/setup-mcp-integrations.sh +472 -0
- package/.agent/scripts/shared-constants.sh +246 -0
- package/.agent/scripts/site-crawler-helper.sh +1487 -0
- package/.agent/scripts/snyk-helper.sh +940 -0
- package/.agent/scripts/sonarcloud-autofix.sh +193 -0
- package/.agent/scripts/sonarcloud-cli.sh +191 -0
- package/.agent/scripts/sonarscanner-cli.sh +455 -0
- package/.agent/scripts/spaceship-helper.sh +747 -0
- package/.agent/scripts/stagehand-helper.sh +321 -0
- package/.agent/scripts/stagehand-python-helper.sh +321 -0
- package/.agent/scripts/stagehand-python-setup.sh +441 -0
- package/.agent/scripts/stagehand-setup.sh +439 -0
- package/.agent/scripts/system-cleanup.sh +340 -0
- package/.agent/scripts/terminal-title-helper.sh +388 -0
- package/.agent/scripts/terminal-title-setup.sh +549 -0
- package/.agent/scripts/test-stagehand-both-integration.sh +317 -0
- package/.agent/scripts/test-stagehand-integration.sh +309 -0
- package/.agent/scripts/test-stagehand-python-integration.sh +341 -0
- package/.agent/scripts/todo-ready.sh +263 -0
- package/.agent/scripts/tool-version-check.sh +362 -0
- package/.agent/scripts/toon-helper.sh +469 -0
- package/.agent/scripts/twilio-helper.sh +917 -0
- package/.agent/scripts/updown-helper.sh +279 -0
- package/.agent/scripts/validate-mcp-integrations.sh +250 -0
- package/.agent/scripts/validate-version-consistency.sh +131 -0
- package/.agent/scripts/vaultwarden-helper.sh +597 -0
- package/.agent/scripts/vercel-cli-helper.sh +816 -0
- package/.agent/scripts/verify-mirrors.sh +169 -0
- package/.agent/scripts/version-manager.sh +831 -0
- package/.agent/scripts/webhosting-helper.sh +471 -0
- package/.agent/scripts/webhosting-verify.sh +238 -0
- package/.agent/scripts/wordpress-mcp-helper.sh +508 -0
- package/.agent/scripts/worktree-helper.sh +595 -0
- package/.agent/scripts/worktree-sessions.sh +577 -0
- package/.agent/seo/dataforseo.md +215 -0
- package/.agent/seo/domain-research.md +532 -0
- package/.agent/seo/eeat-score.md +659 -0
- package/.agent/seo/google-search-console.md +366 -0
- package/.agent/seo/gsc-sitemaps.md +282 -0
- package/.agent/seo/keyword-research.md +521 -0
- package/.agent/seo/serper.md +278 -0
- package/.agent/seo/site-crawler.md +387 -0
- package/.agent/seo.md +236 -0
- package/.agent/services/accounting/quickfile.md +159 -0
- package/.agent/services/communications/telfon.md +470 -0
- package/.agent/services/communications/twilio.md +569 -0
- package/.agent/services/crm/fluentcrm.md +449 -0
- package/.agent/services/email/ses.md +399 -0
- package/.agent/services/hosting/101domains.md +378 -0
- package/.agent/services/hosting/closte.md +177 -0
- package/.agent/services/hosting/cloudflare.md +251 -0
- package/.agent/services/hosting/cloudron.md +478 -0
- package/.agent/services/hosting/dns-providers.md +335 -0
- package/.agent/services/hosting/domain-purchasing.md +344 -0
- package/.agent/services/hosting/hetzner.md +327 -0
- package/.agent/services/hosting/hostinger.md +287 -0
- package/.agent/services/hosting/localhost.md +419 -0
- package/.agent/services/hosting/spaceship.md +353 -0
- package/.agent/services/hosting/webhosting.md +330 -0
- package/.agent/social-media.md +69 -0
- package/.agent/templates/plans-template.md +114 -0
- package/.agent/templates/prd-template.md +129 -0
- package/.agent/templates/tasks-template.md +108 -0
- package/.agent/templates/todo-template.md +89 -0
- package/.agent/tools/ai-assistants/agno.md +471 -0
- package/.agent/tools/ai-assistants/capsolver.md +326 -0
- package/.agent/tools/ai-assistants/configuration.md +221 -0
- package/.agent/tools/ai-assistants/overview.md +209 -0
- package/.agent/tools/ai-assistants/status.md +171 -0
- package/.agent/tools/ai-assistants/windsurf.md +193 -0
- package/.agent/tools/ai-orchestration/autogen.md +406 -0
- package/.agent/tools/ai-orchestration/crewai.md +445 -0
- package/.agent/tools/ai-orchestration/langflow.md +405 -0
- package/.agent/tools/ai-orchestration/openprose.md +487 -0
- package/.agent/tools/ai-orchestration/overview.md +362 -0
- package/.agent/tools/ai-orchestration/packaging.md +647 -0
- package/.agent/tools/browser/agent-browser.md +464 -0
- package/.agent/tools/browser/browser-automation.md +400 -0
- package/.agent/tools/browser/chrome-devtools.md +282 -0
- package/.agent/tools/browser/crawl4ai-integration.md +422 -0
- package/.agent/tools/browser/crawl4ai-resources.md +277 -0
- package/.agent/tools/browser/crawl4ai-usage.md +416 -0
- package/.agent/tools/browser/crawl4ai.md +585 -0
- package/.agent/tools/browser/dev-browser.md +341 -0
- package/.agent/tools/browser/pagespeed.md +260 -0
- package/.agent/tools/browser/playwright.md +266 -0
- package/.agent/tools/browser/playwriter.md +310 -0
- package/.agent/tools/browser/stagehand-examples.md +456 -0
- package/.agent/tools/browser/stagehand-python.md +483 -0
- package/.agent/tools/browser/stagehand.md +421 -0
- package/.agent/tools/build-agent/agent-review.md +224 -0
- package/.agent/tools/build-agent/build-agent.md +784 -0
- package/.agent/tools/build-mcp/aidevops-plugin.md +476 -0
- package/.agent/tools/build-mcp/api-wrapper.md +445 -0
- package/.agent/tools/build-mcp/build-mcp.md +240 -0
- package/.agent/tools/build-mcp/deployment.md +401 -0
- package/.agent/tools/build-mcp/server-patterns.md +632 -0
- package/.agent/tools/build-mcp/transports.md +366 -0
- package/.agent/tools/code-review/auditing.md +383 -0
- package/.agent/tools/code-review/automation.md +219 -0
- package/.agent/tools/code-review/best-practices.md +203 -0
- package/.agent/tools/code-review/codacy.md +151 -0
- package/.agent/tools/code-review/code-simplifier.md +174 -0
- package/.agent/tools/code-review/code-standards.md +309 -0
- package/.agent/tools/code-review/coderabbit.md +101 -0
- package/.agent/tools/code-review/management.md +155 -0
- package/.agent/tools/code-review/qlty.md +248 -0
- package/.agent/tools/code-review/secretlint.md +565 -0
- package/.agent/tools/code-review/setup.md +250 -0
- package/.agent/tools/code-review/snyk.md +563 -0
- package/.agent/tools/code-review/tools.md +230 -0
- package/.agent/tools/content/summarize.md +353 -0
- package/.agent/tools/context/augment-context-engine.md +468 -0
- package/.agent/tools/context/context-builder-agent.md +76 -0
- package/.agent/tools/context/context-builder.md +375 -0
- package/.agent/tools/context/context7.md +371 -0
- package/.agent/tools/context/dspy.md +302 -0
- package/.agent/tools/context/dspyground.md +374 -0
- package/.agent/tools/context/llm-tldr.md +219 -0
- package/.agent/tools/context/osgrep.md +488 -0
- package/.agent/tools/context/prompt-optimization.md +338 -0
- package/.agent/tools/context/toon.md +292 -0
- package/.agent/tools/conversion/pandoc.md +304 -0
- package/.agent/tools/credentials/api-key-management.md +154 -0
- package/.agent/tools/credentials/api-key-setup.md +224 -0
- package/.agent/tools/credentials/environment-variables.md +180 -0
- package/.agent/tools/credentials/vaultwarden.md +382 -0
- package/.agent/tools/data-extraction/outscraper.md +974 -0
- package/.agent/tools/deployment/coolify-cli.md +388 -0
- package/.agent/tools/deployment/coolify-setup.md +353 -0
- package/.agent/tools/deployment/coolify.md +345 -0
- package/.agent/tools/deployment/vercel.md +390 -0
- package/.agent/tools/git/authentication.md +132 -0
- package/.agent/tools/git/gitea-cli.md +193 -0
- package/.agent/tools/git/github-actions.md +207 -0
- package/.agent/tools/git/github-cli.md +223 -0
- package/.agent/tools/git/gitlab-cli.md +190 -0
- package/.agent/tools/git/opencode-github-security.md +350 -0
- package/.agent/tools/git/opencode-github.md +328 -0
- package/.agent/tools/git/opencode-gitlab.md +252 -0
- package/.agent/tools/git/security.md +196 -0
- package/.agent/tools/git.md +207 -0
- package/.agent/tools/opencode/oh-my-opencode.md +375 -0
- package/.agent/tools/opencode/opencode-anthropic-auth.md +446 -0
- package/.agent/tools/opencode/opencode.md +651 -0
- package/.agent/tools/social-media/bird.md +437 -0
- package/.agent/tools/task-management/beads.md +336 -0
- package/.agent/tools/terminal/terminal-title.md +251 -0
- package/.agent/tools/ui/shadcn.md +196 -0
- package/.agent/tools/ui/ui-skills.md +115 -0
- package/.agent/tools/wordpress/localwp.md +311 -0
- package/.agent/tools/wordpress/mainwp.md +391 -0
- package/.agent/tools/wordpress/scf.md +527 -0
- package/.agent/tools/wordpress/wp-admin.md +729 -0
- package/.agent/tools/wordpress/wp-dev.md +940 -0
- package/.agent/tools/wordpress/wp-preferred.md +398 -0
- package/.agent/tools/wordpress.md +95 -0
- package/.agent/workflows/branch/bugfix.md +63 -0
- package/.agent/workflows/branch/chore.md +95 -0
- package/.agent/workflows/branch/experiment.md +115 -0
- package/.agent/workflows/branch/feature.md +59 -0
- package/.agent/workflows/branch/hotfix.md +98 -0
- package/.agent/workflows/branch/refactor.md +92 -0
- package/.agent/workflows/branch/release.md +96 -0
- package/.agent/workflows/branch.md +347 -0
- package/.agent/workflows/bug-fixing.md +267 -0
- package/.agent/workflows/changelog.md +129 -0
- package/.agent/workflows/code-audit-remote.md +279 -0
- package/.agent/workflows/conversation-starter.md +69 -0
- package/.agent/workflows/error-feedback.md +578 -0
- package/.agent/workflows/feature-development.md +355 -0
- package/.agent/workflows/git-workflow.md +702 -0
- package/.agent/workflows/multi-repo-workspace.md +268 -0
- package/.agent/workflows/plans.md +709 -0
- package/.agent/workflows/postflight.md +604 -0
- package/.agent/workflows/pr.md +571 -0
- package/.agent/workflows/preflight.md +278 -0
- package/.agent/workflows/ralph-loop.md +773 -0
- package/.agent/workflows/release.md +498 -0
- package/.agent/workflows/session-manager.md +254 -0
- package/.agent/workflows/session-review.md +311 -0
- package/.agent/workflows/sql-migrations.md +631 -0
- package/.agent/workflows/version-bump.md +283 -0
- package/.agent/workflows/wiki-update.md +333 -0
- package/.agent/workflows/worktree.md +477 -0
- package/LICENSE +21 -0
- package/README.md +1446 -0
- package/VERSION +1 -0
- package/aidevops.sh +1746 -0
- package/bin/aidevops +21 -0
- package/package.json +75 -0
- package/scripts/npm-postinstall.js +60 -0
- package/setup.sh +2366 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Google Search Console - Sitemap Submission Helper
|
|
3
|
+
# Uses Playwright to automate sitemap submissions to GSC
|
|
4
|
+
# Part of AI DevOps Framework
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Source shared constants
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
# shellcheck source=shared-constants.sh
|
|
11
|
+
source "${SCRIPT_DIR}/shared-constants.sh" 2>/dev/null || true
|
|
12
|
+
|
|
13
|
+
# Configuration
|
|
14
|
+
readonly CONFIG_FILE="${HOME}/.config/aidevops/gsc-config.json"
|
|
15
|
+
readonly WORK_DIR="${HOME}/.aidevops/.agent-workspace/tmp"
|
|
16
|
+
readonly GSC_SCRIPT="${WORK_DIR}/gsc-sitemap-submit.js"
|
|
17
|
+
readonly CHROME_PROFILE="${HOME}/.aidevops/.agent-workspace/chrome-gsc-profile"
|
|
18
|
+
readonly SCREENSHOT_DIR="/tmp/gsc-screenshots"
|
|
19
|
+
readonly DEFAULT_SITEMAP="sitemap.xml"
|
|
20
|
+
|
|
21
|
+
# Colors
|
|
22
|
+
readonly RED='\033[0;31m'
|
|
23
|
+
readonly GREEN='\033[0;32m'
|
|
24
|
+
readonly YELLOW='\033[1;33m'
|
|
25
|
+
readonly BLUE='\033[0;34m'
|
|
26
|
+
readonly NC='\033[0m'
|
|
27
|
+
|
|
28
|
+
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
29
|
+
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
|
30
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
31
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
|
32
|
+
|
|
33
|
+
show_help() {
|
|
34
|
+
cat << 'HELP'
|
|
35
|
+
Usage: gsc-sitemap-helper.sh <command> [options]
|
|
36
|
+
|
|
37
|
+
Commands:
|
|
38
|
+
submit <domain...> Submit sitemap for one or more domains
|
|
39
|
+
submit --file <file> Submit sitemaps for domains listed in file
|
|
40
|
+
status <domain> Check sitemap status for a domain
|
|
41
|
+
list <domain> List all sitemaps for a domain
|
|
42
|
+
login Open browser to login to Google (first-time setup)
|
|
43
|
+
setup Install dependencies (Node.js, Playwright)
|
|
44
|
+
help Show this help message
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--sitemap <path> Custom sitemap path (default: sitemap.xml)
|
|
48
|
+
--dry-run Show what would be done without making changes
|
|
49
|
+
--skip-existing Skip domains that already have sitemaps
|
|
50
|
+
--headless Run in headless mode (after initial login)
|
|
51
|
+
--timeout <ms> Timeout in milliseconds (default: 60000)
|
|
52
|
+
|
|
53
|
+
Examples:
|
|
54
|
+
gsc-sitemap-helper.sh submit example.com
|
|
55
|
+
gsc-sitemap-helper.sh submit example.com example.net example.org
|
|
56
|
+
gsc-sitemap-helper.sh submit --file domains.txt
|
|
57
|
+
gsc-sitemap-helper.sh submit example.com --sitemap news-sitemap.xml
|
|
58
|
+
gsc-sitemap-helper.sh status example.com
|
|
59
|
+
gsc-sitemap-helper.sh login
|
|
60
|
+
|
|
61
|
+
Requirements:
|
|
62
|
+
- Node.js and npm installed
|
|
63
|
+
- Playwright: npm install playwright
|
|
64
|
+
- Chrome browser installed
|
|
65
|
+
- User logged into Google in the Chrome profile
|
|
66
|
+
|
|
67
|
+
First-time setup:
|
|
68
|
+
1. Run: gsc-sitemap-helper.sh setup
|
|
69
|
+
2. Run: gsc-sitemap-helper.sh login
|
|
70
|
+
3. Log into Google in the browser that opens
|
|
71
|
+
4. Close browser when done
|
|
72
|
+
5. Now you can submit sitemaps
|
|
73
|
+
HELP
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get_chrome_profile_path() {
|
|
77
|
+
echo "${CHROME_PROFILE}"
|
|
78
|
+
return 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ensure_directories() {
|
|
82
|
+
mkdir -p "${WORK_DIR}"
|
|
83
|
+
mkdir -p "${CHROME_PROFILE}"
|
|
84
|
+
mkdir -p "${SCREENSHOT_DIR}"
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ensure_playwright() {
|
|
89
|
+
if ! command -v npx &> /dev/null; then
|
|
90
|
+
log_error "npx not found. Please install Node.js"
|
|
91
|
+
return 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Check if playwright is available in WORK_DIR
|
|
95
|
+
if [[ ! -d "${WORK_DIR}/node_modules/playwright" ]]; then
|
|
96
|
+
log_info "Installing Playwright in ${WORK_DIR}..."
|
|
97
|
+
# Install in WORK_DIR to avoid polluting other projects
|
|
98
|
+
npm --prefix "${WORK_DIR}" install playwright > /dev/null 2>&1 || {
|
|
99
|
+
log_error "Failed to install Playwright"
|
|
100
|
+
return 1
|
|
101
|
+
}
|
|
102
|
+
fi
|
|
103
|
+
return 0
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Sanitize domain for safe embedding in JavaScript
|
|
107
|
+
sanitize_domain() {
|
|
108
|
+
local domain="$1"
|
|
109
|
+
# Remove any characters that could break JS string literals
|
|
110
|
+
# Allow only alphanumeric, dots, and hyphens (valid domain chars)
|
|
111
|
+
echo "$domain" | tr -cd 'a-zA-Z0-9.-'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Sanitize sitemap path for safe embedding in JavaScript
|
|
115
|
+
sanitize_sitemap_path() {
|
|
116
|
+
local path="$1"
|
|
117
|
+
# Allow only alphanumeric, dots, hyphens, underscores, and forward slashes
|
|
118
|
+
# These are valid URL path characters for sitemaps
|
|
119
|
+
echo "$path" | tr -cd 'a-zA-Z0-9./_-'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
create_submit_script() {
|
|
123
|
+
local domains_json="$1"
|
|
124
|
+
local sitemap_path
|
|
125
|
+
sitemap_path="$(sanitize_sitemap_path "$2")"
|
|
126
|
+
local dry_run="$3"
|
|
127
|
+
local headless="$4"
|
|
128
|
+
local timeout="$5"
|
|
129
|
+
|
|
130
|
+
local chrome_profile
|
|
131
|
+
chrome_profile="$(get_chrome_profile_path)"
|
|
132
|
+
|
|
133
|
+
cat > "${GSC_SCRIPT}" << SCRIPT
|
|
134
|
+
import { chromium } from 'playwright';
|
|
135
|
+
|
|
136
|
+
const DOMAINS = ${domains_json};
|
|
137
|
+
const SITEMAP_PATH = "${sitemap_path}";
|
|
138
|
+
const DRY_RUN = ${dry_run};
|
|
139
|
+
const HEADLESS = ${headless};
|
|
140
|
+
const TIMEOUT = ${timeout};
|
|
141
|
+
const SCREENSHOT_DIR = "${SCREENSHOT_DIR}";
|
|
142
|
+
|
|
143
|
+
async function waitForGSCLoad(page) {
|
|
144
|
+
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
145
|
+
await page.waitForTimeout(2000);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function main() {
|
|
149
|
+
console.log("=".repeat(60));
|
|
150
|
+
console.log("Google Search Console - Sitemap Submission Tool");
|
|
151
|
+
console.log("=".repeat(60));
|
|
152
|
+
|
|
153
|
+
const browser = await chromium.launchPersistentContext(
|
|
154
|
+
'${chrome_profile}',
|
|
155
|
+
{
|
|
156
|
+
headless: HEADLESS,
|
|
157
|
+
channel: 'chrome',
|
|
158
|
+
ignoreDefaultArgs: ['--enable-automation'],
|
|
159
|
+
args: [
|
|
160
|
+
'--disable-blink-features=AutomationControlled',
|
|
161
|
+
'--disable-infobars',
|
|
162
|
+
'--no-first-run',
|
|
163
|
+
'--no-default-browser-check'
|
|
164
|
+
],
|
|
165
|
+
viewport: { width: 1400, height: 900 }
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const page = await browser.newPage();
|
|
170
|
+
const results = { success: [], skipped: [], failed: [] };
|
|
171
|
+
|
|
172
|
+
for (let i = 0; i < DOMAINS.length; i++) {
|
|
173
|
+
const domain = DOMAINS[i];
|
|
174
|
+
console.log(\`\\n\${'─'.repeat(60)}\`);
|
|
175
|
+
console.log(\`[\${i + 1}/\${DOMAINS.length}] \${domain}\`);
|
|
176
|
+
console.log('─'.repeat(60));
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const gscUrl = \`https://search.google.com/search-console/sitemaps?resource_id=sc-domain:\${domain}\`;
|
|
180
|
+
console.log(\`Opening: \${gscUrl}\`);
|
|
181
|
+
await page.goto(gscUrl, { waitUntil: 'domcontentloaded', timeout: TIMEOUT });
|
|
182
|
+
await waitForGSCLoad(page);
|
|
183
|
+
|
|
184
|
+
const pageContent = await page.content();
|
|
185
|
+
if (pageContent.includes("don't have access") || pageContent.includes("Request access")) {
|
|
186
|
+
console.log(\`⏭ No access to \${domain}\`);
|
|
187
|
+
results.failed.push(\`\${domain} (no access)\`);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check for existing sitemap in table
|
|
192
|
+
const sitemapInTable = await page.\$('table:has-text("' + SITEMAP_PATH + '")') ||
|
|
193
|
+
await page.\$('tr:has-text("' + SITEMAP_PATH + '")') ||
|
|
194
|
+
await page.\$('[role="row"]:has-text("' + SITEMAP_PATH + '")') ||
|
|
195
|
+
await page.\$('a:has-text("' + SITEMAP_PATH + '")');
|
|
196
|
+
|
|
197
|
+
if (sitemapInTable) {
|
|
198
|
+
console.log(\`⏭ Sitemap already submitted for \${domain}\`);
|
|
199
|
+
results.skipped.push(domain);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (DRY_RUN) {
|
|
204
|
+
console.log(\`🔍 Would submit sitemap for \${domain} (dry-run)\`);
|
|
205
|
+
results.success.push(\`\${domain} (dry-run)\`);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Find the "Add a new sitemap" input
|
|
210
|
+
console.log("Looking for 'Add a new sitemap' input...");
|
|
211
|
+
const addSitemapSection = await page.\$('text="Add a new sitemap"');
|
|
212
|
+
|
|
213
|
+
let input = null;
|
|
214
|
+
if (addSitemapSection) {
|
|
215
|
+
const container = await addSitemapSection.evaluateHandle(el => {
|
|
216
|
+
let parent = el.parentElement;
|
|
217
|
+
for (let i = 0; i < 5; i++) {
|
|
218
|
+
if (parent && parent.querySelector('input')) {
|
|
219
|
+
return parent;
|
|
220
|
+
}
|
|
221
|
+
parent = parent?.parentElement;
|
|
222
|
+
}
|
|
223
|
+
return parent;
|
|
224
|
+
});
|
|
225
|
+
input = await container.\$('input');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Fallback: find input that's not search
|
|
229
|
+
if (!input) {
|
|
230
|
+
const allInputs = await page.\$\$('input[type="text"]');
|
|
231
|
+
for (const inp of allInputs) {
|
|
232
|
+
const placeholder = await inp.getAttribute('placeholder') || '';
|
|
233
|
+
const ariaLabel = await inp.getAttribute('aria-label') || '';
|
|
234
|
+
if (placeholder.toLowerCase().includes('search') ||
|
|
235
|
+
ariaLabel.toLowerCase().includes('search') ||
|
|
236
|
+
placeholder.toLowerCase().includes('filter')) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
input = inp;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!input) {
|
|
245
|
+
console.log(\`✗ No 'Add sitemap' input found for \${domain}\`);
|
|
246
|
+
await page.screenshot({ path: \`\${SCREENSHOT_DIR}/\${domain.replace(/\\./g, '-')}-error.png\`, fullPage: true });
|
|
247
|
+
results.failed.push(\`\${domain} (no input)\`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Fill with full URL - use domain as-is (don't force www.)
|
|
252
|
+
// GSC accepts both www and non-www depending on how the property is verified
|
|
253
|
+
const fullSitemapUrl = \`https://\${domain}/\${SITEMAP_PATH}\`;
|
|
254
|
+
console.log(\`Found input, filling \${fullSitemapUrl}...\`);
|
|
255
|
+
await input.click();
|
|
256
|
+
await page.waitForTimeout(300);
|
|
257
|
+
await input.fill(fullSitemapUrl);
|
|
258
|
+
await page.waitForTimeout(500);
|
|
259
|
+
|
|
260
|
+
await page.screenshot({ path: \`\${SCREENSHOT_DIR}/\${domain.replace(/\\./g, '-')}-filled.png\`, fullPage: true });
|
|
261
|
+
|
|
262
|
+
// Find and click SUBMIT button relative to input
|
|
263
|
+
console.log("Clicking SUBMIT button...");
|
|
264
|
+
try {
|
|
265
|
+
await page.waitForTimeout(500);
|
|
266
|
+
|
|
267
|
+
const submitBtn = await input.evaluateHandle(el => {
|
|
268
|
+
let parent = el.parentElement;
|
|
269
|
+
for (let i = 0; i < 10; i++) {
|
|
270
|
+
if (!parent) break;
|
|
271
|
+
const btn = parent.querySelector('[role="button"]');
|
|
272
|
+
if (btn && btn.textContent.trim().toUpperCase() === 'SUBMIT') {
|
|
273
|
+
return btn;
|
|
274
|
+
}
|
|
275
|
+
parent = parent.parentElement;
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (submitBtn) {
|
|
281
|
+
await submitBtn.click();
|
|
282
|
+
} else {
|
|
283
|
+
throw new Error("Could not find SUBMIT button near input");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await page.waitForTimeout(3000);
|
|
287
|
+
await waitForGSCLoad(page);
|
|
288
|
+
|
|
289
|
+
await page.screenshot({ path: \`\${SCREENSHOT_DIR}/\${domain.replace(/\\./g, '-')}-submitted.png\`, fullPage: true });
|
|
290
|
+
console.log(\`✓ Submitted sitemap for \${domain}\`);
|
|
291
|
+
results.success.push(domain);
|
|
292
|
+
|
|
293
|
+
} catch (e) {
|
|
294
|
+
console.log(\`Submit failed: \${e.message}\`);
|
|
295
|
+
await page.screenshot({ path: \`\${SCREENSHOT_DIR}/\${domain.replace(/\\./g, '-')}-submit-error.png\`, fullPage: true });
|
|
296
|
+
results.failed.push(\`\${domain} (submit failed)\`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error(\`✗ Error for \${domain}: \${error.message}\`);
|
|
301
|
+
results.failed.push(\`\${domain} (error)\`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await page.waitForTimeout(1000);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Summary
|
|
308
|
+
console.log("\\n" + "=".repeat(60));
|
|
309
|
+
console.log("SUMMARY");
|
|
310
|
+
console.log("=".repeat(60));
|
|
311
|
+
console.log(\`\\n✓ Submitted: \${results.success.length}\`);
|
|
312
|
+
results.success.forEach(d => console.log(\` \${d}\`));
|
|
313
|
+
console.log(\`\\n⏭ Already done: \${results.skipped.length}\`);
|
|
314
|
+
results.skipped.forEach(d => console.log(\` \${d}\`));
|
|
315
|
+
console.log(\`\\n✗ Failed: \${results.failed.length}\`);
|
|
316
|
+
results.failed.forEach(d => console.log(\` \${d}\`));
|
|
317
|
+
|
|
318
|
+
await browser.close();
|
|
319
|
+
|
|
320
|
+
// Exit with error if any failed
|
|
321
|
+
if (results.failed.length > 0) {
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
main().catch(err => {
|
|
327
|
+
console.error(err);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
});
|
|
330
|
+
SCRIPT
|
|
331
|
+
return 0
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
create_status_script() {
|
|
335
|
+
local domain
|
|
336
|
+
domain="$(sanitize_domain "$1")"
|
|
337
|
+
local chrome_profile
|
|
338
|
+
chrome_profile="$(get_chrome_profile_path)"
|
|
339
|
+
|
|
340
|
+
cat > "${GSC_SCRIPT}" << SCRIPT
|
|
341
|
+
import { chromium } from 'playwright';
|
|
342
|
+
|
|
343
|
+
const DOMAIN = "${domain}";
|
|
344
|
+
|
|
345
|
+
async function main() {
|
|
346
|
+
const browser = await chromium.launchPersistentContext(
|
|
347
|
+
'${chrome_profile}',
|
|
348
|
+
{
|
|
349
|
+
headless: false,
|
|
350
|
+
channel: 'chrome',
|
|
351
|
+
ignoreDefaultArgs: ['--enable-automation'],
|
|
352
|
+
args: [
|
|
353
|
+
'--disable-blink-features=AutomationControlled',
|
|
354
|
+
'--disable-infobars',
|
|
355
|
+
'--no-first-run',
|
|
356
|
+
'--no-default-browser-check'
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const page = await browser.newPage();
|
|
362
|
+
const gscUrl = \`https://search.google.com/search-console/sitemaps?resource_id=sc-domain:\${DOMAIN}\`;
|
|
363
|
+
|
|
364
|
+
console.log(\`Checking sitemap status for \${DOMAIN}...\`);
|
|
365
|
+
await page.goto(gscUrl, { waitUntil: 'networkidle', timeout: 60000 });
|
|
366
|
+
await page.waitForTimeout(2000);
|
|
367
|
+
|
|
368
|
+
const pageContent = await page.content();
|
|
369
|
+
|
|
370
|
+
if (pageContent.includes("don't have access")) {
|
|
371
|
+
console.log("❌ No access to this property");
|
|
372
|
+
await browser.close();
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Look for sitemaps in the table
|
|
377
|
+
const sitemapRows = await page.\$\$('tr:has-text("sitemap")');
|
|
378
|
+
|
|
379
|
+
if (sitemapRows.length === 0) {
|
|
380
|
+
console.log("📭 No sitemaps submitted yet");
|
|
381
|
+
} else {
|
|
382
|
+
console.log(\`📋 Found \${sitemapRows.length} sitemap(s):\`);
|
|
383
|
+
for (const row of sitemapRows) {
|
|
384
|
+
const text = await row.textContent();
|
|
385
|
+
console.log(\` • \${text.trim()}\`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
await browser.close();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
main().catch(err => {
|
|
393
|
+
console.error(err);
|
|
394
|
+
process.exit(1);
|
|
395
|
+
});
|
|
396
|
+
SCRIPT
|
|
397
|
+
return 0
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
create_login_script() {
|
|
401
|
+
local chrome_profile
|
|
402
|
+
chrome_profile="$(get_chrome_profile_path)"
|
|
403
|
+
|
|
404
|
+
cat > "${GSC_SCRIPT}" << SCRIPT
|
|
405
|
+
import { chromium } from 'playwright';
|
|
406
|
+
|
|
407
|
+
async function main() {
|
|
408
|
+
console.log("Opening Chrome for Google login...");
|
|
409
|
+
console.log("Please log into your Google account in the browser.");
|
|
410
|
+
console.log("Close the browser when done.");
|
|
411
|
+
|
|
412
|
+
const browser = await chromium.launchPersistentContext(
|
|
413
|
+
'${chrome_profile}',
|
|
414
|
+
{
|
|
415
|
+
headless: false,
|
|
416
|
+
channel: 'chrome',
|
|
417
|
+
ignoreDefaultArgs: ['--enable-automation'],
|
|
418
|
+
args: [
|
|
419
|
+
'--disable-blink-features=AutomationControlled',
|
|
420
|
+
'--disable-infobars',
|
|
421
|
+
'--no-first-run',
|
|
422
|
+
'--no-default-browser-check'
|
|
423
|
+
]
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
const page = await browser.newPage();
|
|
428
|
+
await page.goto('https://search.google.com/search-console', { waitUntil: 'networkidle' });
|
|
429
|
+
|
|
430
|
+
console.log("\\nBrowser opened. Please:");
|
|
431
|
+
console.log("1. Log into your Google account");
|
|
432
|
+
console.log("2. Verify you can see your GSC properties");
|
|
433
|
+
console.log("3. Close the browser when done");
|
|
434
|
+
|
|
435
|
+
// Wait for browser to close
|
|
436
|
+
await new Promise(() => {});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
main().catch(console.error);
|
|
440
|
+
SCRIPT
|
|
441
|
+
return 0
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
run_script() {
|
|
445
|
+
cd "${WORK_DIR}" || return 1
|
|
446
|
+
node "${GSC_SCRIPT}"
|
|
447
|
+
return $?
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
cmd_submit() {
|
|
451
|
+
local domains=()
|
|
452
|
+
local sitemap_path="${DEFAULT_SITEMAP}"
|
|
453
|
+
local dry_run="false"
|
|
454
|
+
local headless="false"
|
|
455
|
+
local timeout="60000"
|
|
456
|
+
local file=""
|
|
457
|
+
|
|
458
|
+
while [[ $# -gt 0 ]]; do
|
|
459
|
+
case "$1" in
|
|
460
|
+
--sitemap)
|
|
461
|
+
if [[ -z "${2:-}" || "$2" == -* ]]; then
|
|
462
|
+
log_error "--sitemap requires a value"
|
|
463
|
+
return 1
|
|
464
|
+
fi
|
|
465
|
+
sitemap_path="$2"
|
|
466
|
+
shift 2
|
|
467
|
+
;;
|
|
468
|
+
--dry-run)
|
|
469
|
+
dry_run="true"
|
|
470
|
+
shift
|
|
471
|
+
;;
|
|
472
|
+
--headless)
|
|
473
|
+
headless="true"
|
|
474
|
+
shift
|
|
475
|
+
;;
|
|
476
|
+
--timeout)
|
|
477
|
+
if [[ -z "${2:-}" || "$2" == -* ]]; then
|
|
478
|
+
log_error "--timeout requires a value"
|
|
479
|
+
return 1
|
|
480
|
+
fi
|
|
481
|
+
if ! [[ "$2" =~ ^[0-9]+$ ]]; then
|
|
482
|
+
log_error "--timeout must be a number (milliseconds)"
|
|
483
|
+
return 1
|
|
484
|
+
fi
|
|
485
|
+
timeout="$2"
|
|
486
|
+
shift 2
|
|
487
|
+
;;
|
|
488
|
+
--file)
|
|
489
|
+
if [[ -z "${2:-}" || "$2" == -* ]]; then
|
|
490
|
+
log_error "--file requires a value"
|
|
491
|
+
return 1
|
|
492
|
+
fi
|
|
493
|
+
file="$2"
|
|
494
|
+
shift 2
|
|
495
|
+
;;
|
|
496
|
+
--skip-existing)
|
|
497
|
+
# Already handled by script logic
|
|
498
|
+
shift
|
|
499
|
+
;;
|
|
500
|
+
-*)
|
|
501
|
+
log_error "Unknown option: $1"
|
|
502
|
+
return 1
|
|
503
|
+
;;
|
|
504
|
+
*)
|
|
505
|
+
# Sanitize domain input
|
|
506
|
+
domains+=("$(sanitize_domain "$1")")
|
|
507
|
+
shift
|
|
508
|
+
;;
|
|
509
|
+
esac
|
|
510
|
+
done
|
|
511
|
+
|
|
512
|
+
# Read domains from file if specified
|
|
513
|
+
if [[ -n "$file" ]]; then
|
|
514
|
+
if [[ ! -f "$file" ]]; then
|
|
515
|
+
log_error "File not found: $file"
|
|
516
|
+
return 1
|
|
517
|
+
fi
|
|
518
|
+
while IFS= read -r line; do
|
|
519
|
+
# Skip empty lines and comments
|
|
520
|
+
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
|
521
|
+
# Sanitize domain from file
|
|
522
|
+
local sanitized
|
|
523
|
+
sanitized="$(sanitize_domain "$line")"
|
|
524
|
+
[[ -n "$sanitized" ]] && domains+=("$sanitized")
|
|
525
|
+
done < "$file"
|
|
526
|
+
fi
|
|
527
|
+
|
|
528
|
+
if [[ ${#domains[@]} -eq 0 ]]; then
|
|
529
|
+
log_error "No domains specified"
|
|
530
|
+
show_help
|
|
531
|
+
return 1
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
# Convert domains array to JSON safely using jq
|
|
535
|
+
local domains_json
|
|
536
|
+
if command -v jq &> /dev/null; then
|
|
537
|
+
domains_json=$(printf '%s\n' "${domains[@]}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
|
538
|
+
else
|
|
539
|
+
# Fallback: manual construction with basic escaping
|
|
540
|
+
domains_json="["
|
|
541
|
+
for i in "${!domains[@]}"; do
|
|
542
|
+
[[ $i -gt 0 ]] && domains_json+=","
|
|
543
|
+
# Escape quotes and backslashes
|
|
544
|
+
local escaped_domain="${domains[$i]//\\/\\\\}"
|
|
545
|
+
escaped_domain="${escaped_domain//\"/\\\"}"
|
|
546
|
+
domains_json+="\"${escaped_domain}\""
|
|
547
|
+
done
|
|
548
|
+
domains_json+="]"
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
ensure_directories
|
|
552
|
+
ensure_playwright || return 1
|
|
553
|
+
|
|
554
|
+
log_info "Submitting sitemaps for ${#domains[@]} domain(s)..."
|
|
555
|
+
[[ "$dry_run" == "true" ]] && log_warn "DRY RUN - no changes will be made"
|
|
556
|
+
|
|
557
|
+
create_submit_script "$domains_json" "$sitemap_path" "$dry_run" "$headless" "$timeout"
|
|
558
|
+
run_script
|
|
559
|
+
return $?
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
cmd_status() {
|
|
563
|
+
local domain="${1:-}"
|
|
564
|
+
|
|
565
|
+
if [[ -z "$domain" ]]; then
|
|
566
|
+
log_error "Domain required"
|
|
567
|
+
return 1
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
ensure_directories
|
|
571
|
+
ensure_playwright || return 1
|
|
572
|
+
|
|
573
|
+
create_status_script "$domain"
|
|
574
|
+
run_script
|
|
575
|
+
return $?
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
cmd_list() {
|
|
579
|
+
# Same as status for now
|
|
580
|
+
cmd_status "$@"
|
|
581
|
+
return $?
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
cmd_login() {
|
|
585
|
+
ensure_directories
|
|
586
|
+
ensure_playwright || return 1
|
|
587
|
+
|
|
588
|
+
log_info "Opening browser for Google login..."
|
|
589
|
+
create_login_script
|
|
590
|
+
run_script
|
|
591
|
+
return $?
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
cmd_setup() {
|
|
595
|
+
log_info "Setting up GSC Sitemap Helper..."
|
|
596
|
+
|
|
597
|
+
ensure_directories
|
|
598
|
+
|
|
599
|
+
# Check Node.js
|
|
600
|
+
if ! command -v node &> /dev/null; then
|
|
601
|
+
log_error "Node.js not found. Please install Node.js first."
|
|
602
|
+
log_info "Install with: brew install node (macOS) or see https://nodejs.org"
|
|
603
|
+
return 1
|
|
604
|
+
fi
|
|
605
|
+
log_success "Node.js $(node --version) found"
|
|
606
|
+
|
|
607
|
+
# Check npm
|
|
608
|
+
if ! command -v npm &> /dev/null; then
|
|
609
|
+
log_error "npm not found. Please install npm."
|
|
610
|
+
return 1
|
|
611
|
+
fi
|
|
612
|
+
log_success "npm $(npm --version) found"
|
|
613
|
+
|
|
614
|
+
# Install Playwright
|
|
615
|
+
log_info "Installing Playwright..."
|
|
616
|
+
cd "${WORK_DIR}" || return 1
|
|
617
|
+
|
|
618
|
+
if [[ ! -f "package.json" ]]; then
|
|
619
|
+
npm init -y > /dev/null 2>&1
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
# NOSONAR - npm scripts required for Playwright browser automation binaries
|
|
623
|
+
npm install playwright > /dev/null 2>&1
|
|
624
|
+
log_success "Playwright installed"
|
|
625
|
+
|
|
626
|
+
# Create config file if it doesn't exist
|
|
627
|
+
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
628
|
+
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
629
|
+
cat > "$CONFIG_FILE" << 'CONFIG'
|
|
630
|
+
{
|
|
631
|
+
"chrome_profile_dir": "~/.aidevops/.agent-workspace/chrome-gsc-profile",
|
|
632
|
+
"default_sitemap_path": "sitemap.xml",
|
|
633
|
+
"screenshot_dir": "/tmp/gsc-screenshots",
|
|
634
|
+
"timeout_ms": 60000,
|
|
635
|
+
"headless": false
|
|
636
|
+
}
|
|
637
|
+
CONFIG
|
|
638
|
+
log_success "Created config file: $CONFIG_FILE"
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
log_success "Setup complete!"
|
|
642
|
+
log_info "Next steps:"
|
|
643
|
+
log_info " 1. Run: gsc-sitemap-helper.sh login"
|
|
644
|
+
log_info " 2. Log into Google in the browser that opens"
|
|
645
|
+
log_info " 3. Close browser when done"
|
|
646
|
+
log_info " 4. Now you can submit sitemaps!"
|
|
647
|
+
return 0
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
# Main
|
|
651
|
+
case "${1:-}" in
|
|
652
|
+
submit)
|
|
653
|
+
shift
|
|
654
|
+
cmd_submit "$@"
|
|
655
|
+
;;
|
|
656
|
+
status)
|
|
657
|
+
shift
|
|
658
|
+
cmd_status "${1:-}"
|
|
659
|
+
;;
|
|
660
|
+
list)
|
|
661
|
+
shift
|
|
662
|
+
cmd_list "${1:-}"
|
|
663
|
+
;;
|
|
664
|
+
login)
|
|
665
|
+
cmd_login
|
|
666
|
+
;;
|
|
667
|
+
setup)
|
|
668
|
+
cmd_setup
|
|
669
|
+
;;
|
|
670
|
+
-h|--help|help|"")
|
|
671
|
+
show_help
|
|
672
|
+
;;
|
|
673
|
+
*)
|
|
674
|
+
log_error "Unknown command: $1"
|
|
675
|
+
show_help
|
|
676
|
+
exit 1
|
|
677
|
+
;;
|
|
678
|
+
esac
|