gangtise-openapi-cli 0.17.0 → 0.17.2
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/dist/src/cli.js +13 -4
- package/dist/src/core/client.js +22 -2
- package/dist/src/core/errors.js +1 -0
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -261,10 +261,19 @@ insight.addCommand(foreignOpinion);
|
|
|
261
261
|
insight.addCommand(independentOpinion);
|
|
262
262
|
program.addCommand(insight);
|
|
263
263
|
const quote = new Command("quote").description("Quote APIs");
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
const addKlineCommand = (name, endpointKey, securityHelp, shardDays) => quote.command(name)
|
|
265
|
+
.option("--security <code>", securityHelp, collectList, [])
|
|
266
|
+
.option("--start-date <date>", "Start date (default: 1 year before end-date)")
|
|
267
|
+
.option("--end-date <date>", "End date (default: latest)")
|
|
268
|
+
.option("--limit <number>", "Max rows per request (default: 6000, max: 10000)")
|
|
269
|
+
.option("--field <field>", "Field", collectList, [])
|
|
270
|
+
.option("--format <format>", "Output format", "table")
|
|
271
|
+
.option("--output <path>")
|
|
272
|
+
.action((options) => emit(options, (client) => callKlineWithSharding(client, endpointKey, buildQuoteKlineBody(options), { shardDays })));
|
|
273
|
+
addKlineCommand("day-kline", "quote.day-kline", "Security code (A-share: .SH/.SZ/.BJ, or 'all' for full market)", 1);
|
|
274
|
+
addKlineCommand("day-kline-hk", "quote.day-kline-hk", "Security code (HK stock: .HK, or 'all' for full market)", 2);
|
|
275
|
+
addKlineCommand("day-kline-us", "quote.day-kline-us", "Security code (US stock: e.g. AAPL.O, or 'all' for full market)", 1);
|
|
276
|
+
addKlineCommand("index-day-kline", "quote.index-day-kline", "Index code (.SH/.SZ/.BJ, or 'all' for full market)", 30);
|
|
268
277
|
quote.command("minute-kline").option("--security <code>", "Security code (A-share only: .SH/.SZ/.BJ)").option("--start-time <datetime>", "Start time (yyyy-MM-dd HH:mm:ss)").option("--end-time <datetime>", "End time (yyyy-MM-dd HH:mm:ss)").option("--limit <number>", "Max rows per request (default: 5000, max: 10000)").option("--field <field>", "Field", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("quote.minute-kline", { securityCode: options.security, startTime: options.startTime, endTime: options.endTime, limit: parseOptionalNumberOption(options.limit, "--limit", { integer: true, min: 1 }), fieldList: maybeArray(options.field) })));
|
|
269
278
|
quote.command("realtime").description("Realtime quote snapshot (A-share / HK / US)").option("--security <code>", "Security code (e.g. 600519.SH / 00700.HK / AAPL.O), or market keyword: aShares / hkStocks / usStocks", collectList, []).option("--field <field>", "Field", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("quote.realtime", { securityList: maybeArray(options.security), fieldList: maybeArray(options.field) })));
|
|
270
279
|
program.addCommand(quote);
|
package/dist/src/core/client.js
CHANGED
|
@@ -9,7 +9,10 @@ import { ENDPOINTS } from "./endpoints.js";
|
|
|
9
9
|
import { getLookupData } from "./lookupData/index.js";
|
|
10
10
|
import { getDispatcher, isVerbose, logTiming, markRetryable, runWithConcurrency, withRetry } from "./transport.js";
|
|
11
11
|
const PAGINATION_CONCURRENCY = Number(process.env.GANGTISE_PAGE_CONCURRENCY ?? 5) || 5;
|
|
12
|
-
|
|
12
|
+
// Auth errors that warrant a forced re-login + one replay. 8000014/8000015 are
|
|
13
|
+
// AK/SK errors; 0000001008 is a server-side token invalidation (the token still
|
|
14
|
+
// looks valid by local expiry, so only a forced refresh recovers it).
|
|
15
|
+
const AUTH_RETRY_CODES = new Set(["8000014", "8000015", "0000001008"]);
|
|
13
16
|
export class GangtiseClient {
|
|
14
17
|
config;
|
|
15
18
|
refreshPromise = null;
|
|
@@ -165,8 +168,10 @@ export class GangtiseClient {
|
|
|
165
168
|
nextFrom += size;
|
|
166
169
|
}
|
|
167
170
|
const MAX_PAGES = 1000;
|
|
171
|
+
let truncatedByPageCap = false;
|
|
168
172
|
if (pageRequests.length + 1 > MAX_PAGES) {
|
|
169
173
|
pageRequests.length = MAX_PAGES - 1;
|
|
174
|
+
truncatedByPageCap = true;
|
|
170
175
|
}
|
|
171
176
|
let unexpectedShape = false;
|
|
172
177
|
let totalDrift = false;
|
|
@@ -195,6 +200,12 @@ export class GangtiseClient {
|
|
|
195
200
|
if (totalDrift && isVerbose()) {
|
|
196
201
|
process.stderr.write(`[gangtise] warning: 'total' changed across pages (data shifted during fetch)\n`);
|
|
197
202
|
}
|
|
203
|
+
// Always surface a cap-induced truncation (not gated on verbose): the user
|
|
204
|
+
// asked for everything and is silently getting a subset, mirroring the
|
|
205
|
+
// partial-result warning in quoteSharding.
|
|
206
|
+
if (truncatedByPageCap) {
|
|
207
|
+
process.stderr.write(`[gangtise] warning: hit the ${MAX_PAGES}-page safety cap; fetched ${collected.length} of ${total} rows. Narrow the query (e.g. a shorter date range) or pass --size to fetch a bounded subset.\n`);
|
|
208
|
+
}
|
|
198
209
|
return {
|
|
199
210
|
...firstPage,
|
|
200
211
|
total,
|
|
@@ -330,7 +341,16 @@ export class GangtiseClient {
|
|
|
330
341
|
// Stream directly to disk when caller already knows the destination
|
|
331
342
|
if (options?.streamTo) {
|
|
332
343
|
await fs.mkdir(path.dirname(options.streamTo), { recursive: true });
|
|
333
|
-
|
|
344
|
+
try {
|
|
345
|
+
await pipeline(response.body, createWriteStream(options.streamTo));
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
// A mid-stream failure leaves a truncated file on disk; remove it so a
|
|
349
|
+
// failed download never looks like a complete one. withRetry may still
|
|
350
|
+
// replay the request (the next attempt re-creates the file).
|
|
351
|
+
await fs.unlink(options.streamTo).catch(() => { });
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
334
354
|
logTiming(`GET ${endpoint.path} (stream)`, Date.now() - startedAt, `${response.statusCode}`);
|
|
335
355
|
return { contentType, filename, savedPath: options.streamTo };
|
|
336
356
|
}
|
package/dist/src/core/errors.js
CHANGED
|
@@ -16,6 +16,7 @@ const ERROR_HINTS = {
|
|
|
16
16
|
"999995": "当前账号积分不足。",
|
|
17
17
|
"900002": "请求缺少 uid。",
|
|
18
18
|
"900001": "请求参数为空或缺少必填项。",
|
|
19
|
+
"0000001008": "Token 已失效(多为他处登录挤掉本会话);有 AK/SK 时 CLI 会自动重新登录重试一次,否则请重新登录。",
|
|
19
20
|
"8000014": "GANGTISE_ACCESS_KEY 错误。",
|
|
20
21
|
"8000015": "GANGTISE_SECRET_KEY 错误。",
|
|
21
22
|
"8000016": "开发账号状态异常。",
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated — DO NOT EDIT
|
|
2
|
-
export const CLI_VERSION = "0.17.
|
|
2
|
+
export const CLI_VERSION = "0.17.2";
|