@myapihq/cli 1.1.0-wip.4 → 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 +14 -2
- 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
package/dist/commands/org.js
CHANGED
|
@@ -4,6 +4,17 @@ import { success, error, printTable, printJson, info } from '../output.js';
|
|
|
4
4
|
import { confirm } from '../prompt.js';
|
|
5
5
|
import { formatDate, pollJob } from '../utils.js';
|
|
6
6
|
import { requireOrg, requireArg } from '../helpers.js';
|
|
7
|
+
export const EXPOSES = [
|
|
8
|
+
'POST /hq/orgs',
|
|
9
|
+
'GET /hq/orgs',
|
|
10
|
+
'GET /hq/orgs/{org_id}',
|
|
11
|
+
'PATCH /hq/orgs/{org_id}',
|
|
12
|
+
'DELETE /hq/orgs/{org_id}',
|
|
13
|
+
'POST /hq/org-imports',
|
|
14
|
+
'GET /hq/org-imports/{import_id}',
|
|
15
|
+
'POST /hq/org-imports/{import_id}/confirm',
|
|
16
|
+
'GET /funnel/orgs/{org_id}/funnels',
|
|
17
|
+
];
|
|
7
18
|
export const SCHEMA = {
|
|
8
19
|
name: 'string',
|
|
9
20
|
tagline: 'string',
|
|
@@ -107,6 +118,38 @@ export async function del(id, _flags) {
|
|
|
107
118
|
await hq.deleteOrg(config.api_key, id);
|
|
108
119
|
success(`Org ${id} deleted`);
|
|
109
120
|
}
|
|
121
|
+
// Update one or more fields of an org. At least one --flag must be supplied;
|
|
122
|
+
// otherwise we have nothing to send (the SDK accepts a partial payload but
|
|
123
|
+
// PATCH with an empty body would be a wasted round-trip and a confusing
|
|
124
|
+
// no-op for the user).
|
|
125
|
+
export async function update(restArgs, flags) {
|
|
126
|
+
const config = requireConfig();
|
|
127
|
+
const orgId = restArgs[0] || flags.org || config.default_org;
|
|
128
|
+
if (!orgId)
|
|
129
|
+
error('Missing org id. Usage: myapi org update <id> [--field <value> ...]\n(Or set a default: myapi config set-org <id>)');
|
|
130
|
+
const payload = {};
|
|
131
|
+
if (flags.name)
|
|
132
|
+
payload.name = flags.name;
|
|
133
|
+
if (flags.tagline)
|
|
134
|
+
payload.tagline = flags.tagline;
|
|
135
|
+
if (flags.description)
|
|
136
|
+
payload.description = flags.description;
|
|
137
|
+
if (flags['business-sector'])
|
|
138
|
+
payload.business_sector = flags['business-sector'];
|
|
139
|
+
if (flags['logo-url'])
|
|
140
|
+
payload.logo_url = flags['logo-url'];
|
|
141
|
+
if (Object.keys(payload).length === 0) {
|
|
142
|
+
error('No update flags provided. Pass at least one of: --name, --tagline, --description, --business-sector, --logo-url.');
|
|
143
|
+
}
|
|
144
|
+
const updated = await hq.updateOrg(config.api_key, orgId, payload);
|
|
145
|
+
if (flags.json) {
|
|
146
|
+
printJson(updated);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
success(`Org ${updated.id} updated`);
|
|
150
|
+
const fields = Object.keys(payload).map(k => `${k} = ${payload[k]}`);
|
|
151
|
+
info(` ${fields.join('\n ')}`);
|
|
152
|
+
}
|
|
110
153
|
export async function importOrg(args, flags) {
|
|
111
154
|
const config = requireConfig();
|
|
112
155
|
const domain = args[0] || config.default_domain;
|
|
@@ -131,6 +174,16 @@ export async function importOrg(args, flags) {
|
|
|
131
174
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
132
175
|
const SUBCOMMAND_USAGE = {
|
|
133
176
|
'list': 'myapi org list [--json]',
|
|
177
|
+
'update': `myapi org update [<id>] [--name <str>] [--tagline <str>] [--description <str>] [--business-sector <str>] [--logo-url <url>] [--json]
|
|
178
|
+
|
|
179
|
+
Updates one or more fields of an organization. Specify the org by positional
|
|
180
|
+
argument, --org <id>, or fall back to the default org from your config.
|
|
181
|
+
At least one update flag must be provided.
|
|
182
|
+
|
|
183
|
+
Examples:
|
|
184
|
+
myapi org update --tagline "Now with even more cowbell"
|
|
185
|
+
myapi org update abc-123 --name "Acme Inc" --description "Updated tagline"
|
|
186
|
+
myapi org update --logo-url https://example.com/logo.png --json`,
|
|
134
187
|
'create': `myapi org create <name> [--tagline <str>] [--description <str>] [--business-sector <str>] [--logo-url <url>] [--yes] [--json]
|
|
135
188
|
myapi org create --name <name> [...]
|
|
136
189
|
|
|
@@ -166,6 +219,7 @@ export async function run(subcommand, args, flags) {
|
|
|
166
219
|
Subcommands:
|
|
167
220
|
list List organizations
|
|
168
221
|
create Create an organization (e.g. myapi org create "Name" --yes)
|
|
222
|
+
update Update an organization's fields (name, tagline, description, etc.)
|
|
169
223
|
get Get details of an organization
|
|
170
224
|
delete Delete an organization
|
|
171
225
|
sync-brand Sync brand info (name, logo, description) from an existing website`);
|
|
@@ -183,6 +237,7 @@ Subcommands:
|
|
|
183
237
|
switch (subcommand) {
|
|
184
238
|
case 'list': return list(flags);
|
|
185
239
|
case 'create': return create(args, flags);
|
|
240
|
+
case 'update': return update(args, flags);
|
|
186
241
|
case 'get': return get(args[0], flags);
|
|
187
242
|
case 'delete': return del(args[0], flags);
|
|
188
243
|
case 'sync-brand': return importOrg(args, flags);
|
|
@@ -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>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { people as sdkPeople } from '@myapihq/sdk';
|
|
2
|
+
import { requireConfig } from '../config.js';
|
|
3
|
+
import { error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { requireOrg, requireArg } from '../helpers.js';
|
|
5
|
+
export const EXPOSES = [
|
|
6
|
+
'POST /people/orgs/{org_id}/search',
|
|
7
|
+
'GET /people/orgs/{org_id}/{person_id}',
|
|
8
|
+
];
|
|
9
|
+
export const SCHEMA = {
|
|
10
|
+
org: 'string',
|
|
11
|
+
// Goldfox filter dimensions
|
|
12
|
+
confidence: 'string',
|
|
13
|
+
country: 'string',
|
|
14
|
+
'country-consistent': 'boolean',
|
|
15
|
+
'email-type': 'string',
|
|
16
|
+
'has-c-level': 'boolean',
|
|
17
|
+
'has-careers-page': 'boolean',
|
|
18
|
+
'has-decision-maker': 'boolean',
|
|
19
|
+
'has-investors-page': 'boolean',
|
|
20
|
+
'has-shop-page': 'boolean',
|
|
21
|
+
'is-registered-entity': 'boolean',
|
|
22
|
+
keyword: 'string',
|
|
23
|
+
'min-headcount': 'number',
|
|
24
|
+
'min-link-confidence': 'number',
|
|
25
|
+
'min-source-count': 'number',
|
|
26
|
+
seniority: 'string',
|
|
27
|
+
'tld-class': 'string',
|
|
28
|
+
limit: 'number',
|
|
29
|
+
offset: 'number',
|
|
30
|
+
};
|
|
31
|
+
// `--industry saas,fintech` → ['saas', 'fintech']. Returns undefined for
|
|
32
|
+
// empty/missing so we don't send empty arrays to the backend.
|
|
33
|
+
function csv(v) {
|
|
34
|
+
if (typeof v !== 'string' || v.trim() === '')
|
|
35
|
+
return undefined;
|
|
36
|
+
return v.split(',').map(s => s.trim()).filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
function buildFilter(flags) {
|
|
39
|
+
return {
|
|
40
|
+
confidence: csv(flags.confidence),
|
|
41
|
+
country: csv(flags.country),
|
|
42
|
+
country_consistent: typeof flags['country-consistent'] === 'boolean' ? flags['country-consistent'] : undefined,
|
|
43
|
+
email_type: csv(flags['email-type']),
|
|
44
|
+
has_c_level: typeof flags['has-c-level'] === 'boolean' ? flags['has-c-level'] : undefined,
|
|
45
|
+
has_careers_page: typeof flags['has-careers-page'] === 'boolean' ? flags['has-careers-page'] : undefined,
|
|
46
|
+
has_decision_maker: typeof flags['has-decision-maker'] === 'boolean' ? flags['has-decision-maker'] : undefined,
|
|
47
|
+
has_investors_page: typeof flags['has-investors-page'] === 'boolean' ? flags['has-investors-page'] : undefined,
|
|
48
|
+
has_shop_page: typeof flags['has-shop-page'] === 'boolean' ? flags['has-shop-page'] : undefined,
|
|
49
|
+
is_registered_entity: typeof flags['is-registered-entity'] === 'boolean' ? flags['is-registered-entity'] : undefined,
|
|
50
|
+
keyword: typeof flags.keyword === 'string' ? flags.keyword : undefined,
|
|
51
|
+
min_headcount: typeof flags['min-headcount'] === 'number' ? flags['min-headcount'] : undefined,
|
|
52
|
+
min_link_confidence: typeof flags['min-link-confidence'] === 'number' ? flags['min-link-confidence'] : undefined,
|
|
53
|
+
min_source_count: typeof flags['min-source-count'] === 'number' ? flags['min-source-count'] : undefined,
|
|
54
|
+
seniority: csv(flags.seniority),
|
|
55
|
+
tld_class: csv(flags['tld-class']),
|
|
56
|
+
limit: typeof flags.limit === 'number' ? flags.limit : undefined,
|
|
57
|
+
offset: typeof flags.offset === 'number' ? flags.offset : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function search(flags) {
|
|
61
|
+
const config = requireConfig();
|
|
62
|
+
const orgId = requireOrg(flags, config, 'myapi people search [filters...] [--org <id>]');
|
|
63
|
+
const filter = buildFilter(flags);
|
|
64
|
+
const res = await sdkPeople.searchPeople(config.api_key, orgId, filter);
|
|
65
|
+
if (flags.json) {
|
|
66
|
+
printJson(res);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
info(`${res.people.length} of ${res.total} people${res.has_more ? ' (more available — pass --offset)' : ''}`);
|
|
70
|
+
printTable(res.people.map(p => ({
|
|
71
|
+
id: p.id,
|
|
72
|
+
name: p.full_name,
|
|
73
|
+
email: p.email ?? '',
|
|
74
|
+
domain: p.company?.domain ?? '',
|
|
75
|
+
country: p.location?.country ?? '',
|
|
76
|
+
link_conf: typeof p.link_confidence === 'number' ? p.link_confidence.toFixed(2) : '',
|
|
77
|
+
})), { flags, empty: 'No people matched. Try fewer filters.' });
|
|
78
|
+
}
|
|
79
|
+
async function get(personId, flags) {
|
|
80
|
+
const config = requireConfig();
|
|
81
|
+
const orgId = requireOrg(flags, config, 'myapi people get <person_id> [--org <id>]');
|
|
82
|
+
requireArg(personId, 'person_id', 'myapi people get <person_id> [--org <id>]');
|
|
83
|
+
const res = await sdkPeople.getPerson(config.api_key, orgId, personId);
|
|
84
|
+
printJson(res);
|
|
85
|
+
}
|
|
86
|
+
const SUBCOMMAND_USAGE = {
|
|
87
|
+
'search': `myapi people search [--confidence <csv>] [--country <csv>] [--country-consistent]
|
|
88
|
+
[--email-type <csv>] [--seniority <csv>] [--tld-class <csv>]
|
|
89
|
+
[--has-c-level] [--has-careers-page] [--has-decision-maker]
|
|
90
|
+
[--has-investors-page] [--has-shop-page] [--is-registered-entity]
|
|
91
|
+
[--keyword <text>] [--min-headcount N] [--min-link-confidence 0..1]
|
|
92
|
+
[--min-source-count N] [--limit N] [--offset N] [--org <id>]
|
|
93
|
+
|
|
94
|
+
Goldfox-sourced filters; OR within an array, AND across fields.
|
|
95
|
+
Empty filter returns all rows with default confidence=high.
|
|
96
|
+
|
|
97
|
+
Values:
|
|
98
|
+
--confidence high (default), low, very_low
|
|
99
|
+
--seniority c_level, vp_director, manager, senior_ic, ic
|
|
100
|
+
--email-type corporate, freemail, role_based, other_corporate
|
|
101
|
+
--tld-class cctld, generic, vanity, low_trust, other
|
|
102
|
+
--country ISO 3166-1 alpha-2 (US, DE, GB, ...)
|
|
103
|
+
--keyword substring match on domain only (not name/title)
|
|
104
|
+
|
|
105
|
+
Examples:
|
|
106
|
+
myapi people search --seniority c_level,vp_director --country US
|
|
107
|
+
myapi people search --email-type corporate --has-decision-maker --min-source-count 3
|
|
108
|
+
myapi people search --confidence high,low --country DE --tld-class cctld`,
|
|
109
|
+
'get': 'myapi people get <person_id> [--org <id>]',
|
|
110
|
+
};
|
|
111
|
+
export async function run(subcommand, args, flags) {
|
|
112
|
+
if (!subcommand || (flags.help && !subcommand)) {
|
|
113
|
+
info(`Usage: myapi people <subcommand>
|
|
114
|
+
|
|
115
|
+
Subcommands:
|
|
116
|
+
search Filter people across the Goldfox database
|
|
117
|
+
get Get a single person (with embedded company)
|
|
118
|
+
|
|
119
|
+
All commands accept --org <id> (or set a default: myapi config set-org <id>).
|
|
120
|
+
Filter shape is shared with company + audience — see myapi audience --help.`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (flags.help) {
|
|
124
|
+
const usage = SUBCOMMAND_USAGE[subcommand];
|
|
125
|
+
if (usage)
|
|
126
|
+
info(`Usage: ${usage}`);
|
|
127
|
+
else
|
|
128
|
+
info(`Unknown subcommand: ${subcommand}. Run "myapi people --help" for the list.`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
switch (subcommand) {
|
|
132
|
+
case 'search': return search(flags);
|
|
133
|
+
case 'get': return get(args[0], flags);
|
|
134
|
+
default: error(`Unknown subcommand: ${subcommand}. Run "myapi people --help" for valid subcommands.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
package/dist/commands/pixel.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Exposes } from '../exposes.js';
|
|
2
|
+
import type { Flags } from '../helpers.js';
|
|
3
|
+
import type { FlagSchema } from '../flags.js';
|
|
4
|
+
export declare const SCHEMA: FlagSchema;
|
|
5
|
+
export declare const EXPOSES: Exposes;
|
|
6
|
+
export declare function interactions(flags: Flags): Promise<void>;
|
|
7
|
+
export declare function visits(flags: Flags): Promise<void>;
|
|
8
|
+
export declare function events(flags: Flags): Promise<void>;
|
|
9
|
+
export declare function audience(flags: Flags): Promise<void>;
|
|
10
|
+
export declare function identity(pixelId: string, flags: Flags): Promise<void>;
|
|
11
|
+
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
package/dist/commands/pixel.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { pixel as sdkPixel } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
export const SCHEMA = {
|
|
5
|
+
org: 'string',
|
|
6
|
+
website: 'string',
|
|
7
|
+
'campaign-id': 'string',
|
|
8
|
+
domain: 'string',
|
|
9
|
+
from: 'string',
|
|
10
|
+
to: 'string',
|
|
11
|
+
limit: 'number',
|
|
12
|
+
offset: 'number',
|
|
13
|
+
};
|
|
14
|
+
export const EXPOSES = [
|
|
15
|
+
'GET /pixel/orgs/{org_id}/interactions',
|
|
16
|
+
'GET /pixel/orgs/{org_id}/visits',
|
|
17
|
+
'GET /pixel/orgs/{org_id}/events',
|
|
18
|
+
'GET /pixel/orgs/{org_id}/identity/{pixel_id}',
|
|
19
|
+
'GET /pixel/orgs/{org_id}/audience/get_geo_sample',
|
|
20
|
+
];
|
|
4
21
|
export async function interactions(flags) {
|
|
5
22
|
const config = requireConfig();
|
|
6
23
|
const orgId = flags.org || config.default_org;
|
|
@@ -34,6 +51,73 @@ export async function interactions(flags) {
|
|
|
34
51
|
info(`Total Visits: ${res.total_visits} | Total Events: ${res.total_events} | Showing: ${res.limit} | Offset: ${res.offset}`);
|
|
35
52
|
}
|
|
36
53
|
}
|
|
54
|
+
// Page-visit timeline scoped to a website (host).
|
|
55
|
+
export async function visits(flags) {
|
|
56
|
+
const config = requireConfig();
|
|
57
|
+
const orgId = flags.org || config.default_org;
|
|
58
|
+
if (!orgId)
|
|
59
|
+
error("Missing org id. Usage: myapi pixel visits --website <domain> [--org <id>]\n(Or set defaults via: myapi config set-org <id>)");
|
|
60
|
+
if (!flags.website)
|
|
61
|
+
error("Missing required arg: --website <domain>. Visits are scoped to a host.");
|
|
62
|
+
const params = {
|
|
63
|
+
website: flags.website,
|
|
64
|
+
};
|
|
65
|
+
if (flags.from)
|
|
66
|
+
params.from = flags.from;
|
|
67
|
+
if (flags.to)
|
|
68
|
+
params.to = flags.to;
|
|
69
|
+
if (flags.limit)
|
|
70
|
+
params.limit = parseInt(flags.limit, 10);
|
|
71
|
+
if (flags.offset)
|
|
72
|
+
params.offset = parseInt(flags.offset, 10);
|
|
73
|
+
const res = await sdkPixel.getVisits(config.api_key, orgId, params);
|
|
74
|
+
if (flags.json) {
|
|
75
|
+
printJson(res);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
printTable(res.visits);
|
|
79
|
+
info(`Total: ${res.total} | Showing: ${res.limit} | Offset: ${res.offset}`);
|
|
80
|
+
}
|
|
81
|
+
// Engagement events (open / click / page_visit / sent) — filterable by
|
|
82
|
+
// campaign or by domain.
|
|
83
|
+
export async function events(flags) {
|
|
84
|
+
const config = requireConfig();
|
|
85
|
+
const orgId = flags.org || config.default_org;
|
|
86
|
+
if (!orgId)
|
|
87
|
+
error("Missing org id. Usage: myapi pixel events [--campaign-id <id> | --domain <domain>] [--org <id>]\n(Or set defaults via: myapi config set-org <id>)");
|
|
88
|
+
const params = {};
|
|
89
|
+
if (flags['campaign-id'])
|
|
90
|
+
params.campaign_id = flags['campaign-id'];
|
|
91
|
+
if (flags.domain)
|
|
92
|
+
params.domain = flags.domain;
|
|
93
|
+
if (flags.from)
|
|
94
|
+
params.from = flags.from;
|
|
95
|
+
if (flags.to)
|
|
96
|
+
params.to = flags.to;
|
|
97
|
+
if (flags.limit)
|
|
98
|
+
params.limit = parseInt(flags.limit, 10);
|
|
99
|
+
if (flags.offset)
|
|
100
|
+
params.offset = parseInt(flags.offset, 10);
|
|
101
|
+
if (!params.campaign_id && !params.domain) {
|
|
102
|
+
error("You must provide at least one filter: --campaign-id or --domain.");
|
|
103
|
+
}
|
|
104
|
+
const res = await sdkPixel.getEvents(config.api_key, orgId, params);
|
|
105
|
+
if (flags.json) {
|
|
106
|
+
printJson(res);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
printTable(res.events);
|
|
110
|
+
info(`Total: ${res.total} | Showing: ${res.limit} | Offset: ${res.offset}`);
|
|
111
|
+
}
|
|
112
|
+
// Geographic distribution sample of the org's pixel audience.
|
|
113
|
+
export async function audience(flags) {
|
|
114
|
+
const config = requireConfig();
|
|
115
|
+
const orgId = flags.org || config.default_org;
|
|
116
|
+
if (!orgId)
|
|
117
|
+
error("Missing org id. Usage: myapi pixel audience [--org <id>]\n(Or set defaults via: myapi config set-org <id>)");
|
|
118
|
+
const res = await sdkPixel.getGeoSample(config.api_key, orgId);
|
|
119
|
+
printJson(res);
|
|
120
|
+
}
|
|
37
121
|
export async function identity(pixelId, flags) {
|
|
38
122
|
const config = requireConfig();
|
|
39
123
|
const orgId = flags.org || config.default_org;
|
|
@@ -46,6 +130,9 @@ export async function identity(pixelId, flags) {
|
|
|
46
130
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
47
131
|
const SUBCOMMAND_USAGE = {
|
|
48
132
|
'interactions': 'myapi pixel interactions [--website <domain>] [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--org <id>] [--json]',
|
|
133
|
+
'visits': 'myapi pixel visits --website <domain> [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--org <id>] [--json]',
|
|
134
|
+
'events': 'myapi pixel events [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--org <id>] [--json]',
|
|
135
|
+
'audience': 'myapi pixel audience [--org <id>]',
|
|
49
136
|
'identity': 'myapi pixel identity <pixel_id> [--org <id>]',
|
|
50
137
|
};
|
|
51
138
|
export async function run(subcommand, args, flags) {
|
|
@@ -55,6 +142,9 @@ export async function run(subcommand, args, flags) {
|
|
|
55
142
|
Subcommands:
|
|
56
143
|
interactions Get a unified timeline of visits and events
|
|
57
144
|
(requires at least one filter: --website, --campaign-id, or --domain)
|
|
145
|
+
visits Page-visit timeline scoped to a website host
|
|
146
|
+
events Engagement events (sent / open / click / page_visit) by campaign or domain
|
|
147
|
+
audience Geographic distribution sample of your pixel audience
|
|
58
148
|
identity Resolve the identity graph (emails, IPs, profiles) for a pixel ID
|
|
59
149
|
|
|
60
150
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
@@ -70,6 +160,9 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
|
70
160
|
}
|
|
71
161
|
switch (subcommand) {
|
|
72
162
|
case 'interactions': return interactions(flags);
|
|
163
|
+
case 'visits': return visits(flags);
|
|
164
|
+
case 'events': return events(flags);
|
|
165
|
+
case 'audience': return audience(flags);
|
|
73
166
|
case 'identity': return identity(args[0], flags);
|
|
74
167
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for a list of valid subcommands.`);
|
|
75
168
|
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Flags } from '../helpers.js';
|
|
2
|
+
import type { Exposes } from '../exposes.js';
|
|
3
|
+
export declare const EXPOSES: Exposes;
|
|
2
4
|
export declare function installSkills(): Promise<void>;
|
|
3
5
|
export declare function importKey(apiKey: string, flags: Flags): Promise<void>;
|
|
4
6
|
export declare function setup(flags?: Flags): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -5,6 +5,35 @@ import { loadConfig, saveConfig, addAccount, loadFullConfig } from '../config.js
|
|
|
5
5
|
import { info, success } from '../output.js';
|
|
6
6
|
import { ask, confirm } from '../prompt.js';
|
|
7
7
|
import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
|
|
8
|
+
import { emitSkillsInstall } from '../telemetry.js';
|
|
9
|
+
// Resolved at first call from package.json; used by the telemetry payload.
|
|
10
|
+
// Inlined as a getter so import order doesn't matter (package.json is read
|
|
11
|
+
// in index.ts at startup, but installSkills can be called from outside that
|
|
12
|
+
// path).
|
|
13
|
+
let _cliVersion = null;
|
|
14
|
+
function getCliVersion() {
|
|
15
|
+
if (_cliVersion !== null)
|
|
16
|
+
return _cliVersion;
|
|
17
|
+
try {
|
|
18
|
+
const pkgUrl = new URL('../../package.json', import.meta.url);
|
|
19
|
+
_cliVersion = JSON.parse(fs.readFileSync(pkgUrl, 'utf-8')).version || 'unknown';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
_cliVersion = 'unknown';
|
|
23
|
+
}
|
|
24
|
+
return _cliVersion;
|
|
25
|
+
}
|
|
26
|
+
export const EXPOSES = [
|
|
27
|
+
'POST /hq/account/anonymous',
|
|
28
|
+
'POST /hq/account/send-code',
|
|
29
|
+
'POST /hq/account/verify-code',
|
|
30
|
+
'GET /hq/account/me',
|
|
31
|
+
'GET /hq/orgs',
|
|
32
|
+
'GET /funnel/orgs/{org_id}/funnels',
|
|
33
|
+
// installSkills() emits the skills.install telemetry event via
|
|
34
|
+
// ../telemetry.ts; that's an outbound POST to this endpoint.
|
|
35
|
+
'POST /v1/integrations/skills-install',
|
|
36
|
+
];
|
|
8
37
|
// ---------------------------------------------------------------------------
|
|
9
38
|
// Skills installation
|
|
10
39
|
// ---------------------------------------------------------------------------
|
|
@@ -38,24 +67,40 @@ export async function installSkills() {
|
|
|
38
67
|
// Symlink each skill into agent config directories. Surface failures so
|
|
39
68
|
// users can fix permission issues — skills that didn't install will not
|
|
40
69
|
// be available to the agent and "skills installed" would otherwise lie.
|
|
70
|
+
const agentsTargeted = [];
|
|
41
71
|
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
42
72
|
try {
|
|
43
73
|
fs.mkdirSync(dir, { recursive: true });
|
|
44
74
|
for (const skill of skills) {
|
|
45
75
|
const link = path.join(dir, skill);
|
|
46
76
|
const target = path.join(SKILLS_CANONICAL, skill);
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
77
|
+
// Clean up an existing entry if it's unambiguously ours, so re-runs
|
|
78
|
+
// are idempotent. Three cases:
|
|
79
|
+
// - symlink: always safe to replace (we made it).
|
|
80
|
+
// - directory containing only SKILL.md: legacy pre-symlink install
|
|
81
|
+
// style; safe to replace.
|
|
82
|
+
// - anything else (regular file, dir with extra content, etc.):
|
|
83
|
+
// leave it alone so a user-authored skill that happens to share
|
|
84
|
+
// a `my-X-api/` name doesn't get clobbered. The symlinkSync
|
|
85
|
+
// below will then fail with EEXIST and surface to the user.
|
|
86
|
+
// ENOENT from lstat just means "nothing there".
|
|
50
87
|
try {
|
|
51
88
|
const stat = fs.lstatSync(link);
|
|
52
|
-
if (stat.isSymbolicLink())
|
|
89
|
+
if (stat.isSymbolicLink()) {
|
|
53
90
|
fs.rmSync(link, { force: true });
|
|
91
|
+
}
|
|
92
|
+
else if (stat.isDirectory()) {
|
|
93
|
+
const entries = fs.readdirSync(link);
|
|
94
|
+
if (entries.length === 0 || (entries.length === 1 && entries[0] === 'SKILL.md')) {
|
|
95
|
+
fs.rmSync(link, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
54
98
|
}
|
|
55
99
|
catch { /* ENOENT: nothing there */ }
|
|
56
100
|
fs.symlinkSync(target, link);
|
|
57
101
|
}
|
|
58
102
|
info(` ✓ ${agent}`);
|
|
103
|
+
agentsTargeted.push(agent);
|
|
59
104
|
}
|
|
60
105
|
catch (err) {
|
|
61
106
|
// Skip agents not installed on this machine (ENOENT on parent dir is OK).
|
|
@@ -68,6 +113,12 @@ export async function installSkills() {
|
|
|
68
113
|
info(` ✗ ${agent}: ${err.message || code} (${dir})`);
|
|
69
114
|
}
|
|
70
115
|
}
|
|
116
|
+
// Emit telemetry — fire-and-forget, never blocks. See packages/cli/src/telemetry.ts.
|
|
117
|
+
// Skipped silently if user isn't authenticated (e.g. install-skills run before
|
|
118
|
+
// auth setup). When at least one agent dir was successfully written.
|
|
119
|
+
if (agentsTargeted.length > 0) {
|
|
120
|
+
emitSkillsInstall(agentsTargeted, getCliVersion());
|
|
121
|
+
}
|
|
71
122
|
}
|
|
72
123
|
// ---------------------------------------------------------------------------
|
|
73
124
|
// Shared helpers
|
|
@@ -194,7 +245,7 @@ export async function setup(flags = {}) {
|
|
|
194
245
|
const activeType = existing.is_anonymous ? 'anonymous' : 'registered';
|
|
195
246
|
const othersNote = total > 1 ? ` · ${total - 1} more saved` : '';
|
|
196
247
|
info(`› Connected: ${activeLabel} (${activeType})${othersNote}`);
|
|
197
|
-
const add = await confirm('› Connect a new account? (
|
|
248
|
+
const add = await confirm('› Connect a new account? (Y/n) ', true);
|
|
198
249
|
if (!add) {
|
|
199
250
|
if (!existing.skills_installed) {
|
|
200
251
|
const wants = await resolveSkillsPreference(flags);
|
|
@@ -227,6 +278,7 @@ export async function setup(flags = {}) {
|
|
|
227
278
|
}
|
|
228
279
|
else {
|
|
229
280
|
info('› Continuing as anonymous — sites ship to *.makeautonomous.com');
|
|
281
|
+
info(' (Anonymous accounts have $0 credit. Link an email anytime to unlock $5 free credit and paid actions: myapi auth link <email>)');
|
|
230
282
|
const data = await anonymousFlow();
|
|
231
283
|
apiKey = data.api_key;
|
|
232
284
|
accountId = data.account_id;
|
|
@@ -0,0 +1,7 @@
|
|
|
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 const STATUS_HELP = "Usage: myapi status [--org <id>] [--json]\n\nSingle-screen view of your account and what's running in the default org:\nidentity, balance, free-tier usage, plus resource counts across domains,\nfunnels, campaigns, webhooks, and workflows. Useful before a\nco-build session or when checking \"what's mid-flight\" without running six\nlist commands.\n\nEach section is best-effort \u2014 a failing service is shown as \"\u2014\", not an\nerror, so the rest of the snapshot still prints.\n";
|
|
7
|
+
export declare function run(_subcommand: string | undefined, _args: string[], flags?: Flags): Promise<void>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { hq, domain, funnel, email, webhook, workflow, MyApiError } from '@myapihq/sdk';
|
|
2
|
+
// listMailboxes is omitted from this view: the backend requires a domain or
|
|
3
|
+
// filter, so there is no global "all mailboxes in this org" call. Use
|
|
4
|
+
// `myapi email mailbox list --domain <d>` for per-domain detail.
|
|
5
|
+
import { loadConfig } from '../config.js';
|
|
6
|
+
import { info, error, printJson } from '../output.js';
|
|
7
|
+
export const EXPOSES = [
|
|
8
|
+
'GET /hq/billing/balance',
|
|
9
|
+
'GET /hq/account/free-tier',
|
|
10
|
+
'GET /domain/orgs/{org_id}/domains',
|
|
11
|
+
'GET /funnel/orgs/{org_id}/funnels',
|
|
12
|
+
'GET /email/orgs/{org_id}/campaigns',
|
|
13
|
+
'GET /webhook/orgs/{org_id}/endpoints',
|
|
14
|
+
'GET /workflow/orgs/{org_id}/workflows',
|
|
15
|
+
];
|
|
16
|
+
export const SCHEMA = {
|
|
17
|
+
org: 'string',
|
|
18
|
+
};
|
|
19
|
+
export const STATUS_HELP = `Usage: myapi status [--org <id>] [--json]
|
|
20
|
+
|
|
21
|
+
Single-screen view of your account and what's running in the default org:
|
|
22
|
+
identity, balance, free-tier usage, plus resource counts across domains,
|
|
23
|
+
funnels, campaigns, webhooks, and workflows. Useful before a
|
|
24
|
+
co-build session or when checking "what's mid-flight" without running six
|
|
25
|
+
list commands.
|
|
26
|
+
|
|
27
|
+
Each section is best-effort — a failing service is shown as "—", not an
|
|
28
|
+
error, so the rest of the snapshot still prints.
|
|
29
|
+
`;
|
|
30
|
+
function unwrap(r) {
|
|
31
|
+
return r.status === 'fulfilled' ? r.value : null;
|
|
32
|
+
}
|
|
33
|
+
export async function run(_subcommand, _args, flags = {}) {
|
|
34
|
+
if (flags.help) {
|
|
35
|
+
info(STATUS_HELP);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const config = loadConfig();
|
|
39
|
+
if (!config?.api_key)
|
|
40
|
+
error('Not configured. Run: myapi auth setup');
|
|
41
|
+
const orgId = flags.org ?? config.default_org;
|
|
42
|
+
// Account-level info works without an org; org-scoped resources are skipped if none set.
|
|
43
|
+
const orgKnown = Boolean(orgId);
|
|
44
|
+
// Account-level (always fetched).
|
|
45
|
+
const accountResults = await Promise.allSettled([
|
|
46
|
+
hq.getBalance(config.api_key),
|
|
47
|
+
hq.getFreeTier(config.api_key),
|
|
48
|
+
]);
|
|
49
|
+
const balance = unwrap(accountResults[0]);
|
|
50
|
+
// The backend currently returns an array of { service, used, allowance }
|
|
51
|
+
// per-service entries. The SDK type lies about this (claims scalar) — same
|
|
52
|
+
// bug surfaces in `whoami`. Treat as unknown here and narrow at render time.
|
|
53
|
+
const freeTier = unwrap(accountResults[1]);
|
|
54
|
+
// Catch a server-side rejection across any best-effort probe so we can flag
|
|
55
|
+
// the snapshot as untrustworthy rather than silently printing local config.
|
|
56
|
+
const keyRejected = accountResults.some(r => r.status === 'rejected' && r.reason instanceof MyApiError && r.reason.status === 401);
|
|
57
|
+
// Org-scoped (skipped if no default org set).
|
|
58
|
+
let domains = null, funnels = null, campaigns = null, webhooks = null, workflows = null;
|
|
59
|
+
if (orgKnown) {
|
|
60
|
+
const orgResults = await Promise.allSettled([
|
|
61
|
+
domain.listDomains(config.api_key, orgId),
|
|
62
|
+
funnel.listFunnels(config.api_key, orgId),
|
|
63
|
+
email.listCampaigns(config.api_key, orgId),
|
|
64
|
+
webhook.listEndpoints(config.api_key, orgId),
|
|
65
|
+
workflow.listWorkflows(config.api_key, orgId),
|
|
66
|
+
]);
|
|
67
|
+
domains = unwrap(orgResults[0]);
|
|
68
|
+
funnels = unwrap(orgResults[1]);
|
|
69
|
+
campaigns = unwrap(orgResults[2]);
|
|
70
|
+
webhooks = unwrap(orgResults[3]);
|
|
71
|
+
workflows = unwrap(orgResults[4]);
|
|
72
|
+
}
|
|
73
|
+
if (flags.json) {
|
|
74
|
+
printJson({
|
|
75
|
+
account: {
|
|
76
|
+
email: config.email ?? null,
|
|
77
|
+
account_id: config.account_id,
|
|
78
|
+
type: config.is_anonymous ? 'anonymous' : 'registered',
|
|
79
|
+
default_org: orgId ?? null,
|
|
80
|
+
default_funnel: config.default_funnel ?? null,
|
|
81
|
+
key_rejected: keyRejected,
|
|
82
|
+
},
|
|
83
|
+
balance,
|
|
84
|
+
free_tier: freeTier,
|
|
85
|
+
org_resources: orgKnown ? {
|
|
86
|
+
domains, funnels, campaigns, webhooks, workflows,
|
|
87
|
+
} : null,
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (keyRejected) {
|
|
92
|
+
info('⚠ The backend rejected your API key. Values may be stale — run: myapi auth setup');
|
|
93
|
+
info('');
|
|
94
|
+
}
|
|
95
|
+
// ── Identity ──────────────────────────────────────────────────────────────
|
|
96
|
+
if (config.email)
|
|
97
|
+
info(`Email: ${config.email}`);
|
|
98
|
+
info(`Account: ${config.account_id}`);
|
|
99
|
+
info(`Org: ${orgId ?? '(none — run: myapi config set-org <id>)'}`);
|
|
100
|
+
info(`Funnel: ${config.default_funnel ?? '(none)'}`);
|
|
101
|
+
info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
102
|
+
info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display}` : '—'}`);
|
|
103
|
+
if (config.is_anonymous) {
|
|
104
|
+
info(' → Link an email to unlock $5 free credit + paid actions: myapi auth link <email>');
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(freeTier) && freeTier.length > 0) {
|
|
107
|
+
const parts = freeTier
|
|
108
|
+
.filter((e) => e && typeof e.service === 'string')
|
|
109
|
+
.map((e) => `${e.service} ${e.used ?? 0}/${e.allowance ?? '?'}`);
|
|
110
|
+
if (parts.length > 0)
|
|
111
|
+
info(`FreeTier: ${parts.join(' · ')}`);
|
|
112
|
+
}
|
|
113
|
+
// ── Resources ─────────────────────────────────────────────────────────────
|
|
114
|
+
info('');
|
|
115
|
+
info('Resources:');
|
|
116
|
+
if (!orgKnown) {
|
|
117
|
+
info(' (set a default org to see domains/funnels/campaigns/webhooks/workflows)');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (domains) {
|
|
121
|
+
const verified = domains.filter(d => /verified|active|ready/i.test(d.status)).length;
|
|
122
|
+
const pending = domains.length - verified;
|
|
123
|
+
const pendingStr = pending > 0 ? `, ${pending} pending` : '';
|
|
124
|
+
info(` Domains: ${domains.length} (${verified} verified${pendingStr})`);
|
|
125
|
+
}
|
|
126
|
+
else
|
|
127
|
+
info(' Domains: —');
|
|
128
|
+
info(` Funnels: ${funnels ? funnels.length : '—'}`);
|
|
129
|
+
if (campaigns) {
|
|
130
|
+
// Campaigns: backend statuses include 'draft', 'active', 'paused', 'completed'.
|
|
131
|
+
// Surface counts for the two states that matter mid-flight.
|
|
132
|
+
const active = campaigns.filter(c => /active|running/i.test(c.status)).length;
|
|
133
|
+
const paused = campaigns.filter(c => /paused/i.test(c.status)).length;
|
|
134
|
+
const extras = [
|
|
135
|
+
active > 0 ? `${active} active` : null,
|
|
136
|
+
paused > 0 ? `${paused} paused` : null,
|
|
137
|
+
].filter(Boolean).join(', ');
|
|
138
|
+
info(` Campaigns: ${campaigns.length}${extras ? ` (${extras})` : ''}`);
|
|
139
|
+
}
|
|
140
|
+
else
|
|
141
|
+
info(' Campaigns: —');
|
|
142
|
+
info(` Webhooks: ${webhooks ? webhooks.length : '—'}`);
|
|
143
|
+
if (workflows) {
|
|
144
|
+
const enabled = workflows.filter(w => w.enabled).length;
|
|
145
|
+
const disabled = workflows.length - enabled;
|
|
146
|
+
const extras = [
|
|
147
|
+
enabled > 0 ? `${enabled} enabled` : null,
|
|
148
|
+
disabled > 0 ? `${disabled} disabled` : null,
|
|
149
|
+
].filter(Boolean).join(', ');
|
|
150
|
+
info(` Workflows: ${workflows.length}${extras ? ` (${extras})` : ''}`);
|
|
151
|
+
}
|
|
152
|
+
else
|
|
153
|
+
info(' Workflows: —');
|
|
154
|
+
}
|
|
@@ -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>;
|