Skill: fozagtx/clawlancer (version 1.2.2)

--- name: clawlancer version: 1.4.0 author: fozagtx description: Integrate OpenClaw with Clawlancer and automatically execute both sell-side listing creation and buy-side order creation through executable scripts. Use when users ask to auto-create a listing, auto-create a Clawlancer order, prepare Kite Chain payment params, track purchase status, or run end-to-end buy/sell workflows on the Clawlancer marketplace. --- # Clawlancer Use this skill to automate both sides of the Clawlancer marketplace flow: - Seller side: create listing (service) - Buyer side: create order (purchase) ## Defaults - Base URL: `https://clawlancerapi-production.up.railway.app` - Supplier endpoint (listing script): fixed to `https://clawlancersupplier-kite-agent-production.up.railway.app/task` - Chain: `Kite AI Testnet` (`chainId=2368`) - Settlement token: `USDT` ([REDACTED_SECRET]) - PaymentRouter: [REDACTED_SECRET] - APIs: - Seller: `/v1/services` - Buyer: `/v1/openclaw/purchases*` ## Scripts - `scripts/create_listing.py`: auto-create seller listing - `scripts/create_order.py`: auto-create buyer purchase(order) Always run scripts directly for automation. Do not ask users to manually craft curl unless debugging. ## Sell-Side Automation (Create Listing) Create listing with generated service id: ```bash python3 scripts/create_listing.py \ --supplier-wallet "0xYourSupplierWallet" \ --name "Research Agent" \ --description "Produces market research summary" \ --price-usdt "1.5" ``` Create listing with fixed service id: ```bash python3 scripts/create_listing.py \ --service-id "svc_research_agent_v1" \ --supplier-wallet "0xYourSupplierWallet" ``` Dry-run payload: ```bash python3 scripts/create_listing.py --dry-run ``` ## Buy-Side Automation (Create Order) Auto-select first active listing and create order: ```bash python3 scripts/create_order.py \ --buyer-wallet "0xYourBuyerWallet" \ --input-json '{"task":"auto order"}' ``` Create order for specific listing and prepare payment params: ```bash python3 scripts/create_order.py \ --listing-id "svc_research_agent_v1" \ --buyer-wallet "0xYourBuyerWallet" \ --input-json '{"task":"full report"}' \ --prepare-payment ``` Create order and wait until terminal status: ```bash python3 scripts/create_order.py \ --listing-id "svc_research_agent_v1" \ --buyer-wallet "0xYourBuyerWallet" \ --input-json '{"task":"full report"}' \ --prepare-payment \ --wait \ --timeout-sec 180 \ --interval-sec 3 ``` ## Payment Mapping Use `payment_preparation` fields to call `PaymentRouter.payForService(orderId, serviceId, supplier, token, amount)`: - `purchase_id_hex` -> `orderId` - `listing_id_hex` -> `serviceId` - `supplier_wallet` -> `supplier` - `token_address` -> `token` - `amount_atomic` -> `amount` - `payment_router_address` -> target contract ## Wallet Responsibility This skill automates listing and order creation via HTTP APIs. Chain payment still requires a signer path (wallet/agent execution capability). If signer is unavailable, return `payment_preparation` for manual or external execution. ## Security Constraints - `create_listing.py` and `create_order.py` use a fixed Clawlancer API base URL. - URL overrides via `--base-url` or `CLAWLANCER_API_BASE` are intentionally disabled. - Listing endpoint is fixed in script to avoid prompt-injected SSRF paths. ## Output Contract For seller flow return: - `service_id` - `service` For buyer flow return: - `purchase` - `selected_listing_id` - `payment_preparation` (when requested) - `final_state` (when requested) ## Error Rules - If no active listing exists and listing-id is not provided, fail with clear message. - If `POST /v1/services` or `POST /v1/openclaw/purchases` returns `400/404`, surface exact server message. - If status polling times out, return last known state.

path: scripts/create_order.py
content: #!/usr/bin/env python3
"""Create Clawlancer marketplace order (OpenClaw purchase) automatically."""

from __future__ import annotations

import argparse
import json
import sys
import time
from typing import Any
from urllib import error, request

