@dsh-ft/dsh-fft-settings 0.0.0-stage → 0.5.1

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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +395 -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 +806 -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 +17 -0
  52. package/lib/features/api-key/probe.js +148 -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/locale/en.json +6 -0
  92. package/locale/zh.json +6 -0
  93. package/package.json +108 -4
  94. package/src/agent.ts +31 -0
  95. package/src/brand.ts +79 -0
  96. package/src/client.tsx +21 -0
  97. package/src/contract.ts +14 -0
  98. package/src/core/client-transport.ts +37 -0
  99. package/src/core/contract.ts +57 -0
  100. package/src/core/module-registry.ts +102 -0
  101. package/src/core/rpc-core.ts +107 -0
  102. package/src/core/shell.tsx +81 -0
  103. package/src/core/styles.ts +43 -0
  104. package/src/core/transport.ts +82 -0
  105. package/src/default-preset.ts +151 -0
  106. package/src/features/api-key/card.tsx +77 -0
  107. package/src/features/api-key/client-api.ts +12 -0
  108. package/src/features/api-key/contract.ts +63 -0
  109. package/src/features/api-key/host.ts +36 -0
  110. package/src/features/api-key/index.ts +13 -0
  111. package/src/features/api-key/logging.ts +15 -0
  112. package/src/features/api-key/model.ts +63 -0
  113. package/src/features/api-key/probe.ts +125 -0
  114. package/src/features/api-key/rpc.ts +18 -0
  115. package/src/features/api-key/service.ts +157 -0
  116. package/src/features/api-key/styles.ts +24 -0
  117. package/src/features/logs/archive.ts +230 -0
  118. package/src/features/logs/card.tsx +169 -0
  119. package/src/features/logs/client-api.ts +20 -0
  120. package/src/features/logs/collector.ts +313 -0
  121. package/src/features/logs/config.ts +115 -0
  122. package/src/features/logs/contract.ts +149 -0
  123. package/src/features/logs/export.ts +274 -0
  124. package/src/features/logs/format.ts +188 -0
  125. package/src/features/logs/home.ts +43 -0
  126. package/src/features/logs/host.ts +153 -0
  127. package/src/features/logs/index.ts +12 -0
  128. package/src/features/logs/rpc.ts +29 -0
  129. package/src/features/logs/styles.ts +13 -0
  130. package/src/guidance.ts +114 -0
  131. package/src/index.ts +59 -0
  132. package/src/rpc.ts +10 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * `logs` 命名空间 Host 半边:常驻采集器 + 导出器。
