dsh-ris-yds 0.0.0-stage → 1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -2
  3. package/assets/yds-chrome-resource-override.crx +0 -0
  4. package/bin/check-listing.mjs +247 -0
  5. package/bin/yds.js +19 -0
  6. package/cordis.patch.yml +13 -0
  7. package/lib/browser.js +428 -0
  8. package/lib/build.js +115 -0
  9. package/lib/cli.js +1130 -0
  10. package/lib/confcenter.js +377 -0
  11. package/lib/config.js +552 -0
  12. package/lib/fe-dev.js +206 -0
  13. package/lib/ide-proxy.js +906 -0
  14. package/lib/ide-watch.js +188 -0
  15. package/lib/ide.js +728 -0
  16. package/lib/index.js +267 -0
  17. package/lib/proc.js +270 -0
  18. package/lib/risd-peer.js +353 -0
  19. package/lib/rules.js +193 -0
  20. package/lib/socks.js +279 -0
  21. package/lib/token.js +36 -0
  22. package/lib/yms.js +336 -0
  23. package/lib/yoncode.js +249 -0
  24. package/native/yds-rust.darwin-arm64.node +0 -0
  25. package/native/yds-rust.darwin-x64.node +0 -0
  26. package/native/yds-rust.linux-arm64.node +0 -0
  27. package/native/yds-rust.linux-x64.node +0 -0
  28. package/native/yds-rust.win32-arm64.node +0 -0
  29. package/native/yds-rust.win32-x64.node +0 -0
  30. package/package.json +58 -4
  31. package/skills/dsh-ris-yds/SKILL.md +134 -0
  32. package/test/browser.test.mjs +442 -0
  33. package/test/cli-stop.test.mjs +40 -0
  34. package/test/cli.test.mjs +246 -0
  35. package/test/config.test.mjs +106 -0
  36. package/test/fe-dev.test.mjs +93 -0
  37. package/test/hints.test.mjs +13 -0
  38. package/test/ide-config.test.mjs +155 -0
  39. package/test/ide-proxy.test.mjs +256 -0
  40. package/test/ide-watch.test.mjs +79 -0
  41. package/test/launch-proxy.test.mjs +83 -0
  42. package/test/lease-release.test.mjs +76 -0
  43. package/test/mode-separation.test.mjs +147 -0
  44. package/test/pidfile.test.mjs +105 -0
  45. package/test/proc.test.mjs +172 -0
  46. package/test/proxy-standalone.test.mjs +121 -0
  47. package/test/risd-leases.test.mjs +111 -0
  48. package/test/risd-tier.test.mjs +361 -0
  49. package/test/rules-e2e.test.mjs +122 -0
  50. package/test/rules.test.mjs +154 -0
  51. package/test/yoncode.test.mjs +159 -0
