@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.
Files changed (54) hide show
  1. package/README.md +314 -6
  2. package/claude-plugin/.claude-plugin/plugin.json +13 -5
  3. package/claude-plugin/.mcp.json +8 -0
  4. package/claude-plugin/agents/invinite-data.md +6 -2
  5. package/claude-plugin/commands/analyze-earnings-call.md +24 -0
  6. package/claude-plugin/commands/analyze-filing.md +15 -0
  7. package/claude-plugin/commands/compare-companies.md +15 -0
  8. package/claude-plugin/commands/financial-research.md +2 -1
  9. package/claude-plugin/commands/ownership-review.md +14 -0
  10. package/claude-plugin/skills/invinite-data-cli/SKILL.md +119 -8
  11. package/dist/src/cli.d.ts +2 -0
  12. package/dist/src/cli.js +22 -1
  13. package/dist/src/cli.js.map +1 -1
  14. package/dist/src/client/api-client.d.ts +13 -1
  15. package/dist/src/client/api-client.js +46 -13
  16. package/dist/src/client/api-client.js.map +1 -1
  17. package/dist/src/client/download.d.ts +23 -0
  18. package/dist/src/client/download.js +116 -0
  19. package/dist/src/client/download.js.map +1 -0
  20. package/dist/src/client/earnings-live.d.ts +79 -0
  21. package/dist/src/client/earnings-live.js +156 -0
  22. package/dist/src/client/earnings-live.js.map +1 -0
  23. package/dist/src/client/pagination.js +2 -1
  24. package/dist/src/client/pagination.js.map +1 -1
  25. package/dist/src/client/websocket-client.d.ts +12 -1
  26. package/dist/src/client/websocket-client.js +114 -37
  27. package/dist/src/client/websocket-client.js.map +1 -1
  28. package/dist/src/commands/earnings-selector.d.ts +15 -0
  29. package/dist/src/commands/earnings-selector.js +29 -0
  30. package/dist/src/commands/earnings-selector.js.map +1 -0
  31. package/dist/src/commands/earnings.d.ts +13 -0
  32. package/dist/src/commands/earnings.js +705 -0
  33. package/dist/src/commands/earnings.js.map +1 -0
  34. package/dist/src/commands/plugin.d.ts +11 -0
  35. package/dist/src/commands/plugin.js +23 -8
  36. package/dist/src/commands/plugin.js.map +1 -1
  37. package/dist/src/commands/webhooks.d.ts +6 -0
  38. package/dist/src/commands/webhooks.js +144 -0
  39. package/dist/src/commands/webhooks.js.map +1 -0
  40. package/dist/src/commands/ws.js +23 -3
  41. package/dist/src/commands/ws.js.map +1 -1
  42. package/dist/src/formatters/flatten.d.ts +4 -0
  43. package/dist/src/formatters/flatten.js +15 -0
  44. package/dist/src/formatters/flatten.js.map +1 -0
  45. package/dist/src/formatters/time.d.ts +2 -0
  46. package/dist/src/formatters/time.js +14 -0
  47. package/dist/src/formatters/time.js.map +1 -0
  48. package/dist/src/shared/errors.d.ts +2 -1
  49. package/dist/src/shared/errors.js +5 -1
  50. package/dist/src/shared/errors.js.map +1 -1
  51. package/dist/src/shared/options.d.ts +2 -0
  52. package/dist/src/shared/options.js +6 -0
  53. package/dist/src/shared/options.js.map +1 -1
  54. package/package.json +1 -1
@@ -1,21 +1,56 @@
1
1
  ---
2
- name: invinite-data-cli
3
- description: Fetch financial data using the Invinite Data API CLI. Use this skill whenever the user wants to look up company information, financial statements, SEC filings, institutional ownership, insider trades, IPO data, market holidays, financial metrics, or any stock/company-related data. Also use when the user mentions tickers, CIKs, balance sheets, income statements, cash flows, 10-K, 10-Q, ratios, CAGR, growth rates, dividends, stock splits, or institutional holdings — even if they don't mention "Invinite" by name.
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 CLI
7
+ # Invinite Data API
8
8
 
9
- The `invd` CLI provides access to comprehensive financial data from the Invinite Data API. Always use `--json` when fetching data for analysis — it returns structured JSON you can pipe to `jq` or parse programmatically.
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
- The CLI must be installed (`npm install -g @invinite-org/invinite-data-cli`) and authenticated. Authentication is resolved in this order:
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. OS keychain (via optional `keytar`)
16
- 3. Stored config (set via `invd config set-key`)
49
+ 2. `.env` file
50
+ 3. OS keychain (via optional `keytar`)
51
+ 4. Stored config
17
52
 
18
- If a command fails with an auth error, tell the user to run `invd config set-key` or set `INVINITE_DATA_API_KEY`.
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
@@ -1,2 +1,4 @@
1
1
  import { Command } from 'commander';
2
+ /** `package.json` sits one level up under tsx (`src/cli.ts`), two in the build (`dist/src/cli.js`). */
3
+ export declare function packageVersion(): string;
2
4
  export declare function createProgram(): Command;
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('0.1.0')
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);
@@ -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,OAAO,CAAC;SAChB,MAAM,CAAC,QAAQ,EAAE,iCAAiC,CAAC;SACnD,MAAM,CAAC,OAAO,EAAE,mCAAmC,CAAC;SACpD,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,WAAW,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,0BAA0B,CAAC,OAAO,CAAC,CAAC;IACpC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAClC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACnC,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACvC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAClC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC;AACjB,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
- params?: Record<string, string | undefined>;
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
- if (value !== undefined && value !== '') {
11
- url.searchParams.set(key, value);
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 apiKey = await getApiKey();
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;AAQrD,SAAS,QAAQ,CAAC,IAAY,EAAE,MAA2C;IACzE,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACxC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAc,IAAoB;IAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,MAAM,EAAE;gBACnC,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAS,CAAC;QAE1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,QAAQ,CAChB,QAAQ,CAAC,MAAM,EACf,IAAI,EAAE,KAAK,IAAI,eAAe,EAC9B,IAAI,EAAE,OAAO,IAAI,8BAA8B,QAAQ,CAAC,MAAM,EAAE,CACjE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,IAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,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;