@dsh-ft/dsh-fft-settings 0.0.0-stage → 0.5.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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +392 -2
  3. package/cordis.patch.yml +18 -0
  4. package/docs/compatibility-dsh-0.1.6-alpha.1.md +58 -0
  5. package/docs/extension-service-design.md +351 -0
  6. package/docs/extension-service-sequences.html +545 -0
  7. package/docs/ftshare-settings-er.html +139 -0
  8. package/docs/ftshare-settings-er.svg +113 -0
  9. package/docs/ftshare-settings-results-er.html +148 -0
  10. package/docs/ftshare-settings-results-er.svg +122 -0
  11. package/docs/kline-integration.md +258 -0
  12. package/lib/agent.d.ts +24 -0
  13. package/lib/agent.js +26 -0
  14. package/lib/brand.d.ts +53 -0
  15. package/lib/brand.js +50 -0
  16. package/lib/client.d.ts +8 -0
  17. package/lib/client.js +822 -0
  18. package/lib/client.js.map +1 -0
  19. package/lib/contract.d.ts +14 -0
  20. package/lib/contract.js +14 -0
  21. package/lib/core/client-transport.d.ts +15 -0
  22. package/lib/core/client-transport.js +31 -0
  23. package/lib/core/contract.d.ts +62 -0
  24. package/lib/core/contract.js +28 -0
  25. package/lib/core/module-registry.d.ts +54 -0
  26. package/lib/core/module-registry.js +68 -0
  27. package/lib/core/rpc-core.d.ts +69 -0
  28. package/lib/core/rpc-core.js +88 -0
  29. package/lib/core/shell.d.ts +59 -0
  30. package/lib/core/shell.js +28 -0
  31. package/lib/core/styles.d.ts +31 -0
  32. package/lib/core/styles.js +43 -0
  33. package/lib/core/transport.d.ts +19 -0
  34. package/lib/core/transport.js +80 -0
  35. package/lib/default-preset.d.ts +52 -0
  36. package/lib/default-preset.js +121 -0
  37. package/lib/features/api-key/card.d.ts +24 -0
  38. package/lib/features/api-key/card.js +39 -0
  39. package/lib/features/api-key/client-api.d.ts +4 -0
  40. package/lib/features/api-key/client-api.js +11 -0
  41. package/lib/features/api-key/contract.d.ts +69 -0
  42. package/lib/features/api-key/contract.js +24 -0
  43. package/lib/features/api-key/host.d.ts +17 -0
  44. package/lib/features/api-key/host.js +32 -0
  45. package/lib/features/api-key/index.d.ts +13 -0
  46. package/lib/features/api-key/index.js +12 -0
  47. package/lib/features/api-key/logging.d.ts +15 -0
  48. package/lib/features/api-key/logging.js +1 -0
  49. package/lib/features/api-key/model.d.ts +24 -0
  50. package/lib/features/api-key/model.js +105 -0
  51. package/lib/features/api-key/probe.d.ts +11 -0
  52. package/lib/features/api-key/probe.js +142 -0
  53. package/lib/features/api-key/rpc.d.ts +4 -0
  54. package/lib/features/api-key/rpc.js +17 -0
  55. package/lib/features/api-key/service.d.ts +21 -0
  56. package/lib/features/api-key/service.js +181 -0
  57. package/lib/features/api-key/styles.d.ts +9 -0
  58. package/lib/features/api-key/styles.js +24 -0
  59. package/lib/features/logs/archive.d.ts +87 -0
  60. package/lib/features/logs/archive.js +156 -0
  61. package/lib/features/logs/card.d.ts +22 -0
  62. package/lib/features/logs/card.js +107 -0
  63. package/lib/features/logs/client-api.d.ts +4 -0
  64. package/lib/features/logs/client-api.js +11 -0
  65. package/lib/features/logs/collector.d.ts +91 -0
  66. package/lib/features/logs/collector.js +258 -0
  67. package/lib/features/logs/config.d.ts +70 -0
  68. package/lib/features/logs/config.js +97 -0
  69. package/lib/features/logs/contract.d.ts +130 -0
  70. package/lib/features/logs/contract.js +67 -0
  71. package/lib/features/logs/export.d.ts +92 -0
  72. package/lib/features/logs/export.js +212 -0
  73. package/lib/features/logs/format.d.ts +42 -0
  74. package/lib/features/logs/format.js +172 -0
  75. package/lib/features/logs/home.d.ts +18 -0
  76. package/lib/features/logs/home.js +39 -0
  77. package/lib/features/logs/host.d.ts +19 -0
  78. package/lib/features/logs/host.js +139 -0
  79. package/lib/features/logs/index.d.ts +12 -0
  80. package/lib/features/logs/index.js +12 -0
  81. package/lib/features/logs/rpc.d.ts +6 -0
  82. package/lib/features/logs/rpc.js +27 -0
  83. package/lib/features/logs/styles.d.ts +9 -0
  84. package/lib/features/logs/styles.js +13 -0
  85. package/lib/guidance.d.ts +41 -0
  86. package/lib/guidance.js +88 -0
  87. package/lib/index.d.ts +67 -0
  88. package/lib/index.js +50 -0
  89. package/lib/rpc.d.ts +10 -0
  90. package/lib/rpc.js +10 -0
  91. package/package.json +86 -4
  92. package/src/agent.ts +31 -0
  93. package/src/brand.ts +79 -0
  94. package/src/client.tsx +21 -0
  95. package/src/contract.ts +14 -0
  96. package/src/core/client-transport.ts +37 -0
  97. package/src/core/contract.ts +57 -0
  98. package/src/core/module-registry.ts +102 -0
  99. package/src/core/rpc-core.ts +107 -0
  100. package/src/core/shell.tsx +81 -0
  101. package/src/core/styles.ts +43 -0
  102. package/src/core/transport.ts +82 -0
  103. package/src/default-preset.ts +151 -0
  104. package/src/features/api-key/card.tsx +77 -0
  105. package/src/features/api-key/client-api.ts +12 -0
  106. package/src/features/api-key/contract.ts +63 -0
  107. package/src/features/api-key/host.ts +36 -0
  108. package/src/features/api-key/index.ts +13 -0
  109. package/src/features/api-key/logging.ts +15 -0
  110. package/src/features/api-key/model.ts +63 -0
  111. package/src/features/api-key/probe.ts +116 -0
  112. package/src/features/api-key/rpc.ts +18 -0
  113. package/src/features/api-key/service.ts +157 -0
  114. package/src/features/api-key/styles.ts +24 -0
  115. package/src/features/logs/archive.ts +230 -0
  116. package/src/features/logs/card.tsx +169 -0
  117. package/src/features/logs/client-api.ts +20 -0
  118. package/src/features/logs/collector.ts +313 -0
  119. package/src/features/logs/config.ts +115 -0
  120. package/src/features/logs/contract.ts +149 -0
  121. package/src/features/logs/export.ts +274 -0
  122. package/src/features/logs/format.ts +188 -0
  123. package/src/features/logs/home.ts +43 -0
  124. package/src/features/logs/host.ts +153 -0
  125. package/src/features/logs/index.ts +12 -0
  126. package/src/features/logs/rpc.ts +29 -0
  127. package/src/features/logs/styles.ts +13 -0
  128. package/src/guidance.ts +114 -0
  129. package/src/index.ts +59 -0
  130. package/src/rpc.ts +10 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `logs` 命名空间的 RPC 方法:只暴露显式登记的三个只读方法,沿用严格 payload 校验。
