@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.
- package/LICENSE +21 -0
- package/README.md +395 -2
- package/cordis.patch.yml +18 -0
- package/docs/compatibility-dsh-0.1.6-alpha.1.md +58 -0
- package/docs/extension-service-design.md +351 -0
- package/docs/extension-service-sequences.html +545 -0
- package/docs/ftshare-settings-er.html +139 -0
- package/docs/ftshare-settings-er.svg +113 -0
- package/docs/ftshare-settings-results-er.html +148 -0
- package/docs/ftshare-settings-results-er.svg +122 -0
- package/docs/kline-integration.md +258 -0
- package/lib/agent.d.ts +24 -0
- package/lib/agent.js +26 -0
- package/lib/brand.d.ts +53 -0
- package/lib/brand.js +50 -0
- package/lib/client.d.ts +8 -0
- package/lib/client.js +806 -0
- package/lib/client.js.map +1 -0
- package/lib/contract.d.ts +14 -0
- package/lib/contract.js +14 -0
- package/lib/core/client-transport.d.ts +15 -0
- package/lib/core/client-transport.js +31 -0
- package/lib/core/contract.d.ts +62 -0
- package/lib/core/contract.js +28 -0
- package/lib/core/module-registry.d.ts +54 -0
- package/lib/core/module-registry.js +68 -0
- package/lib/core/rpc-core.d.ts +69 -0
- package/lib/core/rpc-core.js +88 -0
- package/lib/core/shell.d.ts +59 -0
- package/lib/core/shell.js +28 -0
- package/lib/core/styles.d.ts +31 -0
- package/lib/core/styles.js +43 -0
- package/lib/core/transport.d.ts +19 -0
- package/lib/core/transport.js +80 -0
- package/lib/default-preset.d.ts +52 -0
- package/lib/default-preset.js +121 -0
- package/lib/features/api-key/card.d.ts +24 -0
- package/lib/features/api-key/card.js +39 -0
- package/lib/features/api-key/client-api.d.ts +4 -0
- package/lib/features/api-key/client-api.js +11 -0
- package/lib/features/api-key/contract.d.ts +69 -0
- package/lib/features/api-key/contract.js +24 -0
- package/lib/features/api-key/host.d.ts +17 -0
- package/lib/features/api-key/host.js +32 -0
- package/lib/features/api-key/index.d.ts +13 -0
- package/lib/features/api-key/index.js +12 -0
- package/lib/features/api-key/logging.d.ts +15 -0
- package/lib/features/api-key/logging.js +1 -0
- package/lib/features/api-key/model.d.ts +24 -0
- package/lib/features/api-key/model.js +105 -0
- package/lib/features/api-key/probe.d.ts +17 -0
- package/lib/features/api-key/probe.js +148 -0
- package/lib/features/api-key/rpc.d.ts +4 -0
- package/lib/features/api-key/rpc.js +17 -0
- package/lib/features/api-key/service.d.ts +21 -0
- package/lib/features/api-key/service.js +181 -0
- package/lib/features/api-key/styles.d.ts +9 -0
- package/lib/features/api-key/styles.js +24 -0
- package/lib/features/logs/archive.d.ts +87 -0
- package/lib/features/logs/archive.js +156 -0
- package/lib/features/logs/card.d.ts +22 -0
- package/lib/features/logs/card.js +107 -0
- package/lib/features/logs/client-api.d.ts +4 -0
- package/lib/features/logs/client-api.js +11 -0
- package/lib/features/logs/collector.d.ts +91 -0
- package/lib/features/logs/collector.js +258 -0
- package/lib/features/logs/config.d.ts +70 -0
- package/lib/features/logs/config.js +97 -0
- package/lib/features/logs/contract.d.ts +130 -0
- package/lib/features/logs/contract.js +67 -0
- package/lib/features/logs/export.d.ts +92 -0
- package/lib/features/logs/export.js +212 -0
- package/lib/features/logs/format.d.ts +42 -0
- package/lib/features/logs/format.js +172 -0
- package/lib/features/logs/home.d.ts +18 -0
- package/lib/features/logs/home.js +39 -0
- package/lib/features/logs/host.d.ts +19 -0
- package/lib/features/logs/host.js +139 -0
- package/lib/features/logs/index.d.ts +12 -0
- package/lib/features/logs/index.js +12 -0
- package/lib/features/logs/rpc.d.ts +6 -0
- package/lib/features/logs/rpc.js +27 -0
- package/lib/features/logs/styles.d.ts +9 -0
- package/lib/features/logs/styles.js +13 -0
- package/lib/guidance.d.ts +41 -0
- package/lib/guidance.js +88 -0
- package/lib/index.d.ts +67 -0
- package/lib/index.js +50 -0
- package/lib/rpc.d.ts +10 -0
- package/lib/rpc.js +10 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +108 -4
- package/src/agent.ts +31 -0
- package/src/brand.ts +79 -0
- package/src/client.tsx +21 -0
- package/src/contract.ts +14 -0
- package/src/core/client-transport.ts +37 -0
- package/src/core/contract.ts +57 -0
- package/src/core/module-registry.ts +102 -0
- package/src/core/rpc-core.ts +107 -0
- package/src/core/shell.tsx +81 -0
- package/src/core/styles.ts +43 -0
- package/src/core/transport.ts +82 -0
- package/src/default-preset.ts +151 -0
- package/src/features/api-key/card.tsx +77 -0
- package/src/features/api-key/client-api.ts +12 -0
- package/src/features/api-key/contract.ts +63 -0
- package/src/features/api-key/host.ts +36 -0
- package/src/features/api-key/index.ts +13 -0
- package/src/features/api-key/logging.ts +15 -0
- package/src/features/api-key/model.ts +63 -0
- package/src/features/api-key/probe.ts +125 -0
- package/src/features/api-key/rpc.ts +18 -0
- package/src/features/api-key/service.ts +157 -0
- package/src/features/api-key/styles.ts +24 -0
- package/src/features/logs/archive.ts +230 -0
- package/src/features/logs/card.tsx +169 -0
- package/src/features/logs/client-api.ts +20 -0
- package/src/features/logs/collector.ts +313 -0
- package/src/features/logs/config.ts +115 -0
- package/src/features/logs/contract.ts +149 -0
- package/src/features/logs/export.ts +274 -0
- package/src/features/logs/format.ts +188 -0
- package/src/features/logs/home.ts +43 -0
- package/src/features/logs/host.ts +153 -0
- package/src/features/logs/index.ts +12 -0
- package/src/features/logs/rpc.ts +29 -0
- package/src/features/logs/styles.ts +13 -0
- package/src/guidance.ts +114 -0
- package/src/index.ts +59 -0
- package/src/rpc.ts +10 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="ftshare-settings-results-er-title ftshare-settings-results-er-desc" id="ftshare-settings-results-er-root">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&family=Noto+Serif:ital@0;1&family=Noto+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@400&family=Noto+Sans+TC:wght@400;500;600&family=Noto+Serif+TC:wght@400&display=swap');
|
|
5
|
+
#ftshare-settings-results-er-root { font-family: 'Geist', 'PingFang SC', 'Noto Sans SC', system-ui, sans-serif; color: #2d3142; }
|
|
6
|
+
#ftshare-settings-results-er-root .lede { max-width: 74ch; color: #4f5d75; font-size: 0.95rem; line-height: 1.6; margin-bottom: 2rem; }
|
|
7
|
+
#ftshare-settings-results-er-root .diagram-container { width: 100%; overflow-x: auto; }
|
|
8
|
+
#ftshare-settings-results-er-root .diagram-container { overflow-x: visible; }
|
|
9
|
+
#ftshare-settings-results-er-root .note { max-width: 78ch; color: #4f5d75; font-size: 0.8rem; line-height: 1.6; margin-top: 1.25rem; }
|
|
10
|
+
#ftshare-settings-results-er-root .colophon { font-family: 'Geist Mono', monospace; font-size: 0.7rem; letter-spacing: 0.08em; color: #7a8399;
|
|
11
|
+
border-top: 1px solid rgba(45,49,66,0.10); padding-top: 0.85rem; margin-top: 2rem; }</style>
|
|
12
|
+
</defs>
|
|
13
|
+
<title id="ftshare-settings-results-er-title">设置状态、保存结果与检测结果的值对象</title>
|
|
14
|
+
<desc id="ftshare-settings-results-er-desc">ER 图:API Key 状态继承凭据状态,配置结果分别携带本次保存结果与验证结果,验证结果由远端服务和 Key 两个探测结果组成。</desc>
|
|
15
|
+
<rect width="100%" height="100%" fill="#f5f5f5"/>
|
|
16
|
+
<rect x="60" y="180" width="300" height="188" rx="6" fill="#f5f5f5"/>
|
|
17
|
+
<rect x="60" y="180" width="300" height="188" rx="6" fill="#eb6c36" fill-opacity="0.08" stroke="#eb6c36" stroke-width="1"/>
|
|
18
|
+
<rect x="68" y="188" width="34" height="12" rx="2" fill="none" stroke="#eb6c36" stroke-width="0.8"/>
|
|
19
|
+
<text x="85" y="197" fill="#eb6c36" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
20
|
+
<text x="110" y="197" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">ApiKeyStatus</text>
|
|
21
|
+
<text x="72" y="216" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
22
|
+
<line x1="72" y1="232" x2="348" y2="232" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
23
|
+
<text x="72" y="254" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">configured: boolean</text>
|
|
24
|
+
<text x="72" y="274" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">writable: boolean</text>
|
|
25
|
+
<text x="72" y="294" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">source?: string</text>
|
|
26
|
+
<text x="72" y="314" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">maskedValue?: string(仅展示)</text>
|
|
27
|
+
<text x="72" y="334" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">revision: number(进程内)</text>
|
|
28
|
+
<text x="72" y="354" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">validation: ValidationResult</text>
|
|
29
|
+
<rect x="460" y="180" width="320" height="148" rx="6" fill="#f5f5f5"/>
|
|
30
|
+
<rect x="460" y="180" width="320" height="148" rx="6" fill="#2d3142" fill-opacity="0.03" stroke="#2d3142" stroke-opacity="0.30" stroke-width="1"/>
|
|
31
|
+
<rect x="468" y="188" width="34" height="12" rx="2" fill="none" stroke="#2d3142" stroke-opacity="0.30" stroke-width="0.8"/>
|
|
32
|
+
<text x="485" y="197" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
33
|
+
<text x="510" y="197" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">ValidationResult</text>
|
|
34
|
+
<text x="472" y="216" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
35
|
+
<line x1="472" y1="232" x2="768" y2="232" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
36
|
+
<text x="472" y="254" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">status: not_tested | passed | …</text>
|
|
37
|
+
<text x="472" y="274" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">remote: ProbeResult</text>
|
|
38
|
+
<text x="472" y="294" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">apiKey: ProbeResult</text>
|
|
39
|
+
<text x="472" y="314" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">testedAt?: string</text>
|
|
40
|
+
<rect x="860" y="180" width="320" height="108" rx="6" fill="#f5f5f5"/>
|
|
41
|
+
<rect x="860" y="180" width="320" height="108" rx="6" fill="#2d3142" fill-opacity="0.03" stroke="#2d3142" stroke-opacity="0.30" stroke-width="1"/>
|
|
42
|
+
<rect x="868" y="188" width="34" height="12" rx="2" fill="none" stroke="#2d3142" stroke-opacity="0.30" stroke-width="0.8"/>
|
|
43
|
+
<text x="885" y="197" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
44
|
+
<text x="910" y="197" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">ProbeResult</text>
|
|
45
|
+
<text x="872" y="216" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
46
|
+
<line x1="872" y1="232" x2="1168" y2="232" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
47
|
+
<text x="872" y="254" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">status: ProbeState(11 项)</text>
|
|
48
|
+
<text x="872" y="274" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">message: string</text>
|
|
49
|
+
<rect x="60" y="440" width="300" height="128" rx="6" fill="#f5f5f5"/>
|
|
50
|
+
<rect x="60" y="440" width="300" height="128" rx="6" fill="#2d3142" fill-opacity="0.03" stroke="#2d3142" stroke-opacity="0.30" stroke-width="1"/>
|
|
51
|
+
<rect x="68" y="448" width="34" height="12" rx="2" fill="none" stroke="#2d3142" stroke-opacity="0.30" stroke-width="0.8"/>
|
|
52
|
+
<text x="85" y="457" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
53
|
+
<text x="110" y="457" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">CredentialStatus</text>
|
|
54
|
+
<text x="72" y="476" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
55
|
+
<line x1="72" y1="492" x2="348" y2="492" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
56
|
+
<text x="72" y="514" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">configured: boolean</text>
|
|
57
|
+
<text x="72" y="534" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">writable: boolean</text>
|
|
58
|
+
<text x="72" y="554" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">source?: string</text>
|
|
59
|
+
<rect x="460" y="440" width="320" height="128" rx="6" fill="#f5f5f5"/>
|
|
60
|
+
<rect x="460" y="440" width="320" height="128" rx="6" fill="#2d3142" fill-opacity="0.03" stroke="#2d3142" stroke-opacity="0.30" stroke-width="1"/>
|
|
61
|
+
<rect x="468" y="448" width="34" height="12" rx="2" fill="none" stroke="#2d3142" stroke-opacity="0.30" stroke-width="0.8"/>
|
|
62
|
+
<text x="485" y="457" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
63
|
+
<text x="510" y="457" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">ConfigureResult</text>
|
|
64
|
+
<text x="472" y="476" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
65
|
+
<line x1="472" y1="492" x2="768" y2="492" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
66
|
+
<text x="472" y="514" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">save: SaveOutcome</text>
|
|
67
|
+
<text x="472" y="534" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">validation: ValidationResult</text>
|
|
68
|
+
<text x="472" y="554" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">保存失败不回滚验证结果</text>
|
|
69
|
+
<rect x="860" y="440" width="320" height="128" rx="6" fill="#f5f5f5"/>
|
|
70
|
+
<rect x="860" y="440" width="320" height="128" rx="6" fill="#2d3142" fill-opacity="0.03" stroke="#2d3142" stroke-opacity="0.30" stroke-width="1"/>
|
|
71
|
+
<rect x="868" y="448" width="34" height="12" rx="2" fill="none" stroke="#2d3142" stroke-opacity="0.30" stroke-width="0.8"/>
|
|
72
|
+
<text x="885" y="457" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ENTITY</text>
|
|
73
|
+
<text x="910" y="457" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">SaveOutcome</text>
|
|
74
|
+
<text x="872" y="476" fill="#7a8399" font-size="8" font-family="'Geist Mono', monospace">features/api-key/contract.ts</text>
|
|
75
|
+
<line x1="872" y1="492" x2="1168" y2="492" stroke="#2d3142" stroke-opacity="0.12" stroke-width="0.8"/>
|
|
76
|
+
<text x="872" y="514" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">status: saved | failed</text>
|
|
77
|
+
<text x="872" y="534" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">code?: invalid_input | read_only</text>
|
|
78
|
+
<text x="872" y="554" fill="#4f5d75" font-size="9" font-family="'Geist Mono', 'Noto Sans Mono CJK SC', monospace">message: string</text>
|
|
79
|
+
<line x1="360" y1="254" x2="460" y2="254" stroke="#4f5d75" stroke-width="1.2"/>
|
|
80
|
+
<rect x="376" y="228" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
81
|
+
<text x="384" y="237" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
82
|
+
<rect x="428" y="228" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
83
|
+
<text x="436" y="237" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
84
|
+
<rect x="381.5" y="268" width="57" height="12" rx="2" fill="#f5f5f5"/>
|
|
85
|
+
<text x="410" y="277" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">validation</text>
|
|
86
|
+
<line x1="210" y1="368" x2="210" y2="440" stroke="#4f5d75" stroke-width="1.2"/>
|
|
87
|
+
<rect x="186" y="382" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
88
|
+
<text x="194" y="391" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
89
|
+
<rect x="186" y="414" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
90
|
+
<text x="194" y="423" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
91
|
+
<rect x="218.36" y="398.0" width="42" height="12" rx="2" fill="#f5f5f5"/>
|
|
92
|
+
<text x="239.36" y="407.0" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">extends</text>
|
|
93
|
+
<line x1="620" y1="328" x2="620" y2="440" stroke="#4f5d75" stroke-width="1.2"/>
|
|
94
|
+
<rect x="596" y="342" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
95
|
+
<text x="604" y="351" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
96
|
+
<rect x="596" y="414" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
97
|
+
<text x="604" y="423" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
98
|
+
<rect x="628.3" y="378.0" width="57" height="12" rx="2" fill="#f5f5f5"/>
|
|
99
|
+
<text x="656.8" y="387.0" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">validation</text>
|
|
100
|
+
<line x1="780" y1="240" x2="860" y2="240" stroke="#4f5d75" stroke-width="1.2"/>
|
|
101
|
+
<rect x="796" y="214" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
102
|
+
<text x="804" y="223" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
103
|
+
<rect x="822.5" y="214" width="27" height="12" rx="2" fill="#f5f5f5"/>
|
|
104
|
+
<text x="836" y="223" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1..2</text>
|
|
105
|
+
<rect x="794" y="254" width="52" height="12" rx="2" fill="#f5f5f5"/>
|
|
106
|
+
<text x="820" y="263" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">two roles</text>
|
|
107
|
+
<line x1="780" y1="504" x2="860" y2="504" stroke="#4f5d75" stroke-width="1.2"/>
|
|
108
|
+
<rect x="796" y="478" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
109
|
+
<text x="804" y="487" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
110
|
+
<rect x="828" y="478" width="16" height="12" rx="2" fill="#f5f5f5"/>
|
|
111
|
+
<text x="836" y="487" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">1</text>
|
|
112
|
+
<rect x="806.5" y="518" width="27" height="12" rx="2" fill="#f5f5f5"/>
|
|
113
|
+
<text x="820" y="527" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">save</text>
|
|
114
|
+
<line x1="40" y1="640" x2="1240" y2="640" stroke="#2d3142" stroke-opacity="0.10" stroke-width="0.8"/>
|
|
115
|
+
<text x="40" y="656" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
|
|
116
|
+
<text x="40" y="684" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace">1</text>
|
|
117
|
+
<line x1="50" y1="681" x2="86" y2="681" stroke="#4f5d75" stroke-width="1.2"/>
|
|
118
|
+
<text x="94" y="684" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace">N</text>
|
|
119
|
+
<text x="114" y="684" fill="#4f5d75" font-size="8.5" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">关系基数</text>
|
|
120
|
+
<rect x="192" y="674" width="14" height="10" rx="2" fill="#eb6c36" fill-opacity="0.08" stroke="#eb6c36" stroke-width="1"/>
|
|
121
|
+
<text x="216" y="684" fill="#4f5d75" font-size="8.5" font-family="'Geist', 'PingFang SC', 'Noto Sans SC', sans-serif">聚合根</text>
|
|
122
|
+
</svg>
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# dsh_kline:实际代码与接入后代码对照
|
|
2
|
+
|
|
3
|
+
核对时间:2026-09-17。基线:`dsh_kline@d6253f07c90ebbeb25a31dd3a76565c242558d36`。
|
|
4
|
+
|
|
5
|
+
本文的“当前”摘自工作区真实源码,省略部分用注释标记。“接入后”是待 kline 实现的示例,不代表本包已修改 kline。FTShare Settings 不提供 Python 明文凭据 HTTP 服务;下面的 `privateBridge` / `settings_bridge` 是 kline 需要实现的私有 IPC 适配层。
|
|
6
|
+
|
|
7
|
+
## 0. 落地状态(2026-10-14 更新)
|
|
8
|
+
|
|
9
|
+
kline 侧已实现**凭据这一段**,但不是本文设想的私有 IPC,而是更小的文件契约:`FTShare-Lab/dsh_kline` PR #10(分支 `feat/ftshare-settings-bridge`,提交 `21539cd`,基线 `v0.3.5`)。该实现已随上游 `v0.4.0` 发布(上游未合 PR,而是本地整合并加了校验修正),本包现在钉的是这个上游版本,不再跟 `-ft` 补丁 tag。
|
|
10
|
+
|
|
11
|
+
- 已实现:`src/ftshare-settings-bridge.ts` 用 `apiKey.resolve()` + `subscribe()` 把当前 Key 发布到 `<runtime>/credentials/ftshare-<hostPid>.json`(`0600`/目录 `0700`、原子替换、宿主退出即删、启动时清理已死宿主的残留);`src/index.ts` 以 `ctx.inject(['ftshareSettings'], …)` 起桥,`inject` 仍是 `['webServer']`,没有该服务的宿主行为不变;启动器仅在拿到 `DSH_KLINE_HOST_PID` 时注入 `DSH_KLINE_SETTINGS_KEY_FILE`;`tools/fetch.py` 在**每次**建 FTShare 客户端前重读该文件,来源标记为 `settings_bridge` 且 `can_clear=false`(该 Key 归设置页所有)。
|
|
12
|
+
- 取代 IPC 的理由:两侧本就共享运行时目录(`services/<hostPid>.json` 已是同一约定),文件无需握手、实例认证与 Python 重连,跨 MCP 重启依然成立;`createEnvironment()` 仍未使用(它只是启动快照,本文档对它的判断不变)。
|
|
13
|
+
- 尚未实现:§2/§3 的工具委托——`configure_ftshare` / `test_ftshare_connection` / `data_source_status` 仍走 kline 自己的本地实现,因此“保存与测试结果不混淆”的适配层、Python 侧 IPC 客户端与 `persist=false` 的语义统一都还缺。
|
|
14
|
+
- 与 §4 的差异:当前实现**保留**了环境变量与插件自有 JSON 回退,只是把顺序固定为 显式 `FTSHARE_API_KEY_FILE` → 设置桥文件 → 插件自有存储;并非本文设想的“DSH 模式停用旧回退”。这一条要继续推进还是就此定型,取决于是否要让设置页成为唯一凭据来源。
|
|
15
|
+
|
|
16
|
+
## 1. Host:现在只向 Python 转发
|
|
17
|
+
|
|
18
|
+
当前 `dsh_kline/src/index.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
export const name = 'dsh-kline-sidebar'
|
|
22
|
+
export const inject = ['webServer']
|
|
23
|
+
|
|
24
|
+
export function apply(ctx: WebServerContext): void {
|
|
25
|
+
ctx.effect(() => ctx.webServer.register({
|
|
26
|
+
kind: 'prefix',
|
|
27
|
+
path: '/dsh-kline',
|
|
28
|
+
handler: serveRuntimeSession,
|
|
29
|
+
}), 'dsh-kline: chart session route')
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
同一文件的 `serveRuntimeSession` 当前分支(包括 `configure_ftshare` 和 `test_ftshare_connection`):
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const body = await readRequestBody(request)
|
|
37
|
+
await proxyJson(response, `${serviceOrigin(session)}/api/tools/${encodeURIComponent(action)}`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { ...chartServiceHeaders(session), 'Content-Type': 'application/json' },
|
|
40
|
+
body: body.toString('utf8'),
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
因此只改 Host 页面的代理分支,仍覆盖不了 MCP 直接调用 Python 的入口。建议保留行情代理,让两种入口最终都从 Python 私有桥调用同一个 Host 适配器。
|
|
45
|
+
|
|
46
|
+
接入后 kline 仓库 `src/index.ts` 的关键变化(本节与下文写作 `src/…`、`scripts/…` 的路径都相对 kline 仓库根,不是本包):
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
50
|
+
import type {} from '@ft/dsh-fft-settings'
|
|
51
|
+
import { createKlineFtshareAdapter } from './ftshare-adapter.js'
|
|
52
|
+
// 由 kline 新增,不是 FTShare Settings 已有导出:
|
|
53
|
+
import { startPrivateSettingsBridge } from './private-settings-bridge.js'
|
|
54
|
+
|
|
55
|
+
export const inject = ['webServer', 'ftshareSettings']
|
|
56
|
+
export function apply(ctx: Context): void {
|
|
57
|
+
const adapter = createKlineFtshareAdapter(ctx.ftshareSettings.apiKey)
|
|
58
|
+
// 保留原 /dsh-kline 行情路由注册。
|
|
59
|
+
ctx.effect(() => ctx.webServer.register({
|
|
60
|
+
kind: 'prefix', path: '/dsh-kline', handler: serveRuntimeSession,
|
|
61
|
+
}), 'dsh-kline: chart session route')
|
|
62
|
+
|
|
63
|
+
// 私有 IPC 必须仅允许当前实例的 Python 访问,不能注册为浏览器公开路由。
|
|
64
|
+
const bridge = startPrivateSettingsBridge(adapter)
|
|
65
|
+
ctx.effect(() => () => bridge.dispose(), 'kline: private settings bridge')
|
|
66
|
+
ctx.effect(() => ctx.ftshareSettings.apiKey.subscribe(() => {
|
|
67
|
+
bridge.notifyCredentialChanged() // 只发失效事件,不带 Key
|
|
68
|
+
}), 'kline: credential invalidation')
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`startPrivateSettingsBridge` 的实现、实例认证、启动信息交接和 Python 重连均属于 kline 后续工作;此处没有伪装成可直接运行的完整桥。创建桥后,需要在现有 `cordis.patch.yml` → `scripts/start-mcp.mjs` → `server.py` 启动链传递其连接参数,并保证 Python 连接到同一个 Host 实例。`createEnvironment()` 只提供启动快照,不能替代动态凭据桥。
|
|
73
|
+
|
|
74
|
+
> 2026-10-14:凭据这一段已按 §0 的文件契约落地(PR #10),未采用本节的 `startPrivateSettingsBridge`/适配器注入路径;本节其余内容仍是未完成的设想。
|
|
75
|
+
|
|
76
|
+
## 2. 新增 Host 适配器:保存与测试结果不混淆(未落地)
|
|
77
|
+
|
|
78
|
+
建议新增 `dsh_kline/src/ftshare-adapter.ts`:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import type { ApiKeySettings, ValidationResult } from '@ft/dsh-fft-settings/contract'
|
|
82
|
+
|
|
83
|
+
const failure = (message: string) => ({ ok: false, message })
|
|
84
|
+
const tested = (validation: ValidationResult) => ({
|
|
85
|
+
ok: validation.status === 'passed',
|
|
86
|
+
connection_ok: validation.remote.status === 'available',
|
|
87
|
+
key_ok: validation.apiKey.status === 'available',
|
|
88
|
+
validation,
|
|
89
|
+
message: `${validation.remote.message};${validation.apiKey.message}`,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
export function createKlineFtshareAdapter(apiKey: ApiKeySettings) {
|
|
93
|
+
return {
|
|
94
|
+
async configure(args: {
|
|
95
|
+
api_key?: string | null
|
|
96
|
+
test_connection?: boolean
|
|
97
|
+
persist?: boolean
|
|
98
|
+
}) {
|
|
99
|
+
if (args.persist === false) return failure('DSH 模式不支持临时 Key,请使用持久化配置')
|
|
100
|
+
const value = (args.api_key ?? '').trim()
|
|
101
|
+
const shouldTest = args.test_connection !== false
|
|
102
|
+
try {
|
|
103
|
+
if (!value) {
|
|
104
|
+
await apiKey.clear()
|
|
105
|
+
const status = await apiKey.describe() // 清除可能露出 .env 回退值
|
|
106
|
+
const validation = shouldTest ? await apiKey.testConnection() : undefined
|
|
107
|
+
return {
|
|
108
|
+
...(validation ? tested(validation) : { ok: true }),
|
|
109
|
+
key_saved: false,
|
|
110
|
+
configured: status.configured,
|
|
111
|
+
source: status.source,
|
|
112
|
+
cleared: true,
|
|
113
|
+
message: validation
|
|
114
|
+
? `已清除 DSH 保存值;${validation.remote.message};${validation.apiKey.message}`
|
|
115
|
+
: '已清除 DSH 保存值',
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!shouldTest) {
|
|
119
|
+
await apiKey.set(value)
|
|
120
|
+
return { ok: true, key_saved: true, message: '已保存,未测试' }
|
|
121
|
+
}
|
|
122
|
+
const result = await apiKey.configureAndTest(value)
|
|
123
|
+
return {
|
|
124
|
+
...tested(result.validation),
|
|
125
|
+
ok: result.save.status === 'saved' && result.validation.status === 'passed',
|
|
126
|
+
key_saved: result.save.status === 'saved',
|
|
127
|
+
save: result.save,
|
|
128
|
+
message: `${result.save.message};${result.validation.remote.message};${result.validation.apiKey.message}`,
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
return failure('配置请求未完成,请重新读取状态;不能据此断言未保存')
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
async test() { return tested(await apiKey.testConnection()) },
|
|
135
|
+
async status() {
|
|
136
|
+
const { configured, source, writable, validation } = await apiKey.describe()
|
|
137
|
+
return { configured, source, writable, validation }
|
|
138
|
+
},
|
|
139
|
+
// 只供认证后的私有 Python IPC;禁止进入浏览器 RPC 或模型工具返回值。
|
|
140
|
+
async resolveForPython() { return { value: (await apiKey.resolve()) ?? null } },
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
桥的输入层仍须校验 JSON 字段类型、未知字段、大小和操作白名单;TypeScript 类型不代替运行时校验。`resolveForPython` 只能供 Python 内部行情请求使用。不能把 `Object.keys(adapter)` 全部注册到公共 RPC。
|
|
146
|
+
|
|
147
|
+
## 3. Python MCP:当前调用本地配置函数(未落地)
|
|
148
|
+
|
|
149
|
+
当前 `dsh_kline/server.py` 的函数体:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# configure_ftshare(...) 内
|
|
153
|
+
data = configure_ftshare_api_key(api_key, test_connection=test_connection, persist=persist)
|
|
154
|
+
if not data.get("ok"):
|
|
155
|
+
return _result(data, str(data.get("message") or data.get("error") or "FTShare 配置失败"), error=True)
|
|
156
|
+
return _result(data, str(data.get("message") or "FTShare 配置已更新"))
|
|
157
|
+
|
|
158
|
+
# test_ftshare_connection_tool() 内
|
|
159
|
+
data = test_ftshare_connection()
|
|
160
|
+
if not data.get("ok"):
|
|
161
|
+
return _result(data, str(data.get("message") or data.get("error") or "FTShare 连接失败"), error=True)
|
|
162
|
+
return _result(data, str(data.get("message") or "FTShare 连接成功"))
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
当前 `tools/fetch.py::configure_ftshare_api_key` 的核心行为(摘录):
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
if persist:
|
|
169
|
+
if value:
|
|
170
|
+
_write_persisted_ftshare_key(value)
|
|
171
|
+
else:
|
|
172
|
+
_remove_persisted_ftshare_key()
|
|
173
|
+
# 省略异常处理
|
|
174
|
+
if value:
|
|
175
|
+
os.environ["FTSHARE_API_KEY"] = value
|
|
176
|
+
else:
|
|
177
|
+
os.environ.pop("FTSHARE_API_KEY", None)
|
|
178
|
+
clear_provider_caches()
|
|
179
|
+
# ...
|
|
180
|
+
connection = test_ftshare_connection()
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
接入后,DSH 模式在同名 helper 中委托 Host;这样 `server.py` 和 `chart_service.py` 的调用可复用一个适配点:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
# tools/fetch.py:建议替换 DSH 模式下的实现
|
|
187
|
+
# settings_bridge 是由 kline 实现的同步、带超时且每实例认证的 IPC 客户端。
|
|
188
|
+
from .settings_bridge import settings_bridge
|
|
189
|
+
|
|
190
|
+
def configure_ftshare_api_key(api_key, *, test_connection=True, persist=True):
|
|
191
|
+
result = settings_bridge.call("configure", {
|
|
192
|
+
"api_key": api_key,
|
|
193
|
+
"test_connection": test_connection,
|
|
194
|
+
"persist": persist,
|
|
195
|
+
})
|
|
196
|
+
# 即使测试失败,保存也可能已成功,缓存必须失效。
|
|
197
|
+
clear_provider_caches()
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
def test_ftshare_connection():
|
|
201
|
+
return settings_bridge.call("test", {})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
在 `server.py` 的 async MCP 函数中使用 `await asyncio.to_thread(configure_ftshare_api_key, ...)` 和 `await asyncio.to_thread(test_ftshare_connection)`,避免同步 IPC 阻塞事件循环。原 `_result(...)` 封装可以保留,`data` 中的 `key_saved`、`save`、`validation` 分别说明保存和测试结果。
|
|
205
|
+
|
|
206
|
+
`data_source_status` 中,SDK 安装状态和版本仍由 kline 本地计算;`configured/source/validation` 改用 `settings_bridge.call("status", {})`。不要直接用 Host 的状态替换包含 SDK 信息的整个工具响应。
|
|
207
|
+
|
|
208
|
+
## 4. Python 行情:每次操作读取当前 Key(已按文件契约落地,见 §0)
|
|
209
|
+
|
|
210
|
+
当前 `tools/fetch.py::_ftshare_market_api`:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
def _ftshare_market_api(timeout: float) -> Any:
|
|
214
|
+
"""Create an SDK client with the process key attached to every request."""
|
|
215
|
+
import ftshare as ft
|
|
216
|
+
|
|
217
|
+
key = _valid_ftshare_key(os.environ.get("FTSHARE_API_KEY"))
|
|
218
|
+
headers = {"FTSHARE_API_KEY": key} if key else None
|
|
219
|
+
return ft.market_api(timeout=timeout, headers=headers)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
接入后(DSH 模式;这是本文设想的 IPC 版本,kline 实际改用 §0 的文件契约):
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
def _ftshare_market_api(timeout: float) -> Any:
|
|
226
|
+
import ftshare as ft
|
|
227
|
+
|
|
228
|
+
resolved = settings_bridge.call("resolveForPython", {})
|
|
229
|
+
key = _valid_ftshare_key(resolved.get("value"))
|
|
230
|
+
# 未配置就中止,避免 SDK 自动使用旧环境变量或本地文件作为隐藏回退。
|
|
231
|
+
if not key:
|
|
232
|
+
raise RuntimeError("请先在 FTShare 设置中配置 API Key")
|
|
233
|
+
return ft.market_api(timeout=timeout, headers={"FTSHARE_API_KEY": key})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
一次行情操作使用同一客户端/Key 快照,不应在逐根 K 线处理时反复读取。DSH 模式要停用 `_load_persisted_ftshare_key()` 和旧凭据 JSON 回退,不能同时维护两份配置。独立运行模式可以保留现有本地策略,但必须明确分支。Host 通知用于失效 kline 缓存,不能替代请求时解析。
|
|
237
|
+
|
|
238
|
+
## 验收边界
|
|
239
|
+
|
|
240
|
+
- 两个入口都覆盖:浏览器图表 HTTP、Python MCP。
|
|
241
|
+
- 设置页改 Key 后,已运行 Python 的下一次操作使用新值。
|
|
242
|
+
- 清除后不继续使用旧 Key;存在 DSH 环境回退时使用实际生效值。
|
|
243
|
+
- 保存成功、检测失败时仍保留 `key_saved=true`;不能只显示“保存失败”。
|
|
244
|
+
- `persist=false` 不静默变为持久化。
|
|
245
|
+
- 明文只流经 Host → 私有 Python IPC → FTShare,不出现在浏览器/MCP 工具结果/日志中。
|
|
246
|
+
- `validation.remote` 和 `validation.apiKey` 分别表示服务与认证,不代表行情数据完整性或全部套餐权限。
|
|
247
|
+
|
|
248
|
+
### 截至 2026-10-14 的达成情况
|
|
249
|
+
|
|
250
|
+
| 边界 | 状态 |
|
|
251
|
+
| --- | --- |
|
|
252
|
+
| 两个入口都覆盖 | ✅ 文件契约对浏览器代理与 MCP 直调同时生效(同一份 `tools/fetch.py`) |
|
|
253
|
+
| 设置页改 Key 后,下一次操作使用新值 | ✅ 每次建客户端前重读 |
|
|
254
|
+
| 清除后不继续使用旧 Key | ⚠️ 该文件被清除后不再使用,但环境变量/插件自有存储的回退仍可提供另一个 Key(§4 未收敛项) |
|
|
255
|
+
| 保存成功、检测失败仍保留 `key_saved=true` | ❌ 工具委托未做,仍由 kline 本地实现 |
|
|
256
|
+
| `persist=false` 不静默变持久化 | ❌ 同上,既有行为未变 |
|
|
257
|
+
| 明文只流经 Host → Python → FTShare | ✅ 改为 Host → `0600` 文件 → Python → FTShare,不入日志/工具结果 |
|
|
258
|
+
| 两个 validation 字段的含义 | ✅ 不变(设置页自身语义) |
|
package/lib/agent.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-plane entry for the FT prompt contract.
|
|
3
|
+
*
|
|
4
|
+
* Mounted by an agent preset so the contract exists only for that preset's
|
|
5
|
+
* agents; the package's host entry keeps providing the settings service and
|
|
6
|
+
* must not be mounted from a preset (a service row there needs an isolate
|
|
7
|
+
* realm). The texts and the registration both live in `./guidance.ts`, so this
|
|
8
|
+
* entry adds no second copy.
|
|
9
|
+
* @module @dsh-ft/dsh-fft-settings/agent
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
/** Cordis plugin name. */
|
|
13
|
+
export declare const name = "@dsh-ft/dsh-fft-settings/agent";
|
|
14
|
+
/**
|
|
15
|
+
* The prompt registry this entry contributes to. `ftshareSettings` is optional
|
|
16
|
+
* and mounted in `installGuidance`, so a missing settings row costs only the
|
|
17
|
+
* credential fact instead of the whole contract.
|
|
18
|
+
*/
|
|
19
|
+
export declare const inject: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Register the FT prompt contract in the mounting scope.
|
|
22
|
+
* @param ctx - the preset subtree's context; the preset's agents are the scope.
|
|
23
|
+
*/
|
|
24
|
+
export declare function apply(ctx: Context): void;
|
package/lib/agent.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-plane entry for the FT prompt contract.
|
|
3
|
+
*
|
|
4
|
+
* Mounted by an agent preset so the contract exists only for that preset's
|
|
5
|
+
* agents; the package's host entry keeps providing the settings service and
|
|
6
|
+
* must not be mounted from a preset (a service row there needs an isolate
|
|
7
|
+
* realm). The texts and the registration both live in `./guidance.ts`, so this
|
|
8
|
+
* entry adds no second copy.
|
|
9
|
+
* @module @dsh-ft/dsh-fft-settings/agent
|
|
10
|
+
*/
|
|
11
|
+
import { installGuidance } from './guidance.js';
|
|
12
|
+
/** Cordis plugin name. */
|
|
13
|
+
export const name = '@dsh-ft/dsh-fft-settings/agent';
|
|
14
|
+
/**
|
|
15
|
+
* The prompt registry this entry contributes to. `ftshareSettings` is optional
|
|
16
|
+
* and mounted in `installGuidance`, so a missing settings row costs only the
|
|
17
|
+
* credential fact instead of the whole contract.
|
|
18
|
+
*/
|
|
19
|
+
export const inject = ['systemPrompt'];
|
|
20
|
+
/**
|
|
21
|
+
* Register the FT prompt contract in the mounting scope.
|
|
22
|
+
* @param ctx - the preset subtree's context; the preset's agents are the scope.
|
|
23
|
+
*/
|
|
24
|
+
export function apply(ctx) {
|
|
25
|
+
installGuidance(ctx);
|
|
26
|
+
}
|
package/lib/brand.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FT brand tokens published as a host theme override layer.
|
|
3
|
+
*
|
|
4
|
+
* The layer only *adds* brand variables on `body`; it never overrides a
|
|
5
|
+
* `--dsw-alias-*` value, so host chrome keeps its own palette and any user
|
|
6
|
+
* theme keeps working underneath. Consumers read `var(--ft-*)` with the same
|
|
7
|
+
* value as their fallback: the layer is a convenience, not a dependency, so
|
|
8
|
+
* every sub-plugin keeps its brand colors when this plugin is absent (or when
|
|
9
|
+
* the host has no `theme` service at all). Scheme-invariant tokens repeat one
|
|
10
|
+
* value; palette-aware tokens spell both with `light-dark()`.
|
|
11
|
+
*
|
|
12
|
+
* Keep those fallbacks equal to {@link FT_BRAND_TOKENS}; `tests/brand.test.mjs`
|
|
13
|
+
* scans every package for the pairing and fails on a drift.
|
|
14
|
+
*/
|
|
15
|
+
/** One token value per palette mode; the host rejects a bare string. */
|
|
16
|
+
export interface BrandTokenModes {
|
|
17
|
+
readonly light: string;
|
|
18
|
+
readonly dark: string;
|
|
19
|
+
}
|
|
20
|
+
/** Layer identity reported to `ctx.theme.overrideTokens` (one layer per source). */
|
|
21
|
+
export declare const FT_BRAND_SOURCE = "@dsh-ft/dsh-fft-settings";
|
|
22
|
+
/**
|
|
23
|
+
* The published brand colors.
|
|
24
|
+
*
|
|
25
|
+
* `accent` is the FT yellow used by this plugin's primary action and eyebrow;
|
|
26
|
+
* `accent-warm` is the orange trading accent shared by market and trade
|
|
27
|
+
* surfaces (it is the only palette-aware group); `wechat` is the enterprise-
|
|
28
|
+
* WeChat green the SSO card keeps as its identity. Inks are the text colors on
|
|
29
|
+
* those fills, and `-soft` is the tinted surface behind the accent.
|
|
30
|
+
*/
|
|
31
|
+
export declare const FT_BRAND_TOKENS: Readonly<Record<string, BrandTokenModes>>;
|
|
32
|
+
/** The slice of the host theme service this layer uses. */
|
|
33
|
+
export interface BrandThemeService {
|
|
34
|
+
overrideTokens(source: string, tokens: Readonly<Record<string, BrandTokenModes>>): () => void;
|
|
35
|
+
}
|
|
36
|
+
/** Scoped context delivered once the `theme` service is available. */
|
|
37
|
+
export interface BrandThemeScope {
|
|
38
|
+
readonly theme: BrandThemeService;
|
|
39
|
+
effect(callback: () => () => void, label: string): void;
|
|
40
|
+
}
|
|
41
|
+
/** The slice of the client context this layer needs: a deferred service inject. */
|
|
42
|
+
export interface BrandThemeContext {
|
|
43
|
+
inject(deps: readonly string[], callback: (scope: BrandThemeScope) => void): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Stack the brand layer once the host theme service exists.
|
|
47
|
+
*
|
|
48
|
+
* The deferred `inject` is deliberate: this plugin's declared client context
|
|
49
|
+
* names only the services its cards read, and a host without `theme` must
|
|
50
|
+
* still load the settings page — the CSS fallbacks then carry the colors.
|
|
51
|
+
* @param ctx - client context carrying the host theme service.
|
|
52
|
+
*/
|
|
53
|
+
export declare function applyBrandTokens(ctx: BrandThemeContext): void;
|
package/lib/brand.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FT brand tokens published as a host theme override layer.
|
|
3
|
+
*
|
|
4
|
+
* The layer only *adds* brand variables on `body`; it never overrides a
|
|
5
|
+
* `--dsw-alias-*` value, so host chrome keeps its own palette and any user
|
|
6
|
+
* theme keeps working underneath. Consumers read `var(--ft-*)` with the same
|
|
7
|
+
* value as their fallback: the layer is a convenience, not a dependency, so
|
|
8
|
+
* every sub-plugin keeps its brand colors when this plugin is absent (or when
|
|
9
|
+
* the host has no `theme` service at all). Scheme-invariant tokens repeat one
|
|
10
|
+
* value; palette-aware tokens spell both with `light-dark()`.
|
|
11
|
+
*
|
|
12
|
+
* Keep those fallbacks equal to {@link FT_BRAND_TOKENS}; `tests/brand.test.mjs`
|
|
13
|
+
* scans every package for the pairing and fails on a drift.
|
|
14
|
+
*/
|
|
15
|
+
/** One value for both palettes: brand fills are scheme-invariant. */
|
|
16
|
+
const invariant = (value) => ({ light: value, dark: value });
|
|
17
|
+
/** Layer identity reported to `ctx.theme.overrideTokens` (one layer per source). */
|
|
18
|
+
export const FT_BRAND_SOURCE = '@dsh-ft/dsh-fft-settings';
|
|
19
|
+
/**
|
|
20
|
+
* The published brand colors.
|
|
21
|
+
*
|
|
22
|
+
* `accent` is the FT yellow used by this plugin's primary action and eyebrow;
|
|
23
|
+
* `accent-warm` is the orange trading accent shared by market and trade
|
|
24
|
+
* surfaces (it is the only palette-aware group); `wechat` is the enterprise-
|
|
25
|
+
* WeChat green the SSO card keeps as its identity. Inks are the text colors on
|
|
26
|
+
* those fills, and `-soft` is the tinted surface behind the accent.
|
|
27
|
+
*/
|
|
28
|
+
export const FT_BRAND_TOKENS = Object.freeze({
|
|
29
|
+
'--ft-accent': invariant('#f0c433'),
|
|
30
|
+
'--ft-accent-ink': invariant('#171717'),
|
|
31
|
+
'--ft-accent-warm': { light: '#cf5623', dark: '#ff8352' },
|
|
32
|
+
'--ft-accent-warm-hover': { light: '#b8481b', dark: '#ff9a72' },
|
|
33
|
+
'--ft-accent-warm-ink': { light: '#ffffff', dark: '#1a120e' },
|
|
34
|
+
'--ft-accent-warm-soft': { light: '#fbeee8', dark: '#2b1d16' },
|
|
35
|
+
'--ft-wechat': invariant('#2ebd68'),
|
|
36
|
+
'--ft-wechat-ink': invariant('#102419'),
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Stack the brand layer once the host theme service exists.
|
|
40
|
+
*
|
|
41
|
+
* The deferred `inject` is deliberate: this plugin's declared client context
|
|
42
|
+
* names only the services its cards read, and a host without `theme` must
|
|
43
|
+
* still load the settings page — the CSS fallbacks then carry the colors.
|
|
44
|
+
* @param ctx - client context carrying the host theme service.
|
|
45
|
+
*/
|
|
46
|
+
export function applyBrandTokens(ctx) {
|
|
47
|
+
ctx.inject(['theme'], (scope) => {
|
|
48
|
+
scope.effect(() => scope.theme.overrideTokens(FT_BRAND_SOURCE, FT_BRAND_TOKENS), 'ft-settings: FT brand tokens');
|
|
49
|
+
});
|
|
50
|
+
}
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ApiKeyCardContext } from './features/api-key/card.js';
|
|
2
|
+
export * from './core/shell.js';
|
|
3
|
+
/** Browser context this plugin's own client half requires. */
|
|
4
|
+
export type FtshareSettingsClientContext = ApiKeyCardContext;
|
|
5
|
+
export declare const inject: string[];
|
|
6
|
+
export declare function apply(ctx: FtshareSettingsClientContext): void;
|
|
7
|
+
/** Contribute the section shell, then the built-in cards through the slot it declares. */
|
|
8
|
+
export declare function registerFtshareSettings(ctx: FtshareSettingsClientContext): void;
|