@myapihq/cli 1.3.13 → 2.0.1

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.
Files changed (48) hide show
  1. package/dist/commands/account.d.ts +7 -1
  2. package/dist/commands/account.js +356 -18
  3. package/dist/commands/authproduct.d.ts +7 -0
  4. package/dist/commands/authproduct.js +286 -0
  5. package/dist/commands/billing.js +1 -1
  6. package/dist/commands/config.js +1 -1
  7. package/dist/commands/container.d.ts +1 -0
  8. package/dist/commands/container.js +115 -11
  9. package/dist/commands/domain.js +2 -2
  10. package/dist/commands/email/index.js +0 -13
  11. package/dist/commands/fn.d.ts +1 -0
  12. package/dist/commands/fn.js +64 -12
  13. package/dist/commands/keys.js +3 -3
  14. package/dist/commands/queue.d.ts +1 -0
  15. package/dist/commands/queue.js +10 -0
  16. package/dist/commands/setup.js +8 -8
  17. package/dist/commands/status.d.ts +1 -1
  18. package/dist/commands/status.js +11 -27
  19. package/dist/commands/storage.js +3 -1
  20. package/dist/commands/workflow.js +21 -6
  21. package/dist/completion.js +5 -4
  22. package/dist/config.js +1 -1
  23. package/dist/exposes.test.js +1 -2
  24. package/dist/index.js +31 -28
  25. package/dist/registrant.js +5 -5
  26. package/dist/sdk-queue.test.js +3 -2
  27. package/dist/skills/my-api-hq/SKILL.md +15 -12
  28. package/dist/skills/my-auth-api/README.md +33 -0
  29. package/dist/skills/my-auth-api/SKILL.md +112 -0
  30. package/dist/skills/my-auth-api/claude/.claude-plugin/plugin.json +6 -0
  31. package/dist/skills/my-auth-api/openapi/.gitkeep +0 -0
  32. package/dist/skills/my-crm-api/SKILL.md +1 -1
  33. package/dist/skills/my-domain-api/SKILL.md +4 -4
  34. package/dist/skills/my-email-api/README.md +1 -1
  35. package/dist/skills/my-email-api/SKILL.md +7 -18
  36. package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +1 -1
  37. package/dist/skills/my-email-verify-api/SKILL.md +1 -1
  38. package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +1 -1
  39. package/dist/skills/my-git-api/README.md +43 -0
  40. package/dist/skills/my-git-api/SKILL.md +115 -0
  41. package/dist/skills/my-git-api/claude/.claude-plugin/plugin.json +6 -0
  42. package/dist/skills/my-git-api/openapi/.gitkeep +0 -0
  43. package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +1 -1
  44. package/package.json +2 -2
  45. package/dist/commands/auth.d.ts +0 -11
  46. package/dist/commands/auth.js +0 -345
  47. package/dist/commands/email/campaign.d.ts +0 -4
  48. package/dist/commands/email/campaign.js +0 -200