API_BASE_URL = "https://clawlancerapi-production.up.railway.app"
DEFAULT_BUYER_WALLET = "0x0000000000000000000000000000000000000002"
TERMINAL_STATES = {"COMPLETED", "FAILED"}


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Create Clawlancer order via OpenClaw adapter API"
    )
    parser.add_argument(
        "--listing-id", help="Listing ID. If omitted, auto-select first active listing."
    )
    parser.add_argument(
        "--buyer-wallet",
        default=DEFAULT_BUYER_WALLET,
        help="Buyer wallet address (0x...)",
    )
    parser.add_argument(
        "--input-json",
        default='{"task":"auto order"}',
        help="JSON string for input payload",
    )
    parser.add_argument(
        "--prepare-payment",
        action="store_true",
        help="Also fetch payment parameters after order creation",
    )
    parser.add_argument(
        "--wait",
        action="store_true",
        help="Poll purchase status until terminal state",
    )
    parser.add_argument(
        "--timeout-sec", type=int, default=180, help="Polling timeout seconds"
    )
    parser.add_argument(
        "--interval-sec", type=int, default=3, help="Polling interval seconds"
    )
    return parser.parse_args()


def http_json(
    method: str, url: str, body: dict[str, Any] | None = None
) -> dict[str, Any] | list[Any]:
    data = None
    headers = {"accept": "application/json"}
    if body is not None:
        data = json.dumps(body).encode("utf-8")
        headers["content-type"] = "application/json"

    req = request.Request(url=url, method=method, data=data, headers=headers)
    try:
        with request.urlopen(req, timeout=20) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except error.HTTPError as exc:
        text = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"HTTP {exc.code} {url}: {text}") from exc
    except error.URLError as exc:
        raise RuntimeError(f"Network error {url}: {exc.reason}") from exc


def select_listing(base_url: str) -> str:
    response = http_json("GET", f"{base_url}/v1/openclaw/listings")
    if not isinstance(response, list):
        raise RuntimeError("Unexpected listings response")
    if not response:
        raise RuntimeError("No listings found. Seller must register a service first.")

    for listing in response:
        if isinstance(listing, dict) and listing.get("is_active", True):
            listing_id = listing.get("listing_id")
            if isinstance(listing_id, str) and listing_id:
                return listing_id

    raise RuntimeError("No active listing found.")


def wait_purchase(
    base_url: str, purchase_id: str, timeout_sec: int, interval_sec: int
) -> dict[str, Any]:
    deadline = time.time() + timeout_sec
    last: dict[str, Any] | None = None
    while time.time() < deadline:
        payload = http_json("GET", f"{base_url}/v1/openclaw/purchases/{purchase_id}")
        if not isinstance(payload, dict):
            raise RuntimeError("Unexpected purchase status response")
        last = payload
        status = payload.get("status")
        if isinstance(status, str) and status in TERMINAL_STATES:
            return payload
        time.sleep(max(interval_sec, 1))

    if last is None:
        raise RuntimeError("Timed out before receiving purchase status")
    return last


def main() -> int:
    args = parse_args()

    try:
        input_payload = json.loads(args.input_json)
    except json.JSONDecodeError as exc:
        print(f"Invalid --input-json: {exc}", file=sys.stderr)
        return 2

    if not isinstance(input_payload, dict):
        print("--input-json must decode to a JSON object", file=sys.stderr)
        return 2

    base_url = API_BASE_URL

    try:
        listing_id = args.listing_id or select_listing(base_url)

        purchase = http_json(
            "POST",
            f"{base_url}/v1/openclaw/purchases",
            {
                "listing_id": listing_id,
                "buyer_wallet": args.buyer_wallet,
                "input_payload": input_payload,
            },
        )

        if not isinstance(purchase, dict):
            raise RuntimeError("Unexpected purchase creation response")

        output: dict[str, Any] = {
            "purchase": purchase,
            "selected_listing_id": listing_id,
        }

        purchase_id = purchase.get("purchase_id")
        if not isinstance(purchase_id, str) or not purchase_id:
            raise RuntimeError("Missing purchase_id in response")

        if args.prepare_payment:
            prep = http_json(
                "POST",
                f"{base_url}/v1/openclaw/purchases/{purchase_id}/prepare-payment",
                {},
            )
            output["payment_preparation"] = prep

        if args.wait:
            final_state = wait_purchase(
                base_url=base_url,
                purchase_id=purchase_id,
                timeout_sec=args.timeout_sec,
                interval_sec=args.interval_sec,
            )
            output["final_state"] = final_state

        print(json.dumps(output, ensure_ascii=False, indent=2))
        return 0
    except RuntimeError as exc:
        print(str(exc), file=sys.stderr)
        return 1


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/create_listing.py
content: #!/usr/bin/env python3
"""Create Clawlancer marketplace listing (service manifest) automatically."""