3
+ */
4
+
5
+ import { LOGS_DEFAULT_PREVIEW_LINES, LOGS_MAX_PREVIEW_LINES, type LogsSettings } from './contract.js'
6
+ import type { SettingsRpcMethods } from '../../core/rpc-core.js'
7
+
8
+ const record = (value: unknown): value is Record<string, unknown> =>
9
+ typeof value === 'object' && value !== null && !Array.isArray(value)
10
+ const empty = (value: unknown): boolean => record(value) && Object.keys(value).length === 0
11
+ /** 只允许 `{ lines?: number }`,且值必须落在预览上限内。 */
12
+ const previewPayload = (value: unknown): boolean => {
13
+ if (!record(value)) return false
14
+ const keys = Object.keys(value)
15
+ if (keys.some(key => key !== 'lines')) return false
16
+ if (value.lines === undefined) return true
17
+ return typeof value.lines === 'number' && Number.isInteger(value.lines) && value.lines >= 1 && value.lines <= LOGS_MAX_PREVIEW_LINES
18
+ }
19
+
20
+ export function createLogsRpcMethods(service: LogsSettings): SettingsRpcMethods {
21
+ return {
22
+ stats: { validate: empty, invoke: () => service.stats() },
23
+ preview: {
24
+ validate: previewPayload,
25
+ invoke: payload => service.preview((payload as { lines?: number }).lines ?? LOGS_DEFAULT_PREVIEW_LINES),
26
+ },
27
+ export: { validate: empty, invoke: () => service.export() },
28
+ }
29
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The `logs` card's own styles, rendered by the card component's `<style>`.
3
+ *
4
+ * These two rules used to live in the core section stylesheet; a card owns
5
+ * everything past the section shell, so they moved here with the card. Every
6
+ * selector stays scoped under `.ftshare-settings`, so a card never styles
7
+ * another card's markup.
8
+ */
9
+ export const LOGS_CARD_CSS = `
10
+ .ftshare-settings select { height:32px; border:0.5px solid var(--ftshare-control); border-radius:var(--dsw-radius-md); padding:0 10px; background:var(--ftshare-input); color:inherit; font:inherit; }
11
+ .ftshare-settings .ftshare-download { display:inline-flex; align-items:center; min-height:32px; padding:0 14px; border:0.5px solid var(--ftshare-control); border-radius:var(--dsw-radius-md); color:inherit; text-decoration:none; cursor:pointer; }
12
+ .ftshare-settings .ftshare-log-preview { max-height:320px; overflow:auto; margin:0 0 12px; padding:12px; background:var(--ftshare-input); border:0.5px solid var(--ftshare-border); border-radius:var(--dsw-radius-lg); font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; line-height:1.5; white-space:pre-wrap; overflow-wrap:anywhere; }
13
+ `
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The FT prompt contract: two ordered system sections and one runtime fact.
3
+ *
4
+ * The identity is not here: the preset that mounts this entry states it in its own
5
+ * `persona` row (order 0), and repeating it as a section would render it twice.
6
+ *
7
+ * Registration is a single {@link installGuidance} call so the text and the
8
+ * mount point stay independent — moving these registrations into an agent preset
9
+ * later changes only the row that calls this module.
10
+ *
11
+ * A section is paid for on every request and any edit invalidates the prompt's
12
+ * KV-cache prefix, so the contract stays short; methodology that needs examples,
13
+ * checklists, or branch flow belongs in a skill instead.
14
+ * @module @dsh-ft/dsh-fft-settings/guidance
15
+ */
16
+
17
+ import type { Context } from '@deepseek-ai/cordis'
18
+
19
+ /**
20
+ * The prompt-registry calls this module makes, declared structurally: the host
21
+ * owns `systemPrompt`, and `inject` guarantees the service exists before `apply`
22
+ * runs, so the package keeps no harness dependency just to name the type.
23
+ */
24
+ interface PromptRegistry {
25
+ section(section: { readonly name: string; readonly order: number; readonly text: string }): () => void
26
+ context(entry: { readonly name: string; readonly order: number; readonly text: () => string }): () => void
27
+ }
28
+
29
+ /** Section name of the capability list. */
30
+ export const FT_SERVICES_SECTION = 'ft:services'
31
+
32
+ /** Section name of the financial-data discipline. */
33
+ export const FT_DATA_POLICY_SECTION = 'ft:data-policy'
34
+
35
+ /** Runtime-context name of the FTShare credential fact. */
36
+ export const FT_CREDENTIALS_CONTEXT = 'ft:credentials'
37
+
38
+ /**
39
+ * Section orders. Consecutive integers keep a third-party section from sorting
40
+ * between two of ours (`1`–`499` is the shared free band), while `6` still lands
41
+ * after the deployment persona (`0`) and before core's plan policy (`500`).
42
+ */
43
+ export const FT_SECTION_ORDERS = { services: 6, dataPolicy: 7 } as const
44
+
45
+ /** Order of the credential fact — `getContextOrder` owns only three named positions. */
46
+ export const FT_CREDENTIALS_ORDER = 130
47
+
48
+ export const FT_SERVICES_TEXT = '行情、K线与指标用 dsh-kline 工具(代码先用 search_symbols 解析);投研报告与组合推荐用 cockpit_* 工具;因子入库与评价用 factor-intake-standard 技能;FTShare API Key 在设置页维护。'
49
+
50
+ export const FT_DATA_POLICY_TEXT = '金融数据优先取 FTShare,其次才用联网检索补充;引用必须给出处与时点;严格区分已披露事实、机构判断与待验证信息;不编造数字,也不构成投资建议。'
51
+
52
+ export const FT_CREDENTIALS_MISSING_TEXT = 'FTShare 凭据未配置:行情可能退化为免费兜底源,覆盖有限,引用时必须标明来源;需要完整数据时引导用户在设置页填写 API Key。'
53
+
54
+ /**
55
+ * Resolve the host prompt registry.
56
+ * @param ctx - the plugin context whose `inject` declared `systemPrompt`.
57
+ * @returns the registry, typed by this module's structural declaration.
58
+ */
59
+ function promptRegistry(ctx: Context): PromptRegistry {
60
+ const registry = ctx.get('systemPrompt') as PromptRegistry | undefined
61
+ if (registry === undefined) {
62
+ throw new Error('dsh-fft-settings: the systemPrompt service is missing although inject declared it')
63
+ }
64
+ return registry
65
+ }
66
+
67
+ /**
68
+ * Keep one boolean for the credential fact, because a prompt provider is
69
+ * synchronous while the settings service answers asynchronously.
70
+ * @param ctx - the plugin context.
71
+ */
72
+ function installCredentialFact(ctx: Context): void {
73
+ const apiKey = ctx.ftshareSettings.apiKey
74
+ // `undefined` is "not known yet" and renders nothing: an unresolved fact must
75
+ // not tell the model the credential is missing.
76
+ let configured: boolean | undefined
77
+ const refresh = (): void => {
78
+ void apiKey.describe().then(
79
+ (status) => { configured = status.configured },
80
+ (error: unknown) => {
81
+ configured = undefined
82
+ ctx.logger.debug('dsh-fft-settings: credential status unavailable', error)
83
+ },
84
+ )
85
+ }
86
+ refresh()
87
+ ctx.effect(() => apiKey.subscribe(refresh), 'dsh-fft-settings: credential fact refresh')
88
+ ctx.effect(() => promptRegistry(ctx).context({
89
+ name: FT_CREDENTIALS_CONTEXT,
90
+ order: FT_CREDENTIALS_ORDER,
91
+ text: () => configured === false ? FT_CREDENTIALS_MISSING_TEXT : '',
92
+ }), 'dsh-fft-settings: ft:credentials')
93
+ }
94
+
95
+ /**
96
+ * Register the FT prompt contract for every agent the calling context reaches.
97
+ * @param ctx - the plugin's host context; an unscoped context registers globally.
98
+ */
99
+ export function installGuidance(ctx: Context): void {
100
+ const prompt = promptRegistry(ctx)
101
+ ctx.effect(() => prompt.section({
102
+ name: FT_SERVICES_SECTION,
103
+ order: FT_SECTION_ORDERS.services,
104
+ text: FT_SERVICES_TEXT,
105
+ }), 'dsh-fft-settings: ft:services')
106
+ ctx.effect(() => prompt.section({
107
+ name: FT_DATA_POLICY_SECTION,
108
+ order: FT_SECTION_ORDERS.dataPolicy,
109
+ text: FT_DATA_POLICY_TEXT,
110
+ }), 'dsh-fft-settings: ft:data-policy')
111
+ // Optional service: the three sections never depend on the settings row, and
112
+ // a missing provider only means the credential fact renders nothing.
113
+ ctx.inject(['ftshareSettings'], scope => installCredentialFact(scope))
114
+ }
package/src/index.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { Context } from '@deepseek-ai/cordis'
2
+ import z from '@deepseek-ai/schemastery'
3
+ import { type FtshareSettingsService } from './contract.js'
4
+ import { createSettingsHost } from './core/module-registry.js'
5
+ import { createApiKeyModule } from './features/api-key/index.js'
6
+ import { DEFAULT_LOGS_CONFIG, logsConfigSchema, resolveLogsConfig, type LogsConfig } from './features/logs/config.js'
7
+ import { createLogsModule } from './features/logs/index.js'
8
+ import type { HostFetch } from './core/rpc-core.js'
9
+ import { createSettingsRoute } from './core/transport.js'
10
+ import { installDefaultPresetOnce } from './default-preset.js'
11
+ export * from './contract.js'
12
+ export { defineSettingsModule } from './core/module-registry.js'
13
+ export const name = '@dsh-ft/dsh-fft-settings'
14
+ /**
15
+ * Host services this plugin's own built-in modules resolve at registration.
16
+ * A consuming plugin declares its own requirement; settings never declares
17
+ * services on a module's behalf. The FT prompt contract lives in the `./agent`
18
+ * entry, which only an agent preset mounts.
19
+ */
20
+ export const inject = ['credentials']
21
+ export const provide = ['ftshareSettings']
22
+ /** Plugin config, assembled from each feature's own slice schema. */
23
+ export interface FtshareSettingsConfig { logs: LogsConfig }
24
+ /**
25
+ * Native Schemastery schema of the whole plugin config: the features own their
26
+ * slices, this entry only composes them. DSH's config collector accepts
27
+ * nothing but a native Schemastery schema.
28
+ */
29
+ export const Config = z.object({ logs: logsConfigSchema })
30
+ export { DEFAULT_LOGS_CONFIG }
31
+ export type { LogsConfig }
32
+ declare module '@deepseek-ai/cordis' {
33
+ interface Context { ftshareSettings: FtshareSettingsService }
34
+ }
35
+ /**
36
+ * @param config - validated plugin config. Each built-in module is registered
37
+ * eagerly, so the log collector starts here, unconditionally, instead of
38
+ * waiting for a settings-page render.
39
+ *
40
+ * The parameter default is deliberately **not** relied on: cordis validates the
41
+ * raw row through {@link Config} before this call, and a direct `apply(ctx)`
42
+ * call (tests, hand-built contexts) never triggers one — so the fallback parse
43
+ * happens in the body and every feature slice is always defined.
44
+ */
45
+ export function apply(ctx: Context, config?: FtshareSettingsConfig): void {
46
+ const { service, handler } = createSettingsHost()
47
+ ctx.provide('ftshareSettings', service)
48
+ // 具名 logger:`ctx.logger()` 无参调用时 logger name = 插件名(cordis 既有语义),
49
+ // 采集器据此把「是哪个包打的」自动带上。feature 不接受模块级单例。
50
+ const logger = ctx.logger()
51
+ // Built-in namespaces take the same path a third-party module would take.
52
+ service.modules.register(createApiKeyModule(logger), ctx)
53
+ service.modules.register(createLogsModule(resolveLogsConfig(config)), ctx)
54
+ installDefaultPresetOnce(ctx)
55
+ ctx.inject(['connection'], scoped => {
56
+ const connection = scoped.get('connection') as { fetch: HostFetch }
57
+ scoped.effect(() => connection.fetch.register(createSettingsRoute({ handler })), 'dsh-fft-settings: /api route')
58
+ })
59
+ }
package/src/rpc.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@dsh-ft/dsh-fft-settings/client-api` — the generic browser caller entry.
3
+ *
4
+ * Transport plus envelope helpers and their types, nothing feature-specific: a
5
+ * card for any namespace uses `callSettingsRpc` against the namespace and
6
+ * method its own host module registered. A feature's browser adapter lives
7
+ * beside that feature instead of growing this entry.
8
+ */
9
+ export * from './core/rpc-core.js'
10
+ export { createSettingsTransport } from './core/client-transport.js'