@shundoo-ai/dsh-cosmic 1.0.2 → 1.0.3-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shundoo-ai/dsh-cosmic",
3
- "version": "1.0.2",
3
+ "version": "1.0.3-alpha.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  ".config",
@@ -42,6 +42,32 @@ description: >
42
42
 
43
43
  ---
44
44
 
45
+ ## Windows:一条命令走完全程(推荐)
46
+
47
+ > 下面各职责给的是 **macOS/Linux** 命令。Windows 上**手拼这套流程代价极高** —— 实测一个 Windows 会话为跑通「构建 + 热部署」发了 **63 条 pwsh**、**6 次提权**(探测 PID → 找 JDK → 找 tools.jar → attach,每步都要单独授权)。
48
+
49
+ 本 skill 随包提供封装脚本,**把全程收敛成一次调用 ⇒ 只需一次提权**:
50
+
51
+ ```powershell
52
+ # 体检(不部署)
53
+ pwsh -File {SKILL_DIR}\scripts\cosmic-hotdeploy.ps1
54
+ # 构建 + 热部署
55
+ pwsh -File {SKILL_DIR}\scripts\cosmic-hotdeploy.ps1 -Module <模块名>
56
+ # 干净重建 + 热部署
57
+ pwsh -File {SKILL_DIR}\scripts\cosmic-hotdeploy.ps1 -Module <模块名> -Clean
58
+ # 只热部署已有 jar
59
+ pwsh -File {SKILL_DIR}\scripts\cosmic-hotdeploy.ps1 -Jar <jar路径>
60
+ # 只构建、不部署
61
+ pwsh -File {SKILL_DIR}\scripts\cosmic-hotdeploy.ps1 -Module <模块名> -WhatIf
62
+ ```
63
+
64
+ 内部依次完成:定位工作区 → 解析 preset bundle → 读 `dev.json` → **按端口反查 cosmic PID**(不是 `jps`,见职责②)→ 挑含 `tools.jar` 的 JDK → 构建 → Attacher 热加载。
65
+
66
+ 参数:`-Module`|`-Jar`|`-Clean`|`-WhatIf`|`-LogFile`|`-Workspace`|`-PresetDir`|`-Port`。
67
+ 工作区默认从「脚本位置的上两级」或当前目录推断;布局不同就传 `-Workspace <项目根>`。
68
+
69
+ ---
70
+
45
71
  ## 职责①:开启热部署(每个环境一次性)
46
72
 
47
73
  1. **IDEA 启动 MCP(可选)**:JetBrains MCP,固定 URL `http://127.0.0.1:64342/stream`,用于**代码智能**(符号查找、调用分析、重构、即时查错)。⚠️ **非构建必需**——产 jar 走 gradle 命令行(见职责③),MCP 未配/未连不影响「build + 热部署」主线。
@@ -89,6 +115,16 @@ description: >
89
115
 
90
116
  - 通用:`jps -l | grep kd.bos.service.webserver.JettyServer`(取 `appName=cosmic` 的那个)
91
117
  - macOS 备选:`lsof -tiTCP:<port> -sTCP:LISTEN`
118
+ - **Windows 推荐(实测最可靠)**:按端口反查 + 命令行校验
119
+
120
+ ```powershell
121
+ $conn = Get-NetTCPConnection -LocalPort <port> -State Listen | Select-Object -First 1
122
+ (Get-CimInstance Win32_Process -Filter "ProcessId=$($conn.OwningProcess)").CommandLine # 应含 kd.bos.service.webserver.JettyServer
123
+ ```
124
+
125
+ > ⚠️ **Windows 上 `jps` 看不到 cosmic**(实测):cosmic 常由 **SYSTEM** 账户启动,而 `jps` 只列举**当前用户**的 JVM → 输出里完全没有它,**极易误判「cosmic 没启动」**。用上面的方式零替代。
126
+ > 另:发现 PID 后先扫命令行里有没有 `-XX:+DisableAttachMechanism` —— 带此参数的 JVM **无法 attach**(硬阻断)。
127
+ > 跨用户 attach 本身**可用、无需提权**(Windows JVM attach 命名管道对 Administrators 组放行)。
92
128
 
93
129
  ### 3. 热部署
94
130
 
