@fa-software/semi2-dsh 0.0.10-preview.20260920042705

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 (60) hide show
  1. package/cordis.patch.yml +5 -0
  2. package/lib/client.js +351 -0
  3. package/lib/client.js.map +1 -0
  4. package/lib/codegraph/cli.js +142 -0
  5. package/lib/codegraph/config.js +22 -0
  6. package/lib/codegraph/ensure.js +25 -0
  7. package/lib/codegraph/guide.js +21 -0
  8. package/lib/codegraph/index.js +97 -0
  9. package/lib/codegraph/tools.js +227 -0
  10. package/lib/codegraph/workspace.js +52 -0
  11. package/lib/configuration/compile.js +177 -0
  12. package/lib/configuration/credentials.js +104 -0
  13. package/lib/configuration/generated/graphql.js +1 -0
  14. package/lib/configuration/generated/schema.js +95 -0
  15. package/lib/configuration/graphql.js +193 -0
  16. package/lib/configuration/index.js +29 -0
  17. package/lib/configuration/login.js +96 -0
  18. package/lib/configuration/runtime.js +1100 -0
  19. package/lib/configuration/skill-resources.js +218 -0
  20. package/lib/configuration/skills.js +185 -0
  21. package/lib/configuration/status.js +57 -0
  22. package/lib/configuration/workspace.js +55 -0
  23. package/lib/configuration/workspaces.js +169 -0
  24. package/lib/contract.js +42 -0
  25. package/lib/index.js +14 -0
  26. package/lib/types/codegraph/cli.d.ts +20 -0
  27. package/lib/types/codegraph/config.d.ts +10 -0
  28. package/lib/types/codegraph/ensure.d.ts +3 -0
  29. package/lib/types/codegraph/guide.d.ts +4 -0
  30. package/lib/types/codegraph/index.d.ts +5 -0
  31. package/lib/types/codegraph/tools.d.ts +17 -0
  32. package/lib/types/codegraph/workspace.d.ts +8 -0
  33. package/lib/types/configuration/compile.d.ts +81 -0
  34. package/lib/types/configuration/credentials.d.ts +35 -0
  35. package/lib/types/configuration/generated/graphql.d.ts +100 -0
  36. package/lib/types/configuration/generated/schema.d.ts +1003 -0
  37. package/lib/types/configuration/graphql.d.ts +46 -0
  38. package/lib/types/configuration/index.d.ts +9 -0
  39. package/lib/types/configuration/login.d.ts +22 -0
  40. package/lib/types/configuration/runtime.d.ts +320 -0
  41. package/lib/types/configuration/skill-resources.d.ts +14 -0
  42. package/lib/types/configuration/skills.d.ts +8 -0
  43. package/lib/types/configuration/status.d.ts +9 -0
  44. package/lib/types/configuration/workspace.d.ts +7 -0
  45. package/lib/types/configuration/workspaces.d.ts +96 -0
  46. package/lib/types/contract.d.ts +32 -0
  47. package/lib/types/index.d.ts +11 -0
  48. package/lib/types/web/index.d.ts +2 -0
  49. package/lib/types/web/login.d.ts +2 -0
  50. package/lib/types/web/loopback-bridge.d.ts +8 -0
  51. package/lib/types/web/preview-proxy.d.ts +17 -0
  52. package/lib/types/web/runtime.d.ts +8 -0
  53. package/lib/types/web/welcome-ack.d.ts +33 -0
  54. package/lib/web/index.js +38 -0
  55. package/lib/web/login.js +79 -0
  56. package/lib/web/loopback-bridge.js +12 -0
  57. package/lib/web/preview-proxy.js +201 -0
  58. package/lib/web/runtime.js +19 -0
  59. package/lib/web/welcome-ack.js +51 -0
  60. package/package.json +81 -0
