@hithink-tech/hithink-finance-cli 0.1.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 (203) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +161 -0
  4. package/SECURITY.md +3 -0
  5. package/dist/application/config.d.ts +44 -0
  6. package/dist/application/config.js +111 -0
  7. package/dist/application/ports/auth-provider.d.ts +23 -0
  8. package/dist/application/ports/auth-provider.js +2 -0
  9. package/dist/application/source-policy.d.ts +13 -0
  10. package/dist/application/source-policy.js +31 -0
  11. package/dist/application/use-cases/data-clean.d.ts +2 -0
  12. package/dist/application/use-cases/data-clean.js +18 -0
  13. package/dist/application/use-cases/data-init.d.ts +4 -0
  14. package/dist/application/use-cases/data-init.js +9 -0
  15. package/dist/application/use-cases/data-remove.d.ts +2 -0
  16. package/dist/application/use-cases/data-remove.js +27 -0
  17. package/dist/application/use-cases/data-repair.d.ts +5 -0
  18. package/dist/application/use-cases/data-repair.js +5 -0
  19. package/dist/application/use-cases/data-sync.d.ts +25 -0
  20. package/dist/application/use-cases/data-sync.js +55 -0
  21. package/dist/application/use-cases/local-query.d.ts +4 -0
  22. package/dist/application/use-cases/local-query.js +88 -0
  23. package/dist/application/use-cases/market-history.d.ts +8 -0
  24. package/dist/application/use-cases/market-history.js +16 -0
  25. package/dist/application/use-cases/market-panel.d.ts +3 -0
  26. package/dist/application/use-cases/market-panel.js +5 -0
  27. package/dist/application/use-cases/remote-query.d.ts +4 -0
  28. package/dist/application/use-cases/remote-query.js +66 -0
  29. package/dist/cli/context.d.ts +17 -0
  30. package/dist/cli/context.js +52 -0
  31. package/dist/cli/i18n.d.ts +23 -0
  32. package/dist/cli/i18n.js +150 -0
  33. package/dist/cli/main.d.ts +3 -0
  34. package/dist/cli/main.js +80 -0
  35. package/dist/cli/program.d.ts +20 -0
  36. package/dist/cli/program.js +111 -0
  37. package/dist/commands/auth/index.d.ts +10 -0
  38. package/dist/commands/auth/index.js +131 -0
  39. package/dist/commands/config/index.d.ts +13 -0
  40. package/dist/commands/config/index.js +26 -0
  41. package/dist/commands/data/index.d.ts +10 -0
  42. package/dist/commands/data/index.js +185 -0
  43. package/dist/commands/db/index.d.ts +4 -0
  44. package/dist/commands/db/index.js +72 -0
  45. package/dist/commands/financials/index.d.ts +5 -0
  46. package/dist/commands/financials/index.js +6 -0
  47. package/dist/commands/index/index.d.ts +5 -0
  48. package/dist/commands/index/index.js +6 -0
  49. package/dist/commands/market/index.d.ts +5 -0
  50. package/dist/commands/market/index.js +6 -0
  51. package/dist/commands/market/local.d.ts +4 -0
  52. package/dist/commands/market/local.js +97 -0
  53. package/dist/commands/remote.d.ts +11 -0
  54. package/dist/commands/remote.js +195 -0
  55. package/dist/commands/skills/index.d.ts +4 -0
  56. package/dist/commands/skills/index.js +43 -0
  57. package/dist/commands/special/index.d.ts +5 -0
  58. package/dist/commands/special/index.js +6 -0
  59. package/dist/commands/symbol/index.d.ts +5 -0
  60. package/dist/commands/symbol/index.js +6 -0
  61. package/dist/commands/system/capabilities.d.ts +4 -0
  62. package/dist/commands/system/capabilities.js +24 -0
  63. package/dist/commands/system/doctor.d.ts +4 -0
  64. package/dist/commands/system/doctor.js +15 -0
  65. package/dist/commands/system/schema.d.ts +4 -0
  66. package/dist/commands/system/schema.js +47 -0
  67. package/dist/commands/system/uninstall.d.ts +11 -0
  68. package/dist/commands/system/uninstall.js +68 -0
  69. package/dist/commands/system/update.d.ts +11 -0
  70. package/dist/commands/system/update.js +72 -0
  71. package/dist/contracts/envelope.d.ts +37 -0
  72. package/dist/contracts/envelope.js +36 -0
  73. package/dist/contracts/errors.d.ts +23 -0
  74. package/dist/contracts/errors.js +33 -0
  75. package/dist/contracts/local-capabilities.d.ts +13 -0
  76. package/dist/contracts/local-capabilities.js +37 -0
  77. package/dist/contracts/registry.d.ts +9 -0
  78. package/dist/contracts/registry.js +4 -0
  79. package/dist/contracts/remote-capabilities.d.ts +24 -0
  80. package/dist/contracts/remote-capabilities.js +667 -0
  81. package/dist/domains/financials/types.d.ts +6 -0
  82. package/dist/domains/financials/types.js +2 -0
  83. package/dist/domains/index/types.d.ts +6 -0
  84. package/dist/domains/index/types.js +2 -0
  85. package/dist/domains/market/types.d.ts +6 -0
  86. package/dist/domains/market/types.js +2 -0
  87. package/dist/domains/special/types.d.ts +5 -0
  88. package/dist/domains/special/types.js +2 -0
  89. package/dist/domains/symbol/types.d.ts +6 -0
  90. package/dist/domains/symbol/types.js +2 -0
  91. package/dist/infrastructure/credentials/api-key-provider.d.ts +14 -0
  92. package/dist/infrastructure/credentials/api-key-provider.js +102 -0
  93. package/dist/infrastructure/credentials/keyring.d.ts +15 -0
  94. package/dist/infrastructure/credentials/keyring.js +21 -0
  95. package/dist/infrastructure/credentials/redact.d.ts +3 -0
  96. package/dist/infrastructure/credentials/redact.js +26 -0
  97. package/dist/infrastructure/duckdb/connection.d.ts +8 -0
  98. package/dist/infrastructure/duckdb/connection.js +18 -0
  99. package/dist/infrastructure/duckdb/dump-client.d.ts +20 -0
  100. package/dist/infrastructure/duckdb/dump-client.js +66 -0
  101. package/dist/infrastructure/duckdb/factors.d.ts +3 -0
  102. package/dist/infrastructure/duckdb/factors.js +83 -0
  103. package/dist/infrastructure/duckdb/importer.d.ts +11 -0
  104. package/dist/infrastructure/duckdb/importer.js +105 -0
  105. package/dist/infrastructure/duckdb/migrations.d.ts +17 -0
  106. package/dist/infrastructure/duckdb/migrations.js +92 -0
  107. package/dist/infrastructure/duckdb/quality.d.ts +12 -0
  108. package/dist/infrastructure/duckdb/quality.js +56 -0
  109. package/dist/infrastructure/duckdb/schema.d.ts +4 -0
  110. package/dist/infrastructure/duckdb/schema.js +14 -0
  111. package/dist/infrastructure/filesystem/atomic-file.d.ts +2 -0
  112. package/dist/infrastructure/filesystem/atomic-file.js +38 -0
  113. package/dist/infrastructure/filesystem/platform-paths.d.ts +15 -0
  114. package/dist/infrastructure/filesystem/platform-paths.js +42 -0
  115. package/dist/infrastructure/filesystem/process-lock.d.ts +6 -0
  116. package/dist/infrastructure/filesystem/process-lock.js +56 -0
  117. package/dist/infrastructure/filesystem/stdin.d.ts +5 -0
  118. package/dist/infrastructure/filesystem/stdin.js +9 -0
  119. package/dist/infrastructure/fuyao/client.d.ts +31 -0
  120. package/dist/infrastructure/fuyao/client.js +145 -0
  121. package/dist/infrastructure/fuyao/envelope.d.ts +9 -0
  122. package/dist/infrastructure/fuyao/envelope.js +8 -0
  123. package/dist/infrastructure/fuyao/pagination.d.ts +16 -0
  124. package/dist/infrastructure/fuyao/pagination.js +44 -0
  125. package/dist/infrastructure/fuyao/retry.d.ts +5 -0
  126. package/dist/infrastructure/fuyao/retry.js +18 -0
  127. package/dist/infrastructure/fuyao/windowing.d.ts +8 -0
  128. package/dist/infrastructure/fuyao/windowing.js +25 -0
  129. package/dist/infrastructure/skills/drift.d.ts +3 -0
  130. package/dist/infrastructure/skills/drift.js +8 -0
  131. package/dist/infrastructure/skills/installer.d.ts +13 -0
  132. package/dist/infrastructure/skills/installer.js +48 -0
  133. package/dist/infrastructure/skills/manifest.d.ts +11 -0
  134. package/dist/infrastructure/skills/manifest.js +62 -0
  135. package/dist/infrastructure/updater/cache.d.ts +15 -0
  136. package/dist/infrastructure/updater/cache.js +76 -0
  137. package/dist/infrastructure/updater/check.d.ts +13 -0
  138. package/dist/infrastructure/updater/check.js +51 -0
  139. package/dist/infrastructure/updater/install.d.ts +4 -0
  140. package/dist/infrastructure/updater/install.js +39 -0
  141. package/dist/output/json.d.ts +3 -0
  142. package/dist/output/json.js +16 -0
  143. package/dist/output/renderer.d.ts +4 -0
  144. package/dist/output/renderer.js +47 -0
  145. package/dist/output/table.d.ts +2 -0
  146. package/dist/output/table.js +31 -0
  147. package/migrations/001-initial.sql +95 -0
  148. package/migrations/manifest.json +12 -0
  149. package/package.json +66 -0
  150. package/schemas/capabilities.json +290 -0
  151. package/schemas/command-envelope.schema.json +12 -0
  152. package/scripts/postinstall.mjs +27 -0
  153. package/scripts/update-check.mjs +23 -0
  154. package/skills/hithink-finance-data/SKILL.md +74 -0
  155. package/skills/hithink-finance-data/references/data-clean.md +33 -0
  156. package/skills/hithink-finance-data/references/data-init.md +35 -0
  157. package/skills/hithink-finance-data/references/data-migrate.md +33 -0
  158. package/skills/hithink-finance-data/references/data-remove.md +33 -0
  159. package/skills/hithink-finance-data/references/data-repair.md +33 -0
  160. package/skills/hithink-finance-data/references/data-status.md +33 -0
  161. package/skills/hithink-finance-data/references/data-sync.md +34 -0
  162. package/skills/hithink-finance-data/references/data-validate.md +33 -0
  163. package/skills/hithink-finance-data/references/db-describe.md +33 -0
  164. package/skills/hithink-finance-data/references/db-export.md +33 -0
  165. package/skills/hithink-finance-data/references/db-query.md +33 -0
  166. package/skills/hithink-finance-financials/SKILL.md +59 -0
  167. package/skills/hithink-finance-financials/references/financials-balance-sheet.md +40 -0
  168. package/skills/hithink-finance-financials/references/financials-cash-flow.md +40 -0
  169. package/skills/hithink-finance-financials/references/financials-income.md +40 -0
  170. package/skills/hithink-finance-financials/references/financials-indicators.md +37 -0
  171. package/skills/hithink-finance-index/SKILL.md +59 -0
  172. package/skills/hithink-finance-index/references/index-catalog.md +36 -0
  173. package/skills/hithink-finance-index/references/index-constituents.md +36 -0
  174. package/skills/hithink-finance-index/references/index-history.md +38 -0
  175. package/skills/hithink-finance-index/references/index-snapshot.md +37 -0
  176. package/skills/hithink-finance-market/SKILL.md +63 -0
  177. package/skills/hithink-finance-market/references/market-adjustment-factors.md +33 -0
  178. package/skills/hithink-finance-market/references/market-calendar.md +35 -0
  179. package/skills/hithink-finance-market/references/market-corporate-actions.md +38 -0
  180. package/skills/hithink-finance-market/references/market-history.md +39 -0
  181. package/skills/hithink-finance-market/references/market-panel.md +33 -0
  182. package/skills/hithink-finance-market/references/market-snapshot.md +39 -0
  183. package/skills/hithink-finance-research/SKILL.md +58 -0
  184. package/skills/hithink-finance-research/references/research-workflow.md +34 -0
  185. package/skills/hithink-finance-shared/SKILL.md +55 -0
  186. package/skills/hithink-finance-shared/references/auth-and-config.md +31 -0
  187. package/skills/hithink-finance-shared/references/global-rules.md +21 -0
  188. package/skills/hithink-finance-shared/references/lifecycle.md +23 -0
  189. package/skills/hithink-finance-shared/references/skills-management.md +22 -0
  190. package/skills/hithink-finance-special-data/SKILL.md +68 -0
  191. package/skills/hithink-finance-special-data/references/special-anomaly-list.md +36 -0
  192. package/skills/hithink-finance-special-data/references/special-anomaly-stock.md +37 -0
  193. package/skills/hithink-finance-special-data/references/special-dragon-tiger.md +37 -0
  194. package/skills/hithink-finance-special-data/references/special-hot-stock-history.md +36 -0
  195. package/skills/hithink-finance-special-data/references/special-hot-stock-trend.md +38 -0
  196. package/skills/hithink-finance-special-data/references/special-hot-stock.md +36 -0
  197. package/skills/hithink-finance-special-data/references/special-limit-up-ladder.md +35 -0
  198. package/skills/hithink-finance-special-data/references/special-limit-up-pool.md +40 -0
  199. package/skills/hithink-finance-special-data/references/special-skyrocket.md +36 -0
  200. package/skills/hithink-finance-symbol/SKILL.md +56 -0
  201. package/skills/hithink-finance-symbol/references/symbol-list.md +39 -0
  202. package/skills/hithink-finance-symbol/references/symbol-search.md +39 -0
  203. package/skills/manifest.json +55 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - Unreleased
