@myapihq/cli 1.0.17 → 1.0.19

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.
@@ -30,15 +30,13 @@ export async function importDomain(domainArg, flags) {
30
30
  const orgId = flags.org || config.default_org;
31
31
  const domain = domainArg || config.default_domain;
32
32
  if (!orgId || !domain)
33
- error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> --namecheap-user <user> --namecheap-key <key>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
34
- if (!flags['namecheap-user'] || !flags['namecheap-key']) {
35
- error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> --namecheap-user <user> --namecheap-key <key>");
36
- }
37
- await sdkDomain.importDomain(config.api_key, orgId, {
38
- domain,
39
- namecheap_api_user: flags['namecheap-user'],
40
- namecheap_api_key: flags['namecheap-key']
41
- });
33
+ error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> [--namecheap-user <user> --namecheap-key <key>]\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
34
+ const payload = { domain };
35
+ if (flags['namecheap-user'])
36
+ payload.namecheap_api_user = flags['namecheap-user'];
37
+ if (flags['namecheap-key'])
38
+ payload.namecheap_api_key = flags['namecheap-key'];
39
+ await sdkDomain.importDomain(config.api_key, orgId, payload);
42
40
  success(`Imported ${domain}`);
43
41
  }
44
42
  export async function list(flags) {
@@ -5,6 +5,7 @@ export declare function send(flags: Record<string, string | boolean>): Promise<v
5
5
  export declare function inbox(address: string, flags: Record<string, string | boolean>): Promise<void>;
6
6
  export declare function outbox(address: string, flags: Record<string, string | boolean>): Promise<void>;
7
7
  export declare function listTemplates(flags: Record<string, string | boolean>): Promise<void>;
8
+ export declare function deleteTemplate(id: string, flags: Record<string, string | boolean>): Promise<void>;
8
9
  export declare function generateTemplate(flags: Record<string, string | boolean>): Promise<void>;
9
10
  export declare function listCampaigns(flags: Record<string, string | boolean>): Promise<void>;
10
11
  export declare function campaignStats(id: string, flags: Record<string, string | boolean>): Promise<void>;
@@ -84,9 +84,20 @@ export async function listTemplates(flags) {
84
84
  const config = requireConfig();
85
85
  const orgId = flags.org || config.default_org;
86
86
  if (!orgId)
87
- error("Missing required arguments.\nUsage: myapi email list-templates --org <id>\n(Or set a default org via: myapi config set-org <id>)");
87
+ error("Missing required arguments.\nUsage: myapi email list-templates --org <id>\n(Or set defaults via: myapi config set-org <id>)");
88
88
  const templates = await sdkEmail.listTemplates(config.api_key, orgId);
89
- printTable(templates);
89
+ if (flags.json)
90
+ printJson(templates);
91
+ else
92
+ printTable(templates);
93
+ }
94
+ export async function deleteTemplate(id, flags) {
95
+ const config = requireConfig();
96
+ const orgId = flags.org || config.default_org;
97
+ if (!orgId || !id)
98
+ error("Missing required arguments.\nUsage: myapi email delete-template <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
99
+ await sdkEmail.deleteTemplate(config.api_key, orgId, id);
100
+ success(`Deleted template ${id}`);
90
101
  }
91
102
  export async function generateTemplate(flags) {
92
103
  const config = requireConfig();
@@ -138,7 +149,7 @@ export async function campaignStats(id, flags) {
138
149
  }
139
150
  export async function run(subcommand, args, flags) {
140
151
  if (!subcommand || (flags.help && !subcommand)) {
141
- info('Usage: myapi email <subcommand>\n\nSubcommands:\n create-mailbox Create a new mailbox\n list-mailboxes List mailboxes\n send Send an email\n sent List sent emails\n inbox Read received emails\n outbox Read sent emails for address\n list-templates List email templates\n generate-template Generate AI template\n list-campaigns List campaigns\n campaign-stats Get campaign stats\n\nNote: All email commands require the --org <id> flag.');
152
+ info('Usage: myapi email <subcommand>\n\nSubcommands:\n create-mailbox Create a new mailbox\n list-mailboxes List mailboxes\n send Send an email\n sent List sent emails\n inbox Read received emails\n outbox Read sent emails for address\n list-templates List email templates\n generate-template Generate AI template\n delete-template Delete a template\n list-campaigns List campaigns\n campaign-stats Get campaign stats\n\nNote: All email commands require the --org <id> flag.');
142
153
  return;
143
154
  }
144
155
  if (flags.help) {
@@ -158,6 +169,8 @@ export async function run(subcommand, args, flags) {
158
169
  info('Usage: myapi email list-templates --org <id>');
159
170
  else if (subcommand === 'generate-template')
160
171
  info('Usage: myapi email generate-template --org <id> --prompt <str> --name <str>');
172
+ else if (subcommand === 'delete-template')
173
+ info('Usage: myapi email delete-template <id> --org <id>');
161
174
  else if (subcommand === 'list-campaigns')
162
175
  info('Usage: myapi email list-campaigns --org <id>');
163
176
  else if (subcommand === 'campaign-stats')
@@ -180,6 +193,8 @@ export async function run(subcommand, args, flags) {
180
193
  await listTemplates(flags);
181
194
  else if (subcommand === 'generate-template')
182
195
  await generateTemplate(flags);
196
+ else if (subcommand === 'delete-template')
197
+ await deleteTemplate(args[0], flags);
183
198
  else if (subcommand === 'list-campaigns')
184
199
  await listCampaigns(flags);
185
200
  else if (subcommand === 'campaign-stats')
@@ -1,4 +1,4 @@
1
- import { funnel as sdkFunnel } from '@myapihq/sdk';
1
+ import { funnel as sdkFunnel, hq } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { success, error, printTable, info, printJson } from '../output.js';
4
4
  export async function list(flags) {
@@ -18,6 +18,10 @@ export async function create(flags) {
18
18
  }
19
19
  const funnel = await sdkFunnel.createFunnel(config.api_key, orgId, domain);
20
20
  success(`Funnel created! ID: ${funnel.id}`);
21
+ const org = await hq.getOrg(config.api_key, orgId);
22
+ if (org.preview_subdomain) {
23
+ info(`Preview: https://${org.preview_subdomain}.makeautonomous.com`);
24
+ }
21
25
  }
22
26
  export async function get(id, flags) {
23
27
  const config = requireConfig();
@@ -50,8 +54,17 @@ export async function push(id, slug, flags) {
50
54
  });
51
55
  if (!html)
52
56
  error("No HTML provided via stdin");
53
- await sdkFunnel.pushFunnelPage(config.api_key, orgId, id, { slug, html });
57
+ const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, id, { slug, html });
54
58
  success(`Pushed page to ${slug}`);
59
+ if (result?.url) {
60
+ info(`Preview: ${result.url}`);
61
+ }
62
+ else {
63
+ const org = await hq.getOrg(config.api_key, orgId);
64
+ if (org.preview_subdomain) {
65
+ info(`Preview: https://${org.preview_subdomain}.makeautonomous.com/${slug}`);
66
+ }
67
+ }
55
68
  }
56
69
  export async function verify(id, flags) {
57
70
  const config = requireConfig();
@@ -1,2 +1,4 @@
1
1
  export declare function generate(flags: Record<string, string | boolean>): Promise<void>;
2
2
  export declare function list(flags: Record<string, string | boolean>): Promise<void>;
3
+ export declare function del(id: string, flags: Record<string, string | boolean>): Promise<void>;
4
+ export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
@@ -1,11 +1,13 @@
1
1
  import { image as sdkImage } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
- import { success, error, printTable } from '../output.js';
3
+ import { success, error, printTable, info, printJson } from '../output.js';
4
4
  import { sleep } from '../utils.js';
5
5
  export async function generate(flags) {
6
- if (!flags.prompt)
7
- error("Missing --prompt flag");
8
6
  const config = requireConfig();
7
+ const orgId = flags.org || config.default_org;
8
+ if (!orgId || !flags.prompt) {
9
+ error("Missing required arguments.\nUsage: myapi image generate --prompt <text> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
10
+ }
9
11
  const payload = { prompt: flags.prompt };
10
12
  if (flags.ratio)
11
13
  payload.aspect_ratio = flags.ratio;
@@ -13,16 +15,19 @@ export async function generate(flags) {
13
15
  payload.style = flags.style;
14
16
  if (flags.colors)
15
17
  payload.colors = flags.colors;
16
- const res = await sdkImage.generateImage(config.api_key, payload);
18
+ const res = await sdkImage.generateImage(config.api_key, orgId, payload);
17
19
  process.stdout.write("Generating image ");
18
20
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
19
21
  let i = 0;
20
22
  let elapsed = 0;
21
23
  while (elapsed < 60000) {
22
- const job = await sdkImage.getImageJob(config.api_key, res.job_id);
24
+ const job = await sdkImage.getImageJob(config.api_key, orgId, res.job_id);
23
25
  if (job.status === 'completed') {
24
26
  process.stdout.write('\r\x1b[K');
25
- success(`Image generated!\nURL: ${job.url}`);
27
+ if (flags.json)
28
+ printJson(job);
29
+ else
30
+ success(`Image generated!\nURL: ${job.url}`);
26
31
  return;
27
32
  }
28
33
  if (job.status === 'failed') {
@@ -38,6 +43,44 @@ export async function generate(flags) {
38
43
  }
39
44
  export async function list(flags) {
40
45
  const config = requireConfig();
41
- const images = await sdkImage.listImages(config.api_key);
42
- printTable(images);
46
+ const orgId = flags.org || config.default_org;
47
+ if (!orgId)
48
+ error("Missing required arguments.\nUsage: myapi image list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
49
+ const images = await sdkImage.listImages(config.api_key, orgId);
50
+ if (flags.json)
51
+ printJson(images);
52
+ else
53
+ printTable(images);
54
+ }
55
+ export async function del(id, flags) {
56
+ const config = requireConfig();
57
+ const orgId = flags.org || config.default_org;
58
+ if (!orgId || !id) {
59
+ error("Missing required arguments.\nUsage: myapi image delete <job_id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
60
+ }
61
+ await sdkImage.deleteImage(config.api_key, orgId, id);
62
+ success(`Image deleted! (Job ${id} history retained, URL nullified)`);
63
+ }
64
+ export async function run(subcommand, args, flags) {
65
+ if (!subcommand || (flags.help && !subcommand)) {
66
+ info('Usage: myapi image <subcommand>\n\nSubcommands:\n list List all your generated images\n generate Generate a new AI image from a prompt\n delete Delete the physical image file for a job\n\nNote: All image commands require the --org <id> flag.');
67
+ return;
68
+ }
69
+ if (flags.help) {
70
+ if (subcommand === 'list')
71
+ info('Usage: myapi image list --org <id> [--json]');
72
+ else if (subcommand === 'generate')
73
+ info('Usage: myapi image generate --prompt "<text>" [--ratio <1:1|16:9>] [--style <style>] [--colors <hex_list>] --org <id>\n\nGenerates an AI image and polls until the public URL is ready. Costs $0.05 per generation.');
74
+ else if (subcommand === 'delete')
75
+ info('Usage: myapi image delete <job_id> --org <id>\n\nDeletes the physical image file from storage. The generation job history is kept, but its URL will become null.');
76
+ return;
77
+ }
78
+ if (subcommand === 'list')
79
+ await list(flags);
80
+ else if (subcommand === 'generate')
81
+ await generate(flags);
82
+ else if (subcommand === 'delete')
83
+ await del(args[0], flags);
84
+ else
85
+ error(`Unknown subcommand: ${subcommand}. Run "myapi image --help" for a list of valid subcommands.`);
43
86
  }
@@ -23,6 +23,9 @@ export async function create(flags) {
23
23
  payload.logo_url = flags['logo-url'];
24
24
  const org = await hq.createOrg(config.api_key, payload);
25
25
  success(`Org created! ID: ${org.id}, Name: ${org.name}`);
26
+ if (org.preview_subdomain) {
27
+ info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
28
+ }
26
29
  }
27
30
  export async function list(flags) {
28
31
  if (flags.help) {
@@ -45,6 +48,9 @@ export async function get(id, flags) {
45
48
  const config = requireConfig();
46
49
  const org = await hq.getOrg(config.api_key, id);
47
50
  printJson(org);
51
+ if (org.preview_subdomain) {
52
+ info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
53
+ }
48
54
  }
49
55
  export async function del(id, flags) {
50
56
  if (flags.help) {
@@ -0,0 +1,3 @@
1
+ export declare function interactions(flags: Record<string, string | boolean>): Promise<void>;
2
+ export declare function identity(pixelId: string, flags: Record<string, string | boolean>): Promise<void>;
3
+ export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
@@ -0,0 +1,64 @@
1
+ import { pixel as sdkPixel } from '@myapihq/sdk';
2
+ import { requireConfig } from '../config.js';
3
+ import { error, printTable, info, printJson } from '../output.js';
4
+ export async function interactions(flags) {
5
+ const config = requireConfig();
6
+ const orgId = flags.org || config.default_org;
7
+ if (!orgId) {
8
+ error("Missing required arguments.\nUsage: myapi pixel interactions --org <id> [--website <domain>] [--campaign-id <id>] [--domain <domain>]\n(Or set defaults via: myapi config set-org <id>)");
9
+ }
10
+ const params = {};
11
+ if (flags.website)
12
+ params.website = flags.website;
13
+ if (flags['campaign-id'])
14
+ params.campaign_id = flags['campaign-id'];
15
+ if (flags.domain)
16
+ params.domain = flags.domain;
17
+ if (flags.from)
18
+ params.from = flags.from;
19
+ if (flags.to)
20
+ params.to = flags.to;
21
+ if (flags.limit)
22
+ params.limit = parseInt(flags.limit, 10);
23
+ if (flags.offset)
24
+ params.offset = parseInt(flags.offset, 10);
25
+ if (!params.website && !params.campaign_id && !params.domain) {
26
+ error("You must provide at least one filter: --website, --campaign-id, or --domain");
27
+ }
28
+ const res = await sdkPixel.getInteractions(config.api_key, orgId, params);
29
+ if (flags.json) {
30
+ printJson(res);
31
+ }
32
+ else {
33
+ printTable(res.interactions);
34
+ info(`Total Visits: ${res.total_visits} | Total Events: ${res.total_events} | Showing: ${res.limit} | Offset: ${res.offset}`);
35
+ }
36
+ }
37
+ export async function identity(pixelId, flags) {
38
+ const config = requireConfig();
39
+ const orgId = flags.org || config.default_org;
40
+ if (!orgId || !pixelId) {
41
+ error("Missing required arguments.\nUsage: myapi pixel identity <pixel_id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
42
+ }
43
+ const res = await sdkPixel.getIdentity(config.api_key, orgId, pixelId);
44
+ printJson(res);
45
+ }
46
+ export async function run(subcommand, args, flags) {
47
+ if (!subcommand || (flags.help && !subcommand)) {
48
+ info('Usage: myapi pixel <subcommand>\n\nSubcommands:\n interactions Get a unified timeline of visits and events\n identity Resolve the identity graph for a pixel ID\n\nNote: All pixel commands require the --org <id> flag.');
49
+ return;
50
+ }
51
+ if (flags.help) {
52
+ if (subcommand === 'interactions')
53
+ info('Usage: myapi pixel interactions --org <id> [--website <domain>] [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--json]\n\nRetrieves a merged timeline of web visits and email events. Requires at least one filter (--website, --campaign-id, or --domain).');
54
+ else if (subcommand === 'identity')
55
+ info('Usage: myapi pixel identity <pixel_id> --org <id>\n\nResolves the full identity graph (emails, IPs, profiles) for a specific pixel tracker ID.');
56
+ return;
57
+ }
58
+ if (subcommand === 'interactions')
59
+ await interactions(flags);
60
+ else if (subcommand === 'identity')
61
+ await identity(args[0], flags);
62
+ else
63
+ error(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for a list of valid subcommands.`);
64
+ }
@@ -1,3 +1,4 @@
1
1
  export declare function list(flags: Record<string, string | boolean>): Promise<void>;
2
2
  export declare function ingest(url: string, flags: Record<string, string | boolean>): Promise<void>;
3
3
  export declare function del(id: string, flags: Record<string, string | boolean>): Promise<void>;
4
+ export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
@@ -1,31 +1,55 @@
1
1
  import { storage as sdkStorage } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
- import { success, error, printTable } from '../output.js';
4
- function getOrgId(flags, config) {
5
- const orgId = flags.org || config.org_id;
6
- if (!orgId)
7
- error("Missing org-id. Pass --org or set it in config.");
8
- return orgId;
9
- }
3
+ import { success, error, printTable, info, printJson } from '../output.js';
10
4
  export async function list(flags) {
11
5
  const config = requireConfig();
12
- const orgId = getOrgId(flags, config);
6
+ const orgId = flags.org || config.default_org;
7
+ if (!orgId)
8
+ error("Missing required arguments.\nUsage: myapi storage list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
13
9
  const assets = await sdkStorage.listAssets(config.api_key, orgId);
14
- printTable(assets);
10
+ if (flags.json)
11
+ printJson(assets);
12
+ else
13
+ printTable(assets);
15
14
  }
16
15
  export async function ingest(url, flags) {
17
- if (!url)
18
- error("Missing url");
19
16
  const config = requireConfig();
20
- const orgId = getOrgId(flags, config);
17
+ const orgId = flags.org || config.default_org;
18
+ if (!orgId || !url) {
19
+ error("Missing required arguments.\nUsage: myapi storage ingest <url> [--name <name>] --org <id>\n(Or set defaults via: myapi config set-org <id>)");
20
+ }
21
21
  const res = await sdkStorage.ingestAsset(config.api_key, orgId, url, flags.name);
22
22
  success(`Asset ingested! ID: ${res.asset_id}\nHosted URL: ${res.url}`);
23
23
  }
24
24
  export async function del(id, flags) {
25
- if (!id)
26
- error("Missing id");
27
25
  const config = requireConfig();
28
- const orgId = getOrgId(flags, config);
26
+ const orgId = flags.org || config.default_org;
27
+ if (!orgId || !id) {
28
+ error("Missing required arguments.\nUsage: myapi storage delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
29
+ }
29
30
  await sdkStorage.deleteAsset(config.api_key, orgId, id);
30
31
  success(`Asset ${id} deleted`);
31
32
  }
33
+ export async function run(subcommand, args, flags) {
34
+ if (!subcommand || (flags.help && !subcommand)) {
35
+ info('Usage: myapi storage <subcommand>\n\nSubcommands:\n list List all your uploaded assets\n ingest Ingest a public image URL into your edge storage\n delete Delete a stored asset\n\nNote: All storage commands require the --org <id> flag.');
36
+ return;
37
+ }
38
+ if (flags.help) {
39
+ if (subcommand === 'list')
40
+ info('Usage: myapi storage list --org <id> [--json]');
41
+ else if (subcommand === 'ingest')
42
+ info('Usage: myapi storage ingest <url> [--name <name>] --org <id>\n\nDownloads a public image (JPEG/PNG) and permanently hosts it on your MyAPI storage. Returns the new URL.');
43
+ else if (subcommand === 'delete')
44
+ info('Usage: myapi storage delete <asset_id> --org <id>');
45
+ return;
46
+ }
47
+ if (subcommand === 'list')
48
+ await list(flags);
49
+ else if (subcommand === 'ingest')
50
+ await ingest(args[0], flags);
51
+ else if (subcommand === 'delete')
52
+ await del(args[0], flags);
53
+ else
54
+ error(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for a list of valid subcommands.`);
55
+ }
@@ -2,5 +2,6 @@ export declare function list(flags: Record<string, string | boolean>): Promise<v
2
2
  export declare function create(flags: Record<string, string | boolean>): Promise<void>;
3
3
  export declare function enable(id: string, flags: Record<string, string | boolean>): Promise<void>;
4
4
  export declare function disable(id: string, flags: Record<string, string | boolean>): Promise<void>;
5
+ export declare function del(id: string, flags: Record<string, string | boolean>): Promise<void>;
5
6
  export declare function runs(id: string, flags: Record<string, string | boolean>): Promise<void>;
6
7
  export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
@@ -49,7 +49,15 @@ export async function disable(id, flags) {
49
49
  if (!orgId || !id)
50
50
  error("Missing required arguments.\nUsage: myapi workflow disable <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
51
51
  await sdkWorkflow.disableWorkflow(config.api_key, orgId, id);
52
- success(`Workflow ${id} disabled`);
52
+ success(`Disabled workflow ${id}`);
53
+ }
54
+ export async function del(id, flags) {
55
+ const config = requireConfig();
56
+ const orgId = flags.org || config.default_org;
57
+ if (!orgId || !id)
58
+ error("Missing required arguments.\nUsage: myapi workflow delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
59
+ await sdkWorkflow.deleteWorkflow(config.api_key, orgId, id);
60
+ success(`Deleted workflow ${id}`);
53
61
  }
54
62
  export async function runs(id, flags) {
55
63
  const config = requireConfig();
@@ -64,7 +72,7 @@ export async function runs(id, flags) {
64
72
  }
65
73
  export async function run(subcommand, args, flags) {
66
74
  if (!subcommand || (flags.help && !subcommand)) {
67
- info('Usage: myapi workflow <subcommand>\n\nSubcommands:\n list List all workflows\n create Create and enable a new workflow\n enable Enable a workflow\n disable Disable a workflow\n runs List recent executions (runs) of a workflow\n\nNote: All workflow commands require the --org <id> flag.');
75
+ info('Usage: myapi workflow <subcommand>\n\nSubcommands:\n list List all workflows\n create Create and enable a new workflow\n enable Enable a workflow\n disable Disable a workflow\n delete Delete a workflow\n runs List recent executions (runs) of a workflow\n\nNote: All workflow commands require the --org <id> flag.');
68
76
  return;
69
77
  }
70
78
  if (flags.help) {
@@ -76,6 +84,8 @@ export async function run(subcommand, args, flags) {
76
84
  info('Usage: myapi workflow enable <id> --org <id>\n\nActivates a disabled workflow so it begins listening to its webhook trigger again.');
77
85
  else if (subcommand === 'disable')
78
86
  info('Usage: myapi workflow disable <id> --org <id>\n\nPauses a workflow. The attached webhook will still accept data, but the workflow steps will not execute.');
87
+ else if (subcommand === 'delete')
88
+ info('Usage: myapi workflow delete <id> --org <id>\n\nDeletes the workflow permanently.');
79
89
  else if (subcommand === 'runs')
80
90
  info('Usage: myapi workflow runs <workflow_id> --org <id> [--json]\n\nLists the 100 most recent executions of the specified workflow, including completion status and any error messages.');
81
91
  return;
@@ -88,6 +98,8 @@ export async function run(subcommand, args, flags) {
88
98
  await enable(args[0], flags);
89
99
  else if (subcommand === 'disable')
90
100
  await disable(args[0], flags);
101
+ else if (subcommand === 'delete')
102
+ await del(args[0], flags);
91
103
  else if (subcommand === 'runs')
92
104
  await runs(args[0], flags);
93
105
  else
package/dist/config.d.ts CHANGED
@@ -4,6 +4,7 @@ export interface Config {
4
4
  pin: string;
5
5
  default_org?: string;
6
6
  default_domain?: string;
7
+ autocomplete_setup?: boolean;
7
8
  }
8
9
  export declare function loadConfig(): Config | null;
9
10
  export declare function saveConfig(config: Config): void;
package/dist/config.js CHANGED
@@ -4,16 +4,24 @@ import * as os from 'os';
4
4
  const CONFIG_DIR = path.join(os.homedir(), '.myapi');
5
5
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
6
6
  export function loadConfig() {
7
+ let fileConfig = {};
7
8
  try {
8
- if (!fs.existsSync(CONFIG_FILE)) {
9
- return null;
9
+ if (fs.existsSync(CONFIG_FILE)) {
10
+ const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
11
+ fileConfig = JSON.parse(data);
10
12
  }
11
- const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
12
- return JSON.parse(data);
13
13
  }
14
14
  catch (err) {
15
+ // Ignore read errors
16
+ }
17
+ const envKey = process.env.MYAPI_KEY;
18
+ if (envKey) {
19
+ fileConfig.api_key = envKey;
20
+ }
21
+ if (Object.keys(fileConfig).length === 0) {
15
22
  return null;
16
23
  }
24
+ return fileConfig;
17
25
  }
18
26
  export function saveConfig(config) {
19
27
  if (!fs.existsSync(CONFIG_DIR)) {
@@ -24,7 +32,7 @@ export function saveConfig(config) {
24
32
  export function requireConfig() {
25
33
  const config = loadConfig();
26
34
  if (!config || !config.api_key) {
27
- console.error("No API key found. Run: myapi account create");
35
+ console.error("No API key found. Provide MYAPI_KEY env var or run: myapi setup");
28
36
  process.exit(1);
29
37
  }
30
38
  return config;
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // AUTO-GENERATED by scripts/generate-indexes.js — do not edit manually
3
3
  import { parseArgs } from './utils.js';
4
- import { error, info } from './output.js';
4
+ import { error, info, success } from './output.js';
5
+ import { loadConfig, saveConfig } from './config.js';
5
6
  import { MyApiError } from '@myapihq/sdk';
6
7
  import updateNotifier from 'update-notifier';
8
+ import omelette from 'omelette';
7
9
  import * as fs from 'fs';
8
10
  const pkgPath = new URL('../package.json', import.meta.url);
9
11
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
@@ -14,10 +16,29 @@ import * as setupCmd from './commands/setup.js';
14
16
  import * as configCmd from './commands/config.js';
15
17
  import * as domainCmd from './commands/domain.js';
16
18
  import * as funnelCmd from './commands/funnel.js';
19
+ import * as imageCmd from './commands/image.js';
20
+ import * as storageCmd from './commands/storage.js';
17
21
  import * as urlCmd from './commands/url.js';
18
22
  import * as webhookCmd from './commands/webhook.js';
19
23
  import * as workflowCmd from './commands/workflow.js';
20
24
  async function main() {
25
+ const completion = omelette('myapi');
26
+ completion.on('myapi', ({ reply }) => {
27
+ reply(['keys', 'billing', 'org', 'setup', 'config', 'domain', 'funnel', 'image', 'storage', 'url', 'webhook', 'workflow', 'autocomplete']);
28
+ });
29
+ completion.init();
30
+ const config = loadConfig();
31
+ if (config && !config.autocomplete_setup) {
32
+ try {
33
+ completion.setupShellInitFile();
34
+ config.autocomplete_setup = true;
35
+ saveConfig(config);
36
+ info('✨ Autocomplete has been auto-configured! Restart your terminal (or run: source ~/.bashrc) to use it.');
37
+ }
38
+ catch (e) {
39
+ // Ignore if we can't write to shell profiles
40
+ }
41
+ }
21
42
  updateNotifier({ pkg }).notify();
22
43
  const { args, flags } = parseArgs(process.argv.slice(2));
23
44
  if (args.length === 0) {
@@ -27,6 +48,10 @@ async function main() {
27
48
  const [command, subcommand, ...restArgs] = args;
28
49
  try {
29
50
  switch (command) {
51
+ case 'autocomplete':
52
+ completion.setupShellInitFile();
53
+ success('Autocomplete successfully configured! Please restart your terminal or run: source ~/.bashrc (or ~/.zshrc)');
54
+ break;
30
55
  case 'setup':
31
56
  if (flags.help) {
32
57
  info('Usage: myapi setup\n\nSetup CLI credentials and view integration instructions');
@@ -91,6 +116,12 @@ async function main() {
91
116
  case 'funnel':
92
117
  await funnelCmd.run(subcommand, restArgs, flags);
93
118
  break;
119
+ case 'image':
120
+ await imageCmd.run(subcommand, restArgs, flags);
121
+ break;
122
+ case 'storage':
123
+ await storageCmd.run(subcommand, restArgs, flags);
124
+ break;
94
125
  case 'url':
95
126
  await urlCmd.run(subcommand, restArgs, flags);
96
127
  break;
@@ -121,6 +152,7 @@ function printHelp() {
121
152
  Usage: myapi <command> [subcommand] [args]
122
153
 
123
154
  Commands:
155
+ autocomplete Autocomplete setup for bash/zsh
124
156
  keys Manage API keys
125
157
  billing Check balance and manage billing
126
158
  org Manage organizations
@@ -128,6 +160,8 @@ Commands:
128
160
  config Manage CLI defaults like org_id and domain
129
161
  domain Manage domain configurations
130
162
  funnel Manage headless funnels and pages
163
+ image Generate AI images
164
+ storage Manage static assets
131
165
  url Shorten URLs and manage links
132
166
  webhook Manage inbound webhooks
133
167
  workflow Manage workflow automations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "MyAPI command-line interface",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,10 +13,12 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@myapihq/sdk": "*",
16
+ "omelette": "^0.4.17",
16
17
  "update-notifier": "^7.3.1"
17
18
  },
18
19
  "devDependencies": {
19
20
  "@types/node": "^25.6.0",
21
+ "@types/omelette": "^0.4.5",
20
22
  "@types/update-notifier": "^6.0.8",
21
23
  "typescript": "^5.4.0"
22
24
  }
@@ -29,15 +29,11 @@ export async function importDomain(domainArg: string, flags: Record<string, stri
29
29
  const config = requireConfig();
30
30
  const orgId = (flags.org as string) || config.default_org;
31
31
  const domain = domainArg || (config.default_domain as string);
32
- if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> --namecheap-user <user> --namecheap-key <key>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
33
- if (!flags['namecheap-user'] || !flags['namecheap-key']) {
34
- error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> --namecheap-user <user> --namecheap-key <key>");
35
- }
36
- await sdkDomain.importDomain(config.api_key, orgId, {
37
- domain,
38
- namecheap_api_user: flags['namecheap-user'] as string,
39
- namecheap_api_key: flags['namecheap-key'] as string
40
- });
32
+ if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> [--namecheap-user <user> --namecheap-key <key>]\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
33
+ const payload: { domain: string; namecheap_api_user?: string; namecheap_api_key?: string } = { domain };
34
+ if (flags['namecheap-user']) payload.namecheap_api_user = flags['namecheap-user'] as string;
35
+ if (flags['namecheap-key']) payload.namecheap_api_key = flags['namecheap-key'] as string;
36
+ await sdkDomain.importDomain(config.api_key, orgId, payload);
41
37
  success(`Imported ${domain}`);
42
38
  }
43
39
 
@@ -80,13 +80,21 @@ export async function outbox(address: string, flags: Record<string, string | boo
80
80
  const messages = await sdkEmail.getOutbox(config.api_key, orgId, address);
81
81
  printTable(messages as unknown as Record<string, unknown>[]);
82
82
  }
83
-
84
83
  export async function listTemplates(flags: Record<string, string | boolean>) {
85
84
  const config = requireConfig();
86
85
  const orgId = (flags.org as string) || config.default_org;
87
- if (!orgId) error("Missing required arguments.\nUsage: myapi email list-templates --org <id>\n(Or set a default org via: myapi config set-org <id>)");
86
+ if (!orgId) error("Missing required arguments.\nUsage: myapi email list-templates --org <id>\n(Or set defaults via: myapi config set-org <id>)");
88
87
  const templates = await sdkEmail.listTemplates(config.api_key, orgId);
89
- printTable(templates as unknown as Record<string, unknown>[]);
88
+ if (flags.json) printJson(templates);
89
+ else printTable(templates as unknown as Record<string, unknown>[]);
90
+ }
91
+
92
+ export async function deleteTemplate(id: string, flags: Record<string, string | boolean>) {
93
+ const config = requireConfig();
94
+ const orgId = (flags.org as string) || config.default_org;
95
+ if (!orgId || !id) error("Missing required arguments.\nUsage: myapi email delete-template <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
96
+ await sdkEmail.deleteTemplate(config.api_key, orgId, id);
97
+ success(`Deleted template ${id}`);
90
98
  }
91
99
 
92
100
  export async function generateTemplate(flags: Record<string, string | boolean>) {
@@ -143,7 +151,7 @@ export async function campaignStats(id: string, flags: Record<string, string | b
143
151
 
144
152
  export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
145
153
  if (!subcommand || (flags.help && !subcommand)) {
146
- info('Usage: myapi email <subcommand>\n\nSubcommands:\n create-mailbox Create a new mailbox\n list-mailboxes List mailboxes\n send Send an email\n sent List sent emails\n inbox Read received emails\n outbox Read sent emails for address\n list-templates List email templates\n generate-template Generate AI template\n list-campaigns List campaigns\n campaign-stats Get campaign stats\n\nNote: All email commands require the --org <id> flag.');
154
+ info('Usage: myapi email <subcommand>\n\nSubcommands:\n create-mailbox Create a new mailbox\n list-mailboxes List mailboxes\n send Send an email\n sent List sent emails\n inbox Read received emails\n outbox Read sent emails for address\n list-templates List email templates\n generate-template Generate AI template\n delete-template Delete a template\n list-campaigns List campaigns\n campaign-stats Get campaign stats\n\nNote: All email commands require the --org <id> flag.');
147
155
  return;
148
156
  }
149
157
 
@@ -156,6 +164,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
156
164
  else if (subcommand === 'outbox') info('Usage: myapi email outbox <address> --org <id>');
157
165
  else if (subcommand === 'list-templates') info('Usage: myapi email list-templates --org <id>');
158
166
  else if (subcommand === 'generate-template') info('Usage: myapi email generate-template --org <id> --prompt <str> --name <str>');
167
+ else if (subcommand === 'delete-template') info('Usage: myapi email delete-template <id> --org <id>');
159
168
  else if (subcommand === 'list-campaigns') info('Usage: myapi email list-campaigns --org <id>');
160
169
  else if (subcommand === 'campaign-stats') info('Usage: myapi email campaign-stats <campaign_id> --org <id>');
161
170
  return;
@@ -169,6 +178,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
169
178
  else if (subcommand === 'outbox') await outbox(args[0], flags);
170
179
  else if (subcommand === 'list-templates') await listTemplates(flags);
171
180
  else if (subcommand === 'generate-template') await generateTemplate(flags);
181
+ else if (subcommand === 'delete-template') await deleteTemplate(args[0], flags);
172
182
  else if (subcommand === 'list-campaigns') await listCampaigns(flags);
173
183
  else if (subcommand === 'campaign-stats') await campaignStats(args[0], flags);
174
184
  else error(`Unknown subcommand: ${subcommand}. Run "myapi email --help" for a list of valid subcommands.`);
@@ -1,4 +1,4 @@
1
- import { funnel as sdkFunnel } from '@myapihq/sdk';
1
+ import { funnel as sdkFunnel, hq } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { success, error, printTable, info, printJson } from '../output.js';
4
4
  import * as fs from 'fs';
@@ -22,6 +22,10 @@ export async function create(flags: Record<string, string | boolean>) {
22
22
 
23
23
  const funnel = await sdkFunnel.createFunnel(config.api_key, orgId, domain);
24
24
  success(`Funnel created! ID: ${funnel.id}`);
25
+ const org = await hq.getOrg(config.api_key, orgId);
26
+ if (org.preview_subdomain) {
27
+ info(`Preview: https://${org.preview_subdomain}.makeautonomous.com`);
28
+ }
25
29
  }
26
30
 
27
31
  export async function get(id: string, flags: Record<string, string | boolean>) {
@@ -58,8 +62,16 @@ export async function push(id: string, slug: string, flags: Record<string, strin
58
62
 
59
63
  if (!html) error("No HTML provided via stdin");
60
64
 
61
- await sdkFunnel.pushFunnelPage(config.api_key, orgId, id, { slug, html });
65
+ const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, id, { slug, html });
62
66
  success(`Pushed page to ${slug}`);
67
+ if (result?.url) {
68
+ info(`Preview: ${result.url}`);
69
+ } else {
70
+ const org = await hq.getOrg(config.api_key, orgId);
71
+ if (org.preview_subdomain) {
72
+ info(`Preview: https://${org.preview_subdomain}.makeautonomous.com/${slug}`);
73
+ }
74
+ }
63
75
  }
64
76
 
65
77
  export async function verify(id: string, flags: Record<string, string | boolean>) {
@@ -1,18 +1,22 @@
1
1
  import { image as sdkImage } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
- import { success, error, printTable } from '../output.js';
3
+ import { success, error, printTable, info, printJson } from '../output.js';
4
4
  import { sleep } from '../utils.js';
5
5
 
6
6
  export async function generate(flags: Record<string, string | boolean>) {
7
- if (!flags.prompt) error("Missing --prompt flag");
8
7
  const config = requireConfig();
8
+ const orgId = (flags.org as string) || config.default_org;
9
+
10
+ if (!orgId || !flags.prompt) {
11
+ error("Missing required arguments.\nUsage: myapi image generate --prompt <text> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
12
+ }
9
13
 
10
14
  const payload: any = { prompt: flags.prompt as string };
11
15
  if (flags.ratio) payload.aspect_ratio = flags.ratio as string;
12
16
  if (flags.style) payload.style = flags.style as string;
13
17
  if (flags.colors) payload.colors = flags.colors as string;
14
18
 
15
- const res = await sdkImage.generateImage(config.api_key, payload);
19
+ const res = await sdkImage.generateImage(config.api_key, orgId, payload);
16
20
 
17
21
  process.stdout.write("Generating image ");
18
22
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
@@ -20,10 +24,11 @@ export async function generate(flags: Record<string, string | boolean>) {
20
24
  let elapsed = 0;
21
25
 
22
26
  while (elapsed < 60000) {
23
- const job = await sdkImage.getImageJob(config.api_key, res.job_id);
27
+ const job = await sdkImage.getImageJob(config.api_key, orgId, res.job_id);
24
28
  if (job.status === 'completed') {
25
29
  process.stdout.write('\r\x1b[K');
26
- success(`Image generated!\nURL: ${job.url}`);
30
+ if (flags.json) printJson(job);
31
+ else success(`Image generated!\nURL: ${job.url}`);
27
32
  return;
28
33
  }
29
34
  if (job.status === 'failed') {
@@ -40,6 +45,41 @@ export async function generate(flags: Record<string, string | boolean>) {
40
45
 
41
46
  export async function list(flags: Record<string, string | boolean>) {
42
47
  const config = requireConfig();
43
- const images = await sdkImage.listImages(config.api_key);
44
- printTable(images as unknown as Record<string, unknown>[]);
48
+ const orgId = (flags.org as string) || config.default_org;
49
+ if (!orgId) error("Missing required arguments.\nUsage: myapi image list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
50
+
51
+ const images = await sdkImage.listImages(config.api_key, orgId);
52
+ if (flags.json) printJson(images);
53
+ else printTable(images as unknown as Record<string, unknown>[]);
54
+ }
55
+
56
+ export async function del(id: string, flags: Record<string, string | boolean>) {
57
+ const config = requireConfig();
58
+ const orgId = (flags.org as string) || config.default_org;
59
+
60
+ if (!orgId || !id) {
61
+ error("Missing required arguments.\nUsage: myapi image delete <job_id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
62
+ }
63
+
64
+ await sdkImage.deleteImage(config.api_key, orgId, id);
65
+ success(`Image deleted! (Job ${id} history retained, URL nullified)`);
66
+ }
67
+
68
+ export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
69
+ if (!subcommand || (flags.help && !subcommand)) {
70
+ info('Usage: myapi image <subcommand>\n\nSubcommands:\n list List all your generated images\n generate Generate a new AI image from a prompt\n delete Delete the physical image file for a job\n\nNote: All image commands require the --org <id> flag.');
71
+ return;
72
+ }
73
+
74
+ if (flags.help) {
75
+ if (subcommand === 'list') info('Usage: myapi image list --org <id> [--json]');
76
+ else if (subcommand === 'generate') info('Usage: myapi image generate --prompt "<text>" [--ratio <1:1|16:9>] [--style <style>] [--colors <hex_list>] --org <id>\n\nGenerates an AI image and polls until the public URL is ready. Costs $0.05 per generation.');
77
+ else if (subcommand === 'delete') info('Usage: myapi image delete <job_id> --org <id>\n\nDeletes the physical image file from storage. The generation job history is kept, but its URL will become null.');
78
+ return;
79
+ }
80
+
81
+ if (subcommand === 'list') await list(flags);
82
+ else if (subcommand === 'generate') await generate(flags);
83
+ else if (subcommand === 'delete') await del(args[0], flags);
84
+ else error(`Unknown subcommand: ${subcommand}. Run "myapi image --help" for a list of valid subcommands.`);
45
85
  }
@@ -21,6 +21,9 @@ export async function create(flags: Record<string, string | boolean>) {
21
21
 
22
22
  const org = await hq.createOrg(config.api_key, payload);
23
23
  success(`Org created! ID: ${org.id}, Name: ${org.name}`);
24
+ if (org.preview_subdomain) {
25
+ info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
26
+ }
24
27
  }
25
28
 
26
29
  export async function list(flags: Record<string, string | boolean>) {
@@ -45,6 +48,9 @@ export async function get(id: string, flags: Record<string, string | boolean>) {
45
48
  const config = requireConfig();
46
49
  const org = await hq.getOrg(config.api_key, id);
47
50
  printJson(org);
51
+ if (org.preview_subdomain) {
52
+ info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
53
+ }
48
54
  }
49
55
 
50
56
  export async function del(id: string, flags: Record<string, string | boolean>) {
@@ -0,0 +1,62 @@
1
+ import { pixel as sdkPixel } from '@myapihq/sdk';
2
+ import { requireConfig } from '../config.js';
3
+ import { success, error, printTable, info, printJson } from '../output.js';
4
+
5
+ export async function interactions(flags: Record<string, string | boolean>) {
6
+ const config = requireConfig();
7
+ const orgId = (flags.org as string) || config.default_org;
8
+
9
+ if (!orgId) {
10
+ error("Missing required arguments.\nUsage: myapi pixel interactions --org <id> [--website <domain>] [--campaign-id <id>] [--domain <domain>]\n(Or set defaults via: myapi config set-org <id>)");
11
+ }
12
+
13
+ const params: any = {};
14
+ if (flags.website) params.website = flags.website as string;
15
+ if (flags['campaign-id']) params.campaign_id = flags['campaign-id'] as string;
16
+ if (flags.domain) params.domain = flags.domain as string;
17
+ if (flags.from) params.from = flags.from as string;
18
+ if (flags.to) params.to = flags.to as string;
19
+ if (flags.limit) params.limit = parseInt(flags.limit as string, 10);
20
+ if (flags.offset) params.offset = parseInt(flags.offset as string, 10);
21
+
22
+ if (!params.website && !params.campaign_id && !params.domain) {
23
+ error("You must provide at least one filter: --website, --campaign-id, or --domain");
24
+ }
25
+
26
+ const res = await sdkPixel.getInteractions(config.api_key, orgId, params);
27
+ if (flags.json) {
28
+ printJson(res);
29
+ } else {
30
+ printTable(res.interactions as unknown as Record<string, unknown>[]);
31
+ info(`Total Visits: ${res.total_visits} | Total Events: ${res.total_events} | Showing: ${res.limit} | Offset: ${res.offset}`);
32
+ }
33
+ }
34
+
35
+ export async function identity(pixelId: string, flags: Record<string, string | boolean>) {
36
+ const config = requireConfig();
37
+ const orgId = (flags.org as string) || config.default_org;
38
+
39
+ if (!orgId || !pixelId) {
40
+ error("Missing required arguments.\nUsage: myapi pixel identity <pixel_id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
41
+ }
42
+
43
+ const res = await sdkPixel.getIdentity(config.api_key, orgId, pixelId);
44
+ printJson(res);
45
+ }
46
+
47
+ export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
48
+ if (!subcommand || (flags.help && !subcommand)) {
49
+ info('Usage: myapi pixel <subcommand>\n\nSubcommands:\n interactions Get a unified timeline of visits and events\n identity Resolve the identity graph for a pixel ID\n\nNote: All pixel commands require the --org <id> flag.');
50
+ return;
51
+ }
52
+
53
+ if (flags.help) {
54
+ if (subcommand === 'interactions') info('Usage: myapi pixel interactions --org <id> [--website <domain>] [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--json]\n\nRetrieves a merged timeline of web visits and email events. Requires at least one filter (--website, --campaign-id, or --domain).');
55
+ else if (subcommand === 'identity') info('Usage: myapi pixel identity <pixel_id> --org <id>\n\nResolves the full identity graph (emails, IPs, profiles) for a specific pixel tracker ID.');
56
+ return;
57
+ }
58
+
59
+ if (subcommand === 'interactions') await interactions(flags);
60
+ else if (subcommand === 'identity') await identity(args[0], flags);
61
+ else error(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for a list of valid subcommands.`);
62
+ }
@@ -1,32 +1,56 @@
1
1
  import { storage as sdkStorage } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
- import { success, error, printTable } from '../output.js';
4
-
5
- function getOrgId(flags: Record<string, string | boolean>, config: any): string {
6
- const orgId = flags.org || config.org_id;
7
- if (!orgId) error("Missing org-id. Pass --org or set it in config.");
8
- return orgId as string;
9
- }
3
+ import { success, error, printTable, info, printJson } from '../output.js';
10
4
 
11
5
  export async function list(flags: Record<string, string | boolean>) {
12
6
  const config = requireConfig();
13
- const orgId = getOrgId(flags, config);
7
+ const orgId = (flags.org as string) || config.default_org;
8
+ if (!orgId) error("Missing required arguments.\nUsage: myapi storage list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
9
+
14
10
  const assets = await sdkStorage.listAssets(config.api_key, orgId);
15
- printTable(assets as unknown as Record<string, unknown>[]);
11
+ if (flags.json) printJson(assets);
12
+ else printTable(assets as unknown as Record<string, unknown>[]);
16
13
  }
17
14
 
18
15
  export async function ingest(url: string, flags: Record<string, string | boolean>) {
19
- if (!url) error("Missing url");
20
16
  const config = requireConfig();
21
- const orgId = getOrgId(flags, config);
17
+ const orgId = (flags.org as string) || config.default_org;
18
+
19
+ if (!orgId || !url) {
20
+ error("Missing required arguments.\nUsage: myapi storage ingest <url> [--name <name>] --org <id>\n(Or set defaults via: myapi config set-org <id>)");
21
+ }
22
+
22
23
  const res = await sdkStorage.ingestAsset(config.api_key, orgId, url, flags.name as string);
23
24
  success(`Asset ingested! ID: ${res.asset_id}\nHosted URL: ${res.url}`);
24
25
  }
25
26
 
26
27
  export async function del(id: string, flags: Record<string, string | boolean>) {
27
- if (!id) error("Missing id");
28
28
  const config = requireConfig();
29
- const orgId = getOrgId(flags, config);
29
+ const orgId = (flags.org as string) || config.default_org;
30
+
31
+ if (!orgId || !id) {
32
+ error("Missing required arguments.\nUsage: myapi storage delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
33
+ }
34
+
30
35
  await sdkStorage.deleteAsset(config.api_key, orgId, id);
31
36
  success(`Asset ${id} deleted`);
32
37
  }
38
+
39
+ export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
40
+ if (!subcommand || (flags.help && !subcommand)) {
41
+ info('Usage: myapi storage <subcommand>\n\nSubcommands:\n list List all your uploaded assets\n ingest Ingest a public image URL into your edge storage\n delete Delete a stored asset\n\nNote: All storage commands require the --org <id> flag.');
42
+ return;
43
+ }
44
+
45
+ if (flags.help) {
46
+ if (subcommand === 'list') info('Usage: myapi storage list --org <id> [--json]');
47
+ else if (subcommand === 'ingest') info('Usage: myapi storage ingest <url> [--name <name>] --org <id>\n\nDownloads a public image (JPEG/PNG) and permanently hosts it on your MyAPI storage. Returns the new URL.');
48
+ else if (subcommand === 'delete') info('Usage: myapi storage delete <asset_id> --org <id>');
49
+ return;
50
+ }
51
+
52
+ if (subcommand === 'list') await list(flags);
53
+ else if (subcommand === 'ingest') await ingest(args[0], flags);
54
+ else if (subcommand === 'delete') await del(args[0], flags);
55
+ else error(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for a list of valid subcommands.`);
56
+ }
@@ -52,9 +52,18 @@ export async function disable(id: string, flags: Record<string, string | boolean
52
52
  const config = requireConfig();
53
53
  const orgId = (flags.org as string) || config.default_org;
54
54
  if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow disable <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
55
-
55
+
56
56
  await sdkWorkflow.disableWorkflow(config.api_key, orgId, id);
57
- success(`Workflow ${id} disabled`);
57
+ success(`Disabled workflow ${id}`);
58
+ }
59
+
60
+ export async function del(id: string, flags: Record<string, string | boolean>) {
61
+ const config = requireConfig();
62
+ const orgId = (flags.org as string) || config.default_org;
63
+ if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
64
+
65
+ await sdkWorkflow.deleteWorkflow(config.api_key, orgId, id);
66
+ success(`Deleted workflow ${id}`);
58
67
  }
59
68
 
60
69
  export async function runs(id: string, flags: Record<string, string | boolean>) {
@@ -69,7 +78,7 @@ export async function runs(id: string, flags: Record<string, string | boolean>)
69
78
 
70
79
  export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
71
80
  if (!subcommand || (flags.help && !subcommand)) {
72
- info('Usage: myapi workflow <subcommand>\n\nSubcommands:\n list List all workflows\n create Create and enable a new workflow\n enable Enable a workflow\n disable Disable a workflow\n runs List recent executions (runs) of a workflow\n\nNote: All workflow commands require the --org <id> flag.');
81
+ info('Usage: myapi workflow <subcommand>\n\nSubcommands:\n list List all workflows\n create Create and enable a new workflow\n enable Enable a workflow\n disable Disable a workflow\n delete Delete a workflow\n runs List recent executions (runs) of a workflow\n\nNote: All workflow commands require the --org <id> flag.');
73
82
  return;
74
83
  }
75
84
 
@@ -78,6 +87,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
78
87
  else if (subcommand === 'create') info('Usage: myapi workflow create --name <name> --endpoint-id <id> --steps <json_array> --org <id>\n\nCreates a new webhook-triggered workflow and immediately enables it.\n\nArguments:\n --name A friendly name for your workflow.\n --endpoint-id The UUID of the my-webhook-api endpoint that will trigger this workflow.\n --steps A raw JSON array defining the actions to take when triggered.\n\nExample Steps Payload:\n \'[{"type": "send_email", "from": "hello@example.com", "to": "{{payload.user.email}}", "subject": "Welcome!", "template_id": "abc-123"}]\n \'[{"type": "slack_message", "webhook_url": "https://hooks.slack.com/...", "text": "New lead: {{payload.name}}"}]\'');
79
88
  else if (subcommand === 'enable') info('Usage: myapi workflow enable <id> --org <id>\n\nActivates a disabled workflow so it begins listening to its webhook trigger again.');
80
89
  else if (subcommand === 'disable') info('Usage: myapi workflow disable <id> --org <id>\n\nPauses a workflow. The attached webhook will still accept data, but the workflow steps will not execute.');
90
+ else if (subcommand === 'delete') info('Usage: myapi workflow delete <id> --org <id>\n\nDeletes the workflow permanently.');
81
91
  else if (subcommand === 'runs') info('Usage: myapi workflow runs <workflow_id> --org <id> [--json]\n\nLists the 100 most recent executions of the specified workflow, including completion status and any error messages.');
82
92
  return;
83
93
  }
@@ -86,6 +96,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
86
96
  else if (subcommand === 'create') await create(flags);
87
97
  else if (subcommand === 'enable') await enable(args[0], flags);
88
98
  else if (subcommand === 'disable') await disable(args[0], flags);
99
+ else if (subcommand === 'delete') await del(args[0], flags);
89
100
  else if (subcommand === 'runs') await runs(args[0], flags);
90
101
  else error(`Unknown subcommand: ${subcommand}. Run "myapi workflow --help" for a list of valid subcommands.`);
91
102
  }
package/src/config.ts CHANGED
@@ -8,21 +8,33 @@ export interface Config {
8
8
  pin: string;
9
9
  default_org?: string;
10
10
  default_domain?: string;
11
+ autocomplete_setup?: boolean;
11
12
  }
12
13
 
13
14
  const CONFIG_DIR = path.join(os.homedir(), '.myapi');
14
15
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
15
16
 
16
17
  export function loadConfig(): Config | null {
18
+ let fileConfig: Partial<Config> = {};
17
19
  try {
18
- if (!fs.existsSync(CONFIG_FILE)) {
19
- return null;
20
+ if (fs.existsSync(CONFIG_FILE)) {
21
+ const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
22
+ fileConfig = JSON.parse(data) as Partial<Config>;
20
23
  }
21
- const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
22
- return JSON.parse(data) as Config;
23
24
  } catch (err) {
25
+ // Ignore read errors
26
+ }
27
+
28
+ const envKey = process.env.MYAPI_KEY;
29
+ if (envKey) {
30
+ fileConfig.api_key = envKey;
31
+ }
32
+
33
+ if (Object.keys(fileConfig).length === 0) {
24
34
  return null;
25
35
  }
36
+
37
+ return fileConfig as Config;
26
38
  }
27
39
 
28
40
  export function saveConfig(config: Config): void {
@@ -35,7 +47,7 @@ export function saveConfig(config: Config): void {
35
47
  export function requireConfig(): Config {
36
48
  const config = loadConfig();
37
49
  if (!config || !config.api_key) {
38
- console.error("No API key found. Run: myapi account create");
50
+ console.error("No API key found. Provide MYAPI_KEY env var or run: myapi setup");
39
51
  process.exit(1);
40
52
  }
41
53
  return config;
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // AUTO-GENERATED by scripts/generate-indexes.js — do not edit manually
3
3
  import { parseArgs } from './utils.js';
4
- import { error, info } from './output.js';
4
+ import { error, info, success } from './output.js';
5
+ import { loadConfig, saveConfig } from './config.js';
5
6
  import { MyApiError } from '@myapihq/sdk';
6
7
  import updateNotifier from 'update-notifier';
8
+ import omelette from 'omelette';
7
9
  import * as fs from 'fs';
8
10
  import * as path from 'path';
9
11
 
@@ -16,11 +18,31 @@ import * as setupCmd from './commands/setup.js';
16
18
  import * as configCmd from './commands/config.js';
17
19
  import * as domainCmd from './commands/domain.js';
18
20
  import * as funnelCmd from './commands/funnel.js';
21
+ import * as imageCmd from './commands/image.js';
22
+ import * as storageCmd from './commands/storage.js';
19
23
  import * as urlCmd from './commands/url.js';
20
24
  import * as webhookCmd from './commands/webhook.js';
21
25
  import * as workflowCmd from './commands/workflow.js';
22
26
 
23
27
  async function main() {
28
+ const completion = omelette('myapi');
29
+ completion.on('myapi', ({ reply }) => {
30
+ reply(['keys', 'billing', 'org', 'setup', 'config', 'domain', 'funnel', 'image', 'storage', 'url', 'webhook', 'workflow', 'autocomplete']);
31
+ });
32
+ completion.init();
33
+
34
+ const config = loadConfig();
35
+ if (config && !config.autocomplete_setup) {
36
+ try {
37
+ completion.setupShellInitFile();
38
+ config.autocomplete_setup = true;
39
+ saveConfig(config);
40
+ info('✨ Autocomplete has been auto-configured! Restart your terminal (or run: source ~/.bashrc) to use it.');
41
+ } catch (e) {
42
+ // Ignore if we can't write to shell profiles
43
+ }
44
+ }
45
+
24
46
  updateNotifier({ pkg }).notify();
25
47
 
26
48
  const { args, flags } = parseArgs(process.argv.slice(2));
@@ -28,6 +50,10 @@ async function main() {
28
50
  const [command, subcommand, ...restArgs] = args;
29
51
  try {
30
52
  switch (command) {
53
+ case 'autocomplete':
54
+ completion.setupShellInitFile();
55
+ success('Autocomplete successfully configured! Please restart your terminal or run: source ~/.bashrc (or ~/.zshrc)');
56
+ break;
31
57
  case 'setup':
32
58
  if (flags.help) {
33
59
  info('Usage: myapi setup\n\nSetup CLI credentials and view integration instructions');
@@ -77,6 +103,12 @@ async function main() {
77
103
  case 'funnel':
78
104
  await funnelCmd.run(subcommand, restArgs, flags);
79
105
  break;
106
+ case 'image':
107
+ await imageCmd.run(subcommand, restArgs, flags);
108
+ break;
109
+ case 'storage':
110
+ await storageCmd.run(subcommand, restArgs, flags);
111
+ break;
80
112
  case 'url':
81
113
  await urlCmd.run(subcommand, restArgs, flags);
82
114
  break;
@@ -105,6 +137,7 @@ function printHelp() {
105
137
  Usage: myapi <command> [subcommand] [args]
106
138
 
107
139
  Commands:
140
+ autocomplete Autocomplete setup for bash/zsh
108
141
  keys Manage API keys
109
142
  billing Check balance and manage billing
110
143
  org Manage organizations
@@ -112,6 +145,8 @@ Commands:
112
145
  config Manage CLI defaults like org_id and domain
113
146
  domain Manage domain configurations
114
147
  funnel Manage headless funnels and pages
148
+ image Generate AI images
149
+ storage Manage static assets
115
150
  url Shorten URLs and manage links
116
151
  webhook Manage inbound webhooks
117
152
  workflow Manage workflow automations