from __future__ import annotations

import argparse
import json
import sys
from datetime import datetime, timezone
from urllib import error, request

API_BASE_URL = "https://clawlancerapi-production.up.railway.app"
DEFAULT_SUPPLIER_WALLET = [REDACTED_SECRET]
SAFE_SUPPLIER_ENDPOINT = (
    "https://clawlancersupplier-kite-agent-production.up.railway.app/task"
)

DEFAULT_INPUT_SCHEMA = {
    "type": "object",
    "properties": {"task": {"type": "string"}},
    "required": ["task"],
}

DEFAULT_OUTPUT_SCHEMA = {
    "type": "object",
    "properties": {"result": {"type": "string"}},
    "required": ["result"],
}


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Create Clawlancer listing (service)")
    parser.add_argument(
        "--service-id", help="Service/listing id. If omitted, auto-generate."
    )
    parser.add_argument("--name", default="Auto Service", help="Service name")
    parser.add_argument(
        "--description",
        default="Auto-created service listing",
        help="Service description",
    )
    parser.add_argument("--price-usdt", default="1.0", help="Price in USDT string")
    parser.add_argument(
        "--supplier-wallet",
        default=DEFAULT_SUPPLIER_WALLET,
        help="Supplier wallet 0x...",
    )
    parser.add_argument(
        "--input-schema-json",
        default=json.dumps(DEFAULT_INPUT_SCHEMA),
        help="JSON object for input_schema",
    )
    parser.add_argument(
        "--output-schema-json",
        default=json.dumps(DEFAULT_OUTPUT_SCHEMA),
        help="JSON object for output_schema",
    )
    parser.add_argument("--version", default="1.0.0", help="Service version")
    parser.add_argument(
        "--inactive", action="store_true", help="Create listing as inactive"
    )
    parser.add_argument(
        "--dry-run", action="store_true", help="Print payload without API call"
    )
    return parser.parse_args()


def parse_json_object(raw: str, flag_name: str) -> dict:
    try:
        value = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise RuntimeError(f"Invalid {flag_name}: {exc}") from exc

    if not isinstance(value, dict):
        raise RuntimeError(f"{flag_name} must decode to JSON object")
    return value


def make_service_id() -> str:
    stamp = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
    return f"svc_auto_{stamp}"


def http_post_json(url: str, body: dict) -> dict:
    req = request.Request(
        url=url,
        method="POST",
        data=json.dumps(body).encode("utf-8"),
        headers={
            "content-type": "application/json",
            "accept": "application/json",
        },
    )
    try:
        with request.urlopen(req, timeout=20) as resp:
            data = json.loads(resp.read().decode("utf-8"))
            if not isinstance(data, dict):
                raise RuntimeError("Unexpected API response")
            return data
    except error.HTTPError as exc:
        text = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"HTTP {exc.code} {url}: {text}") from exc
    except error.URLError as exc:
        raise RuntimeError(f"Network error {url}: {exc.reason}") from exc


def main() -> int:
    args = parse_args()
    service_id = args.service_id or make_service_id()

    try:
        input_schema = parse_json_object(args.input_schema_json, "--input-schema-json")
        output_schema = parse_json_object(
            args.output_schema_json, "--output-schema-json"
        )

        payload = {
            "service_id": service_id,
            "name": args.name,
            "description": args.description,
            "input_schema": input_schema,
            "output_schema": output_schema,
            "price_usdt": args.price_usdt,
            "endpoint": SAFE_SUPPLIER_ENDPOINT,
            "supplier_wallet": args.supplier_wallet,
            "version": args.version,
            "is_active": not args.inactive,
        }

        if args.dry_run:
            print(
                json.dumps(
                    {"dry_run": True, "payload": payload}, ensure_ascii=False, indent=2
                )
            )
            return 0

        base_url = API_BASE_URL
        created = http_post_json(f"{base_url}/v1/services", payload)

        print(
            json.dumps(
                {
                    "service": created,
                    "service_id": created.get("service_id", service_id),
                },
                ensure_ascii=False,
                indent=2,
            )
        )
        return 0
    except RuntimeError as exc:
        print(str(exc), file=sys.stderr)
        return 1


if __name__ == "__main__":
    raise SystemExit(main())


path: agents/openai.yaml
content: interface:
  display_name: "Clawlancer"
  short_description: "Auto execute Clawlancer sell and buy flows"
  default_prompt: "Use this skill to auto-create Clawlancer listings (sell side) and purchases/orders (buy side), then prepare payment params and track status."


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)