Skip to content
Merged
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
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,61 @@ npx @insforge/cli deployments env delete <id> # delete a variable

---

### Domains — `npx @insforge/cli domains`

Register a domain through the user's Cloudflare account, attach it to the linked InsForge deployment, sync Cloudflare DNS records, and verify SSL/custom domain readiness.

Cloudflare is connected through OAuth and saved locally in `~/.insforge/cloudflare.json`:

```bash
npx @insforge/cli domains cloudflare login
npx @insforge/cli domains cloudflare login --account-id <cloudflare-account-id> # skip account selection
```

The CLI opens Cloudflare in the browser, receives the OAuth callback on
`http://127.0.0.1:8787/callback`, stores the returned Cloudflare tokens locally,
and discovers the Cloudflare account selected during authorization. For
non-browser environments, pass `--skip-browser` and open the printed URL
manually. `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_ACCESS_TOKEN` can override the
local OAuth credentials for automation.

Use the split commands when you want to inspect or resume a workflow:

```bash
npx @insforge/cli domains search my-app
npx @insforge/cli domains search my-app --tlds com,app,dev # optional local filter
npx @insforge/cli domains check my-app.dev
npx @insforge/cli domains buy my-app.dev
npx @insforge/cli domains attach my-app.dev
npx @insforge/cli domains dns sync my-app.dev
npx @insforge/cli domains verify my-app.dev
npx @insforge/cli domains status my-app.dev --cloudflare
```

Cloudflare only allows programmatic registration for TLDs currently supported
by its Registrar API. The CLI surfaces Cloudflare's availability reason when a
TLD is dashboard-only or not supported by the API.

For agent runs, use explicit purchase confirmations. The global `--yes` flag does not bypass domain purchase confirmation:

```bash
npx @insforge/cli domains buy-and-attach my-app.dev \
--confirm-domain my-app.dev \
--confirm-price 10.11 \
--confirm-currency USD \
--confirm-cloudflare-billing \
--confirm-non-refundable \
--json
```

If Cloudflare registration is still in progress, retry with:

```bash
npx @insforge/cli domains resume my-app.dev
```

---

### Payments — `npx @insforge/cli payments`

Manage the payments foundation for the linked InsForge project. Provider-specific commands live under `payments stripe` and `payments razorpay`. These commands are intended for developers and agents configuring provider keys, syncing mirrored provider state, inspecting customers, and managing provider catalog records. Runtime checkout/order/subscription calls should usually be made from the app via the SDK.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@insforge/cli",
"version": "0.1.89",
"version": "0.1.90",
"description": "InsForge CLI - Command line tool for InsForge platform",
"type": "module",
"bin": {
Expand Down
109 changes: 109 additions & 0 deletions src/commands/domains/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { describe, expect, it } from 'vitest';
import {
buildDnsSetupRecords,
confirmPurchase,
hasExplicitPurchaseConfirmation,
type DnsSetupRecord,
} from './index.js';

describe('domains command helpers', () => {
it('builds apex routing and verification DNS records', () => {
const records = buildDnsSetupRecords({
domain: 'example.com',
apexDomain: 'example.com',
verified: false,
misconfigured: false,
cnameTarget: null,
aRecordValue: '216.150.16.1',
verification: [
{
type: 'TXT',
domain: '_vercel.example.com',
value: 'vc-domain-verify=example.com,abc123',
},
],
});

expect(records).toEqual<DnsSetupRecord[]>([
{
type: 'A',
name: 'example.com',
content: '216.150.16.1',
purpose: 'routing',
},
{
type: 'TXT',
name: '_vercel.example.com',
content: 'vc-domain-verify=example.com,abc123',
purpose: 'verification',
},
]);
});

it('builds subdomain CNAME records', () => {
const records = buildDnsSetupRecords({
domain: 'www.example.com',
apexDomain: 'example.com',
verified: false,
misconfigured: false,
cnameTarget: 'cname.vercel-dns.com',
aRecordValue: '216.150.16.1',
verification: [],
});

expect(records).toEqual<DnsSetupRecord[]>([
{
type: 'CNAME',
name: 'www.example.com',
content: 'cname.vercel-dns.com',
purpose: 'routing',
},
]);
});

it('requires all purchase confirmation fields to match exactly', () => {
const candidate = {
name: 'example.dev',
registrable: true,
pricing: {
currency: 'USD',
registration_cost: '10.11',
renewal_cost: '10.11',
},
};

expect(
hasExplicitPurchaseConfirmation('example.dev', candidate, {
confirmDomain: 'example.dev',
confirmPrice: '10.11',
confirmCurrency: 'usd',
confirmCloudflareBilling: true,
confirmNonRefundable: true,
}),
).toBe(true);

expect(
hasExplicitPurchaseConfirmation('example.dev', candidate, {
confirmDomain: 'example.dev',
confirmPrice: '10.10',
confirmCurrency: 'USD',
confirmCloudflareBilling: true,
confirmNonRefundable: true,
}),
).toBe(false);
});

it('rejects non-interactive purchases without explicit confirmation flags', async () => {
await expect(confirmPurchase('example.dev', {
name: 'example.dev',
registrable: true,
pricing: {
currency: 'USD',
registration_cost: '10.11',
renewal_cost: '10.11',
},
}, {})).rejects.toMatchObject({
code: 'DOMAIN_PURCHASE_CONFIRMATION_REQUIRED',
});
});
});
Loading
Loading