@myapihq/cli 2.7.0 → 2.7.2
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/account.js +15 -0
- package/dist/commands/audience.js +7 -5
- package/dist/commands/container.js +50 -26
- package/dist/commands/crm/companies.js +4 -4
- package/dist/commands/crm/contacts.js +4 -4
- package/dist/commands/crm/index.js +3 -0
- package/dist/commands/crm/origin-flag.test.d.ts +1 -0
- package/dist/commands/crm/origin-flag.test.js +38 -0
- package/dist/commands/crm/pagination.d.ts +2 -0
- package/dist/commands/crm/pagination.js +9 -0
- package/dist/commands/database.js +21 -14
- package/dist/commands/domain.js +11 -0
- package/dist/commands/fn.js +10 -5
- package/dist/commands/task.js +12 -4
- package/dist/skills/my-api-hq/SKILL.md +25 -1
- package/dist/skills/my-audience-api/SKILL.md +5 -5
- package/dist/skills/my-auth-api/SKILL.md +8 -1
- package/dist/skills/my-company-api/SKILL.md +3 -3
- package/dist/skills/my-container-api/SKILL.md +47 -30
- package/dist/skills/my-crm-api/SKILL.md +6 -6
- package/dist/skills/my-database-api/README.md +1 -1
- package/dist/skills/my-database-api/SKILL.md +25 -4
- package/dist/skills/my-domain-api/SKILL.md +18 -1
- package/dist/skills/my-email-api/SKILL.md +36 -1
- package/dist/skills/my-function-api/SKILL.md +38 -10
- package/dist/skills/my-funnel-api/SKILL.md +6 -3
- package/dist/skills/my-git-api/SKILL.md +10 -4
- package/dist/skills/my-people-api/SKILL.md +3 -3
- package/dist/skills/my-pixel-api/SKILL.md +12 -1
- package/dist/skills/my-storage-api/SKILL.md +31 -2
- package/dist/skills/my-task-api/SKILL.md +9 -1
- package/dist/skills/my-webhook-api/SKILL.md +12 -1
- package/package.json +3 -2
package/dist/commands/account.js
CHANGED
|
@@ -25,6 +25,21 @@ export const SCHEMA = {
|
|
|
25
25
|
'no-skills': 'boolean',
|
|
26
26
|
anonymous: 'boolean',
|
|
27
27
|
anon: 'boolean',
|
|
28
|
+
// Flags belonging to SUBCOMMANDS of `account`. They have to be declared here
|
|
29
|
+
// because the foreign-flag check runs against the top-level command's schema
|
|
30
|
+
// and cannot see subcommand parsers. Omitting them made the CLI print
|
|
31
|
+
// "--registrant-json is not a flag of `myapi account` — the value was
|
|
32
|
+
// ignored" and then use the value anyway: a warning that was itself false,
|
|
33
|
+
// which is worse than the missing check it was added to provide.
|
|
34
|
+
'registrant-json': 'string',
|
|
35
|
+
'registrant-name': 'string',
|
|
36
|
+
'registrant-email': 'string',
|
|
37
|
+
'registrant-phone': 'string',
|
|
38
|
+
'registrant-street': 'string',
|
|
39
|
+
'registrant-city': 'string',
|
|
40
|
+
'registrant-state': 'string',
|
|
41
|
+
'registrant-postal-code': 'string',
|
|
42
|
+
'registrant-country-code': 'string',
|
|
28
43
|
};
|
|
29
44
|
export const HELP = `Usage: myapi account <subcommand>
|
|
30
45
|
|
|
@@ -15,6 +15,8 @@ export const SCHEMA = {
|
|
|
15
15
|
org: 'string',
|
|
16
16
|
name: 'string',
|
|
17
17
|
description: 'string',
|
|
18
|
+
from: 'string',
|
|
19
|
+
// Deprecated alias for --from; undocumented, removable next minor.
|
|
18
20
|
source: 'string',
|
|
19
21
|
filter: 'string',
|
|
20
22
|
limit: 'number',
|
|
@@ -47,11 +49,11 @@ function summarizeAudience(a) {
|
|
|
47
49
|
}
|
|
48
50
|
async function create(nameArg, flags) {
|
|
49
51
|
const config = requireConfig();
|
|
50
|
-
const orgId = requireOrg(flags, config, 'myapi audience create <name> --
|
|
52
|
+
const orgId = requireOrg(flags, config, 'myapi audience create <name> --from <people|company> --filter <json>');
|
|
51
53
|
const name = nameArg || flags.name;
|
|
52
54
|
if (!name)
|
|
53
|
-
error('Missing required argument <name>.\nUsage: myapi audience create <name> --
|
|
54
|
-
const source = flags.source;
|
|
55
|
+
error('Missing required argument <name>.\nUsage: myapi audience create <name> --from <people|company> --filter <json>');
|
|
56
|
+
const source = (flags.from ?? flags.source);
|
|
55
57
|
if (source !== 'people' && source !== 'company') {
|
|
56
58
|
error(`--source must be "people" or "company" (got: ${source ?? '<missing>'}).`);
|
|
57
59
|
}
|
|
@@ -76,7 +78,7 @@ async function list(flags) {
|
|
|
76
78
|
}
|
|
77
79
|
printTable(res.map(summarizeAudience), {
|
|
78
80
|
flags,
|
|
79
|
-
empty: 'No audiences yet. Create one with: myapi audience create <name> --
|
|
81
|
+
empty: 'No audiences yet. Create one with: myapi audience create <name> --from <people|company> --filter <json>',
|
|
80
82
|
});
|
|
81
83
|
}
|
|
82
84
|
async function get(id, flags) {
|
|
@@ -168,7 +170,7 @@ async function refresh(id, flags) {
|
|
|
168
170
|
success(`Refreshed. ${res.previous} → ${res.total} (${sign}${res.delta})`);
|
|
169
171
|
}
|
|
170
172
|
const SUBCOMMAND_USAGE = {
|
|
171
|
-
'create': `myapi audience create <name> --
|
|
173
|
+
'create': `myapi audience create <name> --from <people|company> --filter <json> [--description <text>] [--org <id>]
|
|
172
174
|
|
|
173
175
|
The Goldfox filter shape is shared across people/company/audience:
|
|
174
176
|
{
|
|
@@ -133,7 +133,7 @@ export async function create(nameArg, flags) {
|
|
|
133
133
|
info(`Name: ${result.container.name}`);
|
|
134
134
|
info(`Type: ${result.container.type}${result.container.cron_schedule ? ` (${result.container.cron_schedule})` : ''}`);
|
|
135
135
|
info(`Resources: ${result.container.cpu} CPU, ${result.container.memory}, instances ${result.container.min_instances}-${result.container.max_instances}`);
|
|
136
|
-
// The scoped key is returned ONCE — it's delivered to the running
|
|
136
|
+
// The scoped API key is returned ONCE — it's delivered to the running
|
|
137
137
|
// container as the MYAPI_KEY env var. Deploy rotates it.
|
|
138
138
|
info('');
|
|
139
139
|
info(`Scoped API key (returned once — save it if you need it):`);
|
|
@@ -221,6 +221,36 @@ export async function deploy(id, image, flags) {
|
|
|
221
221
|
const orgId = requireOrg(flags, config, 'myapi container deploy <id> <image-ref> | --source <dir|tar> [--org <id>]');
|
|
222
222
|
if (!id)
|
|
223
223
|
error('Missing id.\nUsage: myapi container deploy <id> <image-ref> (or --source <dir|tar>)');
|
|
224
|
+
// --no-promote and --smoke are REFUSED, not honoured, as of 2026-07-28.
|
|
225
|
+
//
|
|
226
|
+
// They shipped in 2.7.0 and do not work on either deploy path. A customer
|
|
227
|
+
// found it by testing rather than trusting the flags, which is the outcome
|
|
228
|
+
// this CLI spends most of its error messages trying to prevent:
|
|
229
|
+
//
|
|
230
|
+
// --source: the multipart body accepts only `source`. The flags were
|
|
231
|
+
// silently dropped — accepted by our flag parser, never sent. That was
|
|
232
|
+
// our bug, and it is the exact failure these flags exist to stop.
|
|
233
|
+
//
|
|
234
|
+
// image ref: the API accepts promote/smoke, then the deploy fails with a
|
|
235
|
+
// runtime error setting traffic to the revision it just created. The
|
|
236
|
+
// previous revision keeps serving, so it is safe, but it does not work.
|
|
237
|
+
//
|
|
238
|
+
// Refusing is strictly better than accepting. A missing guard makes people
|
|
239
|
+
// write their own; a guard that silently passes makes them stop. Restore
|
|
240
|
+
// these the moment the upstream fix lands — see
|
|
241
|
+
// docs/cross-repo-prompts/backend-consolidated-2026-07-28.md.
|
|
242
|
+
if (flags['no-promote'] === true || typeof flags.smoke === 'string') {
|
|
243
|
+
const which = flags['no-promote'] === true ? '--no-promote' : '--smoke';
|
|
244
|
+
error(`${which} is not honoured yet, so this CLI refuses it rather than letting you believe a deploy was guarded.\n\n` +
|
|
245
|
+
'It shipped in 2.7.0 and does not work end to end on either deploy path:\n' +
|
|
246
|
+
' --source the API accepts only the tarball on that path; the flag never reaches it\n' +
|
|
247
|
+
' <image-ref> the API accepts the flag, then fails while moving traffic\n\n' +
|
|
248
|
+
'Until it lands, the safe sequence is:\n' +
|
|
249
|
+
` 1. deploy to a non-production container first\n` +
|
|
250
|
+
` 2. check it yourself (curl for a string only a real build emits)\n` +
|
|
251
|
+
` 3. deploy the same image to production\n\n` +
|
|
252
|
+
'Reported upstream; this message goes away when the flag works.');
|
|
253
|
+
}
|
|
224
254
|
const source = typeof flags.source === 'string' ? flags.source : undefined;
|
|
225
255
|
// --image is an alias for the positional image ref.
|
|
226
256
|
if (!image && typeof flags.image === 'string')
|
|
@@ -290,15 +320,10 @@ export async function deploy(id, image, flags) {
|
|
|
290
320
|
// ── Pre-built image path (sync) ─────────────────────────────────────────
|
|
291
321
|
if (!image)
|
|
292
322
|
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).');
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
opts.smoke = _parseSmoke(flags.smoke);
|
|
298
|
-
if (opts.promote === false && opts.smoke) {
|
|
299
|
-
error('--smoke already withholds traffic until the check passes, then promotes.\nUse one or the other: --smoke to verify-and-promote, --no-promote to hold the revision back.');
|
|
300
|
-
}
|
|
301
|
-
const result = await sdkContainer.deployContainer(config.api_key, orgId, id, image, opts);
|
|
323
|
+
// No DeployOptions built here: --no-promote and --smoke are refused above
|
|
324
|
+
// until the upstream fix lands. The SDK still carries them so the wiring is
|
|
325
|
+
// one commit away, and sdk-container.test.ts keeps them covered.
|
|
326
|
+
const result = await sdkContainer.deployContainer(config.api_key, orgId, id, image);
|
|
302
327
|
if (flags.json) {
|
|
303
328
|
printJson(result);
|
|
304
329
|
return;
|
|
@@ -306,6 +331,9 @@ export async function deploy(id, image, flags) {
|
|
|
306
331
|
// An unpromoted revision must NOT read like a completed deploy. A response
|
|
307
332
|
// that looked the same either way is how an agent concludes it has shipped
|
|
308
333
|
// when it has not — the original outage in miniature.
|
|
334
|
+
// Unreachable while the flags are refused above. Kept because it is the
|
|
335
|
+
// render we want the moment they are restored, and deleting it would mean
|
|
336
|
+
// rewriting it from memory later.
|
|
309
337
|
if (result.promoted === false) {
|
|
310
338
|
success(`Revision ${result.revision_id} built — NOT serving traffic`);
|
|
311
339
|
info(`Test it: ${result.revision_url ?? '(no revision URL returned)'}`);
|
|
@@ -392,8 +420,10 @@ export async function revisions(id, flags) {
|
|
|
392
420
|
if (revs.length > 0 && revs.every(r => !r.serving && !r.traffic_percent) && container?.status === 'active') {
|
|
393
421
|
info('');
|
|
394
422
|
info('Note: every revision reports 0% traffic while this container is active and serving.');
|
|
395
|
-
info('
|
|
396
|
-
info('
|
|
423
|
+
info('The traffic column is wrong, not the container. Verified on a container created');
|
|
424
|
+
info('today, so this is not limited to older ones — an earlier version of this message');
|
|
425
|
+
info('said redeploying fixes it, which was wrong.');
|
|
426
|
+
info('promote depends on this data and currently fails. Reported upstream.');
|
|
397
427
|
}
|
|
398
428
|
}
|
|
399
429
|
// promote moves all traffic to one revision. Omitting the revision rolls back
|
|
@@ -535,23 +565,19 @@ Two ways to deploy:
|
|
|
535
565
|
built server-side (typically ~4 minutes), then deployed.
|
|
536
566
|
Asynchronous — the CLI polls until it's live.
|
|
537
567
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
previous revision serving.
|
|
568
|
+
TEMPORARILY REFUSED: --no-promote and --smoke
|
|
569
|
+
|
|
570
|
+
Both shipped in 2.7.0 and do not work end to end. Rather than accept a flag
|
|
571
|
+
and deploy anyway, the CLI now refuses them and explains what to do instead.
|
|
572
|
+
A guard that silently passes is worse than no guard.
|
|
544
573
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
true of a placeholder page too.
|
|
574
|
+
Until they land: deploy to a non-production container, verify it yourself,
|
|
575
|
+
then deploy the same image to production.
|
|
548
576
|
|
|
549
577
|
Examples:
|
|
550
578
|
myapi container deploy <id> registry.example.com/my-app:v2
|
|
551
579
|
myapi container deploy <id> --source ./my-app
|
|
552
|
-
myapi container deploy <id> --source ./context.tar.gz
|
|
553
|
-
myapi container deploy <id> <image> --no-promote
|
|
554
|
-
myapi container deploy <id> <image> --smoke 'GET / contains assets/'`,
|
|
580
|
+
myapi container deploy <id> --source ./context.tar.gz`,
|
|
555
581
|
'revisions': `myapi container revisions <id> [--org <id>] [--json]
|
|
556
582
|
|
|
557
583
|
Every revision the runtime currently holds, newest first, with the traffic
|
|
@@ -595,10 +621,8 @@ dependencies and long execution.
|
|
|
595
621
|
Subcommands:
|
|
596
622
|
build-logs <id> Why the last --source build failed (--tail N; default 100)
|
|
597
623
|
create Register a container and get its scoped API key (returned once)
|
|
598
|
-
(--health-check /livez to probe with HTTP, not a bare TCP connect)
|
|
599
624
|
delete <id> Soft-delete and revoke its scoped API key
|
|
600
625
|
deploy <id> <image> Ship a pre-built image (or --source <dir|tar> to build) and go live
|
|
601
|
-
(--no-promote to hold it back; --smoke to verify before promoting)
|
|
602
626
|
domain <id> <domain> Bind a custom domain (--remove to unbind)
|
|
603
627
|
get <id> Inspect a container
|
|
604
628
|
list List containers in your org
|
|
@@ -3,7 +3,7 @@ import { crm } from '@myapihq/sdk';
|
|
|
3
3
|
import { requireConfig } from '../../config.js';
|
|
4
4
|
import { success, error, info, printTable, printJson } from '../../output.js';
|
|
5
5
|
import { requireOrg, requireArg } from '../../helpers.js';
|
|
6
|
-
import { pageLine } from './pagination.js';
|
|
6
|
+
import { pageLine, originFlag } from './pagination.js';
|
|
7
7
|
export const EXPOSES = [
|
|
8
8
|
'POST /crm/orgs/{org_id}/companies',
|
|
9
9
|
'POST /crm/orgs/{org_id}/companies/promote',
|
|
@@ -32,7 +32,7 @@ function parseCustom(v) {
|
|
|
32
32
|
function buildSearchFilter(flags) {
|
|
33
33
|
return {
|
|
34
34
|
lifecycle_stage: csv(flags.stage),
|
|
35
|
-
source: csv(flags
|
|
35
|
+
source: csv(originFlag(flags)),
|
|
36
36
|
domain: typeof flags.domain === 'string' ? flags.domain : undefined,
|
|
37
37
|
include_deleted: flags['include-deleted'] === true || undefined,
|
|
38
38
|
limit: typeof flags.limit === 'number' ? flags.limit : undefined,
|
|
@@ -144,11 +144,11 @@ async function promote(domain, flags) {
|
|
|
144
144
|
// ── Dispatcher ──────────────────────────────────────────────────────────
|
|
145
145
|
const SUBCOMMAND_USAGE = {
|
|
146
146
|
list: 'myapi crm companies list [--limit N] [--offset N] [--org <id>] [--json]',
|
|
147
|
-
search: `myapi crm companies search [--stage <csv>] [--
|
|
147
|
+
search: `myapi crm companies search [--stage <csv>] [--origin <csv>] [--domain <d>]
|
|
148
148
|
[--include-deleted] [--limit N] [--offset N] [--org <id>] [--json]
|
|
149
149
|
|
|
150
150
|
--stage cold, warm, qualified, customer, churned
|
|
151
|
-
--
|
|
151
|
+
--origin goldfox, email, pixel, webhook, manual`,
|
|
152
152
|
create: 'myapi crm companies create <domain> [--name <n>] [--stage <s>] [--custom-json <json>] [--org <id>]',
|
|
153
153
|
get: 'myapi crm companies get <id> [--org <id>]',
|
|
154
154
|
update: 'myapi crm companies update <id> [--stage <s>] [--name <n>] [--custom-json <json>] [--org <id>]',
|
|
@@ -3,7 +3,7 @@ import { crm } from '@myapihq/sdk';
|
|
|
3
3
|
import { requireConfig } from '../../config.js';
|
|
4
4
|
import { success, error, info, printTable, printJson } from '../../output.js';
|
|
5
5
|
import { requireOrg, requireArg } from '../../helpers.js';
|
|
6
|
-
import { pageLine } from './pagination.js';
|
|
6
|
+
import { pageLine, originFlag } from './pagination.js';
|
|
7
7
|
export const EXPOSES = [
|
|
8
8
|
'POST /crm/orgs/{org_id}/contacts',
|
|
9
9
|
'POST /crm/orgs/{org_id}/contacts/promote',
|
|
@@ -35,7 +35,7 @@ function parseCustom(v) {
|
|
|
35
35
|
function buildSearchFilter(flags) {
|
|
36
36
|
return {
|
|
37
37
|
lifecycle_stage: csv(flags.stage),
|
|
38
|
-
source: csv(flags
|
|
38
|
+
source: csv(originFlag(flags)),
|
|
39
39
|
email: typeof flags.email === 'string' ? flags.email : undefined,
|
|
40
40
|
company_id: typeof flags['company-id'] === 'string' ? flags['company-id'] : undefined,
|
|
41
41
|
min_last_engagement_days: typeof flags['min-last-engagement-days'] === 'number' ? flags['min-last-engagement-days'] : undefined,
|
|
@@ -179,12 +179,12 @@ async function events(id, flags) {
|
|
|
179
179
|
// ── Dispatcher ──────────────────────────────────────────────────────────
|
|
180
180
|
const SUBCOMMAND_USAGE = {
|
|
181
181
|
list: 'myapi crm contacts list [--limit N] [--offset N] [--org <id>] [--json]',
|
|
182
|
-
search: `myapi crm contacts search [--stage <csv>] [--
|
|
182
|
+
search: `myapi crm contacts search [--stage <csv>] [--origin <csv>] [--email <e>]
|
|
183
183
|
[--company-id <id>] [--min-last-engagement-days N] [--max-last-engagement-days N]
|
|
184
184
|
[--include-deleted] [--limit N] [--offset N] [--org <id>] [--json]
|
|
185
185
|
|
|
186
186
|
--stage cold, warm, qualified, customer, churned
|
|
187
|
-
--
|
|
187
|
+
--origin goldfox, email, pixel, webhook, manual
|
|
188
188
|
|
|
189
189
|
Engagement filters:
|
|
190
190
|
--min-last-engagement-days N contacts engaged within N days
|
|
@@ -16,6 +16,9 @@ export const SCHEMA = {
|
|
|
16
16
|
'last-name': 'string',
|
|
17
17
|
name: 'string',
|
|
18
18
|
stage: 'string',
|
|
19
|
+
origin: 'string',
|
|
20
|
+
// Deprecated alias for --origin. Kept so existing scripts keep working;
|
|
21
|
+
// undocumented, and removable no earlier than the next minor.
|
|
19
22
|
source: 'string',
|
|
20
23
|
'company-id': 'string',
|
|
21
24
|
'custom-json': 'string',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// `--source` meant five different things across the CLI: a container build
|
|
2
|
+
// context, a git merge branch, an audience dataset, a task origin, and a CRM
|
|
3
|
+
// provenance enum. An agent that learned it in one place carried a wrong prior
|
|
4
|
+
// into the others, and no linter could catch it — `--source` really is valid
|
|
5
|
+
// for every one of those commands, so nothing was ever "unknown".
|
|
6
|
+
//
|
|
7
|
+
// It is `--origin` for provenance now (crm, task) and `--from` for the
|
|
8
|
+
// audience dataset. `--source` keeps its container meaning, and stays on
|
|
9
|
+
// `git merge` where `--target` anchors it and the pair is idiomatic.
|
|
10
|
+
//
|
|
11
|
+
// The old spellings still work, undocumented, so nobody's scripts break.
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { originFlag } from './pagination.js';
|
|
14
|
+
describe('originFlag', () => {
|
|
15
|
+
it('reads the new flag', () => {
|
|
16
|
+
expect(originFlag({ origin: 'webhook' })).toBe('webhook');
|
|
17
|
+
});
|
|
18
|
+
it('still accepts the old one', () => {
|
|
19
|
+
expect(originFlag({ source: 'webhook' })).toBe('webhook');
|
|
20
|
+
});
|
|
21
|
+
// Someone mid-migration may have both in a script. The new name is the
|
|
22
|
+
// one they meant.
|
|
23
|
+
it('prefers --origin when both are given', () => {
|
|
24
|
+
expect(originFlag({ origin: 'manual', source: 'webhook' })).toBe('manual');
|
|
25
|
+
});
|
|
26
|
+
it('is undefined when neither is given', () => {
|
|
27
|
+
expect(originFlag({})).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
// csv() downstream splits this; the helper must not mangle it.
|
|
30
|
+
it('passes a comma-separated list through untouched', () => {
|
|
31
|
+
expect(originFlag({ origin: 'goldfox,webhook' })).toBe('goldfox,webhook');
|
|
32
|
+
});
|
|
33
|
+
// An explicitly empty --origin is a real (if useless) input, and must not
|
|
34
|
+
// silently fall through to a stale --source in the same command.
|
|
35
|
+
it('does not fall back when --origin is present but empty', () => {
|
|
36
|
+
expect(originFlag({ origin: '', source: 'webhook' })).toBe('');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type Flags } from '../../helpers.js';
|
|
1
2
|
import type { Exposes } from '../../exposes.js';
|
|
2
3
|
export declare const EXPOSES: Exposes;
|
|
4
|
+
export declare function originFlag(flags: Flags): string | boolean | number | undefined;
|
|
3
5
|
export declare function pageLine(returned: number, total: number | undefined, hasMore: boolean | undefined, singular: string, plural: string): string;
|
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
// exempted, so the coverage gate's "every module states its surface" rule
|
|
19
19
|
// stays absolute.
|
|
20
20
|
export const EXPOSES = [];
|
|
21
|
+
// --source meant five different things across the CLI: a build context, a
|
|
22
|
+
// merge branch, a dataset, a task origin, and this — where a contact came
|
|
23
|
+
// from. An agent that learned it once carried a wrong prior everywhere else,
|
|
24
|
+
// and no linter could catch it because --source really is valid for each.
|
|
25
|
+
//
|
|
26
|
+
// It is --origin here now. The old name still works and is undocumented.
|
|
27
|
+
export function originFlag(flags) {
|
|
28
|
+
return flags.origin !== undefined ? flags.origin : flags.source;
|
|
29
|
+
}
|
|
21
30
|
// "3 contacts" · "3 of 128 contacts" · "3 of 128 contacts (more available)".
|
|
22
31
|
//
|
|
23
32
|
// `total` is only rendered when the API supplied it, and the more-available
|
|
@@ -106,8 +106,8 @@ async function nsDelete(name, flags) {
|
|
|
106
106
|
// ── Keys ─────────────────────────────────────────────────────────────────
|
|
107
107
|
async function keysList(flags) {
|
|
108
108
|
const config = requireConfig();
|
|
109
|
-
const orgId = requireOrg(flags, config, 'myapi database
|
|
110
|
-
const ns = requireNs(flags, 'myapi database
|
|
109
|
+
const orgId = requireOrg(flags, config, 'myapi database entries --ns <ns> [--prefix <p>] [--limit N] [--values] [--cursor <c>] [--org <id>]');
|
|
110
|
+
const ns = requireNs(flags, 'myapi database entries --ns <ns> [--prefix <p>] [--limit N] [--values]');
|
|
111
111
|
const opts = {};
|
|
112
112
|
if (typeof flags.prefix === 'string')
|
|
113
113
|
opts.prefix = flags.prefix;
|
|
@@ -197,12 +197,16 @@ const SUBCOMMAND_USAGE = {
|
|
|
197
197
|
'create': 'myapi database create <name> [--org <id>]',
|
|
198
198
|
'delete-namespace': `myapi database delete-namespace <name> [--org <id>]
|
|
199
199
|
|
|
200
|
-
Deletes the namespace AND all
|
|
201
|
-
'
|
|
200
|
+
Deletes the namespace AND all entries in it. Irreversible.`,
|
|
201
|
+
'entries': `myapi database entries --ns <namespace> [--prefix <p>] [--limit N] [--values] [--cursor <c>] [--org <id>] [--json]
|
|
202
202
|
|
|
203
|
-
Default render is one key per line. --values inlines each value
|
|
204
|
-
its key + etag. --cursor takes the next_cursor printed at the end
|
|
205
|
-
previous page
|
|
203
|
+
Default render is one entry key per line. --values inlines each value
|
|
204
|
+
alongside its key + etag. --cursor takes the next_cursor printed at the end
|
|
205
|
+
of a previous page.
|
|
206
|
+
|
|
207
|
+
(\`myapi database keys\` still works. It was renamed because "key" also means
|
|
208
|
+
an API credential everywhere else on the platform, and an agent reading
|
|
209
|
+
"list keys" could not tell which was meant.)`,
|
|
206
210
|
'get': `myapi database get <key> --ns <namespace> [--org <id>] [--json]
|
|
207
211
|
|
|
208
212
|
Prints the value as compact JSON on stdout; etag + updated_at go to
|
|
@@ -226,17 +230,17 @@ export async function run(subcommand, args, flags) {
|
|
|
226
230
|
|
|
227
231
|
Namespaces:
|
|
228
232
|
create <name> Create a namespace
|
|
229
|
-
delete-namespace <name> Delete a namespace AND all its
|
|
233
|
+
delete-namespace <name> Delete a namespace AND all its entries (irreversible)
|
|
230
234
|
namespaces List namespaces in the org
|
|
231
235
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
get <key> Get
|
|
235
|
-
set <key> <value-json> Set
|
|
236
|
-
del <key> Delete
|
|
236
|
+
Entries (require --ns <namespace>):
|
|
237
|
+
entries List entries in a namespace
|
|
238
|
+
get <key> Get an entry's value + etag
|
|
239
|
+
set <key> <value-json> Set an entry. Value is JSON; CAS via --if-match
|
|
240
|
+
del <key> Delete an entry (CAS via --if-match)
|
|
237
241
|
|
|
238
242
|
All commands accept --org <id> (or set default: myapi config set-org <id>).
|
|
239
|
-
Values are JSON; per-
|
|
243
|
+
Values are JSON; per-entry size cap is 256 KB. Use myapi storage for binary.`);
|
|
240
244
|
return;
|
|
241
245
|
}
|
|
242
246
|
if (flags.help) {
|
|
@@ -251,6 +255,9 @@ Values are JSON; per-key size cap is 256 KB. Use myapi storage for binary.`);
|
|
|
251
255
|
case 'namespaces': return nsList(flags);
|
|
252
256
|
case 'create': return nsCreate(args[0], flags);
|
|
253
257
|
case 'delete-namespace': return nsDelete(args[0], flags);
|
|
258
|
+
// `keys` kept as an undocumented alias: renaming a verb people have in
|
|
259
|
+
// scripts should not break them. Dropped no earlier than the next minor.
|
|
260
|
+
case 'entries':
|
|
254
261
|
case 'keys': return keysList(flags);
|
|
255
262
|
case 'get': return keyGet(args[0], flags);
|
|
256
263
|
case 'set': return keySet(args[0], args[1], flags);
|
package/dist/commands/domain.js
CHANGED
|
@@ -89,6 +89,17 @@ export async function check(domainArg, flags) {
|
|
|
89
89
|
else {
|
|
90
90
|
const msg = typeof res.message === 'string' ? res.message : res.message ? JSON.stringify(res.message) : 'Not available';
|
|
91
91
|
info(`${domain} — ${msg} ✗`);
|
|
92
|
+
// "This TLD is not currently supported" reads as a dead end, and a team
|
|
93
|
+
// building for Switzerland took it as one and settled for a .com. It is
|
|
94
|
+
// not: registering elsewhere and importing the nameservers gives the same
|
|
95
|
+
// DNS, SSL and mail behaviour, with no registrar credentials needed. The
|
|
96
|
+
// answer belongs in the same message as the refusal.
|
|
97
|
+
if (/TLD is not (currently )?supported|not supported/i.test(msg)) {
|
|
98
|
+
info('');
|
|
99
|
+
info(`→ You can still use ${domain} on MyAPI. Register it with any registrar,`);
|
|
100
|
+
info(` point its nameservers at MyAPI, then: myapi domain import ${domain}`);
|
|
101
|
+
info(' DNS, SSL and mail records work the same way afterwards.');
|
|
102
|
+
}
|
|
92
103
|
}
|
|
93
104
|
}
|
|
94
105
|
export async function register(domainArg, flags) {
|
package/dist/commands/fn.js
CHANGED
|
@@ -22,7 +22,7 @@ export const SCHEMA = {
|
|
|
22
22
|
scope: 'list',
|
|
23
23
|
set: 'string',
|
|
24
24
|
};
|
|
25
|
-
// Backend: Story 1 (function CRUD + scoped key) and Story 2/4/5 (deploy a
|
|
25
|
+
// Backend: Story 1 (function CRUD + scoped API key) and Story 2/4/5 (deploy a
|
|
26
26
|
// JS bundle to the edge runtime, list runs, set env secrets).
|
|
27
27
|
// Mirrors validateName in myapi-hq/internal/routes/function/crud.go. We
|
|
28
28
|
// pre-validate client-side so typos fail before the network call; backend
|
|
@@ -54,7 +54,10 @@ function summarizeFn(f) {
|
|
|
54
54
|
id: f.id,
|
|
55
55
|
name: f.name,
|
|
56
56
|
trigger: f.trigger_type === 'cron' ? `cron ${f.cron_schedule ?? '?'}` : 'http',
|
|
57
|
-
|
|
57
|
+
// A cron function is never reachable over HTTP — its URL 404s. Printing
|
|
58
|
+
// one contradicted the docs more loudly than the docs denied it, and a
|
|
59
|
+
// user wrote a `fetch` handler for manual runs that could never fire.
|
|
60
|
+
url: f.trigger_type === 'cron' ? '— (cron: not HTTP-invocable)' : (f.invocation_url || '(not deployed)'),
|
|
58
61
|
updated_at: f.updated_at,
|
|
59
62
|
};
|
|
60
63
|
}
|
|
@@ -89,7 +92,7 @@ export async function create(nameArg, flags) {
|
|
|
89
92
|
success(`Function created: ${result.function.id}`);
|
|
90
93
|
info(`Name: ${result.function.name}`);
|
|
91
94
|
info(`Trigger: ${result.function.trigger_type}${result.function.cron_schedule ? ` (${result.function.cron_schedule})` : ''}`);
|
|
92
|
-
// The scoped key is returned ONCE — surface it prominently. It's used by
|
|
95
|
+
// The scoped API key is returned ONCE — surface it prominently. It's used by
|
|
93
96
|
// the function runtime shim to call other slot endpoints without a
|
|
94
97
|
// baked-in auth token. Deploy rotates this key.
|
|
95
98
|
info('');
|
|
@@ -128,7 +131,9 @@ export async function get(id, flags) {
|
|
|
128
131
|
info(`ID: ${fn.id}`);
|
|
129
132
|
info(`Name: ${fn.name}`);
|
|
130
133
|
info(`Trigger: ${fn.trigger_type}${fn.cron_schedule ? ` (${fn.cron_schedule})` : ''}`);
|
|
131
|
-
info(
|
|
134
|
+
info(fn.trigger_type === 'cron'
|
|
135
|
+
? 'Invocation URL: — (cron functions are not reachable over HTTP)'
|
|
136
|
+
: `Invocation URL: ${fn.invocation_url || '(not deployed)'}`);
|
|
132
137
|
info(`Created: ${fn.created_at}`);
|
|
133
138
|
info(`Updated: ${fn.updated_at}`);
|
|
134
139
|
}
|
|
@@ -205,7 +210,7 @@ async function waitForLive(url, budgetMs = 75_000) {
|
|
|
205
210
|
clearLine();
|
|
206
211
|
info(`Status: not serving yet after ${Math.round(budgetMs / 1000)}s.`);
|
|
207
212
|
info(` The deploy itself succeeded — give it another minute.`);
|
|
208
|
-
info(` Do NOT redeploy to "fix" it: that rotates the scoped key again.`);
|
|
213
|
+
info(` Do NOT redeploy to "fix" it: that rotates the scoped API key again.`);
|
|
209
214
|
}
|
|
210
215
|
// _parseSetPairs parses `--set K=V` entries into a map. Accepts a single
|
|
211
216
|
// string (comma-joined: K=V,K2=V2) or an array of strings (when the flag is
|
package/dist/commands/task.js
CHANGED
|
@@ -15,6 +15,12 @@ export const EXPOSES = [
|
|
|
15
15
|
'POST /task/orgs/{org_id}/tasks/{id}/fail',
|
|
16
16
|
'POST /task/orgs/{org_id}/tasks/{id}/resolve',
|
|
17
17
|
];
|
|
18
|
+
// --source meant five different things across the CLI. Here it is --origin;
|
|
19
|
+
// the old name still works and is undocumented. See crm/pagination.ts.
|
|
20
|
+
function taskOrigin(flags) {
|
|
21
|
+
const v = flags.origin !== undefined ? flags.origin : flags.source;
|
|
22
|
+
return typeof v === 'string' ? v : undefined;
|
|
23
|
+
}
|
|
18
24
|
export const SCHEMA = {
|
|
19
25
|
body: 'string',
|
|
20
26
|
importance: 'string',
|
|
@@ -24,6 +30,8 @@ export const SCHEMA = {
|
|
|
24
30
|
'depends-on': 'string',
|
|
25
31
|
'dedup-key': 'string',
|
|
26
32
|
'resolve-on': 'string',
|
|
33
|
+
origin: 'string',
|
|
34
|
+
// Deprecated alias for --origin; undocumented, removable next minor.
|
|
27
35
|
source: 'string',
|
|
28
36
|
status: 'string',
|
|
29
37
|
limit: 'number',
|
|
@@ -106,7 +114,7 @@ export async function create(description, flags) {
|
|
|
106
114
|
dependsOn: _splitList(flags['depends-on']),
|
|
107
115
|
dedupKey: typeof flags['dedup-key'] === 'string' ? flags['dedup-key'] : undefined,
|
|
108
116
|
resolveOn,
|
|
109
|
-
source:
|
|
117
|
+
source: taskOrigin(flags),
|
|
110
118
|
});
|
|
111
119
|
if (flags.json) {
|
|
112
120
|
printJson(t);
|
|
@@ -125,7 +133,7 @@ export async function list(flags) {
|
|
|
125
133
|
tag: typeof flags.tag === 'string' ? flags.tag : undefined,
|
|
126
134
|
importance: typeof flags.importance === 'string' ? flags.importance : undefined,
|
|
127
135
|
assignee: typeof flags.assignee === 'string' ? flags.assignee : undefined,
|
|
128
|
-
source:
|
|
136
|
+
source: taskOrigin(flags),
|
|
129
137
|
limit: typeof flags.limit === 'number' ? flags.limit : undefined,
|
|
130
138
|
});
|
|
131
139
|
if (flags.json) {
|
|
@@ -235,8 +243,8 @@ export async function cancel(id, flags) {
|
|
|
235
243
|
}
|
|
236
244
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
237
245
|
const SUBCOMMAND_USAGE = {
|
|
238
|
-
'create': 'myapi task create "<description>" [--body <md|@file>] [--importance <i>] [--due <rfc3339>] [--assignee <email>] [--tag <t,t>] [--depends-on <id,id>] [--dedup-key <k>] [--resolve-on <event[:field=value]>] [--
|
|
239
|
-
'list': 'myapi task list [--status <s>] [--tag <t>] [--importance <i>] [--assignee <email>] [--
|
|
246
|
+
'create': 'myapi task create "<description>" [--body <md|@file>] [--importance <i>] [--due <rfc3339>] [--assignee <email>] [--tag <t,t>] [--depends-on <id,id>] [--dedup-key <k>] [--resolve-on <event[:field=value]>] [--origin <s>] [--org <id>]',
|
|
247
|
+
'list': 'myapi task list [--status <s>] [--tag <t>] [--importance <i>] [--assignee <email>] [--origin <s>] [--limit <n>] [--org <id>] [--json]',
|
|
240
248
|
'get': 'myapi task get <id> [--body] [--org <id>] [--json]\n\n--body additionally fetches the Markdown body tier (a separate read).',
|
|
241
249
|
'claim': 'myapi task claim <id> [--lease <seconds>] [--worker <name>] [--org <id>]',
|
|
242
250
|
'extend': 'myapi task extend <id> [--lease <seconds>] [--org <id>]',
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Auth, organizations, and billing hub. Start here to get an api_key and org_id — every other service depends on both.
|
|
6
6
|
triggers: [api key, account, organization, org, billing, balance, topup, credits, setup, defaults, brand, sync brand, doctor, health check, is my org healthy]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-fab324491446026e1600ac531bfbb164c10d46a617e528e6886be74b7dbdb4b1
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyApiHQ
|
|
@@ -98,4 +98,28 @@ Each org gets a free preview subdomain (`*.makeautonomous.com`) usable before re
|
|
|
98
98
|
- API keys have format `hq_live_...` and are sent as `Authorization: Bearer <key>`.
|
|
99
99
|
- `org sync-brand` is async (scrapes the site, polls the job).
|
|
100
100
|
|
|
101
|
+
## Minting a least-privilege API key
|
|
102
|
+
|
|
103
|
+
A key's authority is inline and always a subset of the key that mints it, so
|
|
104
|
+
you can hand work a key that cannot exceed its job:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
myapi keys create --name ci --grant funnel:write,storage:read
|
|
108
|
+
myapi keys create --name readonly --grant '*:read' # read anything, write nothing
|
|
109
|
+
myapi keys create --name billing-fn --org <id> --grant email --spend-cap 25
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- `--grant <list>` — `slot:read` / `slot:write`; a bare slot means write, `*`
|
|
113
|
+
means all. **Omitting `--grant` mints an unrestricted key.**
|
|
114
|
+
- `--org <id>` — lock it to one org. Omit for account-wide.
|
|
115
|
+
- `--spend-cap <usd>` — hard ceiling; `0` means the key cannot spend at all.
|
|
116
|
+
- `keys revoke-all --kind function|manual|account` narrows the kill switch.
|
|
117
|
+
|
|
118
|
+
## Org profile fields
|
|
119
|
+
|
|
120
|
+
`myapi org create <name>` also takes `--tagline`, `--description`,
|
|
121
|
+
`--business-sector` and `--logo-url`. They populate the org's public profile
|
|
122
|
+
and the funnel created alongside it, so setting them at create avoids editing
|
|
123
|
+
two places later.
|
|
124
|
+
|
|
101
125
|
Run `myapi --help` or `myapi <command> --help` for full flag reference.
|
|
@@ -4,7 +4,7 @@ version: 1.0.0
|
|
|
4
4
|
description: >
|
|
5
5
|
Saved audiences = named Goldfox-filter snapshots over the people or company database. Build a target list once, name it, reuse it across campaigns, refresh to re-evaluate against current data. The persistence layer on top of my-people-api + my-company-api.
|
|
6
6
|
triggers: [audience, segment, target list, saved filter, goldfox, lead list, abm list, refresh, members, prospect database]
|
|
7
|
-
checksum: sha256-
|
|
7
|
+
checksum: sha256-a2c7142e880932ac2aa53cdb29b8632ce2c4d5ef33c3f8b6df705f7e987ab6af
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MyAudienceAPI
|
|
@@ -70,7 +70,7 @@ Allowed values:
|
|
|
70
70
|
<!-- generated:start -->
|
|
71
71
|
| Command | What it does |
|
|
72
72
|
|---|---|
|
|
73
|
-
| `myapi audience create <name> --
|
|
73
|
+
| `myapi audience create <name> --from <people\|company> --filter '<json>' [--description <text>]` | Save a Goldfox filter as a named audience; returns id + initial member_count |
|
|
74
74
|
| `myapi audience list` | List all audiences in the org |
|
|
75
75
|
| `myapi audience get <id>` | Single audience (name, filter, member_count, timestamps) |
|
|
76
76
|
| `myapi audience update <id> [--name <x>] [--description <y>] [--filter '<json>']` | Patch name/description/filter; member_count re-evaluates if filter changes |
|
|
@@ -84,7 +84,7 @@ Allowed values:
|
|
|
84
84
|
```bash
|
|
85
85
|
# 1. Create the audience
|
|
86
86
|
AID=$(myapi audience create "EU decision makers w/ corporate emails" \
|
|
87
|
-
--
|
|
87
|
+
--from people \
|
|
88
88
|
--filter '{"seniority":["c_level","vp_director"],"country":["DE","FR","GB"],"email_type":["corporate"]}' \
|
|
89
89
|
--json | jq -r .id)
|
|
90
90
|
|
|
@@ -108,7 +108,7 @@ myapi audience delete $AID
|
|
|
108
108
|
```bash
|
|
109
109
|
# Build with quality controls — definitive links + registered companies + careers signal
|
|
110
110
|
AID=$(myapi audience create "EU growth-stage decision makers" \
|
|
111
|
-
--
|
|
111
|
+
--from people \
|
|
112
112
|
--filter '{"seniority":["c_level","vp_director"],"country":["DE","FR","GB"],"email_type":["corporate"],"min_link_confidence":0.9,"has_careers_page":true,"is_registered_entity":true}' \
|
|
113
113
|
--json | jq -r .id)
|
|
114
114
|
|
|
@@ -123,7 +123,7 @@ myapi audience refresh $AID
|
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
125
|
myapi audience create "EU growth-stage SaaS accounts" \
|
|
126
|
-
--
|
|
126
|
+
--from company \
|
|
127
127
|
--filter '{"country":["DE","FR","GB","NL"],"has_careers_page":true,"has_decision_maker":true,"is_registered_entity":true,"min_source_count":3}'
|
|
128
128
|
```
|
|
129
129
|
<!-- llm:end -->
|