@myapihq/cli 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/account.js +15 -0
- package/dist/commands/audience.js +7 -5
- 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/domain.js +11 -0
- package/dist/commands/feedback.d.ts +10 -0
- package/dist/commands/feedback.js +173 -0
- package/dist/commands/flag-reachability.test.d.ts +1 -0
- package/dist/commands/flag-reachability.test.js +274 -0
- package/dist/commands/fn.js +7 -2
- package/dist/commands/llm.d.ts +1 -0
- package/dist/commands/llm.js +25 -9
- package/dist/commands/task.js +12 -4
- package/dist/completion.js +2 -1
- package/dist/exposes.test.js +1 -0
- package/dist/index.js +7 -0
- 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 +25 -4
- package/dist/skills/my-crm-api/SKILL.md +6 -6
- package/dist/skills/my-database-api/SKILL.md +22 -1
- 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 +35 -1
- package/dist/skills/my-funnel-api/SKILL.md +6 -3
- package/dist/skills/my-git-api/SKILL.md +7 -1
- package/dist/skills/my-llm-api/SKILL.md +17 -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 +7 -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
|
{
|
|
@@ -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
|
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) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FlagSchema } from '../flags.js';
|
|
2
|
+
import { type Flags } from '../helpers.js';
|
|
3
|
+
import type { Exposes } from '../exposes.js';
|
|
4
|
+
export declare const EXPOSES: Exposes;
|
|
5
|
+
export declare const SCHEMA: FlagSchema;
|
|
6
|
+
export declare function list(flags: Flags): Promise<void>;
|
|
7
|
+
export declare function create(bodyArg: string | undefined, flags: Flags): Promise<void>;
|
|
8
|
+
export declare function resolve(id: string, flags: Flags): Promise<void>;
|
|
9
|
+
export declare function widget(sub: string | undefined, arg: string | undefined, flags: Flags): Promise<void>;
|
|
10
|
+
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// `myapi feedback` — collect what the people using your product tell you.
|
|
2
|
+
//
|
|
3
|
+
// The slot shipped on 2026-07-28 with five endpoints and no CLI. A primitive
|
|
4
|
+
// with no surface is one nobody finds, which is the failure this repo spent a
|
|
5
|
+
// week fixing: two customers concluded a shipped capability did not exist
|
|
6
|
+
// because nothing they read mentioned it.
|
|
7
|
+
import { feedback as sdkFeedback } from '@myapihq/sdk';
|
|
8
|
+
import { requireConfig } from '../config.js';
|
|
9
|
+
import { success, error, info, printTable, printJson, banner } from '../output.js';
|
|
10
|
+
import { formatDate } from '../utils.js';
|
|
11
|
+
import { requireOrg, requireArg, confirmDestructive } from '../helpers.js';
|
|
12
|
+
export const EXPOSES = [
|
|
13
|
+
'GET /feedback/orgs/{org_id}/items',
|
|
14
|
+
'POST /feedback/orgs/{org_id}/items',
|
|
15
|
+
'POST /feedback/orgs/{org_id}/items/{id}/resolve',
|
|
16
|
+
'POST /feedback/orgs/{org_id}/widgets',
|
|
17
|
+
'DELETE /feedback/orgs/{org_id}/widgets/{id}',
|
|
18
|
+
];
|
|
19
|
+
export const SCHEMA = {
|
|
20
|
+
kind: 'string',
|
|
21
|
+
status: 'string',
|
|
22
|
+
body: 'string',
|
|
23
|
+
'page-url': 'string',
|
|
24
|
+
route: 'string',
|
|
25
|
+
origins: 'string',
|
|
26
|
+
};
|
|
27
|
+
const KINDS = ['bug', 'idea', 'praise', 'confusion', 'other'];
|
|
28
|
+
export async function list(flags) {
|
|
29
|
+
const config = requireConfig();
|
|
30
|
+
const orgId = requireOrg(flags, config, 'myapi feedback list [--kind <k>] [--status open|resolved] [--org <id>]');
|
|
31
|
+
if (flags.kind !== undefined && !KINDS.includes(flags.kind)) {
|
|
32
|
+
error(`Invalid --kind "${flags.kind}". Use one of: ${KINDS.join(', ')}.`);
|
|
33
|
+
}
|
|
34
|
+
if (flags.status !== undefined && flags.status !== 'open' && flags.status !== 'resolved') {
|
|
35
|
+
error(`Invalid --status "${flags.status}". Use "open" or "resolved".`);
|
|
36
|
+
}
|
|
37
|
+
const page = await sdkFeedback.listFeedback(config.api_key, orgId, {
|
|
38
|
+
kind: flags.kind,
|
|
39
|
+
status: flags.status,
|
|
40
|
+
limit: typeof flags.limit === 'number' ? flags.limit : undefined,
|
|
41
|
+
offset: typeof flags.offset === 'number' ? flags.offset : undefined,
|
|
42
|
+
});
|
|
43
|
+
if (flags.json) {
|
|
44
|
+
printJson(page);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// `total` here is the match count, not the page size — the platform states
|
|
48
|
+
// that explicitly, having been bitten by the opposite in CRM search.
|
|
49
|
+
const shown = page.items?.length ?? 0;
|
|
50
|
+
info(shown === page.total ? `${shown} item${shown === 1 ? '' : 's'}` : `${shown} of ${page.total} items`);
|
|
51
|
+
printTable((page.items ?? []).map(i => ({
|
|
52
|
+
id: i.id,
|
|
53
|
+
kind: i.kind,
|
|
54
|
+
status: i.status,
|
|
55
|
+
body: i.body.length > 60 ? `${i.body.slice(0, 57)}…` : i.body,
|
|
56
|
+
route: i.route ?? i.page_url ?? '',
|
|
57
|
+
created: i.created_at ? formatDate(i.created_at) : '',
|
|
58
|
+
})), { flags, empty: 'No feedback yet. Put a widget on a page: myapi feedback widget create <name>' });
|
|
59
|
+
if (page.has_more)
|
|
60
|
+
info('More available — raise --limit or pass --offset.');
|
|
61
|
+
}
|
|
62
|
+
export async function create(bodyArg, flags) {
|
|
63
|
+
const config = requireConfig();
|
|
64
|
+
const orgId = requireOrg(flags, config, 'myapi feedback create "<text>" --kind <k> [--org <id>]');
|
|
65
|
+
const body = bodyArg ?? flags.body;
|
|
66
|
+
requireArg(body, 'text', 'myapi feedback create "<text>" --kind bug');
|
|
67
|
+
const kind = flags.kind ?? 'other';
|
|
68
|
+
if (!KINDS.includes(kind)) {
|
|
69
|
+
error(`Invalid --kind "${kind}". Use one of: ${KINDS.join(', ')}.\n\n→ Kind is what the PERSON says it is. "bug" is a claim that the product is broken; do not infer it from the wording.`);
|
|
70
|
+
}
|
|
71
|
+
const item = await sdkFeedback.createFeedback(config.api_key, orgId, {
|
|
72
|
+
kind: kind,
|
|
73
|
+
body: body,
|
|
74
|
+
page_url: typeof flags['page-url'] === 'string' ? flags['page-url'] : undefined,
|
|
75
|
+
route: typeof flags.route === 'string' ? flags.route : undefined,
|
|
76
|
+
});
|
|
77
|
+
if (flags.json) {
|
|
78
|
+
printJson(item);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
success(`Recorded ${item.kind}: ${item.id}`);
|
|
82
|
+
}
|
|
83
|
+
export async function resolve(id, flags) {
|
|
84
|
+
const config = requireConfig();
|
|
85
|
+
const orgId = requireOrg(flags, config, 'myapi feedback resolve <id> [--org <id>]');
|
|
86
|
+
if (!id)
|
|
87
|
+
error('Missing id.\nUsage: myapi feedback resolve <id>');
|
|
88
|
+
await sdkFeedback.resolveFeedback(config.api_key, orgId, id);
|
|
89
|
+
// Already-resolved and not-found answer identically, so this is not proof
|
|
90
|
+
// the id existed. Say so rather than implying a state change happened.
|
|
91
|
+
success(`Resolved ${id}`);
|
|
92
|
+
info('(An unknown id answers the same way, so this is not confirmation the item existed.)');
|
|
93
|
+
}
|
|
94
|
+
export async function widget(sub, arg, flags) {
|
|
95
|
+
const config = requireConfig();
|
|
96
|
+
const orgId = requireOrg(flags, config, 'myapi feedback widget create <name> | revoke <id>');
|
|
97
|
+
if (sub === 'create') {
|
|
98
|
+
requireArg(arg, 'name', 'myapi feedback widget create <name> [--origins a.com,b.com]');
|
|
99
|
+
const origins = typeof flags.origins === 'string'
|
|
100
|
+
? flags.origins.split(',').map(s => s.trim()).filter(Boolean)
|
|
101
|
+
: undefined;
|
|
102
|
+
const w = await sdkFeedback.createWidget(config.api_key, orgId, arg, origins);
|
|
103
|
+
if (flags.json) {
|
|
104
|
+
printJson(w);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
success(`Widget created: ${w.id}`);
|
|
108
|
+
info(`Key: ${w.key}`);
|
|
109
|
+
info('');
|
|
110
|
+
// Saying this plainly matters: a value that looks like a credential and is
|
|
111
|
+
// not one gets treated as a secret, and then nobody puts it in the page.
|
|
112
|
+
info('This key is PUBLIC. It ships in your page source and authenticates nobody —');
|
|
113
|
+
info('it names your org so a visitor can submit without signing in.');
|
|
114
|
+
if (!origins?.length) {
|
|
115
|
+
banner('No --origins set, so any site can post through this key. Set them unless that is intended.');
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (sub === 'revoke') {
|
|
120
|
+
requireArg(arg, 'id', 'myapi feedback widget revoke <id>');
|
|
121
|
+
await confirmDestructive(flags, `revoke widget ${arg} (submissions with it stop immediately)`, 'myapi feedback widget revoke <id> [--yes] [--org <id>]');
|
|
122
|
+
await sdkFeedback.revokeWidget(config.api_key, orgId, arg);
|
|
123
|
+
success(`Widget ${arg} revoked`);
|
|
124
|
+
info('Feedback already collected through it is kept.');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
error('Usage: myapi feedback widget create <name> [--origins <list>]\n myapi feedback widget revoke <id>');
|
|
128
|
+
}
|
|
129
|
+
const SUBCOMMAND_USAGE = {
|
|
130
|
+
'list': `myapi feedback list [--kind bug|idea|praise|confusion|other] [--status open|resolved]
|
|
131
|
+
[--limit N] [--offset N] [--org <id>] [--json]
|
|
132
|
+
|
|
133
|
+
Newest first. \`total\` is the number of matches, not the page size.`,
|
|
134
|
+
'create': `myapi feedback create "<text>" --kind <k> [--page-url <url>] [--route <path>] [--org <id>]
|
|
135
|
+
|
|
136
|
+
--kind is what the person reporting says it is, not what the text sounds like.`,
|
|
137
|
+
'resolve': 'myapi feedback resolve <id> [--org <id>]',
|
|
138
|
+
'widget': `myapi feedback widget create <name> [--origins a.com,b.com] [--org <id>]
|
|
139
|
+
myapi feedback widget revoke <id> [--yes] [--org <id>]
|
|
140
|
+
|
|
141
|
+
The key a widget mints is PUBLIC — it ships in page source and authenticates
|
|
142
|
+
nobody. --origins stops another site posting through it.`,
|
|
143
|
+
};
|
|
144
|
+
export async function run(subcommand, args, flags) {
|
|
145
|
+
if (!subcommand || (flags.help && !subcommand)) {
|
|
146
|
+
info(`Usage: myapi feedback <subcommand>
|
|
147
|
+
|
|
148
|
+
Collect feedback from the people using what you built. A widget key lets a
|
|
149
|
+
page submit without a credential; you list, filter and resolve the results.
|
|
150
|
+
|
|
151
|
+
Subcommands:
|
|
152
|
+
create "<text>" Record one piece of feedback (--kind bug|idea|praise|confusion|other)
|
|
153
|
+
list List feedback, newest first (--kind, --status, --limit, --offset)
|
|
154
|
+
resolve <id> Close a piece of feedback
|
|
155
|
+
widget create <name> Mint a PUBLIC widget key for a site (--origins to restrict)
|
|
156
|
+
widget revoke <id> Revoke a widget key; collected feedback is kept
|
|
157
|
+
|
|
158
|
+
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (flags.help) {
|
|
162
|
+
const usage = SUBCOMMAND_USAGE[subcommand];
|
|
163
|
+
info(usage ? `Usage: ${usage}` : `Unknown subcommand: ${subcommand}. Run "myapi feedback --help" for the list.`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
switch (subcommand) {
|
|
167
|
+
case 'create': return create(args[0], flags);
|
|
168
|
+
case 'list': return list(flags);
|
|
169
|
+
case 'resolve': return resolve(args[0], flags);
|
|
170
|
+
case 'widget': return widget(args[0], args[1], flags);
|
|
171
|
+
default: error(`Unknown subcommand: ${subcommand}. Run "myapi feedback --help" for a list of valid subcommands.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|