4
+
5
+ - Initial TypeScript CLI with hithink finance REST capabilities, DuckDB data engine, Agent Skills, and lifecycle commands.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HiThink-Tech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # hithink-finance CLI
2
+
3
+ `hithink-finance` 是同花顺金融数据服务的 Node.js 命令行客户端,面向人类终端、AI Agent、CI 和自动化脚本。它统一提供远端数据、本地 DuckDB、认证、结构化输出、能力发现、诊断和生命周期管理,运行时不依赖 monorepo 的 Python 子项目。
4
+
5
+ CLI 文档只维护命令和运行语义;上游 REST 参数与响应字段统一见 [`docs/api/`](../docs/api/README.md)。
6
+
7
+ ## 环境与安装
8
+
9
+ 需要 Node.js `>=22.12.0`。
10
+
11
+ ### 从当前仓库运行
12
+
13
+ ```bash
14
+ cd hithink-finance-cli
15
+ npm ci --ignore-scripts
16
+ npm run build
17
+ node dist/cli/main.js --help
18
+ node dist/cli/main.js capabilities --format json
19
+ ```
20
+
21
+ ### npm 发布后安装
22
+
23
+ ```bash
24
+ npm install -g @hithink-tech/hithink-finance-cli
25
+ hithink-finance version --format json
26
+ hithink-finance doctor --format json
27
+ ```
28
+
29
+ 如果 `npm install` 返回 `E404`,说明包尚未发布或当前 registry 无法访问。报告实际状态,不要在终端指引中把源码安装伪装成正式发布安装。
30
+
31
+ ## API Key 与认证
32
+
33
+ 远端命令使用统一 API Key,获取地址:<https://fuyao.aicubes.cn/admin>。
34
+
35
+ 交互式终端:
36
+
37
+ ```bash
38
+ hithink-finance auth login
39
+ hithink-finance auth status --format json
40
+ ```
41
+
42
+ Agent/CI 使用 stdin 或进程环境变量:
43
+
44
+ ```bash
45
+ printf '%s' "$HITHINK_FINANCE_API_KEY" | \
46
+ hithink-finance auth login --api-key-stdin --format json
47
+ ```
48
+
49
+ 不要把 Key 写入配置文件、命令参数记录、日志、Markdown、对话或 Git。`config show` 只展示非敏感配置。
50
+
51
+ ## 能力发现
52
+
53
+ CLI 的当前命令事实由运行时输出提供:
54
+
55
+ ```bash
56
+ hithink-finance capabilities --format json
57
+ hithink-finance schema market.snapshot --format json
58
+ hithink-finance market snapshot --help
59
+ ```
60
+
61
+ Agent 不应只凭 README 猜参数。先读取 `capabilities`,再对目标 capability 读取 `schema`。
62
+
63
+ ## 命令导航
64
+
65
+ | 命令组 | 用途 |
66
+ | ------------------------------- | ---------------------------------------------------- |
67
+ | `version`, `doctor` | 版本和环境诊断 |
68
+ | `auth`, `config` | API Key 与非敏感配置 |
69
+ | `symbol` | 标的检索与代码表 |
70
+ | `market` | 个股行情、交易日历、本地面板和复权因子 |
71
+ | `financials` | 财务报表与财务指标 |
72
+ | `index` | 指数/板块目录、成分和行情 |
73
+ | `special` | 涨停、连板、异动、热榜和龙虎榜 |
74
+ | `data` | 本地数据库初始化、同步、状态、校验、迁移、修复和清理 |
75
+ | `db` | DuckDB 描述、只读 SQL 和导出 |
76
+ | `capabilities`, `schema` | 机器可读命令契约 |
77
+ | `skills`, `update`, `uninstall` | Agent Skills 与 CLI 生命周期 |
78
+
79
+ 常见调用:
80
+
81
+ ```bash
82
+ hithink-finance symbol search --q 600519 --limit 5 --format json
83
+ hithink-finance market snapshot --thscodes 600519.SH --format json
84
+ hithink-finance financials income --thscode 600519.SH --limit 4 --format json
85
+ hithink-finance index constituents --thscode 000300.SH --format json
86
+ hithink-finance special limit-up-pool --size 50 --format json
87
+ hithink-finance data status --format json
88
+ hithink-finance db query --sql "SELECT * FROM v_daily_qfq LIMIT 10" --format json
89
+ ```
90
+
91
+ ## 输出与错误语义
92
+
93
+ - 机器读取显式使用 `--format json`;人类交互可选择 `table`。
94
+ - 成功以进程退出码 0 和 JSON 信封 `ok=true` 为准。
95
+ - 失败以非 0 退出码和 `ok=false` 为准,读取 `error.code`、`error.category`、`error.hint` 与请求标识。
96
+ - 远端上游使用 `{code, message, request_id, data}`,但 CLI 会规范化为自己的信封;不要混用两套成功判断。
97
+ - `--debug` 只向 stderr 输出诊断,不污染机器可读 stdout。
98
+
99
+ ## 大结果与数据源
100
+
101
+ `--source auto|local|remote` 控制支持该选项的行情路由。只有具体命令声明的 `--output <path>` 才能直接落盘,它不是全局选项。
102
+
103
+ ```bash
104
+ hithink-finance market panel \
105
+ --start 2026-01-01 --end 2026-01-31 \
106
+ --output panel.parquet --file-format parquet --format json
107
+
108
+ hithink-finance db export \
109
+ --sql "SELECT * FROM v_daily_qfq" \
110
+ --output daily.parquet --file-format parquet --format json
111
+ ```
112
+
113
+ 全市场、分页全集、多标的或长时间窗口必须落盘。终端和 Agent 对话只报告路径、行数、窗口和摘要。
114
+
115
+ ## 本地 DuckDB
116
+
117
+ ```bash
118
+ hithink-finance data init --format json
119
+ hithink-finance data sync --format json
120
+ hithink-finance data status --format json
121
+ hithink-finance data validate --format json
122
+ hithink-finance db describe --format json
123
+ ```
124
+
125
+ 初始化和同步需要远端 API Key;已有本地库的只读查询通常不需要。迁移、修复、清理和卸载等有副作用操作先查看计划或帮助,并在命令要求时获得用户明确确认。
126
+
127
+ ## 配置优先级
128
+
129
+ CLI 按以下顺序解析非敏感配置:
130
+
131
+ 1. CLI 参数
132
+ 2. 环境变量
133
+ 3. 项目 `hithink-finance.config.json`
134
+ 4. 用户配置
135
+ 5. 默认值
136
+
137
+ 使用 `hithink-finance config show --format json` 查看最终生效的非敏感配置。多套环境使用 `--profile <name>`。
138
+
139
+ ## Agent Skill
140
+
141
+ 跨 REST API、MCP、CLI 和 Python SDK 的统一入口是仓库根 [`skills/hithink-finance`](../skills/hithink-finance/SKILL.md):
142
+
143
+ ```bash
144
+ npx skills add HiThink-Tech/Financial-API --skill hithink-finance -g --yes
145
+ ```
146
+
147
+ CLI 包还可以通过 `hithink-finance skills status|sync|remove` 管理命令专用的领域 Skills。它们补充 CLI 参数路由,不替代统一 `hithink-finance` Skill,也不是上游 API 文档的事实源。
148
+
149
+ ## 开发与验证
150
+
151
+ ```bash
152
+ npm ci --ignore-scripts
153
+ npm run generate:contracts
154
+ npm run verify
155
+ node scripts/release-smoke.mjs
156
+ ```
157
+
158
+ - 改动命令、选项、输出或错误语义后,先运行 `npm run generate:contracts` 更新 CLI 自带能力与 Skill 契约。
159
+ - `npm run verify` 依次检查格式、lint、类型、测试和构建。
160
+ - 发布流程与许可守门见 [`docs/maintainers/npm-publishing.md`](docs/maintainers/npm-publishing.md)。
161
+ - 版本变化见 [`CHANGELOG.md`](CHANGELOG.md),安全问题按 [`SECURITY.md`](SECURITY.md) 私下报告。
package/SECURITY.md ADDED
@@ -0,0 +1,3 @@
1
+ # Security
2
+
3
+ Report vulnerabilities privately through GitHub Security Advisories for `HiThink-Tech/Financial-API`. Do not include API keys, credentials, private datasets, or production logs containing secrets.
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { type PlatformPaths } from '../infrastructure/filesystem/platform-paths.js';
3
+ declare const configFileSchema: z.ZodObject<{
4
+ dbPath: z.ZodOptional<z.ZodString>;
5
+ profile: z.ZodOptional<z.ZodString>;
6
+ format: z.ZodOptional<z.ZodEnum<{
7
+ auto: "auto";
8
+ json: "json";
9
+ ndjson: "ndjson";
10
+ csv: "csv";
11
+ table: "table";
12
+ }>>;
13
+ language: z.ZodOptional<z.ZodEnum<{
14
+ "zh-CN": "zh-CN";
15
+ en: "en";
16
+ }>>;
17
+ updateCheck: z.ZodOptional<z.ZodBoolean>;
18
+ }, z.core.$strict>;
19
+ export type ConfigFile = z.infer<typeof configFileSchema>;
20
+ export interface ConfigOverrides {
21
+ dbPath?: string;
22
+ profile?: string;
23
+ format?: ConfigFile['format'];
24
+ language?: ConfigFile['language'];
25
+ configPath?: string;
26
+ }
27
+ export interface ResolvedConfig {
28
+ dbPath: string;
29
+ profile: string;
30
+ format: NonNullable<ConfigFile['format']>;
31
+ language?: ConfigFile['language'];
32
+ updateCheck: boolean;
33
+ configPath?: string;
34
+ }
35
+ export interface LoadConfigInput {
36
+ cli?: ConfigOverrides;
37
+ env?: NodeJS.ProcessEnv;
38
+ cwd?: string;
39
+ paths?: PlatformPaths;
40
+ }
41
+ export declare function parseConfigFile(value: unknown, source: string): ConfigFile;
42
+ export declare function loadConfig(input?: LoadConfigInput): Promise<ResolvedConfig>;
43
+ export {};
44
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,111 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ import { CliError } from '../contracts/errors.js';
5
+ import { createPlatformPaths, } from '../infrastructure/filesystem/platform-paths.js';
6
+ const configFileSchema = z
7
+ .object({
8
+ dbPath: z.string().min(1).optional(),
9
+ profile: z.string().min(1).optional(),
10
+ format: z.enum(['auto', 'json', 'ndjson', 'csv', 'table']).optional(),
11
+ language: z.enum(['zh-CN', 'en']).optional(),
12
+ updateCheck: z.boolean().optional(),
13
+ })
14
+ .strict();
15
+ const SECRET_KEYS = new Set(['apikey', 'token', 'authorization', 'cookie', 'secret']);
16
+ function containsSecret(value) {
17
+ if (Array.isArray(value))
18
+ return value.some(containsSecret);
19
+ if (value === null || typeof value !== 'object')
20
+ return false;
21
+ return Object.entries(value).some(([key, entry]) => {
22
+ const normalized = key.toLowerCase().replaceAll(/[^a-z]/g, '');
23
+ return SECRET_KEYS.has(normalized) || containsSecret(entry);
24
+ });
25
+ }
26
+ function configError(code, message, hint) {
27
+ return new CliError({
28
+ code,
29
+ category: 'validation',
30
+ message: `${code}: ${message}`,
31
+ hint,
32
+ retryable: false,
33
+ exitCode: 2,
34
+ });
35
+ }
36
+ export function parseConfigFile(value, source) {
37
+ if (containsSecret(value)) {
38
+ throw configError('CONFIG_SECRET_FORBIDDEN', `Secrets are not allowed in ${source}.`, 'Use HITHINK_FINANCE_API_KEY, --api-key-stdin, or the system credential store.');
39
+ }
40
+ const parsed = configFileSchema.safeParse(value);
41
+ if (!parsed.success) {
42
+ throw configError('CONFIG_INVALID', `Configuration in ${source} does not match the supported schema.`, parsed.error.issues.map((issue) => issue.message).join('; '));
43
+ }
44
+ return parsed.data;
45
+ }
46
+ async function readConfig(configPath) {
47
+ let content;
48
+ try {
49
+ content = await readFile(configPath, 'utf8');
50
+ }
51
+ catch (error) {
52
+ if (typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT') {
53
+ return undefined;
54
+ }
55
+ throw error;
56
+ }
57
+ try {
58
+ return parseConfigFile(JSON.parse(content), configPath);
59
+ }
60
+ catch (error) {
61
+ if (error instanceof CliError)
62
+ throw error;
63
+ throw configError('CONFIG_INVALID', `Configuration in ${configPath} is not valid JSON.`, 'Fix the JSON syntax or remove the file.');
64
+ }
65
+ }
66
+ function resolveFrom(baseDirectory, value) {
67
+ return path.isAbsolute(value) ? path.normalize(value) : path.resolve(baseDirectory, value);
68
+ }
69
+ export async function loadConfig(input = {}) {
70
+ const cwd = input.cwd ?? process.cwd();
71
+ const env = input.env ?? process.env;
72
+ const paths = input.paths ?? createPlatformPaths({ env });
73
+ const cli = input.cli ?? {};
74
+ const explicitConfigPath = cli.configPath ?? env.HITHINK_FINANCE_CONFIG;
75
+ const projectConfigPath = resolveFrom(cwd, explicitConfigPath ?? 'hithink-finance.config.json');
76
+ const [userConfig, projectConfig] = await Promise.all([
77
+ readConfig(paths.userConfigFile),
78
+ readConfig(projectConfigPath),
79
+ ]);
80
+ let dbPath = paths.defaultDbPath;
81
+ if (userConfig?.dbPath !== undefined) {
82
+ dbPath = resolveFrom(path.dirname(paths.userConfigFile), userConfig.dbPath);
83
+ }
84
+ if (projectConfig?.dbPath !== undefined) {
85
+ dbPath = resolveFrom(path.dirname(projectConfigPath), projectConfig.dbPath);
86
+ }
87
+ if (env.HITHINK_FINANCE_DB_PATH !== undefined) {
88
+ dbPath = resolveFrom(cwd, env.HITHINK_FINANCE_DB_PATH);
89
+ }
90
+ if (cli.dbPath !== undefined) {
91
+ dbPath = resolveFrom(cwd, cli.dbPath);
92
+ }
93
+ const language = cli.language ?? projectConfig?.language ?? userConfig?.language;
94
+ const result = {
95
+ dbPath,
96
+ profile: cli.profile ??
97
+ env.HITHINK_FINANCE_PROFILE ??
98
+ projectConfig?.profile ??
99
+ userConfig?.profile ??
100
+ 'default',
101
+ format: cli.format ?? projectConfig?.format ?? userConfig?.format ?? 'auto',
102
+ updateCheck: env.HITHINK_FINANCE_NO_UPDATE_CHECK === undefined &&
103
+ (projectConfig?.updateCheck ?? userConfig?.updateCheck ?? true),
104
+ };
105
+ if (language !== undefined)
106
+ result.language = language;
107
+ if (explicitConfigPath !== undefined)
108
+ result.configPath = projectConfigPath;
109
+ return result;
110
+ }
111
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,23 @@
1
+ export interface AuthSession {
2
+ method: 'api-key';
3
+ profile: string;
4
+ apiKey: string;
5
+ source: 'explicit' | 'environment' | 'keyring';
6
+ }
7
+ export interface LoginInput {
8
+ profile: string;
9
+ apiKey: string;
10
+ }
11
+ export interface AuthStatus {
12
+ method: 'api-key';
13
+ profile: string;
14
+ configured: boolean;
15
+ }
16
+ export interface AuthProvider {
17
+ readonly method: string;
18
+ resolve(profile: string, explicit?: string): Promise<AuthSession>;
19
+ login(input: LoginInput): Promise<AuthStatus>;
20
+ logout(profile: string): Promise<void>;
21
+ status(profile: string): Promise<AuthStatus>;
22
+ }
23
+ //# sourceMappingURL=auth-provider.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth-provider.js.map
@@ -0,0 +1,13 @@
1
+ export type DataSource = 'auto' | 'local' | 'remote';
2
+ export type RequestKind = 'snapshot' | 'financials' | 'index' | 'special' | 'calendar' | 'panel' | 'factors' | 'db' | 'maintenance' | 'history';
3
+ export interface SourceRequest {
4
+ kind: RequestKind;
5
+ requested: DataSource;
6
+ symbolCount?: number;
7
+ }
8
+ export interface LocalState {
9
+ exists: boolean;
10
+ coversWindow: boolean;
11
+ }
12
+ export declare function chooseSource(request: SourceRequest, local: LocalState): Exclude<DataSource, 'auto'>;
13
+ //# sourceMappingURL=source-policy.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { CliError } from '../contracts/errors.js';
2
+ function sourceError(code, message) {
3
+ throw new CliError({
4
+ code,
5
+ category: 'local-data',
6
+ message: `${code}: ${message}`,
7
+ hint: 'Initialize/sync local data or choose a supported --source.',
8
+ retryable: false,
9
+ exitCode: 5,
10
+ });
11
+ }
12
+ export function chooseSource(request, local) {
13
+ const remoteOnly = ['snapshot', 'financials', 'index', 'special', 'calendar'].includes(request.kind);
14
+ const localOnly = ['panel', 'factors', 'db', 'maintenance'].includes(request.kind);
15
+ if (request.requested === 'local' && remoteOnly)
16
+ return sourceError('SOURCE_NOT_SUPPORTED', `${request.kind} is remote-only.`);
17
+ if (request.requested === 'remote' && localOnly)
18
+ return sourceError('SOURCE_NOT_SUPPORTED', `${request.kind} is local-only.`);
19
+ if (request.requested !== 'auto')
20
+ return request.requested;
21
+ if (remoteOnly)
22
+ return 'remote';
23
+ if (localOnly)
24
+ return 'local';
25
+ if (local.exists && local.coversWindow)
26
+ return 'local';
27
+ if ((request.symbolCount ?? 1) === 1)
28
+ return 'remote';
29
+ return sourceError('DATA_INITIALIZATION_REQUIRED', 'Bulk history requires covered local data.');
30
+ }
31
+ //# sourceMappingURL=source-policy.js.map
@@ -0,0 +1,2 @@
1
+ export declare function cleanManagedCache(cachePath: string, managedRoot: string): Promise<void>;
2
+ //# sourceMappingURL=data-clean.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { CliError } from '../../contracts/errors.js';
4
+ export async function cleanManagedCache(cachePath, managedRoot) {
5
+ const target = path.resolve(cachePath);
6
+ const root = path.resolve(managedRoot);
7
+ if (target !== root && !target.startsWith(`${root}${path.sep}`))
8
+ throw new CliError({
9
+ code: 'PATH_OUTSIDE_MANAGED_ROOT',
10
+ category: 'validation',
11
+ message: 'The cleanup target is outside the managed cache root.',
12
+ hint: 'Only pass paths inside the CLI-managed cache directory.',
13
+ retryable: false,
14
+ exitCode: 2,
15
+ });
16
+ await rm(target, { recursive: true, force: true });
17
+ }
18
+ //# sourceMappingURL=data-clean.js.map
@@ -0,0 +1,4 @@
1
+ import type { DuckDBConnection } from '@duckdb/node-api';
2
+ import { type ParquetBundle } from '../../infrastructure/duckdb/importer.js';
3
+ export declare function initializeData(connection: DuckDBConnection, bundle: ParquetBundle): Promise<number>;
4
+ //# sourceMappingURL=data-init.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { importParquetBundle } from '../../infrastructure/duckdb/importer.js';
2
+ import { applyMigrations } from '../../infrastructure/duckdb/migrations.js';
3
+ import { rebuildAdjustmentFactors } from '../../infrastructure/duckdb/factors.js';
4
+ export async function initializeData(connection, bundle) {
5
+ await applyMigrations(connection);
6
+ await importParquetBundle(connection, bundle);
7
+ return rebuildAdjustmentFactors(connection);
8
+ }
9
+ //# sourceMappingURL=data-init.js.map
@@ -0,0 +1,2 @@
1
+ export declare function removeDatabase(databasePath: string, allowedExactPath: string, confirmed: boolean): Promise<void>;
2
+ //# sourceMappingURL=data-remove.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { CliError } from '../../contracts/errors.js';
4
+ export async function removeDatabase(databasePath, allowedExactPath, confirmed) {
5
+ const target = path.resolve(databasePath);
6
+ const allowed = path.resolve(allowedExactPath);
7
+ if (target !== allowed)
8
+ throw new CliError({
9
+ code: 'PATH_NOT_CONFIRMED',
10
+ category: 'validation',
11
+ message: 'The database path does not match the confirmed removal target.',
12
+ hint: 'Run `data remove --plan` and confirm the exact path before deleting.',
13
+ retryable: false,
14
+ exitCode: 2,
15
+ });
16
+ if (!confirmed)
17
+ throw new CliError({
18
+ code: 'CONFIRMATION_REQUIRED',
19
+ category: 'validation',
20
+ message: 'Database removal requires explicit confirmation.',
21
+ hint: 'Re-run with --yes after reviewing the removal plan.',
22
+ retryable: false,
23
+ exitCode: 2,
24
+ });
25
+ await rm(target, { force: true });
26
+ }
27
+ //# sourceMappingURL=data-remove.js.map
@@ -0,0 +1,5 @@
1
+ import type { DuckDBConnection } from '@duckdb/node-api';
2
+ export declare function repairData(connection: DuckDBConnection): Promise<{
3
+ factorRows: number;
4
+ }>;
5
+ //# sourceMappingURL=data-repair.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { rebuildAdjustmentFactors } from '../../infrastructure/duckdb/factors.js';
2
+ export async function repairData(connection) {
3
+ return { factorRows: await rebuildAdjustmentFactors(connection) };
4
+ }
5
+ //# sourceMappingURL=data-repair.js.map
@@ -0,0 +1,25 @@
1
+ export type SyncDecision = 'SKIP' | 'INCREMENTAL' | 'FULL';
2
+ export interface LocalSyncState {
3
+ maxDate: string | null;
4
+ releaseId: string | null;
5
+ }
6
+ export interface RemoteReleaseState {
7
+ latestDate: string;
8
+ releaseId: string;
9
+ lagTradingDays: number;
10
+ }
11
+ export declare function chooseSyncDecision(local: LocalSyncState, remote: RemoteReleaseState, maxIncrementalLag?: number): SyncDecision;
12
+ import type { DuckDBConnection } from '@duckdb/node-api';
13
+ export interface FuyaoSyncOptions {
14
+ baseUrl: string;
15
+ apiKey: string;
16
+ cacheDir: string;
17
+ now?: Date;
18
+ }
19
+ export declare function syncDataFromFuyao(connection: DuckDBConnection, options: FuyaoSyncOptions): Promise<{
20
+ decision: SyncDecision;
21
+ releaseId: string;
22
+ factorRows: number;
23
+ quality: unknown;
24
+ }>;
25
+ //# sourceMappingURL=data-sync.d.ts.map
@@ -0,0 +1,55 @@
1
+ export function chooseSyncDecision(local, remote, maxIncrementalLag = 5) {
2
+ if (local.maxDate === null)
3
+ return 'FULL';
4
+ if (local.maxDate >= remote.latestDate && local.releaseId === remote.releaseId)
5
+ return 'SKIP';
6
+ return remote.lagTradingDays <= maxIncrementalLag ? 'INCREMENTAL' : 'FULL';
7
+ }
8
+ import { fetchFuyaoDump } from '../../infrastructure/duckdb/dump-client.js';
9
+ import { importParquetBundle } from '../../infrastructure/duckdb/importer.js';
10
+ import { applyMigrations } from '../../infrastructure/duckdb/migrations.js';
11
+ import { rebuildAdjustmentFactors } from '../../infrastructure/duckdb/factors.js';
12
+ import { validateDatabase } from '../../infrastructure/duckdb/quality.js';
13
+ async function scalar(connection, sql) {
14
+ const reader = await connection.runAndReadAll(sql);
15
+ const value = reader.getRowsJson()[0]?.[0];
16
+ return value === null || value === undefined ? null : String(value);
17
+ }
18
+ export async function syncDataFromFuyao(connection, options) {
19
+ await applyMigrations(connection);
20
+ const maxDate = await scalar(connection, 'SELECT max(date)::VARCHAR FROM raw_kline_daily');
21
+ const previousRelease = await scalar(connection, "SELECT value FROM _meta WHERE key='last_kline_release_id'");
22
+ const now = options.now ?? new Date();
23
+ const lagDays = maxDate === null
24
+ ? Number.POSITIVE_INFINITY
25
+ : Math.floor((now.getTime() - Date.parse(`${maxDate}T00:00:00Z`)) / 86_400_000);
26
+ const kind = maxDate === null || lagDays > 14 ? 'daily-k' : 'daily-k-10d';
27
+ const kline = await fetchFuyaoDump({ ...options, kind });
28
+ if (previousRelease === kline.releaseId) {
29
+ return {
30
+ decision: 'SKIP',
31
+ releaseId: kline.releaseId,
32
+ factorRows: 0,
33
+ quality: await validateDatabase(connection),
34
+ };
35
+ }
36
+ const events = await fetchFuyaoDump({ ...options, kind: 'adjustment-factors' });
37
+ const decision = kind === 'daily-k' ? 'FULL' : 'INCREMENTAL';
38
+ const batchId = `${decision.toLowerCase()}-${Date.now()}`;
39
+ await importParquetBundle(connection, {
40
+ klinePath: kline.path,
41
+ eventsPath: events.path,
42
+ batchId,
43
+ source: `fuyao:${kline.releaseId}`,
44
+ mode: decision,
45
+ });
46
+ const factorRows = await rebuildAdjustmentFactors(connection);
47
+ await connection.run("INSERT OR REPLACE INTO _meta VALUES ('last_kline_release_id',$kline),('last_adjustment_release_id',$events),('last_sync_mode',$mode)", { kline: kline.releaseId, events: events.releaseId, mode: decision });
48
+ return {
49
+ decision,
50
+ releaseId: kline.releaseId,
51
+ factorRows,
52
+ quality: await validateDatabase(connection),
53
+ };
54
+ }
55
+ //# sourceMappingURL=data-sync.js.map
@@ -0,0 +1,4 @@
1
+ import type { DuckDBConnection } from '@duckdb/node-api';
2
+ export declare function queryReadOnly(connection: DuckDBConnection, sql: string): Promise<Record<string, unknown>[]>;
3
+ export declare function exportQuery(connection: DuckDBConnection, sql: string, outputPath: string, format: 'ndjson' | 'csv' | 'parquet'): Promise<number>;
4
+ //# sourceMappingURL=local-query.d.ts.map