@@ -0,0 +1,286 @@
1
+ import { auth as sdkAuth } from '@myapihq/sdk';
2
+ import { requireConfig } from '../config.js';
3
+ import { success, error, info, printTable, printJson } from '../output.js';
4
+ import { requireOrg } from '../helpers.js';
5
+ // `myapi auth` — the END-USER auth product (my-auth-api): a managed OIDC IdP
6
+ // for the users of apps built on MyAPI. Operator/account auth lives under
7
+ // `myapi account` (see commands/auth.ts + index.ts dispatch). The machine
8
+ // OIDC endpoints (discovery/jwks/authorize/token/userinfo) are consumed by
9
+ // apps + the JS SDK, not the CLI, so only the management surface is here.
10
+ export const SCHEMA = {
11
+ name: 'string',
12
+ type: 'string', // spa | web
13
+ redirect: 'string', // comma-separated redirect URIs
14
+ theme: 'string', // JSON for the hosted login page
15
+ connections: 'string', // comma-separated sign-in methods: google,password,magic
16
+ domain: 'string', // custom auth domain, e.g. auth.acme.com
17
+ };
18
+ export const EXPOSES = [
19
+ 'POST /auth/orgs/{org_id}/tenant',
20
+ 'GET /auth/orgs/{org_id}/tenant',
21
+ 'POST /auth/orgs/{org_id}/clients',
22
+ 'GET /auth/orgs/{org_id}/clients',
23
+ 'GET /auth/orgs/{org_id}/usage',
24
+ 'POST /auth/orgs/{org_id}/domain',
25
+ 'GET /auth/orgs/{org_id}/domain',
26
+ 'DELETE /auth/orgs/{org_id}/domain',
27
+ ];
28
+ const SUBCOMMAND_USAGE = {
29
+ 'tenant': `myapi auth tenant [show|create] [--connections <list>] [--theme <json>] [--org <id>] [--json]
30
+
31
+ Your org's OIDC auth tenant — the identity provider your app's end users sign
32
+ in against. One per org.
33
+
34
+ myapi auth tenant Show the tenant (issuer + hosted login URL)
35
+ myapi auth tenant create Create/enable it (idempotent)
36
+
37
+ --connections <list> Sign-in methods on the hosted login page, comma-separated:
38
+ google,password,magic (default: google).
39
+ --theme <json> Branding JSON for the hosted login page.`,
40
+ 'usage': `myapi auth usage [--org <id>] [--json]
41
+
42
+ Monthly active users (MAU) for the current period — auth is billed per MAU.`,
43
+ 'domain': `myapi auth domain [show|set|delete] [--domain <host>] [--org <id>] [--json]
44
+
45
+ Serve auth on your own domain (e.g. auth.acme.com) instead of the default issuer.
46
+
47
+ myapi auth domain Show the current custom domain + status
48
+ myapi auth domain set --domain auth.acme.com Register it (prints the DNS record to create)
49
+ myapi auth domain delete Remove the custom domain
50
+
51
+ After 'set', create the printed DNS A record; TLS provisions automatically
52
+ (~30 min) and the domain becomes your issuer once active.`,
53
+ 'client': `myapi auth client <list|create> [--org <id>] [--json]
54
+
55
+ OIDC clients are the apps that authenticate against your tenant.
56
+
57
+ myapi auth client list
58
+ myapi auth client create --name "My App" --type spa --redirect https://app.example.com/callback
59
+
60
+ --name <n> Human label for the app (required)
61
+ --type spa|web spa = public (no secret); web = confidential (secret once)
62
+ --redirect <urls> Allowed redirect URIs, comma-separated (required).
63
+ Absolute https (or http://localhost for dev).`,
64
+ };
65
+ export const HELP = `Usage: myapi auth <subcommand>
66
+
67
+ Authentication for your app's end users — a managed OIDC identity provider
68
+ (à la Kinde/Auth0). One auth tenant per org; register OIDC clients (apps)
69
+ against it; sign users in via the hosted login page or the JS SDK; verify
70
+ tokens locally against the tenant JWKS.
71
+
72
+ End users sign in with managed Google, email/password, or magic links — pick
73
+ which via \`tenant create --connections\`. (Operator/account commands moved to
74
+ \`myapi account\`.)
75
+
76
+ Subcommands:
77
+ client Register and list OIDC clients (your apps)
78
+ domain Serve auth on your own domain (auth.acme.com)
79
+ tenant Show or create your org's OIDC auth tenant (+ sign-in methods)
80
+ usage Monthly active users (MAU) for the current period`;
81
+ export async function run(subcommand, args, flags) {
82
+ if (!subcommand || (flags.help && !subcommand)) {
83
+ info(HELP);
84
+ return;
85
+ }
86
+ if (flags.help) {
87
+ const usage = SUBCOMMAND_USAGE[subcommand];
88
+ info(usage ? `Usage: ${usage}` : `Unknown subcommand "${subcommand}". Run "myapi auth --help".`);
89
+ return;
90
+ }
91
+ switch (subcommand) {
92
+ case 'tenant': return tenant(args, flags);
93
+ case 'client': return client(args, flags);
94
+ case 'usage': return usage(flags);
95
+ case 'domain': return domain(args, flags);
96
+ default: error(`Unknown subcommand "${subcommand}". Run "myapi auth --help" for tenant/client/usage/domain.`);
97
+ }
98
+ }
99
+ async function tenant(args, flags) {
100
+ const action = args[0] || 'show';
101
+ const config = requireConfig();
102
+ const orgId = requireOrg(flags, config, 'myapi auth tenant [show|create] [--org <id>]');
103
+ if (action === 'show') {
104
+ try {
105
+ const t = await sdkAuth.getTenant(config.api_key, orgId);
106
+ if (flags.json) {
107
+ printJson(t);
108
+ return;
109
+ }
110
+ info(`Auth tenant: ${t.tenant_id}`);
111
+ info(`Issuer: ${t.issuer}`);
112
+ info(`Login URL: ${t.login_url}`);
113
+ if (t.connections?.length)
114
+ info(`Sign-in: ${t.connections.join(', ')}`);
115
+ }
116
+ catch (e) {
117
+ if (e?.code === 'TENANT_NOT_FOUND' || e?.status === 404) {
118
+ error('No auth tenant yet for this org.\nCreate it with: myapi auth tenant create');
119
+ }
120
+ throw e;
121
+ }
122
+ return;
123
+ }
124
+ if (action === 'create' || action === 'init') {
125
+ let theme;
126
+ if (flags.theme) {
127
+ try {
128
+ theme = JSON.parse(flags.theme);
129
+ }
130
+ catch {
131
+ error('--theme must be valid JSON');
132
+ }
133
+ }
134
+ let connections;
135
+ if (flags.connections) {
136
+ const allowed = ['google', 'password', 'magic'];
137
+ connections = flags.connections.split(',').map(s => s.trim()).filter(Boolean);
138
+ const bad = connections.filter(c => !allowed.includes(c));
139
+ if (bad.length)
140
+ error(`--connections may only contain google, password, magic (got: ${bad.join(', ')})`);
141
+ }
142
+ const t = await sdkAuth.createTenant(config.api_key, orgId, { theme, connections });
143
+ if (flags.json) {
144
+ printJson(t);
145
+ return;
146
+ }
147
+ success(`Auth tenant ready: ${t.tenant_id}`);
148
+ info(`Issuer: ${t.issuer}`);
149
+ info(`Login URL: ${t.login_url}`);
150
+ if (t.connections?.length)
151
+ info(`Sign-in: ${t.connections.join(', ')}`);
152
+ info('Next: register an app → myapi auth client create --name <n> --type spa --redirect <url>');
153
+ return;
154
+ }
155
+ error(`Unknown action "${action}". Use: myapi auth tenant [show|create]`);
156
+ }
157
+ async function usage(flags) {
158
+ const config = requireConfig();
159
+ const orgId = requireOrg(flags, config, 'myapi auth usage [--org <id>]');
160
+ const u = await sdkAuth.getUsage(config.api_key, orgId);
161
+ if (flags.json) {
162
+ printJson(u);
163
+ return;
164
+ }
165
+ info(`Period: ${u.period}`);
166
+ info(`Active users: ${u.active_users} MAU`);
167
+ info(`Price: $${(u.price_cents_each / 100).toFixed(2)} per MAU`);
168
+ }
169
+ async function domain(args, flags) {
170
+ const action = args[0] || 'show';
171
+ const config = requireConfig();
172
+ const orgId = requireOrg(flags, config, 'myapi auth domain [show|set|delete] [--org <id>]');
173
+ const showDomain = (d) => {
174
+ info(`Domain: ${d.domain}`);
175
+ info(`Status: ${d.status}`);
176
+ if (d.status !== 'active') {
177
+ info('');
178
+ info(`DNS — create this record:`);
179
+ info(` ${d.dns.type} ${d.dns.name} → ${d.dns.value}`);
180
+ if (d.next)
181
+ info(`\n${d.next}`);
182
+ }
183
+ else {
184
+ info(`Issuer: ${d.issuer}`);
185
+ info(`Login URL: ${d.login_url}`);
186
+ }
187
+ };
188
+ if (action === 'show') {
189
+ try {
190
+ const d = await sdkAuth.getDomain(config.api_key, orgId);
191
+ if (flags.json) {
192
+ printJson(d);
193
+ return;
194
+ }
195
+ showDomain(d);
196
+ }
197
+ catch (e) {
198
+ if (e?.code === 'NO_DOMAIN' || e?.status === 404) {
199
+ error('No custom auth domain set.\nSet one with: myapi auth domain set --domain auth.example.com');
200
+ }
201
+ throw e;
202
+ }
203
+ return;
204
+ }
205
+ if (action === 'set' || action === 'register') {
206
+ const host = flags.domain || args[1] || '';
207
+ if (!host)
208
+ error('--domain is required (e.g. --domain auth.example.com)');
209
+ const d = await sdkAuth.registerDomain(config.api_key, orgId, host);
210
+ if (flags.json) {
211
+ printJson(d);
212
+ return;
213
+ }
214
+ success(`Custom auth domain registered: ${d.domain} (${d.status})`);
215
+ showDomain(d);
216
+ return;
217
+ }
218
+ if (action === 'delete' || action === 'remove') {
219
+ const host = flags.domain || args[1];
220
+ let target = host;
221
+ if (!target) {
222
+ // No host given — resolve the current one so the user doesn't have to retype it.
223
+ try {
224
+ target = (await sdkAuth.getDomain(config.api_key, orgId)).domain;
225
+ }
226
+ catch {
227
+ error('No custom auth domain to delete.');
228
+ }
229
+ }
230
+ await sdkAuth.deleteDomain(config.api_key, orgId, target);
231
+ success(`Custom auth domain removed: ${target}`);
232
+ return;
233
+ }
234
+ error(`Unknown action "${action}". Use: myapi auth domain [show|set|delete]`);
235
+ }
236
+ async function client(args, flags) {
237
+ const action = args[0] || 'list';
238
+ const config = requireConfig();
239
+ const orgId = requireOrg(flags, config, 'myapi auth client <list|create> [--org <id>]');
240
+ if (action === 'list') {
241
+ const res = await sdkAuth.listClients(config.api_key, orgId);
242
+ if (flags.json) {
243
+ printJson(res);
244
+ return;
245
+ }
246
+ printTable((res.clients || []).map(c => ({
247
+ ClientID: c.client_id,
248
+ Name: c.name || '',
249
+ Type: c.type,
250
+ Redirects: (c.redirect_uris || []).join(', '),
251
+ })), {
252
+ flags,
253
+ empty: 'No OIDC clients yet.\nCreate one: myapi auth client create --name <n> --type spa --redirect <url>',
254
+ });
255
+ return;
256
+ }
257
+ if (action === 'create') {
258
+ const name = flags.name || '';
259
+ const type = flags.type;
260
+ const redirect = flags.redirect || '';
261
+ if (!name)
262
+ error('--name is required (e.g. --name "My App")');
263
+ if (type !== 'spa' && type !== 'web')
264
+ error("--type must be 'spa' (public) or 'web' (confidential)");
265
+ if (!redirect)
266
+ error('--redirect is required (comma-separate multiple URIs)');
267
+ const redirect_uris = redirect.split(',').map(s => s.trim()).filter(Boolean);
268
+ const c = await sdkAuth.createClient(config.api_key, orgId, { name, type: type, redirect_uris });
269
+ if (flags.json) {
270
+ printJson(c);
271
+ return;
272
+ }
273
+ success(`Client created: ${c.client_id}`);
274
+ info(`Type: ${c.type}`);
275
+ info(`Redirects: ${(c.redirect_uris || []).join(', ')}`);
276
+ if (c.issuer)
277
+ info(`Issuer: ${c.issuer}`);
278
+ if (c.client_secret) {
279
+ info('');
280
+ info('Client secret (shown ONCE — store it now, it cannot be retrieved again):');
281
+ info(` ${c.client_secret}`);
282
+ }
283
+ return;
284
+ }
285
+ error(`Unknown action "${action}". Use: myapi auth client <list|create>`);
286
+ }
@@ -92,7 +92,7 @@ export async function balance(flags) {
92
92
  // Anonymous accounts can't transact — paid surface is gated on a
93
93
  // verified email and credits only fund on upgrade. Surface the unblock.
94
94
  if (config.is_anonymous) {
95
- info('→ Link an email to unlock $5 free credit + paid actions: myapi auth link <email>');
95
+ info('→ Link an email to unlock $5 free credit + paid actions: myapi account link <email>');
96
96
  }
97
97
  }