@@ -131,6 +167,13 @@ GRADLE_USER_HOME=<cwd>/.gradle/user-home "$GRADLE" :<模块>:build -x test
131
167
  - 产物在 `<模块>/build/libs/*.jar`(本环境 `code/yun/sd-khxty-yun/build/libs/sd-khxty-yun-1.0.0.jar`)。
132
168
  - ⚠️ 产 jar **只认 gradle 命令行**:IDEA MCP `build_project` 默认增量、源码未变会跳过(`isSuccess:true` 但 jar 不更新);勿用 `buildJar`/`deployJar`(那是冷启动拷贝到 lib/cus,热部署不需要)。
133
169
 
170
+ > **Windows 必读(实测踩坑,失败一次排查很久)**
171
+ > - **必须显式设 `JAVA_HOME` 指向 JDK(不是 JRE)**:Windows 注册表 `HKLM\SOFTWARE\JavaSoft\Java Development Kit` 的 `JavaHome` 会被 JRE 安装包改写成 `...\jre1.8.0_481`,Gradle 采信它 → `java.home` 指向 JRE → 报 `Could not find tools.jar`。显式 `$env:JAVA_HOME="C:\Program Files\Java\jdk1.8.0_XXX"`
172
+ > - **Gradle 版本必须按 `gradle/wrapper/gradle-wrapper.properties` 锁定**,不要「取 `~/.gradle/wrapper/dists` 下最新」—— 机器上可能同时装了要求 Java 22 的新版 Gradle,会直接把构建带偏。
173
+ > - ⚠️ **「伪成功构建」陷阱**:`compileJava` 处于 `UP-TO-DATE` 时 Gradle **根本不加载编译器**,缺 tools.jar 也不报错 → 看到 `BUILD SUCCESSFUL` + jar 正常产出,**但里面是旧字节码**。首次接入务必 `clean build` 验证一次真编译。
174
+ >
175
+ > 完整分析见 `cosmic-dev-best-practices/references/cosmic-hotdeploy-build-slow-windows.md` §4.3 / §4.7。Windows 上直接用上面「Windows:一条命令走完全程」的封装脚本即可,它已内置这些约束。
176
+
134
177
  ### 2. 定位错误 / 理解代码(IDEA MCP 代码智能,可选加分项)
135
178
 
136
179
  > 此节可选:MCP 未配/未连不影响「build + 热部署」主线(gradle stderr 本身也会报编译错误,含文件名+行号)。
@@ -181,4 +224,5 @@ agent(`kd.reloadjar.ReloadAgent`)在 cosmic 进程内自动执行这两步
181
224
  |------|------|------|
182
225
  | `assets/dev.json` | ① 开启热部署 | 空配置模板(`dev.py` 读它作字段基础,生成工作区 `.dsh/cosmic/config/dev.json`) |
183
226
  | `scripts/dev.py` | ① 开启热部署 | 环境配置脚本(`init`=读 `.kd/config.json` 覆盖 dev.json;`read`=只读;`__file__` 反推 skill 根) |
227
+ | `scripts/cosmic-hotdeploy.ps1` | ②③ 热加载 / 构建+热部署 | **Windows 一键封装**(可移植:工作区自动推断、无硬编码路径):体检 / `-Module` 构建+热部署 / `-Clean` / `-Jar` 直传 / `-WhatIf`。见「Windows:一条命令走完全程」 |
184
228
  | `assets/kd-reloadjar-agent.jar` | ② 热加载 | 热重载 agent(含 `kd.reloadjar.ReloadAgent` + `Attacher`) |
