@myapihq/cli 1.0.61 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/auth.js +13 -5
- package/dist/commands/config.d.ts +4 -4
- package/dist/commands/config.js +13 -13
- package/dist/commands/domain.d.ts +0 -1
- package/dist/commands/domain.js +10 -24
- package/dist/commands/funnel.js +7 -3
- package/dist/commands/keys.js +2 -2
- package/dist/commands/org.js +8 -4
- package/dist/commands/update.js +1 -1
- package/dist/index.js +6 -6
- package/dist/skills/my-api-hq/README.md +37 -0
- package/dist/skills/my-api-hq/SKILL.md +116 -0
- package/dist/skills/my-api-hq/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-api-hq/make/.gitkeep +0 -0
- package/dist/skills/my-api-hq/n8n/.gitkeep +0 -0
- package/dist/skills/my-api-hq/openapi/.gitkeep +0 -0
- package/dist/skills/my-domain-api/README.md +37 -0
- package/dist/skills/my-domain-api/SKILL.md +83 -0
- package/dist/skills/my-domain-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-domain-api/make/.gitkeep +0 -0
- package/dist/skills/my-domain-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-domain-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/README.md +39 -0
- package/dist/skills/my-funnel-api/SKILL.md +35 -0
- package/dist/skills/my-funnel-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-funnel-api/make/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/openapi/.gitkeep +0 -0
- package/package.json +4 -1
- package/scripts/copy-skills.js +0 -13
- package/src/commands/auth.ts +0 -190
- package/src/commands/billing.ts +0 -92
- package/src/commands/config.ts +0 -71
- package/src/commands/domain.ts +0 -146
- package/src/commands/email.ts +0 -185
- package/src/commands/funnel.ts +0 -122
- package/src/commands/image.ts +0 -85
- package/src/commands/keys.ts +0 -68
- package/src/commands/org.ts +0 -134
- package/src/commands/pixel.ts +0 -62
- package/src/commands/setup.ts +0 -335
- package/src/commands/storage.ts +0 -56
- package/src/commands/update.ts +0 -89
- package/src/commands/url.ts +0 -28
- package/src/commands/webhook.ts +0 -66
- package/src/commands/workflow.ts +0 -102
- package/src/config.ts +0 -123
- package/src/index.ts +0 -200
- package/src/output.ts +0 -49
- package/src/utils.ts +0 -45
- package/thank-you.html +0 -56
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: my-domain-api
|
|
3
|
+
description: >
|
|
4
|
+
Register new domains, check availability and pricing, import existing domains, and manage edge settings. Use this before creating mailboxes or funnels — both require an owned domain.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# MyDomainAPI Skill
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
1. `GET /domain/orgs/{org_id}/list?filter=all` — lists domains owned by your account. `filter` can be `all`, `unassigned`, or `org` (default).
|
|
11
|
+
2. `GET /domain/orgs/{org_id}/check/available/{domain}` — confirm availability and price.
|
|
12
|
+
3. `POST /domain/orgs/{org_id}/register` with `domain` and optional `years`.
|
|
13
|
+
4. Proceed to `my-email-api` for mailboxes or `my-funnel-api` for a website.
|
|
14
|
+
|
|
15
|
+
DNS is fully managed by the platform — enabling seamless email deliverability, tracking pixel, and edge delivery integration. Manual DNS record management is not exposed.
|
|
16
|
+
|
|
17
|
+
## Dependencies & Backlinks
|
|
18
|
+
- **Auth & Billing:** 401/402 → fall back to `my-api-hq`.
|
|
19
|
+
- **Next Steps:** After registration → `my-email-api` for mailboxes or `my-funnel-api` for a website.
|
|
20
|
+
|
|
21
|
+
## Authentication
|
|
22
|
+
`Authorization: Bearer <api_key>` (from `my-api-hq`).
|
|
23
|
+
|
|
24
|
+
## Endpoints
|
|
25
|
+
|
|
26
|
+
### Check Availability
|
|
27
|
+
```
|
|
28
|
+
GET /domain/orgs/{org_id}/check/available/{domain}
|
|
29
|
+
→ { "available": true, "price_cents": 1200 }
|
|
30
|
+
```
|
|
31
|
+
Errors: `400` INVALID_DOMAIN, TLD_NOT_SUPPORTED.
|
|
32
|
+
|
|
33
|
+
### Register Domain
|
|
34
|
+
```
|
|
35
|
+
POST /domain/orgs/{org_id}/register
|
|
36
|
+
{ "domain": "example.com", "years": 1 }
|
|
37
|
+
→ { "domain": "...", "status": "provisioning", "domain_id": "..." }
|
|
38
|
+
```
|
|
39
|
+
Errors: `400` invalid request, INVALID_DOMAIN, TLD_NOT_SUPPORTED · `409` DOMAIN_ALREADY_OWNED, DOMAIN_UNAVAILABLE · `402` INSUFFICIENT_BALANCE (includes `required_cents`) or UPGRADE_REQUIRED (free account) · `403` `already_owned` flag is not permitted.
|
|
40
|
+
|
|
41
|
+
### Import Existing Domain
|
|
42
|
+
```
|
|
43
|
+
POST /domain/orgs/{org_id}/import
|
|
44
|
+
{ "domain": "example.com", "namecheap_api_user": "optional", "namecheap_api_key": "optional" }
|
|
45
|
+
```
|
|
46
|
+
Sets up DNS and email infrastructure automatically. Optionally updates Namecheap NS if credentials are provided.
|
|
47
|
+
Errors: `402` insufficient balance.
|
|
48
|
+
|
|
49
|
+
To use Namecheap automation: go to **Profile > Tools > Namecheap API Access**, generate an API Key, and whitelist the MyAPI-HQ server IP — otherwise the API calls will be rejected.
|
|
50
|
+
|
|
51
|
+
### List & Status
|
|
52
|
+
```
|
|
53
|
+
GET /domain/orgs/{org_id}/list
|
|
54
|
+
GET /domain/orgs/{org_id}/{domain}/status
|
|
55
|
+
```
|
|
56
|
+
Errors (status): `404` DOMAIN_NOT_FOUND.
|
|
57
|
+
|
|
58
|
+
### Assign / Unassign Domain
|
|
59
|
+
```
|
|
60
|
+
POST /domain/orgs/{org_id}/{domain}/assign
|
|
61
|
+
{ "org_id": "<target_org_id>" } // Pass null to unassign
|
|
62
|
+
```
|
|
63
|
+
Associates a domain already in the account with a specific organization, or removes it from its current organization if `org_id` is null.
|
|
64
|
+
Errors: `404` DOMAIN_NOT_FOUND · `422` ORG_NOT_FOUND.
|
|
65
|
+
|
|
66
|
+
### Edge Settings
|
|
67
|
+
|
|
68
|
+
**Update:**
|
|
69
|
+
```
|
|
70
|
+
POST /domain/orgs/{org_id}/{domain}/settings
|
|
71
|
+
{
|
|
72
|
+
"security_level": "essentially_off", // essentially_off | medium | high | under_attack
|
|
73
|
+
"browser_check": "off", // on | off
|
|
74
|
+
"purge_cache": true
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
*To allow AI training bots and crawlers: set `security_level: "essentially_off"` and `browser_check: "off"`.*
|
|
78
|
+
|
|
79
|
+
**Get:**
|
|
80
|
+
```
|
|
81
|
+
GET /domain/orgs/{org_id}/{domain}/settings
|
|
82
|
+
→ { "domain": "...", "security_level": "...", "browser_check": "...", "ai_bots_protection": "disabled", "is_robots_txt_managed": false }
|
|
83
|
+
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# my-funnel-api
|
|
2
|
+
|
|
3
|
+
Host multi-page sites and funnels by pushing raw HTML or using AI to plan, build, and edit complete funnels. Handles routing, edge delivery, webhook wiring, link/workflow verification, and lead management.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Create and host multi-page funnels from raw HTML
|
|
8
|
+
- AI-assisted funnel generation and editing
|
|
9
|
+
- Edge delivery on your domain
|
|
10
|
+
- Lead capture and management
|
|
11
|
+
- Webhook and workflow integration
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Create a funnel page
|
|
17
|
+
curl -sS -X POST https://myfunnelapi.com/funnel/page \
|
|
18
|
+
-H "Authorization: Bearer $MYAPI_KEY" \
|
|
19
|
+
-H "Content-Type: application/json" \
|
|
20
|
+
-d '{
|
|
21
|
+
"org_id": "<org_id>",
|
|
22
|
+
"domain": "yourdomain.com",
|
|
23
|
+
"slug": "/",
|
|
24
|
+
"html": "<html>...</html>"
|
|
25
|
+
}'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Authentication
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Authorization: Bearer <api_key>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires `org_id` and an owned domain from `my-domain-api`.
|
|
35
|
+
|
|
36
|
+
## Links
|
|
37
|
+
|
|
38
|
+
- [Full skill documentation (SKILL.md)](./SKILL.md)
|
|
39
|
+
- [MyFunnelAPI](https://myfunnelapi.com)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: my-funnel-api:funnel
|
|
3
|
+
description: >
|
|
4
|
+
A lean CRUD and CDN Publishing API. Manage funnel configurations, push raw HTML pages, and deploy static assets to the edge KV.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# MyFunnelAPI Skill
|
|
8
|
+
|
|
9
|
+
## 1. Funnel Management (Authenticated)
|
|
10
|
+
These endpoints manage the database records and structural configuration of funnels.
|
|
11
|
+
|
|
12
|
+
- `GET /funnel/orgs/{org_id}/funnels`
|
|
13
|
+
Lists all funnels for the specified organization.
|
|
14
|
+
- `POST /funnel/orgs/{org_id}/funnels`
|
|
15
|
+
Creates a new funnel entry. Expects basic configuration metadata (name, domain, etc.). Body: `{ "domain": "example.com" }`
|
|
16
|
+
- `GET /funnel/orgs/{org_id}/funnels/{id}`
|
|
17
|
+
Retrieves the metadata and configuration details of a specific funnel.
|
|
18
|
+
- `DELETE /funnel/orgs/{org_id}/funnels/{id}`
|
|
19
|
+
Deletes a funnel from the database and automatically purges all of its preview and published pages from the edge KV cache.
|
|
20
|
+
|
|
21
|
+
## 2. Publishing & Edge Deployment (Authenticated)
|
|
22
|
+
These endpoints interact with the edge KV cache to push HTML/JS content to the edge domains. As soon as you push a page, it is live.
|
|
23
|
+
|
|
24
|
+
- `POST /funnel/orgs/{org_id}/funnels/{id}/push-page`
|
|
25
|
+
Deploys raw HTML to a specific slug on the live funnel (e.g., pushing custom HTML to /contact). Body: `{"slug": "/route", "html": "..."}`.
|
|
26
|
+
- `POST /funnel/orgs/{org_id}/funnels/{id}/verify`
|
|
27
|
+
Pre-publish verification. Validates syntax and structure of raw HTML or an existing page slug.
|
|
28
|
+
|
|
29
|
+
## 3. Public Proxies (Unauthenticated)
|
|
30
|
+
These endpoints are called directly by the end-users' browsers (via the deployed static HTML). They do not require API keys. They are stateless and act as routing proxies to the Webhook API.
|
|
31
|
+
|
|
32
|
+
- `POST /funnel/funnels/{id}/submit/{slug...}`
|
|
33
|
+
The endpoint for HTML form submissions. Validates the JSON payload, returns a 200 OK to the browser, and asynchronously POSTs the data to the organization's matching webhook (or fallback webhook).
|
|
34
|
+
- `POST /funnel/funnels/{id}/event`
|
|
35
|
+
The endpoint for analytics and tracking scripts. Proxies click events, pageviews, and pixel tracking data to the configured webhook endpoints. Includes built-in rate limiting (max 60 req/min per funnel).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
package/scripts/copy-skills.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Skills are not bundled in the CLI package for now.
|
|
3
|
-
import { existsSync, rmSync, mkdirSync } from 'fs';
|
|
4
|
-
import { join, dirname } from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
|
|
7
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const dest = join(__dirname, '..', 'src', 'skills');
|
|
9
|
-
|
|
10
|
-
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
11
|
-
mkdirSync(dest, { recursive: true });
|
|
12
|
-
|
|
13
|
-
console.log('copy-skills: no skills bundled.');
|
package/src/commands/auth.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import * as readline from 'readline';
|
|
2
|
-
|
|
3
|
-
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
4
|
-
import { info, success, error, printJson } from '../output.js';
|
|
5
|
-
import { installSkills } from './setup.js';
|
|
6
|
-
import { hq } from '@myapihq/sdk';
|
|
7
|
-
|
|
8
|
-
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
9
|
-
|
|
10
|
-
function ask(rl: readline.Interface, q: string): Promise<string> {
|
|
11
|
-
return new Promise(resolve => rl.question(q, resolve));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function yn(answer: string, defaultYes = true): boolean {
|
|
15
|
-
const t = answer.trim().toLowerCase();
|
|
16
|
-
if (t === '') return defaultYes;
|
|
17
|
-
return t === 'y' || t === 'yes';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function post(path: string, body: unknown, apiKey?: string): Promise<unknown> {
|
|
21
|
-
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
22
|
-
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
23
|
-
const res = await fetch(`${API_BASE}${path}`, {
|
|
24
|
-
method: 'POST',
|
|
25
|
-
headers,
|
|
26
|
-
body: JSON.stringify(body),
|
|
27
|
-
});
|
|
28
|
-
const json = await res.json() as { data?: unknown; error?: string };
|
|
29
|
-
if (!res.ok) throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
30
|
-
return json.data ?? json;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function patch(path: string, body: unknown, apiKey: string): Promise<unknown> {
|
|
34
|
-
const res = await fetch(`${API_BASE}${path}`, {
|
|
35
|
-
method: 'PATCH',
|
|
36
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
37
|
-
body: JSON.stringify(body),
|
|
38
|
-
});
|
|
39
|
-
const json = await res.json() as { data?: unknown; error?: string };
|
|
40
|
-
if (!res.ok) throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
41
|
-
return json.data ?? json;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// myapi auth link — upgrade anonymous session to registered account.
|
|
45
|
-
export async function link(flags: Record<string, string | boolean> = {}, emailArg?: string) {
|
|
46
|
-
if (flags.help) {
|
|
47
|
-
info('Usage: myapi auth link [email]\n\nAttaches a verified email to your current anonymous account, converting it into\na registered account. A one-time code will be sent to the email to confirm.\n\nIf that email already belongs to an existing MyAPI account, you will be signed\ninto that account instead — it is added as a second active session alongside\nyour current one.\n\nTo create a brand-new account from scratch, use: myapi auth setup');
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const config = loadConfig();
|
|
51
|
-
if (!config?.api_key) error('Not configured. Run: myapi auth setup');
|
|
52
|
-
if (!config!.is_anonymous) error('Already registered. Use your existing account.');
|
|
53
|
-
|
|
54
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
55
|
-
try {
|
|
56
|
-
const email = emailArg || (await ask(rl, '› Email? ')).trim();
|
|
57
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) error(`Invalid email address: "${email}".`);
|
|
58
|
-
|
|
59
|
-
let upgradeOk = true;
|
|
60
|
-
try {
|
|
61
|
-
await patch('/hq/account/upgrade', { email }, config!.api_key);
|
|
62
|
-
} catch (err: any) {
|
|
63
|
-
if (err.message !== 'EMAIL_TAKEN') throw err;
|
|
64
|
-
upgradeOk = false;
|
|
65
|
-
info(`› That email already has an account — accounts cannot be merged.`);
|
|
66
|
-
info(`› Your anonymous account will be kept and you can switch back to it anytime.`);
|
|
67
|
-
const ans = (await ask(rl, '› Sign in and add it as a second account? (Y/n) ')).trim();
|
|
68
|
-
if (!yn(ans)) return;
|
|
69
|
-
await post('/hq/account/send-code', { email });
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
info(`› Sent a code to ${email} · paste it below`);
|
|
73
|
-
|
|
74
|
-
const code = (await ask(rl, '› Code? ')).trim();
|
|
75
|
-
const data = await post('/hq/account/verify-code', { email, code }) as {
|
|
76
|
-
api_key: string;
|
|
77
|
-
account_id: string;
|
|
78
|
-
default_org: string;
|
|
79
|
-
default_funnel: string;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const skillsFromFlag = flags['install-skills'] ? true : flags['no-skills'] ? false : null;
|
|
83
|
-
const wantsSkills = skillsFromFlag !== null ? skillsFromFlag : yn((await ask(rl, '› Install the MyAPI skills pack? (Y/n) ')).trim());
|
|
84
|
-
|
|
85
|
-
if (upgradeOk) {
|
|
86
|
-
saveConfig({
|
|
87
|
-
...config!,
|
|
88
|
-
api_key: data.api_key,
|
|
89
|
-
account_id: data.account_id,
|
|
90
|
-
email,
|
|
91
|
-
default_org: data.default_org || config!.default_org,
|
|
92
|
-
default_funnel: data.default_funnel || config!.default_funnel,
|
|
93
|
-
is_anonymous: false,
|
|
94
|
-
skills_installed: wantsSkills,
|
|
95
|
-
});
|
|
96
|
-
success(`› Welcome! Account upgraded · ${email}`);
|
|
97
|
-
} else {
|
|
98
|
-
const idx = addAccount({
|
|
99
|
-
api_key: data.api_key,
|
|
100
|
-
account_id: data.account_id,
|
|
101
|
-
email,
|
|
102
|
-
default_org: data.default_org,
|
|
103
|
-
default_funnel: data.default_funnel,
|
|
104
|
-
is_anonymous: false,
|
|
105
|
-
skills_installed: wantsSkills,
|
|
106
|
-
});
|
|
107
|
-
success(`› Signed in · ${email} (account #${idx + 1})`);
|
|
108
|
-
info(`› Use "myapi auth switch" to toggle between accounts.`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (wantsSkills) await installSkills();
|
|
112
|
-
} finally {
|
|
113
|
-
rl.close();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// myapi auth whoami — show current session info.
|
|
118
|
-
export async function whoami(flags: Record<string, string | boolean> = {}) {
|
|
119
|
-
if (flags.help) {
|
|
120
|
-
info('Usage: myapi auth whoami [--json]\n\nDisplays the active account details: email, account ID, default org, default funnel, account type, and current balance.\n\nFlags:\n --json Output raw JSON');
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const config = loadConfig();
|
|
124
|
-
if (!config?.api_key) error('Not configured. Run: myapi auth setup');
|
|
125
|
-
|
|
126
|
-
let balance: { balance_display: string; credits_display: string } | null = null;
|
|
127
|
-
try {
|
|
128
|
-
balance = await hq.getBalance(config!.api_key);
|
|
129
|
-
} catch {}
|
|
130
|
-
|
|
131
|
-
if (flags.json) {
|
|
132
|
-
printJson({
|
|
133
|
-
email: config!.email ?? null,
|
|
134
|
-
account_id: config!.account_id,
|
|
135
|
-
default_org: config!.default_org ?? null,
|
|
136
|
-
default_funnel: config!.default_funnel ?? null,
|
|
137
|
-
is_anonymous: config!.is_anonymous ?? false,
|
|
138
|
-
balance: balance?.balance_display ?? null,
|
|
139
|
-
credits: balance?.credits_display ?? null,
|
|
140
|
-
});
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (config!.email) info(`Email: ${config!.email}`);
|
|
145
|
-
info(`Account: ${config!.account_id}`);
|
|
146
|
-
info(`Org: ${config!.default_org ?? '(none)'}`);
|
|
147
|
-
info(`Funnel: ${config!.default_funnel ?? '(none)'}`);
|
|
148
|
-
info(`Type: ${config!.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
149
|
-
info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display} (not usable for domains)` : '(unavailable)'}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// myapi auth switch [index] — switch between saved accounts.
|
|
153
|
-
export async function switchCmd(flags: Record<string, string | boolean> = {}, indexArg?: string) {
|
|
154
|
-
if (flags.help) {
|
|
155
|
-
info('Usage: myapi auth switch [index]\n\nSwitches the active account. Pass an account index to skip the prompt.\n\nExample:\n myapi auth switch 2');
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const accounts = listAccounts();
|
|
159
|
-
if (accounts.length === 0) error('No accounts configured. Run: myapi auth setup');
|
|
160
|
-
if (accounts.length === 1) {
|
|
161
|
-
info(`Only one account configured: ${accounts[0].email ?? accounts[0].account_id}`);
|
|
162
|
-
info('To add another account, run: myapi auth setup');
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
info('Accounts:');
|
|
167
|
-
for (const a of accounts) {
|
|
168
|
-
const label = a.email ?? (a.is_anonymous ? `anonymous · ${a.account_id.slice(0, 8)}` : a.account_id.slice(0, 8));
|
|
169
|
-
const marker = a.active ? ' ◀ active' : '';
|
|
170
|
-
info(` ${a.index + 1}. ${label}${marker}`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
let idxStr = indexArg ?? '';
|
|
174
|
-
if (!idxStr) {
|
|
175
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
176
|
-
try {
|
|
177
|
-
idxStr = (await ask(rl, `› Switch to account (1-${accounts.length})? `)).trim();
|
|
178
|
-
} finally {
|
|
179
|
-
rl.close();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const idx = parseInt(idxStr, 10) - 1;
|
|
183
|
-
if (isNaN(idx) || idx < 0 || idx >= accounts.length) {
|
|
184
|
-
error('Invalid selection.');
|
|
185
|
-
}
|
|
186
|
-
if (switchAccount(idx)) {
|
|
187
|
-
const a = accounts[idx];
|
|
188
|
-
success(`› Switched to ${a.email ?? a.account_id}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
package/src/commands/billing.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import * as readline from 'readline';
|
|
2
|
-
import { hq } from '@myapihq/sdk';
|
|
3
|
-
import { requireConfig } from '../config.js';
|
|
4
|
-
import { success, error, printTable, info, printJson } from '../output.js';
|
|
5
|
-
import { formatDate } from '../utils.js';
|
|
6
|
-
|
|
7
|
-
export async function balance(flags: Record<string, string | boolean>) {
|
|
8
|
-
if (flags.help) {
|
|
9
|
-
info('Usage: myapi billing balance [--json]\n\nShows your current account balance, credits, and payment method status.\n\nFlags:\n --json Output raw JSON');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const config = requireConfig();
|
|
13
|
-
const result = await hq.getBalance(config.api_key);
|
|
14
|
-
|
|
15
|
-
if (flags.json) {
|
|
16
|
-
printJson({
|
|
17
|
-
balance: result.balance_display,
|
|
18
|
-
credits: result.credits_display,
|
|
19
|
-
has_payment_method: result.has_payment_method,
|
|
20
|
-
});
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const pm = result.has_payment_method ? 'yes' : 'no';
|
|
25
|
-
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
26
|
-
info(`Account: ${accountType}`);
|
|
27
|
-
info(`Balance: ${result.balance_display} | Credits: ${result.credits_display} (not usable for domains) | Payment method: ${pm}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function history(flags: Record<string, string | boolean>) {
|
|
31
|
-
if (flags.help) {
|
|
32
|
-
info('Usage: myapi billing history [--json]\n\nShows your recent billing transactions and top-ups.\n\nFlags:\n --json Output raw JSON');
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const config = requireConfig();
|
|
36
|
-
const items = await hq.getBillingHistory(config.api_key);
|
|
37
|
-
|
|
38
|
-
if (flags.json) {
|
|
39
|
-
printJson(items);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (items.length === 0) {
|
|
44
|
-
info('No transactions yet.');
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const formattedItems = items.map(item => ({
|
|
49
|
-
Type: item.type || 'unknown',
|
|
50
|
-
Amount: item.amount_display,
|
|
51
|
-
Status: item.status ? item.status.charAt(0).toUpperCase() + item.status.slice(1) : '',
|
|
52
|
-
Date: formatDate(item.created_at),
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
printTable(formattedItems as unknown as Record<string, unknown>[]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function topup(amountStr: string, flags: Record<string, string | boolean>) {
|
|
59
|
-
if (flags.help) {
|
|
60
|
-
info('Usage: myapi billing topup <amount>\n\nAmount is in whole dollars (e.g. "10" charges $10).\nConfirmation is required for amounts of $50 or more. Use --yes to skip it.\nExample: myapi billing topup 10\n\nFlags:\n --yes Skip confirmation prompt for large amounts');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const amount = Math.round(parseFloat(amountStr));
|
|
64
|
-
if (!amountStr || isNaN(amount) || amount <= 0) {
|
|
65
|
-
error("Amount must be a positive whole number of dollars (e.g. myapi billing topup 10)");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!flags.yes && !flags.y && amount >= 50) {
|
|
70
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
71
|
-
const ans = await new Promise<string>(resolve => rl.question(`› Charge $${amount} to your saved payment method? (y/N) `, resolve));
|
|
72
|
-
rl.close();
|
|
73
|
-
if (ans.trim().toLowerCase() !== 'y' && ans.trim().toLowerCase() !== 'yes') {
|
|
74
|
-
info('Cancelled.');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const config = requireConfig();
|
|
80
|
-
const result = await hq.topUp(config.api_key, amount);
|
|
81
|
-
success(`Top up successful! New balance: ${result.new_balance_display}`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function setup(flags: Record<string, string | boolean>) {
|
|
85
|
-
if (flags.help) {
|
|
86
|
-
info('Usage: myapi billing setup\n\nGenerates a secure checkout link to add or update your payment method.');
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const config = requireConfig();
|
|
90
|
-
const result = await hq.setupPayment(config.api_key);
|
|
91
|
-
success(`Open this URL in your browser to set up payment:\n${result.url}`);
|
|
92
|
-
}
|
package/src/commands/config.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig, saveConfig } from '../config.js';
|
|
3
|
-
import { success, error, info } from '../output.js';
|
|
4
|
-
|
|
5
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
|
-
|
|
7
|
-
export async function setOrg(id: string, flags: Record<string, string | boolean>) {
|
|
8
|
-
if (!id || flags.help) {
|
|
9
|
-
info("Usage: myapi config set-org <id>\n\nSets a default organization ID for subsequent commands.");
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
if (!UUID_RE.test(id)) error(`Invalid organization ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
13
|
-
const config = requireConfig();
|
|
14
|
-
info('› Validating…');
|
|
15
|
-
const org = await hq.getOrg(config.api_key, id);
|
|
16
|
-
config.default_org = org.id;
|
|
17
|
-
saveConfig(config);
|
|
18
|
-
success(`Default organization set to: ${org.name || org.id}`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function setFunnel(id: string, flags: Record<string, string | boolean>) {
|
|
22
|
-
if (!id || flags.help) {
|
|
23
|
-
info("Usage: myapi config set-funnel <id>\n\nSets a default funnel ID for subsequent commands.");
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (!UUID_RE.test(id)) error(`Invalid funnel ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
27
|
-
const config = requireConfig();
|
|
28
|
-
const orgId = config.default_org;
|
|
29
|
-
if (!orgId) error('No default organization set. Run: myapi auth config set-org <id>');
|
|
30
|
-
info('› Validating…');
|
|
31
|
-
const funnel = await sdkFunnel.getFunnel(config.api_key, orgId, id);
|
|
32
|
-
config.default_funnel = funnel.id;
|
|
33
|
-
saveConfig(config);
|
|
34
|
-
success(`Default funnel set to: ${funnel.id}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function setDomain(domain: string, flags: Record<string, string | boolean>) {
|
|
38
|
-
if (!domain || flags.help) {
|
|
39
|
-
info("Usage: myapi config set-domain <domain>\n\nSets a default domain for subsequent commands.");
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const config = requireConfig();
|
|
43
|
-
config.default_domain = domain;
|
|
44
|
-
saveConfig(config);
|
|
45
|
-
success(`Default domain set to: ${domain}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function view(flags: Record<string, string | boolean>) {
|
|
49
|
-
if (flags.help) {
|
|
50
|
-
info("Usage: myapi config view\n\nViews current config settings.");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const config = requireConfig();
|
|
54
|
-
info(`Default Org: ${config.default_org || 'Not set'}`);
|
|
55
|
-
info(`Default Funnel: ${config.default_funnel || 'Not set'}`);
|
|
56
|
-
info(`Default Domain: ${config.default_domain || 'Not set'}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
60
|
-
const via = (flags._via as string) || 'auth config';
|
|
61
|
-
if (!subcommand || (flags.help && !subcommand)) {
|
|
62
|
-
info(`Usage: myapi ${via} <subcommand>\n\nSubcommands:\n view View current config\n set-org Set default organization\n set-funnel Set default funnel\n set-domain Set default domain`);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (subcommand === 'view') await view(flags);
|
|
67
|
-
else if (subcommand === 'set-org') await setOrg(args[0], flags);
|
|
68
|
-
else if (subcommand === 'set-funnel') await setFunnel(args[0], flags);
|
|
69
|
-
else if (subcommand === 'set-domain') await setDomain(args[0], flags);
|
|
70
|
-
else error(`Unknown subcommand: ${subcommand}. Run "myapi config --help" for a list of valid subcommands.`);
|
|
71
|
-
}
|