@invinite-org/invinite-data-cli 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -802
- package/claude-plugin/agents/invinite-data.md +1 -1
- package/claude-plugin/commands/analyze-earnings-call.md +3 -3
- package/claude-plugin/skills/invinite-data-cli/SKILL.md +444 -187
- package/dist/src/cli.d.ts +6 -0
- package/dist/src/cli.js +21 -31
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client/api-client.d.ts +2 -4
- package/dist/src/client/api-client.js +15 -15
- package/dist/src/client/api-client.js.map +1 -1
- package/dist/src/client/pagination.d.ts +23 -0
- package/dist/src/client/pagination.js +76 -7
- package/dist/src/client/pagination.js.map +1 -1
- package/dist/src/commands/earnings.d.ts +0 -3
- package/dist/src/commands/earnings.js +46 -524
- package/dist/src/commands/earnings.js.map +1 -1
- package/dist/src/generated/cli-manifest.json +4983 -0
- package/dist/src/hand-written-commands.d.ts +43 -0
- package/dist/src/hand-written-commands.js +21 -0
- package/dist/src/hand-written-commands.js.map +1 -0
- package/dist/src/shared/errors.d.ts +12 -1
- package/dist/src/shared/errors.js +27 -1
- package/dist/src/shared/errors.js.map +1 -1
- package/dist/src/shared/options.d.ts +0 -13
- package/dist/src/shared/options.js +0 -13
- package/dist/src/shared/options.js.map +1 -1
- package/dist/src/spec-commands.d.ts +10 -0
- package/dist/src/spec-commands.js +224 -0
- package/dist/src/spec-commands.js.map +1 -0
- package/dist/src/spec-flags.d.ts +6 -0
- package/dist/src/spec-flags.js +9 -0
- package/dist/src/spec-flags.js.map +1 -0
- package/dist/src/spec-manifest.d.ts +382 -0
- package/dist/src/spec-manifest.js +65 -0
- package/dist/src/spec-manifest.js.map +1 -0
- package/package.json +7 -3
- package/dist/src/commands/company.d.ts +0 -2
- package/dist/src/commands/company.js +0 -220
- package/dist/src/commands/company.js.map +0 -1
- package/dist/src/commands/earnings-selector.d.ts +0 -15
- package/dist/src/commands/earnings-selector.js +0 -29
- package/dist/src/commands/earnings-selector.js.map +0 -1
- package/dist/src/commands/filings.d.ts +0 -2
- package/dist/src/commands/filings.js +0 -139
- package/dist/src/commands/filings.js.map +0 -1
- package/dist/src/commands/financials.d.ts +0 -2
- package/dist/src/commands/financials.js +0 -162
- package/dist/src/commands/financials.js.map +0 -1
- package/dist/src/commands/health.d.ts +0 -2
- package/dist/src/commands/health.js +0 -18
- package/dist/src/commands/health.js.map +0 -1
- package/dist/src/commands/insider-trades.d.ts +0 -2
- package/dist/src/commands/insider-trades.js +0 -56
- package/dist/src/commands/insider-trades.js.map +0 -1
- package/dist/src/commands/ipos.d.ts +0 -2
- package/dist/src/commands/ipos.js +0 -42
- package/dist/src/commands/ipos.js.map +0 -1
- package/dist/src/commands/market.d.ts +0 -2
- package/dist/src/commands/market.js +0 -34
- package/dist/src/commands/market.js.map +0 -1
- package/dist/src/commands/metadata.d.ts +0 -2
- package/dist/src/commands/metadata.js +0 -39
- package/dist/src/commands/metadata.js.map +0 -1
- package/dist/src/commands/metrics.d.ts +0 -2
- package/dist/src/commands/metrics.js +0 -158
- package/dist/src/commands/metrics.js.map +0 -1
- package/dist/src/commands/ownership.d.ts +0 -2
- package/dist/src/commands/ownership.js +0 -152
- package/dist/src/commands/ownership.js.map +0 -1
- package/dist/src/commands/segments.d.ts +0 -2
- package/dist/src/commands/segments.js +0 -64
- package/dist/src/commands/segments.js.map +0 -1
- package/dist/src/commands/usage.d.ts +0 -38
- package/dist/src/commands/usage.js +0 -82
- package/dist/src/commands/usage.js.map +0 -1
- package/dist/src/commands/webhooks.d.ts +0 -16
- package/dist/src/commands/webhooks.js +0 -186
- package/dist/src/commands/webhooks.js.map +0 -1
- package/dist/src/formatters/sparkline.d.ts +0 -2
- package/dist/src/formatters/sparkline.js +0 -11
- package/dist/src/formatters/sparkline.js.map +0 -1
- package/dist/src/formatters/tree-formatter.d.ts +0 -11
- package/dist/src/formatters/tree-formatter.js +0 -30
- package/dist/src/formatters/tree-formatter.js.map +0 -1
|
@@ -4,38 +4,51 @@ import chalk from 'chalk';
|
|
|
4
4
|
import { apiGet } from '../client/api-client.js';
|
|
5
5
|
import { downloadToFile, ensureDir, extensionFor, writeTextFile } from '../client/download.js';
|
|
6
6
|
import { fetchAllPages } from '../client/pagination.js';
|
|
7
|
-
import { withErrorHandling } from '../shared/errors.js';
|
|
7
|
+
import { apiErrorHint, withErrorHandling } from '../shared/errors.js';
|
|
8
8
|
import { output } from '../shared/output.js';
|
|
9
|
-
import { splitList } from '../shared/options.js';
|
|
10
9
|
import { setJsonMode } from '../shared/spinner.js';
|
|
11
10
|
import { formatJson } from '../formatters/json-formatter.js';
|
|
12
11
|
import { formatKeyValue, formatTable, printPaginationHint } from '../formatters/table-formatter.js';
|
|
13
|
-
import { flattenRows } from '../formatters/flatten.js';
|
|
14
12
|
import { formatMs } from '../formatters/time.js';
|
|
15
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The hand-written `invd earnings` commands: each writes a file or renders text, so none is one REST call plus one
|
|
15
|
+
* view (`src/hand-written-commands.ts` rosters them). Every other `earnings` action is generated from the manifest
|
|
16
|
+
* and attached to the same group (`src/spec-commands.ts`).
|
|
17
|
+
*/
|
|
16
18
|
const BASE = '/v1/earnings-calls';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
/** IDA's `event_type` values (the spec's enum on the selector operations). */
|
|
20
|
+
const EVENT_TYPES = ['earnings_call', 'investor_day', 'conference', 'agm', 'other'];
|
|
21
|
+
/**
|
|
22
|
+
* The event selector, as the generated commands spell it (the spec's query parameters): `--event-id`, or `-i` with
|
|
23
|
+
* `--year` + `--quarter` or `--date`. An incomplete selector is the API's own 400.
|
|
24
|
+
*/
|
|
25
|
+
function selectorOptions(cmd) {
|
|
26
|
+
return cmd
|
|
27
|
+
.option('--event-id <event_id>', 'Event ID (wins over the other selectors)')
|
|
28
|
+
.option('-i, --identifier <ticker>', 'Ticker or CIK (with --year + --quarter, or --date)')
|
|
29
|
+
.option('--year <n>', 'Fiscal year of the call (with --quarter)')
|
|
30
|
+
.option('--quarter <n>', 'Fiscal quarter of the call (with --year)')
|
|
31
|
+
.option('--date <date>', 'Day of the call (YYYY-MM-DD): a calendar day in New York time (America/New_York); a call without a confirmed time is listed on its announced date')
|
|
32
|
+
.addOption(new Option('--event-type <type>', 'Event type of the selected call').choices(EVENT_TYPES));
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
function selectorParams(opts) {
|
|
35
|
+
return {
|
|
36
|
+
event_id: opts.eventId,
|
|
37
|
+
identifier: opts.identifier,
|
|
38
|
+
year: opts.year,
|
|
39
|
+
quarter: opts.quarter,
|
|
40
|
+
date: opts.date,
|
|
41
|
+
event_type: opts.eventType,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const run = (fn) => withErrorHandling(fn, apiErrorHint);
|
|
45
|
+
/** `--all`: follows `next_url` and concatenates the `documents` pages. */
|
|
46
|
+
async function allDocuments(data, all) {
|
|
47
|
+
if (!all)
|
|
35
48
|
return data;
|
|
36
|
-
return fetchAllPages(data,
|
|
49
|
+
return fetchAllPages(data, page => page.next_url, (accumulated, page) => ({
|
|
37
50
|
...page,
|
|
38
|
-
|
|
51
|
+
documents: [...accumulated.documents, ...page.documents],
|
|
39
52
|
}));
|
|
40
53
|
}
|
|
41
54
|
function eventLine(event) {
|
|
@@ -51,10 +64,6 @@ function eventLine(event) {
|
|
|
51
64
|
function printSaved(saved) {
|
|
52
65
|
console.log(chalk.green(`Saved ${saved.path} (${saved.bytes.toLocaleString('en-US')} bytes)`));
|
|
53
66
|
}
|
|
54
|
-
/** `*_ms` numbers → `m:ss` for key / value output. */
|
|
55
|
-
function humanizeMs(row) {
|
|
56
|
-
return Object.fromEntries(Object.entries(row).map(([key, value]) => [key, key.endsWith('_ms') && typeof value === 'number' ? formatMs(value) : value]));
|
|
57
|
-
}
|
|
58
67
|
function speakerName(turn) {
|
|
59
68
|
return turn.name ?? `Speaker ${turn.speaker_label}`;
|
|
60
69
|
}
|
|
@@ -101,15 +110,8 @@ export function pagesText(pagesJson) {
|
|
|
101
110
|
})
|
|
102
111
|
.join('\n\n');
|
|
103
112
|
}
|
|
104
|
-
/** A table cell of at most `SEARCH_TEXT_MAX` characters. */
|
|
105
|
-
function clip(value) {
|
|
106
|
-
const text = String(value ?? '');
|
|
107
|
-
return text.length > SEARCH_TEXT_MAX ? `${text.slice(0, SEARCH_TEXT_MAX - 1)}…` : text;
|
|
108
|
-
}
|
|
109
113
|
export function registerEarningsCommands(program) {
|
|
110
|
-
const earnings = program
|
|
111
|
-
.command('earnings')
|
|
112
|
-
.description('Earnings calls: calendar, transcripts, audio, slides, guidance, Q&A, search');
|
|
114
|
+
const earnings = program.commands.find(command => command.name() === 'earnings') ?? program.command('earnings');
|
|
113
115
|
const globalOpts = () => {
|
|
114
116
|
const opts = program.opts();
|
|
115
117
|
if (opts.json)
|
|
@@ -117,104 +119,6 @@ export function registerEarningsCommands(program) {
|
|
|
117
119
|
return opts;
|
|
118
120
|
};
|
|
119
121
|
const selectorCommand = (name, description) => selectorOptions(earnings.command(name).description(description));
|
|
120
|
-
// ── Calendar / events ──────────────────────────────────────────────
|
|
121
|
-
earnings
|
|
122
|
-
.command('calendar')
|
|
123
|
-
.description('Upcoming and recent earnings calls across companies')
|
|
124
|
-
.option('--from <date>', 'First New York day (YYYY-MM-DD, default today)')
|
|
125
|
-
.option('--to <date>', 'Last New York day (YYYY-MM-DD, default from + 14 days, at most 92)')
|
|
126
|
-
.option('--status <list>', 'Date statuses, comma-separated (estimated, unconfirmed, confirmed, postponed, cancelled)')
|
|
127
|
-
.addOption(new Option('--session <session>', 'Session').choices(['bmo', 'amc', 'intraday']))
|
|
128
|
-
.option('--exchange <exchange>', 'Exchange')
|
|
129
|
-
.addOption(new Option('--event-type <type>', 'Event type (default earnings_call; all = every type)').choices(EVENT_TYPE_FILTERS))
|
|
130
|
-
.addOption(new Option('-s, --sort <dir>', 'Sort direction').choices(['asc', 'desc']))
|
|
131
|
-
.option('-l, --limit <n>', 'Max results (1-500)')
|
|
132
|
-
.option('--offset <n>', 'Offset')
|
|
133
|
-
.action(run(async (cmdOpts) => {
|
|
134
|
-
const opts = globalOpts();
|
|
135
|
-
const data = await paginate(await apiGet({
|
|
136
|
-
path: `${BASE}/calendar`,
|
|
137
|
-
params: {
|
|
138
|
-
from: cmdOpts.from,
|
|
139
|
-
to: cmdOpts.to,
|
|
140
|
-
// IDA's `status` is an array parameter: one `status=` per value, never one comma string.
|
|
141
|
-
status: splitList(cmdOpts.status),
|
|
142
|
-
session: cmdOpts.session,
|
|
143
|
-
exchange: cmdOpts.exchange,
|
|
144
|
-
event_type: cmdOpts.eventType,
|
|
145
|
-
sort: cmdOpts.sort,
|
|
146
|
-
limit: cmdOpts.limit,
|
|
147
|
-
offset: cmdOpts.offset,
|
|
148
|
-
},
|
|
149
|
-
}), 'events', opts.all);
|
|
150
|
-
output(data, opts, () => {
|
|
151
|
-
console.log(chalk.bold(`\nEarnings calendar ${data.from} … ${data.to}\n`));
|
|
152
|
-
formatTable(data.events, EVENT_COLUMNS);
|
|
153
|
-
printPaginationHint(data.next_url);
|
|
154
|
-
});
|
|
155
|
-
}));
|
|
156
|
-
earnings
|
|
157
|
-
.command('events')
|
|
158
|
-
.description("A company's earnings calls")
|
|
159
|
-
.requiredOption('-i, --identifier <ticker>', 'Ticker or CIK')
|
|
160
|
-
.option('--from <date>', 'Calls on or after this New York day (YYYY-MM-DD)')
|
|
161
|
-
.option('--to <date>', 'Calls on or before this New York day (YYYY-MM-DD)')
|
|
162
|
-
.addOption(new Option('--event-type <type>', 'Event type (default all)').choices(EVENT_TYPE_FILTERS))
|
|
163
|
-
.option('--observations', 'Include the date / time observations per call')
|
|
164
|
-
.addOption(new Option('-s, --sort <dir>', 'Sort direction').choices(['asc', 'desc']))
|
|
165
|
-
.option('-l, --limit <n>', 'Max results (1-200)')
|
|
166
|
-
.option('--offset <n>', 'Offset')
|
|
167
|
-
.action(run(async (cmdOpts) => {
|
|
168
|
-
const opts = globalOpts();
|
|
169
|
-
const data = await paginate(await apiGet({
|
|
170
|
-
path: `${BASE}/events`,
|
|
171
|
-
params: {
|
|
172
|
-
identifier: cmdOpts.identifier,
|
|
173
|
-
from: cmdOpts.from,
|
|
174
|
-
to: cmdOpts.to,
|
|
175
|
-
event_type: cmdOpts.eventType,
|
|
176
|
-
include: cmdOpts.observations ? 'observations' : undefined,
|
|
177
|
-
sort: cmdOpts.sort,
|
|
178
|
-
limit: cmdOpts.limit,
|
|
179
|
-
offset: cmdOpts.offset,
|
|
180
|
-
},
|
|
181
|
-
}), 'events', opts.all);
|
|
182
|
-
output(data, opts, () => {
|
|
183
|
-
console.log(chalk.bold(`\n${data.symbol} (${data.cik}) Earnings Calls\n`));
|
|
184
|
-
if (!data.covered)
|
|
185
|
-
console.log(chalk.yellow('Not covered by the earnings-calls pipeline.\n'));
|
|
186
|
-
formatTable(data.events, EVENT_COLUMNS);
|
|
187
|
-
printPaginationHint(data.next_url);
|
|
188
|
-
});
|
|
189
|
-
}));
|
|
190
|
-
earnings
|
|
191
|
-
.command('live')
|
|
192
|
-
.description('Calls being captured right now')
|
|
193
|
-
.action(run(async () => {
|
|
194
|
-
const opts = globalOpts();
|
|
195
|
-
const data = await apiGet({ path: `${BASE}/live` });
|
|
196
|
-
output(data, opts, () => {
|
|
197
|
-
console.log(chalk.bold(`\nLive earnings calls (as of ${data.as_of})\n`));
|
|
198
|
-
formatTable(data.events, [...EVENT_COLUMNS, 'live_audio']);
|
|
199
|
-
});
|
|
200
|
-
}));
|
|
201
|
-
earnings
|
|
202
|
-
.command('event')
|
|
203
|
-
.description('One earnings call with its date / time observations')
|
|
204
|
-
.argument('<event_id>', 'Event ID')
|
|
205
|
-
.action(run(async (eventId) => {
|
|
206
|
-
const opts = globalOpts();
|
|
207
|
-
const data = await apiGet({ path: `${BASE}/event/${encodeURIComponent(eventId)}` });
|
|
208
|
-
output(data, opts, () => {
|
|
209
|
-
const { observations, ...event } = data;
|
|
210
|
-
console.log(chalk.bold(`\n${eventLine(event)}\n`));
|
|
211
|
-
formatKeyValue(event);
|
|
212
|
-
if (observations?.length) {
|
|
213
|
-
console.log(chalk.bold('\nObservations\n'));
|
|
214
|
-
formatTable(observations, ['observed_source', 'observed_at', 'starts_at', 'tz', 'status_hint', 'announcement_kind']);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}));
|
|
218
122
|
// ── Transcript ─────────────────────────────────────────────────────
|
|
219
123
|
selectorCommand('transcript', 'Transcript of a call')
|
|
220
124
|
.addOption(new Option('--format <format>', 'Transcript format').choices(['speakers', 'text', 'words', 'sections', 'cleaned']))
|
|
@@ -225,7 +129,6 @@ export function registerEarningsCommands(program) {
|
|
|
225
129
|
.option('--force', 'Overwrite an existing --output file')
|
|
226
130
|
.action(run(async (cmdOpts) => {
|
|
227
131
|
const opts = globalOpts();
|
|
228
|
-
validateSelector(cmdOpts);
|
|
229
132
|
const data = await apiGet({
|
|
230
133
|
path: `${BASE}/transcript`,
|
|
231
134
|
params: { ...selectorParams(cmdOpts), format: cmdOpts.format, version: cmdOpts.transcriptVersion, as_of: cmdOpts.asOf },
|
|
@@ -249,141 +152,12 @@ export function registerEarningsCommands(program) {
|
|
|
249
152
|
console.log(text);
|
|
250
153
|
});
|
|
251
154
|
}));
|
|
252
|
-
selectorCommand('versions', 'Published transcript versions of a call')
|
|
253
|
-
.action(run(async (cmdOpts) => {
|
|
254
|
-
const opts = globalOpts();
|
|
255
|
-
validateSelector(cmdOpts);
|
|
256
|
-
const data = await apiGet({
|
|
257
|
-
path: `${BASE}/transcript/versions`,
|
|
258
|
-
params: selectorParams(cmdOpts),
|
|
259
|
-
});
|
|
260
|
-
output(data, opts, () => {
|
|
261
|
-
console.log(chalk.bold(`\nTranscript versions (current: ${data.current_version})\n`));
|
|
262
|
-
formatTable(data.versions, ['version', 'version_kind', 'published_at', 'word_count', 'change_note', 'corrected_by']);
|
|
263
|
-
});
|
|
264
|
-
}));
|
|
265
|
-
selectorCommand('transcript-diff', 'Word diff between two transcript versions')
|
|
266
|
-
.requiredOption('--from <n>', 'Older version')
|
|
267
|
-
.option('--to <n>', 'Newer version (default: the current one)')
|
|
268
|
-
.action(run(async (cmdOpts) => {
|
|
269
|
-
const opts = globalOpts();
|
|
270
|
-
validateSelector(cmdOpts);
|
|
271
|
-
const data = await apiGet({
|
|
272
|
-
path: `${BASE}/transcript/diff`,
|
|
273
|
-
params: { ...selectorParams(cmdOpts), from: cmdOpts.from, to: cmdOpts.to },
|
|
274
|
-
});
|
|
275
|
-
output(data, opts, () => {
|
|
276
|
-
console.log(chalk.bold(`\nTranscript diff v${data.from} → v${data.to}\n`));
|
|
277
|
-
formatTable(data.changes.map(c => ({ ...c, at_ms: formatMs(c.at_ms) })), ['op', 'at_ms', 'text']);
|
|
278
|
-
console.log(`\n+${data.stats.added_words} / -${data.stats.removed_words} words${data.truncated ? chalk.yellow(' (truncated)') : ''}`);
|
|
279
|
-
});
|
|
280
|
-
}));
|
|
281
|
-
selectorCommand('qa', 'Q&A pairs of a call')
|
|
282
|
-
.option('-l, --limit <n>', 'Max pairs (1-200)')
|
|
283
|
-
.option('--offset <n>', 'Offset')
|
|
284
|
-
.action(run(async (cmdOpts) => {
|
|
285
|
-
const opts = globalOpts();
|
|
286
|
-
validateSelector(cmdOpts);
|
|
287
|
-
const data = await paginate(await apiGet({
|
|
288
|
-
path: `${BASE}/qa`,
|
|
289
|
-
params: { ...selectorParams(cmdOpts), limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
290
|
-
}), 'pairs', opts.all);
|
|
291
|
-
output(data, opts, () => {
|
|
292
|
-
if (data.pairs.length === 0)
|
|
293
|
-
console.log(chalk.yellow('No results.'));
|
|
294
|
-
for (const pair of data.pairs) {
|
|
295
|
-
const analyst = pair.analyst ?? {};
|
|
296
|
-
const firm = analyst.firm ? ` (${analyst.firm})` : '';
|
|
297
|
-
const followUp = pair.is_follow_up ? chalk.dim(' follow-up') : '';
|
|
298
|
-
console.log(chalk.bold(`\n#${pair.seq} ${speakerName(analyst)}${firm}`) + followUp + chalk.dim(` [${formatMs(pair.question_start_ms)}]`));
|
|
299
|
-
console.log(` Q: ${pair.question}`);
|
|
300
|
-
const answeredBy = (pair.answers ?? []).map(speakerName).join(', ');
|
|
301
|
-
console.log(` A${answeredBy ? ` (${answeredBy})` : ''}: ${pair.answer}`);
|
|
302
|
-
}
|
|
303
|
-
printPaginationHint(data.next_url);
|
|
304
|
-
});
|
|
305
|
-
}));
|
|
306
|
-
selectorCommand('participants', 'Speakers of a call')
|
|
307
|
-
.option('--evidence', 'Include the quotes each name was resolved from')
|
|
308
|
-
.action(run(async (cmdOpts) => {
|
|
309
|
-
const opts = globalOpts();
|
|
310
|
-
validateSelector(cmdOpts);
|
|
311
|
-
const data = await apiGet({
|
|
312
|
-
path: `${BASE}/participants`,
|
|
313
|
-
params: { ...selectorParams(cmdOpts), include: cmdOpts.evidence ? 'evidence' : undefined },
|
|
314
|
-
});
|
|
315
|
-
output(data, opts, () => {
|
|
316
|
-
console.log(chalk.bold('\nParticipants\n'));
|
|
317
|
-
formatTable(data.participants, ['speaker_label', 'name', 'title', 'firm', 'role', 'confidence', 'resolved_by']);
|
|
318
|
-
if (cmdOpts.evidence) {
|
|
319
|
-
for (const p of data.participants.filter(p => p.evidence?.length)) {
|
|
320
|
-
console.log(chalk.bold(`\n${speakerName(p)}`));
|
|
321
|
-
for (const e of p.evidence)
|
|
322
|
-
console.log(` [${formatMs(e.start_ms)}] "${e.quote}"`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (data.participation_events?.length) {
|
|
326
|
-
console.log(chalk.bold('\nParticipation flags\n'));
|
|
327
|
-
formatTable(data.participation_events, ['kind', 'person_id', 'event_id']);
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}));
|
|
331
|
-
// ── People ─────────────────────────────────────────────────────────
|
|
332
|
-
earnings
|
|
333
|
-
.command('analysts')
|
|
334
|
-
.description("Analysts on a company's calls")
|
|
335
|
-
.requiredOption('-i, --identifier <ticker>', 'Ticker or CIK')
|
|
336
|
-
.option('-l, --limit <n>', 'Max results (1-100)')
|
|
337
|
-
.option('--offset <n>', 'Offset')
|
|
338
|
-
.action(run(async (cmdOpts) => {
|
|
339
|
-
const opts = globalOpts();
|
|
340
|
-
const data = await paginate(await apiGet({
|
|
341
|
-
path: `${BASE}/analysts`,
|
|
342
|
-
params: { identifier: cmdOpts.identifier, limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
343
|
-
}), 'analysts', opts.all);
|
|
344
|
-
output(data, opts, () => {
|
|
345
|
-
console.log(chalk.bold(`\n${data.symbol} (${data.cik}) Analysts\n`));
|
|
346
|
-
formatTable(data.analysts, ['name', 'firm', 'appearances', 'questions', 'first_seen', 'last_seen', 'person_id']);
|
|
347
|
-
printPaginationHint(data.next_url);
|
|
348
|
-
});
|
|
349
|
-
}));
|
|
350
|
-
earnings
|
|
351
|
-
.command('person')
|
|
352
|
-
.description("One person's call appearances")
|
|
353
|
-
.argument('<person_id>', 'Person ID')
|
|
354
|
-
.option('-l, --limit <n>', 'Max appearances (1-200)')
|
|
355
|
-
.option('--offset <n>', 'Offset')
|
|
356
|
-
.action(run(async (personId, cmdOpts) => {
|
|
357
|
-
const opts = globalOpts();
|
|
358
|
-
const data = await paginate(await apiGet({
|
|
359
|
-
path: `${BASE}/people/${encodeURIComponent(personId)}`,
|
|
360
|
-
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
361
|
-
}), 'appearances', opts.all);
|
|
362
|
-
output(data, opts, () => {
|
|
363
|
-
console.log(chalk.bold(`\n${data.name ?? data.person_id}\n`));
|
|
364
|
-
formatKeyValue({
|
|
365
|
-
kind: data.kind,
|
|
366
|
-
current_firm: data.current_firm,
|
|
367
|
-
current_title: data.current_title,
|
|
368
|
-
appearance_count: data.appearance_count,
|
|
369
|
-
aliases: data.aliases,
|
|
370
|
-
});
|
|
371
|
-
console.log(chalk.bold('\nAppearances\n'));
|
|
372
|
-
formatTable(data.appearances, [
|
|
373
|
-
'scheduled_at', 'symbol', 'company_name', 'fiscal_year', 'fiscal_quarter',
|
|
374
|
-
'role', 'title', 'firm', 'word_count', 'question_count', 'event_id',
|
|
375
|
-
]);
|
|
376
|
-
printPaginationHint(data.next_url);
|
|
377
|
-
});
|
|
378
|
-
}));
|
|
379
|
-
// ── Press release / extractions / guidance ─────────────────────────
|
|
380
155
|
selectorCommand('press-release', 'Results press release of a call')
|
|
381
156
|
.option('--body', 'Include the release text')
|
|
382
157
|
.option('-o, --output <file>', 'Save the release text to a file')
|
|
383
158
|
.option('--force', 'Overwrite an existing --output file')
|
|
384
159
|
.action(run(async (cmdOpts) => {
|
|
385
160
|
const opts = globalOpts();
|
|
386
|
-
validateSelector(cmdOpts);
|
|
387
161
|
const data = await apiGet({
|
|
388
162
|
path: `${BASE}/press-release`,
|
|
389
163
|
params: { ...selectorParams(cmdOpts), include: cmdOpts.body || cmdOpts.output ? 'body' : undefined },
|
|
@@ -413,173 +187,12 @@ export function registerEarningsCommands(program) {
|
|
|
413
187
|
console.log(`\n${data.body_text}`);
|
|
414
188
|
});
|
|
415
189
|
}));
|
|
416
|
-
selectorCommand('extractions', 'Guidance or KPI extractions of a call')
|
|
417
|
-
.addOption(new Option('--kind <kind>', 'guidance (default) or kpi').choices(['guidance', 'kpi']))
|
|
418
|
-
.addOption(new Option('--review <filter>', 'Guidance review filter').choices(['approved', 'auto', 'all']))
|
|
419
|
-
.option('-l, --limit <n>', 'Max items (1-500)')
|
|
420
|
-
.option('--offset <n>', 'Offset')
|
|
421
|
-
.action(run(async (cmdOpts) => {
|
|
422
|
-
const opts = globalOpts();
|
|
423
|
-
validateSelector(cmdOpts);
|
|
424
|
-
const first = await apiGet({
|
|
425
|
-
path: `${BASE}/extractions`,
|
|
426
|
-
params: {
|
|
427
|
-
...selectorParams(cmdOpts),
|
|
428
|
-
kind: cmdOpts.kind,
|
|
429
|
-
review: cmdOpts.review,
|
|
430
|
-
limit: cmdOpts.limit,
|
|
431
|
-
offset: cmdOpts.offset,
|
|
432
|
-
},
|
|
433
|
-
});
|
|
434
|
-
const data = await paginate(first, first.kind === 'kpi' ? 'kpis' : 'guidance', opts.all);
|
|
435
|
-
output(data, opts, () => {
|
|
436
|
-
console.log(chalk.bold(`\n${eventLine(data.event)} · ${data.kind === 'kpi' ? 'KPIs' : 'Guidance'}\n`));
|
|
437
|
-
if (data.kind === 'kpi') {
|
|
438
|
-
const columns = ['metric_normalized', 'value_text', 'period_label', 'speaker.name', 'origin', 'filing_link.form', 'filing_link.matched_value'];
|
|
439
|
-
formatTable(flattenRows(data.kpis ?? [], columns), columns);
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
formatTable(data.guidance ?? [], [
|
|
443
|
-
'metric_normalized', 'period_label', 'low', 'high', 'midpoint', 'unit',
|
|
444
|
-
'basis', 'direction', 'confidence', 'review_status',
|
|
445
|
-
]);
|
|
446
|
-
}
|
|
447
|
-
printPaginationHint(data.next_url);
|
|
448
|
-
});
|
|
449
|
-
}));
|
|
450
|
-
earnings
|
|
451
|
-
.command('guidance-history')
|
|
452
|
-
.description("A company's guidance for one metric across calls")
|
|
453
|
-
.requiredOption('-i, --identifier <ticker>', 'Ticker or CIK')
|
|
454
|
-
.requiredOption('--metric <metric>', 'Normalized metric (e.g. revenue, eps_non_gaap, segment:iphone)')
|
|
455
|
-
.addOption(new Option('--period-kind <kind>', 'Period kind').choices(['quarter', 'fiscal_year', 'half', 'other']))
|
|
456
|
-
.addOption(new Option('--basis <basis>', 'Basis').choices(['gaap', 'non_gaap', 'unknown']))
|
|
457
|
-
.option('-l, --limit <n>', 'Max series entries (1-40)')
|
|
458
|
-
.action(run(async (cmdOpts) => {
|
|
459
|
-
const opts = globalOpts();
|
|
460
|
-
const data = await apiGet({
|
|
461
|
-
path: `${BASE}/guidance/history`,
|
|
462
|
-
params: {
|
|
463
|
-
identifier: cmdOpts.identifier,
|
|
464
|
-
metric: cmdOpts.metric,
|
|
465
|
-
period_kind: cmdOpts.periodKind,
|
|
466
|
-
basis: cmdOpts.basis,
|
|
467
|
-
limit: cmdOpts.limit,
|
|
468
|
-
},
|
|
469
|
-
});
|
|
470
|
-
output(data, opts, () => {
|
|
471
|
-
console.log(chalk.bold(`\n${data.symbol} (${data.cik}) ${data.metric_normalized} guidance history\n`));
|
|
472
|
-
const columns = ['conference_date', 'period_label', 'low', 'high', 'midpoint', 'direction', 'change_vs_prior.midpoint_delta'];
|
|
473
|
-
formatTable(flattenRows(data.series, columns), columns);
|
|
474
|
-
});
|
|
475
|
-
}));
|
|
476
|
-
// ── Derived ────────────────────────────────────────────────────────
|
|
477
|
-
selectorCommand('diff', 'What changed against the prior call')
|
|
478
|
-
.addOption(new Option('--vs <base>', 'Comparison base').choices(['prior']))
|
|
479
|
-
.action(run(async (cmdOpts) => {
|
|
480
|
-
const opts = globalOpts();
|
|
481
|
-
validateSelector(cmdOpts);
|
|
482
|
-
const data = await apiGet({
|
|
483
|
-
path: `${BASE}/diff`,
|
|
484
|
-
params: { ...selectorParams(cmdOpts), vs: cmdOpts.vs },
|
|
485
|
-
});
|
|
486
|
-
output(data, opts, () => {
|
|
487
|
-
console.log(chalk.bold(`\n${eventLine(data.event)} vs ${data.prior_event?.conference_date ?? 'prior call'}\n`));
|
|
488
|
-
formatKeyValue({
|
|
489
|
-
prior_event_id: data.prior_event?.event_id,
|
|
490
|
-
similarity: data.similarity,
|
|
491
|
-
tone_shift: data.tone_shift,
|
|
492
|
-
hedging_level: data.hedging?.level,
|
|
493
|
-
hedging_change: data.hedging?.change,
|
|
494
|
-
uncertainty_level: data.uncertainty?.level,
|
|
495
|
-
uncertainty_change: data.uncertainty?.change,
|
|
496
|
-
numeric_specificity_level: data.numeric_specificity?.level,
|
|
497
|
-
numeric_specificity_change: data.numeric_specificity?.change,
|
|
498
|
-
slide_page_delta: data.slide_diff?.page_delta,
|
|
499
|
-
slide_new_titles: data.slide_diff?.new_titles,
|
|
500
|
-
slide_dropped_titles: data.slide_diff?.dropped_titles,
|
|
501
|
-
});
|
|
502
|
-
for (const [label, topics] of [['New topics', data.new_topics], ['Dropped topics', data.dropped_topics]]) {
|
|
503
|
-
if (!topics?.length)
|
|
504
|
-
continue;
|
|
505
|
-
console.log(chalk.bold(`\n${label}\n`));
|
|
506
|
-
formatTable(topics.map((t) => ({ ...t, start_ms: formatMs(t.start_ms) })), ['topic', 'start_ms', 'evidence']);
|
|
507
|
-
}
|
|
508
|
-
if (data.summary)
|
|
509
|
-
console.log(`\n${data.summary}`);
|
|
510
|
-
});
|
|
511
|
-
}));
|
|
512
|
-
selectorCommand('metrics', 'Call structure metrics (length, Q&A share, questions, …)')
|
|
513
|
-
.action(run(async (cmdOpts) => {
|
|
514
|
-
const opts = globalOpts();
|
|
515
|
-
validateSelector(cmdOpts);
|
|
516
|
-
const data = await apiGet({ path: `${BASE}/metrics`, params: selectorParams(cmdOpts) });
|
|
517
|
-
output(data, opts, () => {
|
|
518
|
-
const { event, ...metrics } = data;
|
|
519
|
-
console.log(chalk.bold(`\n${eventLine(event)} · metrics\n`));
|
|
520
|
-
formatKeyValue(humanizeMs(metrics));
|
|
521
|
-
});
|
|
522
|
-
}));
|
|
523
|
-
selectorCommand('prosody', 'Speech timing / pitch / energy per speaker')
|
|
524
|
-
.addOption(new Option('--section <section>', 'Section').choices(['all', 'prepared', 'qa']))
|
|
525
|
-
.action(run(async (cmdOpts) => {
|
|
526
|
-
const opts = globalOpts();
|
|
527
|
-
validateSelector(cmdOpts);
|
|
528
|
-
const data = await apiGet({
|
|
529
|
-
path: `${BASE}/prosody`,
|
|
530
|
-
params: { ...selectorParams(cmdOpts), section: cmdOpts.section },
|
|
531
|
-
});
|
|
532
|
-
output(data, opts, () => {
|
|
533
|
-
console.log(chalk.bold(`\n${eventLine(data.event)} · prosody (${data.section})\n`));
|
|
534
|
-
const columns = [
|
|
535
|
-
'speaker.name', 'speaker.role', 'section', 'speech_rate_wpm', 'pause_count', 'pause_mean_ms',
|
|
536
|
-
'filler_rate', 'time_to_answer_mean_ms', 'pitch_mean_hz', 'energy_mean_db', 'spoken_ms',
|
|
537
|
-
'word_count', 'acoustic_available',
|
|
538
|
-
];
|
|
539
|
-
formatTable(flattenRows(data.speakers, columns).map(row => ({ ...row, spoken_ms: formatMs(row.spoken_ms) })), columns);
|
|
540
|
-
});
|
|
541
|
-
}));
|
|
542
|
-
selectorCommand('price-attribution', 'Minute-level price moves during a call next to what was said (coincidence, not cause)')
|
|
543
|
-
.action(run(async (cmdOpts) => {
|
|
544
|
-
const opts = globalOpts();
|
|
545
|
-
validateSelector(cmdOpts);
|
|
546
|
-
const data = await apiGet({ path: `${BASE}/price-attribution`, params: selectorParams(cmdOpts) });
|
|
547
|
-
output(data, opts, () => {
|
|
548
|
-
const { event, buckets, top_minutes: topMinutes, live, settled, ...summary } = data;
|
|
549
|
-
console.log(chalk.bold(`\n${eventLine(event)} · price moves during the call\n`));
|
|
550
|
-
console.log(chalk.dim('Temporal coincidence, not attribution of cause; pre-market and after-hours bars are sparse.\n'));
|
|
551
|
-
if (live === true) {
|
|
552
|
-
console.log(chalk.yellow('LIVE — minute rows so far, not settled; the post-call run replaces them\n'));
|
|
553
|
-
}
|
|
554
|
-
else if (settled === false) {
|
|
555
|
-
console.log(chalk.yellow('Not settled — read before the bars settled; a later run may replace them\n'));
|
|
556
|
-
}
|
|
557
|
-
// While live the summary fields are null: print only what is known (bar_count = rows so far).
|
|
558
|
-
formatKeyValue(humanizeMs(Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== null && value !== undefined))));
|
|
559
|
-
const columns = ['bucket', 'return_bps', 'cumulative_return_bps', 'said'];
|
|
560
|
-
const said = (row) => {
|
|
561
|
-
const text = (row.utterance_refs ?? [])
|
|
562
|
-
.map((ref) => `[${formatMs(ref.start_ms)}] ${ref.text}`)
|
|
563
|
-
.join(' ');
|
|
564
|
-
return text.length > SEARCH_TEXT_MAX ? `${text.slice(0, SEARCH_TEXT_MAX - 1)}…` : text;
|
|
565
|
-
};
|
|
566
|
-
if (topMinutes?.length) {
|
|
567
|
-
console.log(chalk.bold('\nTop minutes\n'));
|
|
568
|
-
formatTable(topMinutes.map((row) => ({ ...row, said: said(row) })), ['bucket', 'return_bps', 'said']);
|
|
569
|
-
}
|
|
570
|
-
if (buckets?.length) {
|
|
571
|
-
console.log(chalk.bold('\nMinutes\n'));
|
|
572
|
-
formatTable(buckets.map((row) => ({ ...row, said: said(row) })), columns);
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
}));
|
|
576
190
|
// ── Media ──────────────────────────────────────────────────────────
|
|
577
191
|
selectorCommand('audio', 'Call recording (signed URL, or --output to save it)')
|
|
578
192
|
.option('-o, --output <file>', 'Save the recording (e.g. call.mp3)')
|
|
579
193
|
.option('--force', 'Overwrite an existing --output file')
|
|
580
194
|
.action(run(async (cmdOpts) => {
|
|
581
195
|
const opts = globalOpts();
|
|
582
|
-
validateSelector(cmdOpts);
|
|
583
196
|
const data = await apiGet({ path: `${BASE}/audio`, params: selectorParams(cmdOpts) });
|
|
584
197
|
const saved = cmdOpts.output
|
|
585
198
|
? await downloadToFile(data.url, cmdOpts.output, { force: cmdOpts.force, spinnerText: 'Downloading audio...' })
|
|
@@ -604,7 +217,6 @@ export function registerEarningsCommands(program) {
|
|
|
604
217
|
.option('--force', 'Overwrite an existing --output file')
|
|
605
218
|
.action(run(async (cmdOpts) => {
|
|
606
219
|
const opts = globalOpts();
|
|
607
|
-
validateSelector(cmdOpts);
|
|
608
220
|
const data = await apiGet({
|
|
609
221
|
path: `${BASE}/slides`,
|
|
610
222
|
params: { ...selectorParams(cmdOpts), include: cmdOpts.markdown ? 'markdown' : undefined, pages: cmdOpts.pages },
|
|
@@ -647,8 +259,7 @@ export function registerEarningsCommands(program) {
|
|
|
647
259
|
.option('--force', 'Overwrite existing files in --output')
|
|
648
260
|
.action(run(async (cmdOpts) => {
|
|
649
261
|
const opts = globalOpts();
|
|
650
|
-
|
|
651
|
-
const data = await paginate(await apiGet({
|
|
262
|
+
const data = await allDocuments(await apiGet({
|
|
652
263
|
path: `${BASE}/documents`,
|
|
653
264
|
params: {
|
|
654
265
|
...selectorParams(cmdOpts),
|
|
@@ -657,7 +268,7 @@ export function registerEarningsCommands(program) {
|
|
|
657
268
|
limit: cmdOpts.limit,
|
|
658
269
|
offset: cmdOpts.offset,
|
|
659
270
|
},
|
|
660
|
-
}),
|
|
271
|
+
}), opts.all);
|
|
661
272
|
const saved = [];
|
|
662
273
|
const skipped = [];
|
|
663
274
|
if (cmdOpts.output) {
|
|
@@ -704,47 +315,6 @@ export function registerEarningsCommands(program) {
|
|
|
704
315
|
}
|
|
705
316
|
}
|
|
706
317
|
}));
|
|
707
|
-
// ── Search / live stream ───────────────────────────────────────────
|
|
708
|
-
earnings
|
|
709
|
-
.command('search')
|
|
710
|
-
.description('Semantic search over call transcripts and slides')
|
|
711
|
-
.requiredOption('-q, --query <text>', 'Search text')
|
|
712
|
-
.option('--event <event_id>', 'Only this call')
|
|
713
|
-
.option('-i, --identifier <ticker>', 'Only this company')
|
|
714
|
-
.option('--from <date>', 'First New York call day (YYYY-MM-DD)')
|
|
715
|
-
.option('--to <date>', 'Last New York call day (YYYY-MM-DD)')
|
|
716
|
-
.addOption(new Option('--type <type>', 'Passage type').choices(['transcript', 'qa', 'preparedRemarks', 'presentation']))
|
|
717
|
-
.addOption(new Option('--event-type <type>', 'Event type (default earnings_call; all = every type)').choices(EVENT_TYPE_FILTERS))
|
|
718
|
-
.option('-l, --limit <n>', 'Max passages (1-8)')
|
|
719
|
-
.action(run(async (cmdOpts) => {
|
|
720
|
-
const opts = globalOpts();
|
|
721
|
-
const data = await apiGet({
|
|
722
|
-
path: `${BASE}/search`,
|
|
723
|
-
params: {
|
|
724
|
-
q: cmdOpts.query,
|
|
725
|
-
event_id: cmdOpts.event,
|
|
726
|
-
identifier: cmdOpts.identifier,
|
|
727
|
-
from: cmdOpts.from,
|
|
728
|
-
to: cmdOpts.to,
|
|
729
|
-
type: cmdOpts.type,
|
|
730
|
-
event_type: cmdOpts.eventType,
|
|
731
|
-
limit: cmdOpts.limit,
|
|
732
|
-
},
|
|
733
|
-
});
|
|
734
|
-
output(data, opts, () => {
|
|
735
|
-
console.log(chalk.bold(`\nSearch: ${data.query}\n`));
|
|
736
|
-
const columns = ['score', 'type', 'symbol', 'conference_date', 'speaker.name', 'start_ms', 'text', 'audio'];
|
|
737
|
-
formatTable(flattenRows(data.hits, columns).map((row, i) => {
|
|
738
|
-
const text = String(row.text ?? '');
|
|
739
|
-
return {
|
|
740
|
-
...row,
|
|
741
|
-
start_ms: formatMs(row.start_ms),
|
|
742
|
-
text: text.length > SEARCH_TEXT_MAX ? `${text.slice(0, SEARCH_TEXT_MAX - 1)}…` : text,
|
|
743
|
-
audio: Boolean(data.hits[i].audio_url),
|
|
744
|
-
};
|
|
745
|
-
}), columns);
|
|
746
|
-
});
|
|
747
|
-
}));
|
|
748
318
|
// ── Company documents (IR library) ─────────────────────────────────
|
|
749
319
|
earnings
|
|
750
320
|
.command('company-documents')
|
|
@@ -754,14 +324,14 @@ export function registerEarningsCommands(program) {
|
|
|
754
324
|
.option('--kind <list>', 'Kinds, comma-separated (annual_report, proxy_statement, esg_report, investor_day_deck, conference_presentation, shareholder_letter, fact_sheet, webcast, …)')
|
|
755
325
|
.option('--year <n>', 'Year the document belongs to')
|
|
756
326
|
.option('--period <period>', 'Period within the year (Q1-Q4, H1, H2, FY)')
|
|
757
|
-
.option('--event <event_id>', 'Only the documents of this event (investor day, conference, AGM)')
|
|
327
|
+
.option('--event-id <event_id>', 'Only the documents of this event (investor day, conference, AGM)')
|
|
758
328
|
.option('-l, --limit <n>', 'Max results (1-100)')
|
|
759
329
|
.option('--offset <n>', 'Offset')
|
|
760
330
|
.option('-o, --output <dir>', 'Save every stored file into this directory as <year>-<kind>-<document_id>.<ext>; a link-only row (webcast) is reported on stderr, exit 1 only when none could be saved')
|
|
761
331
|
.option('--force', 'Overwrite existing files in --output')
|
|
762
332
|
.action(run(async (cmdOpts) => {
|
|
763
333
|
const opts = globalOpts();
|
|
764
|
-
const data = await
|
|
334
|
+
const data = await allDocuments(await apiGet({
|
|
765
335
|
path: `${BASE}/company-documents`,
|
|
766
336
|
params: {
|
|
767
337
|
identifier: cmdOpts.identifier,
|
|
@@ -769,11 +339,11 @@ export function registerEarningsCommands(program) {
|
|
|
769
339
|
kind: cmdOpts.kind,
|
|
770
340
|
year: cmdOpts.year,
|
|
771
341
|
period: cmdOpts.period,
|
|
772
|
-
event_id: cmdOpts.
|
|
342
|
+
event_id: cmdOpts.eventId,
|
|
773
343
|
limit: cmdOpts.limit,
|
|
774
344
|
offset: cmdOpts.offset,
|
|
775
345
|
},
|
|
776
|
-
}),
|
|
346
|
+
}), opts.all);
|
|
777
347
|
const saved = [];
|
|
778
348
|
const skipped = [];
|
|
779
349
|
if (cmdOpts.output) {
|
|
@@ -813,14 +383,14 @@ export function registerEarningsCommands(program) {
|
|
|
813
383
|
earnings
|
|
814
384
|
.command('document')
|
|
815
385
|
.description('One company IR document (metadata and signed URLs, or --pages for its text)')
|
|
816
|
-
.
|
|
386
|
+
.requiredOption('--document-id <document_id>', 'Document ID (from company-documents or document-search)')
|
|
817
387
|
.option('--pages', 'Print the per-page text')
|
|
818
388
|
.option('-o, --output <file>', 'Save the file (or, with --pages, the text)')
|
|
819
389
|
.option('--force', 'Overwrite an existing --output file')
|
|
820
|
-
.action(run(async (
|
|
390
|
+
.action(run(async (cmdOpts) => {
|
|
821
391
|
const opts = globalOpts();
|
|
822
392
|
const data = await apiGet({
|
|
823
|
-
path: `${BASE}/company-documents/${encodeURIComponent(documentId)}`,
|
|
393
|
+
path: `${BASE}/company-documents/${encodeURIComponent(cmdOpts.documentId)}`,
|
|
824
394
|
params: { include: cmdOpts.pages ? 'pages' : undefined },
|
|
825
395
|
});
|
|
826
396
|
const doc = data.document;
|
|
@@ -862,53 +432,5 @@ export function registerEarningsCommands(program) {
|
|
|
862
432
|
console.log(`\n${text}`);
|
|
863
433
|
});
|
|
864
434
|
}));
|
|
865
|
-
earnings
|
|
866
|
-
.command('document-search')
|
|
867
|
-
.description('Semantic search over company documents (call documents and IR libraries): passages with page numbers')
|
|
868
|
-
.requiredOption('-q, --query <text>', 'Search text')
|
|
869
|
-
.option('-i, --identifier <ticker>', 'Only this company')
|
|
870
|
-
.option('--kind <list>', 'Kinds, comma-separated (annual_report, proxy_statement, cfo_commentary, …)')
|
|
871
|
-
.option('--section <list>', 'IR sections, comma-separated (company documents only)')
|
|
872
|
-
.option('--year <n>', 'Only documents of this year')
|
|
873
|
-
.addOption(new Option('--table <table>', 'Only call documents or only IR libraries').choices(['event_documents', 'company_documents']))
|
|
874
|
-
.option('-l, --limit <n>', 'Max passages (1-20)')
|
|
875
|
-
.action(run(async (cmdOpts) => {
|
|
876
|
-
const opts = globalOpts();
|
|
877
|
-
const data = await apiGet({
|
|
878
|
-
path: `${BASE}/documents/search`,
|
|
879
|
-
params: {
|
|
880
|
-
q: cmdOpts.query,
|
|
881
|
-
identifier: cmdOpts.identifier,
|
|
882
|
-
kind: cmdOpts.kind,
|
|
883
|
-
section: cmdOpts.section,
|
|
884
|
-
year: cmdOpts.year,
|
|
885
|
-
table: cmdOpts.table,
|
|
886
|
-
limit: cmdOpts.limit,
|
|
887
|
-
},
|
|
888
|
-
});
|
|
889
|
-
output(data, opts, () => {
|
|
890
|
-
console.log(chalk.bold(`\nDocument search: ${data.query}\n`));
|
|
891
|
-
console.log(chalk.dim('Passages from company-published documents, not verified facts.\n'));
|
|
892
|
-
formatTable(data.hits.map(hit => ({ ...hit, title: clip(hit.title), text: clip(hit.text) })), ['score', 'kind', 'year', 'page', 'symbol', 'title', 'text', 'document_id']);
|
|
893
|
-
});
|
|
894
|
-
}));
|
|
895
|
-
selectorCommand('stream', 'Live audio stream URL of a call in progress (HLS)')
|
|
896
|
-
.action(run(async (cmdOpts) => {
|
|
897
|
-
const opts = globalOpts();
|
|
898
|
-
validateSelector(cmdOpts);
|
|
899
|
-
const data = await apiGet({ path: `${BASE}/live/stream`, params: selectorParams(cmdOpts) });
|
|
900
|
-
output(data, opts, () => {
|
|
901
|
-
console.log(chalk.bold(`\n${eventLine(data.event)} · live stream\n`));
|
|
902
|
-
formatKeyValue({
|
|
903
|
-
status: data.status,
|
|
904
|
-
url: data.url,
|
|
905
|
-
expires_at: data.expires_at,
|
|
906
|
-
call_t0: data.call_t0,
|
|
907
|
-
segment_seconds: data.segment_seconds,
|
|
908
|
-
});
|
|
909
|
-
if (data.url)
|
|
910
|
-
console.log(chalk.dim(`\nPlay with: ffplay "${data.url}" or vlc "${data.url}"`));
|
|
911
|
-
});
|
|
912
|
-
}));
|
|
913
435
|
}
|
|
914
436
|
//# sourceMappingURL=earnings.js.map
|