@invinite-org/invinite-data-cli 0.1.21 → 0.2.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 +314 -6
- package/claude-plugin/.claude-plugin/plugin.json +13 -5
- package/claude-plugin/.mcp.json +8 -0
- package/claude-plugin/agents/invinite-data.md +6 -2
- package/claude-plugin/commands/analyze-earnings-call.md +24 -0
- package/claude-plugin/commands/analyze-filing.md +15 -0
- package/claude-plugin/commands/compare-companies.md +15 -0
- package/claude-plugin/commands/financial-research.md +2 -1
- package/claude-plugin/commands/ownership-review.md +14 -0
- package/claude-plugin/skills/invinite-data-cli/SKILL.md +119 -8
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +22 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client/api-client.d.ts +13 -1
- package/dist/src/client/api-client.js +46 -13
- package/dist/src/client/api-client.js.map +1 -1
- package/dist/src/client/download.d.ts +23 -0
- package/dist/src/client/download.js +116 -0
- package/dist/src/client/download.js.map +1 -0
- package/dist/src/client/earnings-live.d.ts +79 -0
- package/dist/src/client/earnings-live.js +156 -0
- package/dist/src/client/earnings-live.js.map +1 -0
- package/dist/src/client/pagination.js +2 -1
- package/dist/src/client/pagination.js.map +1 -1
- package/dist/src/client/websocket-client.d.ts +12 -1
- package/dist/src/client/websocket-client.js +114 -37
- package/dist/src/client/websocket-client.js.map +1 -1
- package/dist/src/commands/earnings-selector.d.ts +15 -0
- package/dist/src/commands/earnings-selector.js +29 -0
- package/dist/src/commands/earnings-selector.js.map +1 -0
- package/dist/src/commands/earnings.d.ts +13 -0
- package/dist/src/commands/earnings.js +705 -0
- package/dist/src/commands/earnings.js.map +1 -0
- package/dist/src/commands/plugin.d.ts +11 -0
- package/dist/src/commands/plugin.js +23 -8
- package/dist/src/commands/plugin.js.map +1 -1
- package/dist/src/commands/webhooks.d.ts +6 -0
- package/dist/src/commands/webhooks.js +144 -0
- package/dist/src/commands/webhooks.js.map +1 -0
- package/dist/src/commands/ws.js +23 -3
- package/dist/src/commands/ws.js.map +1 -1
- package/dist/src/formatters/flatten.d.ts +4 -0
- package/dist/src/formatters/flatten.js +15 -0
- package/dist/src/formatters/flatten.js.map +1 -0
- package/dist/src/formatters/time.d.ts +2 -0
- package/dist/src/formatters/time.js +14 -0
- package/dist/src/formatters/time.js.map +1 -0
- package/dist/src/shared/errors.d.ts +2 -1
- package/dist/src/shared/errors.js +5 -1
- package/dist/src/shared/errors.js.map +1 -1
- package/dist/src/shared/options.d.ts +2 -0
- package/dist/src/shared/options.js +6 -0
- package/dist/src/shared/options.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: invinite-data
|
|
3
|
-
description: Fetch financial data
|
|
2
|
+
name: invinite-financial-data
|
|
3
|
+
description: Fetch financial data through the Invinite Data API MCP connector or invd CLI. Use whenever the user wants company information, financial statements, SEC filings, institutional ownership, insider trades, IPO data, market holidays, financial metrics, earnings calls, or other stock/company data, including requests mentioning tickers, CIKs, balance sheets, income statements, cash flows, 10-K, 10-Q, ratios, CAGR, growth rates, dividends, stock splits, institutional holdings, call transcripts, earnings guidance, KPIs, Q&A, analysts, live calls, slides, or press releases.
|
|
4
4
|
allowed-tools: Bash(invd *)
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Invinite Data API
|
|
7
|
+
# Invinite Data API
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Use the connected Invinite Data API MCP tools when available. Fall back to the `invd` CLI when MCP is unavailable. Both transports access the same team-billed Data API.
|
|
10
|
+
|
|
11
|
+
## Transport selection
|
|
12
|
+
|
|
13
|
+
1. Prefer MCP tools when an Invinite Data connector is available. Call the direct domain tool matching the request; unlike the Web API MCP server, the Data API MCP server does not use meta-tools such as `call_endpoint`.
|
|
14
|
+
2. Use the CLI examples below when MCP is unavailable or when the user explicitly requests CLI usage. Always pass `--json` for data used in analysis.
|
|
15
|
+
3. Do not mix transports merely to repeat a successful request.
|
|
16
|
+
|
|
17
|
+
### MCP and CLI mapping
|
|
18
|
+
|
|
19
|
+
| Data need | MCP tool | CLI command family |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| Search/details/list | `company_search`, `company_details`, `available_companies` | `invd company ...` |
|
|
22
|
+
| Dividends/periods/splits | `company_dividends`, `company_fiscal_periods`, `company_splits` | `invd company ...` |
|
|
23
|
+
| Standardized statements/snapshots | `standardized_financial_statement`, `standardized_financial_snapshot` | `invd financials ...` |
|
|
24
|
+
| As-reported statements/snapshots | `as_reported_financial_statement`, `as_reported_financial_snapshot` | `invd financials ... --as-reported` |
|
|
25
|
+
| Ratios/growth/CAGR | `financial_ratios`, `financial_growth`, `financial_cagr` | `invd metrics ...` |
|
|
26
|
+
| Segment data | `segmented_financials` | `invd segments ...` |
|
|
27
|
+
| SEC filings/search/sections | `sec_filings`, `sec_filings_search`, `sec_filing_sections` | `invd filings ...` |
|
|
28
|
+
| Filing and metric discovery | `sec_available_form_types`, `list_available_section_ids`, `list_available_metrics` | `invd filings form-types`, `invd metadata ...` |
|
|
29
|
+
| Institutional ownership | `holdings_by_company`, `holdings_by_investor`, `institutional_investors`, `institutional_transactions` | `invd ownership ...` |
|
|
30
|
+
| Insider trades/IPOs/holidays | `insider_trades`, `ipos`, `market_holidays` | `invd insider-trades`, `invd ipos`, `invd market ...` |
|
|
31
|
+
| Earnings call calendar/events/live | `earnings_call_calendar`, `earnings_call_events`, `earnings_call_live` | `invd earnings calendar\|events\|live` |
|
|
32
|
+
| Call transcript/Q&A/participants | `earnings_call_transcript`, `earnings_call_qa`, `earnings_call_participants` | `invd earnings transcript\|qa\|participants` |
|
|
33
|
+
| Call guidance/KPIs, diff vs prior call | `earnings_call_guidance`, `earnings_call_diff` | `invd earnings extractions\|guidance-history\|diff` |
|
|
34
|
+
| Call documents/press release | `earnings_call_documents`, `earnings_call_press_release` | `invd earnings documents\|press-release` |
|
|
35
|
+
| Search across calls | `earnings_call_search` | `invd earnings search` |
|
|
36
|
+
|
|
37
|
+
The discovery rules below apply to both transports: call `list_available_metrics` before ratios, growth, CAGR, or financial-statement analysis, and call `list_available_section_ids` before requesting filing sections.
|
|
10
38
|
|
|
11
39
|
## Prerequisites
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
### MCP
|
|
42
|
+
|
|
43
|
+
Connect `https://data.invinite.com/t/<teamId>/mcp` through OAuth. The team in the URL controls data billing. Leave OAuth client ID and secret empty so the client uses Dynamic Client Registration and PKCE.
|
|
44
|
+
|
|
45
|
+
### CLI
|
|
46
|
+
|
|
47
|
+
Install the CLI with `npm install -g @invinite-org/invinite-data-cli`, then run `invd login` or `invd login --team <teamId>`. Authentication is resolved in this order:
|
|
14
48
|
1. `INVINITE_DATA_API_KEY` environment variable
|
|
15
|
-
2.
|
|
16
|
-
3.
|
|
49
|
+
2. `.env` file
|
|
50
|
+
3. OS keychain (via optional `keytar`)
|
|
51
|
+
4. Stored config
|
|
17
52
|
|
|
18
|
-
If
|
|
53
|
+
If an MCP call fails with an auth error, reconnect the connector and complete OAuth. If a CLI call fails with an auth error, run `invd login`; manual `invd config set-key` and `INVINITE_DATA_API_KEY` remain supported fallbacks.
|
|
19
54
|
|
|
20
55
|
## Global Options
|
|
21
56
|
|
|
@@ -197,6 +232,71 @@ invd metadata metrics --json
|
|
|
197
232
|
invd metadata section-ids --json
|
|
198
233
|
```
|
|
199
234
|
|
|
235
|
+
### Earnings Calls
|
|
236
|
+
|
|
237
|
+
Commands about one call take a selector: `--event <event_id>` (from `events` / `calendar`), or `-i <ticker>` with `--year` + `--quarter` (fiscal) or `--date YYYY-MM-DD` (New York call day).
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Calendar across companies, a company's calls, calls being captured right now
|
|
241
|
+
# --event-type: earnings_call (calendar default), investor_day, conference, agm, other, all (events default)
|
|
242
|
+
invd earnings calendar --from 2026-10-20 --to 2026-10-31 --json
|
|
243
|
+
invd earnings calendar --event-type all --json # investor days, conferences, AGMs too
|
|
244
|
+
invd earnings events -i AAPL -s desc -l 4 --json
|
|
245
|
+
invd earnings live --json
|
|
246
|
+
invd earnings event <event_id> --json
|
|
247
|
+
|
|
248
|
+
# Transcript (formats: speakers, text, words, sections, cleaned), versions, word diff between versions
|
|
249
|
+
# qa_start_ms = where the Q&A begins (ms into the call, null without one)
|
|
250
|
+
invd earnings transcript -i AAPL --year 2026 --quarter 4 --format speakers --json
|
|
251
|
+
invd earnings versions --event <event_id> --json
|
|
252
|
+
invd earnings transcript-diff --event <event_id> --from 1 --json
|
|
253
|
+
|
|
254
|
+
# Q&A pairs, speakers, analysts of a company, one person's appearances
|
|
255
|
+
invd earnings qa --event <event_id> --json
|
|
256
|
+
invd earnings participants --event <event_id> --json
|
|
257
|
+
invd earnings analysts -i AAPL --json
|
|
258
|
+
invd earnings person <person_id> --json
|
|
259
|
+
|
|
260
|
+
# Press release, guidance / KPI extractions, a metric's guidance across calls
|
|
261
|
+
invd earnings press-release --event <event_id> --body --json
|
|
262
|
+
invd earnings extractions --event <event_id> --kind guidance --json
|
|
263
|
+
invd earnings extractions --event <event_id> --kind kpi --json
|
|
264
|
+
invd earnings guidance-history -i AAPL --metric revenue --json
|
|
265
|
+
|
|
266
|
+
# Derived: diff vs the prior call, call metrics, prosody
|
|
267
|
+
invd earnings diff --event <event_id> --json
|
|
268
|
+
invd earnings metrics --event <event_id> --json
|
|
269
|
+
invd earnings prosody --event <event_id> --section qa --json
|
|
270
|
+
|
|
271
|
+
# Media (signed URLs expire after 15 min; --output saves the file)
|
|
272
|
+
invd earnings audio --event <event_id> --output call.mp3
|
|
273
|
+
invd earnings slides --event <event_id> --markdown --json
|
|
274
|
+
invd earnings documents --event <event_id> --kind cfo_commentary --json
|
|
275
|
+
invd earnings documents --event <event_id> --output ./docs # a row without url (link-only sec_filing) is warned on stderr; use its source_url
|
|
276
|
+
invd earnings stream --event <event_id> --json # live HLS URL of a call in progress (PRO)
|
|
277
|
+
|
|
278
|
+
# Semantic search over transcripts and slides
|
|
279
|
+
invd earnings search -q "data center demand" -i NVDA --json
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- `--transcript-version N` (not `--version`) selects a published transcript version; `--as-of <iso>` the version current at that instant.
|
|
283
|
+
- `extractions --review approved|auto|all` filters guidance by review status; `diff --vs prior` is the only comparison base.
|
|
284
|
+
- Every `*_ms` field is milliseconds from the call's start: cite it as `[m:ss]`.
|
|
285
|
+
- The live transcript (`ws listen --earnings`), audio, slides, the live stream and webhooks need the PRO plan (calendar, events and `live` need LITE, the other reads STANDARD); a `403` names the plan a route needs.
|
|
286
|
+
|
|
287
|
+
### Live transcripts and webhooks
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Follow a live call over the WebSocket (event ID or CIK; NDJSON with --json)
|
|
291
|
+
invd ws listen --earnings <event_id> --json
|
|
292
|
+
invd ws listen --earnings 320193 --names
|
|
293
|
+
|
|
294
|
+
# Earnings-call webhook subscriptions (the secret is printed once)
|
|
295
|
+
invd webhooks list --json
|
|
296
|
+
invd webhooks create --url https://hooks.example.com/invinite --events earnings_call.transcript_ready --ciks 320193
|
|
297
|
+
invd webhooks deliveries <id> --json
|
|
298
|
+
```
|
|
299
|
+
|
|
200
300
|
### Health Check
|
|
201
301
|
|
|
202
302
|
```bash
|
|
@@ -234,6 +334,15 @@ invd ownership holdings-by-company -i AAPL --min-value 10000000 --json
|
|
|
234
334
|
invd filings search -r "artificial intelligence strategy" -i MSFT --json
|
|
235
335
|
```
|
|
236
336
|
|
|
337
|
+
### Reading an earnings call
|
|
338
|
+
Find the call, then read it from the cheapest source to the most detailed:
|
|
339
|
+
```bash
|
|
340
|
+
invd earnings events -i NVDA -s desc -l 4 --json # pick the event_id
|
|
341
|
+
invd earnings transcript --event <event_id> --format sections --json
|
|
342
|
+
invd earnings extractions --event <event_id> --kind guidance --json
|
|
343
|
+
invd earnings search -q "gross margin outlook" --event <event_id> --json
|
|
344
|
+
```
|
|
345
|
+
|
|
237
346
|
### Reading specific filing sections
|
|
238
347
|
First find section IDs, then fetch the section:
|
|
239
348
|
```bash
|
|
@@ -249,3 +358,5 @@ invd filings sections -i AAPL --form-type 10-K --section-id risk_factors --json
|
|
|
249
358
|
- For financial statements, `annual` gives the full year, `quarterly` gives each quarter, `ttm` gives trailing twelve months, `ytd` gives year-to-date
|
|
250
359
|
- The `snapshot` command is the most efficient way to get complete financials for one or more companies at once
|
|
251
360
|
- Filing sections return full text — great for detailed analysis of risk factors, MD&A, business descriptions, etc.
|
|
361
|
+
- Following a live call: `invd earnings live --json` lists the calls in progress; `invd ws listen --earnings <event_id>` streams the transcript (PRO) and exits once the final transcript is ready
|
|
362
|
+
- Earnings-call identifiers: `event_id` (UUID) from `invd earnings events` / `calendar` is the most precise selector; ticker + `--year` + `--quarter` uses the company's fiscal quarter
|
package/dist/src/cli.d.ts
CHANGED
package/dist/src/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import { Command } from 'commander';
|
|
2
3
|
import { setJsonMode } from './shared/spinner.js';
|
|
3
4
|
import { registerConfigCommands } from './commands/config.js';
|
|
@@ -10,17 +11,35 @@ import { registerIposCommands } from './commands/ipos.js';
|
|
|
10
11
|
import { registerOwnershipCommands } from './commands/ownership.js';
|
|
11
12
|
import { registerInsiderTradesCommands } from './commands/insider-trades.js';
|
|
12
13
|
import { registerFilingsCommands } from './commands/filings.js';
|
|
14
|
+
import { registerEarningsCommands } from './commands/earnings.js';
|
|
13
15
|
import { registerMarketCommands } from './commands/market.js';
|
|
14
16
|
import { registerMetadataCommands } from './commands/metadata.js';
|
|
17
|
+
import { registerWebhooksCommands } from './commands/webhooks.js';
|
|
15
18
|
import { registerWsCommands } from './commands/ws.js';
|
|
16
19
|
import { registerPluginCommands } from './commands/plugin.js';
|
|
17
20
|
import { registerLoginCommands } from './commands/login.js';
|
|
21
|
+
const PACKAGE_NAME = '@invinite-org/invinite-data-cli';
|
|
22
|
+
/** `package.json` sits one level up under tsx (`src/cli.ts`), two in the build (`dist/src/cli.js`). */
|
|
23
|
+
export function packageVersion() {
|
|
24
|
+
const requireJson = createRequire(import.meta.url);
|
|
25
|
+
for (const candidate of ['../package.json', '../../package.json']) {
|
|
26
|
+
try {
|
|
27
|
+
const pkg = requireJson(candidate);
|
|
28
|
+
if (pkg.name === PACKAGE_NAME && pkg.version)
|
|
29
|
+
return pkg.version;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// not at this level
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return '0.0.0';
|
|
36
|
+
}
|
|
18
37
|
export function createProgram() {
|
|
19
38
|
const program = new Command();
|
|
20
39
|
program
|
|
21
40
|
.name('invd')
|
|
22
41
|
.description('CLI for the Invinite financial data API')
|
|
23
|
-
.version(
|
|
42
|
+
.version(packageVersion())
|
|
24
43
|
.option('--json', 'Output raw JSON (pipe-friendly)')
|
|
25
44
|
.option('--all', 'Auto-paginate through all results')
|
|
26
45
|
.hook('preAction', (thisCommand) => {
|
|
@@ -39,8 +58,10 @@ export function createProgram() {
|
|
|
39
58
|
registerOwnershipCommands(program);
|
|
40
59
|
registerInsiderTradesCommands(program);
|
|
41
60
|
registerFilingsCommands(program);
|
|
61
|
+
registerEarningsCommands(program);
|
|
42
62
|
registerMarketCommands(program);
|
|
43
63
|
registerMetadataCommands(program);
|
|
64
|
+
registerWebhooksCommands(program);
|
|
44
65
|
registerWsCommands(program);
|
|
45
66
|
registerPluginCommands(program);
|
|
46
67
|
registerLoginCommands(program);
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CAAC,yCAAyC,CAAC;SACtD,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
export interface RequestOptions {
|
|
2
2
|
path: string;
|
|
3
|
-
|
|
3
|
+
/** An array value is sent as a repeated parameter (`status=a&status=b`). */
|
|
4
|
+
params?: Record<string, string | string[] | undefined>;
|
|
4
5
|
silent?: boolean;
|
|
5
6
|
}
|
|
7
|
+
/** Same auth / URL building as `apiGet`, without the JSON parse; a non-2xx throws `ApiError`. */
|
|
8
|
+
export declare function apiGetRaw(opts: RequestOptions & {
|
|
9
|
+
accept?: string;
|
|
10
|
+
}): Promise<Response>;
|
|
6
11
|
export declare function apiGet<T = unknown>(opts: RequestOptions): Promise<T>;
|
|
12
|
+
export interface ApiRequestOptions extends RequestOptions {
|
|
13
|
+
method: 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
14
|
+
/** Sent as JSON. */
|
|
15
|
+
body?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** A write with a JSON body; same auth / `ApiError` handling as `apiGet`. A 204 / empty body → `undefined`. */
|
|
18
|
+
export declare function apiRequest<T = unknown>(opts: ApiRequestOptions): Promise<T>;
|
|
@@ -7,28 +7,47 @@ function buildUrl(path, params) {
|
|
|
7
7
|
const url = new URL(`${base}${path}`);
|
|
8
8
|
if (params) {
|
|
9
9
|
for (const [key, value] of Object.entries(params)) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
11
|
+
if (item !== undefined && item !== '') {
|
|
12
|
+
url.searchParams.append(key, item);
|
|
13
|
+
}
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
return url.toString();
|
|
16
18
|
}
|
|
19
|
+
async function apiErrorFrom(response) {
|
|
20
|
+
const body = await response.json().catch(() => null);
|
|
21
|
+
return new ApiError(response.status, body?.error || response.statusText || 'Unknown Error', body?.message || `Request failed with status ${response.status}`);
|
|
22
|
+
}
|
|
23
|
+
async function request(opts, accept, init = {}) {
|
|
24
|
+
const apiKey = await getApiKey();
|
|
25
|
+
const url = buildUrl(opts.path, opts.params);
|
|
26
|
+
const headers = {
|
|
27
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
28
|
+
'Accept': accept,
|
|
29
|
+
};
|
|
30
|
+
if (init.body !== undefined)
|
|
31
|
+
headers['Content-Type'] = 'application/json';
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
headers,
|
|
34
|
+
...(init.method ? { method: init.method } : {}),
|
|
35
|
+
...(init.body !== undefined ? { body: JSON.stringify(init.body) } : {}),
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw await apiErrorFrom(response);
|
|
39
|
+
}
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
/** Same auth / URL building as `apiGet`, without the JSON parse; a non-2xx throws `ApiError`. */
|
|
43
|
+
export async function apiGetRaw(opts) {
|
|
44
|
+
return request(opts, opts.accept ?? '*/*');
|
|
45
|
+
}
|
|
17
46
|
export async function apiGet(opts) {
|
|
18
47
|
const spinner = opts.silent ? null : createSpinner('Fetching data...');
|
|
19
48
|
try {
|
|
20
|
-
const
|
|
21
|
-
const url = buildUrl(opts.path, opts.params);
|
|
22
|
-
const response = await fetch(url, {
|
|
23
|
-
headers: {
|
|
24
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
25
|
-
'Accept': 'application/json',
|
|
26
|
-
},
|
|
27
|
-
});
|
|
49
|
+
const response = await request(opts, 'application/json');
|
|
28
50
|
const body = await response.json();
|
|
29
|
-
if (!response.ok) {
|
|
30
|
-
throw new ApiError(response.status, body?.error || 'Unknown Error', body?.message || `Request failed with status ${response.status}`);
|
|
31
|
-
}
|
|
32
51
|
spinner?.succeed('Done');
|
|
33
52
|
return body;
|
|
34
53
|
}
|
|
@@ -37,4 +56,18 @@ export async function apiGet(opts) {
|
|
|
37
56
|
throw error;
|
|
38
57
|
}
|
|
39
58
|
}
|
|
59
|
+
/** A write with a JSON body; same auth / `ApiError` handling as `apiGet`. A 204 / empty body → `undefined`. */
|
|
60
|
+
export async function apiRequest(opts) {
|
|
61
|
+
const spinner = opts.silent ? null : createSpinner('Sending request...');
|
|
62
|
+
try {
|
|
63
|
+
const response = await request(opts, 'application/json', { method: opts.method, body: opts.body });
|
|
64
|
+
const text = response.status === 204 ? '' : await response.text();
|
|
65
|
+
spinner?.succeed('Done');
|
|
66
|
+
return (text ? JSON.parse(text) : undefined);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
spinner?.fail('Failed');
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
40
73
|
//# sourceMappingURL=api-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/client/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/client/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SaveOptions {
|
|
2
|
+
/** Replace an existing file. */
|
|
3
|
+
force?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Streams a presigned URL to disk. Plain fetch: NO Authorization header — the URL is a
|
|
7
|
+
* bearer URL and R2 would reject the extra header. Written through `writeViaPart`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function downloadToFile(url: string, file: string, opts?: SaveOptions & {
|
|
10
|
+
spinnerText?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
path: string;
|
|
13
|
+
bytes: number;
|
|
14
|
+
}>;
|
|
15
|
+
/** Writes text (transcript, press release) to `file`; refuses an existing target unless `force`. */
|
|
16
|
+
export declare function writeTextFile(file: string, text: string, opts?: SaveOptions): Promise<{
|
|
17
|
+
path: string;
|
|
18
|
+
bytes: number;
|
|
19
|
+
}>;
|
|
20
|
+
/** `mkdir -p` of a directory target (`documents --output dir/`); returns its absolute path. */
|
|
21
|
+
export declare function ensureDir(dir: string): Promise<string>;
|
|
22
|
+
/** File extension for a `content_type` (parameters ignored); `bin` when unknown. */
|
|
23
|
+
export declare function extensionFor(contentType: string | null | undefined): string;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createWriteStream, rmSync } from 'node:fs';
|
|
2
|
+
import { access, mkdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { Readable } from 'node:stream';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { createSpinner } from '../shared/spinner.js';
|
|
7
|
+
async function exists(path) {
|
|
8
|
+
try {
|
|
9
|
+
await access(path);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Resolves `file` against cwd and refuses an existing target unless `force`. */
|
|
17
|
+
async function prepareTarget(file, opts) {
|
|
18
|
+
const path = resolve(file);
|
|
19
|
+
if (!opts.force && await exists(path)) {
|
|
20
|
+
throw new Error(`${path} already exists (use --force to overwrite)`);
|
|
21
|
+
}
|
|
22
|
+
await mkdir(dirname(path), { recursive: true });
|
|
23
|
+
return path;
|
|
24
|
+
}
|
|
25
|
+
/** Exit codes of the signals that abort a write (128 + signal number, as a shell reports them). */
|
|
26
|
+
const ABORT_SIGNALS = { SIGINT: 130, SIGTERM: 143 };
|
|
27
|
+
/**
|
|
28
|
+
* Runs `write(part, signal)` against `<path>.part` and renames it onto `path` on success, so a
|
|
29
|
+
* failed write never leaves a partial file or clobbers an existing one. While it runs, SIGINT /
|
|
30
|
+
* SIGTERM (Ctrl-C) abort `signal`, remove the `.part` file synchronously and exit with 130 / 143 —
|
|
31
|
+
* a listener replaces Node's default exit, which would skip the cleanup.
|
|
32
|
+
*/
|
|
33
|
+
async function writeViaPart(path, write) {
|
|
34
|
+
const part = `${path}.part`;
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const onSignal = (signal) => {
|
|
37
|
+
controller.abort();
|
|
38
|
+
rmSync(part, { force: true });
|
|
39
|
+
process.exit(ABORT_SIGNALS[signal]);
|
|
40
|
+
};
|
|
41
|
+
for (const signal of Object.keys(ABORT_SIGNALS))
|
|
42
|
+
process.once(signal, onSignal);
|
|
43
|
+
try {
|
|
44
|
+
await write(part, controller.signal);
|
|
45
|
+
await rename(part, path);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
await rm(part, { force: true });
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
for (const signal of Object.keys(ABORT_SIGNALS))
|
|
53
|
+
process.off(signal, onSignal);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Streams a presigned URL to disk. Plain fetch: NO Authorization header — the URL is a
|
|
58
|
+
* bearer URL and R2 would reject the extra header. Written through `writeViaPart`.
|
|
59
|
+
*/
|
|
60
|
+
export async function downloadToFile(url, file, opts = {}) {
|
|
61
|
+
const path = await prepareTarget(file, opts);
|
|
62
|
+
const spinner = createSpinner(opts.spinnerText ?? 'Downloading...');
|
|
63
|
+
try {
|
|
64
|
+
await writeViaPart(path, async (part, signal) => {
|
|
65
|
+
const response = await fetch(url, { signal });
|
|
66
|
+
if (!response.ok || !response.body) {
|
|
67
|
+
throw new Error(`Download failed with status ${response.status}`);
|
|
68
|
+
}
|
|
69
|
+
await pipeline(Readable.fromWeb(response.body), createWriteStream(part), { signal });
|
|
70
|
+
});
|
|
71
|
+
const { size } = await stat(path);
|
|
72
|
+
spinner.succeed('Downloaded');
|
|
73
|
+
return { path, bytes: size };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
spinner.fail('Download failed');
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Writes text (transcript, press release) to `file`; refuses an existing target unless `force`. */
|
|
81
|
+
export async function writeTextFile(file, text, opts = {}) {
|
|
82
|
+
const path = await prepareTarget(file, opts);
|
|
83
|
+
await writeViaPart(path, (part, signal) => writeFile(part, text, { encoding: 'utf8', signal }));
|
|
84
|
+
return { path, bytes: Buffer.byteLength(text, 'utf8') };
|
|
85
|
+
}
|
|
86
|
+
/** `mkdir -p` of a directory target (`documents --output dir/`); returns its absolute path. */
|
|
87
|
+
export async function ensureDir(dir) {
|
|
88
|
+
const path = resolve(dir);
|
|
89
|
+
await mkdir(path, { recursive: true });
|
|
90
|
+
return path;
|
|
91
|
+
}
|
|
92
|
+
const EXTENSIONS = {
|
|
93
|
+
'application/pdf': 'pdf',
|
|
94
|
+
'text/html': 'html',
|
|
95
|
+
'text/plain': 'txt',
|
|
96
|
+
'text/csv': 'csv',
|
|
97
|
+
'application/json': 'json',
|
|
98
|
+
'application/xml': 'xml',
|
|
99
|
+
'text/xml': 'xml',
|
|
100
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
|
101
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
|
102
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
|
103
|
+
'application/msword': 'doc',
|
|
104
|
+
'application/vnd.ms-excel': 'xls',
|
|
105
|
+
'application/vnd.ms-powerpoint': 'ppt',
|
|
106
|
+
'image/png': 'png',
|
|
107
|
+
'image/jpeg': 'jpg',
|
|
108
|
+
'image/gif': 'gif',
|
|
109
|
+
'audio/mpeg': 'mp3',
|
|
110
|
+
};
|
|
111
|
+
/** File extension for a `content_type` (parameters ignored); `bin` when unknown. */
|
|
112
|
+
export function extensionFor(contentType) {
|
|
113
|
+
const type = (contentType ?? '').split(';')[0].trim().toLowerCase();
|
|
114
|
+
return EXTENSIONS[type] ?? 'bin';
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../../src/client/download.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure pieces of `invd ws listen --earnings`: the subscribe frame and one step per server
|
|
3
|
+
* frame of IDA's `earnings-calls.live` channel (`src/routes/websocket/types/earningsMessages.ts`).
|
|
4
|
+
*/
|
|
5
|
+
export declare const EARNINGS_LIVE_CHANNEL = "earnings-calls.live";
|
|
6
|
+
export type LiveTarget = {
|
|
7
|
+
event_id: string;
|
|
8
|
+
} | {
|
|
9
|
+
cik: string;
|
|
10
|
+
};
|
|
11
|
+
export interface SpeakerAssignment {
|
|
12
|
+
speaker_label: string;
|
|
13
|
+
from_seq: number;
|
|
14
|
+
to_seq: number | null;
|
|
15
|
+
name: string;
|
|
16
|
+
role?: string;
|
|
17
|
+
title?: string | null;
|
|
18
|
+
firm?: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface LiveSegment {
|
|
21
|
+
seq: number;
|
|
22
|
+
start_ms: number;
|
|
23
|
+
speaker_label: string | null;
|
|
24
|
+
text: string;
|
|
25
|
+
is_final: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface LiveState {
|
|
28
|
+
/** Highest final seq printed (or the `since_seq` asked for): a reconnect resumes after it. */
|
|
29
|
+
lastSeq: number;
|
|
30
|
+
eventId: string | null;
|
|
31
|
+
names: {
|
|
32
|
+
revision: number;
|
|
33
|
+
assignments: SpeakerAssignment[];
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
export interface LiveOptions {
|
|
37
|
+
partials: boolean;
|
|
38
|
+
names: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface LiveStep {
|
|
41
|
+
state: LiveState;
|
|
42
|
+
lines: string[];
|
|
43
|
+
/** A partial to show in place of the previous one (TTY only). */
|
|
44
|
+
partial?: string;
|
|
45
|
+
/** Stop: exit code + a closing line. */
|
|
46
|
+
end?: {
|
|
47
|
+
code: 0 | 1;
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
/** Close the socket and resubscribe through the backoff. */
|
|
51
|
+
reconnect?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** `--earnings`: a UUID is an event_id, 1–10 digits a CIK. */
|
|
54
|
+
export declare function parseTarget(value: string): LiveTarget;
|
|
55
|
+
/** `--since-seq`: an integer ≥ -1; default -1 (the whole call from its first final). */
|
|
56
|
+
export declare function parseSinceSeq(value: string | undefined): number;
|
|
57
|
+
export declare function subscribeFrame(target: LiveTarget, sinceSeq: number, partials: boolean): {
|
|
58
|
+
since_seq: number;
|
|
59
|
+
partials: boolean;
|
|
60
|
+
event_id: string;
|
|
61
|
+
type: string;
|
|
62
|
+
channel: string;
|
|
63
|
+
} | {
|
|
64
|
+
since_seq: number;
|
|
65
|
+
partials: boolean;
|
|
66
|
+
cik: string;
|
|
67
|
+
type: string;
|
|
68
|
+
channel: string;
|
|
69
|
+
};
|
|
70
|
+
export declare function initialLiveState(sinceSeq: number): LiveState;
|
|
71
|
+
/** The assignment of `label` covering `seq` (the latest-starting one wins), else null. */
|
|
72
|
+
export declare function nameFor(assignments: SpeakerAssignment[], label: string | null, seq: number): string | null;
|
|
73
|
+
/** `[m:ss] name: text`, the name from the snapshot, else `Speaker <label>`. */
|
|
74
|
+
export declare function formatSegment(segment: LiveSegment, assignments: SpeakerAssignment[]): string;
|
|
75
|
+
export declare function finalTranscriptHint(eventId: string | null): string;
|
|
76
|
+
/** One server frame → the new state and what to print / do. Unknown frames are ignored. */
|
|
77
|
+
export declare function liveStep(state: LiveState, frame: Record<string, any>, opts: LiveOptions): LiveStep;
|
|
78
|
+
/** Frames of the earnings channel (the socket also carries `new_filing` broadcasts and pings). */
|
|
79
|
+
export declare function isEarningsFrame(frame: Record<string, any>): boolean;
|