sapbuddy-dsh 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,13 +10,13 @@
10
10
  在 DSH 的插件管理页添加:
11
11
 
12
12
  ```text
13
- sapbuddy-dsh@0.0.9
13
+ sapbuddy-dsh@0.0.10
14
14
  ```
15
15
 
16
16
  也可以通过 DSH 命令行安装:
17
17
 
18
18
  ```bash
19
- dsh plugin --profile desktop add sapbuddy-dsh@0.0.9
19
+ dsh plugin --profile desktop add sapbuddy-dsh@0.0.10
20
20
  ```
21
21
 
22
22
  更新已安装插件后,完整退出并重新启动 DSH,再新建会话选择 **SapBuddy**。
@@ -34,16 +34,16 @@ dsh plugin --profile desktop add sapbuddy-dsh@0.0.9
34
34
 
35
35
  ## 连接配置与排查
36
36
 
37
- 地址必须写全协议,协议要和端口一致:
37
+ 在 **设置 → SAP 连接** 中填写主机地址时,必须带协议,且协议与端口一致:
38
38
 
39
39
  | 端口 | 协议 | 示例 |
40
40
  |---|---|---|
41
- | ICM HTTP(常见 8000) | `http://` | `http://192.168.111.85:8000` |
42
- | ICM HTTPS(常见 44300) | `https://` | `https://10.8.202.35:44300` |
41
+ | ICM HTTP(常见 8000) | `http://` | `http://<host>:8000` |
42
+ | ICM HTTPS(常见 44300) | `https://` | `https://<host>:44300` |
43
43
 
44
- 把 HTTP 端口写成 `https://`(或反过来)会在 TLS 阶段直接失败。客户端号就是登录用的 `sap-client`,必须与要登录的客户端一致——系统是 300 却填 200 会直接返回 401 认证失败;登录语言不确定时填 `EN`。内部系统若是自签名证书,编辑连接时保持「允许自签名证书」勾选。
44
+ 协议写错(例如 HTTP 端口却用 `https://`)会导致 TLS/连接失败。客户端号即登录用的 `sap-client`,必须与目标客户端一致,否则会返回 401。登录语言不确定时建议填 `EN`。内网自签名证书可勾选「允许自签名证书」。
45
45
 
46
- 设置页的 **测试** 按钮与真实 SAP 工具走同一套协议/客户端号/语言(早期版本把探活写死成 https 且只发 `X-sap-login-client` 头,会出现"账号密码都对却报 401"或"http 端口永远探测失败"的假阴性)。测试返回 401 时按提示核对客户端号、账号状态与登录语言;403/404 多为 ADT 服务未激活或用户无权限。
46
+ 设置页的 **测试** 与真实 SAP 工具使用同一套协议、客户端号与语言。返回 401 时核对客户端号、账号与语言;403/404 多为 ADT 未开通或权限不足。
47
47
 
48
48
  ## 连接切换与会话自愈
49
49
 
package/dist/client.js CHANGED
@@ -4,7 +4,6 @@ window.__ModuleLoader__.load({
4
4
  factory: (require) => {
5
5
  var module = { exports: {} };
6
6
  var exports = module.exports;
7
- "use strict";
8
7
  var __defProp = Object.defineProperty;
9
8
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
9
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -79,6 +78,20 @@ var S = {
79
78
  hint: { margin: 0, fontSize: 12, lineHeight: "18px", color: "var(--dsw-alias-label-tertiary)" }
80
79
  };
81
80
  var EMPTY = { language: "ZH", authMethod: "basic", ssl: { allowSelfSigned: true } };
81
+ var DEFAULT_PORT = { https: "44300", http: "8000" };
82
+ var ADDRESS_HINT = "https://\u4E3B\u673A:44300";
83
+ function parseAddress(url) {
84
+ const m = /^\s*(https?):\/\/([^/:?#\s]+)(?::(\d+))?/i.exec(String(url ?? ""));
85
+ if (!m) return { protocol: "https", host: "", port: DEFAULT_PORT.https };
86
+ const protocol = m[1].toLowerCase();
87
+ return { protocol, host: m[2], port: m[3] || DEFAULT_PORT[protocol] };
88
+ }
89
+ function buildAddress(d) {
90
+ const protocol = d.protocol === "http" ? "http" : "https";
91
+ const host = String(d.host ?? "").trim().replace(/^https?:\/\//i, "").replace(/[/:?#].*$/, "");
92
+ const port = String(d.port ?? "").trim() || DEFAULT_PORT[protocol];
93
+ return host ? protocol + "://" + host + ":" + port : "";
94
+ }
82
95
  function Section() {
83
96
  const [conns, setConns] = useState([]);
84
97
  const [mode, setMode] = useState(null);
@@ -105,7 +118,16 @@ function Section() {
105
118
  const save = async () => {
106
119
  setBusy(true);
107
120
  try {
108
- const r = await post(API, { connection: draft });
121
+ const { protocol, host, port, ...rest } = draft;
122
+ const url = buildAddress(draft);
123
+ if (!url) {
124
+ setErr("\u8BF7\u586B\u5199\u4E3B\u673A");
125
+ setMsg(null);
126
+ return;
127
+ }
128
+ const connection = { ...rest, url };
129
+ if (protocol === "http") delete connection.ssl;
130
+ const r = await post(API, { connection });
109
131
  if (!r.ok) {
110
132
  setErr("\u4FDD\u5B58\u5931\u8D25: " + (r.error ?? "\u672A\u77E5\u9519\u8BEF"));
111
133
  setMsg(null);
@@ -147,12 +169,50 @@ function Section() {
147
169
  };
148
170
  const startNew = () => {
149
171
  setMode("new");
150
- setDraft({ ...EMPTY });
172
+ setDraft({ ...EMPTY, protocol: "https", host: "", port: DEFAULT_PORT.https });
151
173
  };
152
174
  const startEdit = (c) => {
153
175
  setMode(c.id);
154
- setDraft({ ...c, password: "" });
176
+ setDraft({ ...c, password: "", ...parseAddress(c.url), ssl: c.ssl ?? { allowSelfSigned: true } });
177
+ };
178
+ const isHttp = draft.protocol === "http";
179
+ const changeProtocol = (evt) => {
180
+ const next = evt.target.value === "http" ? "http" : "https";
181
+ setDraft((d) => {
182
+ const prev = d.protocol === "http" ? "http" : "https";
183
+ const port = !d.port || d.port === DEFAULT_PORT[prev] ? DEFAULT_PORT[next] : d.port;
184
+ return { ...d, protocol: next, port };
185
+ });
155
186
  };
187
+ const addressField = React.createElement("div", { key: "addr", style: S.field }, [
188
+ React.createElement("span", { key: "l", style: S.label }, "\u5730\u5740"),
189
+ React.createElement("div", { key: "r", style: { display: "flex", gap: 6, alignItems: "center" } }, [
190
+ React.createElement("select", {
191
+ key: "p",
192
+ style: { ...S.input, flex: "none", width: 92, padding: "0 6px" },
193
+ value: isHttp ? "http" : "https",
194
+ onChange: changeProtocol
195
+ }, [
196
+ React.createElement("option", { key: "s", value: "https" }, "HTTPS"),
197
+ React.createElement("option", { key: "h", value: "http" }, "HTTP")
198
+ ]),
199
+ React.createElement("input", {
200
+ key: "h",
201
+ style: { ...S.input, flex: 1, minWidth: 0 },
202
+ value: draft.host ?? "",
203
+ placeholder: "\u4E3B\u673A",
204
+ onChange: set("host")
205
+ }),
206
+ React.createElement("input", {
207
+ key: "o",
208
+ style: { ...S.input, flex: "none", width: 80 },
209
+ value: draft.port ?? "",
210
+ placeholder: isHttp ? DEFAULT_PORT.http : DEFAULT_PORT.https,
211
+ onChange: set("port")
212
+ })
213
+ ]),
214
+ React.createElement("span", { key: "hint", style: S.hint }, ADDRESS_HINT)
215
+ ]);
156
216
  const input = (key, label, ph) => React.createElement("label", { className: void 0, style: S.field }, [
157
217
  React.createElement("span", { key: "l", style: S.label }, label),
158
218
  React.createElement("input", {
@@ -185,11 +245,11 @@ function Section() {
185
245
  React.createElement("div", { key: "g1", style: S.grid }, [
186
246
  input("id", "\u8FDE\u63A5 ID\uFF08\u552F\u4E00\uFF0C\u5982 dev/prod\uFF09", "dev"),
187
247
  input("name", "\u540D\u79F0", "\u5F00\u53D1\u7CFB\u7EDF"),
188
- input("url", "\u5730\u5740\uFF08http(s)://host:port\uFF09", "http://10.8.202.35:8000 \u6216 https://10.8.202.35:44300"),
248
+ addressField,
189
249
  input("client", "\u5BA2\u6237\u7AEF\u53F7\uFF083 \u4F4D\uFF0C\u5FC5\u987B\u4E0E\u7CFB\u7EDF\u4E00\u81F4\uFF09", "100")
190
250
  ]),
191
251
  React.createElement("div", { key: "g2", style: S.grid }, [
192
- input("username", "\u7528\u6237\u540D", "YOUR_USER"),
252
+ input("username", "\u7528\u6237\u540D", "BC01"),
193
253
  input("password", "\u5BC6\u7801\uFF08\u7F16\u8F91\u65F6\u7559\u7A7A\u4FDD\u6301\u539F\u5BC6\u7801\uFF09", "\u2022\u2022\u2022\u2022\u2022\u2022"),
194
254
  input("language", "\u767B\u5F55\u8BED\u8A00\uFF08\u4E0D\u786E\u5B9A\u586B EN\uFF09", "ZH"),
195
255
  input("authMethod", "\u8BA4\u8BC1\u65B9\u5F0F", "basic")
@@ -197,19 +257,19 @@ function Section() {
197
257
  React.createElement("div", { key: "g3", style: S.grid }, [
198
258
  input("description", "\u63CF\u8FF0", "\u5F00\u53D1\u7CFB\u7EDF")
199
259
  ]),
200
- React.createElement("label", { key: "ssl", style: { ...S.field, flexDirection: "row", alignItems: "center", gap: 8 } }, [
260
+ isHttp ? null : React.createElement("label", { key: "ssl", style: { ...S.field, flexDirection: "row", alignItems: "center", gap: 8 } }, [
201
261
  React.createElement("input", {
202
262
  key: "i",
203
263
  type: "checkbox",
204
- checked: !draft.ssl || draft.ssl.allowSelfSigned !== false,
205
- onChange: (evt) => setDraft((d) => ({ ...d, ssl: { ...(d.ssl || {}), allowSelfSigned: evt.target.checked } }))
264
+ checked: draft.ssl?.allowSelfSigned !== false,
265
+ onChange: (evt) => setDraft((d) => ({ ...d, ssl: { ...d.ssl ?? {}, allowSelfSigned: evt.target.checked } }))
206
266
  }),
207
267
  React.createElement("span", { key: "l", style: S.label }, "\u5141\u8BB8\u81EA\u7B7E\u540D\u8BC1\u4E66\uFF08\u5185\u90E8\u7CFB\u7EDF https \u7528\uFF09")
208
268
  ]),
209
269
  React.createElement(
210
270
  "p",
211
271
  { key: "hint", style: S.hint },
212
- "\u7AEF\u53E3\u5BF9\u5E94\u534F\u8BAE\uFF1ASAP \u7684 ICM HTTP \u7AEF\u53E3\uFF08\u5E38\u89C1 8000\uFF09\u7528 http://\uFF0CHTTPS \u7AEF\u53E3\uFF08\u5E38\u89C1 44300\uFF09\u7528 https://\u3002\u5BA2\u6237\u7AEF\u53F7\u5C31\u662F\u767B\u5F55\u5BA2\u6237\u7AEF\uFF08sap-client\uFF09\uFF0C\u586B\u9519\u4F1A\u8FD4\u56DE 401\uFF1B\u767B\u5F55\u8BED\u8A00\u4E0D\u786E\u5B9A\u65F6\u586B EN\u3002"
272
+ "\u5BA2\u6237\u7AEF\u53F7\u5C31\u662F\u767B\u5F55\u5BA2\u6237\u7AEF\uFF08sap-client\uFF09\uFF0C\u586B\u9519\u4F1A\u8FD4\u56DE 401\uFF1B\u767B\u5F55\u8BED\u8A00\u4E0D\u786E\u5B9A\u65F6\u586B EN\u3002"
213
273
  ),
214
274
  React.createElement("div", { key: "acts", style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, [
215
275
  React.createElement("button", { key: "c", style: S.btn, onClick: () => {
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ export function buildProbeRequest(conf) {
50
50
  const raw = String(conf?.url ?? "").trim().replace(/\/+$/, "");
51
51
  if (!/^https?:\/\//i.test(raw))
52
52
  return { error: `连接地址要以 http:// 或 https:// 开头(当前是 "${raw || "(空)"}")。` +
53
- `例如 http://10.8.202.35:8000(ICM HTTP 端口)或 https://10.8.202.35:44300(HTTPS 端口)` };
53
+ `例如 http://<host>:8000(ICM HTTP 端口)或 https://<host>:44300(HTTPS 端口)` };
54
54
  let url;
55
55
  try {
56
56
  url = new URL(`${raw}/sap/bc/adt/compatibility/graph`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapbuddy-dsh",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
4
4
  "description": "SapBuddy —— SAP ABAP AI 全能助手(DeepSeek Harness 插件):41 个 SAP 工具 + 10 个 ABAP 技能 + SapBuddy preset + 连接设置页。一个包名装完。",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",