@@ -0,0 +1,5 @@
1
+ # @fa-software/semi2-dsh —— 单插件装配(三运行面共用)。
2
+ # 运行面差异由插件内部的服务作用域注入表达,此处只有一行。
3
+ - insert:
4
+ - id: semi2-dsh
5
+ name: '@fa-software/semi2-dsh'
package/lib/client.js ADDED
@@ -0,0 +1,351 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@fa-software/semi2-dsh",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/client/index.jsx
10
+ const inject = ["slots", "settingsScope"];
11
+ const styles = {
12
+ section: {
13
+ display: "flex",
14
+ flexDirection: "column",
15
+ gap: 12,
16
+ maxWidth: 720
17
+ },
18
+ title: {
19
+ margin: 0,
20
+ fontSize: 16,
21
+ fontWeight: 500
22
+ },
23
+ intro: {
24
+ margin: 0,
25
+ fontSize: 14,
26
+ opacity: .7
27
+ },
28
+ rows: {
29
+ display: "grid",
30
+ gridTemplateColumns: "max-content 1fr",
31
+ gap: "6px 16px",
32
+ margin: 0,
33
+ fontSize: 13
34
+ },
35
+ key: { opacity: .6 },
36
+ value: {
37
+ margin: 0,
38
+ overflowWrap: "anywhere"
39
+ },
40
+ error: {
41
+ margin: 0,
42
+ fontSize: 12,
43
+ color: "var(--dsw-alias-state-warn-label, #b45309)"
44
+ },
45
+ ok: {
46
+ margin: 0,
47
+ fontSize: 12,
48
+ color: "var(--dsw-alias-state-success-primary, #15803d)"
49
+ },
50
+ button: {
51
+ alignSelf: "flex-start",
52
+ padding: "6px 14px",
53
+ borderRadius: 8,
54
+ border: "0.5px solid var(--dsw-alias-border-l4, #d4d4d8)",
55
+ background: "transparent",
56
+ color: "inherit",
57
+ cursor: "pointer",
58
+ fontSize: 13
59
+ },
60
+ actions: {
61
+ display: "flex",
62
+ gap: 8
63
+ }
64
+ };
65
+ function Row({ label, value }) {
66
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
67
+ style: styles.key,
68
+ children: label
69
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
70
+ style: styles.value,
71
+ children: value ?? "—"
72
+ })] });
73
+ }
74
+ function readState(snapshot) {
75
+ const value = snapshot?.value;
76
+ return value && typeof value.status === "string" && value.status !== "" ? value : null;
77
+ }
78
+ const SURFACE_LABELS = {
79
+ cnb: "CNB",
80
+ container: "容器",
81
+ host: "宿主直跑"
82
+ };
83
+ function platformText(platform) {
84
+ return SURFACE_LABELS[platform] ?? platform ?? null;
85
+ }
86
+ /** 一次动作前后的状态签名:host 每次领取结束都会回写这三项(失败改 error)。 */
87
+ function signature(value) {
88
+ return value === null ? "" : `${value.status}|${value.loadedAt}|${value.error}`;
89
+ }
90
+ function Semi2Section({ scope }) {
91
+ const [snapshot, setSnapshot] = (0, react.useState)(() => scope.getSnapshot());
92
+ const [busy, setBusy] = (0, react.useState)(false);
93
+ const [error, setError] = (0, react.useState)(null);
94
+ const [url, setUrl] = (0, react.useState)("");
95
+ const [username, setUsername] = (0, react.useState)("");
96
+ const [password, setPassword] = (0, react.useState)("");
97
+ const [loginBusy, setLoginBusy] = (0, react.useState)(false);
98
+ const [loginError, setLoginError] = (0, react.useState)(null);
99
+ const [loginOk, setLoginOk] = (0, react.useState)(null);
100
+ (0, react.useEffect)(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope]);
101
+ const state = readState(snapshot);
102
+ const urlValue = url || state?.consoleUrl || "";
103
+ const readOnlyCredentials = state?.credentialSource === "env" || state?.credentialSource === "runtime";
104
+ const connected = state !== null && (state.credentialSource !== "" || state.status !== "local");
105
+ const waitForChange = async (before) => {
106
+ const deadline = Date.now() + 2e4;
107
+ while (Date.now() < deadline) {
108
+ await new Promise((resolve) => setTimeout(resolve, 500));
109
+ const next = readState(scope.getSnapshot());
110
+ if (next !== null && signature(next) !== before) return;
111
+ }
112
+ };
113
+ const onLogin = async () => {
114
+ setLoginBusy(true);
115
+ setLoginError(null);
116
+ setLoginOk(null);
117
+ try {
118
+ const before = signature(state);
119
+ const response = await fetch("/semi2/login", {
120
+ method: "POST",
121
+ headers: { "content-type": "application/json" },
122
+ body: JSON.stringify({
123
+ url: urlValue,
124
+ username,
125
+ password
126
+ })
127
+ });
128
+ const body = await response.json().catch(() => ({}));
129
+ if (!response.ok) throw new Error(body.code ?? "LOGIN_FAILED");
130
+ setPassword("");
131
+ setLoginOk(`已登录 ${body.userName ?? ""}`.trim());
132
+ await waitForChange(before);
133
+ } catch (cause) {
134
+ setLoginError(cause?.message ?? String(cause));
135
+ } finally {
136
+ setLoginBusy(false);
137
+ }
138
+ };
139
+ const onLogout = async () => {
140
+ setLoginBusy(true);
141
+ setLoginError(null);
142
+ setLoginOk(null);
143
+ try {
144
+ const before = signature(state);
145
+ if (!(await fetch("/semi2/logout", { method: "POST" })).ok) throw new Error("LOGOUT_FAILED");
146
+ await waitForChange(before);
147
+ } catch (cause) {
148
+ setLoginError(cause?.message ?? String(cause));
149
+ } finally {
150
+ setLoginBusy(false);
151
+ }
152
+ };
153
+ const onReload = async () => {
154
+ setBusy(true);
155
+ setError(null);
156
+ try {
157
+ const before = signature(state);
158
+ await scope.set("reload", globalThis.crypto.randomUUID());
159
+ await waitForChange(before);
160
+ } catch (cause) {
161
+ setError(cause?.message ?? String(cause));
162
+ } finally {
163
+ setBusy(false);
164
+ }
165
+ };
166
+ const source = state == null ? "" : state.status === "ready" ? "semi2-hub 配置" : state.status === "failed" ? "未连接 semi2-hub(读取失败)" : "未连接 semi2-hub";
167
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
168
+ style: styles.section,
169
+ children: [
170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
171
+ style: styles.title,
172
+ children: "Semi2 运行配置"
173
+ }),
174
+ connected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
175
+ style: styles.intro,
176
+ children: [
177
+ "配置由 ",
178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: "@fa-software/semi2-dsh" }),
179
+ " 在启动时领取一次并保存在内存里,连不上 semi2-hub 时不做任何注入(一切按 DSH 自身配置运行);同步会重取 semi2-hub 的全部插件配置与「已添加工作区」对应项目的配置(含技能目录),无需重启。 没有添加为工作区的项目不会被拉取。"
180
+ ]
181
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
182
+ style: styles.intro,
183
+ children: "登录 semi2-hub 后领取运行配置。登录换取的令牌存进 dsh credentials,重启仍在;CNB 面由 secret import 注入的接入密钥(环境变量)优先,且不可在此登出。"
184
+ }),
185
+ state === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
186
+ style: styles.intro,
187
+ children: "读取中…"
188
+ }) : connected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
189
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
190
+ style: styles.rows,
191
+ children: [
192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
193
+ label: "来源",
194
+ value: source
195
+ }),
196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
197
+ label: "状态",
198
+ value: state.status
199
+ }),
200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
201
+ label: "semi2-hub",
202
+ value: state.consoleUrl
203
+ }),
204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
205
+ label: "身份",
206
+ value: state.identity || null
207
+ }),
208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
209
+ label: "凭据",
210
+ value: state.credentialSource === "env" ? "环境变量(部署注入,只读)" : state.credentialSource === "runtime" ? "运行面契约(部署 env,只读)" : state.credentialSource ? `dsh credentials(${state.credentialSource})` : "未配置"
211
+ }),
212
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
213
+ label: "运行面",
214
+ value: platformText(state.platform)
215
+ }),
216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
217
+ label: "启动目录",
218
+ value: state.startupDir
219
+ }),
220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
221
+ label: "工作区数",
222
+ value: state.workspaceCount || null
223
+ }),
224
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
225
+ label: "工作区",
226
+ value: state.workspaces || null
227
+ }),
228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
229
+ label: "项目数",
230
+ value: state.projectCount || null
231
+ }),
232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
233
+ label: "工作区项目",
234
+ value: state.projects || null
235
+ }),
236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
237
+ label: "模型",
238
+ value: state.model || null
239
+ }),
240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
241
+ label: "技能目录",
242
+ value: state.skills ? `${state.skills} 条` : null
243
+ }),
244
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
245
+ label: "加载时间",
246
+ value: state.loadedAt
247
+ })
248
+ ]
249
+ }),
250
+ state.status === "ready" && !state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
251
+ style: styles.ok,
252
+ children: "已应用"
253
+ }) : null,
254
+ state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
255
+ style: styles.error,
256
+ children: ["最近失败:", state.error]
257
+ }) : null,
258
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
259
+ style: styles.error,
260
+ children: error
261
+ }) : null,
262
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
263
+ style: styles.actions,
264
+ children: [readOnlyCredentials ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
265
+ type: "button",
266
+ style: styles.button,
267
+ onClick: onLogout,
268
+ disabled: loginBusy,
269
+ children: loginBusy ? "退出中…" : "退出登录"
270
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
271
+ type: "button",
272
+ style: styles.button,
273
+ onClick: onReload,
274
+ disabled: busy,
275
+ children: busy ? "同步中…" : "同步"
276
+ })]
277
+ })
278
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
279
+ style: styles.rows,
280
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
281
+ style: styles.key,
282
+ children: "登录 semi2-hub"
283
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
284
+ style: {
285
+ display: "flex",
286
+ flexDirection: "column",
287
+ gap: 8
288
+ },
289
+ children: [
290
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
291
+ value: urlValue,
292
+ onChange: (event) => setUrl(event.target.value),
293
+ placeholder: "https://semi2-hub.example.com"
294
+ }),
295
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
296
+ style: {
297
+ display: "flex",
298
+ gap: 8
299
+ },
300
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
301
+ value: username,
302
+ onChange: (event) => setUsername(event.target.value),
303
+ placeholder: "用户名"
304
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
305
+ type: "password",
306
+ value: password,
307
+ onChange: (event) => setPassword(event.target.value),
308
+ placeholder: "密码"
309
+ })]
310
+ }),
311
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
312
+ style: styles.actions,
313
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
314
+ type: "button",
315
+ style: styles.button,
316
+ onClick: onLogin,
317
+ disabled: loginBusy || !urlValue || !username || !password,
318
+ children: loginBusy ? "登录中…" : "登录"
319
+ })
320
+ })
321
+ ]
322
+ })]
323
+ }), loginError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
324
+ style: styles.error,
325
+ children: ["登录失败:", loginError]
326
+ }) : null] }),
327
+ loginOk ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
328
+ style: styles.ok,
329
+ children: loginOk
330
+ }) : null
331
+ ]
332
+ });
333
+ }
334
+ function apply(ctx) {
335
+ const scope = ctx.settingsScope.bind({ namespace: "semi2" });
336
+ const Section = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Semi2Section, { scope });
337
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
338
+ name: "settings.section",
339
+ id: "semi2",
340
+ order: 40,
341
+ label: () => "Semi2"
342
+ }, Section), "semi2-dsh: settings section");
343
+ }
344
+ //#endregion
345
+ exports.apply = apply;
346
+ exports.inject = inject;
347
+ return module.exports;
348
+ }
349
+ });
350
+
351
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState"],"sources":["../src/client/index.jsx"],"sourcesContent":["// @fa-software/semi2-dsh/client —— 设置页唯一 Semi2 分区(client 侧)。\n// 传输走官方 settings 命名空间 `semi2`(host 侧插件注册):UI 写 `reload` 触发重新领取,状态经 `ctx.settingsScope` 订阅;不轮询、不订阅远程事件。\nimport { useEffect, useState } from \"react\";\n\nexport const inject = [\"slots\", \"settingsScope\"];\n\nconst styles = {\n section: { display: \"flex\", flexDirection: \"column\", gap: 12, maxWidth: 720 },\n title: { margin: 0, fontSize: 16, fontWeight: 500 },\n intro: { margin: 0, fontSize: 14, opacity: 0.7 },\n rows: {\n display: \"grid\",\n gridTemplateColumns: \"max-content 1fr\",\n gap: \"6px 16px\",\n margin: 0,\n fontSize: 13,\n },\n key: { opacity: 0.6 },\n value: { margin: 0, overflowWrap: \"anywhere\" },\n error: { margin: 0, fontSize: 12, color: \"var(--dsw-alias-state-warn-label, #b45309)\" },\n ok: { margin: 0, fontSize: 12, color: \"var(--dsw-alias-state-success-primary, #15803d)\" },\n button: {\n alignSelf: \"flex-start\",\n padding: \"6px 14px\",\n borderRadius: 8,\n border: \"0.5px solid var(--dsw-alias-border-l4, #d4d4d8)\",\n background: \"transparent\",\n color: \"inherit\",\n cursor: \"pointer\",\n fontSize: 13,\n },\n actions: { display: \"flex\", gap: 8 },\n};\n\nfunction Row({ label, value }) {\n return (\n <>\n <span style={styles.key}>{label}</span>\n <p style={styles.value}>{value ?? \"—\"}</p>\n </>\n );\n}\n\n// 状态字段由 host 侧 STATUS_FIELDS(configuration/status.ts)定义:全为扁平标量字符串。\nfunction readState(snapshot) {\n const value = snapshot?.value;\n return value && typeof value.status === \"string\" && value.status !== \"\"\n ? value\n : null;\n}\n\n// 运行面原值(数据层只存 cnb / container / host)→ 展示标签。\nconst SURFACE_LABELS = { cnb: \"CNB\", container: \"容器\", host: \"宿主直跑\" };\n\nfunction platformText(platform) {\n return SURFACE_LABELS[platform] ?? platform ?? null;\n}\n\n/** 一次动作前后的状态签名:host 每次领取结束都会回写这三项(失败改 error)。 */\nfunction signature(value) {\n return value === null ? \"\" : `${value.status}|${value.loadedAt}|${value.error}`;\n}\n\nfunction Semi2Section({ scope }) {\n const [snapshot, setSnapshot] = useState(() => scope.getSnapshot());\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState(null);\n const [url, setUrl] = useState(\"\");\n const [username, setUsername] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [loginBusy, setLoginBusy] = useState(false);\n const [loginError, setLoginError] = useState(null);\n const [loginOk, setLoginOk] = useState(null);\n\n useEffect(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope]);\n\n const state = readState(snapshot);\n // 登录表单默认带出当前 semi2-hub 地址(状态字段 consoleUrl)。\n const urlValue = url || state?.consoleUrl || \"\";\n // 凭据来源 env = 部署 env(CNB secret imports 等)、runtime = 运行面契约(部署 env):两者只读;\n // 其余(dsh credentials 存储)= 宿主直跑,可登录/登出。\n const readOnlyCredentials =\n state?.credentialSource === \"env\" || state?.credentialSource === \"runtime\";\n // 已连接 = 有可用凭据:未连接时 host 状态恒为 local 且 credentialSource 为空。\n const connected =\n state !== null && (state.credentialSource !== \"\" || state.status !== \"local\");\n\n // 等 host 回写新状态(最多 20s,覆盖领取的 15s 网络超时);失败也会改 error,故不会空等。\n const waitForChange = async (before) => {\n const deadline = Date.now() + 20_000;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n const next = readState(scope.getSnapshot());\n if (next !== null && signature(next) !== before) return;\n }\n };\n\n const onLogin = async () => {\n setLoginBusy(true);\n setLoginError(null);\n setLoginOk(null);\n try {\n const before = signature(state);\n const response = await fetch(\"/semi2/login\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ url: urlValue, username, password }),\n });\n const body = await response.json().catch(() => ({}));\n if (!response.ok) throw new Error(body.code ?? \"LOGIN_FAILED\");\n setPassword(\"\");\n setLoginOk(`已登录 ${body.userName ?? \"\"}`.trim());\n await waitForChange(before);\n } catch (cause) {\n setLoginError(cause?.message ?? String(cause));\n } finally {\n setLoginBusy(false);\n }\n };\n\n const onLogout = async () => {\n setLoginBusy(true);\n setLoginError(null);\n setLoginOk(null);\n try {\n const before = signature(state);\n const response = await fetch(\"/semi2/logout\", { method: \"POST\" });\n if (!response.ok) throw new Error(\"LOGOUT_FAILED\");\n await waitForChange(before);\n } catch (cause) {\n setLoginError(cause?.message ?? String(cause));\n } finally {\n setLoginBusy(false);\n }\n };\n\n const onReload = async () => {\n setBusy(true);\n setError(null);\n try {\n const before = signature(state);\n await scope.set(\"reload\", globalThis.crypto.randomUUID());\n await waitForChange(before);\n } catch (cause) {\n setError(cause?.message ?? String(cause));\n } finally {\n setBusy(false);\n }\n };\n\n const source =\n state == null\n ? \"\"\n : state.status === \"ready\"\n ? \"semi2-hub 配置\"\n : state.status === \"failed\"\n ? \"未连接 semi2-hub(读取失败)\"\n : \"未连接 semi2-hub\";\n return (\n <section style={styles.section}>\n <h2 style={styles.title}>Semi2 运行配置</h2>\n {connected ? (\n <p style={styles.intro}>\n 配置由 <code>@fa-software/semi2-dsh</code> 在启动时领取一次并保存在内存里,连不上\n semi2-hub 时不做任何注入(一切按 DSH 自身配置运行);同步会重取 semi2-hub\n 的全部插件配置与「已添加工作区」对应项目的配置(含技能目录),无需重启。\n 没有添加为工作区的项目不会被拉取。\n </p>\n ) : (\n <p style={styles.intro}>\n 登录 semi2-hub 后领取运行配置。登录换取的令牌存进 dsh credentials,重启仍在;CNB 面由 secret import 注入的接入密钥(环境变量)优先,且不可在此登出。\n </p>\n )}\n {state === null ? (\n <p style={styles.intro}>读取中…</p>\n ) : connected ? (\n <>\n <div style={styles.rows}>\n <Row label=\"来源\" value={source} />\n <Row label=\"状态\" value={state.status} />\n <Row label=\"semi2-hub\" value={state.consoleUrl} />\n <Row label=\"身份\" value={state.identity || null} />\n <Row\n label=\"凭据\"\n value={\n state.credentialSource === \"env\"\n ? \"环境变量(部署注入,只读)\"\n : state.credentialSource === \"runtime\"\n ? \"运行面契约(部署 env,只读)\"\n : state.credentialSource\n ? `dsh credentials(${state.credentialSource})`\n : \"未配置\"\n }\n />\n <Row label=\"运行面\" value={platformText(state.platform)} />\n <Row label=\"启动目录\" value={state.startupDir} />\n <Row label=\"工作区数\" value={state.workspaceCount || null} />\n <Row label=\"工作区\" value={state.workspaces || null} />\n <Row label=\"项目数\" value={state.projectCount || null} />\n <Row label=\"工作区项目\" value={state.projects || null} />\n <Row label=\"模型\" value={state.model || null} />\n <Row\n label=\"技能目录\"\n value={state.skills ? `${state.skills} 条` : null}\n />\n <Row label=\"加载时间\" value={state.loadedAt} />\n </div>\n {state.status === \"ready\" && !state.error ? <p style={styles.ok}>已应用</p> : null}\n {state.error ? <p style={styles.error}>最近失败:{state.error}</p> : null}\n {error ? <p style={styles.error}>{error}</p> : null}\n <div style={styles.actions}>\n {readOnlyCredentials ? null : (\n <button type=\"button\" style={styles.button} onClick={onLogout} disabled={loginBusy}>\n {loginBusy ? \"退出中…\" : \"退出登录\"}\n </button>\n )}\n <button type=\"button\" style={styles.button} onClick={onReload} disabled={busy}>\n {busy ? \"同步中…\" : \"同步\"}\n </button>\n </div>\n </>\n ) : (\n <>\n <div style={styles.rows}>\n <span style={styles.key}>登录 semi2-hub</span>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <input\n value={urlValue}\n onChange={(event) => setUrl(event.target.value)}\n placeholder=\"https://semi2-hub.example.com\"\n />\n <div style={{ display: \"flex\", gap: 8 }}>\n <input\n value={username}\n onChange={(event) => setUsername(event.target.value)}\n placeholder=\"用户名\"\n />\n <input\n type=\"password\"\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n placeholder=\"密码\"\n />\n </div>\n <div style={styles.actions}>\n <button\n type=\"button\"\n style={styles.button}\n onClick={onLogin}\n disabled={loginBusy || !urlValue || !username || !password}\n >\n {loginBusy ? \"登录中…\" : \"登录\"}\n </button>\n </div>\n </div>\n </div>\n {loginError ? <p style={styles.error}>登录失败:{loginError}</p> : null}\n </>\n )}\n {loginOk ? <p style={styles.ok}>{loginOk}</p> : null}\n </section>\n );\n}\n\nexport function apply(ctx) {\n const scope = ctx.settingsScope.bind({ namespace: \"semi2\" });\n const Section = () => <Semi2Section scope={scope} />;\n ctx.slots.inject(\n \"settings.section\",\n () =>\n ctx.slots.register(\n {\n name: \"settings.section\",\n id: \"semi2\",\n order: 40,\n label: () => \"Semi2\",\n },\n Section,\n ),\n \"semi2-dsh: settings section\",\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ // @fa-software/semi2-dsh/codegraph/cli —— 通过 dsh subprocess seam 执行 codegraph CLI。
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, join } from 'node:path';
5
+ /** subprocess 收集完整 stdout 的上限;超限明确失败,不把不完整图谱当真。 */
6
+ const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
7
+ const STDERR_MAX_BYTES = 64 * 1024;
8
+ const GRACE_MS = 2_000;
9
+ export class CodegraphCliError extends Error {
10
+ exitCode;
11
+ constructor(message, exitCode, options) {
12
+ super(message, options);
13
+ this.exitCode = exitCode;
14
+ }
15
+ }
16
+ export function stripAnsi(text) {
17
+ return text.replace(/\x1B\[[0-9;]*[A-Za-z]/g, '');
18
+ }
19
+ /** 仅供短诊断文本使用;模型结果不在插件内截断,交给 dsh spill policy。 */
20
+ export function truncateText(text, max) {
21
+ if (text.length <= max)
22
+ return text;
23
+ return text.slice(0, max) + '\n... [truncated] ...';
24
+ }
25
+ function detailOf(stderr, stdout) {
26
+ const detail = stderr.trim() !== '' ? stderr.trim() : stdout.trim();
27
+ return truncateText(detail, 2_000);
28
+ }
29
+ let dependencyBin;
30
+ let dependencyBinResolved = false;
31
+ /** 依赖自带的 bin;exports 可能不暴露 package.json,从入口向上寻找包根。 */
32
+ function resolveDependencyBin() {
33
+ if (dependencyBinResolved)
34
+ return dependencyBin;
35
+ dependencyBinResolved = true;
36
+ try {
37
+ const require = createRequire(import.meta.url);
38
+ let dir = dirname(require.resolve('@colbymchenry/codegraph'));
39
+ for (let i = 0; i < 8 && dir !== ''; i++) {
40
+ const pkgPath = join(dir, 'package.json');
41
+ if (existsSync(pkgPath)) {
42
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
43
+ if (pkg.name === '@colbymchenry/codegraph') {
44
+ const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.['codegraph'];
45
+ if (bin !== undefined) {
46
+ dependencyBin = join(dir, bin);
47
+ return dependencyBin;
48
+ }
49
+ }
50
+ }
51
+ const parent = dirname(dir);
52
+ if (parent === dir)
53
+ break;
54
+ dir = parent;
55
+ }
56
+ }
57
+ catch {
58
+ // 裁剪安装可能没有依赖自带 bin,执行世界仍可从 PATH 解析。
59
+ }
60
+ return undefined;
61
+ }
62
+ /** 测试与诊断只判断显式覆盖或依赖自带 bin;PATH 由 subprocess 执行世界解析。 */
63
+ export function resolveCodegraphBin() {
64
+ const override = process.env.SEMI2_CODEGRAPH_BIN;
65
+ if (override !== undefined && override !== '')
66
+ return override;
67
+ return resolveDependencyBin();
68
+ }
69
+ export const CODEGRAPH_MISSING_HINT = 'codegraph CLI 不可用:执行环境 PATH 与插件依赖里都没有可执行文件。' +
70
+ '请安装 @colbymchenry/codegraph,或设 SEMI2_CODEGRAPH_BIN 指向可执行文件。';
71
+ async function executable(subprocess, signal) {
72
+ const configured = process.env.SEMI2_CODEGRAPH_BIN;
73
+ if (configured !== undefined && configured !== '') {
74
+ return subprocess.resolveExecutable(configured, undefined, signal);
75
+ }
76
+ try {
77
+ return await subprocess.resolveExecutable('codegraph', undefined, signal);
78
+ }
79
+ catch (pathError) {
80
+ const bundled = resolveDependencyBin();
81
+ if (bundled === undefined)
82
+ throw new CodegraphCliError(CODEGRAPH_MISSING_HINT, null, { cause: pathError });
83
+ try {
84
+ return await subprocess.resolveExecutable(bundled, undefined, signal);
85
+ }
86
+ catch (dependencyError) {
87
+ throw new CodegraphCliError(CODEGRAPH_MISSING_HINT, null, { cause: dependencyError });
88
+ }
89
+ }
90
+ }
91
+ /** 创建绑定到 dsh 执行世界的 runner;取消和插件卸载均由 subprocess provider 管理进程范围。 */
92
+ export function createCodegraphRunner(subprocess) {
93
+ return async (argv, options = {}) => {
94
+ const bin = await executable(subprocess, options.signal);
95
+ const cwd = options.cwd ?? process.cwd();
96
+ let handle;
97
+ try {
98
+ handle = subprocess.spawn({
99
+ argv: [bin, ...argv],
100
+ cwd,
101
+ stdio: {
102
+ stdin: 'ignore',
103
+ stdout: { maxBytes: MAX_OUTPUT_BYTES },
104
+ stderr: { maxBytes: STDERR_MAX_BYTES },
105
+ },
106
+ graceMs: GRACE_MS,
107
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
108
+ env: {
109
+ DO_NOT_TRACK: '1',
110
+ CODEGRAPH_TELEMETRY: '0',
111
+ ...(options.noDaemon === true ? { CODEGRAPH_NO_DAEMON: '1' } : {}),
112
+ },
113
+ });
114
+ }
115
+ catch (error) {
116
+ throw new CodegraphCliError(`codegraph 启动失败:${error instanceof Error ? error.message : String(error)}`, null, { cause: error });
117
+ }
118
+ let outcome;
119
+ try {
120
+ outcome = await handle.done;
121
+ }
122
+ catch (error) {
123
+ throw new CodegraphCliError('codegraph 执行器未返回进程结果', null, { cause: error });
124
+ }
125
+ const stdout = handle.collected.stdout?.readFrom(0);
126
+ const stderr = handle.collected.stderr?.readFrom(0);
127
+ if (stdout === undefined || stderr === undefined)
128
+ throw new CodegraphCliError('codegraph 执行器没有收集输出流', outcome.exitCode);
129
+ if (stdout.lossy)
130
+ throw new CodegraphCliError(`codegraph 输出超过 ${MAX_OUTPUT_BYTES} 字节上限`, outcome.exitCode);
131
+ const out = stripAnsi(stdout.text);
132
+ const err = stripAnsi(stderr.text);
133
+ if (outcome.signal !== null || outcome.exitCode === null) {
134
+ throw new CodegraphCliError(`codegraph 被信号 ${outcome.signal ?? 'unknown'} 终止`, null);
135
+ }
136
+ if (outcome.exitCode !== 0) {
137
+ const detail = detailOf(err, out);
138
+ throw new CodegraphCliError(detail !== '' ? `codegraph 退出码 ${outcome.exitCode}:${detail}` : `codegraph 退出码 ${outcome.exitCode}`, outcome.exitCode);
139
+ }
140
+ return out;
141
+ };
142
+ }
@@ -0,0 +1,22 @@
1
+ import { SEMI2_RUNTIME_SERVICE } from "../contract.js";
2
+ /** 契约平台**懒取**:apply 期适配器可能还没 provide(配置轮询同理),只在决策点读。 */
3
+ export function platformOf(ctx) {
4
+ const runtime = ctx.get(SEMI2_RUNTIME_SERVICE, false);
5
+ return runtime === undefined ? 'host' : runtime.platform;
6
+ }
7
+ function override(env, key) {
8
+ const value = env[key];
9
+ if (value === '1' || value === 'true')
10
+ return true;
11
+ if (value === '0' || value === 'false')
12
+ return false;
13
+ return undefined;
14
+ }
15
+ /** 总开关:SEMI2_CODEGRAPH=0 整块能力 no-op(宿主不想用时的退出阀)。 */
16
+ export function moduleDisabled(env) {
17
+ return override(env, 'SEMI2_CODEGRAPH') === false;
18
+ }
19
+ /** 自动索引默认值:运行面容器(cnb/container)= 开(无人可替它决定);宿主 = 关(索引是用户的决定)。 */
20
+ export function autoIndexEnabled(platform, env) {
21
+ return override(env, 'SEMI2_CODEGRAPH_AUTOINDEX') ?? platform !== 'host';
22
+ }
@@ -0,0 +1,25 @@
1
+ import { ensureGitExclude } from "./workspace.js";
2
+ const inflight = new WeakMap();
3
+ function runsFor(run) {
4
+ let runs = inflight.get(run);
5
+ if (runs === undefined) {
6
+ runs = new Map();
7
+ inflight.set(run, runs);
8
+ }
9
+ return runs;
10
+ }
11
+ /** 确保目录已建索引;同一 runner/root 的并发初始化共享一次执行,signal 只接受插件生命周期所有者。 */
12
+ export function ensureIndex(run, root, signal) {
13
+ const runs = runsFor(run);
14
+ const existing = runs.get(root);
15
+ if (existing !== undefined)
16
+ return existing;
17
+ const tracked = (async () => {
18
+ await run(['init', '--yes', root], { cwd: root, noDaemon: true, ...(signal === undefined ? {} : { signal }) });
19
+ ensureGitExclude(root);
20
+ })().finally(() => {
21
+ runs.delete(root);
22
+ });
23
+ runs.set(root, tracked);
24
+ return tracked;
25
+ }
@@ -0,0 +1,21 @@
1
+ const CODEGRAPH_SECTION = 'tool:codegraph';
2
+ export const CODEGRAPH_GUIDE = 'CodeGraph provides indexed repository structure and symbol relationships. Filesystem tools provide exact ' +
3
+ 'file contents and literal text search. CodeGraph uses the local .codegraph index of the selected workspace. Relative paths resolve from the ' +
4
+ 'current session workspace, and indexes are not inherited from ancestor directories.';
5
+ /** 注册范围感知指引;工具在当前 agent 不可见时不向模型描述它。 */
6
+ export function registerGuide(ctx, warn) {
7
+ try {
8
+ const registry = ctx.get('systemPrompt');
9
+ if (registry === undefined)
10
+ return () => { };
11
+ return registry.section({
12
+ name: CODEGRAPH_SECTION,
13
+ order: registry.getSectionOrder('TOOL_READ') - 1,
14
+ text: ({ scope }) => ctx.tools.get('codegraph_explore', scope) === undefined ? '' : CODEGRAPH_GUIDE,
15
+ });
16
+ }
17
+ catch (error) {
18
+ warn(`codegraph guide register failed: ${error instanceof Error ? error.message : String(error)}`);
19
+ return () => { };
20
+ }
21
+ }