Skip to content

feat: add 宝塔面板 (Baota Panel) CLI harness#324

Open
gyorkluu wants to merge 7 commits into
HKUDS:mainfrom
gyorkluu:feat/baota
Open

feat: add 宝塔面板 (Baota Panel) CLI harness#324
gyorkluu wants to merge 7 commits into
HKUDS:mainfrom
gyorkluu:feat/baota

Conversation

@gyorkluu

@gyorkluu gyorkluu commented Jun 1, 2026

Copy link
Copy Markdown

Description

Adds cli-anything-baota — a CLI harness for 宝塔面板 (Baota Panel), a popular Linux server management panel. Provides agent-native command-line access to sites, SSL, databases, DNS, firewall, cron, files, and panel configuration.

This is a re-submission of #323, now targeting the feat/baota branch as required by CONTRIBUTING.md.

Closes #323

Type of Change

  • New Software CLI (in-repo) — adds a CLI harness inside this monorepo

For New Software CLIs (in-repo)

  • BAOTA.md SOP document exists at baota/agent-harness/BAOTA.md
  • Canonical SKILL.md exists at skills/cli-anything-baota/SKILL.md
  • Packaged compatibility SKILL.md exists at cli_anything/baota/skills/SKILL.md
  • Unit tests at cli_anything/baota/tests/test_core.py are present and pass without backend
  • E2E tests at cli_anything/baota/tests/test_full_e2e.py are present
  • README.md includes the new software (with link to harness directory)
  • registry.json includes an entry with source_url: null (see Contributing guide)
  • repl_skin.py in utils/ is an unmodified copy from the plugin

General Checklist

  • Code follows existing patterns and conventions
  • --json flag is supported on any new commands
  • Commit messages follow the conventional format (feat:, fix:, docs:, test:)
  • I have tested my changes locally

Test Results

============================= 121 passed in 6.33s ==============================

Changes from #323

The previous PR #323 was closed so this submission follows the contribution guide's requirement to develop on a feature branch (feat/baota) rather than main directly. All review feedback from the prior round (P1 files/export shadowing, P2 DNS flag mapping, P1 bridge auto-deploy, P2 cron wildcards, P2 JSON status, P2 REPL shlex, multi-provider DNS support) is already addressed on this branch.

gyorkluu added 5 commits June 1, 2026 13:25
- Full CLI for server management: sites, SSL, databases, DNS, firewall, cron
- Bridge pattern via sudo + panel Python for all operations
- 121 tests (85 unit + 36 E2E), 100% pass rate
- SKILL.md for AI agent discovery
- registry.json entry in devops category
…ildcards, JSON status, E2E module fallback
@github-actions github-actions Bot added new-cli Adds a new CLI or generated harness cli-anything-skill Changes CLI-Anything plugin or skill files cli-anything-hub Changes CLI-Hub, registries, or hub docs labels Jun 1, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3037d7767

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return format_output({'status': False, 'msg': f'Unsupported provider: {provider}'}, use_json)
pdata = {}
for f in prov['fields']:
val = kwargs.get(f.lower())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor DNS credentials passed on CLI

When config dns set dnspod --id ... --token ... is used, baota_cli passes provider-specific keys such as ID/Token (or AccessKeyId/AccessKeySecret) into set_dns_api, but this lookup lowercases the field name before reading kwargs. The explicit CLI credentials are therefore ignored and the command reports “No credentials” unless matching environment variables are set, so the documented config dns set flow cannot configure DNS APIs from its flags.

Useful? React with 👍 / 👎.

Comment on lines +107 to +109
data = call_bridge('list_dns_records', domain=domain)
records = data if isinstance(data, list) else []
return format_output(records, use_json, f'DNS Records for {domain}')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface DNS list errors instead of empty success

When no DNS credentials are configured, the bridge returns an error dict (status: False), but this code coerces any non-list response to [] and format_output then reports a successful empty record list under --json. In that setup-failure scenario, agents and users are told the domain has no records instead of being told to run config dns set, which makes DNS troubleshooting and automation decisions incorrect.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95d2a7adb2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +125 to +126
result = call_bridge('apply_ssl', timeout=120, site_id=site_id, domains=domain_list,
auth_type=auth_type, auth_to='')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass a valid auth_to to SSL validation

When sites ssl apply or sites ssl deploy uses the default --auth dns, this sends auth_to='' into aaPanel's apply_cert_api. I checked the upstream acme_v2 implementation: DNS validation expects auth_to to be either dns for manual DNS or a Provider|account|token API descriptor, and an empty string reaches create_dns_record and fails with dns_name or account or token is empty; HTTP validation similarly expects a site id/path. As a result the default SSL workflow cannot issue certificates even after config dns set succeeds.

