@myapihq/cli 1.1.0-wip.5 → 1.2.0
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/{email.d.ts → audience.d.ts} +2 -0
- package/dist/commands/audience.js +245 -0
- package/dist/commands/auth.d.ts +5 -2
- package/dist/commands/auth.js +133 -5
- package/dist/commands/billing.d.ts +2 -0
- package/dist/commands/billing.js +18 -1
- package/dist/commands/company.d.ts +6 -0
- package/dist/commands/company.js +149 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +20 -0
- package/dist/commands/crm/companies.d.ts +6 -0
- package/dist/commands/crm/companies.js +193 -0
- package/dist/commands/crm/contacts.d.ts +6 -0
- package/dist/commands/crm/contacts.js +241 -0
- package/dist/commands/crm/index.d.ts +6 -0
- package/dist/commands/crm/index.js +74 -0
- package/dist/commands/database.d.ts +6 -0
- package/dist/commands/database.js +259 -0
- package/dist/commands/domain.d.ts +3 -0
- package/dist/commands/domain.js +67 -1
- package/dist/commands/email/campaign.d.ts +2 -0
- package/dist/commands/email/campaign.js +34 -1
- package/dist/commands/email/index.d.ts +2 -0
- package/dist/commands/email/index.js +9 -0
- package/dist/commands/email/mailbox.d.ts +2 -0
- package/dist/commands/email/mailbox.js +5 -0
- package/dist/commands/email/message.d.ts +2 -0
- package/dist/commands/email/message.js +8 -0
- package/dist/commands/email/template.d.ts +2 -0
- package/dist/commands/email/template.js +54 -0
- package/dist/commands/email/verify.d.ts +7 -0
- package/dist/commands/email/verify.js +52 -0
- package/dist/commands/email/warmup.d.ts +2 -0
- package/dist/commands/email/warmup.js +7 -0
- package/dist/commands/funnel.d.ts +3 -0
- package/dist/commands/funnel.js +31 -0
- package/dist/commands/image.d.ts +2 -0
- package/dist/commands/image.js +43 -9
- package/dist/commands/keys.d.ts +2 -0
- package/dist/commands/keys.js +5 -0
- package/dist/commands/llm.d.ts +6 -0
- package/dist/commands/llm.js +156 -0
- package/dist/commands/org.d.ts +3 -0
- package/dist/commands/org.js +55 -0
- package/dist/commands/people.d.ts +6 -0
- package/dist/commands/people.js +136 -0
- package/dist/commands/pixel.d.ts +11 -3
- package/dist/commands/pixel.js +93 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +57 -5
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.js +154 -0
- package/dist/commands/storage.d.ts +2 -0
- package/dist/commands/storage.js +9 -1
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +3 -0
- package/dist/commands/url.d.ts +7 -2
- package/dist/commands/url.js +6 -0
- package/dist/commands/verify.d.ts +7 -0
- package/dist/commands/verify.js +56 -0
- package/dist/commands/webhook.d.ts +2 -0
- package/dist/commands/webhook.js +31 -6
- package/dist/commands/workflow.d.ts +2 -0
- package/dist/commands/workflow.js +19 -1
- package/dist/completion.d.ts +3 -0
- package/dist/completion.js +78 -0
- package/dist/config.d.ts +2 -0
- package/dist/exposes.d.ts +2 -0
- package/dist/exposes.js +13 -0
- package/dist/exposes.test.js +99 -0
- package/dist/flags.test.js +84 -0
- package/dist/index.js +106 -14
- package/dist/prompt.d.ts +15 -0
- package/dist/prompt.js +27 -0
- package/dist/registrant.d.ts +6 -0
- package/dist/registrant.js +155 -0
- package/dist/services-sync.test.d.ts +1 -0
- package/dist/services-sync.test.js +32 -0
- package/dist/skills/my-api-hq/SKILL.md +51 -45
- package/dist/skills/my-crm-api/README.md +42 -0
- package/dist/skills/my-crm-api/SKILL.md +165 -0
- package/dist/skills/my-crm-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-database-api/README.md +41 -0
- package/dist/skills/my-database-api/SKILL.md +107 -0
- package/dist/skills/my-database-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-domain-api/SKILL.md +53 -26
- package/dist/skills/my-email-verify-api/README.md +33 -0
- package/dist/skills/my-email-verify-api/SKILL.md +91 -0
- package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-funnel-api/SKILL.md +41 -37
- package/dist/skills/my-image-api/SKILL.md +48 -44
- package/dist/skills/my-llm-api/README.md +35 -0
- package/dist/skills/my-llm-api/SKILL.md +125 -0
- package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-storage-api/README.md +1 -1
- package/dist/skills/my-storage-api/SKILL.md +44 -54
- package/dist/skills/my-webhook-api/README.md +1 -1
- package/dist/skills/my-webhook-api/SKILL.md +48 -92
- package/dist/skills/my-workflow-api/SKILL.md +46 -115
- package/dist/telemetry.d.ts +12 -0
- package/dist/telemetry.js +80 -0
- package/dist/telemetry.test.d.ts +1 -0
- package/dist/telemetry.test.js +101 -0
- package/package.json +13 -8
- package/dist/commands/email.js +0 -586
- package/dist/skills/my-email-api/README.md +0 -45
- package/dist/skills/my-email-api/SKILL.md +0 -104
- package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +0 -6
- package/dist/skills/my-email-api/make/.gitkeep +0 -0
- package/dist/skills/my-email-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-email-api/openapi/.gitkeep +0 -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-image-api/make/.gitkeep +0 -0
- package/dist/skills/my-image-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-storage-api/make/.gitkeep +0 -0
- package/dist/skills/my-storage-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-webhook-api/make/.gitkeep +0 -0
- package/dist/skills/my-webhook-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-workflow-api/make/.gitkeep +0 -0
- package/dist/skills/my-workflow-api/n8n/.gitkeep +0 -0
- package/dist/utils.test.js +0 -48
- /package/dist/{utils.test.d.ts → exposes.test.d.ts} +0 -0
- /package/dist/skills/{my-api-hq/make → my-crm-api/openapi}/.gitkeep +0 -0
- /package/dist/skills/{my-api-hq/n8n → my-database-api/openapi}/.gitkeep +0 -0
- /package/dist/skills/{my-domain-api/make → my-email-verify-api/openapi}/.gitkeep +0 -0
- /package/dist/skills/{my-domain-api/n8n → my-llm-api/openapi}/.gitkeep +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { FlagSchema } from '../flags.js';
|
|
2
2
|
import { type Flags } from '../helpers.js';
|
|
3
|
+
import type { Exposes } from '../exposes.js';
|
|
4
|
+
export declare const EXPOSES: Exposes;
|
|
3
5
|
export declare const SCHEMA: FlagSchema;
|
|
4
6
|
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { audience as sdkAudience } from '@myapihq/sdk';
|
|
2
|
+
import { requireConfig } from '../config.js';
|
|
3
|
+
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { requireOrg, requireArg } from '../helpers.js';
|
|
5
|
+
export const EXPOSES = [
|
|
6
|
+
'POST /audience/orgs/{org_id}/audiences',
|
|
7
|
+
'GET /audience/orgs/{org_id}/audiences',
|
|
8
|
+
'GET /audience/orgs/{org_id}/audiences/{audience_id}',
|
|
9
|
+
'PATCH /audience/orgs/{org_id}/audiences/{audience_id}',
|
|
10
|
+
'DELETE /audience/orgs/{org_id}/audiences/{audience_id}',
|
|
11
|
+
'GET /audience/orgs/{org_id}/audiences/{audience_id}/members',
|
|
12
|
+
'POST /audience/orgs/{org_id}/audiences/{audience_id}/refresh',
|
|
13
|
+
];
|
|
14
|
+
export const SCHEMA = {
|
|
15
|
+
org: 'string',
|
|
16
|
+
name: 'string',
|
|
17
|
+
description: 'string',
|
|
18
|
+
source: 'string',
|
|
19
|
+
filter: 'string',
|
|
20
|
+
limit: 'number',
|
|
21
|
+
offset: 'number',
|
|
22
|
+
};
|
|
23
|
+
function parseFilter(raw) {
|
|
24
|
+
if (typeof raw !== 'string' || raw.trim() === '') {
|
|
25
|
+
error('Missing --filter. Pass a JSON object, e.g.\n --filter \'{"industry":["saas"],"seniority":["vp","c_level"]}\'');
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
30
|
+
error('--filter must be a JSON object (e.g. {"industry":["saas"]}).');
|
|
31
|
+
}
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
error(`--filter is not valid JSON: ${e?.message ?? e}`);
|
|
36
|
+
throw e; // unreachable; error() exits
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function summarizeAudience(a) {
|
|
40
|
+
return {
|
|
41
|
+
id: a.id,
|
|
42
|
+
name: a.name,
|
|
43
|
+
source: a.source,
|
|
44
|
+
members: a.member_count,
|
|
45
|
+
created_at: a.created_at,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function create(nameArg, flags) {
|
|
49
|
+
const config = requireConfig();
|
|
50
|
+
const orgId = requireOrg(flags, config, 'myapi audience create <name> --source <people|company> --filter <json>');
|
|
51
|
+
const name = nameArg || flags.name;
|
|
52
|
+
if (!name)
|
|
53
|
+
error('Missing required argument <name>.\nUsage: myapi audience create <name> --source <people|company> --filter <json>');
|
|
54
|
+
const source = flags.source;
|
|
55
|
+
if (source !== 'people' && source !== 'company') {
|
|
56
|
+
error(`--source must be "people" or "company" (got: ${source ?? '<missing>'}).`);
|
|
57
|
+
}
|
|
58
|
+
const filter = parseFilter(flags.filter);
|
|
59
|
+
const description = typeof flags.description === 'string' ? flags.description : undefined;
|
|
60
|
+
const res = await sdkAudience.createAudience(config.api_key, orgId, {
|
|
61
|
+
name, source: source, filter, description,
|
|
62
|
+
});
|
|
63
|
+
if (flags.json) {
|
|
64
|
+
printJson(res);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
success(`Audience created! ID: ${res.id}\nMembers: ${res.member_count}\nSource: ${res.source}`);
|
|
68
|
+
}
|
|
69
|
+
async function list(flags) {
|
|
70
|
+
const config = requireConfig();
|
|
71
|
+
const orgId = requireOrg(flags, config, 'myapi audience list [--org <id>]');
|
|
72
|
+
const res = await sdkAudience.listAudiences(config.api_key, orgId);
|
|
73
|
+
if (flags.json) {
|
|
74
|
+
printJson(res);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
printTable(res.map(summarizeAudience), {
|
|
78
|
+
flags,
|
|
79
|
+
empty: 'No audiences yet. Create one with: myapi audience create <name> --source <people|company> --filter <json>',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function get(id, flags) {
|
|
83
|
+
const config = requireConfig();
|
|
84
|
+
const orgId = requireOrg(flags, config, 'myapi audience get <id> [--org <id>]');
|
|
85
|
+
requireArg(id, 'id', 'myapi audience get <id> [--org <id>]');
|
|
86
|
+
const res = await sdkAudience.getAudience(config.api_key, orgId, id);
|
|
87
|
+
printJson(res);
|
|
88
|
+
}
|
|
89
|
+
async function update(id, flags) {
|
|
90
|
+
const config = requireConfig();
|
|
91
|
+
const orgId = requireOrg(flags, config, 'myapi audience update <id> [--name x] [--description y] [--filter <json>]');
|
|
92
|
+
requireArg(id, 'id', 'myapi audience update <id> [--name x] [--description y] [--filter <json>]');
|
|
93
|
+
const patch = {};
|
|
94
|
+
if (typeof flags.name === 'string')
|
|
95
|
+
patch.name = flags.name;
|
|
96
|
+
if (typeof flags.description === 'string')
|
|
97
|
+
patch.description = flags.description;
|
|
98
|
+
if (typeof flags.filter === 'string')
|
|
99
|
+
patch.filter = parseFilter(flags.filter);
|
|
100
|
+
if (Object.keys(patch).length === 0) {
|
|
101
|
+
error('Pass at least one of --name / --description / --filter to update.');
|
|
102
|
+
}
|
|
103
|
+
const res = await sdkAudience.updateAudience(config.api_key, orgId, id, patch);
|
|
104
|
+
if (flags.json) {
|
|
105
|
+
printJson(res);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
success(`Audience ${res.id} updated. Members: ${res.member_count}`);
|
|
109
|
+
}
|
|
110
|
+
async function del(id, flags) {
|
|
111
|
+
const config = requireConfig();
|
|
112
|
+
const orgId = requireOrg(flags, config, 'myapi audience delete <id> [--org <id>]');
|
|
113
|
+
requireArg(id, 'id', 'myapi audience delete <id> [--org <id>]');
|
|
114
|
+
await sdkAudience.deleteAudience(config.api_key, orgId, id);
|
|
115
|
+
success(`Audience ${id} deleted`);
|
|
116
|
+
}
|
|
117
|
+
async function members(id, flags) {
|
|
118
|
+
const config = requireConfig();
|
|
119
|
+
const orgId = requireOrg(flags, config, 'myapi audience members <id> [--limit N] [--offset N] [--org <id>]');
|
|
120
|
+
requireArg(id, 'id', 'myapi audience members <id> [--limit N] [--offset N] [--org <id>]');
|
|
121
|
+
const opts = {};
|
|
122
|
+
if (typeof flags.limit === 'number')
|
|
123
|
+
opts.limit = flags.limit;
|
|
124
|
+
if (typeof flags.offset === 'number')
|
|
125
|
+
opts.offset = flags.offset;
|
|
126
|
+
const res = await sdkAudience.getAudienceMembers(config.api_key, orgId, id, opts);
|
|
127
|
+
if (flags.json) {
|
|
128
|
+
printJson(res);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Response doesn't carry `source` — detect by which array is present.
|
|
132
|
+
const isPeople = Array.isArray(res.people);
|
|
133
|
+
const kind = isPeople ? 'people' : 'companies';
|
|
134
|
+
const count = isPeople ? (res.people?.length ?? 0) : (res.companies?.length ?? 0);
|
|
135
|
+
info(`${count} of ${res.total} ${kind}${res.has_more ? ' (more available — pass --offset)' : ''}`);
|
|
136
|
+
if (isPeople) {
|
|
137
|
+
printTable((res.people ?? []).map(p => ({
|
|
138
|
+
id: p.id,
|
|
139
|
+
name: p.full_name,
|
|
140
|
+
email: p.email ?? '',
|
|
141
|
+
domain: p.company?.domain ?? '',
|
|
142
|
+
country: p.location?.country ?? '',
|
|
143
|
+
link_conf: typeof p.link_confidence === 'number' ? p.link_confidence.toFixed(2) : '',
|
|
144
|
+
})), { flags, empty: 'Audience has no members.' });
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
printTable((res.companies ?? []).map(c => ({
|
|
148
|
+
id: c.id,
|
|
149
|
+
domain: c.domain ?? '',
|
|
150
|
+
name: c.name,
|
|
151
|
+
country: c.country ?? '',
|
|
152
|
+
confidence: c.confidence ?? '',
|
|
153
|
+
headcount: c.headcount_lower_bound ?? '',
|
|
154
|
+
sources: c.source_count ?? '',
|
|
155
|
+
})), { flags, empty: 'Audience has no members.' });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function refresh(id, flags) {
|
|
159
|
+
const config = requireConfig();
|
|
160
|
+
const orgId = requireOrg(flags, config, 'myapi audience refresh <id> [--org <id>]');
|
|
161
|
+
requireArg(id, 'id', 'myapi audience refresh <id> [--org <id>]');
|
|
162
|
+
const res = await sdkAudience.refreshAudience(config.api_key, orgId, id);
|
|
163
|
+
if (flags.json) {
|
|
164
|
+
printJson(res);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const sign = res.delta > 0 ? '+' : '';
|
|
168
|
+
success(`Refreshed. ${res.previous} → ${res.total} (${sign}${res.delta})`);
|
|
169
|
+
}
|
|
170
|
+
const SUBCOMMAND_USAGE = {
|
|
171
|
+
'create': `myapi audience create <name> --source <people|company> --filter <json> [--description <text>] [--org <id>]
|
|
172
|
+
|
|
173
|
+
The Goldfox filter shape is shared across people/company/audience:
|
|
174
|
+
{
|
|
175
|
+
"confidence": ["high"], // high (default), low, very_low
|
|
176
|
+
"country": ["US", "DE"], // ISO 3166-1 alpha-2
|
|
177
|
+
"country_consistent": true, // TLD+address+phone all agree
|
|
178
|
+
"seniority": ["c_level", "vp_director"], // people-source only
|
|
179
|
+
"email_type": ["corporate"], // people-source only
|
|
180
|
+
"tld_class": ["cctld"], // cctld/generic/vanity/low_trust/other
|
|
181
|
+
"has_c_level": true, // boolean signals
|
|
182
|
+
"has_decision_maker": true,
|
|
183
|
+
"has_careers_page": true,
|
|
184
|
+
"has_investors_page": false,
|
|
185
|
+
"has_shop_page": false,
|
|
186
|
+
"is_registered_entity": true, // recognised legal suffix
|
|
187
|
+
"min_headcount": 5,
|
|
188
|
+
"min_source_count": 3,
|
|
189
|
+
"min_link_confidence": 0.8, // people-source only
|
|
190
|
+
"keyword": "platform", // substring match on domain
|
|
191
|
+
"limit": 100,
|
|
192
|
+
"offset": 0
|
|
193
|
+
}
|
|
194
|
+
Within an array → OR. Across fields → AND. Empty filter = all rows in
|
|
195
|
+
source with default confidence=high (Goldfox's curated tier).
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
myapi audience create "EU decision makers" \\
|
|
199
|
+
--source people \\
|
|
200
|
+
--filter '{"seniority":["c_level","vp_director"],"country":["DE","FR","GB"],"email_type":["corporate"]}'`,
|
|
201
|
+
'list': 'myapi audience list [--org <id>] [--json]',
|
|
202
|
+
'get': 'myapi audience get <id> [--org <id>]',
|
|
203
|
+
'update': 'myapi audience update <id> [--name <x>] [--description <y>] [--filter <json>] [--org <id>]',
|
|
204
|
+
'delete': 'myapi audience delete <id> [--org <id>]',
|
|
205
|
+
'members': 'myapi audience members <id> [--limit N] [--offset N] [--org <id>] [--json]',
|
|
206
|
+
'refresh': 'myapi audience refresh <id> [--org <id>]',
|
|
207
|
+
};
|
|
208
|
+
export async function run(subcommand, args, flags) {
|
|
209
|
+
if (!subcommand || (flags.help && !subcommand)) {
|
|
210
|
+
info(`Usage: myapi audience <subcommand>
|
|
211
|
+
|
|
212
|
+
Subcommands:
|
|
213
|
+
create Save a filter as a named audience (people or company source)
|
|
214
|
+
list List all audiences in the org
|
|
215
|
+
get Get a single audience (with its filter + current member_count)
|
|
216
|
+
update Patch name / description / filter (re-evaluates count if filter changes)
|
|
217
|
+
delete Remove an audience
|
|
218
|
+
members Get paged member snapshot (people or companies depending on source)
|
|
219
|
+
refresh Re-evaluate filter against current data; returns delta vs previous
|
|
220
|
+
|
|
221
|
+
All commands accept --org <id> (or set a default: myapi config set-org <id>).
|
|
222
|
+
|
|
223
|
+
Audiences are saved filter snapshots. Same filter shape as myapi people search /
|
|
224
|
+
company search — see "myapi audience create --help" for the schema.`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (flags.help) {
|
|
228
|
+
const usage = SUBCOMMAND_USAGE[subcommand];
|
|
229
|
+
if (usage)
|
|
230
|
+
info(`Usage: ${usage}`);
|
|
231
|
+
else
|
|
232
|
+
info(`Unknown subcommand: ${subcommand}. Run "myapi audience --help" for the list.`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
switch (subcommand) {
|
|
236
|
+
case 'create': return create(args[0], flags);
|
|
237
|
+
case 'list': return list(flags);
|
|
238
|
+
case 'get': return get(args[0], flags);
|
|
239
|
+
case 'update': return update(args[0], flags);
|
|
240
|
+
case 'delete': return del(args[0], flags);
|
|
241
|
+
case 'members': return members(args[0], flags);
|
|
242
|
+
case 'refresh': return refresh(args[0], flags);
|
|
243
|
+
default: error(`Unknown subcommand: ${subcommand}. Run "myapi audience --help" for valid subcommands.`);
|
|
244
|
+
}
|
|
245
|
+
}
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { FlagSchema } from '../flags.js';
|
|
2
2
|
import type { Flags } from '../helpers.js';
|
|
3
|
+
import type { Exposes } from '../exposes.js';
|
|
4
|
+
export declare const EXPOSES: Exposes;
|
|
3
5
|
export declare const SCHEMA: FlagSchema;
|
|
4
|
-
export declare const HELP = "Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account \u00B7 supports --json\n link [email] Upgrade anonymous account to registered (or add a second session)\n Use myapi auth setup to create a completely new account\n switch [index] Switch active account by index or email\n config Manage CLI defaults (org, funnel, domain) \u00B7 supports set-org / set-funnel / set-domain\n install-skills
|
|
5
|
-
export declare const INSTALL_SKILLS_HELP = "Usage: myapi
|
|
6
|
+
export declare const HELP = "Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account \u00B7 supports --json\n link [email] Upgrade anonymous account to registered (or add a second session)\n Use myapi auth setup to create a completely new account\n switch [index] Switch active account by index or email\n config Manage CLI defaults (org, funnel, domain) \u00B7 supports set-org / set-funnel / set-domain\n registrant Manage stored WHOIS contact info for domain registration \u00B7 set / get / clear\n install-skills DEPRECATED \u2014 use `myapi install-skills` instead\n api-keys Manage API keys \u00B7 list / create / revoke\n keys Alias for api-keys";
|
|
7
|
+
export declare const INSTALL_SKILLS_HELP = "Usage: myapi install-skills\n\nInstalls the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).\n\nThis command writes skill definition files to:\n ~/.agents/skills/myapi/\n\nAnd creates symlinks in the appropriate agent config directories:\n ~/.claude/ (Claude)\n ~/.gemini/ (Gemini)\n ~/.cursor/ (Cursor, if detected)\n\nThese files teach agents how to use the MyAPI CLI and API directly.\nRun this command again to update existing skills to the latest version.\n\nNote: `myapi auth install-skills` is deprecated and will be removed in the next minor.\nUse `myapi install-skills` going forward.";
|
|
6
8
|
export declare function link(flags?: Flags, emailArg?: string): Promise<void>;
|
|
7
9
|
export declare function whoami(flags?: Flags): Promise<void>;
|
|
8
10
|
export declare function switchCmd(flags?: Flags, indexArg?: string): Promise<void>;
|
|
11
|
+
export declare function registrant(sub: string | undefined, flags?: Flags): Promise<void>;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
1
|
+
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts, requireConfig } from '../config.js';
|
|
2
2
|
import { info, success, error, printJson } from '../output.js';
|
|
3
3
|
import { ask, confirm, withReadline } from '../prompt.js';
|
|
4
4
|
import { installSkills } from './setup.js';
|
|
5
5
|
import { hq } from '@myapihq/sdk';
|
|
6
|
+
import { promptAndSave as promptAndSaveRegistrant } from '../registrant.js';
|
|
7
|
+
export const EXPOSES = [
|
|
8
|
+
'PATCH /hq/account/upgrade',
|
|
9
|
+
'POST /hq/account/send-code',
|
|
10
|
+
'POST /hq/account/verify-code',
|
|
11
|
+
'GET /hq/billing/balance',
|
|
12
|
+
'GET /hq/account/free-tier',
|
|
13
|
+
];
|
|
6
14
|
export const SCHEMA = {
|
|
7
15
|
'install-skills': 'boolean',
|
|
8
16
|
'no-skills': 'boolean',
|
|
@@ -19,10 +27,11 @@ Subcommands:
|
|
|
19
27
|
Use myapi auth setup to create a completely new account
|
|
20
28
|
switch [index] Switch active account by index or email
|
|
21
29
|
config Manage CLI defaults (org, funnel, domain) · supports set-org / set-funnel / set-domain
|
|
22
|
-
|
|
30
|
+
registrant Manage stored WHOIS contact info for domain registration · set / get / clear
|
|
31
|
+
install-skills DEPRECATED — use \`myapi install-skills\` instead
|
|
23
32
|
api-keys Manage API keys · list / create / revoke
|
|
24
33
|
keys Alias for api-keys`;
|
|
25
|
-
export const INSTALL_SKILLS_HELP = `Usage: myapi
|
|
34
|
+
export const INSTALL_SKILLS_HELP = `Usage: myapi install-skills
|
|
26
35
|
|
|
27
36
|
Installs the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).
|
|
28
37
|
|
|
@@ -35,7 +44,10 @@ And creates symlinks in the appropriate agent config directories:
|
|
|
35
44
|
~/.cursor/ (Cursor, if detected)
|
|
36
45
|
|
|
37
46
|
These files teach agents how to use the MyAPI CLI and API directly.
|
|
38
|
-
Run this command again to update existing skills to the latest version
|
|
47
|
+
Run this command again to update existing skills to the latest version.
|
|
48
|
+
|
|
49
|
+
Note: \`myapi auth install-skills\` is deprecated and will be removed in the next minor.
|
|
50
|
+
Use \`myapi install-skills\` going forward.`;
|
|
39
51
|
const LINK_HELP = `Usage: myapi auth link [email]
|
|
40
52
|
|
|
41
53
|
Behavior depends on whether the email is new or already registered:
|
|
@@ -147,10 +159,30 @@ export async function whoami(flags = {}) {
|
|
|
147
159
|
if (!config?.api_key)
|
|
148
160
|
error('Not configured. Run: myapi auth setup');
|
|
149
161
|
let balance = null;
|
|
162
|
+
// Backend returns an array of per-service entries, or null. The SDK type
|
|
163
|
+
// used to lie about this (claimed scalar) — see hq.ts FreeTierEntry.
|
|
164
|
+
let freeTier = null;
|
|
165
|
+
// Note any auth-rejection from the balance probe so we don't silently
|
|
166
|
+
// print stale cached config when the key has actually been invalidated
|
|
167
|
+
// server-side. Other failures (endpoint missing, transient 500s) stay
|
|
168
|
+
// swallowed — balance is best-effort, not a key-validation primitive.
|
|
169
|
+
let keyRejected = false;
|
|
150
170
|
try {
|
|
151
171
|
balance = await hq.getBalance(config.api_key);
|
|
152
172
|
}
|
|
153
|
-
catch {
|
|
173
|
+
catch (err) {
|
|
174
|
+
if (err?.status === 401 || /invalid api key/i.test(err?.code ?? ''))
|
|
175
|
+
keyRejected = true;
|
|
176
|
+
}
|
|
177
|
+
// Free-tier query is best-effort — backend may not have it for every
|
|
178
|
+
// account type, and surfacing nothing is better than failing whoami.
|
|
179
|
+
try {
|
|
180
|
+
freeTier = await hq.getFreeTier(config.api_key);
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
if (err?.status === 401 || /invalid api key/i.test(err?.code ?? ''))
|
|
184
|
+
keyRejected = true;
|
|
185
|
+
}
|
|
154
186
|
if (flags.json) {
|
|
155
187
|
printJson({
|
|
156
188
|
email: config.email ?? null,
|
|
@@ -160,9 +192,16 @@ export async function whoami(flags = {}) {
|
|
|
160
192
|
is_anonymous: config.is_anonymous ?? false,
|
|
161
193
|
balance: balance?.balance_display ?? null,
|
|
162
194
|
credits: balance?.credits_display ?? null,
|
|
195
|
+
free_tier: freeTier ?? null,
|
|
196
|
+
key_rejected: keyRejected,
|
|
163
197
|
});
|
|
164
198
|
return;
|
|
165
199
|
}
|
|
200
|
+
if (keyRejected) {
|
|
201
|
+
info('⚠ The backend rejected your API key. The values below are from local config and may not match server state.');
|
|
202
|
+
info(' Run: myapi auth setup');
|
|
203
|
+
info('');
|
|
204
|
+
}
|
|
166
205
|
if (config.email)
|
|
167
206
|
info(`Email: ${config.email}`);
|
|
168
207
|
info(`Account: ${config.account_id}`);
|
|
@@ -170,6 +209,13 @@ export async function whoami(flags = {}) {
|
|
|
170
209
|
info(`Funnel: ${config.default_funnel ?? '(none)'}`);
|
|
171
210
|
info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
172
211
|
info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display} (not usable for domains)` : '(unavailable)'}`);
|
|
212
|
+
if (Array.isArray(freeTier) && freeTier.length > 0) {
|
|
213
|
+
const parts = freeTier
|
|
214
|
+
.filter((e) => e && typeof e.service === 'string')
|
|
215
|
+
.map((e) => `${e.service} ${e.used ?? 0}/${e.allowance ?? '?'}`);
|
|
216
|
+
if (parts.length > 0)
|
|
217
|
+
info(`FreeTier: ${parts.join(' · ')}`);
|
|
218
|
+
}
|
|
173
219
|
}
|
|
174
220
|
// myapi auth switch [index] — switch between saved accounts.
|
|
175
221
|
export async function switchCmd(flags = {}, indexArg) {
|
|
@@ -215,3 +261,85 @@ export async function switchCmd(flags = {}, indexArg) {
|
|
|
215
261
|
success(`› Switched to ${a.email ?? a.account_id}`);
|
|
216
262
|
}
|
|
217
263
|
}
|
|
264
|
+
// ── myapi auth registrant <set|get|clear> ─────────────────────────────────
|
|
265
|
+
//
|
|
266
|
+
// Manages the locally-stored WHOIS registrant info that `myapi domain
|
|
267
|
+
// register` injects into every call. `set` is the one-time interactive
|
|
268
|
+
// setup; `--registrant-json` is the agent-friendly bypass. `get` and
|
|
269
|
+
// `clear` are housekeeping.
|
|
270
|
+
const REGISTRANT_HELP = `Usage: myapi auth registrant <set|get|clear>
|
|
271
|
+
|
|
272
|
+
Manages the locally-stored WHOIS contact info that \`myapi domain register\`
|
|
273
|
+
requires (ICANN-mandated). Stored in ~/.myapi/config.json under the active
|
|
274
|
+
account; never synced to the backend except as a per-request field.
|
|
275
|
+
|
|
276
|
+
Subcommands:
|
|
277
|
+
set Interactive prompt for all fields. Or pass --registrant-json
|
|
278
|
+
'<json>' to set non-interactively (agent-friendly).
|
|
279
|
+
get Print the stored registrant (or "not set").
|
|
280
|
+
clear Remove the stored registrant from the active account.`;
|
|
281
|
+
export async function registrant(sub, flags = {}) {
|
|
282
|
+
if (flags.help || !sub) {
|
|
283
|
+
info(REGISTRANT_HELP);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const config = requireConfig();
|
|
287
|
+
switch (sub) {
|
|
288
|
+
case 'set': {
|
|
289
|
+
// Agent path: --registrant-json '<inline>' writes directly without prompting.
|
|
290
|
+
const inlineJson = typeof flags['registrant-json'] === 'string' ? flags['registrant-json'] : null;
|
|
291
|
+
if (inlineJson) {
|
|
292
|
+
let parsed;
|
|
293
|
+
try {
|
|
294
|
+
parsed = JSON.parse(inlineJson);
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
error(`--registrant-json is not valid JSON: ${e.message}`);
|
|
298
|
+
}
|
|
299
|
+
const required = ['name', 'email', 'phone', 'street', 'city', 'postal_code', 'country_code'];
|
|
300
|
+
const missing = required.filter(k => !parsed[k]);
|
|
301
|
+
if (missing.length > 0)
|
|
302
|
+
error(`Missing required field(s): ${missing.join(', ')}`);
|
|
303
|
+
saveConfig({ ...config, registrant: parsed });
|
|
304
|
+
success('Saved registrant from --registrant-json.');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const r = await promptAndSaveRegistrant(config, config.registrant);
|
|
308
|
+
success(`Saved. ${r.name} · ${r.email} · ${r.country_code}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
case 'get': {
|
|
312
|
+
if (!config.registrant) {
|
|
313
|
+
info('No registrant stored. Set one with: myapi auth registrant set');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (flags.json) {
|
|
317
|
+
printJson(config.registrant);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const r = config.registrant;
|
|
321
|
+
info(`Name: ${r.name}`);
|
|
322
|
+
info(`Email: ${r.email}`);
|
|
323
|
+
info(`Phone: ${r.phone}`);
|
|
324
|
+
info(`Street: ${r.street}`);
|
|
325
|
+
info(`City: ${r.city}`);
|
|
326
|
+
if (r.state)
|
|
327
|
+
info(`State: ${r.state}`);
|
|
328
|
+
info(`Postal code: ${r.postal_code}`);
|
|
329
|
+
info(`Country: ${r.country_code}`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
case 'clear': {
|
|
333
|
+
if (!config.registrant) {
|
|
334
|
+
info('No registrant stored — nothing to clear.');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const { registrant: _drop, ...rest } = config;
|
|
338
|
+
saveConfig(rest);
|
|
339
|
+
success('Cleared.');
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
default:
|
|
343
|
+
error(`Unknown subcommand: ${sub}. Run \`myapi auth registrant --help\` for the list.`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FlagSchema } from '../flags.js';
|
|
2
2
|
import type { Flags } from '../helpers.js';
|
|
3
|
+
import type { Exposes } from '../exposes.js';
|
|
3
4
|
export declare const SCHEMA: FlagSchema;
|
|
5
|
+
export declare const EXPOSES: Exposes;
|
|
4
6
|
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
|
5
7
|
export declare function balance(flags: Flags): Promise<void>;
|
|
6
8
|
export declare function history(flags: Flags): Promise<void>;
|
package/dist/commands/billing.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
-
import { confirm } from '../prompt.js';
|
|
4
|
+
import { confirm, isNonInteractive } from '../prompt.js';
|
|
5
5
|
import { formatDate } from '../utils.js';
|
|
6
6
|
export const SCHEMA = {};
|
|
7
|
+
export const EXPOSES = [
|
|
8
|
+
'GET /hq/billing/balance',
|
|
9
|
+
'GET /hq/billing/history',
|
|
10
|
+
'POST /hq/billing/setup-payment',
|
|
11
|
+
'POST /hq/billing/topup',
|
|
12
|
+
];
|
|
7
13
|
const SUBCOMMAND_USAGE = {
|
|
8
14
|
'balance': 'myapi billing balance [--json]',
|
|
9
15
|
'history': 'myapi billing history [--json]',
|
|
@@ -57,6 +63,11 @@ export async function balance(flags) {
|
|
|
57
63
|
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
58
64
|
info(`Account: ${accountType}`);
|
|
59
65
|
info(`Balance: ${result.balance_display} | Credits: ${result.credits_display} (not usable for domains) | Payment method: ${pm}`);
|
|
66
|
+
// Anonymous accounts can't transact — paid surface is gated on a
|
|
67
|
+
// verified email and credits only fund on upgrade. Surface the unblock.
|
|
68
|
+
if (config.is_anonymous) {
|
|
69
|
+
info('→ Link an email to unlock $5 free credit + paid actions: myapi auth link <email>');
|
|
70
|
+
}
|
|
60
71
|
}
|
|
61
72
|
export async function history(flags) {
|
|
62
73
|
const config = requireConfig();
|
|
@@ -82,6 +93,12 @@ export async function topup(amountStr, flags) {
|
|
|
82
93
|
error('Amount must be a positive whole number of dollars (e.g. myapi billing topup 10)');
|
|
83
94
|
}
|
|
84
95
|
if (!flags.yes && !flags.y && amount >= 50) {
|
|
96
|
+
// Refuse loudly in non-interactive contexts (agent harnesses, CI) instead
|
|
97
|
+
// of either hanging on a prompt no human will answer, or silently
|
|
98
|
+
// auto-cancelling with no actionable error. Force the caller to pass --yes.
|
|
99
|
+
if (isNonInteractive()) {
|
|
100
|
+
error(`Charge of $${amount} requires confirmation. Re-run with --yes:\n myapi billing topup ${amount} --yes`);
|
|
101
|
+
}
|
|
85
102
|
const ok = await confirm(`› Charge $${amount} to your saved payment method? (y/N) `, false);
|
|
86
103
|
if (!ok) {
|
|
87
104
|
info('Cancelled.');
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FlagSchema } from '../flags.js';
|
|
2
|
+
import { type Flags } from '../helpers.js';
|
|
3
|
+
import type { Exposes } from '../exposes.js';
|
|
4
|
+
export declare const EXPOSES: Exposes;
|
|
5
|
+
export declare const SCHEMA: FlagSchema;
|
|
6
|
+
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|