@primitivedotdev/sdk 0.11.0 → 0.13.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/api/generated/index.js +1 -1
- package/dist/api/generated/sdk.gen.js +48 -4
- package/dist/api/index.d.ts +2 -2
- package/dist/{api-CLLpjjWy.js → api-DvJpdOJ8.js} +53 -5
- package/dist/{index-K4KbjppU.d.ts → index-ChLFXxTa.d.ts} +255 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/oclif/api-command.js +127 -29
- package/dist/oclif/commands/send.js +16 -3
- package/dist/oclif/commands/whoami.js +67 -0
- package/dist/oclif/index.js +6 -0
- package/dist/openapi/openapi.generated.js +208 -7
- package/dist/openapi/operations.generated.js +19 -2
- package/oclif.manifest.json +119 -51
- package/package.json +1 -1
|
@@ -67,8 +67,22 @@ function extractBodyFields(schema) {
|
|
|
67
67
|
kind = "complex";
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
// Pull the first paragraph of the schema description for use
|
|
71
|
+
// as the CLI flag's --help string. We split on a blank line
|
|
72
|
+
// (paragraph break) and then collapse any soft line wraps
|
|
73
|
+
// inside that paragraph to spaces. This avoids the previous
|
|
74
|
+
// bug where `split("\n")[0]` truncated wrapped prose like
|
|
75
|
+
// "Optional override for ... Defaults to\nthe inbound's..."
|
|
76
|
+
// to "Optional override for ... Defaults to" - a sentence
|
|
77
|
+
// ending with "to" with nothing after it, which read as
|
|
78
|
+
// ellipsis truncation in --help. The remaining paragraphs
|
|
79
|
+
// are intentionally dropped so multi-paragraph schemas don't
|
|
80
|
+
// blow out the per-flag help block.
|
|
70
81
|
const description = typeof propSchema.description === "string"
|
|
71
|
-
? propSchema.description
|
|
82
|
+
? propSchema.description
|
|
83
|
+
.split(/\n\s*\n/)[0]
|
|
84
|
+
.replace(/\s*\n\s*/g, " ")
|
|
85
|
+
.trim()
|
|
72
86
|
: "";
|
|
73
87
|
const enumRaw = propSchema.enum;
|
|
74
88
|
const enumValues = kind === "string" && Array.isArray(enumRaw)
|
|
@@ -95,15 +109,15 @@ function extractBodyFields(schema) {
|
|
|
95
109
|
* Most scalar fields are exposed as individual `--flag` flags,
|
|
96
110
|
* which oclif auto-renders in the FLAGS section above. To avoid
|
|
97
111
|
* duplicating that, the summary here only documents fields that
|
|
98
|
-
* MUST go through `--body` (complex types: arrays, objects,
|
|
112
|
+
* MUST go through `--raw-body` (complex types: arrays, objects,
|
|
99
113
|
* mixed-non-nullable). When an operation has only scalars, the
|
|
100
114
|
* summary is omitted entirely and oclif's FLAGS section is the
|
|
101
115
|
* full story.
|
|
102
116
|
*
|
|
103
117
|
* For operations with mixed scalar and complex fields, we also
|
|
104
118
|
* include a short header pointing the agent at the flag form so
|
|
105
|
-
* the natural reading is "use the flags above; --body for
|
|
106
|
-
* leftovers below."
|
|
119
|
+
* the natural reading is "use the flags above; --raw-body for
|
|
120
|
+
* the leftovers below."
|
|
107
121
|
*/
|
|
108
122
|
function renderRequestSchemaSummary(schema) {
|
|
109
123
|
const fields = extractBodyFields(schema);
|
|
@@ -115,7 +129,7 @@ function renderRequestSchemaSummary(schema) {
|
|
|
115
129
|
const nameWidth = Math.min(24, Math.max(...complex.map((f) => f.name.length)));
|
|
116
130
|
const descMax = 78;
|
|
117
131
|
const lines = [
|
|
118
|
-
"Body fields requiring --body JSON (these are not exposed as flags):",
|
|
132
|
+
"Body fields requiring --raw-body JSON (these are not exposed as flags):",
|
|
119
133
|
];
|
|
120
134
|
for (const f of complex) {
|
|
121
135
|
const marker = f.required ? " *" : " ";
|
|
@@ -180,9 +194,9 @@ function parseJson(source, flagLabel) {
|
|
|
180
194
|
}
|
|
181
195
|
export function readJsonBody(flags) {
|
|
182
196
|
const bodyFile = flags["body-file"];
|
|
183
|
-
const
|
|
184
|
-
if (bodyFile &&
|
|
185
|
-
throw cliError("Use either --body or --body-file, not both");
|
|
197
|
+
const rawBody = flags["raw-body"];
|
|
198
|
+
if (bodyFile && rawBody) {
|
|
199
|
+
throw cliError("Use either --raw-body or --body-file, not both");
|
|
186
200
|
}
|
|
187
201
|
if (typeof bodyFile === "string") {
|
|
188
202
|
let contents;
|
|
@@ -195,8 +209,8 @@ export function readJsonBody(flags) {
|
|
|
195
209
|
}
|
|
196
210
|
return parseJson(contents, `--body-file ${bodyFile}`);
|
|
197
211
|
}
|
|
198
|
-
if (typeof
|
|
199
|
-
return parseJson(
|
|
212
|
+
if (typeof rawBody === "string") {
|
|
213
|
+
return parseJson(rawBody, "--raw-body");
|
|
200
214
|
}
|
|
201
215
|
return undefined;
|
|
202
216
|
}
|
|
@@ -242,34 +256,93 @@ export function formatErrorPayload(payload) {
|
|
|
242
256
|
}
|
|
243
257
|
return JSON.stringify(payload, null, 2);
|
|
244
258
|
}
|
|
259
|
+
// Pull the top-level error code out of either a server response
|
|
260
|
+
// payload (`{ error: { code: '...' } }` or `{ code: '...' }`) or a
|
|
261
|
+
// thrown Error whose `cause.code` carries the value. Used to drive
|
|
262
|
+
// `--api-key` and similar hints in writeErrorWithHints below.
|
|
263
|
+
// Also exported so individual commands (send, whoami) can branch
|
|
264
|
+
// on auth failures and avoid surfacing misleading "fix this flag"
|
|
265
|
+
// guidance when the real problem is the API key.
|
|
266
|
+
export function extractErrorCode(payload) {
|
|
267
|
+
if (payload instanceof Error) {
|
|
268
|
+
const { code } = extractCauseDetails(payload.cause);
|
|
269
|
+
return code;
|
|
270
|
+
}
|
|
271
|
+
if (payload && typeof payload === "object") {
|
|
272
|
+
const inner = payload.error;
|
|
273
|
+
if (inner && typeof inner === "object" && typeof inner.code === "string") {
|
|
274
|
+
return inner.code;
|
|
275
|
+
}
|
|
276
|
+
const direct = payload.code;
|
|
277
|
+
if (typeof direct === "string")
|
|
278
|
+
return direct;
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
// Common-case actionable hints keyed by error code. The full
|
|
283
|
+
// JSON envelope still goes to stderr unchanged for any caller
|
|
284
|
+
// that wants to parse it; the hint is an extra trailing line so
|
|
285
|
+
// a human reading the output sees "what to actually do next."
|
|
286
|
+
// The AGX walkthrough flagged that an `unauthorized` envelope
|
|
287
|
+
// alone left the agent without context for the env var or the
|
|
288
|
+
// `--api-key` flag; this closes that gap without having to
|
|
289
|
+
// special-case every command.
|
|
290
|
+
const ERROR_CODE_HINTS = {
|
|
291
|
+
unauthorized: "Hint: pass --api-key explicitly, or set PRIMITIVE_API_KEY in your environment. `primitive whoami` is the fastest way to verify a key is live.",
|
|
292
|
+
};
|
|
293
|
+
// Write a server / SDK error to stderr in the canonical envelope
|
|
294
|
+
// shape, plus an actionable hint when the code is one we know how
|
|
295
|
+
// to advise on. Replaces the bare
|
|
296
|
+
// `process.stderr.write(${formatErrorPayload(p)}\n)` dance every
|
|
297
|
+
// command was doing.
|
|
298
|
+
export function writeErrorWithHints(payload) {
|
|
299
|
+
process.stderr.write(`${formatErrorPayload(payload)}\n`);
|
|
300
|
+
const code = extractErrorCode(payload);
|
|
301
|
+
if (code && ERROR_CODE_HINTS[code]) {
|
|
302
|
+
process.stderr.write(`${ERROR_CODE_HINTS[code]}\n`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
245
305
|
// Reserved flag names the body-field expander must never overwrite.
|
|
246
|
-
// `--body` and `--body-file` are the JSON escape hatches.
|
|
306
|
+
// `--raw-body` and `--body-file` are the JSON escape hatches.
|
|
247
307
|
// `--api-key`, `--base-url`, `--output` are infra. Path and query
|
|
248
308
|
// params get added before body fields and take precedence.
|
|
309
|
+
//
|
|
310
|
+
// Note: `--body` is intentionally NOT reserved here. The naive
|
|
311
|
+
// agent expectation (per AGX walkthrough) is that --body means
|
|
312
|
+
// "the message body content," which collides with the JSON
|
|
313
|
+
// escape-hatch meaning we used pre-0.12. The escape hatch is now
|
|
314
|
+
// `--raw-body`; --body is free to be claimed by per-field flag
|
|
315
|
+
// expansion as the kebab-cased version of a `body` schema field
|
|
316
|
+
// (e.g. on a future `body: { ... }` schema). For send-mail today,
|
|
317
|
+
// the body-text field is `body_text` -> `--body-text`, and there
|
|
318
|
+
// is no top-level `body` field, so --body remains unclaimed at
|
|
319
|
+
// the generated-command level. The agent shortcut `primitive
|
|
320
|
+
// send` defines its own --body for the message text.
|
|
249
321
|
const RESERVED_FLAG_NAMES = new Set([
|
|
250
322
|
"api-key",
|
|
251
323
|
"base-url",
|
|
252
|
-
"body",
|
|
324
|
+
"raw-body",
|
|
253
325
|
"body-file",
|
|
254
326
|
"output",
|
|
255
327
|
]);
|
|
256
328
|
function bodyFieldFlag(field) {
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
329
|
+
// Pass the full first-line description through. oclif's --help
|
|
330
|
+
// renderer wraps long values across multiple lines on its own,
|
|
331
|
+
// so a fixed character cap here just produces ellipsis-truncated
|
|
332
|
+
// sentences ("body_html is required. Th...") that mislead the
|
|
333
|
+
// reader. extractBodyFields already normalizes by taking only
|
|
334
|
+
// the first paragraph of the schema description, so multi-
|
|
335
|
+
// paragraph fields don't blow out the help.
|
|
336
|
+
//
|
|
264
337
|
// Field-flag UX choice: do NOT mark scalar body fields as
|
|
265
338
|
// required at the oclif level even when the JSON Schema marks
|
|
266
339
|
// them required. Reason: a caller can satisfy the requirement
|
|
267
|
-
// either via the individual flag OR via --body / --body-file.
|
|
340
|
+
// either via the individual flag OR via --raw-body / --body-file.
|
|
268
341
|
// Marking the flag required would force the individual-flag
|
|
269
342
|
// form. The runtime body merger validates the final assembled
|
|
270
343
|
// body against the same server-side schema either way.
|
|
271
344
|
const common = {
|
|
272
|
-
description:
|
|
345
|
+
description: field.description || field.name,
|
|
273
346
|
};
|
|
274
347
|
if (field.kind === "boolean")
|
|
275
348
|
return Flags.boolean(common);
|
|
@@ -296,11 +369,11 @@ function buildFlags(operation) {
|
|
|
296
369
|
}
|
|
297
370
|
const bodyFieldFlagToProperty = new Map();
|
|
298
371
|
if (operation.hasJsonBody) {
|
|
299
|
-
flags
|
|
300
|
-
description: "Full request body as JSON.
|
|
372
|
+
flags["raw-body"] = Flags.string({
|
|
373
|
+
description: "Full request body as raw JSON. Escape hatch for nested or complex fields (e.g. arrays); prefer per-field flags (e.g. --to, --from, --body-text) when available.",
|
|
301
374
|
});
|
|
302
375
|
flags["body-file"] = Flags.string({
|
|
303
|
-
description: "Path to a JSON file used as the request body. Same role as --body for callers passing a saved payload.",
|
|
376
|
+
description: "Path to a JSON file used as the request body. Same role as --raw-body for callers passing a saved payload.",
|
|
304
377
|
});
|
|
305
378
|
// Expand top-level scalar body fields into individual flags so
|
|
306
379
|
// `primitive sending:send-email --to alice@x --from support@x
|
|
@@ -414,7 +487,7 @@ export function createOperationCommand(operation) {
|
|
|
414
487
|
body = { ...explicit, ...overrides };
|
|
415
488
|
}
|
|
416
489
|
else {
|
|
417
|
-
// Caller passed --body as null, an array, or a
|
|
490
|
+
// Caller passed --raw-body as null, an array, or a
|
|
418
491
|
// primitive AND also passed per-field flags. We can't
|
|
419
492
|
// merge per-field overrides into a non-object body
|
|
420
493
|
// shape, and silently dropping either source would
|
|
@@ -428,7 +501,7 @@ export function createOperationCommand(operation) {
|
|
|
428
501
|
const overrideFlags = Object.keys(overrides)
|
|
429
502
|
.map((p) => `--${flagName(p)}`)
|
|
430
503
|
.join(", ");
|
|
431
|
-
throw new Errors.CLIError(`--body must be a JSON object when also passing per-field flags (got ${explicitKind}); supplied per-field flags: ${overrideFlags}. Either drop --body and rely on the per-field flags, or move every field into the JSON --body and drop the flags.`);
|
|
504
|
+
throw new Errors.CLIError(`--raw-body must be a JSON object when also passing per-field flags (got ${explicitKind}); supplied per-field flags: ${overrideFlags}. Either drop --raw-body and rely on the per-field flags, or move every field into the JSON --raw-body and drop the flags.`);
|
|
432
505
|
}
|
|
433
506
|
}
|
|
434
507
|
else {
|
|
@@ -436,7 +509,7 @@ export function createOperationCommand(operation) {
|
|
|
436
509
|
}
|
|
437
510
|
}
|
|
438
511
|
if (operation.bodyRequired && body === undefined) {
|
|
439
|
-
throw new Errors.CLIError(`Operation ${operation.operationId} requires a body. Pass each field as a --flag (see --help) or supply JSON via --body / --body-file.`);
|
|
512
|
+
throw new Errors.CLIError(`Operation ${operation.operationId} requires a body. Pass each field as a --flag (see --help) or supply JSON via --raw-body / --body-file.`);
|
|
440
513
|
}
|
|
441
514
|
const operationFn = operations[operation.sdkName];
|
|
442
515
|
const result = await operationFn({
|
|
@@ -448,8 +521,7 @@ export function createOperationCommand(operation) {
|
|
|
448
521
|
responseStyle: "fields",
|
|
449
522
|
});
|
|
450
523
|
if (result.error) {
|
|
451
|
-
|
|
452
|
-
process.stderr.write(`${formatErrorPayload(errorPayload)}\n`);
|
|
524
|
+
writeErrorWithHints(extractErrorPayload(result.error));
|
|
453
525
|
process.exitCode = 1;
|
|
454
526
|
return;
|
|
455
527
|
}
|
|
@@ -469,8 +541,34 @@ export function createOperationCommand(operation) {
|
|
|
469
541
|
if (cursor) {
|
|
470
542
|
process.stderr.write(`next cursor: ${cursor}\n`);
|
|
471
543
|
}
|
|
544
|
+
// Empty-result hint. When a list-style operation returns
|
|
545
|
+
// an empty array, emit an operation-specific note to
|
|
546
|
+
// stderr so a naive caller can distinguish "nothing here"
|
|
547
|
+
// from "something isn't set up." Stdout still gets the
|
|
548
|
+
// raw `[]` so machine-readable output is unchanged. The
|
|
549
|
+
// AGX walkthrough flagged this: `list-deliveries` returning
|
|
550
|
+
// `[]` left the agent unsure whether they had an empty
|
|
551
|
+
// delivery log or no endpoints configured at all.
|
|
552
|
+
if (Array.isArray(envelope?.data) && envelope.data.length === 0) {
|
|
553
|
+
const hint = EMPTY_RESULT_HINTS[operation.sdkName];
|
|
554
|
+
if (hint)
|
|
555
|
+
process.stderr.write(`${hint}\n`);
|
|
556
|
+
}
|
|
472
557
|
this.log(JSON.stringify(envelope?.data ?? null, null, 2));
|
|
473
558
|
}
|
|
474
559
|
}
|
|
475
560
|
return OperationCommand;
|
|
476
561
|
}
|
|
562
|
+
// Empty-state hints for list-style operations whose empty result
|
|
563
|
+
// would otherwise leave the caller wondering "is this empty
|
|
564
|
+
// because there's nothing to list, or because something earlier
|
|
565
|
+
// in the setup chain isn't done?" Keys are the manifest's
|
|
566
|
+
// `sdkName` for the operation. Operations without an entry fall
|
|
567
|
+
// back to no hint (silent empty array, same as before).
|
|
568
|
+
const EMPTY_RESULT_HINTS = {
|
|
569
|
+
listDeliveries: "(no results) Often means no webhook endpoints are configured to receive deliveries. Run `primitive endpoints:list-endpoints` to check.",
|
|
570
|
+
listEndpoints: "(no results) No webhook endpoints configured. Add one with `primitive endpoints:create-endpoint --url <your-url>`.",
|
|
571
|
+
listEmails: "(no results) No inbound emails received yet on this account.",
|
|
572
|
+
listDomains: "(no results) No domains on this account. Add one with `primitive domains:add-domain --domain <yourdomain.example>`.",
|
|
573
|
+
listFilters: "(no results) No filter rules configured.",
|
|
574
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command, Errors, Flags } from "@oclif/core";
|
|
2
2
|
import { listDomains, sendEmail } from "../../api/generated/sdk.gen.js";
|
|
3
3
|
import { PrimitiveApiClient } from "../../api/index.js";
|
|
4
|
-
import { extractErrorPayload, formatErrorPayload } from "../api-command.js";
|
|
4
|
+
import { extractErrorCode, extractErrorPayload, formatErrorPayload, writeErrorWithHints, } from "../api-command.js";
|
|
5
5
|
// `primitive send` is the agent-grade shortcut for the most common
|
|
6
6
|
// case: send a fresh outbound email. It wraps `sending:send-email`
|
|
7
7
|
// with two ergonomic defaults that the underlying operation can't
|
|
@@ -52,6 +52,20 @@ async function pickDefaultFromAddress(apiClient) {
|
|
|
52
52
|
});
|
|
53
53
|
if (result.error) {
|
|
54
54
|
const errorPayload = extractErrorPayload(result.error);
|
|
55
|
+
// If the underlying failure is an auth problem, don't pretend
|
|
56
|
+
// --from will fix it: the actual sendEmail call would 401 too.
|
|
57
|
+
// Surface the auth hint via writeErrorWithHints and bail with
|
|
58
|
+
// a focused message instead of the verbose "underlying error"
|
|
59
|
+
// wrapping.
|
|
60
|
+
if (extractErrorCode(errorPayload) === "unauthorized") {
|
|
61
|
+
writeErrorWithHints(errorPayload);
|
|
62
|
+
// exit: 1 to match the run() unauthorized path (which uses
|
|
63
|
+
// `process.exitCode = 1`). oclif's CLIError defaults to 2,
|
|
64
|
+
// so without this override the same "unauthorized" condition
|
|
65
|
+
// exits 2 when surfaced from listDomains and 1 when surfaced
|
|
66
|
+
// from sendEmail, breaking callers that branch on exit code.
|
|
67
|
+
throw new Errors.CLIError("Cannot send: API key is missing or invalid (see hint above).", { exit: 1 });
|
|
68
|
+
}
|
|
55
69
|
throw new Errors.CLIError(`Could not look up your verified domains to default --from. Pass --from explicitly. Underlying error: ${formatErrorPayload(errorPayload)}`);
|
|
56
70
|
}
|
|
57
71
|
const envelope = result.data;
|
|
@@ -147,8 +161,7 @@ class SendCommand extends Command {
|
|
|
147
161
|
responseStyle: "fields",
|
|
148
162
|
});
|
|
149
163
|
if (result.error) {
|
|
150
|
-
|
|
151
|
-
process.stderr.write(`${formatErrorPayload(errorPayload)}\n`);
|
|
164
|
+
writeErrorWithHints(extractErrorPayload(result.error));
|
|
152
165
|
process.exitCode = 1;
|
|
153
166
|
return;
|
|
154
167
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Command, Errors, Flags } from "@oclif/core";
|
|
2
|
+
import { getAccount } from "../../api/generated/sdk.gen.js";
|
|
3
|
+
import { PrimitiveApiClient } from "../../api/index.js";
|
|
4
|
+
import { extractErrorPayload, writeErrorWithHints } from "../api-command.js";
|
|
5
|
+
// `primitive whoami` is the credentials smoke-test the AGX
|
|
6
|
+
// walkthrough kept asking for. Before this command, a user with a
|
|
7
|
+
// suspect API key had no fast way to verify "is my key live and
|
|
8
|
+
// pointed at the org I expect" short of trying any other call and
|
|
9
|
+
// reading a 401. That ambiguity bit two consecutive walkthroughs.
|
|
10
|
+
//
|
|
11
|
+
// Implementation: thin wrapper over /api/v1/account that prints
|
|
12
|
+
// the account email, plan, id, and onboarding status. Any auth
|
|
13
|
+
// problem surfaces as the standard error envelope, same as the
|
|
14
|
+
// generated commands.
|
|
15
|
+
class WhoamiCommand extends Command {
|
|
16
|
+
static description = `Print the account currently authenticated by the API key. Useful as a credentials smoke test: confirms the key is live and shows which account it belongs to.`;
|
|
17
|
+
static summary = "Print the authenticated account (credentials smoke test)";
|
|
18
|
+
static examples = [
|
|
19
|
+
"<%= config.bin %> whoami",
|
|
20
|
+
"<%= config.bin %> whoami --api-key prim_...",
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
"api-key": Flags.string({
|
|
24
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY)",
|
|
25
|
+
env: "PRIMITIVE_API_KEY",
|
|
26
|
+
}),
|
|
27
|
+
"base-url": Flags.string({
|
|
28
|
+
description: "API base URL (defaults to PRIMITIVE_API_URL or production)",
|
|
29
|
+
env: "PRIMITIVE_API_URL",
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
async run() {
|
|
33
|
+
const { flags } = await this.parse(WhoamiCommand);
|
|
34
|
+
const apiClient = new PrimitiveApiClient({
|
|
35
|
+
apiKey: flags["api-key"],
|
|
36
|
+
baseUrl: flags["base-url"],
|
|
37
|
+
});
|
|
38
|
+
const result = await getAccount({
|
|
39
|
+
client: apiClient.client,
|
|
40
|
+
responseStyle: "fields",
|
|
41
|
+
});
|
|
42
|
+
if (result.error) {
|
|
43
|
+
writeErrorWithHints(extractErrorPayload(result.error));
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const envelope = result.data;
|
|
48
|
+
const account = envelope?.data;
|
|
49
|
+
if (!account) {
|
|
50
|
+
process.stderr.write("Server returned an empty account body; this should not happen for a valid key.\n");
|
|
51
|
+
throw new Errors.CLIError("unexpected empty response");
|
|
52
|
+
}
|
|
53
|
+
// Concise human-readable summary on stderr; the full account
|
|
54
|
+
// JSON goes to stdout so a script can pipe it.
|
|
55
|
+
const onboarding = account.onboarding_completed === true
|
|
56
|
+
? "complete"
|
|
57
|
+
: account.onboarding_step
|
|
58
|
+
? `in progress (step: ${account.onboarding_step})`
|
|
59
|
+
: "incomplete";
|
|
60
|
+
process.stderr.write(`Authenticated as ${account.email}\n`);
|
|
61
|
+
process.stderr.write(` Account id: ${account.id}\n`);
|
|
62
|
+
process.stderr.write(` Plan: ${account.plan}\n`);
|
|
63
|
+
process.stderr.write(` Onboarding: ${onboarding}\n`);
|
|
64
|
+
this.log(JSON.stringify(account, null, 2));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export default WhoamiCommand;
|
package/dist/oclif/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Args, Command } from "@oclif/core";
|
|
|
2
2
|
import { operationManifest, } from "../openapi/index.js";
|
|
3
3
|
import { createOperationCommand } from "./api-command.js";
|
|
4
4
|
import SendCommand from "./commands/send.js";
|
|
5
|
+
import WhoamiCommand from "./commands/whoami.js";
|
|
5
6
|
import { renderFishCompletion } from "./fish-completion.js";
|
|
6
7
|
class ListOperationsCommand extends Command {
|
|
7
8
|
static description = "List all generated API operations";
|
|
@@ -44,5 +45,10 @@ export const COMMANDS = {
|
|
|
44
45
|
// operation stays available under sending:send-email for callers
|
|
45
46
|
// who want every flag.
|
|
46
47
|
send: SendCommand,
|
|
48
|
+
// `whoami` is the credentials smoke test. Prints the account the
|
|
49
|
+
// current API key authenticates as. AGX walkthroughs kept
|
|
50
|
+
// wanting this before risking a real call against a possibly-
|
|
51
|
+
// bad key.
|
|
52
|
+
whoami: WhoamiCommand,
|
|
47
53
|
...generatedCommands,
|
|
48
54
|
};
|