Useful? React with 👍 / 👎.

Comment on lines +17 to +20
for name in ['DNSPodDns', 'AliyunDns', 'CloudFlareDns']:
creds = dns_config.get(name, [])
if creds and len(creds) > 0:
return name, creds[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select DNS credentials by domain

When more than one DNS provider is configured, record operations always use the first provider in this hard-coded order, not the provider that owns the requested domain. For example, if DNSPod credentials exist and Cloudflare credentials are later added for a Cloudflare-hosted domain, config dns record add/list/delete example.com ... still sends the request to DNSPod and either mutates the wrong account or returns misleading empty/error results. The bridge should choose credentials based on the domain/provider mapping or accept an explicit provider.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b635b5a2b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +524 to +526
for label, output in results:
click.echo(f'--- {label} ---')
click.echo(output)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit valid JSON from SSL deploy

When --json is used with sites ssl deploy, this loop still prints human-readable section headers and then two separate JSON strings, so the command's overall stdout is not parseable JSON even though automation requested JSON output. This only affects the deploy workflow because it composes the SSL and cron results manually instead of returning one structured object through format_output.

Useful? React with 👍 / 👎.

Comment on lines +95 to +98
if action == 'add':
full_name = kw.get('subdomain', '') + '.' + domain
resp = req.post('https://api.cloudflare.com/client/v4/zones/' + zone_id + '/dns_records',
headers=headers, json={'type': kw.get('type_', 'A'), 'name': full_name, 'content': kw.get('value', ''), 'ttl': int(kw.get('ttl', 600))}, timeout=30).json()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle Cloudflare apex records explicitly

When adding a Cloudflare record for the zone apex, callers normally use @ (or sometimes an empty subdomain), but this concatenation sends @.example.com or .example.com as the Cloudflare record name instead of example.com. That makes root-record creation fail for Cloudflare while DNSPod/Aliyun receive the subdomain separately, so the Cloudflare branch should special-case @/empty to the bare domain.

Useful? React with 👍 / 👎.

@yuh-yang

yuh-yang commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the substantial Baota harness work. The latest review round still has a couple of behavior blockers:

  • sites ssl deploy --json currently prints human-readable section headers plus multiple JSON values, so stdout is not valid JSON even though automation requested JSON mode. Please make the command emit one parseable JSON object for the whole workflow.
  • Cloudflare apex records need explicit handling. Passing @ or an empty subdomain should create/update example.com, not @.example.com or .example.com.

Please add regression coverage for both JSON deploy output and Cloudflare apex record construction. Given this harness touches production panel operations, a concise real-backend smoke/limitations note would also help reviewers understand what was actually exercised.

@omerarslan0

Copy link
Copy Markdown
Collaborator

Registry placement, install metadata, and the harness structure are correct. The blockers are credential handling and two behavior bugs already flagged on HEAD that haven't been fixed.

Security (blocking):

  1. config dns list dumps /www/server/panel/config/dns_mager.conf verbatim — DNSPod ID/Token, Aliyun AccessKeyId/AccessKeySecret, Cloudflare Email/APIKey all land in stdout in cleartext, in both text and --json mode. There's no masking layer anywhere in output_json/output_text/format_output. Mask secrets in both output modes.
  2. system auth dumps /www/server/panel/data/userInfo.json raw — same missing-mask problem.
  3. call_bridge does shutil.copy2(bridge.py → /tmp/baota_bridge.py) then runs it via sudo. /tmp is world-writable and the path is predictable, so a root-executed script staged there is a local TOCTOU/symlink-race privesc vector. Stage into a root-owned, non-world-writable dir (or a locked-down mkstemp dir) and verify ownership before the sudo exec.
  4. sites delete, databases delete, files delete, system stop/restart, firewall-delete, and config password/username execute immediately with no confirmation. For a harness driving production servers as root, gate these behind a --yes/confirmation.

Behavior (already flagged on HEAD 9b635b5, still open):

  1. sites ssl deploy --json still prints --- {label} --- text headers plus two separate JSON blobs, so stdout isn't parseable under --json. Return one structured object via format_output.
  2. Cloudflare apex records: full_name = subdomain + '.' + domain produces .example.com for apex instead of example.com. Special-case empty/@ to the bare domain.

Please add regression tests for 5 and 6 — they were requested last round and still aren't there.

The earlier Codex round (SSL auth_to, swallowed DNS errors, creds lowercasing, provider selection) all check out as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli-anything-hub Changes CLI-Hub, registries, or hub docs cli-anything-skill Changes CLI-Anything plugin or skill files new-cli Adds a new CLI or generated harness

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants