dsh-origin-bridge 0.4.4 → 0.4.5

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/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  <p align="center">
18
18
  <a href="#安装"><img alt="platform" src="https://img.shields.io/badge/platform-Windows%20%2B%20Origin%202021%2B-blue"></a>
19
- <img alt="tests" src="https://img.shields.io/badge/tests-231%2B%20passing-brightgreen">
19
+ <img alt="tests" src="https://img.shields.io/badge/tests-1079%2F1079%20passing-brightgreen">
20
20
  <img alt="license" src="https://img.shields.io/badge/license-MIT-lightgrey">
21
21
  </p>
22
22
 
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,76 +1,82 @@
1
- {
2
- "name": "dsh-origin-bridge",
3
- "version": "0.4.4",
4
- "description": "Drive local Origin/OriginPro from chat: 27 MCP tools, 1079 full-chain test cases, import .dat/.csv/.txt/workstation .bin data, plot, fit, style, export and save editable .opju projects. Third-party tool, not affiliated with or endorsed by OriginLab Corporation.",
5
- "license": "MIT",
6
- "type": "module",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/caob23/dsh-origin-bridge.git"
10
- },
11
- "bugs": {
12
- "url": "https://github.com/caob23/dsh-origin-bridge/issues"
13
- },
14
- "homepage": "https://github.com/caob23/dsh-origin-bridge#readme",
15
- "main": "./index.js",
16
- "icon": "./icon.svg",
17
- "dependencies": {
18
- "@deepseek-ai/dsh-mcp-client": ">=0.1.0-rc.1"
19
- },
20
- "bin": {
21
- "origin-bridge": "bin/origin-bridge.mjs"
22
- },
23
- "exports": {
24
- ".": "./index.js",
25
- "./package.json": "./package.json",
26
- "./locale/*.json": "./locale/*.json"
27
- },
28
- "files": [
29
- "index.js",
30
- "bin/origin-bridge.mjs",
31
- "install_dsh.ps1",
32
- "cordis.patch.yml",
33
- "icon.svg",
34
- "locale/en.json",
35
- "locale/zh.json",
36
- "docs/preview.png",
37
- "scripts/make_icon.py",
38
- "scripts/make_preview.py",
39
- "server.py",
40
- "originlab.py",
41
- "asciiio.py",
42
- "binio.py",
43
- "templates_catalog.py",
44
- "fixtures/make_fixtures.py",
45
- "fixtures/*.dat",
46
- "fixtures/*.csv",
47
- "fixtures/*.txt",
48
- "smoke_test.py",
49
- "edge_checks.py",
50
- "test_asciiio.py",
51
- "test_binio.py",
52
- "test_lifecycle.py",
53
- "client_check.py",
54
- "requirements.txt",
55
- "sample.dat",
56
- "README.md",
57
- "LICENSE"
58
- ],
59
- "dsh": {
60
- "bundle": {
61
- "patch": "./cordis.patch.yml"
62
- }
63
- },
64
- "keywords": [
65
- "dsh",
66
- "dsh-plugin",
67
- "dsh-bundle",
68
- "deepseek-harness",
69
- "mcp",
70
- "origin",
71
- "originpro",
72
- "opju",
73
- "scientific-plotting",
74
- "electrochemistry"
75
- ]
76
- }
1
+ {
2
+ "name": "dsh-origin-bridge",
3
+ "version": "0.4.5",
4
+ "description": "在对话里驱动本机 Origin / OriginPro:导入 .dat/.csv/.txt 与电化学工作站 .bin,绘图、拟合、改样式,导出图片并保存可在 Origin 里继续编辑的 .opju 工程。dsh 插件,27 个 MCP 工具。第三方工具,与 OriginLab Corporation 无关联、未获认可。",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/caob23/dsh-origin-bridge.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/caob23/dsh-origin-bridge/issues"
13
+ },
14
+ "homepage": "https://github.com/caob23/dsh-origin-bridge#readme",
15
+ "main": "./index.js",
16
+ "icon": "./icon.svg",
17
+ "dependencies": {
18
+ "@deepseek-ai/dsh-mcp-client": ">=0.1.0-rc.1"
19
+ },
20
+ "bin": {
21
+ "origin-bridge": "bin/origin-bridge.mjs"
22
+ },
23
+ "exports": {
24
+ ".": "./index.js",
25
+ "./package.json": "./package.json",
26
+ "./locale/*.json": "./locale/*.json"
27
+ },
28
+ "files": [
29
+ "index.js",
30
+ "bin/origin-bridge.mjs",
31
+ "install_dsh.ps1",
32
+ "cordis.patch.yml",
33
+ "icon.svg",
34
+ "locale/en.json",
35
+ "locale/zh.json",
36
+ "docs/preview.png",
37
+ "scripts/make_screenshots.py",
38
+ "docs/shot-workstation.png",
39
+ "docs/shot-heatmap.png",
40
+ "docs/shot-eis.png",
41
+ "docs/shot-cv.png",
42
+ "screenshots.json",
43
+ "scripts/make_icon.py",
44
+ "scripts/make_preview.py",
45
+ "server.py",
46
+ "originlab.py",
47
+ "asciiio.py",
48
+ "binio.py",
49
+ "templates_catalog.py",
50
+ "fixtures/make_fixtures.py",
51
+ "fixtures/*.dat",
52
+ "fixtures/*.csv",
53
+ "fixtures/*.txt",
54
+ "smoke_test.py",
55
+ "edge_checks.py",
56
+ "test_asciiio.py",
57
+ "test_binio.py",
58
+ "test_lifecycle.py",
59
+ "client_check.py",
60
+ "requirements.txt",
61
+ "sample.dat",
62
+ "README.md",
63
+ "LICENSE"
64
+ ],
65
+ "dsh": {
66
+ "bundle": {
67
+ "patch": "./cordis.patch.yml"
68
+ }
69
+ },
70
+ "keywords": [
71
+ "dsh",
72
+ "dsh-plugin",
73
+ "dsh-bundle",
74
+ "deepseek-harness",
75
+ "mcp",
76
+ "origin",
77
+ "originpro",
78
+ "opju",
79
+ "scientific-plotting",
80
+ "electrochemistry"
81
+ ]
82
+ }
@@ -0,0 +1,7 @@
1
+ [
2
+ "docs/preview.png",
3
+ "docs/shot-cv.png",
4
+ "docs/shot-eis.png",
5
+ "docs/shot-heatmap.png",
6
+ "docs/shot-workstation.png"
7
+ ]
@@ -0,0 +1,187 @@
1
+ """Regenerate the storefront screenshots in docs/ through the real engine.
2
+
3
+ Every image here is a genuine Origin export built from closed-form synthetic
4
+ data: no measurement from the user's lab ever appears in a screenshot, and the
5
+ files are reproducible by rerunning this script.
6
+
7
+ python scripts/make_screenshots.py # 全部四张
8
+ python scripts/make_screenshots.py --only cv # 只重做一张
9
+
10
+ The screenshots are declared to the plugin market by the repository-root
11
+ screenshots.json, which points at these paths.
12
+ """
13
+
14
+ import math
15
+ import os
16
+ import sys
17
+ import tempfile
18
+
19
+ HERE = os.path.dirname(os.path.abspath(__file__))
20
+ ROOT = os.path.dirname(HERE)
21
+ sys.path.insert(0, ROOT)
22
+
23
+ import originlab # noqa: E402
24
+
25
+ DOCS = os.path.join(ROOT, "docs")
26
+
27
+
28
+ def _cv():
29
+ """A synthetic reversible voltammogram: forward and reverse sweep, mA/cm2."""
30
+ pts = []
31
+ e_up, e_dn, step = 0.8, -0.2, 0.005
32
+ e = e_up
33
+ direction = -1.0
34
+ while True:
35
+ anodic = 0.42 * math.exp(-((e - 0.42) ** 2) / 0.0045)
36
+ cathodic = -0.36 * math.exp(-((e + 0.06) ** 2) / 0.0055)
37
+ base = 0.02 * e + 0.006 * math.sin(e * 9.0)
38
+ pts.append((round(e, 4), round(anodic + cathodic + base, 5)))
39
+ e = round(e + direction * step, 4)
40
+ if direction < 0 and e <= e_dn:
41
+ direction = 1.0
42
+ if direction > 0 and e >= e_up:
43
+ break
44
+ return {"E / V": [p[0] for p in pts], "i / mA cm-2": [p[1] for p in pts]}
45
+
46
+
47
+ def _bode(n=41):
48
+ """Randles-style synthetic impedance: log-frequency sweep."""
49
+ rs, rct, c = 12.0, 180.0, 6e-5
50
+ freq, zre, zim = [], [], []
51
+ for i in range(n):
52
+ f = 1e5 / (10 ** (i * 0.175))
53
+ w = 2 * math.pi * f
54
+ den = 1 + (w * rct * c) ** 2
55
+ freq.append(round(f, 4))
56
+ zre.append(round(rs + rct / den, 4))
57
+ zim.append(round(w * rct ** 2 * c / den, 4))
58
+ return {"Freq / Hz": freq, "Z' / ohm": zre, "Z'' / ohm": zim}
59
+
60
+
61
+ def _grid(rows=12, cols=12):
62
+ return [[round(3.0 * math.sin(i / 2.4) + 2.0 * math.cos(j / 3.1)
63
+ + 0.6 * math.sin((i + j) / 2.0), 4) for j in range(cols)] for i in range(rows)]
64
+
65
+
66
+ def _workstation_txt():
67
+ """A CH Instruments style export, written to a temp file, to show the .txt route."""
68
+ lines = [
69
+ "Jan. 04, 2026 09:12:07", "Linear Sweep Voltammetry",
70
+ "File: d:\\demo\\synthetic_lsv.txt", "Data Source: Experiment",
71
+ "Instrument Model: CHI660E", "Header: ", "Note: ", "",
72
+ "Init E (V) = 0.0", "Final E (V) = 0.6", "Scan Rate (V/s) = 0.05",
73
+ "Sample Interval (V) = 0.002", "Quiet Time (sec) = 2",
74
+ "Sensitivity (A/V) = 0.0001", "", "Results:", "", "Channel 1:",
75
+ "Segment 1:", "", "Potential/V, Current/A", "",
76
+ ]
77
+ for i in range(120):
78
+ e = i * 0.005
79
+ lines.append("%.3f, %.3e" % (e, 1.2e-5 + 3.4e-5 / (1 + math.exp(-(e - 0.24) * 26))
80
+ + 4e-7 * math.sin(i / 3.0)))
81
+ fd, path = tempfile.mkstemp(prefix="synthetic_lsv_", suffix=".txt")
82
+ os.close(fd)
83
+ with open(path, "w", encoding="utf-8", newline="\r\n") as handle:
84
+ handle.write("\r\n".join(lines) + "\r\n")
85
+ return path
86
+
87
+
88
+ def _finish(graph, out, label):
89
+ try:
90
+ originlab.legend_entries(graph, show=False)
91
+ except originlab.OriginError:
92
+ pass # a heat map has no legend box to hide
93
+ e = originlab.export(graph, out, fmt="png", width=1600)
94
+ print(" %-14s -> %s (%d 字节, %s)" % (label, os.path.basename(out), e["bytes"],
95
+ e.get("dimensions")))
96
+ return e
97
+
98
+
99
+ def _line(graph, color="blue", width=2.5, series=0, symbol=0, size=0.0):
100
+ """A 1 px black hairline reads as a broken chart in a screenshot."""
101
+ args = {"graph": graph, "series": series, "color": color, "line_width": width}
102
+ if symbol:
103
+ args["symbol_kind"] = symbol
104
+ if size:
105
+ args["symbol_size"] = size
106
+ originlab.style(**args)
107
+
108
+
109
+ def shot_cv():
110
+ sheet = originlab.write_columns(_cv())
111
+ p = originlab.plot(sheet["worksheet"], x=1, y=2, plot_type="line",
112
+ title="Synthetic cyclic voltammogram")
113
+ originlab.style(p["graph"], x_title="E / V", y_title="i / mA cm-2")
114
+ _line(p["graph"], "blue", 2.5)
115
+ _finish(p["graph"], os.path.join(DOCS, "shot-cv.png"), "CV")
116
+ return p["graph"]
117
+
118
+
119
+ def shot_bode():
120
+ data = _bode(n=25)
121
+ freq = data["Freq / Hz"]
122
+ sheet = originlab.write_columns({"Freq / Hz": freq, "|Z| / ohm":
123
+ [round(math.hypot(zr, zi), 3) for zr, zi
124
+ in zip(data["Z' / ohm"], data["Z'' / ohm"])]})
125
+ p = originlab.plot(sheet["worksheet"], x=1, y=2, plot_type="line_symbol",
126
+ title="Synthetic Bode plot")
127
+ st = originlab.style(p["graph"], x_title="Frequency / Hz", y_title="|Z| / ohm",
128
+ x_scale="log10")
129
+ read = [o for o in st.get("applied", []) if o.get("op") == "x_scale"]
130
+ if not read or read[0].get("readback") != 2:
131
+ raise SystemExit("横轴没能改成对数(读回 %r),不导出" % (read or "没有这项",))
132
+ # A log axis auto-ranges over the whole decade grid; without this the curve
133
+ # sits in a third of the panel and the tick labels collide.
134
+ originlab.style(p["graph"], xlim=[min(freq) * 0.6, max(freq) * 1.6])
135
+ _line(p["graph"], "red", 2.0, symbol=2, size=7.0)
136
+ _finish(p["graph"], os.path.join(DOCS, "shot-eis.png"), "EIS")
137
+ return p["graph"]
138
+
139
+
140
+ def shot_heatmap():
141
+ m = originlab.matrix_plot(_grid(), template="heat_map", title="Synthetic response surface")
142
+ graph = m.get("graph") or m.get("graph_page")
143
+ originlab.style(graph, x_title="Applied potential / V", y_title="Cycle",
144
+ layer=0)
145
+ _finish(graph, os.path.join(DOCS, "shot-heatmap.png"), "Heat map")
146
+ return graph
147
+
148
+
149
+ def shot_workstation():
150
+ path = _workstation_txt()
151
+ try:
152
+ imp = originlab.import_file(path)
153
+ cols = [c["label"] for c in imp["columns"]]
154
+ if cols[:2] != ["Potential", "Current"]:
155
+ raise SystemExit("工作站 txt 的列名没解析成 Potential/Current,而是 %s" % cols)
156
+ p = originlab.plot(imp["worksheet"], x=1, y=2, plot_type="line",
157
+ title="Workstation .txt import")
158
+ originlab.style(p["graph"], x_title="E / V", y_title="I / A")
159
+ _line(p["graph"], "green", 2.5)
160
+ _finish(p["graph"], os.path.join(DOCS, "shot-workstation.png"), "Workstation txt")
161
+ return p["graph"]
162
+ finally:
163
+ os.remove(path)
164
+
165
+
166
+ SHOTS = {"cv": shot_cv, "eis": shot_bode, "heatmap": shot_heatmap,
167
+ "workstation": shot_workstation}
168
+
169
+
170
+ def main():
171
+ only = None
172
+ if "--only" in sys.argv:
173
+ only = sys.argv[sys.argv.index("--only") + 1]
174
+ os.makedirs(DOCS, exist_ok=True)
175
+ for name, fn in SHOTS.items():
176
+ if only and name != only:
177
+ continue
178
+ print("生成 %s ..." % name)
179
+ fn()
180
+ originlab.close_pages("graph")
181
+ originlab.close_pages("workbook")
182
+ originlab.close_pages("matrix")
183
+ print("完成,图片在 %s" % DOCS)
184
+
185
+
186
+ if __name__ == "__main__":
187
+ main()