feat: add direct Sub2API imagegen skill
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: shujkimagegen-api
|
||||
description: Generate or edit images through the repo-managed server6 Sub2API direct endpoint, using the OPENAI_API_KEY from ~/.codex/auth.json and a fixed OpenAI-compatible base URL at https://sub2apicn.shujk.top/v1. Use when image generation should bypass the Cloudflare-proxied sub2apius.shujk.top route, or when the user asks for Shujakuin direct image generation through sub2apicn.
|
||||
---
|
||||
|
||||
# Shujk ImageGen API
|
||||
|
||||
Use this helper when image generation or editing should reach server6 directly instead of passing through Cloudflare.
|
||||
|
||||
## Boundary
|
||||
|
||||
- Do not print, copy, commit, or summarize API keys.
|
||||
- Read only `OPENAI_API_KEY` from `~/.codex/auth.json`; do not read plaintext `.env` files or unrelated secrets.
|
||||
- Keep the durable source at `ai-infra/skills/sources/shujkimagegen-api/`.
|
||||
- Do not place this skill under the infra repo's `.agents/skills/` or manage it through cc-switch WebDAV.
|
||||
- Treat this as a thin wrapper around the system `imagegen` CLI; do not copy or patch the system script.
|
||||
- The current management state is repo source only. Do not create a HOME runtime placement unless the user explicitly requests it.
|
||||
|
||||
## Endpoint
|
||||
|
||||
- Direct origin: `https://sub2apicn.shujk.top`
|
||||
- Exported OpenAI-compatible base URL: `https://sub2apicn.shujk.top/v1`
|
||||
|
||||
The `/v1` suffix is required because `/models` is an application page while `/v1/models` is the authenticated OpenAI-compatible API.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Use the system `imagegen` skill for prompt, composition, and asset decisions.
|
||||
2. Run the repo source wrapper while the skill remains source-only:
|
||||
|
||||
```bash
|
||||
python3 ai-infra/skills/sources/shujkimagegen-api/scripts/shujk_imagegen.py generate \
|
||||
--prompt "..." \
|
||||
--out output/imagegen/example.png
|
||||
```
|
||||
|
||||
3. For edits, pass through the system CLI arguments:
|
||||
|
||||
```bash
|
||||
python3 ai-infra/skills/sources/shujkimagegen-api/scripts/shujk_imagegen.py edit \
|
||||
--image input.png \
|
||||
--prompt "..." \
|
||||
--out output/imagegen/edited.png
|
||||
```
|
||||
|
||||
4. Inspect non-secret configuration with:
|
||||
|
||||
```bash
|
||||
python3 ai-infra/skills/sources/shujkimagegen-api/scripts/shujk_imagegen.py \
|
||||
--print-config-redacted
|
||||
```
|
||||
|
||||
5. Save project-bound assets inside the active workspace and report the output path and final prompt.
|
||||
|
||||
After an explicit HOME runtime placement, use the equivalent path under `~/.agents/skills/shujkimagegen-api/`.
|
||||
|
||||
## Failure Handling
|
||||
|
||||
- If `OPENAI_API_KEY` is absent, report that auth is unavailable without revealing file contents.
|
||||
- If the system imagegen CLI is missing, report its expected path.
|
||||
- If the endpoint rejects a model or option, retry only with an explicitly compatible model or option.
|
||||
- Do not silently fall back to `sub2apius.shujk.top` or another Cloudflare-proxied endpoint.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Shujk ImageGen API"
|
||||
short_description: "通过 sub2apicn 直连入口运行图片生成与编辑"
|
||||
default_prompt: "使用 $shujkimagegen-api 通过 sub2apicn 直连 API 生成或编辑图片。"
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the system imagegen CLI through the server6 direct Sub2API endpoint."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
DEFAULT_ORIGIN = "https://sub2apicn.shujk.top"
|
||||
DEFAULT_BASE_URL = f"{DEFAULT_ORIGIN}/v1"
|
||||
DEFAULT_SYSTEM_IMAGEGEN = (
|
||||
Path(os.environ.get("CODEX_HOME", Path.home() / ".codex"))
|
||||
/ "skills/.system/imagegen/scripts/image_gen.py"
|
||||
)
|
||||
|
||||
|
||||
def die(message: str) -> None:
|
||||
print(f"Error: {message}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def load_auth(path: Path) -> str:
|
||||
if not path.exists():
|
||||
die(f"Codex auth file not found: {path}")
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
die(f"Codex auth file is not valid JSON: {exc}")
|
||||
if not isinstance(data, dict):
|
||||
die("Codex auth file must contain a JSON object.")
|
||||
api_key = data.get("OPENAI_API_KEY")
|
||||
if not isinstance(api_key, str) or not api_key.strip():
|
||||
die("OPENAI_API_KEY is missing from Codex auth.")
|
||||
return api_key.strip()
|
||||
|
||||
|
||||
def print_redacted_config(
|
||||
*,
|
||||
auth_path: Path,
|
||||
system_script: Path,
|
||||
) -> None:
|
||||
api_key = load_auth(auth_path)
|
||||
payload: dict[str, Any] = {
|
||||
"auth_path": str(auth_path),
|
||||
"direct_origin": DEFAULT_ORIGIN,
|
||||
"openai_base_url": DEFAULT_BASE_URL,
|
||||
"openai_api_key": f"<redacted len={len(api_key)}>",
|
||||
"system_imagegen_script": str(system_script),
|
||||
"system_imagegen_exists": system_script.exists(),
|
||||
}
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Run system imagegen CLI through the server6 direct Sub2API endpoint.",
|
||||
allow_abbrev=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--auth-file",
|
||||
type=Path,
|
||||
default=Path.home() / ".codex/auth.json",
|
||||
help="Path to Codex auth JSON. Defaults to ~/.codex/auth.json.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--system-imagegen-script",
|
||||
type=Path,
|
||||
default=DEFAULT_SYSTEM_IMAGEGEN,
|
||||
help="Path to the system imagegen CLI script.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--print-config-redacted",
|
||||
action="store_true",
|
||||
help="Print resolved non-secret config and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"imagegen_args",
|
||||
nargs=argparse.REMAINDER,
|
||||
help="Arguments passed through to the system imagegen CLI.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
auth_path = args.auth_file.expanduser()
|
||||
system_script = args.system_imagegen_script.expanduser()
|
||||
|
||||
if args.print_config_redacted:
|
||||
print_redacted_config(auth_path=auth_path, system_script=system_script)
|
||||
return 0
|
||||
|
||||
if not args.imagegen_args:
|
||||
die("Missing imagegen CLI arguments. Try: generate --prompt ... --out output.png")
|
||||
if not system_script.exists():
|
||||
die(f"System imagegen CLI not found: {system_script}")
|
||||
|
||||
env = os.environ.copy()
|
||||
env["OPENAI_API_KEY"] = load_auth(auth_path)
|
||||
env["OPENAI_BASE_URL"] = DEFAULT_BASE_URL
|
||||
|
||||
command = [sys.executable, str(system_script), *args.imagegen_args]
|
||||
completed = subprocess.run(command, env=env, check=False)
|
||||
return completed.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user