@jackwener/opencli 0.7.10 → 0.7.11
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/.github/workflows/pkg-pr-new.yml +30 -0
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/browser/discover.d.ts +15 -0
- package/dist/browser/discover.js +60 -12
- package/dist/browser/index.d.ts +5 -1
- package/dist/browser/index.js +5 -1
- package/dist/browser/mcp.js +7 -6
- package/dist/browser.test.js +135 -1
- package/dist/cli-manifest.json +163 -0
- package/dist/clis/barchart/flow.d.ts +1 -0
- package/dist/clis/barchart/flow.js +117 -0
- package/dist/clis/barchart/greeks.d.ts +1 -0
- package/dist/clis/barchart/greeks.js +119 -0
- package/dist/clis/barchart/options.d.ts +1 -0
- package/dist/clis/barchart/options.js +106 -0
- package/dist/clis/barchart/quote.d.ts +1 -0
- package/dist/clis/barchart/quote.js +133 -0
- package/package.json +1 -1
- package/src/browser/discover.ts +73 -12
- package/src/browser/index.ts +5 -1
- package/src/browser/mcp.ts +7 -5
- package/src/browser.test.ts +140 -1
- package/src/clis/barchart/flow.ts +121 -0
- package/src/clis/barchart/greeks.ts +123 -0
- package/src/clis/barchart/options.ts +110 -0
- package/src/clis/barchart/quote.ts +137 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barchart stock quote — price, volume, market cap, P/E, EPS, and key metrics.
|
|
3
|
+
* Auth: CSRF token from <meta name="csrf-token"> + session cookies.
|
|
4
|
+
*/
|
|
5
|
+
import { cli, Strategy } from '../../registry.js';
|
|
6
|
+
|
|
7
|
+
cli({
|
|
8
|
+
site: 'barchart',
|
|
9
|
+
name: 'quote',
|
|
10
|
+
description: 'Barchart stock quote with price, volume, and key metrics',
|
|
11
|
+
domain: 'www.barchart.com',
|
|
12
|
+
strategy: Strategy.COOKIE,
|
|
13
|
+
args: [
|
|
14
|
+
{ name: 'symbol', required: true, help: 'Stock ticker (e.g. AAPL, MSFT, TSLA)' },
|
|
15
|
+
],
|
|
16
|
+
columns: [
|
|
17
|
+
'symbol', 'name', 'price', 'change', 'changePct',
|
|
18
|
+
'open', 'high', 'low', 'prevClose', 'volume',
|
|
19
|
+
'avgVolume', 'marketCap', 'peRatio', 'eps',
|
|
20
|
+
],
|
|
21
|
+
func: async (page, kwargs) => {
|
|
22
|
+
const symbol = kwargs.symbol.toUpperCase().trim();
|
|
23
|
+
await page.goto(`https://www.barchart.com/stocks/quotes/${encodeURIComponent(symbol)}/overview`);
|
|
24
|
+
await page.wait(4);
|
|
25
|
+
|
|
26
|
+
const data = await page.evaluate(`
|
|
27
|
+
(async () => {
|
|
28
|
+
const sym = '${symbol}';
|
|
29
|
+
const csrf = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
|
30
|
+
|
|
31
|
+
// Strategy 1: internal proxy API with CSRF token
|
|
32
|
+
try {
|
|
33
|
+
const fields = [
|
|
34
|
+
'symbol','symbolName','lastPrice','priceChange','percentChange',
|
|
35
|
+
'highPrice','lowPrice','openPrice','previousPrice','volume','averageVolume',
|
|
36
|
+
'marketCap','peRatio','earningsPerShare','tradeTime',
|
|
37
|
+
].join(',');
|
|
38
|
+
const url = '/proxies/core-api/v1/quotes/get?symbol=' + encodeURIComponent(sym) + '&fields=' + fields;
|
|
39
|
+
const resp = await fetch(url, {
|
|
40
|
+
credentials: 'include',
|
|
41
|
+
headers: { 'X-CSRF-TOKEN': csrf },
|
|
42
|
+
});
|
|
43
|
+
if (resp.ok) {
|
|
44
|
+
const d = await resp.json();
|
|
45
|
+
const row = d?.data?.[0] || null;
|
|
46
|
+
if (row) {
|
|
47
|
+
return { source: 'api', row };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch(e) {}
|
|
51
|
+
|
|
52
|
+
// Strategy 2: parse from DOM
|
|
53
|
+
try {
|
|
54
|
+
const priceEl = document.querySelector('span.last-change');
|
|
55
|
+
const price = priceEl ? priceEl.textContent.trim() : null;
|
|
56
|
+
|
|
57
|
+
// Change values are sibling spans inside .pricechangerow > .last-change
|
|
58
|
+
const changeParent = priceEl?.parentElement;
|
|
59
|
+
const changeSpans = changeParent ? changeParent.querySelectorAll('span') : [];
|
|
60
|
+
let change = null;
|
|
61
|
+
let changePct = null;
|
|
62
|
+
for (const s of changeSpans) {
|
|
63
|
+
const t = s.textContent.trim();
|
|
64
|
+
if (s === priceEl) continue;
|
|
65
|
+
if (t.includes('%')) changePct = t.replace(/[()]/g, '');
|
|
66
|
+
else if (t.match(/^[+-]?[\\d.]+$/)) change = t;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Financial data rows
|
|
70
|
+
const rows = document.querySelectorAll('.financial-data-row');
|
|
71
|
+
const fdata = {};
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
const spans = row.querySelectorAll('span');
|
|
74
|
+
if (spans.length >= 2) {
|
|
75
|
+
const label = spans[0].textContent.trim();
|
|
76
|
+
const valSpan = row.querySelector('span.right span:not(.ng-hide)');
|
|
77
|
+
fdata[label] = valSpan ? valSpan.textContent.trim() : '';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Day high/low from row chart
|
|
82
|
+
const dayLow = document.querySelector('.bc-quote-row-chart .small-6:first-child .inline:not(.ng-hide)');
|
|
83
|
+
const dayHigh = document.querySelector('.bc-quote-row-chart .text-right .inline:not(.ng-hide)');
|
|
84
|
+
const openEl = document.querySelector('.mark span');
|
|
85
|
+
const openText = openEl ? openEl.textContent.trim().replace('Open ', '') : null;
|
|
86
|
+
|
|
87
|
+
const name = document.querySelector('h1 span.symbol');
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
source: 'dom',
|
|
91
|
+
row: {
|
|
92
|
+
symbol: sym,
|
|
93
|
+
symbolName: name ? name.textContent.trim() : sym,
|
|
94
|
+
lastPrice: price,
|
|
95
|
+
priceChange: change,
|
|
96
|
+
percentChange: changePct,
|
|
97
|
+
open: openText,
|
|
98
|
+
highPrice: dayHigh ? dayHigh.textContent.trim() : null,
|
|
99
|
+
lowPrice: dayLow ? dayLow.textContent.trim() : null,
|
|
100
|
+
previousClose: fdata['Previous Close'] || null,
|
|
101
|
+
volume: fdata['Volume'] || null,
|
|
102
|
+
averageVolume: fdata['Average Volume'] || null,
|
|
103
|
+
marketCap: null,
|
|
104
|
+
peRatio: null,
|
|
105
|
+
earningsPerShare: null,
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
} catch(e) {
|
|
109
|
+
return { error: 'Could not fetch quote for ' + sym + ': ' + e.message };
|
|
110
|
+
}
|
|
111
|
+
})()
|
|
112
|
+
`);
|
|
113
|
+
|
|
114
|
+
if (!data || data.error) return [];
|
|
115
|
+
|
|
116
|
+
const r = data.row || {};
|
|
117
|
+
// API returns formatted strings like "+1.41" and "+0.56%"; use raw if available
|
|
118
|
+
const raw = r.raw || {};
|
|
119
|
+
|
|
120
|
+
return [{
|
|
121
|
+
symbol: r.symbol || symbol,
|
|
122
|
+
name: r.symbolName || r.name || symbol,
|
|
123
|
+
price: r.lastPrice ?? null,
|
|
124
|
+
change: r.priceChange ?? null,
|
|
125
|
+
changePct: r.percentChange ?? null,
|
|
126
|
+
open: r.openPrice ?? r.open ?? null,
|
|
127
|
+
high: r.highPrice ?? null,
|
|
128
|
+
low: r.lowPrice ?? null,
|
|
129
|
+
prevClose: r.previousPrice ?? r.previousClose ?? null,
|
|
130
|
+
volume: r.volume ?? null,
|
|
131
|
+
avgVolume: r.averageVolume ?? null,
|
|
132
|
+
marketCap: r.marketCap ?? null,
|
|
133
|
+
peRatio: r.peRatio ?? null,
|
|
134
|
+
eps: r.earningsPerShare ?? null,
|
|
135
|
+
}];
|
|
136
|
+
},
|
|
137
|
+
});
|
package/vitest.config.ts
CHANGED
|
@@ -7,6 +7,10 @@ export default defineConfig({
|
|
|
7
7
|
test: {
|
|
8
8
|
name: 'unit',
|
|
9
9
|
include: ['src/**/*.test.ts'],
|
|
10
|
+
// Run unit tests before e2e tests to avoid project-level contention in CI.
|
|
11
|
+
sequence: {
|
|
12
|
+
groupOrder: 0,
|
|
13
|
+
},
|
|
10
14
|
},
|
|
11
15
|
},
|
|
12
16
|
{
|
|
@@ -14,6 +18,9 @@ export default defineConfig({
|
|
|
14
18
|
name: 'e2e',
|
|
15
19
|
include: ['tests/**/*.test.ts'],
|
|
16
20
|
maxWorkers: 2,
|
|
21
|
+
sequence: {
|
|
22
|
+
groupOrder: 1,
|
|
23
|
+
},
|
|
17
24
|
},
|
|
18
25
|
},
|
|
19
26
|
],
|