Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# CLAUDE.md
# CLAUDE.md - GoDaddy CLI

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Local Development Ports

This is a command-line application that does not run on a network port.

# GoDaddy CLI Development Guide

## Commands
Expand Down
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pnpm tsx src/index.tsx application <command>

## Features

- **API Access**: Make direct, authenticated requests to any GoDaddy API endpoint
- **Application Management**: Create, view, and release applications
- **Authentication**: Secure OAuth-based authentication with GoDaddy
- **Webhook Management**: List available webhook event types
Expand Down Expand Up @@ -230,6 +231,102 @@ godaddy webhook events # Lists all available webhook event types y
-o, --output <format> # Output format: json or text (default: text)
```

### API Command

The `api` command allows you to make direct, authenticated requests to any GoDaddy API endpoint. This is useful for exploring APIs, debugging, automation scripts, and AI agent integrations.

```bash
# Basic GET request
godaddy api <endpoint>

# Specify HTTP method
godaddy api <endpoint> -X <method> # method: GET, POST, PUT, PATCH, DELETE

# Full options
godaddy api <endpoint>
Options:
-X, --method <method> # HTTP method (default: GET)
-f, --field <key=value> # Add field to request body (can be repeated)
-F, --file <path> # Read request body from JSON file
-H, --header <header> # Add custom header (can be repeated)
-q, --query <path> # Extract value at JSON path
-i, --include # Include response headers in output
```

#### Examples

```bash
# Get current shopper info
godaddy api /v1/shoppers/me

# Get domains list
godaddy api /v1/domains

# Check domain availability (POST with field)
godaddy api /v1/domains/available -X POST -f domain=example.com

# Extract a specific field from the response
godaddy api /v1/shoppers/me -q .shopperId

# Extract nested data
godaddy api /v1/domains -q .domains[0].domain

# Include response headers
godaddy api /v1/shoppers/me -i

# Add custom headers
godaddy api /v1/domains -H "X-Request-Context: cli-test"

# POST with JSON file body
godaddy api /v1/domains/purchase -X POST -F ./domain-request.json

# Multiple fields
godaddy api /v1/domains/contacts -X PUT \
-f firstName=John \
-f lastName=Doe \
-f [email protected]

# Debug mode (shows request/response details)
godaddy --debug api /v1/shoppers/me
```

#### Query Path Syntax

The `-q, --query` option supports simple JSON path expressions:

| Pattern | Description | Example |
|---------|-------------|---------|
| `.key` | Access object property | `.shopperId` |
| `.key.nested` | Access nested property | `.customer.email` |
| `[0]` | Access array index | `[0]` |
| `.key[0]` | Combined access | `.domains[0]` |
| `.key[0].nested` | Complex path | `.domains[0].status` |

#### Authentication

The `api` command uses the same authentication as other CLI commands. You must be logged in:

```bash
# Login first
godaddy auth login

# Then make API calls
godaddy api /v1/shoppers/me
```

#### Common API Endpoints

| Endpoint | Description |
|----------|-------------|
| `/v1/shoppers/me` | Current authenticated shopper |
| `/v1/domains` | List domains |
| `/v1/domains/available` | Check domain availability |
| `/v1/domains/{domain}` | Get specific domain info |
| `/v1/orders` | List orders |
| `/v1/subscriptions` | List subscriptions |

For the complete API reference, visit the [GoDaddy Developer Portal](https://developer.godaddy.com/).

### Actions Commands

```bash
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "@godaddy/cli",
"version": "0.1.0",
"description": "GoDaddy CLI for managing applications and webhooks",
"keywords": ["godaddy", "cli", "developer-tools"],
"main": "./dist/cli.js",
"type": "module",
"bin": {
Expand Down
2 changes: 2 additions & 0 deletions src/cli-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Command } from "commander";
import packageJson from "../package.json";
import { createAuthCommand, createEnvCommand } from "./cli";
import { createActionsCommand } from "./cli/commands/actions";
import { createApiCommand } from "./cli/commands/api";
import { createApplicationCommand } from "./cli/commands/application";
import { createWebhookCommand } from "./cli/commands/webhook";
import { validateEnvironment } from "./core/environment";
Expand Down Expand Up @@ -60,6 +61,7 @@ Example Usage:
});

// Add CLI commands
program.addCommand(createApiCommand());
program.addCommand(createEnvCommand());
program.addCommand(createAuthCommand());
program.addCommand(createActionsCommand());
Expand Down
194 changes: 194 additions & 0 deletions src/cli/commands/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { Command } from "commander";
import {
type HttpMethod,
apiRequest,
parseFields,
parseHeaders,
readBodyFromFile,
} from "../../core/api";