98
98
  export async function history(flags) {
@@ -49,7 +49,7 @@ export async function setFunnel(id, _flags, via = 'auth config') {
49
49
  const config = requireConfig();
50
50
  const orgId = config.default_org;
51
51
  if (!orgId)
52
- error('No default organization set. Run: myapi auth config set-org <id>');
52
+ error('No default organization set. Run: myapi account config set-org <id>');
53
53
  info('› Validating…');
54
54
  const { funnel } = await sdkFunnel.getFunnel(config.api_key, orgId, id);
55
55
  const funnelId = funnel.id;
@@ -11,6 +11,7 @@ export declare function create(flags: Flags): Promise<void>;
11
11
  export declare function list(flags: Flags): Promise<void>;
12
12
  export declare function get(id: string, flags: Flags): Promise<void>;
13
13
  export declare function del(id: string, flags: Flags): Promise<void>;
14
+ export declare function _isTarball(p: string): boolean;
14
15
  export declare function deploy(id: string, image: string, flags: Flags): Promise<void>;
15
16
  export declare function logs(id: string, flags: Flags): Promise<void>;
16
17
  export declare function domain(id: string, domainArg: string | undefined, flags: Flags): Promise<void>;
@@ -1,7 +1,9 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFile, stat } from 'node:fs/promises';
1
3
  import { container as sdkContainer } from '@myapihq/sdk';
2
4
  import { requireConfig } from '../config.js';
3
5
  import { success, error, printTable, info, printJson, banner } from '../output.js';
4
- import { formatDate } from '../utils.js';
6
+ import { formatDate, pollJob } from '../utils.js';
5
7
  import { requireOrg } from '../helpers.js';
6
8
  export const EXPOSES = [
7
9
  'POST /container/orgs/{org_id}/containers',
@@ -25,6 +27,8 @@ export const SCHEMA = {
25
27
  env: 'string',
26
28
  tail: 'number',
27
29
  remove: 'boolean',
30
+ source: 'string',
31
+ image: 'string',
28
32
  };
29
33
  const CONTAINER_TYPES = ['service', 'worker', 'job'];
30
34
  // Mirrors validateName in myapi-hq/internal/routes/container/crud.go —
@@ -149,6 +153,8 @@ export async function get(id, flags) {
149
153
  if (c.port)
150
154
  info(`Port: ${c.port}`);
151
155
  info(`URL: ${c.url || '(not deployed)'}`);
156
+ if (c.egress)
157
+ info(`Egress: ${c.egress}`);
152
158
  if (c.custom_domain)
153
159
  info(`Custom domain: ${c.custom_domain}`);
154
160
  info(`Created: ${c.created_at}`);
@@ -162,15 +168,106 @@ export async function del(id, flags) {
162
168
  await sdkContainer.deleteContainer(config.api_key, orgId, id);
163
169
  success(`Deleted container ${id}`);
164
170
  }
165
- // deploy ships a pre-built image to Cloud Run. The scoped API key is
166
- // rotated on every deploy the fresh value is shown once here.
171
+ // _isTarball returns true if the path looks like an already-built tar archive
172
+ // (so we upload it as-is instead of tarring a directory).
173
+ export function _isTarball(p) {
174
+ return /\.(tar|tar\.gz|tgz)$/i.test(p);
175
+ }
176
+ // tarDirectory builds a gzipped tarball of a build-context directory using the
177
+ // system `tar`. Returns the archive bytes. We tar the directory contents (-C
178
+ // <dir> .) so the Dockerfile sits at the root of the build context.
179
+ async function tarDirectory(dir) {
180
+ const res = spawnSync('tar', ['-czf', '-', '-C', dir, '.'], {
181
+ maxBuffer: 200 * 1024 * 1024,
182
+ });
183
+ if (res.error) {
184
+ if (res.error.code === 'ENOENT') {
185
+ error('`tar` was not found on PATH. Install tar, or pass a pre-built .tar.gz to --source.');
186
+ }
187
+ error(`Failed to create build-context archive: ${res.error.message}`);
188
+ }
189
+ if (res.status !== 0) {
190
+ error(`tar exited ${res.status}: ${res.stderr?.toString().trim() || 'unknown error'}`);
191
+ }
192
+ return res.stdout;
193
+ }
194
+ // deploy ships either a pre-built image (--image / positional ref, synchronous)
195
+ // or a source build context (--source <dir-or-tar>, asynchronous Cloud Build).
196
+ // The scoped API key is rotated on every (image) deploy and shown once.
167
197
  export async function deploy(id, image, flags) {
168
198
  const config = requireConfig();
169
- const orgId = requireOrg(flags, config, 'myapi container deploy <id> <image-ref> [--org <id>]');
199
+ const orgId = requireOrg(flags, config, 'myapi container deploy <id> <image-ref> | --source <dir|tar> [--org <id>]');
170
200
  if (!id)
171
- error('Missing id.\nUsage: myapi container deploy <id> <image-ref>');
201
+ error('Missing id.\nUsage: myapi container deploy <id> <image-ref> (or --source <dir|tar>)');
202
+ const source = typeof flags.source === 'string' ? flags.source : undefined;
203
+ // --image is an alias for the positional image ref.
204
+ if (!image && typeof flags.image === 'string')
205
+ image = flags.image;
206
+ if (source && image) {
207
+ error('Pass either an image ref or --source, not both.');
208
+ }
209
+ // ── Source-build path (async) ───────────────────────────────────────────
210
+ if (source) {
211
+ let tarball;
212
+ let filename = 'source.tar.gz';
213
+ let info_st;
214
+ try {
215
+ info_st = await stat(source);
216
+ }
217
+ catch (e) {
218
+ if (e?.code === 'ENOENT')
219
+ error(`Source not found: ${source}`);
220
+ error(`Could not read ${source}: ${e?.message ?? e}`);
221
+ }
222
+ if (info_st.isDirectory()) {
223
+ tarball = await tarDirectory(source);
224
+ }
225
+ else if (_isTarball(source)) {
226
+ tarball = await readFile(source);
227
+ filename = source.split('/').pop() || filename;
228
+ }
229
+ else {
230
+ error(`--source must be a directory or a .tar/.tar.gz/.tgz archive — got ${source}`);
231
+ }
232
+ const start = await sdkContainer.deployContainerSource(config.api_key, orgId, id, tarball, filename);
233
+ if (flags.json && start.status !== 'building') {
234
+ printJson(start);
235
+ return;
236
+ }
237
+ // Async: poll the container until it leaves the building state.
238
+ if (start.status === 'building') {
239
+ info(`Build started (revision ${start.revision_id}). Building from source via Cloud Build…`);
240
+ const final = await pollJob({
241
+ label: 'Building & deploying',
242
+ check: () => sdkContainer.getContainer(config.api_key, orgId, id),
243
+ isDone: (c) => c.status === 'active',
244
+ isFailed: (c) => c.status === 'build_error' || c.status === 'deploy_error',
245
+ failedMessage: 'Build/deploy failed — check `myapi container logs ' + id + '`.',
246
+ timeoutMs: 600_000,
247
+ intervalMs: 5_000,
248
+ });
249
+ if (flags.json) {
250
+ printJson(final);
251
+ return;
252
+ }
253
+ success(`Deployed container ${id} from source`);
254
+ info(`Status: ${final.status}`);
255
+ info(`URL: ${final.url || '(none — job container)'}`);
256
+ return;
257
+ }
258
+ // Backend deployed synchronously after all (unexpected for source) —
259
+ // surface whatever it returned.
260
+ if (flags.json) {
261
+ printJson(start);
262
+ return;
263
+ }
264
+ success(`Deployed container ${id} (revision ${start.revision_id})`);
265
+ info(`Status: ${start.status}`);
266
+ return;
267
+ }
268
+ // ── Pre-built image path (sync) ─────────────────────────────────────────
172
269
  if (!image)
173
- error('Missing image ref.\nUsage: myapi container deploy <id> <image-ref>\n\n→ <image-ref> is a pre-built container image (e.g. a registry path).');
270
+ error('Missing image ref.\nUsage: myapi container deploy <id> <image-ref>\n or: myapi container deploy <id> --source <dir|tar>\n\n→ <image-ref> is a pre-built container image (e.g. a registry path).');
174
271
  const result = await sdkContainer.deployContainer(config.api_key, orgId, id, image);
175
272
  if (flags.json) {
176
273
  printJson(result);
@@ -247,12 +344,19 @@ Examples:
247
344
  myapi container create --name nightly --type job --cron "0 3 * * *"
248
345
  myapi container create --name queue-worker --type worker --memory 1Gi`,
249
346
  'deploy': `myapi container deploy <id> <image-ref> [--org <id>] [--json]
347
+ myapi container deploy <id> --source <dir|tar.gz> [--org <id>] [--json]
250
348
 
251
- Ships a pre-built container image to the runtime. The scoped API key is
252
- rotated on every deploy the fresh value is printed once.
349
+ Two ways to deploy:
350
+ image Ship a pre-built image (positional ref or --image). Synchronous;
351
+ the scoped API key is rotated and printed once.
352
+ source Upload a build context with --source <dir> (tarred locally) or a
353
+ pre-built --source <archive.tar.gz>. MyAPI builds it via Cloud
354
+ Build, then deploys. Asynchronous — the CLI polls until it's live.
253
355
 
254
- Example:
255
- myapi container deploy <id> registry.example.com/my-app:v2`,
356
+ Examples:
357
+ myapi container deploy <id> registry.example.com/my-app:v2
358
+ myapi container deploy <id> --source ./my-app
359
+ myapi container deploy <id> --source ./context.tar.gz`,
256
360
  'list': 'myapi container list [--org <id>] [--json]',
257
361
  'get': 'myapi container get <id> [--org <id>] [--json]',
258
362
  'logs': `myapi container logs <id> [--tail <n>] [--org <id>] [--json]
@@ -281,7 +385,7 @@ dependencies and long execution.
281
385
  Subcommands:
282
386
  create Register a container and get its scoped API key (returned once)
283
387
  delete <id> Soft-delete and revoke its scoped API key
284
- deploy <id> <image> Ship a pre-built image and go live
388
+ deploy <id> <image> Ship a pre-built image (or --source <dir|tar> to build) and go live
285
389
  domain <id> <domain> Bind a custom domain (--remove to unbind)
286
390
  get <id> Inspect a container
287
391
  list List containers in your org
@@ -299,7 +299,7 @@ export async function emailSetup(domainArg, flags) {
299
299
  printJson(res);
300
300
  return;
301
301
  }
302
- success(`Email infra provisioning on ${res.email_subdomain} (state: ${res.email_infra})`);
302
+ success(`Email infra provisioning on ${res.subdomain} (state: ${res.email_infra})`);
303
303
  if (res.next_step)
304
304
  info(res.next_step);
305
305
  info(`Track with: myapi domain status ${domain} --watch`);
@@ -533,7 +533,7 @@ Prerequisites:
533
533
  - WHOIS registrant info (ICANN requirement — see below)
534
534
 
535
535
  Registrant (required at every register call):
536
- Cheapest path: run \`myapi auth registrant set\` once. The CLI stores
536
+ Cheapest path: run \`myapi account registrant set\` once. The CLI stores
537
537
  your name/email/phone/address and auto-injects it on every register.
538
538
 
539
539
  Per-call override (agent-friendly):
@@ -10,7 +10,6 @@ import * as mailbox from './mailbox.js';
10
10
  import * as message from './message.js';
11
11
  import * as warmup from './warmup.js';
12
12
  import * as template from './template.js';
13
- import * as campaign from './campaign.js';
14
13
  import * as verify from './verify.js';
15
14
  export const SCHEMA = {
16
15
  org: 'string',
@@ -53,15 +52,6 @@ const DEPRECATED_ALIASES = {
53
52
  'edit-template': ['template', 'edit'],
54
53
  'send-test': ['template', 'send-test'],
55
54
  'delete-template': ['template', 'delete'],
56
- 'create-campaign': ['campaign', 'create'],
57
- 'list-campaigns': ['campaign', 'list'],
58
- 'get-campaign': ['campaign', 'get'],
59
- 'update-campaign': ['campaign', 'update'],
60
- 'upload-contacts': ['campaign', 'upload-contacts'],
61
- 'start-campaign': ['campaign', 'start'],
62
- 'pause-campaign': ['campaign', 'pause'],
63
- 'resume-campaign': ['campaign', 'resume'],
64
- 'campaign-stats': ['campaign', 'stats'],
65
55
  };
66
56
  async function dispatchNamespace(ns, sub, restArgs, flags) {
67
57
  switch (ns) {
@@ -69,7 +59,6 @@ async function dispatchNamespace(ns, sub, restArgs, flags) {
69
59
  case 'message': return message.run(sub, restArgs, flags);
70
60
  case 'warmup': return warmup.run(sub, restArgs, flags);
71
61
  case 'template': return template.run(sub, restArgs, flags);
72
- case 'campaign': return campaign.run(sub, restArgs, flags);
73
62
  case 'verify': return verify.run(sub, restArgs, flags);
74
63
  default: error(`Unknown namespace: ${ns}. Run "myapi email --help" for the list.`);
75
64
  }
@@ -79,7 +68,6 @@ export async function run(subcommand, args, flags) {
79
68
  info(`Usage: myapi email <namespace> <subcommand>
80
69
 
81
70
  Namespaces:
82
- campaign Email campaigns (org-scoped) — create, list, get, update, upload-contacts, start, pause, resume, stats
83
71
  mailbox Mailboxes (account-scoped) — create, list, activate-sending
84
72
  message Send & read mail (account-scoped) — send, status, sent, inbox, outbox, get
85
73
  template Email templates (org-scoped) — generate, list, edit, send-test, delete
@@ -90,7 +78,6 @@ Examples:
90
78
  myapi email mailbox create hello@example.com
91
79
  myapi email message send --from a@x --to b@y --subject hi --body "hello"
92
80
  myapi email template generate welcome --prompt "Welcome email"
93
- myapi email campaign create "Q2 Launch" --template-id <id> --from a@x
94
81
  myapi email verify alice@example.com
95
82
 
96
83
  Org-scoped commands accept --org <id> (or set default: myapi config set-org <id>).
@@ -11,6 +11,7 @@ export declare function list(flags: Flags): Promise<void>;
11
11
  export declare function get(id: string, flags: Flags): Promise<void>;
12
12
  export declare function del(id: string, flags: Flags): Promise<void>;
13
13
  export declare function deploy(id: string, bundlePath: string, flags: Flags): Promise<void>;
14
+ export declare function _parseSetPairs(raw: string | string[]): Record<string, string> | string;
14
15
  export declare function setEnv(id: string, name: string, value: string, flags: Flags): Promise<void>;
15
16
  export declare function runs(id: string, flags: Flags): Promise<void>;
16
17
  export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;