package/lib/yoncode.js ADDED
@@ -0,0 +1,249 @@
1
+ /**
2
+ * YonCode host patching.
3
+ *
4
+ * YonCode derives the address it advertises to the platform (serverBindAddress)
5
+ * from a local NIC. When `.vscode/settings.json` points it at a callback address
6
+ * the machine does not own — the normal case behind NAT, in WSL, or through a
7
+ * port forward — it refuses to launch with
8
+ * "Invalid IP address, no interface found with address <ip>".
9
+ *
10
+ * We cannot fix that from our side: the check lives in YonCode's bundled
11
+ * extension (`resources/app/extensions/yonbuilder-development-studio-core/dist/
12
+ * extension.js`). This module detects whether the check is still active and can
13
+ * turn the throw into a warning — with a `.bak` next to it, so it can be undone
14
+ * and re-applied after a YonCode upgrade rewrites the file.
15
+ *
16
+ * Nothing here touches the project; it is opt-in from the user (the agent asks
17
+ * before writing into a Program Files directory).
18
+ */
19
+
20
+ import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'
21
+ import { homedir } from 'node:os'
22
+ import { join } from 'node:path'
23
+
24
+ /** Marker our patch leaves in the bundle (also how a patch is detected). */
25
+ export const YONCODE_PATCH_MARKER = '[yds-patch]'
26
+
27
+ /** The file we patch, relative to the YonCode installation directory. */
28
+ export const YONCODE_EXTENSION_RELATIVE = 'resources/app/extensions/yonbuilder-development-studio-core/dist/extension.js'
29
+
30
+ /** Matches the `if (!ipv4IsValid(ipv4)) { throw … }` block in the shipped bundle. */
31
+ const VALIDATION_RE = /if\s*\(\s*!\s*_util\.ipv4IsValid\([^)]*\)\s*\)\s*\{\s*throw new Error\([^;]*?\);\s*\}/
32
+
33
+ /**
34
+ * The YMS launch `vmArgs` snippet YonCode builds — the anchor we insert the JVM
35
+ * proxy flags after. It is a plain template string in the bundle.
36
+ */
37
+ const SOCKS_ANCHOR = /-Dapp\.metainfo\.preferred-networks=\\?\$\{command:yds\.serverBindAddress\} -Dserver\.port=\\?\$\{config:yds\.serverPort\}/
38
+
39
+ /** Our injected flags, so a re-patch replaces instead of stacking. */
40
+ const SOCKS_FLAGS_RE = / -DsocksProxyHost=[^\s"']+ -DsocksProxyPort=\d+/
41
+
42
+ /**
43
+ * Why this exists: ProxyBridge (WinDivert) breaks the Oracle TNS handshake —
44
+ * the connection is accepted, then reset right after `T2C7Oversion.doOVERSION`,
45
+ * so the YMS module cannot reach its database. A JVM started with
46
+ * `-DsocksProxyHost` opens an ordinary TCP connection through the SOCKS server
47
+ * instead, which is the same path that already works from WSL.
48
+ * @param socks - `{host, port}` from `.env` (`socks.host`/`socks.port`).
49
+ * @returns the flags to append, or null when no SOCKS server is configured.
50
+ */
51
+ export function socksFlags(socks) {
52
+ const host = typeof socks?.host === 'string' ? socks.host.trim() : ''
53
+ const port = Number(socks?.port)
54
+ if (host === '' || !Number.isInteger(port) || port <= 0 || port > 65535) return null
55
+ return ` -DsocksProxyHost=${host} -DsocksProxyPort=${port}`
56
+ }
57
+
58
+ /**
59
+ * Where a YonCode install may be, most specific first.
60
+ *
61
+ * `cfg.yoncodeHome` (`.env` `yoncode.home` or `YONCODE_HOME`) wins; otherwise the
62
+ * platform defaults, including the Windows drives WSL can see.
63
+ * @param cfg - resolved plugin config (`cfg.yoncodeHome` optional).
64
+ * @returns candidate installation directories.
65
+ */
66
+ export function yoncodeCandidates(cfg = {}) {
67
+ const out = []
68
+ const push = (value) => {
69
+ if (typeof value === 'string' && value !== '' && !out.includes(value)) out.push(value)
70
+ }
71
+ // An explicit location is authoritative: if it is wrong we report "absent"
72
+ // rather than silently inspecting some other installation.
73
+ const explicit = typeof cfg.yoncodeHome === 'string' && cfg.yoncodeHome !== ''
74
+ ? cfg.yoncodeHome
75
+ : (typeof process.env.YONCODE_HOME === 'string' && process.env.YONCODE_HOME !== '' ? process.env.YONCODE_HOME : '')
76
+ if (explicit !== '') return [explicit]
77
+ if (process.platform === 'win32') {
78
+ push('C:\\Program Files\\YonCode')
79
+ push('D:\\Program Files\\YonCode')
80
+ }
81
+ if (process.platform === 'darwin') {
82
+ push('/Applications/YonCode.app/Contents/Resources/app')
83
+ push(join(homedir(), 'Applications/YonCode.app/Contents/Resources/app'))
84
+ }
85
+ push('/opt/YonCode')
86
+ push('/usr/share/YonCode')
87
+ push('/usr/local/YonCode')
88
+ // A Windows install reached from WSL.
89
+ for (const drive of ['c', 'd', 'e']) push(`/mnt/${drive}/Program Files/YonCode`)
90
+ return out
91
+ }
92
+
93
+ /**
94
+ * Read the bundle and classify it.
95
+ *
96
+ * States: `patched` (our warning is in place), `unpatched` (the throw is still
97
+ * there), `unrecognised` (file exists but has neither shape — a different
98
+ * YonCode version we refuse to guess about), `absent` (no installation found),
99
+ * `incomplete` (directory exists without the bundle).
100
+ * @param cfg - resolved plugin config.
101
+ * @returns `{ found, home, file, state, backup, detail }`.
102
+ */
103
+ export function detectYoncode(cfg = {}) {
104
+ let seenIncomplete = null
105
+ for (const home of yoncodeCandidates(cfg)) {
106
+ const file = join(home, YONCODE_EXTENSION_RELATIVE)
107
+ if (!existsSync(file)) {
108
+ if (seenIncomplete === null && existsSync(home)) seenIncomplete = home
109
+ continue
110
+ }
111
+ let text = ''
112
+ try {
113
+ if (statSync(file).size > 32 * 1024 * 1024) return { found: true, home, file, state: 'unrecognised', backup: null, detail: '文件过大,跳过' }
114
+ text = readFileSync(file, 'utf8')
115
+ } catch (error) {
116
+ return { found: true, home, file, state: 'unrecognised', backup: null, detail: `无法读取:${error instanceof Error ? error.message : String(error)}` }
117
+ }
118
+ const backup = `${file}.bak`
119
+ const hasBackup = existsSync(backup)
120
+ const socksMatch = SOCKS_FLAGS_RE.exec(text)
121
+ const socksValue = socksMatch === null ? '' : socksMatch[0].trim()
122
+ const socksState = socksMatch !== null ? 'patched' : SOCKS_ANCHOR.test(text) ? 'unpatched' : 'unrecognised'
123
+ const withSocks = (result) => ({ ...result, found: true, home, file, socksState, socksValue, backup: hasBackup ? backup : null })
124
+ if (text.includes(YONCODE_PATCH_MARKER)) {
125
+ return withSocks({ state: 'patched', detail: '网卡 IP 校验已改为告警' })
126
+ }
127
+ if (VALIDATION_RE.test(text)) {
128
+ return withSocks({ state: 'unpatched', detail: '网卡 IP 校验仍在(serverBindAddress 不是本机网卡时拒绝启动)' })
129
+ }
130
+ return withSocks({ state: 'unrecognised', detail: '找不到校验代码,可能是不认识的 YonCode 版本' })
131
+ }
132
+ if (seenIncomplete !== null) {
133
+ return { found: true, home: seenIncomplete, file: join(seenIncomplete, YONCODE_EXTENSION_RELATIVE), state: 'incomplete', backup: null, detail: '安装目录存在但没找到 YonCode 扩展包' }
134
+ }
135
+ return { found: false, home: null, file: null, state: 'absent', backup: null, detail: '没有找到 YonCode 安装(可用 .env 的 yoncode.home 指定)' }
136
+ }
137
+
138
+ /** True when the failure was a permission problem, i.e. elevation would help. */
139
+ function isPermissionError(error) {
140
+ const code = error?.code ?? ''
141
+ return code === 'EACCES' || code === 'EPERM' || code === 'EROFS' || /permission denied/i.test(error?.message ?? '')
142
+ }
143
+
144
+ /**
145
+ * Patch the bundle: turn the NIC check into a warning, and (optionally) append the
146
+ * JVM SOCKS flags to the YMS launch arguments.
147
+ *
148
+ * Both edits share one `.bak` of the untouched file, so {@link restoreYoncode}
149
+ * undoes them together. Idempotent: an already patched bundle reports
150
+ * `changed: false`.
151
+ * @param detected - the result of {@link detectYoncode}.
152
+ * @param {{socks?: {host: string, port: number}}} [options] - SOCKS server to inject.
153
+ * @returns `{ ok, changed, state, socksState, error?, needElevation? }`.
154
+ */
155
+ export function patchYoncode(detected, options = {}) {
156
+ if (!detected.found) return { ok: false, changed: false, state: detected.state, socksState: detected.socksState, error: detected.detail }
157
+ const flags = socksFlags(options.socks)
158
+ const wantsSocks = flags !== null
159
+ const needsValidation = detected.state === 'unpatched'
160
+ // Same flags already in place → nothing to do; a *different* SOCKS server must
161
+ // replace the old pair instead of stacking a second one.
162
+ const needsSocks = wantsSocks && (detected.socksState !== 'patched' || (detected.socksValue ?? '') !== flags.trim())
163
+ if (!needsValidation && !needsSocks) {
164
+ // `ok` means "the bundle has every edit this call asked for": the NIC check
165
+ // when it was still active, the JVM flags when a SOCKS server was given.
166
+ const ok = detected.state === 'patched' && (!wantsSocks || detected.socksState === 'patched')
167
+ const error = ok
168
+ ? undefined
169
+ : detected.socksState === 'unrecognised' && wantsSocks
170
+ ? '找不到 YMS 启动参数模板,无法注入 JVM 代理参数'
171
+ : detected.detail
172
+ return { ok, changed: false, state: detected.state, socksState: detected.socksState, error }
173
+ }
174
+ try {
175
+ let text = readFileSync(detected.file, 'utf8')
176
+ if (needsValidation && !VALIDATION_RE.test(text)) return { ok: false, changed: false, state: 'unrecognised', socksState: detected.socksState, error: '文件内容已变化,无法定位校验代码' }
177
+ if (needsSocks && !SOCKS_FLAGS_RE.test(text) && !SOCKS_ANCHOR.test(text)) {
178
+ return { ok: false, changed: false, state: detected.state, socksState: 'unrecognised', error: '找不到 YMS 启动参数模板,无法注入 JVM 代理参数' }
179
+ }
180
+ if (!existsSync(`${detected.file}.bak`)) copyFileSync(detected.file, `${detected.file}.bak`)
181
+ if (needsValidation) {
182
+ text = text.replace(VALIDATION_RE, (block) => {
183
+ const match = /ipv4IsValid\(([^)]*)\)/.exec(block)
184
+ const argument = match === null ? 'ipv4' : match[1].trim()
185
+ return `if (!_util.ipv4IsValid(${argument})) {\n console.warn("${YONCODE_PATCH_MARKER} serverBindAddress " + ${argument} + " is not a local NIC IP, skip validation");\n }`
186
+ })
187
+ }
188
+ if (needsSocks) {
189
+ text = SOCKS_FLAGS_RE.test(text)
190
+ ? text.replace(SOCKS_FLAGS_RE, flags)
191
+ : text.replace(SOCKS_ANCHOR, (anchor) => anchor + flags)
192
+ }
193
+ writeFileSync(detected.file, text)
194
+ return {
195
+ ok: true,
196
+ changed: true,
197
+ state: needsValidation ? 'patched' : detected.state,
198
+ socksState: needsSocks ? 'patched' : detected.socksState,
199
+ }
200
+ } catch (error) {
201
+ const message = error instanceof Error ? error.message : String(error)
202
+ return { ok: false, changed: false, state: detected.state, socksState: detected.socksState, error: message, needElevation: isPermissionError(error) }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Put the original bundle back from `.bak`.
208
+ * @param detected - the result of {@link detectYoncode}.
209
+ * @returns `{ ok, changed, state, error?, needElevation? }`.
210
+ */
211
+ export function restoreYoncode(detected) {
212
+ const backup = detected.backup ?? (detected.file === null ? null : `${detected.file}.bak`)
213
+ if (backup === null || !existsSync(backup)) {
214
+ return { ok: false, changed: false, state: detected.state, error: '没有 .bak 备份,无法还原(重装 YonCode 也会恢复原文件)' }
215
+ }
216
+ try {
217
+ copyFileSync(backup, detected.file)
218
+ return { ok: true, changed: true, state: 'unpatched' }
219
+ } catch (error) {
220
+ const message = error instanceof Error ? error.message : String(error)
221
+ return { ok: false, changed: false, state: detected.state, error: message, needElevation: isPermissionError(error) }
222
+ }
223
+ }
224
+
225
+ /**
226
+ * One-line summary for `yds doctor` and the CLI.
227
+ * @param detected - the result of {@link detectYoncode}.
228
+ * @returns human readable line.
229
+ */
230
+ export function describeYoncode(detected) {
231
+ if (!detected.found) return `yoncode: ${detected.detail}`
232
+ const where = detected.home ?? detected.file
233
+ const socks = detected.socksState === 'patched' ? ',JVM 代理参数已注入' : ''
234
+ return `yoncode: ${detected.state}(${detected.detail}${socks})${where === null ? '' : ` · ${where}`}`
235
+ }
236
+
237
+ /**
238
+ * Should we warn the user about the launch configuration we are about to write?
239
+ *
240
+ * Only when YonCode is installed AND its check is active: an absent YonCode is
241
+ * not a problem (we run standalone), and a patched one is already fine.
242
+ * @param detected - the result of {@link detectYoncode}.
243
+ * @returns the warning text, or null.
244
+ */
245
+ export function yoncodeWarning(detected) {
246
+ if (!detected.found || detected.state !== 'unpatched') return null
247
+ return 'YonCode 仍在校验 serverBindAddress 必须是本机网卡 IP;用外网/端口转发/回调地址启动会失败。'
248
+ + '可以让 ris-yds 把该校验改成告警(需写 YonCode 安装目录的权限),运行 `yds yoncode patch` 或让我代做。'
249
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "dsh-ris-yds",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.0",
4
+ "description": "YonBIP YDS local runtime for the DSH agent: config resolution (project settings.json + optional .env), YYCToken, confcenter sync, SOCKS forward, yms-home build/start/stop with JDWP, and dsh-ris-ide run-config integration. Cross-OS (linux/darwin/win32, WSL aware). · YonBIP YDS 本地运行时:项目配置解析(settings.json / .env)、YYCToken、confcenter 拉取、SOCKS 转发、yms-home 构建与启停(含 JDWP 调试)、dsh-ris-ide 启动器集成;跨平台(linux/darwin/win32,识别 WSL)。",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ris-cloud/dsh-ris-yds.git"
9
+ },
10
+ "homepage": "https://gitee.com/riscloud/dsh-ris-yds#readme",
11
+ "bugs": {
12
+ "url": "https://gitee.com/riscloud/dsh-ris-yds/issues"
13
+ },
14
+ "keywords": [
15
+ "dsh",
16
+ "dsh-plugin",
17
+ "deepseek-harness",
18
+ "yds",
19
+ "yonbip",
20
+ "yonyou",
21
+ "yms",
22
+ "liberty"
23
+ ],
24
+ "type": "module",
25
+ "main": "lib/index.js",
26
+ "dsh": {
27
+ "manifestVersion": 1,
28
+ "bundle": {
29
+ "patch": "cordis.patch.yml"
30
+ }
31
+ },
32
+ "exports": {
33
+ ".": "./lib/index.js",
34
+ "./cli": "./lib/cli.js",
35
+ "./package.json": "./package.json"
36
+ },
37
+ "bin": {
38
+ "yds": "bin/yds.js"
39
+ },
40
+ "files": [
41
+ "LICENSE",
42
+ "README.md",
43
+ "bin",
44
+ "cordis.patch.yml",
45
+ "lib",
46
+ "native",
47
+ "resources",
48
+ "skills",
49
+ "test",
50
+ "assets"
51
+ ],
52
+ "engines": {
53
+ "node": ">=18",
54
+ "dsh": ">=0.2.0-rc.2"
55
+ },
56
+ "scripts": {
57
+ "test": "node --test \"test/*.test.mjs\"",
58
+ "check:listing": "node bin/check-listing.mjs"
59
+ }
60
+ }
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: dsh-ris-yds
3
+ description: How to run the YonBIP YDS backend locally with the dsh-ris-yds tools — config resolution (settings.json + optional .env), YYCToken, confcenter sync, SOCKS forward, yms-home build/start/stop, and ris_dev JDWP debugging. Use when asked to start/stop/debug the yds backend, generate YYCToken, pull confcenter config, or wire the YMS program into dsh-ris-ide.
4
+ ---
5
+
6
+ # dsh-ris-yds — YonBIP YDS 本地运行时
7
+
8
+ 本地跑 `c-ass-kk-hans`(YDS 客)后端 = 在 yms-home 里起一个 YMS(WebSphere Liberty)容器,
9
+ 应用 WAR 部署在容器里。所有机器相关值来自配置文件,本 skill 不依赖任何写死的 IP。
10
+
11
+ ## 配置模型(动手前先 `yds_doctor`)
12
+
13
+ | 来源 | 文件 | 性质 |
14
+ |---|---|---|
15
+ | 项目配置 | `<project>/.vscode/settings.json`(yds.* 键) | **只读**(YonCode 标准格式,共用工作区不破坏) |
16
+ | 机器配置 | `<workspace>/.env`(可选,KEY=VALUE) | SOCKS 代理、git 凭据、本机端口覆盖 |
17
+ | 环境变量 | `YDS_SOCKS_HOST` 等 | 临时覆盖 |
18
+
19
+ 关键键:`yds.workbenchUrl / accessKey / accessSecret / serverBindAddress / serverPort / env`(项目);
20
+ `socks.host/port`(SOCKS 存在→DB/Redis 走本地转发;不存在→直连)。本地监听端口**自动选空闲端口**(从回调端口 +2 起找),不需要配置。
21
+ 回调地址 = serverBindAddress:serverPort;本地监听端口默认=serverPort,WSL mirrored/端口冲突时用
22
+ 本地端口自动错开,无需配置(Windows 侧可用 portproxy 把回调端口映射回来)。
23
+
24
+ ## 标准流程(新环境从零)
25
+
26
+ 1. `yds_doctor` — 体检,缺什么会明说(settings.json 缺 yds.* 键让用户补)。
27
+ 2. `yds_build` — 从零构建 yms-home(首次下载 77MB kernel,约 19 分钟;之后约 1 分钟)+ 拉配置。
28
+ 3. `yds_start` — 后台启动(socks 转发器/配置缺失自动补,幂等)。输出 rdbga cookie 值。
29
+ 4. `yds_status` / `yds_logs` — 观察就绪(`/_yms/ready` → running;真实库首启 1–2 分钟)。
30
+ 5. `yds_stop` 停止。
31
+
32
+ ## 调试(JDWP 断点)
33
+
34
+ - `yds_ide apply` 写 ris-ide 程序配置前,若 `.dsh-ris-ide/config.json` 不存在,先按 ris-ide 的结构初始化(`version/defaults/programs`);条目名不叫 `yds-yms-*`,而是 **java=parent.artifactId、前端=package.json 平级 module.xml 的 module.name**(老名字会被识别并就地改名,不留重复行)。
35
+ - ide 条目必须 `kind: "spring"` 才能被 ris_dev_debug 识别(manual 条目会报「只有 Spring(Java) 程序支持调试」)——`yds_ide apply` 生成的条目已带;自定义 startCommand 优先于默认 spring 启动路径,调试走 `${JDWP_ARGS}` 占位符替换,行为不变。
36
+
37
+ `yds_ide {action:"apply"}` 把 `yds-yms-<module>` 条目写进 `.dsh-ris-ide/config.json`
38
+ (startCommand 含 `${JDWP_ARGS}` 占位符,仅 upsert 自己的条目,不碰用户其它条目)→
39
+ 之后 `ris_dev_debug` 断点调试 / `ris_dev_start` / `ris_dev_stop` 走 dsh-ris-ide 的标准启停(日志进 logs/)。
40
+
41
+ **同时生成 `.vscode/launch.json`**(写在**项目根**——即含 `.vscode/settings.json` 的目录、
42
+ IDE 实际打开的文件夹,不是 DSH 会话 workspace 根;与 YonCode 的 `ensureLaunchConfig`
43
+ 同名同构条目 `yms-middleware-cloudnative-jdk-21`):YonCode 端 F5 也能直接启动/调试 YMS 中间件,
44
+ 两边写入互不冲突(同名条目、只 upsert 自己那条)。该条目的 vmArgs 含
45
+ `-Dapp.metainfo.start_mode=static`(**关键**:服务发现改读 `config/ip_node_mapping.json`
46
+ 而不是 eureka——开发机到不了内部 k8s registry;实例不可达时 IrisInvoker 再回退
47
+ `domain.url` 云调试改写走公网 WAF)、`-Dyms_fetch_config_version=2.0.0`、
48
+ `-Dyms.bootstrap.enableNewConfig=true`、`-Dinotify_enable=false`,以及完整的
49
+ Java 21 add-exports/add-opens 集(少一条 add-opens 就会让 Liberty JNDI 初始化失败、
50
+ 模块部署 fatal)。`yds start` 的 JVM 参数与之保持一致。
51
+
52
+ **构建日志可追踪**:ris_dev_start 的 startCommand 走 `yds start --foreground`——启动前
53
+ 若 yms-home 未构建会自动跑 mvn,**mvn 全部输出与各阶段进度(`[yds] …` 行)流式写进
54
+ ris-ide 的日志文件**,用 `ris_dev_logs` / `yds_logs` 实时追踪(首次构建约 19 分钟、
55
+ kernel 缓存后约 1 分钟)。启动序列:mvn 构建 → confcenter 配置 → SOCKS 隧道预检 +
56
+ 转发器 → Liberty 前台启动。
57
+
58
+ **SOCKS 隧道预检**:转发器启动前先做一次 SOCKS CONNECT 探测;隧道不可用(远程开发机
59
+ 出口失效)时快速失败并给出明确提示,而不是等到模块部署 20 秒后报
60
+ `数据源无法访问数据库`。隧道是瞬时故障时稍后重试即可。
61
+
62
+ **内网端点全覆盖(yonbip 隧道化)**:SOCKS 模式下除了 DB/Redis,yonbip_config.json 里的
63
+ RFC1918 内网端点(kafka 6880 / ES 6888 / license 30001 等,端口≥1024)也自动改写成
64
+ `127.0.0.1:<同端口>` 并加入转发器目标。例外(均为平台侧限制,改写无意义):
65
+ `*.inner-yonbip-k8s.com` 域名(SOCKS 服务器解析不了 k8s 内部 DNS,保持原值+警告)、
66
+ 端口<1024(本地无权限监听)。适配逻辑升级用 `.dsh-ris-yds/adapt-version` 戳标记,
67
+ 版本不符时 `yds start` 自动从缓存重新适配(无需删文件)。
68
+
69
+ ## API 调试
70
+
71
+ - 本地直连:`YYCToken` 头,`yds_token {url}` 生成(URL 绑定宽松,根 URL token 可打任意路径)。
72
+ - 浏览器路径:在 workbenchUrl 域下种 cookie `rdbga=<yds_start 输出的 base64>`,
73
+ 请求 `/<module>/**` 会经平台网关路由回调到本地实例(会话上下文透传,网关会剥掉 `/<module>` 前缀)。
74
+ - **`rdbga` 必须唯一且非空**:YonCode 内置浏览器启动时可能写一个空值 host-only `rdbga`,
75
+ 它的优先级高于带点域名的正确值 → 网关判定「非调试请求」,全转远端 pod:页面正常、本机
76
+ `logs/access.log` 空、断点不进,**故障完全静默**。先 `yds_browser {action:"status"}` 看(会打印期望值和来源),
77
+ 再 `yds_browser {action:"fix"}`:fix **默认 dry_run=true 只预览**,确认计划后用 `dry_run:false` 真写
78
+ (删空值/重复/不匹配,写入期望值);CLI 对应 `yds browser fix [--rdbga=<值>] [--dry-run]`。
79
+ 期望值默认按项目配置算(`module,/context@bind:port` 的 base64);运行中 JVM 的 `-Drdbga` 只用于校验,
80
+ 所有运行实例的值都与配置不一致时**拒绝写入**并要求 `--rdbga` 指定(别把浏览器指向不存在的实例;
81
+ 同机还有别的项目 JVM 时直接 `--rdbga` 最稳)。`yds start` 成功后会自动校准一次
82
+ (后台/前台/已在运行都会尝试,浏览器没开 DevTools 端口就跳过)。
83
+ - 业务接口 302 且日志里出现 `【yht_access_token_invalid】` / `YmsContextFilter [processOnUnLogin]`:
84
+ 那是**会话**问题(不是代理/回调),控制器根本没执行;用已登录的工作台页签发请求,别用干净上下文。
85
+ - `POST /CloudRemoteCall/<接口全名>/<方法名>` 为 YMS RPC 形态。
86
+
87
+ ## 代理(SOCKS)与端口归属
88
+
89
+ - `yds start` 前会做 SOCKS 预检(握手 + CONNECT 探测目标),隧道不可用会**快速失败**并给出原因,不会白等模块部署 2 分钟。
90
+ - 隧道优先借 dsh-ris-ide 的引擎(`yds_proxy sync|status`),`127.0.0.1:1521/6890/6880/6888/30001` 由它绑定;未装 ris-ide 才回退内置转发器——**不要手动再起第二套**,会抢端口。
91
+ - 引擎随 `dsh` 退出停止(宿主钩子),也可 `yds proxy stop` 手动停;配置与 pid 在 `~/.dsh-ris-ide/`。
92
+ - `yds proxy sync|start` 会把 SOCKS 参数写进 `.vscode/launch.json` 的 yds 条目(YonCode/VS Code F5 启动的 java 才走代理),`yds proxy stop` 自动摘掉——只动自己那条 configuration。
93
+ - 引擎与配置**全机一份、整机生效**:`~/.dsh-ris-ide/proxy.json`(`DSH_RIS_IDE_PROXY_HOME` 可改)。规则不分工作区/会话(`workspace` 只是来源标注);`yds_proxy sync` 只替换 `source=yds` 的行,用户自己的规则不受影响;yds 只写真实程序:`java`(内网 /24 整段、任意端口——JDBC/Redis 驱动不支持 SOCKS,只能靠系统层拦截)+ `git`(git.url 的主机)+ 带 `label` 的 `YonCode java`/`YonCode git`(`<YonCode>/devkit/*/bin/java.exe` 与系统 git,路径在项目外、项目配置推不出来,界面上显示 label)。端点不生成规则行(老的 `jdbc` 名字匹配不到任何进程);ProxyBridge 不可用时的 `127.0.0.1` 同端口兜底监听写在配置的 `shadows` 字段里,属于引擎输入、不进界面。
94
+ - **主路是 ProxyBridge**(系统层按进程拦截):`yds start` 发现隧道可用就自动启动它(提权走系统弹窗),`yds stop` 自动停止并清 git 代理;它没跑时注入与 127 映射自动当后备。手动入口 `yds proxy redirector [status|sync|start|stop]`。
95
+ - git 走 git 自身配置(`http.https://<host>/.proxy`),命令上不需要 `-c`;用 `yds proxy git-on|git-off` 控制。
96
+ - 相关工具:`yds_proxy`;相关 CLI:`yds proxy ...`。
97
+ - 浏览器 cookie 体检/校准:工具 `yds_browser`(fix 默认 dry-run),CLI `yds browser [status|fix] [--rdbga=<值>] [--dry-run]`;DevTools 端口清单的单一来源是 `lib/browser.js` 的 `DEVTOOLS_PORTS`(当前 9222/9223/9224/9333),不要另抄一份。
98
+
99
+ ## 前端调试(本地 extend 脚本)
100
+
101
+ - 远端页面加载的是平台的 `extend*.min.js`;本地 webpack dev server(`npm run debug:extend`,默认 3004)
102
+ 要接管它,三条路:
103
+ 1. `yds fe rules` → 生成 `resource-override-rules.json`,让用户把它 Import 进 Chrome 的 Resource Override 扩展(`yds-chrome-resource-override.crx`)。
104
+ 2. `yds fe plan` → 拿到 `{match, replace, regex, substitution}` 七条;**你自己就能用 CDP 注入**:
105
+ `Fetch.enable` 后对每个 `Fetch.requestPaused`,URL 命中 `regex` 就
106
+ `Fetch.continueRequest({ requestId, url: 用 → 右侧地址(`\1` 展开) })`,其余 `Fetch.continueRequest({ requestId })`。
107
+ 3. `yds fe url <页面>` → 直接给带 `scriptUrl=http://127.0.0.1:<fe.port>/static` 的地址,零配置。
108
+ - 模块名/工作台主机/前端端口都来自项目配置(项目配置里的前端端口 可改),**不要硬编码 c-ass-kk-hans / srmdev 主机 / 3004**。
109
+
110
+ ## YonCode 网卡 IP 校验
111
+ - `yds doctor` / `yds yoncode` 检查 YonCode 的 `serverBindAddress` 校验是否已降级为告警;`unpatched` 时**先问用户**(这是改别人的安装目录),同意后 `yds yoncode patch`,需要写 Program Files 就先说明要提权(管理员 / danger-full-access)。
112
+ - `yds yoncode revert` 从 `.bak` 还原;YonCode 升级重写文件后需重跑 patch。
113
+
114
+ ## 硬约束/口径
115
+
116
+ - 项目源码只读;写只发生在 yms-home(YonCode 同构的标准部署布局)、`.dsh-ris-yds/`、`.dsh-ris-ide/config.json`(只 upsert 自己那一条)、项目根的 `.vscode/launch.json`(同样只 upsert 自己那条 `yms-middleware-cloudnative-jdk-21`,与 YonCode 生成物同名同构;其余 configuration 与注释/尾逗号原样保留——YonCode 自己每次启动也写这个文件)。唯一的浏览器侧写入是工作台域下的 `rdbga` cookie(只此一个名字、只在该域;失败静默,`--dry-run` 可先预览)。
117
+ - 进程身份校验:stop/状态只认 pidfile 里「经 /proc cmdline 验证是本工具进程」的 pid,pid 复用/重启后的陈旧 pidfile 自动清理,绝不误杀无关进程。
118
+ - 启动失败自动回滚:startDetached 端口 120 秒未就绪即报错并回收(TERM+pidfile 清理),SOCKS 转发器随之停止,不留孤儿进程。
119
+ - 前台模式(ris_dev_start)同样写 pidfile——`yds_status`/`yds_stop` 对 IDE 启动的实例可见;前台输出里也打印 rdbga cookie 行。
120
+ - `yds_logs` 只读日志尾部(≤256KB);yms-console.log 超过 16MB 自动轮转为 .old。
121
+ - CLI 未知命令/动作、多余参数/未知选项、执行失败都**非 0 退出**;agent 工具结果带 `ok` 字段(失败 `ok:false`),先看 `ok` 再解析文本。
122
+ - JDBC 适配支持 oracle @//service、oracle SID、mysql 三种形态;无法识别的 jdbc url 会打警告(不重写不转发)。
123
+ - 安全边界(用户已决策文档化):workbenchUrl 是项目级信任根(勿对不可信仓库跑 build/start);SOCKS 转发器本机无鉴权;`yds token` 明文输出(调试用途);Windows 停机为硬杀(无优雅 TERM);`yds browser fix` 会删/写用户浏览器的 `rdbga` cookie(仅工作台域、仅此名字、失败静默;不确定先 `--dry-run`)。
124
+ - 与 YonCode 共用工作区时:YonCode 的 sync 会把 yms-home/config 覆盖为远端原始地址(直连场景正好);
125
+ SOCKS 场景重跑 `yds_confcenter` 即可(幂等秒级)。
126
+ - git 只读(`git.commit=never`),clone 走 `git -c http.proxy=socks5h://<socks>`(仅当配置了 SOCKS)。
127
+ - 模块名从 yonbuilder.json/module.xml 自动识别,不要手工写死。
128
+
129
+ ## ris-ide 只做外挂(监听,不改它)
130
+
131
+ - `yds ide apply` 注入 `id: yds:<项目名>` 的启动器行 + 按 `module.xml`/pom 修正 yds 项目的行名 + 禁用被启动器取代的后端行。
132
+ - 然后插件在宿主里监听 `.dsh-ris-ide/config.json`(`fs.watch` + 2s 轮询、400ms 去抖):ris-ide 初始化/刷新配置把条目冲掉时自动补回,其它行原样保留。登记在 `~/.dsh-ris-ys/ide-watch.json`…实际路径 `~/.dsh-ris-yds/ide-watch.json`。
133
+ - 不要为了联动去改 dsh-ris-ide(用户口径:ris-yds 不能影响 ris-ide 的使用)。
134
+ - `yds yoncode patch` 除了去掉网卡校验,还会给 YMS 的 vmArgs 注入 `-DsocksProxyHost/-DsocksProxyPort`(java 不再走 ProxyBridge:WinDivert 会把 Oracle TNS 握手 reset);`.env proxy.javaRules=on` 可恢复 java 代理规则。