@invinite-org/invinite-data-cli 3.0.0 → 4.1.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/README.md +341 -374
- package/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/agents/invinite-data.md +59 -28
- package/claude-plugin/commands/analyze-earnings-call.md +15 -14
- package/claude-plugin/commands/analyze-filing.md +8 -7
- package/claude-plugin/commands/compare-companies.md +5 -5
- package/claude-plugin/commands/financial-research.md +9 -9
- package/claude-plugin/commands/ownership-review.md +6 -6
- package/claude-plugin/skills/invinite-data-cli/SKILL.md +466 -570
- package/dist/bin/invd.js +11 -2
- package/dist/bin/invd.js.map +1 -1
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +48 -29
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client/api-client.d.ts +24 -24
- package/dist/src/client/api-client.js +52 -70
- package/dist/src/client/api-client.js.map +1 -1
- package/dist/src/client/pagination.d.ts +12 -11
- package/dist/src/client/pagination.js +47 -56
- package/dist/src/client/pagination.js.map +1 -1
- package/dist/src/commands/agent-setup.d.ts +21 -0
- package/dist/src/commands/agent-setup.js +112 -0
- package/dist/src/commands/agent-setup.js.map +1 -0
- package/dist/src/commands/api.d.ts +27 -0
- package/dist/src/commands/api.js +180 -0
- package/dist/src/commands/api.js.map +1 -0
- package/dist/src/commands/auth.d.ts +37 -0
- package/dist/src/commands/auth.js +336 -0
- package/dist/src/commands/auth.js.map +1 -0
- package/dist/src/commands/completion.d.ts +7 -0
- package/dist/src/commands/completion.js +115 -0
- package/dist/src/commands/completion.js.map +1 -0
- package/dist/src/commands/config.d.ts +4 -0
- package/dist/src/commands/config.js +85 -48
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/plugin.d.ts +8 -0
- package/dist/src/commands/plugin.js +8 -10
- package/dist/src/commands/plugin.js.map +1 -1
- package/dist/src/config/auth.d.ts +17 -1
- package/dist/src/config/auth.js +50 -36
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config-manager.d.ts +56 -4
- package/dist/src/config/config-manager.js +173 -29
- package/dist/src/config/config-manager.js.map +1 -1
- package/dist/src/config/toml.d.ts +7 -0
- package/dist/src/config/toml.js +79 -0
- package/dist/src/config/toml.js.map +1 -0
- package/dist/src/examples.d.ts +22 -0
- package/dist/src/examples.js +56 -0
- package/dist/src/examples.js.map +1 -0
- package/dist/src/generated/cli-manifest.json +5573 -5140
- package/dist/src/hand-written-commands.d.ts +11 -41
- package/dist/src/hand-written-commands.js +7 -17
- package/dist/src/hand-written-commands.js.map +1 -1
- package/dist/src/output/jq.d.ts +15 -0
- package/dist/src/output/jq.js +234 -0
- package/dist/src/output/jq.js.map +1 -0
- package/dist/src/output/options.d.ts +29 -0
- package/dist/src/output/options.js +72 -0
- package/dist/src/output/options.js.map +1 -0
- package/dist/src/output/render.d.ts +54 -0
- package/dist/src/output/render.js +160 -0
- package/dist/src/output/render.js.map +1 -0
- package/dist/src/shared/errors.d.ts +59 -15
- package/dist/src/shared/errors.js +126 -38
- package/dist/src/shared/errors.js.map +1 -1
- package/dist/src/shared/prompt.d.ts +10 -0
- package/dist/src/shared/prompt.js +24 -0
- package/dist/src/shared/prompt.js.map +1 -0
- package/dist/src/shared/spinner.js +2 -1
- package/dist/src/shared/spinner.js.map +1 -1
- package/dist/src/spec-commands.d.ts +14 -6
- package/dist/src/spec-commands.js +454 -174
- package/dist/src/spec-commands.js.map +1 -1
- package/dist/src/spec-flags.d.ts +11 -6
- package/dist/src/spec-flags.js +14 -7
- package/dist/src/spec-flags.js.map +1 -1
- package/dist/src/spec-manifest.d.ts +416 -116
- package/dist/src/spec-manifest.js +44 -13
- package/dist/src/spec-manifest.js.map +1 -1
- package/dist/src/update-notifier.d.ts +9 -0
- package/dist/src/update-notifier.js +63 -0
- package/dist/src/update-notifier.js.map +1 -0
- package/package.json +2 -3
- package/dist/src/client/earnings-live.d.ts +0 -194
- package/dist/src/client/earnings-live.js +0 -300
- package/dist/src/client/earnings-live.js.map +0 -1
- package/dist/src/client/news-live.d.ts +0 -72
- package/dist/src/client/news-live.js +0 -120
- package/dist/src/client/news-live.js.map +0 -1
- package/dist/src/client/pdf.d.ts +0 -15
- package/dist/src/client/pdf.js +0 -15
- package/dist/src/client/pdf.js.map +0 -1
- package/dist/src/client/websocket-client.d.ts +0 -19
- package/dist/src/client/websocket-client.js +0 -174
- package/dist/src/client/websocket-client.js.map +0 -1
- package/dist/src/commands/earnings.d.ts +0 -16
- package/dist/src/commands/earnings.js +0 -464
- package/dist/src/commands/earnings.js.map +0 -1
- package/dist/src/commands/login.d.ts +0 -15
- package/dist/src/commands/login.js +0 -249
- package/dist/src/commands/login.js.map +0 -1
- package/dist/src/commands/news.d.ts +0 -2
- package/dist/src/commands/news.js +0 -69
- package/dist/src/commands/news.js.map +0 -1
- package/dist/src/commands/sec.d.ts +0 -47
- package/dist/src/commands/sec.js +0 -191
- package/dist/src/commands/sec.js.map +0 -1
- package/dist/src/commands/ws.d.ts +0 -33
- package/dist/src/commands/ws.js +0 -116
- package/dist/src/commands/ws.js.map +0 -1
- package/dist/src/formatters/json-formatter.d.ts +0 -1
- package/dist/src/formatters/json-formatter.js +0 -4
- package/dist/src/formatters/json-formatter.js.map +0 -1
- package/dist/src/formatters/table-formatter.d.ts +0 -4
- package/dist/src/formatters/table-formatter.js +0 -73
- package/dist/src/formatters/table-formatter.js.map +0 -1
- package/dist/src/formatters/time.d.ts +0 -2
- package/dist/src/formatters/time.js +0 -14
- package/dist/src/formatters/time.js.map +0 -1
- package/dist/src/shared/output.d.ts +0 -3
- package/dist/src/shared/output.js +0 -13
- package/dist/src/shared/output.js.map +0 -1
- package/dist/src/shared/readiness.d.ts +0 -4
- package/dist/src/shared/readiness.js +0 -60
- package/dist/src/shared/readiness.js.map +0 -1
- package/dist/src/shared/types.d.ts +0 -11
- package/dist/src/shared/types.js +0 -2
- package/dist/src/shared/types.js.map +0 -1
|
@@ -1,231 +1,511 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { Option } from 'commander';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
4
|
+
import { apiCall, buildUrl } from './client/api-client.js';
|
|
5
|
+
import { saveVerified } from './client/download.js';
|
|
6
|
+
import { nextCursorOf, rowsOf, walkPages } from './client/pagination.js';
|
|
7
|
+
import { getSetting } from './config/config-manager.js';
|
|
8
|
+
import { exampleLines } from './examples.js';
|
|
9
|
+
import { pick } from './formatters/flatten.js';
|
|
10
|
+
import { runJq } from './output/jq.js';
|
|
11
|
+
import { addOutputOptions, resolveOutput } from './output/options.js';
|
|
12
|
+
import { bodyRows, csvColumns, csvHeader, csvRow, emit, humanText, renderBody, shapeBody, } from './output/render.js';
|
|
13
|
+
import { PartialResultError, UsageError, withErrorHandling } from './shared/errors.js';
|
|
10
14
|
import { splitList } from './shared/options.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { flagName } from './spec-flags.js';
|
|
15
|
-
import { exampleValue } from './spec-manifest.js';
|
|
15
|
+
import { confirmOrRefuse, readStdin } from './shared/prompt.js';
|
|
16
|
+
import { GLOBAL_PARAM_FLAGS } from './spec-flags.js';
|
|
17
|
+
import { commandName, exampleValue, } from './spec-manifest.js';
|
|
16
18
|
/**
|
|
17
|
-
* Registers
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* view as the human output. A group a hand-written command already registered (`earnings`, whose download commands
|
|
21
|
-
* are rostered in `src/hand-written-commands.ts`) gets the generated actions attached; commander refuses a duplicate.
|
|
19
|
+
* Registers the manifest's operations: `invd <group> <action>` (a group's `list` is its default, so
|
|
20
|
+
* `invd filings AAPL` lists), one positional for the natural key, one flag per parameter, the output / paging /
|
|
21
|
+
* metering flags, and `invd financials` as ONE command over its six operations (`--basis` × `--view`).
|
|
22
22
|
*/
|
|
23
23
|
export function registerSpecCommands(program, manifest) {
|
|
24
|
+
const listEvents = manifest.operations.find(operation => operation.operationId === 'listEvents');
|
|
24
25
|
for (const group of manifest.groups) {
|
|
25
26
|
const operations = manifest.operations.filter(operation => operation.group === group.name);
|
|
26
|
-
const groupCommand =
|
|
27
|
-
|
|
27
|
+
const groupCommand = program.command(group.name).description(group.description);
|
|
28
|
+
const multi = operations.filter(operation => operation.action === '');
|
|
29
|
+
if (multi.length > 0) {
|
|
30
|
+
defineSelectorCommand(groupCommand, multi, { listEvents });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
28
33
|
for (const operation of operations) {
|
|
29
|
-
const command =
|
|
30
|
-
?
|
|
31
|
-
|
|
32
|
-
defineOperationCommand(command, operation);
|
|
34
|
+
const command = groupCommand
|
|
35
|
+
.command(operation.action, operation.action === 'list' ? { isDefault: true } : {})
|
|
36
|
+
.description(operation.summary);
|
|
37
|
+
defineOperationCommand(command, operation, { listEvents });
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
37
|
-
/** The value placeholder commander shows after a parameter's flag (a boolean takes none). */
|
|
38
|
-
const VALUE_PLACEHOLDER = {
|
|
39
|
-
boolean: '',
|
|
40
|
-
array: ' <list>',
|
|
41
|
-
string: ' <value>',
|
|
42
|
-
integer: ' <value>',
|
|
43
|
-
number: ' <value>',
|
|
44
|
-
};
|
|
45
|
-
/** Where each pagination shape keeps the next page's URL (`none` has none). */
|
|
46
|
-
const NEXT_URL_PATH = {
|
|
47
|
-
none: undefined,
|
|
48
|
-
top: 'next_url',
|
|
49
|
-
perCompany: 'companies.0.next_url',
|
|
50
|
-
};
|
|
51
42
|
const HTTP_METHOD = {
|
|
52
|
-
post: 'POST',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
get: 'GET', post: 'POST', put: 'PUT', patch: 'PATCH', delete: 'DELETE',
|
|
44
|
+
};
|
|
45
|
+
/** Commander's attribute name of a long flag (`filed-at-gte` → `filedAtGte`). */
|
|
46
|
+
const attributeName = (flag) => flag.replace(/-([a-z0-9])/g, (_match, char) => char.toUpperCase());
|
|
47
|
+
/** Parameters the CLI fills when the flag is absent (spec proposal D10: `period` default on the financials). */
|
|
48
|
+
const IMPLIED = {
|
|
49
|
+
period: values => (['1', '2', '3'].includes(String(values.get('fiscal_quarter'))) ? 'quarterly' : 'annual'),
|
|
56
50
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
const IMPLIED_NOTE = { period: 'quarterly with --fiscal-quarter 1–3, else annual' };
|
|
52
|
+
const impliedFor = (operation) => operation.select ? operation.params.filter(param => param.name in IMPLIED && param.required).map(param => param.name) : [];
|
|
53
|
+
function describeParam(param) {
|
|
60
54
|
const notes = [];
|
|
61
|
-
if (param.type === 'array')
|
|
55
|
+
if (param.type === 'array')
|
|
62
56
|
notes.push(param.enum ? `comma-separated: ${param.enum.join(', ')}` : 'comma-separated');
|
|
63
|
-
}
|
|
64
57
|
if (param.default !== undefined)
|
|
65
58
|
notes.push(`default: ${JSON.stringify(param.default)}`);
|
|
66
59
|
if (param.example !== undefined)
|
|
67
60
|
notes.push(`e.g. ${exampleValue(param.example)}`);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
return [param.description ?? '', notes.length ? `(${notes.join('; ')})` : ''].filter(Boolean).join(' ');
|
|
62
|
+
}
|
|
63
|
+
/** The options of one parameter: `--flag <value>`, or `--flag` / `--no-flag` for a boolean. */
|
|
64
|
+
function paramOptions(param, required) {
|
|
65
|
+
const short = param.alias ? `-${param.alias}, ` : '';
|
|
66
|
+
if (param.type === 'boolean') {
|
|
67
|
+
return [
|
|
68
|
+
new Option(`${short}--${param.flag}`, `${describeParam(param)} (sends true)`),
|
|
69
|
+
new Option(`--no-${param.flag}`, `Sends ${param.name}=false`),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
const option = new Option(`${short}--${param.flag} <${param.type === 'array' ? 'list' : 'value'}>`, describeParam(param));
|
|
73
|
+
if (required)
|
|
72
74
|
option.makeOptionMandatory();
|
|
73
75
|
if (param.enum && param.type !== 'array')
|
|
74
76
|
option.choices(param.enum);
|
|
75
|
-
return option;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (type === '
|
|
81
|
-
return
|
|
82
|
-
if (type === 'number')
|
|
83
|
-
return z.string().refine(value => value.trim() !== '' && Number.isFinite(Number(value)));
|
|
84
|
-
return z.string();
|
|
85
|
-
};
|
|
86
|
-
/** The zod check of one parameter's raw flag value, yielding what goes on the wire. */
|
|
87
|
-
function paramSchema(param) {
|
|
88
|
-
if (param.type === 'boolean')
|
|
89
|
-
return z.boolean().transform(value => String(value));
|
|
90
|
-
if (param.type === 'array') {
|
|
91
|
-
return z.string().transform(value => splitList(value)).pipe(z.array(itemSchema(param.items, param.enum)).min(1));
|
|
92
|
-
}
|
|
93
|
-
if (param.type === 'integer' || param.type === 'number') {
|
|
94
|
-
return z.string().pipe(itemSchema(param.type, undefined)).transform(value => String(Number(value)));
|
|
77
|
+
return [option];
|
|
78
|
+
}
|
|
79
|
+
function bodyOptions(field) {
|
|
80
|
+
const short = field.alias ? `-${field.alias}, ` : '';
|
|
81
|
+
const description = `${field.description ?? ''} (body ${field.path.join('.')})`.trim();
|
|
82
|
+
if (field.type === 'boolean') {
|
|
83
|
+
return [new Option(`${short}--${field.flag}`, `${description}: true`), new Option(`--no-${field.flag}`, `${description}: false`)];
|
|
95
84
|
}
|
|
96
|
-
return
|
|
85
|
+
return [new Option(`${short}--${field.flag} <${field.type === 'array' ? 'list' : 'value'}>`, description)];
|
|
97
86
|
}
|
|
98
87
|
function expectation(param) {
|
|
99
|
-
if (param.type === 'array')
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
if (param.type === 'array')
|
|
89
|
+
return `a comma-separated list${param.enum ? ` of ${param.enum.join(', ')}` : ''}`;
|
|
90
|
+
if (param.enum)
|
|
91
|
+
return `one of ${param.enum.join(', ')}`;
|
|
103
92
|
if (param.type === 'integer')
|
|
104
93
|
return 'an integer';
|
|
105
94
|
if (param.type === 'number')
|
|
106
95
|
return 'a number';
|
|
107
|
-
if (param.
|
|
108
|
-
return
|
|
96
|
+
if (param.pattern)
|
|
97
|
+
return `a value matching ${param.pattern}`;
|
|
109
98
|
return 'a value';
|
|
110
99
|
}
|
|
111
|
-
/**
|
|
112
|
-
function
|
|
100
|
+
/** One flag value validated and converted to what goes on the wire; refuses by flag name (exit 2). */
|
|
101
|
+
export function wireValue(param, raw) {
|
|
102
|
+
const fail = () => new UsageError(`--${param.flag} expects ${expectation(param)} (got ${JSON.stringify(raw)})`);
|
|
103
|
+
if (param.type === 'boolean') {
|
|
104
|
+
if (typeof raw !== 'boolean')
|
|
105
|
+
throw fail();
|
|
106
|
+
return String(raw);
|
|
107
|
+
}
|
|
108
|
+
const text = String(raw);
|
|
109
|
+
if (param.type === 'array') {
|
|
110
|
+
const items = splitList(text);
|
|
111
|
+
const itemOk = (item) => (param.enum ? param.enum.includes(item) : true)
|
|
112
|
+
&& (param.items === 'integer' ? /^-?\d+$/.test(item) : true)
|
|
113
|
+
&& (param.items === 'number' ? Number.isFinite(Number(item)) : true);
|
|
114
|
+
if (items.length === 0 || !items.every(itemOk))
|
|
115
|
+
throw fail();
|
|
116
|
+
return items;
|
|
117
|
+
}
|
|
118
|
+
if (param.type === 'integer' && !/^-?\d+$/.test(text))
|
|
119
|
+
throw fail();
|
|
120
|
+
if (param.type === 'number' && (text.trim() === '' || !Number.isFinite(Number(text))))
|
|
121
|
+
throw fail();
|
|
122
|
+
if (param.enum && !param.enum.includes(text))
|
|
123
|
+
throw fail();
|
|
124
|
+
if (param.pattern && !new RegExp(param.pattern, 'u').test(text))
|
|
125
|
+
throw fail();
|
|
126
|
+
return param.type === 'integer' || param.type === 'number' ? String(Number(text)) : text;
|
|
127
|
+
}
|
|
128
|
+
/** `2026Q3`, `2026 Q3`, `FY2026Q3` → fiscal year and quarter. */
|
|
129
|
+
export function parseFiscalPeriod(text) {
|
|
130
|
+
const match = /^(?:FY)?(\d{4})\s*-?\s*Q([1-4])$/i.exec(text.trim());
|
|
131
|
+
return match?.[1] && match[2] ? { fiscalYear: match[1], fiscalQuarter: match[2] } : undefined;
|
|
132
|
+
}
|
|
133
|
+
const helpExamples = (lines) => `Examples:\n${lines.map(line => ` $ ${line}`).join('\n')}\n`;
|
|
134
|
+
function helpFooter(operations) {
|
|
135
|
+
const [first] = operations;
|
|
136
|
+
if (!first)
|
|
137
|
+
return '';
|
|
138
|
+
const cost = operations.length === 1
|
|
139
|
+
? `Cost: ${first.credits} credit${first.credits === 1 ? '' : 's'} per response · plan: ${first.tier}`
|
|
140
|
+
: `Cost per response: ${operations.map(operation => `${Object.values(operation.select ?? {}).join('/')} ${operation.credits} (${operation.tier})`).join(', ')}`;
|
|
141
|
+
const fields = first.fields.length ? `\nFields (--json a,b): ${first.fields.join(', ')}` : '';
|
|
142
|
+
return `\n${cost}${fields}\n`;
|
|
143
|
+
}
|
|
144
|
+
function addPositionals(command, operation) {
|
|
145
|
+
const positional = operation.positional;
|
|
146
|
+
if (!positional)
|
|
147
|
+
return;
|
|
148
|
+
const param = operation.params.find(candidate => candidate.name === positional.name);
|
|
149
|
+
const description = param?.description ?? positional.name;
|
|
150
|
+
if (positional.name === 'event_id') {
|
|
151
|
+
command.argument('<event_id>', `${description}; or a company followed by a fiscal period (NVDA 2026Q3)`);
|
|
152
|
+
command.argument('[fiscal_period]', 'With a company in place of the event id: the fiscal period (2026Q3), resolved through GET /v2/events');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const name = positional.variadic ? `${positional.name}...` : positional.name;
|
|
156
|
+
const note = positional.variadic === 'comma' ? ' (one or more; sent as a comma list)' : '';
|
|
157
|
+
command.argument(positional.required ? `<${name}>` : `[${name}]`, `${description}${note}`);
|
|
158
|
+
}
|
|
159
|
+
function addParamOptions(command, operation, params) {
|
|
160
|
+
const implied = impliedFor(operation);
|
|
161
|
+
for (const param of params) {
|
|
162
|
+
if (param.name === operation.positional?.name || GLOBAL_PARAM_FLAGS.includes(param.flag))
|
|
163
|
+
continue;
|
|
164
|
+
const options = paramOptions(param, param.required && !implied.includes(param.name));
|
|
165
|
+
if (implied.includes(param.name) && options[0]) {
|
|
166
|
+
options[0].description = `${options[0].description} — absent: ${IMPLIED_NOTE[param.name] ?? 'filled by invd'}`;
|
|
167
|
+
}
|
|
168
|
+
for (const option of options)
|
|
169
|
+
command.addOption(option);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function addCommandOptions(command, operation) {
|
|
173
|
+
if (operation.body) {
|
|
174
|
+
for (const field of operation.body.fields)
|
|
175
|
+
for (const option of bodyOptions(field))
|
|
176
|
+
command.addOption(option);
|
|
177
|
+
command.option('--body <json>', 'The whole JSON request body (flags above override its fields)');
|
|
178
|
+
command.option('--body-file <path>', 'Read the JSON request body from a file (- = stdin)');
|
|
179
|
+
}
|
|
180
|
+
if (operation.download !== undefined) {
|
|
181
|
+
command.option('--save <file>', 'Save the file this response points to (size and SHA-256 verified)');
|
|
182
|
+
command.option('--force', 'With --save: replace an existing file');
|
|
183
|
+
}
|
|
184
|
+
addOutputOptions(command, { list: operation.list, fieldsParam: operation.params.some(param => param.name === 'fields') });
|
|
185
|
+
}
|
|
186
|
+
function defineOperationCommand(command, operation, context) {
|
|
187
|
+
addPositionals(command, operation);
|
|
188
|
+
addParamOptions(command, operation, operation.params);
|
|
189
|
+
addCommandOptions(command, operation);
|
|
190
|
+
command.addHelpText('before', helpExamples(exampleLines(operation)));
|
|
191
|
+
command.addHelpText('after', helpFooter([operation]));
|
|
192
|
+
command.action(withErrorHandling(async (...args) => {
|
|
193
|
+
const invoked = args[args.length - 1];
|
|
194
|
+
await runOperation(operation, invoked.processedArgs, invoked.opts(), context);
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
/** `invd financials`: one command, the operation picked by the selector flags (`--basis`, `--view`). */
|
|
198
|
+
function defineSelectorCommand(command, operations, context) {
|
|
199
|
+
const keys = [...new Set(operations.flatMap(operation => Object.keys(operation.select ?? {})))];
|
|
200
|
+
const choices = (key) => [...new Set(operations.map(operation => operation.select?.[key] ?? ''))];
|
|
201
|
+
// The first operation of each key's first value is the default (standardized, summary).
|
|
202
|
+
const primary = operations.find(operation => keys.every(key => operation.select?.[key] === defaultChoice(key, choices(key)))) ?? operations[0];
|
|
203
|
+
if (!primary)
|
|
204
|
+
return;
|
|
205
|
+
const params = new Map();
|
|
206
|
+
for (const operation of operations)
|
|
207
|
+
for (const param of operation.params)
|
|
208
|
+
if (!params.has(param.name))
|
|
209
|
+
params.set(param.name, param);
|
|
210
|
+
addPositionals(command, primary);
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
command.addOption(new Option(`--${key} <${key}>`, `Which ${key}`).choices(choices(key)).default(defaultChoice(key, choices(key))));
|
|
213
|
+
}
|
|
214
|
+
addParamOptions(command, primary, [...params.values()]);
|
|
215
|
+
addCommandOptions(command, primary);
|
|
216
|
+
const [first] = exampleLines(primary, { implied: impliedFor(primary) });
|
|
217
|
+
const detailed = operations.find(operation => operation.select?.view === 'detailed' && operation.select?.basis === primary.select?.basis);
|
|
218
|
+
const [, second] = exampleLines(detailed ?? primary, { implied: impliedFor(primary), extra: detailed ? ['--view detailed'] : [] });
|
|
219
|
+
command.addHelpText('before', helpExamples([first, second]));
|
|
220
|
+
command.addHelpText('after', helpFooter(operations));
|
|
221
|
+
command.action(withErrorHandling(async (...args) => {
|
|
222
|
+
const invoked = args[args.length - 1];
|
|
223
|
+
const opts = invoked.opts();
|
|
224
|
+
const operation = operations.find(candidate => keys.every(key => candidate.select?.[key] === opts[key]));
|
|
225
|
+
if (!operation)
|
|
226
|
+
throw new UsageError(`No operation for ${keys.map(key => `--${key} ${String(opts[key])}`).join(' ')}`);
|
|
227
|
+
const foreign = [...params.values()].filter(param => !operation.params.some(own => own.name === param.name) && opts[attributeName(param.flag)] !== undefined);
|
|
228
|
+
if (foreign.length > 0) {
|
|
229
|
+
const where = operations.filter(candidate => candidate.params.some(own => own.name === foreign[0]?.name))
|
|
230
|
+
.map(candidate => keys.map(key => `--${key} ${candidate.select?.[key]}`).join(' '));
|
|
231
|
+
const bases = [...new Set(where.map(spelling => spelling.split(' --view')[0]))];
|
|
232
|
+
throw new UsageError(`--${foreign[0]?.flag} applies to ${(bases.length === 1 ? bases : where).join(' | ')} only`);
|
|
233
|
+
}
|
|
234
|
+
await runOperation(operation, invoked.processedArgs, opts, context);
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
const defaultChoice = (key, values) => ({ basis: 'standardized', view: 'summary' }[key] ?? values[0] ?? '');
|
|
238
|
+
/** The request this command line describes, validated; nothing is sent. */
|
|
239
|
+
async function planRequest(operation, positionals, opts, context) {
|
|
113
240
|
const values = new Map();
|
|
114
|
-
|
|
115
|
-
|
|
241
|
+
const positional = operation.positional;
|
|
242
|
+
let lookup;
|
|
243
|
+
if (positional) {
|
|
244
|
+
const [first, second] = positionals;
|
|
245
|
+
if (positional.name === 'event_id' && typeof second === 'string' && second !== '') {
|
|
246
|
+
const period = parseFiscalPeriod(second);
|
|
247
|
+
if (!period)
|
|
248
|
+
throw new UsageError(`${second} is not a fiscal period (expected e.g. 2026Q3)`);
|
|
249
|
+
lookup = {
|
|
250
|
+
request: {
|
|
251
|
+
path: '/v2/events',
|
|
252
|
+
query: { company: String(first), fiscal_year: period.fiscalYear, fiscal_quarter: period.fiscalQuarter, limit: '1' },
|
|
253
|
+
},
|
|
254
|
+
company: String(first),
|
|
255
|
+
period: second,
|
|
256
|
+
credits: context.listEvents?.credits ?? 1,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
else if (Array.isArray(first)) {
|
|
260
|
+
if (first.length > 0)
|
|
261
|
+
values.set(positional.name, first.map(String).join(positional.variadic === 'space' ? ' ' : ','));
|
|
262
|
+
}
|
|
263
|
+
else if (first !== undefined) {
|
|
264
|
+
values.set(positional.name, String(first));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (const param of operation.params) {
|
|
268
|
+
if (param.name === positional?.name)
|
|
269
|
+
continue;
|
|
270
|
+
const raw = GLOBAL_PARAM_FLAGS.includes(param.flag) ? opts[param.flag] : opts[attributeName(param.flag)];
|
|
116
271
|
if (raw === undefined)
|
|
117
272
|
continue;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
273
|
+
values.set(param.name, wireValue(param, raw));
|
|
274
|
+
}
|
|
275
|
+
for (const name of impliedFor(operation)) {
|
|
276
|
+
if (values.has(name))
|
|
277
|
+
continue;
|
|
278
|
+
const value = IMPLIED[name]?.(values);
|
|
279
|
+
if (value === undefined)
|
|
280
|
+
continue;
|
|
281
|
+
values.set(name, value);
|
|
282
|
+
console.error(chalk.dim(`--${name} ${value} assumed (pass --${name} to choose)`));
|
|
283
|
+
}
|
|
284
|
+
const query = {};
|
|
285
|
+
const headers = {};
|
|
286
|
+
for (const param of operation.params) {
|
|
287
|
+
const value = values.get(param.name);
|
|
288
|
+
if (param.in === 'query')
|
|
289
|
+
query[param.name] = value;
|
|
290
|
+
if (param.in === 'header' && typeof value === 'string')
|
|
291
|
+
headers[param.name] = value;
|
|
123
292
|
}
|
|
124
|
-
|
|
293
|
+
const path = operation.path.replace(/\{([^}]+)\}/g, (_match, name) => {
|
|
294
|
+
if (lookup && name === 'event_id')
|
|
295
|
+
return '{event_id}';
|
|
296
|
+
const value = values.get(name);
|
|
297
|
+
if (typeof value !== 'string' || value === '')
|
|
298
|
+
throw new UsageError(`Missing ${name}`);
|
|
299
|
+
return encodeURIComponent(value);
|
|
300
|
+
});
|
|
301
|
+
const body = operation.body ? await requestBody(operation, opts) : undefined;
|
|
302
|
+
return {
|
|
303
|
+
request: {
|
|
304
|
+
path,
|
|
305
|
+
method: HTTP_METHOD[operation.method],
|
|
306
|
+
query,
|
|
307
|
+
...(Object.keys(headers).length ? { headers } : {}),
|
|
308
|
+
...(body !== undefined ? { body } : {}),
|
|
309
|
+
auth: operation.auth,
|
|
310
|
+
},
|
|
311
|
+
...(lookup ? { lookup } : {}),
|
|
312
|
+
};
|
|
125
313
|
}
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
314
|
+
function setPath(target, path, value) {
|
|
315
|
+
const [head, ...rest] = path;
|
|
316
|
+
if (head === undefined)
|
|
317
|
+
return;
|
|
318
|
+
if (rest.length === 0) {
|
|
319
|
+
target[head] = value;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const child = isRecord(target[head]) ? target[head] : {};
|
|
323
|
+
target[head] = child;
|
|
324
|
+
setPath(child, rest, value);
|
|
131
325
|
}
|
|
132
|
-
/** `--body
|
|
326
|
+
/** `--body` / `--body-file` (JSON), then each body flag over it; `undefined` when nothing was given. */
|
|
133
327
|
async function requestBody(operation, opts) {
|
|
134
328
|
const inline = typeof opts.body === 'string' ? opts.body : undefined;
|
|
135
329
|
const file = typeof opts.bodyFile === 'string' ? opts.bodyFile : undefined;
|
|
136
330
|
if (inline !== undefined && file !== undefined)
|
|
137
|
-
throw new
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
331
|
+
throw new UsageError('Pass --body or --body-file, not both');
|
|
332
|
+
let body;
|
|
333
|
+
if (inline !== undefined || file !== undefined) {
|
|
334
|
+
const [flag, text] = inline !== undefined
|
|
335
|
+
? ['--body', inline]
|
|
336
|
+
: [`--body-file ${file}`, file === '-' ? await readStdin() : await readFile(file ?? '', 'utf8')];
|
|
337
|
+
try {
|
|
338
|
+
body = JSON.parse(text);
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
throw new UsageError(`${flag} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const field of operation.body?.fields ?? []) {
|
|
345
|
+
const raw = opts[attributeName(field.flag)];
|
|
346
|
+
if (raw === undefined)
|
|
347
|
+
continue;
|
|
348
|
+
if (body !== undefined && !isRecord(body))
|
|
349
|
+
throw new UsageError('--body must be a JSON object to combine with field flags');
|
|
350
|
+
body ??= {};
|
|
351
|
+
const value = field.type === 'array' ? splitList(String(raw)) : field.type === 'integer' || field.type === 'number' ? Number(raw) : raw;
|
|
352
|
+
setPath(body, field.path, value);
|
|
353
|
+
}
|
|
354
|
+
if (body === undefined && operation.body?.required) {
|
|
355
|
+
const flags = operation.body.fields.map(field => `--${field.flag}`);
|
|
356
|
+
throw new UsageError(`This command needs a request body: ${flags.length ? `${flags.join(', ')}, or ` : ''}--body / --body-file`);
|
|
357
|
+
}
|
|
358
|
+
return body;
|
|
359
|
+
}
|
|
360
|
+
const describeRequest = (request) => `${request.method ?? 'GET'} ${buildUrl(request.path, request.query).replace('%7Bevent_id%7D', '<event_id>')}`;
|
|
361
|
+
function printPlan(operation, plan, opts, all) {
|
|
362
|
+
const asJson = opts.json !== undefined || opts.jq !== undefined || opts.output === 'json' || opts.output === 'jsonl';
|
|
363
|
+
const steps = [
|
|
364
|
+
...(plan.lookup ? [{ step: 'resolve', request: describeRequest(plan.lookup.request), operation: 'listEvents', credits: plan.lookup.credits }] : []),
|
|
365
|
+
{
|
|
366
|
+
step: 'request',
|
|
367
|
+
request: describeRequest(plan.request),
|
|
368
|
+
operation: operation.operationId,
|
|
369
|
+
tier: operation.tier,
|
|
370
|
+
credits: operation.credits,
|
|
371
|
+
...(plan.request.body !== undefined ? { body: plan.request.body } : {}),
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
const perPage = operation.credits;
|
|
375
|
+
const lookupCredits = plan.lookup?.credits ?? 0;
|
|
376
|
+
const estimate = all
|
|
377
|
+
? `${lookupCredits ? `${lookupCredits} + ` : ''}${perPage} × pages (the page count is known only during the walk)`
|
|
378
|
+
: String(lookupCredits + perPage);
|
|
379
|
+
if (asJson) {
|
|
380
|
+
emit(JSON.stringify({ dry_run: true, steps, all, credits_estimate: estimate }, null, opts.output === 'jsonl' ? undefined : 2));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const lines = steps.map(step => `${step.step}: ${step.request}\n ${step.operation}${'tier' in step ? ` (${step.tier})` : ''} · ${step.credits} credit(s) per response${'body' in step ? `\n body: ${JSON.stringify(step.body)}` : ''}`);
|
|
384
|
+
lines.push(`credits: ${estimate}${perPage > 0 ? ' (+1 per started 1,000 rows beyond the first 1,000 of a response)' : ''}`);
|
|
385
|
+
lines.push('dry run: no request was sent');
|
|
386
|
+
emit(lines.join('\n'));
|
|
387
|
+
}
|
|
388
|
+
/** The yellow stderr line of a `meta.coverage` that is not complete (v2 readiness: a 200 with `state`). */
|
|
389
|
+
export function coverageNote(body) {
|
|
390
|
+
const coverage = pick(body, 'meta.coverage');
|
|
391
|
+
if (!isRecord(coverage) || typeof coverage.state !== 'string' || coverage.state === 'complete')
|
|
141
392
|
return undefined;
|
|
393
|
+
const through = typeof coverage.through === 'string' ? ` through ${coverage.through}` : '';
|
|
394
|
+
const retry = typeof coverage.retry_after_s === 'number' ? `; retry in ${coverage.retry_after_s}s` : '';
|
|
395
|
+
const meaning = coverage.state === 'pending' ? 'not available yet — no rows is not "nothing exists"' : 'the answer is incomplete';
|
|
396
|
+
return `coverage: ${coverage.state}${through} — ${meaning}${retry}`;
|
|
397
|
+
}
|
|
398
|
+
async function resolveLookup(plan) {
|
|
399
|
+
if (!plan.lookup)
|
|
400
|
+
return plan;
|
|
401
|
+
const { body } = await apiCall({ ...plan.lookup.request, silent: true });
|
|
402
|
+
const row = isRecord(body) && Array.isArray(body.data) ? body.data[0] : undefined;
|
|
403
|
+
const eventId = isRecord(row) && typeof row.event_id === 'string' ? row.event_id : undefined;
|
|
404
|
+
if (!eventId)
|
|
405
|
+
throw new Error(`No event for ${plan.lookup.company} ${plan.lookup.period} (GET /v2/events)`);
|
|
406
|
+
console.error(chalk.dim(`resolved ${plan.lookup.company} ${plan.lookup.period} → event ${eventId} (GET /v2/events, ${plan.lookup.credits} credit)`));
|
|
407
|
+
return { request: { ...plan.request, path: plan.request.path.replace('{event_id}', encodeURIComponent(eventId)) } };
|
|
408
|
+
}
|
|
409
|
+
async function saveDownload(operation, body, file, force) {
|
|
410
|
+
const target = operation.download === '' ? body : pick(body, operation.download ?? '');
|
|
411
|
+
const url = isRecord(target) ? target.url : undefined;
|
|
412
|
+
if (typeof url !== 'string' || url === '') {
|
|
413
|
+
throw new Error(`Nothing to save: ${operation.download ? `${operation.download}.url` : 'url'} is empty in this response`);
|
|
142
414
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
415
|
+
const record = target;
|
|
416
|
+
const saved = await saveVerified(signal => fetch(url, { signal }), file, {
|
|
417
|
+
force,
|
|
418
|
+
expected: {
|
|
419
|
+
...(typeof record.sha256 === 'string' ? { sha256: record.sha256 } : {}),
|
|
420
|
+
...(typeof record.bytes === 'number' ? { bytes: record.bytes } : {}),
|
|
421
|
+
...(typeof record.content_type === 'string' ? { contentTypes: [record.content_type.split(';')[0]?.trim().toLowerCase() ?? ''] } : {}),
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
console.error(chalk.green(`${saved.skipped ? 'Already saved' : 'Saved'} ${saved.path} (${saved.bytes} bytes, sha256 ${saved.sha256})`));
|
|
425
|
+
}
|
|
426
|
+
/** `--all`: every page streamed in the chosen format; a failure after written rows exits 3 with the resume cursor. */
|
|
427
|
+
async function streamAll(request, first, shape, settings) {
|
|
428
|
+
let written = 0;
|
|
429
|
+
let cursor;
|
|
430
|
+
let columns;
|
|
431
|
+
const collected = [];
|
|
146
432
|
try {
|
|
147
|
-
|
|
433
|
+
for await (const page of walkPages(request, first)) {
|
|
434
|
+
const note = coverageNote(page.body);
|
|
435
|
+
if (note)
|
|
436
|
+
console.error(chalk.yellow(note));
|
|
437
|
+
if (settings.jq) {
|
|
438
|
+
emit(runJq(settings.jq, page.body).join('\n'));
|
|
439
|
+
written += rowsOf(page.body).length || 1;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
const rows = bodyRows(shapeBody(page.body, shape, settings), shape);
|
|
443
|
+
if (settings.format === 'csv') {
|
|
444
|
+
columns ??= settings.select ?? settings.prune ?? settings.order ?? csvColumns(rows);
|
|
445
|
+
if (written === 0)
|
|
446
|
+
emit(csvHeader(columns));
|
|
447
|
+
emit(rows.map(row => csvRow(row, columns ?? [])).join('\n'));
|
|
448
|
+
}
|
|
449
|
+
else if (settings.format === 'table' || settings.format === 'plain') {
|
|
450
|
+
collected.push(...bodyRows(page.body, shape));
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
emit(rows.map(row => JSON.stringify(row)).join('\n'));
|
|
454
|
+
}
|
|
455
|
+
written += rows.length;
|
|
456
|
+
}
|
|
457
|
+
cursor = nextCursorOf(page.body);
|
|
458
|
+
}
|
|
148
459
|
}
|
|
149
460
|
catch (error) {
|
|
150
|
-
|
|
461
|
+
if (written > 0)
|
|
462
|
+
throw new PartialResultError(error, written, cursor);
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
if (settings.format === 'table' || settings.format === 'plain') {
|
|
466
|
+
emit(humanText({ data: collected }, shape, settings), { human: true });
|
|
151
467
|
}
|
|
152
468
|
}
|
|
153
|
-
function
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
formatTable(operation.columns ? flattenRows(rows, operation.columns) : rows.filter(isRecord), operation.columns);
|
|
163
|
-
printPaginationHint(nextUrlOf(operation, data));
|
|
469
|
+
async function runOperation(operation, positionals, opts, context) {
|
|
470
|
+
const fieldsParam = operation.params.some(param => param.name === 'fields');
|
|
471
|
+
const settings = resolveOutput(opts, { knownFields: operation.fields, fieldsParam, list: operation.list });
|
|
472
|
+
const all = opts.all === true;
|
|
473
|
+
if (opts.save !== undefined && all)
|
|
474
|
+
throw new UsageError('--save saves one response; it cannot be combined with --all');
|
|
475
|
+
let plan = await planRequest(operation, positionals, opts, context);
|
|
476
|
+
if (opts.dryRun) {
|
|
477
|
+
printPlan(operation, plan, opts, all);
|
|
164
478
|
return;
|
|
165
479
|
}
|
|
166
|
-
if (operation.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
480
|
+
if (operation.method === 'delete') {
|
|
481
|
+
await confirmOrRefuse(`${describeRequest(plan.request)} deletes this resource.`, opts);
|
|
482
|
+
}
|
|
483
|
+
const threshold = Number(getSetting('confirm_credits').value);
|
|
484
|
+
if (all && Number.isFinite(threshold) && operation.credits >= threshold && threshold > 0) {
|
|
485
|
+
await confirmOrRefuse(`invd ${commandName(operation)} --all costs ${operation.credits} credits per page and may walk many pages. Continue?`, opts);
|
|
486
|
+
}
|
|
487
|
+
plan = await resolveLookup(plan);
|
|
488
|
+
const shape = { list: operation.list, view: operation.view, ...(operation.columns ? { columns: operation.columns } : {}) };
|
|
489
|
+
const first = await apiCall(plan.request);
|
|
490
|
+
if (all) {
|
|
491
|
+
await streamAll(plan.request, first, shape, settings);
|
|
173
492
|
return;
|
|
174
493
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
494
|
+
const note = coverageNote(first.body);
|
|
495
|
+
if (note)
|
|
496
|
+
console.error(chalk.yellow(note));
|
|
497
|
+
if (first.body === undefined) {
|
|
498
|
+
if (settings.format === 'table' || settings.format === 'plain')
|
|
499
|
+
console.error(chalk.green('Done.'));
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (typeof opts.save === 'string')
|
|
503
|
+
await saveDownload(operation, first.body, opts.save, opts.force === true);
|
|
504
|
+
const human = !settings.jq && (settings.format === 'table' || settings.format === 'plain');
|
|
505
|
+
emit(renderBody(first.body, shape, settings), { human });
|
|
506
|
+
const cursor = operation.list ? nextCursorOf(first.body) : undefined;
|
|
507
|
+
if (cursor && human) {
|
|
508
|
+
console.error(chalk.dim(`More rows: --all for every page, or --cursor ${cursor} (same flags) for the next one.`));
|
|
184
509
|
}
|
|
185
|
-
command.action(withErrorHandling(async (_opts, invoked) => {
|
|
186
|
-
const globals = invoked.optsWithGlobals();
|
|
187
|
-
const json = globals.json === true;
|
|
188
|
-
if (json)
|
|
189
|
-
setJsonMode(true);
|
|
190
|
-
if (globals.all === true && operation.pagination === 'none') {
|
|
191
|
-
throw new Error(`invd ${[operation.group, operation.action].filter(Boolean).join(' ')} does not paginate; --all has nothing to fetch`);
|
|
192
|
-
}
|
|
193
|
-
const values = wireValues(bindings, invoked.opts());
|
|
194
|
-
const body = operation.body ? await requestBody(operation, invoked.opts()) : undefined;
|
|
195
|
-
const path = operation.path.replace(/\{([^}]+)\}/g, (_match, name) => {
|
|
196
|
-
const value = values.get(name);
|
|
197
|
-
return encodeURIComponent(typeof value === 'string' ? value : '');
|
|
198
|
-
});
|
|
199
|
-
const params = {};
|
|
200
|
-
for (const param of operation.params) {
|
|
201
|
-
if (param.in === 'query')
|
|
202
|
-
params[param.name] = values.get(param.name);
|
|
203
|
-
}
|
|
204
|
-
let data = operation.method === 'get'
|
|
205
|
-
? await apiGet({ path, params, auth: operation.auth })
|
|
206
|
-
: await apiRequest({
|
|
207
|
-
auth: operation.auth,
|
|
208
|
-
method: HTTP_METHOD[operation.method],
|
|
209
|
-
path,
|
|
210
|
-
params,
|
|
211
|
-
...(body !== undefined ? { body } : {}),
|
|
212
|
-
});
|
|
213
|
-
if (globals.all === true) {
|
|
214
|
-
const itemsPaths = operation.allItemsPath ?? [];
|
|
215
|
-
data = operation.pagination === 'top'
|
|
216
|
-
? await fetchAllTopPages(data, itemsPaths)
|
|
217
|
-
: await fetchAllCompanyPages(data, itemsPaths);
|
|
218
|
-
}
|
|
219
|
-
if (data === undefined) {
|
|
220
|
-
// A 204: nothing to show but that it worked.
|
|
221
|
-
if (!json)
|
|
222
|
-
console.log('Done.');
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
output(data, { json }, () => {
|
|
226
|
-
renderView(operation, data);
|
|
227
|
-
printReadinessNotes(data);
|
|
228
|
-
});
|
|
229
|
-
}, apiErrorHint));
|
|
230
510
|
}
|
|
231
511
|
//# sourceMappingURL=spec-commands.js.map
|