3
+ *
4
+ * 采集器在 `create()` 里**无条件启动**(`apply()` 期注册即执行,与设置页是否渲染无关);
5
+ * exporter 由 cordis 绑定当前 fiber,另用 `ctx.effect` 明确关闭文件句柄与队列。
6
+ *
7
+ * 工厂化:dev 的接缝里 `create(ctx)` 不再收配置,模块由 `apply()` 用一个闭包把
8
+ * 已解析的 `logs` 配置带进来(`createLogsModule(logsConfig)`)。这样采集器仍然在
9
+ * 注册期、而不是等到设置页打开才启动。
10
+ */
11
+ import { credentialRef } from '@deepseek-ai/dsh-credentials';
12
+ import { FTSHARE_API_KEY_REF } from '../api-key/contract.js';
13
+ import { defineSettingsModule } from '../../core/module-registry.js';
14
+ import { createLogsArchiveRoute } from './archive.js';
15
+ import { FtLogCollector } from './collector.js';
16
+ import { LOGS_DEFAULT_PREVIEW_LINES, LOGS_MAX_PREVIEW_LINES, LOGS_PREVIEW_MAX_BYTES, } from './contract.js';
17
+ import { buildLogExport, describeSources, readTailLines } from './export.js';
18
+ import { formatLogTimestamp } from './format.js';
19
+ import { displayDshHome, logsDirectory, resolveDshHome } from './home.js';
20
+ import { createLogsRpcMethods } from './rpc.js';
21
+ /** 从本包 `package.json` 读版本;读不到时退回 `unknown`,不影响导出。 */
22
+ async function readPluginVersion() {
23
+ try {
24
+ const url = new URL('../../../package.json', import.meta.url);
25
+ const { readFile } = await import('node:fs/promises');
26
+ const parsed = JSON.parse(await readFile(url, 'utf8'));
27
+ return typeof parsed.version === 'string' ? parsed.version : 'unknown';
28
+ }
29
+ catch {
30
+ return 'unknown';
31
+ }
32
+ }
33
+ /**
34
+ * 运行时描述串:header 与 manifest 共用一个口径。
35
+ * @returns 形如 `node v22.19.0 darwin arm64`。
36
+ */
37
+ function runtimeDescription() {
38
+ return `node ${process.version} ${process.platform} ${process.arch}`;
39
+ }
40
+ /**
41
+ * 构建 `logs` 模块:把已解析的配置闭包进 `create`。
42
+ * @param config - 本 feature 的 `logs` 配置(`resolveLogsConfig` 的输出,永远完整)。
43
+ * @returns 可交给 `ctx.ftshareSettings.modules.register` 的模块。
44
+ */
45
+ export function createLogsModule(config) {
46
+ return defineSettingsModule({
47
+ namespace: 'logs',
48
+ inject: ['credentials'],
49
+ create(ctx) {
50
+ const home = resolveDshHome();
51
+ const directory = logsDirectory(home);
52
+ const collector = new FtLogCollector({
53
+ directory,
54
+ level: config.level,
55
+ maxBytes: config.maxBytes,
56
+ keep: config.keep,
57
+ // 当前 FTShare Key 的精确值替换:取不到就退化为纯模式脱敏。
58
+ secrets: async () => {
59
+ const ref = credentialRef(FTSHARE_API_KEY_REF);
60
+ const resolved = await ctx.credentials.resolve(ref);
61
+ return resolved?.value === undefined ? [] : [resolved.value];
62
+ },
63
+ });
64
+ const version = readPluginVersion();
65
+ const service = {
66
+ filePath: collector.filePath,
67
+ flush: () => collector.flush(),
68
+ dispose: () => collector.dispose(),
69
+ stats: async () => {
70
+ const snapshot = collector.stats();
71
+ return {
72
+ file: snapshot.filePath,
73
+ fileBytes: snapshot.fileBytes,
74
+ level: config.level,
75
+ maxBytes: config.maxBytes,
76
+ keep: config.keep,
77
+ records: snapshot.records,
78
+ dropped: snapshot.dropped,
79
+ rotations: snapshot.rotations,
80
+ writeFailures: snapshot.writeFailures,
81
+ firstAt: snapshot.firstTs === undefined ? undefined : formatLogTimestamp(snapshot.firstTs),
82
+ lastAt: snapshot.lastTs === undefined ? undefined : formatLogTimestamp(snapshot.lastTs),
83
+ byName: snapshot.byName,
84
+ sources: await describeSources(config.sources),
85
+ };
86
+ },
87
+ preview: async (lines = LOGS_DEFAULT_PREVIEW_LINES) => {
88
+ await collector.flush();
89
+ const wanted = Math.min(LOGS_MAX_PREVIEW_LINES, Math.max(1, Math.trunc(lines)));
90
+ try {
91
+ const tail = await readTailLines(collector.filePath, wanted, LOGS_PREVIEW_MAX_BYTES);
92
+ return { lines: tail.lines, bytes: tail.bytes, truncated: tail.truncated, fileBytes: tail.fileBytes };
93
+ }
94
+ catch {
95
+ // 还没有任何日志时不是错误:返回空预览。
96
+ return { lines: [], bytes: 0, truncated: false, fileBytes: 0 };
97
+ }
98
+ },
99
+ export: async () => {
100
+ await collector.flush();
101
+ const result = await buildLogExport({
102
+ filePath: collector.filePath,
103
+ sources: config.sources,
104
+ homeDisplay: displayDshHome(home),
105
+ pluginVersion: await version,
106
+ environment: runtimeDescription(),
107
+ });
108
+ return { fileName: result.fileName, text: result.text, bytes: result.bytes, truncated: result.truncated, sources: result.summaries };
109
+ },
110
+ };
111
+ // 诊断包由本 feature 自己挂路由(core 不认识 /logs.zip):与插件入口同一条惯用法,
112
+ // register 返回的 disposer 交给 scoped.effect,owner fiber 释放时路由随之撤销。
113
+ ctx.inject(['connection'], scoped => {
114
+ const connection = scoped.get('connection');
115
+ scoped.effect(() => connection.fetch.register(createLogsArchiveRoute({
116
+ filePath: collector.filePath,
117
+ sources: config.sources,
118
+ homeDisplay: displayDshHome(home),
119
+ pluginVersion: version,
120
+ environment: runtimeDescription(),
121
+ flush: () => collector.flush(),
122
+ })), 'ftshare-settings: /logs.zip route');
123
+ });
124
+ const disposeExporter = ctx.logger.exporter({
125
+ // levels 是接收上限:warn=2 收 error/info/warn,debug=3 全收。
126
+ levels: { default: collector.threshold },
127
+ export: message => collector.append(message),
128
+ });
129
+ ctx.effect(() => () => {
130
+ disposeExporter();
131
+ void collector.dispose();
132
+ }, 'ftshare-settings: FT 日志采集器');
133
+ // 回填内置缓冲里残留的 error/info(warn/debug 已被默认门槛丢弃,不可恢复)。
134
+ const buffered = ctx.logger.buffer.filter(message => message.type === 'error' || message.type === 'info');
135
+ void collector.backfill(buffered);
136
+ return { service, rpcMethods: createLogsRpcMethods(service) };
137
+ },
138
+ });
139
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Host half of the `logs` feature: the entry a host assembly imports.
3
+ *
4
+ * Like every feature directory, this file is the host entry, `./contract.js`
5
+ * its shared contract, and `./card.js` the browser entry browser code imports
6
+ * directly instead of going through here (a browser half must never pull the
7
+ * collector, the file system or the config schema).
8
+ */
9
+ export * from './contract.js';
10
+ export { buildLogArchive, createLogsArchiveRoute, type LogsArchive, type LogsArchiveManifest, type LogsArchiveOptions, type LogsArchiveRoute } from './archive.js';
11
+ export { createLogsModule } from './host.js';
12
+ export { createLogsRpcMethods } from './rpc.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Host half of the `logs` feature: the entry a host assembly imports.
3
+ *
4
+ * Like every feature directory, this file is the host entry, `./contract.js`
5
+ * its shared contract, and `./card.js` the browser entry browser code imports
6
+ * directly instead of going through here (a browser half must never pull the
7
+ * collector, the file system or the config schema).
8
+ */
9
+ export * from './contract.js';
10
+ export { buildLogArchive, createLogsArchiveRoute } from './archive.js';
11
+ export { createLogsModule } from './host.js';
12
+ export { createLogsRpcMethods } from './rpc.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `logs` 命名空间的 RPC 方法:只暴露显式登记的三个只读方法,沿用严格 payload 校验。
3
+ */
4
+ import { type LogsSettings } from './contract.js';
5
+ import type { SettingsRpcMethods } from '../../core/rpc-core.js';
6
+ export declare function createLogsRpcMethods(service: LogsSettings): SettingsRpcMethods;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `logs` 命名空间的 RPC 方法:只暴露显式登记的三个只读方法,沿用严格 payload 校验。
3
+ */
4
+ import { LOGS_DEFAULT_PREVIEW_LINES, LOGS_MAX_PREVIEW_LINES } from './contract.js';
5
+ const record = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ const empty = (value) => record(value) && Object.keys(value).length === 0;
7
+ /** 只允许 `{ lines?: number }`,且值必须落在预览上限内。 */
8
+ const previewPayload = (value) => {
9
+ if (!record(value))
10
+ return false;
11
+ const keys = Object.keys(value);
12
+ if (keys.some(key => key !== 'lines'))
13
+ return false;
14
+ if (value.lines === undefined)
15
+ return true;
16
+ return typeof value.lines === 'number' && Number.isInteger(value.lines) && value.lines >= 1 && value.lines <= LOGS_MAX_PREVIEW_LINES;
17
+ };
18
+ export function createLogsRpcMethods(service) {
19
+ return {
20
+ stats: { validate: empty, invoke: () => service.stats() },
21
+ preview: {
22
+ validate: previewPayload,
23
+ invoke: payload => service.preview(payload.lines ?? LOGS_DEFAULT_PREVIEW_LINES),
24
+ },
25
+ export: { validate: empty, invoke: () => service.export() },
26
+ };
27
+ }
@@ -0,0 +1,9 @@
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 declare const LOGS_CARD_CSS = "\n.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; }\n.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; }\n.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; }\n";
@@ -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,41 @@
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
+ import type { Context } from '@deepseek-ai/cordis';
17
+ /** Section name of the capability list. */
18
+ export declare const FT_SERVICES_SECTION = "ft:services";
19
+ /** Section name of the financial-data discipline. */
20
+ export declare const FT_DATA_POLICY_SECTION = "ft:data-policy";
21
+ /** Runtime-context name of the FTShare credential fact. */
22
+ export declare const FT_CREDENTIALS_CONTEXT = "ft:credentials";
23
+ /**
24
+ * Section orders. Consecutive integers keep a third-party section from sorting
25
+ * between two of ours (`1`–`499` is the shared free band), while `6` still lands
26
+ * after the deployment persona (`0`) and before core's plan policy (`500`).
27
+ */
28
+ export declare const FT_SECTION_ORDERS: {
29
+ readonly services: 6;
30
+ readonly dataPolicy: 7;
31
+ };
32
+ /** Order of the credential fact — `getContextOrder` owns only three named positions. */
33
+ export declare const FT_CREDENTIALS_ORDER = 130;
34
+ export declare const FT_SERVICES_TEXT = "\u884C\u60C5\u3001K\u7EBF\u4E0E\u6307\u6807\u7528 dsh-kline \u5DE5\u5177\uFF08\u4EE3\u7801\u5148\u7528 search_symbols \u89E3\u6790\uFF09\uFF1B\u6295\u7814\u62A5\u544A\u4E0E\u7EC4\u5408\u63A8\u8350\u7528 cockpit_* \u5DE5\u5177\uFF1B\u56E0\u5B50\u5165\u5E93\u4E0E\u8BC4\u4EF7\u7528 factor-intake-standard \u6280\u80FD\uFF1BFTShare API Key \u5728\u8BBE\u7F6E\u9875\u7EF4\u62A4\u3002";
35
+ export declare const FT_DATA_POLICY_TEXT = "\u91D1\u878D\u6570\u636E\u4F18\u5148\u53D6 FTShare\uFF0C\u5176\u6B21\u624D\u7528\u8054\u7F51\u68C0\u7D22\u8865\u5145\uFF1B\u5F15\u7528\u5FC5\u987B\u7ED9\u51FA\u5904\u4E0E\u65F6\u70B9\uFF1B\u4E25\u683C\u533A\u5206\u5DF2\u62AB\u9732\u4E8B\u5B9E\u3001\u673A\u6784\u5224\u65AD\u4E0E\u5F85\u9A8C\u8BC1\u4FE1\u606F\uFF1B\u4E0D\u7F16\u9020\u6570\u5B57\uFF0C\u4E5F\u4E0D\u6784\u6210\u6295\u8D44\u5EFA\u8BAE\u3002";
36
+ export declare const FT_CREDENTIALS_MISSING_TEXT = "FTShare \u51ED\u636E\u672A\u914D\u7F6E\uFF1A\u884C\u60C5\u53EF\u80FD\u9000\u5316\u4E3A\u514D\u8D39\u515C\u5E95\u6E90\uFF0C\u8986\u76D6\u6709\u9650\uFF0C\u5F15\u7528\u65F6\u5FC5\u987B\u6807\u660E\u6765\u6E90\uFF1B\u9700\u8981\u5B8C\u6574\u6570\u636E\u65F6\u5F15\u5BFC\u7528\u6237\u5728\u8BBE\u7F6E\u9875\u586B\u5199 API Key\u3002";
37
+ /**
38
+ * Register the FT prompt contract for every agent the calling context reaches.
39
+ * @param ctx - the plugin's host context; an unscoped context registers globally.
40
+ */
41
+ export declare function installGuidance(ctx: Context): void;
@@ -0,0 +1,88 @@
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
+ /** Section name of the capability list. */
17
+ export const FT_SERVICES_SECTION = 'ft:services';
18
+ /** Section name of the financial-data discipline. */
19
+ export const FT_DATA_POLICY_SECTION = 'ft:data-policy';
20
+ /** Runtime-context name of the FTShare credential fact. */
21
+ export const FT_CREDENTIALS_CONTEXT = 'ft:credentials';
22
+ /**
23
+ * Section orders. Consecutive integers keep a third-party section from sorting
24
+ * between two of ours (`1`–`499` is the shared free band), while `6` still lands
25
+ * after the deployment persona (`0`) and before core's plan policy (`500`).
26
+ */
27
+ export const FT_SECTION_ORDERS = { services: 6, dataPolicy: 7 };
28
+ /** Order of the credential fact — `getContextOrder` owns only three named positions. */
29
+ export const FT_CREDENTIALS_ORDER = 130;
30
+ export const FT_SERVICES_TEXT = '行情、K线与指标用 dsh-kline 工具(代码先用 search_symbols 解析);投研报告与组合推荐用 cockpit_* 工具;因子入库与评价用 factor-intake-standard 技能;FTShare API Key 在设置页维护。';
31
+ export const FT_DATA_POLICY_TEXT = '金融数据优先取 FTShare,其次才用联网检索补充;引用必须给出处与时点;严格区分已披露事实、机构判断与待验证信息;不编造数字,也不构成投资建议。';
32
+ export const FT_CREDENTIALS_MISSING_TEXT = 'FTShare 凭据未配置:行情可能退化为免费兜底源,覆盖有限,引用时必须标明来源;需要完整数据时引导用户在设置页填写 API Key。';
33
+ /**
34
+ * Resolve the host prompt registry.
35
+ * @param ctx - the plugin context whose `inject` declared `systemPrompt`.
36
+ * @returns the registry, typed by this module's structural declaration.
37
+ */
38
+ function promptRegistry(ctx) {
39
+ const registry = ctx.get('systemPrompt');
40
+ if (registry === undefined) {
41
+ throw new Error('dsh-fft-settings: the systemPrompt service is missing although inject declared it');
42
+ }
43
+ return registry;
44
+ }
45
+ /**
46
+ * Keep one boolean for the credential fact, because a prompt provider is
47
+ * synchronous while the settings service answers asynchronously.
48
+ * @param ctx - the plugin context.
49
+ */
50
+ function installCredentialFact(ctx) {
51
+ const apiKey = ctx.ftshareSettings.apiKey;
52
+ // `undefined` is "not known yet" and renders nothing: an unresolved fact must
53
+ // not tell the model the credential is missing.
54
+ let configured;
55
+ const refresh = () => {
56
+ void apiKey.describe().then((status) => { configured = status.configured; }, (error) => {
57
+ configured = undefined;
58
+ ctx.logger.debug('dsh-fft-settings: credential status unavailable', error);
59
+ });
60
+ };
61
+ refresh();
62
+ ctx.effect(() => apiKey.subscribe(refresh), 'dsh-fft-settings: credential fact refresh');
63
+ ctx.effect(() => promptRegistry(ctx).context({
64
+ name: FT_CREDENTIALS_CONTEXT,
65
+ order: FT_CREDENTIALS_ORDER,
66
+ text: () => configured === false ? FT_CREDENTIALS_MISSING_TEXT : '',
67
+ }), 'dsh-fft-settings: ft:credentials');
68
+ }
69
+ /**
70
+ * Register the FT prompt contract for every agent the calling context reaches.
71
+ * @param ctx - the plugin's host context; an unscoped context registers globally.
72
+ */
73
+ export function installGuidance(ctx) {
74
+ const prompt = promptRegistry(ctx);
75
+ ctx.effect(() => prompt.section({
76
+ name: FT_SERVICES_SECTION,
77
+ order: FT_SECTION_ORDERS.services,
78
+ text: FT_SERVICES_TEXT,
79
+ }), 'dsh-fft-settings: ft:services');
80
+ ctx.effect(() => prompt.section({
81
+ name: FT_DATA_POLICY_SECTION,
82
+ order: FT_SECTION_ORDERS.dataPolicy,
83
+ text: FT_DATA_POLICY_TEXT,
84
+ }), 'dsh-fft-settings: ft:data-policy');
85
+ // Optional service: the three sections never depend on the settings row, and
86
+ // a missing provider only means the credential fact renders nothing.
87
+ ctx.inject(['ftshareSettings'], scope => installCredentialFact(scope));
88
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ import { type FtshareSettingsService } from './contract.js';
4
+ import { DEFAULT_LOGS_CONFIG, type LogsConfig } from './features/logs/config.js';
5
+ export * from './contract.js';
6
+ export { defineSettingsModule } from './core/module-registry.js';
7
+ export declare const name = "@dsh-ft/dsh-fft-settings";
8
+ /**
9
+ * Host services this plugin's own built-in modules resolve at registration.
10
+ * A consuming plugin declares its own requirement; settings never declares
11
+ * services on a module's behalf. The FT prompt contract lives in the `./agent`
12
+ * entry, which only an agent preset mounts.
13
+ */
14
+ export declare const inject: string[];
15
+ export declare const provide: string[];
16
+ /** Plugin config, assembled from each feature's own slice schema. */
17
+ export interface FtshareSettingsConfig {
18
+ logs: LogsConfig;
19
+ }
20
+ /**
21
+ * Native Schemastery schema of the whole plugin config: the features own their
22
+ * slices, this entry only composes them. DSH's config collector accepts
23
+ * nothing but a native Schemastery schema.
24
+ */
25
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
26
+ logs: z<Schemastery.ObjectS<NoInfer<{
27
+ level: z<"warn" | "debug", "warn" | "debug", "defined">;
28
+ maxBytes: z<number, number, "defined">;
29
+ keep: z<number, number, "defined">;
30
+ sources: z<string[], string[], "defined">;
31
+ }>>, Schemastery.ObjectT<NoInfer<{
32
+ level: z<"warn" | "debug", "warn" | "debug", "defined">;
33
+ maxBytes: z<number, number, "defined">;
34
+ keep: z<number, number, "defined">;
35
+ sources: z<string[], string[], "defined">;
36
+ }>>, "defined">;
37
+ }>>, Schemastery.ObjectT<NoInfer<{
38
+ logs: z<Schemastery.ObjectS<NoInfer<{
39
+ level: z<"warn" | "debug", "warn" | "debug", "defined">;
40
+ maxBytes: z<number, number, "defined">;
41
+ keep: z<number, number, "defined">;
42
+ sources: z<string[], string[], "defined">;
43
+ }>>, Schemastery.ObjectT<NoInfer<{
44
+ level: z<"warn" | "debug", "warn" | "debug", "defined">;
45
+ maxBytes: z<number, number, "defined">;
46
+ keep: z<number, number, "defined">;
47
+ sources: z<string[], string[], "defined">;
48
+ }>>, "defined">;
49
+ }>>, "plain">;
50
+ export { DEFAULT_LOGS_CONFIG };
51
+ export type { LogsConfig };
52
+ declare module '@deepseek-ai/cordis' {
53
+ interface Context {
54
+ ftshareSettings: FtshareSettingsService;
55
+ }
56
+ }
57
+ /**
58
+ * @param config - validated plugin config. Each built-in module is registered
59
+ * eagerly, so the log collector starts here, unconditionally, instead of
60
+ * waiting for a settings-page render.
61
+ *
62
+ * The parameter default is deliberately **not** relied on: cordis validates the
63
+ * raw row through {@link Config} before this call, and a direct `apply(ctx)`
64
+ * call (tests, hand-built contexts) never triggers one — so the fallback parse
65
+ * happens in the body and every feature slice is always defined.
66
+ */
67
+ export declare function apply(ctx: Context, config?: FtshareSettingsConfig): void;
package/lib/index.js ADDED
@@ -0,0 +1,50 @@
1
+ import z from '@deepseek-ai/schemastery';
2
+ import { createSettingsHost } from './core/module-registry.js';
3
+ import { createApiKeyModule } from './features/api-key/index.js';
4
+ import { DEFAULT_LOGS_CONFIG, logsConfigSchema, resolveLogsConfig } from './features/logs/config.js';
5
+ import { createLogsModule } from './features/logs/index.js';
6
+ import { createSettingsRoute } from './core/transport.js';
7
+ import { installDefaultPresetOnce } from './default-preset.js';
8
+ export * from './contract.js';
9
+ export { defineSettingsModule } from './core/module-registry.js';
10
+ export const name = '@dsh-ft/dsh-fft-settings';
11
+ /**
12
+ * Host services this plugin's own built-in modules resolve at registration.
13
+ * A consuming plugin declares its own requirement; settings never declares
14
+ * services on a module's behalf. The FT prompt contract lives in the `./agent`
15
+ * entry, which only an agent preset mounts.
16
+ */
17
+ export const inject = ['credentials'];
18
+ export const provide = ['ftshareSettings'];
19
+ /**
20
+ * Native Schemastery schema of the whole plugin config: the features own their
21
+ * slices, this entry only composes them. DSH's config collector accepts
22
+ * nothing but a native Schemastery schema.
23
+ */
24
+ export const Config = z.object({ logs: logsConfigSchema });
25
+ export { DEFAULT_LOGS_CONFIG };
26
+ /**
27
+ * @param config - validated plugin config. Each built-in module is registered
28
+ * eagerly, so the log collector starts here, unconditionally, instead of
29
+ * waiting for a settings-page render.
30
+ *
31
+ * The parameter default is deliberately **not** relied on: cordis validates the
32
+ * raw row through {@link Config} before this call, and a direct `apply(ctx)`
33
+ * call (tests, hand-built contexts) never triggers one — so the fallback parse
34
+ * happens in the body and every feature slice is always defined.
35
+ */
36
+ export function apply(ctx, config) {
37
+ const { service, handler } = createSettingsHost();
38
+ ctx.provide('ftshareSettings', service);
39
+ // 具名 logger:`ctx.logger()` 无参调用时 logger name = 插件名(cordis 既有语义),
40
+ // 采集器据此把「是哪个包打的」自动带上。feature 不接受模块级单例。
41
+ const logger = ctx.logger();
42
+ // Built-in namespaces take the same path a third-party module would take.
43
+ service.modules.register(createApiKeyModule(logger), ctx);
44
+ service.modules.register(createLogsModule(resolveLogsConfig(config)), ctx);
45
+ installDefaultPresetOnce(ctx);
46
+ ctx.inject(['connection'], scoped => {
47
+ const connection = scoped.get('connection');
48
+ scoped.effect(() => connection.fetch.register(createSettingsRoute({ handler })), 'dsh-fft-settings: /api route');
49
+ });
50
+ }
package/lib/rpc.d.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';
package/lib/rpc.js 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';
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "FT Settings",
4
+ "description": "Shared plugin pages for the financial workbench."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "FT 设置",
4
+ "description": "金融工作台共享插件页"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,110 @@
1
1
  {
2
2
  "name": "@dsh-ft/dsh-fft-settings",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.5.1",
4
+ "description": "FT 设置与凭据底座:FTShare API Key、设置页卡片、运行日志与品牌主题层,供 FT 系列插件共享。",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/gigaflw/dsh-fft-base/tree/dev/packages/dsh-fft-settings#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/gigaflw/dsh-fft-base.git",
10
+ "directory": "packages/dsh-fft-settings"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/gigaflw/dsh-fft-base/issues"
14
+ },
15
+ "keywords": [
16
+ "dsh-plugin",
17
+ "deepseek-harness",
18
+ "ftshare",
19
+ "settings"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "type": "module",
25
+ "main": "lib/index.js",
26
+ "types": "lib/index.d.ts",
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json && tsdown --config tsdown.config.ts",
29
+ "prepare": "pnpm run build",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "pnpm run build && node --test tests/*.test.mjs",
32
+ "theme:preview": "pnpm run build && node tools/theme-preview.mjs"
33
+ },
34
+ "files": [
35
+ "lib",
36
+ "src",
37
+ "README.md",
38
+ "LICENSE",
39
+ "docs",
40
+ "cordis.patch.yml",
41
+ "locale/*.json",
42
+ "package.json"
43
+ ],
44
+ "dsh": {
45
+ "bundle": {
46
+ "patch": "./cordis.patch.yml"
47
+ },
48
+ "client": {
49
+ "platform": "web",
50
+ "inject": [
51
+ "@deepseek-ai/dsh-client-ui-slots",
52
+ "@deepseek-ai/dsh-api-remotes"
53
+ ]
54
+ },
55
+ "engines": {
56
+ "dsh": ">=0.2.0-rc.2"
57
+ }
58
+ },
59
+ "exports": {
60
+ ".": {
61
+ "types": "./lib/index.d.ts",
62
+ "default": "./lib/index.js"
63
+ },
64
+ "./contract": {
65
+ "types": "./lib/contract.d.ts",
66
+ "default": "./lib/contract.js"
67
+ },
68
+ "./agent": {
69
+ "types": "./lib/agent.d.ts",
70
+ "default": "./lib/agent.js"
71
+ },
72
+ "./client": {
73
+ "types": "./lib/client.d.ts",
74
+ "default": "./lib/client.js"
75
+ },
76
+ "./client-api": {
77
+ "types": "./lib/rpc.d.ts",
78
+ "default": "./lib/rpc.js"
79
+ },
80
+ "./package.json": "./package.json",
81
+ "./locale/*.json": "./locale/*.json"
82
+ },
83
+ "dependencies": {
84
+ "fflate": ">=0.8.3"
85
+ },
86
+ "peerDependencies": {
87
+ "@deepseek-ai/cordis": "^4.0.2",
88
+ "@deepseek-ai/dsh-api-remotes": ">=0.2.0-rc.2",
89
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.2",
90
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.2",
91
+ "react": "^18.2.0",
92
+ "@deepseek-ai/dsh-credentials": ">=0.2.0-rc.2",
93
+ "@deepseek-ai/schemastery": ">=3.18.4"
94
+ },
95
+ "devDependencies": {
96
+ "@types/node": "^22.15.0",
97
+ "@types/react": "^18.3.24",
98
+ "react": "^18.3.1",
99
+ "tsdown": "^0.22.2",
100
+ "typescript": "^5.8.3",
101
+ "@deepseek-ai/cordis": "catalog:",
102
+ "@deepseek-ai/dsh-client-ui-settings": "catalog:",
103
+ "@deepseek-ai/dsh-client-ui-slots": "catalog:",
104
+ "@deepseek-ai/dsh-credentials": "catalog:",
105
+ "@deepseek-ai/schemastery": "catalog:"
106
+ },
107
+ "engines": {
108
+ "node": ">=22.19.0"
109
+ }
110
+ }