@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
@@ -0,0 +1,97 @@
1
+ import { localizeText } from '../../cli/i18n.js';
2
+ import { successEnvelope } from '../../contracts/envelope.js';
3
+ import { exportQuery, queryReadOnly } from '../../application/use-cases/local-query.js';
4
+ import { openDatabase } from '../../infrastructure/duckdb/connection.js';
5
+ import { renderResult } from '../../output/renderer.js';
6
+ import { chooseSource } from '../../application/source-policy.js';
7
+ import { CliError } from '../../contracts/errors.js';
8
+ function quote(value) {
9
+ return value.replaceAll("'", "''");
10
+ }
11
+ function assertDate(value) {
12
+ if (!/^\d{4}-\d{2}-\d{2}$/u.test(value))
13
+ throw new CliError({
14
+ code: 'CLI_BAD_ARGUMENT',
15
+ category: 'validation',
16
+ message: 'dates must use YYYY-MM-DD',
17
+ hint: 'Pass dates like 2026-07-09.',
18
+ retryable: false,
19
+ exitCode: 2,
20
+ });
21
+ }
22
+ export function registerLocalMarketCommands(program, context, fallbackDbPath) {
23
+ const market = program.commands.find((command) => command.name() === 'market');
24
+ if (market === undefined)
25
+ throw new Error('market command group is not registered');
26
+ const panel = market
27
+ .command('panel')
28
+ .description(localizeText(context.language, 'Export a local full-market panel'))
29
+ .requiredOption('--start <date>')
30
+ .requiredOption('--end <date>')
31
+ .requiredOption('--output <path>')
32
+ .option('--file-format <format>', localizeText(context.language, 'ndjson, csv, or parquet'), 'parquet');
33
+ panel.action(async () => {
34
+ const options = panel.opts();
35
+ assertDate(options.start);
36
+ assertDate(options.end);
37
+ chooseSource({
38
+ kind: 'panel',
39
+ requested: panel.optsWithGlobals().source ?? 'auto',
40
+ }, { exists: true, coversWindow: true });
41
+ if (!['ndjson', 'csv', 'parquet'].includes(options.fileFormat))
42
+ throw new CliError({
43
+ code: 'CLI_BAD_ARGUMENT',
44
+ category: 'validation',
45
+ message: '--file-format must be ndjson, csv, or parquet',
46
+ hint: 'Use --file-format ndjson, --file-format csv, or --file-format parquet.',
47
+ retryable: false,
48
+ exitCode: 2,
49
+ });
50
+ const globals = panel.optsWithGlobals();
51
+ const opened = await openDatabase(globals.db ?? process.env.HITHINK_FINANCE_DB_PATH ?? fallbackDbPath);
52
+ try {
53
+ const sql = `SELECT * FROM v_daily_qfq WHERE date BETWEEN DATE '${options.start}' AND DATE '${options.end}' ORDER BY date,thscode`;
54
+ const count = await exportQuery(opened.connection, sql, options.output, options.fileFormat);
55
+ await renderResult(successEnvelope('market.panel', { path: options.output, format: options.fileFormat }, { requestId: context.requestId, source: 'local', count }), context);
56
+ }
57
+ finally {
58
+ opened.close();
59
+ }
60
+ });
61
+ const factors = market
62
+ .command('adjustment-factors')
63
+ .description(localizeText(context.language, 'Query local daily adjustment factors'))
64
+ .requiredOption('--thscode <code>')
65
+ .option('--start <date>')
66
+ .option('--end <date>');
67
+ factors.action(async () => {
68
+ const options = factors.opts();
69
+ if (options.start !== undefined)
70
+ assertDate(options.start);
71
+ if (options.end !== undefined)
72
+ assertDate(options.end);
73
+ chooseSource({
74
+ kind: 'factors',
75
+ requested: factors.optsWithGlobals().source ?? 'auto',
76
+ }, { exists: true, coversWindow: true });
77
+ const filters = [`thscode='${quote(options.thscode)}'`];
78
+ if (options.start !== undefined)
79
+ filters.push(`date >= DATE '${options.start}'`);
80
+ if (options.end !== undefined)
81
+ filters.push(`date <= DATE '${options.end}'`);
82
+ const globals = factors.optsWithGlobals();
83
+ const opened = await openDatabase(globals.db ?? process.env.HITHINK_FINANCE_DB_PATH ?? fallbackDbPath);
84
+ try {
85
+ const rows = await queryReadOnly(opened.connection, `SELECT * FROM calc_adjust_factor_daily WHERE ${filters.join(' AND ')} ORDER BY date`);
86
+ await renderResult(successEnvelope('market.adjustment-factors', rows, {
87
+ requestId: context.requestId,
88
+ source: 'local',
89
+ count: rows.length,
90
+ }), context);
91
+ }
92
+ finally {
93
+ opened.close();
94
+ }
95
+ });
96
+ }
97
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1,11 @@
1
+ import { type Command } from 'commander';
2
+ import type { CliContext } from '../cli/context.js';
3
+ import type { RemoteCapabilityDescriptor } from '../contracts/remote-capabilities.js';
4
+ import type { ApiKeyAuthProvider } from '../infrastructure/credentials/api-key-provider.js';
5
+ export interface RemoteCommandDependencies {
6
+ authProvider: ApiKeyAuthProvider;
7
+ baseUrl: string;
8
+ defaultDbPath: string;
9
+ }
10
+ export declare function registerRemoteCapabilityGroup(program: Command, groupName: string, capabilities: readonly RemoteCapabilityDescriptor[], context: CliContext, dependencies: RemoteCommandDependencies): void;
11
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1,195 @@
1
+ import { InvalidArgumentError, Option } from 'commander';
2
+ import { localizeText } from '../cli/i18n.js';
3
+ import { successEnvelope } from '../contracts/envelope.js';
4
+ import { CliError } from '../contracts/errors.js';
5
+ import { executeRemoteQuery } from '../application/use-cases/remote-query.js';
6
+ import { FuyaoClient } from '../infrastructure/fuyao/client.js';
7
+ import { renderResult } from '../output/renderer.js';
8
+ import { chooseSource } from '../application/source-policy.js';
9
+ import { openDatabase } from '../infrastructure/duckdb/connection.js';
10
+ import { getHistory } from '../application/use-cases/market-history.js';
11
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
12
+ import path from 'node:path';
13
+ import { readStdin } from '../infrastructure/filesystem/stdin.js';
14
+ import { renderJson } from '../output/json.js';
15
+ function integer(value) {
16
+ const parsed = Number(value);
17
+ if (!Number.isSafeInteger(parsed))
18
+ throw new InvalidArgumentError('expected a safe integer');
19
+ return parsed;
20
+ }
21
+ function addDescriptorOption(command, descriptor, context) {
22
+ const option = new Option(descriptor.flags, localizeText(context.language, descriptor.description));
23
+ if (descriptor.type === 'integer')
24
+ option.argParser(integer);
25
+ if (descriptor.flags.startsWith('--thscodes '))
26
+ option.argParser((value, previous) => previous === undefined ? value : `${previous},${value}`);
27
+ if (descriptor.choices !== undefined)
28
+ option.choices([...descriptor.choices]);
29
+ if (descriptor.defaultValue !== undefined)
30
+ option.default(descriptor.defaultValue);
31
+ if (descriptor.required === true)
32
+ option.makeOptionMandatory();
33
+ command.addOption(option);
34
+ }
35
+ function normalizedCodes(...inputs) {
36
+ const codes = inputs
37
+ .flatMap((input) => input?.split(/[\s,]+/u) ?? [])
38
+ .map((code) => code.trim())
39
+ .filter(Boolean);
40
+ return codes.length === 0 ? undefined : [...new Set(codes)].join(',');
41
+ }
42
+ async function writeOutputEnvelope(outputPath, envelope) {
43
+ const absolutePath = path.resolve(process.cwd(), outputPath);
44
+ await mkdir(path.dirname(absolutePath), { recursive: true });
45
+ await writeFile(absolutePath, renderJson(envelope), 'utf8');
46
+ return absolutePath;
47
+ }
48
+ export function registerRemoteCapabilityGroup(program, groupName, capabilities, context, dependencies) {
49
+ const group = program
50
+ .command(groupName)
51
+ .description(localizeText(context.language, `${groupName} remote data commands`));
52
+ for (const capability of capabilities) {
53
+ const leaf = group
54
+ .command(capability.command[1])
55
+ .description(localizeText(context.language, capability.description));
56
+ for (const option of capability.options)
57
+ addDescriptorOption(leaf, option, context);
58
+ leaf.option('--output <path>', localizeText(context.language, 'write the full JSON response envelope to a file'));
59
+ const acceptsMultipleCodes = capability.options.some((option) => option.flags.startsWith('--thscodes '));
60
+ if (acceptsMultipleCodes) {
61
+ leaf.option('--codes-file <path>', localizeText(context.language, 'read thscodes from a comma/newline-delimited file'));
62
+ leaf.option('--codes-stdin', localizeText(context.language, 'read thscodes from stdin'));
63
+ }
64
+ leaf.action(async () => {
65
+ const raw = leaf.opts();
66
+ const stdinGlobals = leaf.optsWithGlobals();
67
+ if (raw.codesStdin === true && stdinGlobals.apiKeyStdin === true)
68
+ throw new CliError({
69
+ code: 'CLI_CONFLICTING_ARGUMENTS',
70
+ category: 'validation',
71
+ message: '--codes-stdin and --api-key-stdin cannot be used together.',
72
+ hint: 'Store the key with `auth login --api-key-stdin`, then pipe codes.',
73
+ retryable: false,
74
+ exitCode: 2,
75
+ });
76
+ if (raw.codesFile !== undefined && raw.codesStdin === true)
77
+ throw new CliError({
78
+ code: 'CLI_CONFLICTING_ARGUMENTS',
79
+ category: 'validation',
80
+ message: '--codes-file and --codes-stdin cannot be used together.',
81
+ hint: 'Choose one batch-code input method.',
82
+ retryable: false,
83
+ exitCode: 2,
84
+ });
85
+ const fileCodes = raw.codesFile === undefined ? undefined : await readFile(raw.codesFile, 'utf8');
86
+ const stdinCodes = raw.codesStdin === true ? await readStdin() : undefined;
87
+ const schemaInput = { ...raw };
88
+ delete schemaInput.codesFile;
89
+ delete schemaInput.codesStdin;
90
+ delete schemaInput.output;
91
+ const combinedCodes = normalizedCodes(raw.thscodes, fileCodes, stdinCodes);
92
+ if (combinedCodes !== undefined)
93
+ schemaInput.thscodes = combinedCodes;
94
+ const parsed = capability.inputSchema.safeParse(schemaInput);
95
+ if (!parsed.success) {
96
+ throw new CliError({
97
+ code: 'CLI_BAD_ARGUMENT',
98
+ category: 'validation',
99
+ message: parsed.error.issues.map((issue) => issue.message).join('; '),
100
+ hint: `Run \`hithink-finance ${capability.command.join(' ')} --help\` and correct the arguments.`,
101
+ retryable: false,
102
+ exitCode: 2,
103
+ });
104
+ }
105
+ const globals = leaf.optsWithGlobals();
106
+ const explicitApiKey = globals.apiKeyStdin === true ? (await readStdin()).trim() : globals.apiKey;
107
+ const requested = globals.source ?? 'auto';
108
+ if (capability.id === 'market.history') {
109
+ const input = parsed.data;
110
+ const databasePath = globals.db ?? process.env.HITHINK_FINANCE_DB_PATH ?? dependencies.defaultDbPath;
111
+ const exists = await stat(databasePath)
112
+ .then((value) => value.isFile())
113
+ .catch(() => false);
114
+ let coversWindow = false;
115
+ if (exists) {
116
+ const opened = await openDatabase(databasePath);
117
+ try {
118
+ const start = new Date(input.startMs).toISOString().slice(0, 10);
119
+ const end = new Date(input.endMs).toISOString().slice(0, 10);
120
+ const coverage = await opened.connection.runAndReadAll('SELECT min(date)::VARCHAR,max(date)::VARCHAR FROM raw_kline_daily');
121
+ const row = coverage.getRowsJson()[0];
122
+ coversWindow = String(row?.[0] ?? '') <= start && String(row?.[1] ?? '') >= end;
123
+ const source = chooseSource({ kind: 'history', requested, symbolCount: 1 }, { exists, coversWindow });
124
+ if (source === 'local') {
125
+ const rows = await getHistory(opened.connection, {
126
+ thscodes: [input.thscode],
127
+ start,
128
+ end,
129
+ adjust: input.adjust,
130
+ });
131
+ const envelope = successEnvelope(capability.id, rows, {
132
+ source: 'local',
133
+ requestId: context.requestId,
134
+ count: rows.length,
135
+ });
136
+ if (raw.output !== undefined) {
137
+ const outputPath = await writeOutputEnvelope(raw.output, envelope);
138
+ await renderResult(successEnvelope(capability.id, { path: outputPath, format: 'json', count: rows.length }, {
139
+ source: 'local',
140
+ requestId: context.requestId,
141
+ count: rows.length,
142
+ }), context);
143
+ }
144
+ else {
145
+ await renderResult(envelope, context);
146
+ }
147
+ return;
148
+ }
149
+ }
150
+ finally {
151
+ opened.close();
152
+ }
153
+ }
154
+ else {
155
+ chooseSource({ kind: 'history', requested, symbolCount: 1 }, { exists, coversWindow });
156
+ }
157
+ }
158
+ else if (requested === 'local') {
159
+ chooseSource({
160
+ kind: capability.command[0] === 'financials'
161
+ ? 'financials'
162
+ : capability.command[0] === 'index'
163
+ ? 'index'
164
+ : capability.command[0] === 'special'
165
+ ? 'special'
166
+ : capability.command[1] === 'calendar'
167
+ ? 'calendar'
168
+ : 'snapshot',
169
+ requested,
170
+ }, { exists: false, coversWindow: false });
171
+ }
172
+ const auth = await dependencies.authProvider.resolve(globals.profile ?? 'default', explicitApiKey);
173
+ const result = await executeRemoteQuery(capability, parsed.data, new FuyaoClient({ baseUrl: dependencies.baseUrl, auth }));
174
+ const count = result.data !== null &&
175
+ typeof result.data === 'object' &&
176
+ 'item' in result.data &&
177
+ Array.isArray(result.data.item)
178
+ ? result.data.item.length
179
+ : undefined;
180
+ const meta = {
181
+ source: 'remote',
182
+ requestId: result.requestId ?? context.requestId,
183
+ ...(count === undefined ? {} : { count }),
184
+ };
185
+ const envelope = successEnvelope(capability.id, result.data, meta);
186
+ if (raw.output !== undefined) {
187
+ const outputPath = await writeOutputEnvelope(raw.output, envelope);
188
+ await renderResult(successEnvelope(capability.id, { path: outputPath, format: 'json', count }, meta), context);
189
+ return;
190
+ }
191
+ await renderResult(envelope, context);
192
+ });
193
+ }
194
+ }
195
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1,4 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ export declare function registerSkillsCommands(program: Command, context: CliContext, packageRoot: string): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { localizeText } from '../../cli/i18n.js';
2
+ import { successEnvelope } from '../../contracts/envelope.js';
3
+ import { CliError } from '../../contracts/errors.js';
4
+ import { removeSkills, syncSkills } from '../../infrastructure/skills/installer.js';
5
+ import { renderResult } from '../../output/renderer.js';
6
+ export function registerSkillsCommands(program, context, packageRoot) {
7
+ const skills = program
8
+ .command('skills')
9
+ .description(localizeText(context.language, 'Manage Agent Skills'));
10
+ skills
11
+ .command('status')
12
+ .action(async () => renderResult(successEnvelope('skills.status', { canonical: `${packageRoot}/skills`, managed: true }, { requestId: context.requestId }), context));
13
+ skills
14
+ .command('sync')
15
+ .option('--repair')
16
+ .action(async () => {
17
+ const result = await syncSkills(packageRoot);
18
+ if (result.code !== 0)
19
+ throw new CliError({
20
+ code: 'SKILLS_SYNC_PARTIAL',
21
+ category: 'internal',
22
+ message: 'One or more Agent Skill targets failed to synchronize.',
23
+ hint: 'Run `hithink-finance skills sync --repair` after checking Agent installations.',
24
+ retryable: true,
25
+ exitCode: 6,
26
+ });
27
+ await renderResult(successEnvelope('skills.sync', { synchronized: true }, { requestId: context.requestId }), context);
28
+ });
29
+ skills.command('remove').action(async () => {
30
+ const result = await removeSkills(packageRoot);
31
+ if (result.code !== 0)
32
+ throw new CliError({
33
+ code: 'SKILLS_REMOVE_PARTIAL',
34
+ category: 'internal',
35
+ message: 'One or more managed Agent Skills could not be removed.',
36
+ hint: 'Check Agent discovery directories and retry `hithink-finance skills remove`.',
37
+ retryable: true,
38
+ exitCode: 6,
39
+ });
40
+ await renderResult(successEnvelope('skills.remove', { removed: true }, { requestId: context.requestId }), context);
41
+ });
42
+ }
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ import { type RemoteCommandDependencies } from '../remote.js';
4
+ export declare function registerSpecialCommands(program: Command, context: CliContext, dependencies: RemoteCommandDependencies): void;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { remoteCapabilities } from '../../contracts/remote-capabilities.js';
2
+ import { registerRemoteCapabilityGroup } from '../remote.js';
3
+ export function registerSpecialCommands(program, context, dependencies) {
4
+ registerRemoteCapabilityGroup(program, 'special', remoteCapabilities.filter((item) => item.command[0] === 'special'), context, dependencies);
5
+ }
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ import { type RemoteCommandDependencies } from '../remote.js';
4
+ export declare function registerSymbolCommands(program: Command, context: CliContext, dependencies: RemoteCommandDependencies): void;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { remoteCapabilities } from '../../contracts/remote-capabilities.js';
2
+ import { registerRemoteCapabilityGroup } from '../remote.js';
3
+ export function registerSymbolCommands(program, context, dependencies) {
4
+ registerRemoteCapabilityGroup(program, 'symbol', remoteCapabilities.filter((item) => item.command[0] === 'symbol'), context, dependencies);
5
+ }
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ export declare function registerCapabilitiesCommand(program: Command, context: CliContext): void;
4
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { localizeText } from '../../cli/i18n.js';
2
+ import { remoteCapabilities } from '../../contracts/remote-capabilities.js';
3
+ import { localCapabilities } from '../../contracts/local-capabilities.js';
4
+ import { successEnvelope } from '../../contracts/envelope.js';
5
+ import { renderResult } from '../../output/renderer.js';
6
+ export function registerCapabilitiesCommand(program, context) {
7
+ program
8
+ .command('capabilities')
9
+ .description(localizeText(context.language, 'List machine-readable command capabilities'))
10
+ .action(async () => {
11
+ await renderResult(successEnvelope('capabilities', [
12
+ ...remoteCapabilities.map(({ id, command, endpoint, paging, window }) => ({
13
+ id,
14
+ command,
15
+ endpoint,
16
+ paging,
17
+ window,
18
+ source: 'remote',
19
+ })),
20
+ ...localCapabilities.map((item) => ({ ...item, source: 'local' })),
21
+ ], { requestId: context.requestId }), context);
22
+ });
23
+ }
24
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1,4 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ export declare function registerDoctorCommand(program: Command, context: CliContext): void;
4
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { localizeText } from '../../cli/i18n.js';
2
+ import { successEnvelope } from '../../contracts/envelope.js';
3
+ import { renderResult } from '../../output/renderer.js';
4
+ export function registerDoctorCommand(program, context) {
5
+ program
6
+ .command('doctor')
7
+ .description(localizeText(context.language, 'Run local environment diagnostics'))
8
+ .action(async () => renderResult(successEnvelope('doctor', {
9
+ nodeVersion: process.versions.node,
10
+ platform: process.platform,
11
+ arch: process.arch,
12
+ apiKeyPresent: Boolean(process.env.HITHINK_FINANCE_API_KEY),
13
+ }, { requestId: context.requestId }), context));
14
+ }
15
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,4 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ export declare function registerSchemaCommand(program: Command, context: CliContext): void;
4
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { localizeText } from '../../cli/i18n.js';
2
+ import { remoteCapabilities, } from '../../contracts/remote-capabilities.js';
3
+ import { localCapabilities } from '../../contracts/local-capabilities.js';
4
+ import { successEnvelope } from '../../contracts/envelope.js';
5
+ import { CliError } from '../../contracts/errors.js';
6
+ import { renderResult } from '../../output/renderer.js';
7
+ const remoteOutputOption = {
8
+ flags: '--output <path>',
9
+ description: 'write the full JSON response envelope to a file',
10
+ type: 'string',
11
+ };
12
+ export function registerSchemaCommand(program, context) {
13
+ program
14
+ .command('schema <command>')
15
+ .description(localizeText(context.language, 'Show a command contract'))
16
+ .action(async (id) => {
17
+ const capability = [...remoteCapabilities, ...localCapabilities].find((item) => item.id === id || item.command.join('.') === id);
18
+ if (capability === undefined)
19
+ throw new CliError({
20
+ code: 'CLI_UNKNOWN_COMMAND_SCHEMA',
21
+ category: 'validation',
22
+ message: `No schema exists for ${id}.`,
23
+ hint: 'Run capabilities first.',
24
+ retryable: false,
25
+ exitCode: 2,
26
+ });
27
+ await renderResult(successEnvelope('schema', isRemote(capability)
28
+ ? {
29
+ id: capability.id,
30
+ command: capability.command,
31
+ options: [...capability.options, remoteOutputOption],
32
+ paging: capability.paging,
33
+ window: capability.window,
34
+ }
35
+ : {
36
+ id: capability.id,
37
+ command: capability.command,
38
+ description: capability.description,
39
+ options: capability.options,
40
+ source: 'local',
41
+ }, { requestId: context.requestId }), context);
42
+ });
43
+ }
44
+ function isRemote(capability) {
45
+ return 'endpoint' in capability;
46
+ }
47
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,11 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ import type { PackageMetadata } from '../../cli/program.js';
4
+ import type { PlatformPaths } from '../../infrastructure/filesystem/platform-paths.js';
5
+ import type { ApiKeyAuthProvider } from '../../infrastructure/credentials/api-key-provider.js';
6
+ export declare function registerUninstallCommand(program: Command, context: CliContext, metadata: PackageMetadata, dependencies: {
7
+ packageRoot: string;
8
+ platformPaths: PlatformPaths;
9
+ authProvider: ApiKeyAuthProvider;
10
+ }): void;
11
+ //# sourceMappingURL=uninstall.d.ts.map
@@ -0,0 +1,68 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import { localizeText } from '../../cli/i18n.js';
3
+ import { successEnvelope } from '../../contracts/envelope.js';
4
+ import { CliError } from '../../contracts/errors.js';
5
+ import { removeSkills } from '../../infrastructure/skills/installer.js';
6
+ import { uninstallGlobalPackage } from '../../infrastructure/updater/install.js';
7
+ import { renderResult } from '../../output/renderer.js';
8
+ export function registerUninstallCommand(program, context, metadata, dependencies) {
9
+ const command = program
10
+ .command('uninstall')
11
+ .description(localizeText(context.language, 'Plan or run CLI uninstall cleanup'))
12
+ .option('--plan', localizeText(context.language, 'Show the uninstall plan without deleting anything'))
13
+ .option('--purge-data', localizeText(context.language, 'Delete local data during uninstall'))
14
+ .option('--purge-config', localizeText(context.language, 'Delete local configuration during uninstall'))
15
+ .option('--purge-credentials', localizeText(context.language, 'Delete all CLI-managed API key credentials'));
16
+ command.action(async () => {
17
+ const options = command.opts();
18
+ const globals = command.optsWithGlobals();
19
+ const npmCommand = ['npm', 'uninstall', '-g', metadata.name];
20
+ if (options.plan !== true) {
21
+ if (globals.yes !== true)
22
+ throw new CliError({
23
+ code: 'CONFIRMATION_REQUIRED',
24
+ category: 'validation',
25
+ message: 'Uninstall requires explicit confirmation.',
26
+ hint: 'Review `uninstall --plan`, then rerun with `--yes`.',
27
+ retryable: false,
28
+ exitCode: 2,
29
+ });
30
+ const skillResult = await removeSkills(dependencies.packageRoot);
31
+ if (skillResult.code !== 0)
32
+ throw new CliError({
33
+ code: 'UNINSTALL_SKILLS_PARTIAL',
34
+ category: 'internal',
35
+ message: 'Managed Skills could not be fully removed.',
36
+ hint: 'Run `hithink-finance skills remove` and retry.',
37
+ retryable: true,
38
+ exitCode: 6,
39
+ });
40
+ if (options.purgeData === true)
41
+ await rm(dependencies.platformPaths.dataDir, { recursive: true, force: true });
42
+ if (options.purgeConfig === true)
43
+ await rm(dependencies.platformPaths.configDir, { recursive: true, force: true });
44
+ if (options.purgeCredentials === true)
45
+ await dependencies.authProvider.logoutAll();
46
+ const npm = process.env.HITHINK_FINANCE_NPM_EXECUTABLE ??
47
+ (process.platform === 'win32' ? 'npm.cmd' : 'npm');
48
+ const code = await uninstallGlobalPackage(npm, metadata.name);
49
+ if (code !== 0)
50
+ throw new CliError({
51
+ code: 'UNINSTALL_NPM_FAILED',
52
+ category: 'internal',
53
+ message: 'npm global uninstall failed.',
54
+ hint: `Run ${npmCommand.join(' ')} manually.`,
55
+ retryable: true,
56
+ exitCode: 1,
57
+ });
58
+ }
59
+ await renderResult(successEnvelope('uninstall', {
60
+ planned: options.plan === true,
61
+ purgeData: options.purgeData === true,
62
+ purgeConfig: options.purgeConfig === true,
63
+ purgeCredentials: options.purgeCredentials === true,
64
+ npmCommand,
65
+ }, { requestId: context.requestId }), context);
66
+ });
67
+ }
68
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1,11 @@
1
+ import type { Command } from 'commander';
2
+ import type { CliContext } from '../../cli/context.js';
3
+ import type { PackageMetadata } from '../../cli/program.js';
4
+ import type { ResolvedConfig } from '../../application/config.js';
5
+ import type { PlatformPaths } from '../../infrastructure/filesystem/platform-paths.js';
6
+ export declare function registerUpdateCommand(program: Command, context: CliContext, metadata: PackageMetadata, dependencies: {
7
+ packageRoot: string;
8
+ platformPaths: PlatformPaths;
9
+ resolvedConfig: ResolvedConfig;
10
+ }): void;
11
+ //# sourceMappingURL=update.d.ts.map