@@ -0,0 +1,219 @@
1
+ # cosmic-hotdeploy.ps1 -- Kingdee Cosmic build + hot-deploy (Windows, portable)
2
+ #
3
+ # PURPOSE
4
+ # Windows port of the cosmic-vibe-coding skill workflow (the skill docs give
5
+ # macOS/Linux commands). Discovers the running cosmic JVM, builds the module
6
+ # with the wrapper-pinned Gradle + a real JDK8, then hot-loads the jar via the
7
+ # Attacher agent -- no cosmic restart, no touching mc.
8
+ #
9
+ # USAGE
10
+ # pwsh -File cosmic-hotdeploy.ps1 # health check only
11
+ # pwsh -File cosmic-hotdeploy.ps1 -Module <name> # build + hot-deploy
12
+ # pwsh -File cosmic-hotdeploy.ps1 -Module <name> -Clean # clean build + hot-deploy
13
+ # pwsh -File cosmic-hotdeploy.ps1 -Jar a.jar,b.jar # hot-deploy existing jars
14
+ # pwsh -File cosmic-hotdeploy.ps1 -Module <name> -WhatIf # dry run (build only)
15
+ #
16
+ # PORTABILITY
17
+ # The workspace is inferred from this script's location (expects to live in
18
+ # <workspace>/runtime/); override with -Workspace. The skill bundle is located
19
+ # via $DSH_PROFILE_DIR or $DSH_HOME; override with -PresetDir. No other
20
+ # machine-specific paths are hard-coded -- the JDK is discovered from the
21
+ # running cosmic process, then from JAVA_HOME, then from common install roots.
22
+ #
23
+ # NOTE
24
+ # Windows classpath separator is ';' (not ':'). The Attacher must run on a JDK
25
+ # whose major version matches the target JVM (JDK8 + lib\tools.jar for cosmic).
26
+
27
+ [CmdletBinding()]
28
+ param(
29
+ [string[]]$Jar,
30
+ [string]$Module,
31
+ [switch]$Clean,
32
+ [switch]$WhatIf,
33
+ [string]$LogFile,
34
+ [string]$Workspace,
35
+ [string]$PresetDir,
36
+ [int]$Port
37
+ )
38
+
39
+ $ErrorActionPreference = 'Stop'
40
+
41
+ # ---- 0. Resolve workspace (no hard-coded user paths) ----
42
+ if (-not $Workspace) {
43
+ # Expect this script at <workspace>\runtime\cosmic-hotdeploy.ps1
44
+ if ($PSScriptRoot) {
45
+ $Workspace = Split-Path $PSScriptRoot -Parent
46
+ } else {
47
+ $Workspace = (Get-Location).Path
48
+ }
49
+ # If it was moved elsewhere, fall back to the cwd when that looks like a workspace.
50
+ if (-not (Test-Path (Join-Path $Workspace 'settings.gradle'))) {
51
+ if (Test-Path (Join-Path (Get-Location).Path 'settings.gradle')) {
52
+ $Workspace = (Get-Location).Path
53
+ }
54
+ }
55
+ }
56
+ if (-not (Test-Path (Join-Path $Workspace 'settings.gradle'))) {
57
+ throw "Workspace '$Workspace' has no settings.gradle. Pass -Workspace <project root>."
58
+ }
59
+ $cwd = (Resolve-Path $Workspace).Path
60
+
61
+ # ---- 0.1 Resolve the cosmic skill bundle ----
62
+ if (-not $PresetDir) {
63
+ $roots = @()
64
+ if ($env:DSH_PROFILE_DIR) { $roots += (Join-Path $env:DSH_PROFILE_DIR 'node_modules\@shundoo-ai\dsh-cosmic') }
65
+ if ($env:DSH_HOME) { $roots += (Join-Path $env:DSH_HOME '.agent-presets\cosmic') }
66
+ $PresetDir = $roots | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1
67
+ }
68
+ if (-not $PresetDir) {
69
+ throw "Cannot locate the cosmic skill bundle. Set DSH_PROFILE_DIR/DSH_HOME, or pass -PresetDir <path containing skills\cosmic-build>."
70
+ }
71
+ $agent = Join-Path $PresetDir 'skills\cosmic-build\skills\cosmic-vibe-coding\assets\kd-reloadjar-agent.jar'
72
+ if (-not (Test-Path $agent)) { throw "Missing reload agent: $agent" }
73
+ $devScript = Join-Path $PresetDir 'skills\cosmic-build\skills\cosmic-vibe-coding\scripts\dev.py'
74
+
75
+ # ---- 1. Read dev.json (env config produced by dev.py init) ----
76
+ $devJson = Join-Path $cwd '.dsh\cosmic\config\dev.json'
77
+ if (-not (Test-Path $devJson)) {
78
+ throw "Missing $devJson -- run 'dev.py init' first (enable hot-deploy / set ERP login)."
79
+ }
80
+ $dev = Get-Content $devJson -Raw | ConvertFrom-Json
81
+
82
+ # Port: explicit -Port wins, then dev.json, then the port embedded in erp_url.
83
+ $effPort = if ($Port) { $Port }
84
+ elseif ($dev.port) { [int]$dev.port }
85
+ elseif ($dev.erp_url -match '^https?://[^:/]+:(\d+)') { [int]$Matches[1] }
86
+ else { 80 }
87
+ $accountId = $dev.account_id
88
+ $tenantId = $dev.tenant_id
89
+ if (-not $accountId) { throw "dev.json has no account_id -- re-run 'dev.py init'." }
90
+
91
+ # ---- 2. Discover the cosmic PID by port ownership, then validate the JVM ----
92
+ $conn = Get-NetTCPConnection -LocalPort $effPort -State Listen -ErrorAction SilentlyContinue |
93
+ Select-Object -First 1
94
+ if (-not $conn) { throw "Nothing is listening on port $effPort -- is cosmic running?" }
95
+ $targetPid = $conn.OwningProcess
96
+ $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$targetPid"
97
+ if ($proc.CommandLine -notmatch 'kd\.bos\.service\.webserver\.JettyServer') {
98
+ throw "Process $targetPid on port $effPort is not the cosmic JettyServer: $($proc.CommandLine)"
99
+ }
100
+ if ($proc.CommandLine -match 'DisableAttachMechanism') {
101
+ throw "Process $targetPid has -XX:+DisableAttachMechanism -- hot-deploy cannot attach to it."
102
+ }
103
+ $owner = ($proc | Invoke-CimMethod -MethodName GetOwner).User
104
+
105
+ # ---- 3. Pick the JDK: prefer the one the cosmic process itself runs on ----
106
+ # Derive JAVA_HOME from the process executable (reliable, unlike parsing the
107
+ # command line, which may quote "C:\path with spaces"\bin\java differently).
108
+ $candidates = @()
109
+ if ($proc.ExecutablePath) {
110
+ $javaBin = Split-Path $proc.ExecutablePath -Parent # ...\jdk\bin
111
+ if ((Split-Path $javaBin -Leaf) -ieq 'bin') { $candidates += (Split-Path $javaBin -Parent) }
112
+ }
113
+ if ($env:JAVA_HOME) { $candidates += $env:JAVA_HOME }
114
+ # Common JDK install roots across drives (best effort, machine independent).
115
+ foreach ($root in @("$env:ProgramFiles\Java", "${env:ProgramFiles(x86)}\Java", 'C:\Java',
116
+ 'D:\Java', 'D:\Program Files\Java', "$env:USERPROFILE\.jdks")) {
117
+ if ($root -and (Test-Path $root)) {
118
+ $candidates += (Get-ChildItem $root -Directory -ErrorAction SilentlyContinue |
119
+ Where-Object { $_.Name -match '^jdk' } | ForEach-Object { $_.FullName })
120
+ }
121
+ }
122
+ $candidates = $candidates | Where-Object { $_ } | Select-Object -Unique
123
+
124
+ # A usable JDK for the Attacher has lib\tools.jar (JDK8) and bin\java.exe.
125
+ $attacherJdk = $candidates |
126
+ Where-Object { (Test-Path (Join-Path $_ 'lib\tools.jar')) -and (Test-Path (Join-Path $_ 'bin\java.exe')) } |
127
+ Select-Object -First 1
128
+ if (-not $attacherJdk) {
129
+ throw @"
130
+ No usable JDK for the Attacher (needs lib\tools.jar + bin\java.exe).
131
+ Candidates tried: $($candidates -join ' | ')
132
+ The target cosmic JVM needs a matching JDK whose major version matches it.
133
+ If cosmic runs on JDK8, install a JDK8 (not a JRE!) and pass -Workspace/-PresetDir as needed,
134
+ or set `$env:JAVA_HOME to a real JDK.
135
+ "@
136
+ }
137
+ $java = Join-Path $attacherJdk 'bin\java.exe'
138
+ $toolsJar = Join-Path $attacherJdk 'lib\tools.jar'
139
+
140
+ Write-Host "[env] workspace = $cwd"
141
+ Write-Host "[env] erp_url = $($dev.erp_url) (port $effPort)"
142
+ Write-Host "[env] cosmic PID = $targetPid (owner=$owner)"
143
+ Write-Host "[env] attacher JDK = $attacherJdk"
144
+ Write-Host "[env] accountId = $accountId / tenantId = $tenantId"
145
+
146
+ # ---- 4. Optional: build first ----
147
+ if ($Module) {
148
+ $gradleHome = Join-Path $cwd '.gradle\user-home'
149
+
150
+ # 4.1 Gradle version must follow the wrapper, never "newest dist" -- a newer
151
+ # dist (e.g. 8.x) may demand a newer Java runtime and derail this JDK8 chain.
152
+ $wrapperProps = Join-Path $cwd 'gradle\wrapper\gradle-wrapper.properties'
153
+ if (-not (Test-Path $wrapperProps)) { throw "Missing $wrapperProps." }
154
+ $m = [regex]::Match((Get-Content $wrapperProps -Raw), 'gradle-([0-9.]+)-bin')
155
+ if (-not $m.Success) { throw "Cannot parse the Gradle version from $wrapperProps." }
156
+ $distVer = $m.Groups[1].Value
157
+ $distRoot = Join-Path $env:USERPROFILE ".gradle\wrapper\dists\gradle-$distVer-bin"
158
+ $gradleBat = Get-ChildItem $distRoot -Recurse -Filter 'gradle.bat' -ErrorAction SilentlyContinue |
159
+ Select-Object -First 1
160
+ if (-not $gradleBat) {
161
+ throw "Gradle $distVer not found under $distRoot. Let the wrapper download it first (run .\gradlew.bat)."
162
+ }
163
+
164
+ # 4.2 Build JDK: same preference order as above, but must carry tools.jar.
165
+ $buildJdk = $attacherJdk
166
+ if ($WhatIf) { Write-Host '[whatif] dry run: skipping the actual build.' }
167
+
168
+ $gradleArgs = @()
169
+ if ($Clean) { $gradleArgs += (':' + $Module + ':clean') }
170
+ $gradleArgs += (':' + $Module + ':build')
171
+ $gradleArgs += @('-x', 'test', '--console=plain')
172
+ Write-Host "[build] gradle $distVer | $($gradleArgs -join ' ')"
173
+ Write-Host "[build] JAVA_HOME=$buildJdk (GRADLE_USER_HOME=$gradleHome)"
174
+ $env:GRADLE_USER_HOME = $gradleHome
175
+ $env:JAVA_HOME = $buildJdk
176
+ & $gradleBat.FullName @gradleArgs
177
+ if ($LASTEXITCODE -ne 0) { throw "Gradle build failed (exit $LASTEXITCODE) -- see the compile errors above." }
178
+ }
179
+
180
+ # ---- 5. Resolve jars (auto-locate the build output when -Module was given) ----
181
+ if ((-not $Jar -or $Jar.Count -eq 0) -and $Module) {
182
+ $settings = Get-Content (Join-Path $cwd 'settings.gradle') -Raw
183
+ $mm = [regex]::Match($settings,
184
+ "project\(':$([regex]::Escape($Module))'\)\.projectDir\s*=\s*new File\('([^']+)'\)")
185
+ $moduleDir = if ($mm.Success) { Join-Path $cwd ($mm.Groups[1].Value -replace '/', '\') }
186
+ else { Join-Path $cwd "code\$Module" }
187
+ $libsDir = Join-Path $moduleDir 'build\libs'
188
+ $Jar = @(Get-ChildItem $libsDir -Filter '*.jar' -ErrorAction SilentlyContinue |
189
+ Where-Object { $_.Name -notlike '*sources*' -and $_.Name -notlike '*javadoc*' } |
190
+ Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
191
+ ForEach-Object { $_.FullName })
192
+ if (-not $Jar) { throw "No build output jar in $libsDir." }
193
+ }
194
+ if (-not $Jar -or $Jar.Count -eq 0) {
195
+ Write-Host '[done] Health check only -- no hot-deploy (pass -Jar or -Module).'
196
+ exit 0
197
+ }
198
+ $jars = $Jar | ForEach-Object { (Resolve-Path $_).Path }
199
+ foreach ($j in $jars) {
200
+ Write-Host "[jar] $j ($([math]::Round((Get-Item $j).Length / 1KB)) KB)"
201
+ }
202
+
203
+ # ---- 6. Attach: save the jar bytes, then reload the ExtClassLoader ----
204
+ $attArgs = @(
205
+ '-cp', "$toolsJar;$agent",
206
+ 'kd.reloadjar.Attacher',
207
+ $targetPid, $agent,
208
+ ($jars -join ','),
209
+ $accountId, $tenantId
210
+ )
211
+ if ($LogFile) {
212
+ $dir = Split-Path $LogFile -Parent
213
+ if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
214
+ $attArgs += (Join-Path (Resolve-Path $dir).Path (Split-Path $LogFile -Leaf))
215
+ }
216
+
217
+ & $java @attArgs
218
+ if ($LASTEXITCODE -ne 0) { throw "Hot-deploy failed (exit $LASTEXITCODE)." }
219
+ Write-Host '[done] Hot-deploy complete (SUCCESS = save + reset both applied).'
@@ -139,13 +139,25 @@ api fileTree(trd) {
139
139
  `<cwd>/.dsh/cosmic/config/dev.json`:
140
140
 
141
141
  ```json
142
- { "java_home": "C:\\Program Files\\java\\jdk" }
142
+ { "java_home": "C:\\Program Files\\Java\\jdk1.8.0_281" }
143
143
  ```
144
144
 
145
- - 以 IDEA 项目 SDK 为准(本项目 = `C:\Program Files\java\jdk`,JDK 8,含 tools.jar)
146
- - Attacher 需与 cosmic 进程同 JDK **大版本**(JDK 8,具体小版本无关)
145
+ - 以 IDEA 项目 SDK 为准(实测本机 = `C:\Program Files\Java\jdk1.8.0_281`,JDK 8,含 tools.jar)
146
+ - Attacher 需与 cosmic 进程同 JDK **大版本**(JDK 8,具体小版本无关;cosmic 自带
147
+ `F:\cosmic\xinghan8\jdk` = 1.8.0_212,与本机 1.8.0_281 混用无碍)
147
148
  - `dev.py init` 自动发现不到时手动补;改后 `dev.py read` 验证
148
149
 
150
+ > ⚠️ **必设 `JAVA_HOME`,否则 `compileJava` 必失败**(实测踩坑):本机 Windows 注册表
151
+ > `HKLM\SOFTWARE\JavaSoft\Java Development Kit` 的 `JavaHome` 被 8u481 安装包改写成
152
+ > **JRE** 路径 `C:\Program Files\Java\jre1.8.0_481`。Gradle 在该值存在时优先采信它,
153
+ > 于是 `java.home` 指向 JRE → 没有 `tools.jar` → 报
154
+ > `Could not find tools.jar. Please check that ...\jre1.8.0_481 contains a valid JDK installation`。
155
+ > 显式 `$env:JAVA_HOME="C:\Program Files\Java\jdk1.8.0_281"` 即解(实测全量编译 10s 通过)。
156
+ >
157
+ > 🔎 **同源陷阱:「伪成功构建」**。若 `compileJava` 处于 `UP-TO-DATE`,Gradle 根本不加载
158
+ > 编译器,缺 tools.jar 也不报错 —— 你会看到 `BUILD SUCCESSFUL` + `jar` 正常产出,
159
+ > **但 jar 里是旧字节码**。首次接入务必用 `clean build` 验证一次真编译(本次就是这么暴露的)。
160
+
149
161
  ### 4.4 验证 classpath 是否真的干净(临时 init script)
150
162
 
151
163
  `showcp.gradle`(用完可删):
@@ -205,11 +217,19 @@ Get-ChildItem "<lib>" -Recurse -Filter *.jar | ForEach-Object {
205
217
  > 见 §0),须先单命令级放开 `danger-full-access`;`--no-daemon` 也救不了。
206
218
 
207
219
  ```powershell
208
- $env:JAVA_HOME = "C:\Program Files\java\jdk"
220
+ $env:JAVA_HOME = "C:\Program Files\Java\jdk1.8.0_281" # 必须:注册表 java.home 指向 JRE(见 §4.3)
209
221
  $gradle = "C:\Users\<user>\.gradle\wrapper\dists\gradle-7.6.3-bin\<hash>\gradle-7.6.3\bin\gradle.bat"
210
222
  & $gradle ":<模块>:build" -x test --console=plain
211
223
  ```
212
224
 
225
+ > ⚠️ **Gradle 版本必须按 `gradle/wrapper/gradle-wrapper.properties` 锁定**,不要「取
226
+ > `~/.gradle/wrapper/dists` 下最新」。本机同时存在 `gradle-8.8-bin`,它要求 Java 22
227
+ > 运行时,与本项目 JDK 8 + tools.jar 链路不兼容,会直接把构建带偏。
228
+ >
229
+ > 🧩 本工作区已把整套流程封装为脚本:`runtime/cosmic-hotdeploy.ps1`
230
+ > (体检 / `-Jar` 热加载 / `-Module` 构建+热加载 / `-Clean`),内置上述 JDK、Gradle
231
+ > 版本、PID 发现、account/tenant 读取逻辑,可直接用。
232
+
213
233
  > ❌ 不要加 `--no-daemon`:每次 fork 单次 Daemon(JVM 冷启动 + 重新加载配置 ≈ +30s)
214
234
  > ✅ 复用常驻 Daemon(`org.gradle.daemon=true` 默认开启),改几行代码约 5-10 秒
215
235
 
@@ -314,6 +334,11 @@ for incremental compilation. See the debug log for more details.
314
334
  ### 6.1 发现 cosmic PID
315
335
 
316
336
  ```powershell
337
+ # ⭐ 方式零(推荐):按端口归属进程反查 + 命令行校验(实测最可靠)
338
+ $conn = Get-NetTCPConnection -LocalPort <cosmic_port> -State Listen | Select-Object -First 1
339
+ $cosmicPID = $conn.OwningProcess
340
+ (Get-CimInstance Win32_Process -Filter "ProcessId=$cosmicPID").CommandLine # 应含 kd.bos.service.webserver.JettyServer
341
+
317
342
  # 方式一:HTTP 探测端口(最可靠,权限问题兜底)
318
343
  Invoke-WebRequest http://127.0.0.1:<cosmic_port>/ierp # HTTP 200 = cosmic 活着
319
344
 
@@ -355,6 +380,15 @@ $jar = "<模块>\build\libs\<模块>-1.0.0.jar"
355
380
  多次构建间隔短时用「最新时间戳 ±30s」窗口精确切分,避免混入前一次全量产物
356
381
  8. **`dependencies --configuration compileClasspath` 对 fileTree 是惰性展开**,不一定显示 jar 明细,
357
382
  要确认真实 classpath 必须用自定义任务打印 `sourceSets.main.compileClasspath.files`
383
+ 9. **`jps` 看不到 cosmic 进程**(实测):本机 cosmic 由 **SYSTEM** 账户启动
384
+ (`F:\cosmic\xinghan8\jdk\bin\java ... -DappName=cosmic ... kd.bos.service.webserver.JettyServer`),
385
+ 而 `jps` 只列举当前用户的 JVM → 输出里完全没有它,容易误判「cosmic 没启动」。
386
+ 用 §6.1 方式零(按端口反查 OwningProcess)替代。
387
+ 10. **跨用户 attach 是可用的,无需提权**:cosmic 跑在 SYSTEM 下、Attacher 由
388
+ `lihaisheng\administrator` 启动,实测 attach 成功(Windows JVM attach 命名管道对
389
+ Administrators 组放行),不必 `Start-Process -Verb RunAs`。
390
+ 11. **`-XX:+DisableAttachMechanism` 是硬阻断**:本机 IDEA 侧另一个 JVM(`CosmicStudio-runnable.jar`)
391
+ 带此参数,对它 attach 必然失败;发现 PID 后先扫命令行里有没有这个 flag。
358
392
  9. **`java_home` 以 IDEA 项目 SDK 为准**(Windows 常见 `C:\Program Files\java\jdk`),
359
393
  不要误填 cosmic 自带 JDK 目录;Attacher 只需与 cosmic 同 JDK 大版本(同为 JDK8 即兼容)
360
394
  10. **gradle 构建命令不要加 `--no-daemon`**;多 IDLE Daemon 并存无害,`--stop` 可一次性清理