const VALID_METHODS: HttpMethod[] = ["GET", "POST", "PUT", "PATCH", "DELETE"];

/**
* Extract a value from an object using a simple JSON path
* Supports: .key, .key.nested, .key[0], .key[0].nested
*/
function extractPath(obj: unknown, path: string): unknown {
if (!path || path === ".") {
return obj;
}

// Remove leading dot if present
const normalizedPath = path.startsWith(".") ? path.slice(1) : path;
if (!normalizedPath) {
return obj;
}

// Parse path into segments
const segments: (string | number)[] = [];
const regex = /([\w-]+)|\[(\d+)\]/g;
let match: RegExpExecArray | null;

while ((match = regex.exec(normalizedPath)) !== null) {
if (match[1] !== undefined) {
segments.push(match[1]);
} else if (match[2] !== undefined) {
segments.push(Number.parseInt(match[2], 10));
}
}

// Traverse the object
let current: unknown = obj;
for (const segment of segments) {
if (current === null || current === undefined) {
return undefined;
}
if (typeof segment === "number") {
if (!Array.isArray(current)) {
throw new Error(`Cannot index non-array with [${segment}]`);
}
current = current[segment];
} else {
if (typeof current !== "object") {
throw new Error(`Cannot access property "${segment}" on non-object`);
}
current = (current as Record<string, unknown>)[segment];
}
}

return current;
}

export function createApiCommand(): Command {
const api = new Command("api")
.description("Make authenticated requests to the GoDaddy API")
.argument("<endpoint>", "API endpoint (e.g., /v1/domains)")
.option(
"-X, --method <method>",
"HTTP method (GET, POST, PUT, PATCH, DELETE)",
"GET",
)
.option(
"-f, --field <key=value...>",
"Add field to request body (can be repeated)",
)
.option("-F, --file <path>", "Read request body from JSON file")
.option("-H, --header <header...>", "Add custom header (can be repeated)")
.option(
"-q, --query <path>",
"Extract value at JSON path (e.g., .status, .data[0].name)",
)
.option("-i, --include", "Include response headers in output")
.action(async (endpoint: string, options) => {
// Validate HTTP method
const method = options.method.toUpperCase() as HttpMethod;
if (!VALID_METHODS.includes(method)) {
console.error(
`Invalid HTTP method: ${options.method}. Must be one of: ${VALID_METHODS.join(", ")}`,
);
process.exit(1);
}

// Parse fields
let fields: Record<string, string> | undefined;
if (options.field) {
const fieldArray = Array.isArray(options.field)
? options.field
: [options.field];
const fieldsResult = parseFields(fieldArray);
if (!fieldsResult.success) {
console.error(
fieldsResult.error?.userMessage || "Invalid field format",
);
process.exit(1);
}
fields = fieldsResult.data;
}

// Read body from file
let body: string | undefined;
if (options.file) {
const bodyResult = readBodyFromFile(options.file);
if (!bodyResult.success) {
console.error(bodyResult.error?.userMessage || "Failed to read file");
process.exit(1);
}
body = bodyResult.data;
}

// Parse headers
let headers: Record<string, string> | undefined;
if (options.header) {
const headerArray = Array.isArray(options.header)
? options.header
: [options.header];
const headersResult = parseHeaders(headerArray);
if (!headersResult.success) {
console.error(
headersResult.error?.userMessage || "Invalid header format",
);
process.exit(1);
}
headers = headersResult.data;
}

// Get debug flag from parent command
const parentOptions = api.parent?.opts() || {};
const debug = parentOptions.debug || false;

// Make the request
const result = await apiRequest({
endpoint,
method,
fields,
body,
headers,
debug,
});

if (!result.success) {
console.error(result.error?.userMessage || "API request failed");
process.exit(1);
}

const response = result.data;
if (!response) {
console.error("No response data");
process.exit(1);
}

// Include headers if requested
if (options.include) {
console.log(`HTTP/1.1 ${response.status} ${response.statusText}`);
for (const [key, value] of Object.entries(response.headers)) {
console.log(`${key}: ${value}`);
}
console.log("");
}

// Apply query filter if specified
let output = response.data;
if (options.query && output !== undefined) {
try {
output = extractPath(output, options.query);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`Query error: ${message}`);
process.exit(1);
}
}

if (output !== undefined) {
// Output JSON (pretty print objects, raw strings)
if (typeof output === "string") {
console.log(output);
} else {
console.log(JSON.stringify(output, null, 2));
}
}

process.exit(0);
});

return api;
}
1 change: 1 addition & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
*/

export * from "./types";
export { createApiCommand } from "./commands/api";
export { createEnvCommand } from "./commands/env";
export { createAuthCommand } from "./commands/auth";
Loading
Loading