@shundoo-ai/dsh-cosmic 1.0.1 → 1.0.2-alpha.1

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.1",
3
+ "version": "1.0.2-alpha.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  ".config",
package/run.py CHANGED
@@ -9,9 +9,9 @@
9
9
 
10
10
  用法(子进程执行模式,但去掉 heredoc/临时文件层):
11
11
  macOS/Linux:
12
- ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py runs/query.py
12
+ ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py runs/query.py
13
13
  Windows PowerShell:
14
- $env:ANALYSIS_ID="<id>"; {PRESET}/.venv/Scripts/python.exe {PRESET}/run.py runs/query.py
14
+ $env:ANALYSIS_ID="<id>"; {VENV}/Scripts/python.exe {PRESET}/run.py runs/query.py
15
15
 
16
16
  目标脚本内直接读环境变量:
17
17
  PRESET = os.environ["PRESET_DIR"]
@@ -52,7 +52,7 @@ def main():
52
52
  env["OUT_DIR"] = out_dir
53
53
  env["ANALYSIS_ID"] = analysis_id
54
54
 
55
- # 子进程复用当前解释器(= .venv 里的 python,跨平台恒正确)
55
+ # 子进程复用当前解释器(= venv 里的 python,跨平台恒正确)
56
56
  return subprocess.call([sys.executable] + sys.argv[1:], env=env)
57
57
 
58
58
 
@@ -24,7 +24,7 @@ preset(标准,随分发,不动)
24
24
  agent 查数据时遇到企业扩展的情况(字段缺失 / 对象找不到),按需下载该实体到工作区:
25
25
 
26
26
  ```bash
27
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --entity <实体标识>
27
+ {VENV}/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --entity <实体标识>
28
28
  ```
29
29
 
30
30
  输出到工作区 `.dsh/cosmic/erpdb/scheme/<实体标识>/<实体标识>.entity.md`。
@@ -34,7 +34,7 @@ agent 查数据时遇到企业扩展的情况(字段缺失 / 对象找不到
34
34
  > 切换 ERP 环境、或首次绑定某企业且确认有大范围扩展时,全量刷新:
35
35
 
36
36
  ```bash
37
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --force
37
+ {VENV}/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --force
38
38
  ```
39
39
 
40
40
  - 读 preset `entity-map.md` 全部实体标识,输出到工作区 `.dsh/cosmic/erpdb/scheme/`;
@@ -48,13 +48,13 @@ agent 查数据时遇到企业扩展的情况(字段缺失 / 对象找不到
48
48
  3. **对象找不到**:entity-map(preset→工作区)grep 不到实体标识 → 二开/新增对象。**不要反复试关键词**,直接 queryForms 搜:
49
49
 
50
50
  ```bash
51
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-dev/skills/cosmic-meta-api/scripts/cosmic_api.py queryForms --keyword <关键词>
51
+ {VENV}/bin/python {PRESET}/skills/cosmic-dev/skills/cosmic-meta-api/scripts/cosmic_api.py queryForms --keyword <关键词>
52
52
  ```
53
53
 
54
54
  从结果取目标表单的 `formNumber`(表单编码 = 实体标识)。**若命中多个同名/相似业务对象(如多个「报关单」分属不同应用/业务云),不要自己猜测,列出候选(表单名称 + 实体标识 + 归属)询问用户选哪个**;用户选定后下载:
55
55
 
56
56
  ```bash
57
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --entity <formNumber>
57
+ {VENV}/bin/python {PRESET}/skills/cosmic-dev/scripts/entity-download.py --entity <formNumber>
58
58
  ```
59
59
 
60
60
  下载成功后工作区 entity-map 自动追加该对象(中文名从 entity.md 的 `**DisplayName:**` 读,归属应用/业务云由脚本内部经 `queryForms --keyword <formNumber>` 反查 `appName`/`cloudName` 自动填充,无需手动传参)。
@@ -26,18 +26,19 @@ description: >
26
26
  ## 触发后的第一步:读环境配置
27
27
 
28
28
  > 路径约定(与 erp-analysis 一致):
29
- > - preset 根 `{PRESET}`:preset 顶层目录,含 `.venv/`、`run.py`、`skills/`。定位:`$DSH_HOME/.agent-presets/cosmic`(`$DSH_HOME` DSH 注入的环境变量,不写死用户路径);权威定义见 `run.py` 里 `PRESET = os.path.dirname(os.path.abspath(__file__))`。
29
+ > - preset 根 `{PRESET}` = `$DSH_PROFILE_DIR/node_modules/@shundoo-ai/dsh-cosmic`(preset 包目录,含 `run.py`、`.config/`、`skills/`;`$DSH_PROFILE_DIR` dsh 注入每次 shell)。权威定义见 `run.py` 里 `PRESET = os.path.dirname(os.path.abspath(__file__))`——等价于本 SKILL.md 目录的上两级。
30
30
  > - 本 skill 目录 `{SKILL_DIR}` = `{PRESET}/skills/cosmic-build/skills/cosmic-vibe-coding`
31
- > - Python = `{PRESET}/.venv/bin/python`(macOS/Linux)/ `{PRESET}/.venv/Scripts/python.exe`(Windows)
31
+ > - Python 运行时 `{VENV}` = `$DSH_HOME/cache/cosmic/venv`(机器级,与 preset 版本解耦):
32
+ > `{VENV}/bin/python`(macOS/Linux)/ `{VENV}/Scripts/python.exe`(Windows)
32
33
 
33
34
  无论走哪个职责,agent 先执行:
34
35
 
35
36
  ```bash
36
- {PRESET}/.venv/bin/python {SKILL_DIR}/scripts/dev.py read
37
+ {VENV}/bin/python {SKILL_DIR}/scripts/dev.py read
37
38
  ```
38
39
 
39
40
  - 输出的 JSON 里 `erp_url` / `account_id` / `java_home` 等**关键字段非空** → 配置就绪,直接走职责②③。
40
- - 输出 `{}`(未初始化)→ 引导用户跑 `{PRESET}/.venv/bin/python {SKILL_DIR}/scripts/dev.py init` 初始化(读 `.kd/config.json` 覆盖 dev.json),即职责①。
41
+ - 输出 `{}`(未初始化)→ 引导用户跑 `{VENV}/bin/python {SKILL_DIR}/scripts/dev.py init` 初始化(读 `.kd/config.json` 覆盖 dev.json),即职责①。
41
42
 
42
43
  ---
43
44
 
@@ -63,8 +64,8 @@ description: >
63
64
  2. **用 `scripts/dev.py` 生成环境配置**(从 `.kd/config.json` 读,不手工输入):
64
65
 
65
66
  ```bash
66
- {PRESET}/.venv/bin/python {SKILL_DIR}/scripts/dev.py init # 读 .kd/config.json 的 url → 覆盖 dev.json
67
- {PRESET}/.venv/bin/python {SKILL_DIR}/scripts/dev.py read # 日常只读 dev.json
67
+ {VENV}/bin/python {SKILL_DIR}/scripts/dev.py init # 读 .kd/config.json 的 url → 覆盖 dev.json
68
+ {VENV}/bin/python {SKILL_DIR}/scripts/dev.py read # 日常只读 dev.json
68
69
  ```
69
70
 
70
71
  `dev.py init` 读 `<cwd>/.dsh/cosmic/.kd/config.json`(default env,复用 kd_env_loader,与 erp-analysis 同源),取 `url` 自动解析端口;**accountId 配置有值直接用,为空则调 `getAllDatacenters.do` 自动填**(优先 `default:true` 的数据中心,否则取第一个;失败保持空),**tenantId 配置有值用配置、为空默认 `ierp`**、`java_home` 从 IDEA 项目 SDK 自动发现,并**覆盖** `<cwd>/.dsh/cosmic/config/dev.json`。**若 url 为空,脚本中断并提醒「设置 erp 登录信息」。**
@@ -13,10 +13,10 @@
13
13
  无参打印帮助。
14
14
 
15
15
  用法:
16
- {PRESET}/.venv/bin/python dev.py init # 初始化:读 .kd/config.json → 覆盖 dev.json
17
- {PRESET}/.venv/bin/python dev.py read # 只读 dev.json
18
- {PRESET}/.venv/bin/python dev.py --read # 只读 dev.json(兼容旧用法)
19
- {PRESET}/.venv/bin/python dev.py --config <path> init|read # 指定配置文件路径
16
+ {VENV}/bin/python dev.py init # 初始化:读 .kd/config.json → 覆盖 dev.json
17
+ {VENV}/bin/python dev.py read # 只读 dev.json
18
+ {VENV}/bin/python dev.py --read # 只读 dev.json(兼容旧用法)
19
+ {VENV}/bin/python dev.py --config <path> init|read # 指定配置文件路径
20
20
  """
21
21
 
22
22
  import argparse
@@ -122,7 +122,7 @@ JH=<cosmic安装目录>/jdk_mac_x86
122
122
  **重新热部署 `sd-yun-wrzs`**(重启后执行):
123
123
 
124
124
  ```bash
125
- SKILL_DIR=${DSH_HOME:-$HOME/.dsh}/.agent-presets/cosmic/skills/cosmic-build/skills/cosmic-vibe-coding
125
+ SKILL_DIR=$DSH_PROFILE_DIR/node_modules/@shundoo-ai/dsh-cosmic/skills/cosmic-build/skills/cosmic-vibe-coding
126
126
  JH=<cosmic安装目录>/jdk_mac_x86
127
127
  PID=$(lsof -tiTCP:8080 -sTCP:LISTEN | head -1)
128
128
  "$JH/bin/java" -cp "$JH/lib/tools.jar:$SKILL_DIR/assets/kd-reloadjar-agent.jar" kd.reloadjar.Attacher \
@@ -11,7 +11,7 @@
11
11
 
12
12
  ```bash
13
13
  {PYTHON} {cosmic_api} <命令> [参数]
14
- # {PYTHON} = {PRESET}/.venv/bin/python(macOS/Linux);.venv/Scripts/python.exe(Windows)
14
+ # {PYTHON} = {VENV}/bin/python(macOS/Linux);{VENV}/Scripts/python.exe(Windows)
15
15
  # {cosmic_api} = {PRESET}/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py
16
16
  ```
17
17
 
@@ -43,7 +43,7 @@ erp-analysis/
43
43
  │ ├── report_builder_markdown.py ← Markdown 报告(Jinja2)
44
44
  │ ├── report_analysis_id.py ← 分析 ID 生成
45
45
  │ ├── send_email.py ← 邮件发送(smtplib + MIME)
46
- │ ├── init-python.sh / init-python.ps1 ← Python 运行时初始化(uv + .venv,macOS/Linux / Windows
46
+ │ ├── init-python.sh / init-python.ps1 ← Python 运行时初始化(uv;建到 $DSH_HOME/cache/cosmic/venv
47
47
  │ └── requirements.txt ← 依赖清单(含 requests:传输层 gzip 协商必需,缺失时大查询慢 7 倍+)
48
48
  └── templates/
49
49
  ├── report_template.j2 ← HTML 模板
@@ -54,7 +54,8 @@ erp-analysis/
54
54
 
55
55
  路径分三层,别混:
56
56
 
57
- - **preset 根 `{PRESET}`**:preset 顶层目录,含 `.venv/`、`run.py`、`.config/`、`skills/`。`.venv`、`run.py`、`.config/` 都在这一层(**不在 skill 目录、不在工作目录**);不确定时 read `run.py` 确认——`PRESET = os.path.dirname(os.path.abspath(__file__))` 即权威定义。
57
+ - **preset 根 `{PRESET}`** = `$DSH_PROFILE_DIR/node_modules/@shundoo-ai/dsh-cosmic`(preset 包目录,含 `run.py`、`.config/`、`skills/`;`$DSH_PROFILE_DIR` dsh 注入每次 shell)。**权威定义**:read `{PRESET}/run.py` 里的 `PRESET = os.path.dirname(os.path.abspath(__file__))`——等价于本 SKILL.md 目录(`{PRESET}/skills/erp-analysis/`)的上两级。
58
+ - **Python 运行时 `{VENV}`**:`$DSH_HOME/cache/cosmic/venv` —— **机器级**、与 preset 版本解耦(升级 preset 不重建);**不在 preset 根、不在工作目录**。`$DSH_HOME` 由 dsh 注入每次 shell,缺省 `~/.dsh`。
58
59
  - **skill 目录**:`{PRESET}/skills/erp-analysis/`(本 SKILL.md 所在,含 `references/`、`scripts/`)。
59
60
  - **工作目录 cwd**:独立的产物目录(产物落 `runs/`、`runtime/`、`config/`)。
60
61
 
@@ -70,13 +71,13 @@ erp-analysis/
70
71
 
71
72
  ```bash
72
73
  # macOS/Linux
73
- ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py runs/<session>/<analysis_id>/xxx.py
74
+ ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py runs/<session>/<analysis_id>/xxx.py
74
75
 
75
76
  # Windows PowerShell
76
- $env:ANALYSIS_ID="<id>"; {PRESET}/.venv/Scripts/python.exe {PRESET}/run.py runs/<session>/<analysis_id>/xxx.py
77
+ $env:ANALYSIS_ID="<id>"; {VENV}/Scripts/python.exe {PRESET}/run.py runs/<session>/<analysis_id>/xxx.py
77
78
  ```
78
79
 
79
- `ANALYSIS_ID` 由 agent 分析开始时生成一次(`{PRESET}/.venv/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())"`),一次分析的所有脚本共用一个值。
80
+ `ANALYSIS_ID` 由 agent 分析开始时生成一次(`{VENV}/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())"`),一次分析的所有脚本共用一个值。
80
81
 
81
82
  ## 标准脚本头(agent 写脚本必用,照抄)
82
83
 
@@ -98,9 +99,9 @@ from _core.commands import do_auth
98
99
 
99
100
  第一动作:`read` `{PRESET}/skills/erp-analysis/references/index.md`,严格按其流程执行。
100
101
 
101
- > **entity-map.md**:先调 `{PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --map-paths` 拿两个绝对路径(preset 标准实体 + 工作区企业新增对象),再依次 `grep`:先 grep preset(命中率高)、无果再 grep 工作区。**禁止 `read` 全文加载**(580KB 炸 token)。工作区 entity-map 已由 `erp-config.py` 自动初始化(空表头),grep 无匹配 = 无企业新增对象。
102
+ > **entity-map.md**:先调 `{VENV}/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --map-paths` 拿两个绝对路径(preset 标准实体 + 工作区企业新增对象),再依次 `grep`:先 grep preset(命中率高)、无果再 grep 工作区。**禁止 `read` 全文加载**(580KB 炸 token)。工作区 entity-map 已由 `erp-config.py` 自动初始化(空表头),grep 无匹配 = 无企业新增对象。
102
103
  >
103
- > **scheme 元数据**:先调 `{PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --path {entity}` 拿实体 entity.md 的绝对路径(内部已处理「工作区企业扩展优先、preset 标准兜底」),再 `read` 该路径。字段引用用 **Name 列**(字段标识)。⚠️ **模板 skill 已写死字段名时,跳过 entity.md 全文 read**(直接信 skill 写脚本,详见 index.md Step 4)。
104
+ > **scheme 元数据**:先调 `{VENV}/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --path {entity}` 拿实体 entity.md 的绝对路径(内部已处理「工作区企业扩展优先、preset 标准兜底」),再 `read` 该路径。字段引用用 **Name 列**(字段标识)。⚠️ **模板 skill 已写死字段名时,跳过 entity.md 全文 read**(直接信 skill 写脚本,详见 index.md Step 4)。
104
105
  >
105
106
  > **API 调用**:统一走 `_core.config` 的 `_api_call()`(见标准脚本头)。已注册 endpoint:`dataQuery`、`dataQueryWithPagination`。调用代码见 `references/api-reference.md` §五,执行策略见 `references/api-execution.md`。
106
107
 
@@ -145,7 +146,7 @@ DSH 会话界面只有 `write`/`edit` 工具**本轮**触碰过的文件才会
145
146
 
146
147
  ## 运行时与凭证
147
148
 
148
- - Python:`{PRESET}/.venv/`(preset 根,uv 创建,多工作区共享)。首次使用跑 init-python:macOS/Linux 用 `bash {PRESET}/skills/erp-analysis/scripts/init-python.sh`,Windows 用 `pwsh {PRESET}/skills/erp-analysis/scripts/init-python.ps1`。
149
+ - Python:`{VENV}/`(preset 根,uv 创建,多工作区共享)。首次使用跑 init-python:macOS/Linux 用 `bash {PRESET}/skills/erp-analysis/scripts/init-python.sh`,Windows 用 `pwsh {PRESET}/skills/erp-analysis/scripts/init-python.ps1`。
149
150
  - token 缓存 / openapi 元数据缓存:由 `_core.config` 自动解析到 `RUNTIME_DIR`(有 app 会话时为 `<cwd>/app/<appCode>/_runtime/cosmic-metadata/`,无则为 `<cwd>/app/_runtime/cosmic-metadata/`),无需手动指定;token 缓存默认 25min,到期自动重新登录。
150
151
  - 凭证(per-workspace):`_core` 优先读工作区 `.dsh/cosmic/.kd/config.json`(真实凭证,不进 git),缺失时从 preset 模板 `cosmic-build/skills/config.json` 自动拷贝。
151
152
 
@@ -155,9 +156,9 @@ DSH 会话界面只有 `write`/`edit` 工具**本轮**触碰过的文件才会
155
156
 
156
157
  **主流程(选项目 → 自动填连接信息 → 只补账号密码):**
157
158
 
158
- 1. 列项目:`{PRESET}/.venv/bin/python {PRESET}/skills/erp-analysis/scripts/erp-config.py list`,把输出的 markdown 表格转述给用户选(含项目标识 + 名称 + 生产/测试/自定义)。
159
+ 1. 列项目:`{VENV}/bin/python {PRESET}/skills/erp-analysis/scripts/erp-config.py list`,把输出的 markdown 表格转述给用户选(含项目标识 + 名称 + 生产/测试/自定义)。
159
160
  2. 用户选项目 → `erp-config.py apply --id <项目标识>`:从 preset `cosmic-build/skills/tenant.json` 取该项目的 url/tenantId/accountId/client_id/client_secret/x-acgw-identity 写入,**并清空 user/password**(切换项目 = 换身份,旧账号无意义)。
160
- 3. 读当前配置确认:`{PRESET}/.venv/bin/python {PRESET}/skills/erp-analysis/scripts/erp-config.py read`(首次会自动从 preset 空模板生成;password/appSecret 已脱敏),把 read 结果列给用户(已设置显示当前值、未设置标「未设置」)——此时连接字段应为所选项目的值,user/password 为「未设置」。
161
+ 3. 读当前配置确认:`{VENV}/bin/python {PRESET}/skills/erp-analysis/scripts/erp-config.py read`(首次会自动从 preset 空模板生成;password/appSecret 已脱敏),把 read 结果列给用户(已设置显示当前值、未设置标「未设置」)——此时连接字段应为所选项目的值,user/password 为「未设置」。
161
162
  4. 用户只补账号密码 → `erp-config.py write --user <账号> --password <密码>`。
162
163
  5. 验证登录(`do_auth`),成功后并列执行两个动作:
163
164
  - `{PRESET}/skills/erp-analysis/scripts/pull_project_templates.py` — 拉取项目模板(按 baseUrl 反查项目,见 `references/template-distribution.md`)
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 前置条件
6
6
 
7
- - Python 运行时已初始化(`{PRESET}/.venv/`,跑 init-python 创建)
7
+ - Python 运行时已初始化(`{VENV}/`,跑 init-python 创建)
8
8
  - SMTP 配置优先读工作区 `.dsh/cosmic/config/email-smtp.toml`(由 `pull_project_templates.py` 从 tenant 仓库拉取项目企业邮箱),缺失回退 preset `.config/email-smtp.toml` 兜底
9
9
 
10
10
  ## 邮件发送铁律(一律 background,四步不可跳)
@@ -20,17 +20,17 @@
20
20
 
21
21
  **Step 1 · Phase 2 结束后立即启动 background 进程**
22
22
 
23
- > ⚠️ **脚本路径硬性规定**:邮件脚本路径**唯一为** `{PRESET}/skills/erp-analysis/scripts/send_email.py`。用 `bash` 工具,设 `run_in_background: true`。send_email.py 不依赖 `ANALYSIS_ID`,直接跑 `.venv` 里的解释器即可,无需走 `run.py`。
23
+ > ⚠️ **脚本路径硬性规定**:邮件脚本路径**唯一为** `{PRESET}/skills/erp-analysis/scripts/send_email.py`。用 `bash` 工具,设 `run_in_background: true`。send_email.py 不依赖 `ANALYSIS_ID`,直接跑 `{VENV}` 里的解释器即可,无需走 `run.py`。
24
24
 
25
25
  ```text
26
26
  bash 工具调用:
27
- command: '{PRESET}/.venv/bin/python {PRESET}/skills/erp-analysis/scripts/send_email.py --to "x@y.com" --subject "..." --body "..." --attachment "/path/report.html"'
27
+ command: '{VENV}/bin/python {PRESET}/skills/erp-analysis/scripts/send_email.py --to "x@y.com" --subject "..." --body "..." --attachment "/path/report.html"'
28
28
  run_in_background: true
29
29
  description: "后台发送分析报告邮件"
30
30
  # 立即拿到 job id,继续下一步(Phase 3 / Phase 4 / 输出 chat 等)
31
31
  ```
32
32
 
33
- > 📌 **background 常见错误**:若报 `ModuleNotFoundError`(缺标准库外依赖,本脚本无此类依赖),或 `command not found`,检查 `.venv` 是否初始化、路径引号是否闭合。
33
+ > 📌 **background 常见错误**:若报 `ModuleNotFoundError`(缺标准库外依赖,本脚本无此类依赖),或 `command not found`,检查 `{VENV}` 是否初始化、路径引号是否闭合。
34
34
 
35
35
  **Step 2 · 继续跑其他任务**(Phase 3 PNG、Phase 4 chat、输出 chat 报告等)
36
36
 
@@ -86,7 +86,7 @@ display_name = "发件人显示名"
86
86
 
87
87
  ### 单收件人单附件(最常用)
88
88
  ```text
89
- {PRESET}/.venv/bin/python {PRESET}/skills/erp-analysis/scripts/send_email.py \
89
+ {VENV}/bin/python {PRESET}/skills/erp-analysis/scripts/send_email.py \
90
90
  --to "user@example.com" \
91
91
  --subject "分析报告标题" \
92
92
  --body "您好,附件为交互式 HTML 报告,下载后用浏览器打开查看完整图表。" \
@@ -114,7 +114,7 @@ display_name = "发件人显示名"
114
114
  |---|---|---|
115
115
  | 0 | ✅ 成功 | 邮件已发送 |
116
116
  | 2 | 参数错误(最常见:位置参数代替命名参数) | 必须用命名参数 `--to` `--subject` `--body` `--attachment` |
117
- | 126/127 | 命令未找到 | 检查 `.venv` 是否初始化、路径引号闭合 |
117
+ | 126/127 | 命令未找到 | 检查 `{VENV}` 是否初始化、路径引号闭合 |
118
118
  | 其他 | 认证/网络/TLS/附件超限 | 检查网络、SMTP 配置、附件大小(建议 ≤ 10MB) |
119
119
 
120
120
  > - `--to` 多人用逗号分隔:`--to "a@b.com,c@d.com"`
@@ -120,7 +120,7 @@ for title in titles:
120
120
 
121
121
  ```bash
122
122
  # write 脚本 → runs/<session_key>/<analysis_id>/chart.py
123
- ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/chart.py
123
+ ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/chart.py
124
124
  ```
125
125
 
126
126
  ```python
@@ -173,7 +173,7 @@ print("bundle OK")
173
173
 
174
174
  ```bash
175
175
  # write 脚本 → runs/<session_key>/<analysis_id>/report.py
176
- ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/report.py
176
+ ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/report.py
177
177
  ```
178
178
 
179
179
  ```python
@@ -6,11 +6,11 @@
6
6
 
7
7
  1. **标准脚本头**:见 SKILL.md(读 run.py 注入的 `PRESET_DIR`/`OUT_DIR` + sys.path),agent 写脚本时照抄。
8
8
 
9
- 2. **analysis_id**:agent 在分析开始时生成**一次**(`{PRESET}/.venv/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())"`),硬编码进该分析的所有脚本。**禁止每个脚本各自调 `gen_analysis_id()`**(会产生不同 ID,产物散落)。
9
+ 2. **analysis_id**:agent 在分析开始时生成**一次**(`{VENV}/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())"`),硬编码进该分析的所有脚本。**禁止每个脚本各自调 `gen_analysis_id()`**(会产生不同 ID,产物散落)。
10
10
 
11
11
  3. **产物目录约定**:所有脚本、数据缓存、图表、报告都落 `runs/<session_key>/<analysis_id>/`(session_key = `DSH_SESSION_ID`,analysis_id = 上面生成)。
12
12
 
13
- 4. **Python 执行**:统一走 `{PRESET}/run.py`(`ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py <脚本>`)。首次使用先跑 init-python(macOS/Linux 用 `init-python.sh`,Windows 用 `init-python.ps1`)初始化运行时。
13
+ 4. **Python 执行**:统一走 `{PRESET}/run.py`(`ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py <脚本>`)。首次使用先跑 init-python(macOS/Linux 用 `init-python.sh`,Windows 用 `init-python.ps1`)初始化运行时。
14
14
 
15
15
  ## 流程步骤
16
16
 
@@ -27,7 +27,7 @@
27
27
  先调 `entity-download.py --map-paths` 拿两个 entity-map 绝对路径,再依次 `grep`(先 preset 标准实体、无果再 workspace 企业新增对象):
28
28
 
29
29
  ```bash
30
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --map-paths
30
+ {VENV}/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --map-paths
31
31
  ```
32
32
 
33
33
  ```
@@ -44,13 +44,13 @@ grep pattern="过磅" path="<workspace 路径>"
44
44
  **grep 两处都无果 = 二开/新增对象**。**不要反复试关键词**,直接 queryForms 搜:
45
45
 
46
46
  ```bash
47
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py queryForms --keyword <关键词>
47
+ {VENV}/bin/python {PRESET}/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py queryForms --keyword <关键词>
48
48
  ```
49
49
 
50
50
  从结果取目标表单的 `formNumber`(表单编码,即实体标识)。**若命中多个同名/相似业务对象(如多个「报关单」分属不同应用/业务云),不要自己猜测,列出候选(表单名称 + 实体标识 + 归属)询问用户选哪个**;用户选定后下载:
51
51
 
52
52
  ```bash
53
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --entity <formNumber>
53
+ {VENV}/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --entity <formNumber>
54
54
  ```
55
55
 
56
56
  (下载成功后工作区 entity-map 自动追加该对象,归属应用/业务云由脚本内部经 queryForms 反查自动填充)
@@ -66,7 +66,7 @@ grep pattern="过磅" path="<workspace 路径>"
66
66
  先调 `entity-download.py --path` 拿实体 entity.md 的绝对路径(内部已处理「工作区企业扩展优先、preset 标准兜底」)。**字段定位默认 `read` 全文**:
67
67
 
68
68
  ```bash
69
- {PRESET}/.venv/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --path {entity}
69
+ {VENV}/bin/python {PRESET}/skills/cosmic-build/scripts/entity-download.py --path {entity}
70
70
  # 输出绝对路径 → 按下方字段定位方式读取
71
71
  ```
72
72
 
@@ -161,7 +161,7 @@ df = pd.DataFrame(result['data']['rows'])
161
161
 
162
162
  plotly 制图,详见 `references/chart-guide.md`。图表用 `fig.to_html()` 生成交互式 HTML(**不用 kaleido / write_image / PNG**)。
163
163
 
164
- **执行方式**:`write` 脚本到 `runs/<session_key>/<analysis_id>/xxx.py`(如 `query.py`),再 `ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/xxx.py`。取数与制图可在同一脚本内完成,跨脚本共享数据用 `runs/<session_key>/<analysis_id>/` 下的 JSON / pickle 文件。
164
+ **执行方式**:`write` 脚本到 `runs/<session_key>/<analysis_id>/xxx.py`(如 `query.py`),再 `ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py runs/<session_key>/<analysis_id>/xxx.py`。取数与制图可在同一脚本内完成,跨脚本共享数据用 `runs/<session_key>/<analysis_id>/` 下的 JSON / pickle 文件。
165
165
 
166
166
  ### Step 9:出报告
167
167
 
@@ -7,6 +7,7 @@
7
7
  - **`id` 字段**:JSON 中返回字符串(长整型防精度丢失)
8
8
  - **日期/时间字段**(`biztime`、`createtime` 等):JSON 中返回 ISO 字符串(`"2026-05-15T10:30:00"`)
9
9
  - **数量字段**(`netwbqty1` 等 QtyProp):JSON 中返回 `float`(Decimal 序列化后)
10
+ - **dict 键名**:=`selectFields` 的**完整字段路径**(含单据体前缀,如 `billentry.qty`),**不是字段短名**——`pd.DataFrame(rows)` 后 rename 用错名字会静默变 NaN(详见 [§十二](#十二-dataframe-列名--selectfields-完整路径pddataframerows-后-rename-必须用全名))
10
11
 
11
12
  > ⚠️ **复选框字段**:API 返回 `true`/`false`(bool),**不是字符串**。比较用 `== True` / `~`,不用 `== '1'`。含 null 时用 `.fillna(False)`。
12
13
 
@@ -209,3 +210,22 @@ df['mat_name'] = df['mat_name'].mask(
209
210
 
210
211
  - **chat 表格** → `build_simple_query_tables()`(`report_builder_markdown.py`,纯 Python 拼 Markdown,零依赖,自动 TOP N 截断 + 超行提示)。简单查询的输出规范见 `SKILL.md`《报告格式》。
211
212
  - **调试/日志打印** → `df.to_string()`(pandas 内置,零依赖)。
213
+
214
+
215
+ ## 十二. DataFrame 列名 = selectFields 完整路径(`pd.DataFrame(rows)` 后 rename 必须用全名)
216
+
217
+ **现象**:取数字段有值,但 rename 后数值全为 `0.0`/`NaN`、分组列变空——**静默错误,不报异常**。典型:`df.columns = ["通知单号", "发货组织", "发货数量", ...]` 按顺序硬编码列名。
218
+
219
+ **原因**:响应每个 row 的 key 是 `selectFields` 的**完整字段名**(含单据体前缀,如 `billentry.qty`)——字段命名与响应格式的**唯一权威**是 `api-reference.md` §3.3 / §4.3,此处不重复。按顺序硬编码列名时,只要顺序或前缀对不上就会「错位对齐」:把不存在的键映射成 NaN,**不抛错**。
220
+
221
+ **强制写法**:显式 `COLMAP` + **先校验键齐全**,让错位在取数阶段显式失败:
222
+
223
+ ```python
224
+ df = pd.DataFrame(rows)
225
+ missing = [c for c in COLMAP if c not in df.columns] # ⭐ 关键:先校验
226
+ if missing:
227
+ raise RuntimeError(f"缺失字段: {missing};实际列: {list(df.columns)}")
228
+ df = df[list(COLMAP)].rename(columns=COLMAP)
229
+ ```
230
+
231
+ > 排查:数值列全 0 / 分组列全空 → 先 `print(list(rows[0].keys()))` 核对**真实键名**,再动 rename。**禁止**新增字段后不同步改 COLMAP(会导致错位)。
@@ -127,7 +127,7 @@ print(f"总超时次数: {len(timeouts)}")
127
127
 
128
128
  对耗时 >10s 但非超时的 assistant 消息,检查:
129
129
  - 是否为取数脚本 → 统计 `_api_call` 次数(取数 + 聚合 + 制图)
130
- - 是否为报告脚本 → `write` 脚本再 `ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py` 执行
130
+ - 是否为报告脚本 → `write` 脚本再 `ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py` 执行
131
131
  - 是否为 `edit` 修复 → 查出 bug 根因(从 old_string 和 new_string 对比)
132
132
  - 是否为邮件发送 → 确认成功(`job_output` 输出含 `✅ 邮件已发送至` 且 exit=0)
133
133
 
@@ -8,11 +8,11 @@
8
8
 
9
9
  ## 一. 基础设施
10
10
 
11
- - **执行方式**:`write` 脚本,再 `ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py <脚本>` 执行。
11
+ - **执行方式**:`write` 脚本,再 `ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py <脚本>` 执行。
12
12
  - **统一入口 run.py**:算好路径(`PRESET_DIR`/`OUT_DIR`/`ANALYSIS_ID`)注入环境变量,再以子进程跑目标脚本。
13
13
  - **会话/分析隔离**:`DSH_SESSION_ID` 由 DSH 注入;产物按 `runs/<session_key>/<analysis_id>/` 隔离(多会话、多分析互不干扰)。
14
14
  - **数据通道**:苍穹 API `_api_call()` 统一调用(数据取策略详见 `api-execution.md`)。
15
- - **运行时**:`.venv` preset 根(多工作区共享),首次使用跑 init-python(macOS/Linux 用 `init-python.sh`,Windows 用 `init-python.ps1`)。**依赖不用每次分析前验证**——脚本 import 失败报 `ModuleNotFoundError` 时再跑 init-python 补装即可;日常分析禁止预先跑 `import pandas/plotly/jinja2` 这类 ad-hoc 检查。⚠️ `requests` 是传输层必需依赖(gzip 协商,见 `api-execution.md`),init-python 的验证导入已包含它,缺失时 init 会失败提示。init-python 装依赖会写 `~/.cache/uv`,DSH sandbox 下报 `Operation not permitted` 属正常——需提权(`sandbox_permissions`)或在终端手动跑 init-python。
15
+ - **运行时**:`{VENV}`(= `$DSH_HOME/cache/cosmic/venv`,**机器级**、与 preset 版本解耦、多工作区共享),首次使用跑 init-python(macOS/Linux 用 `init-python.sh`,Windows 用 `init-python.ps1`)。**依赖不用每次分析前验证**——脚本 import 失败报 `ModuleNotFoundError` 时再跑 init-python 补装即可;日常分析禁止预先跑 `import pandas/plotly/jinja2` 这类 ad-hoc 检查。⚠️ `requests` 是传输层必需依赖(gzip 协商,见 `api-execution.md`),init-python 的验证导入已包含它,缺失时 init 会失败提示。init-python 装依赖会写 `~/.cache/uv`,DSH sandbox 下报 `Operation not permitted` 属正常——需提权(`sandbox_permissions`)或在终端手动跑 init-python。
16
16
 
17
17
  ## 二. 执行命令
18
18
 
@@ -30,11 +30,11 @@ Agent 先 `write` 脚本,再用 run.py 执行(run.py 注入 `PRESET_DIR`/`OU
30
30
 
31
31
  ```bash
32
32
  # 1. 生成 analysis_id(一次分析一次)
33
- AID=$({PRESET}/.venv/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())")
33
+ AID=$({VENV}/bin/python -c "import sys; sys.path.insert(0, '{PRESET}/skills/erp-analysis/scripts'); from report_analysis_id import gen_analysis_id; print(gen_analysis_id())")
34
34
  # 2. write 脚本到 OUT_DIR:runs/$DSH_SESSION_ID/$AID/query.py
35
35
  # (脚本与产物同目录,随分析隔离,避免并发分析脚本名互相覆盖)
36
36
  # 3. run.py 执行(cwd = 工作目录,workdir 保持默认)
37
- ANALYSIS_ID=$AID {PRESET}/.venv/bin/python {PRESET}/run.py runs/$DSH_SESSION_ID/$AID/query.py
37
+ ANALYSIS_ID=$AID {VENV}/bin/python {PRESET}/run.py runs/$DSH_SESSION_ID/$AID/query.py
38
38
  ```
39
39
 
40
40
  脚本内容:
@@ -82,7 +82,7 @@ DSH 的 `write` 无 8K 限制,直接一次写完整 `.py` 文件再 `bash` 执
82
82
 
83
83
  ### R1. 执行入口铁律
84
84
 
85
- - 脚本一律 `write` 成文件,再 `ANALYSIS_ID=<id> {PRESET}/.venv/bin/python {PRESET}/run.py <脚本>`。
85
+ - 脚本一律 `write` 成文件,再 `ANALYSIS_ID=<id> {VENV}/bin/python {PRESET}/run.py <脚本>`。
86
86
  - 长任务(大查询 / 多图)用 `run_in_background: true`,通过 `job_output` 收集结果。
87
87
 
88
88
  ### R2. 数据缓存
@@ -1,6 +1,7 @@
1
- # 苍穹数据分析 preset —— Python 运行时初始化(幂等,Windows PowerShell 版)
1
+ # 苍穹数据分析 preset —— Python 运行时初始化(幂等,Windows PowerShell 版)
2
2
  # 用法:pwsh {PRESET}/skills/erp-analysis/scripts/init-python.ps1
3
- # 行为:.venv 建在 preset 根目录(随 preset 分发、多工作区共享);已存在则跳过创建。
3
+ # 行为:把 .venv 建在 **机器级** 目录 `$env:DSH_HOME\cache\cosmic\venv`(与 preset 版本解耦,
4
+ # 升级 preset 不重建;多工作区共享);已存在则跳过创建,但仍按 requirements.txt 同步依赖。
4
5
  $ErrorActionPreference = "Stop"
5
6
 
6
7
  # 国内镜像(可用环境变量覆盖):Python 包走清华源、Python 解释器走 npmmirror
@@ -15,8 +16,11 @@ if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
15
16
  }
16
17
 
17
18
  $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path # .../skills/erp-analysis/scripts
18
- $ROOT = (Resolve-Path (Join-Path $SCRIPT_DIR "../../..")).Path # preset 根目录
19
- $VENV = Join-Path $ROOT ".venv"
19
+ $ROOT = (Resolve-Path (Join-Path $SCRIPT_DIR "../../..")).Path # preset 根目录(run.py 所在)
20
+ # Python 运行时是「机器级」的:放 $DSH_HOME 下,与 preset 包解耦。
21
+ # $env:DSH_HOME 由 dsh 注入每次 shell;缺省 $HOME\.dsh(与 dsh 的 resolveDshHome 同规则)。
22
+ $DshHome = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $HOME ".dsh" }
23
+ $VENV = if ($env:COSMIC_VENV) { $env:COSMIC_VENV } else { Join-Path $DshHome "cache\cosmic\venv" }
20
24
  $REQ = Join-Path $SCRIPT_DIR "requirements.txt"
21
25
  $PY_VER = "3.14"
22
26
 
@@ -25,8 +29,11 @@ Write-Host "==> venv: $VENV"
25
29
 
26
30
  # 1) 创建 venv(幂等;uv 会自动下载缺失的 3.14)
27
31
  if (Test-Path $VENV) {
28
- Write-Host "==> .venv 已存在,跳过创建"
32
+ Write-Host "==> venv 已存在,跳过创建(继续同步依赖)"
29
33
  } else {
34
+ $Parent = Split-Path -Parent $VENV
35
+ Write-Host "==> mkdir -p $Parent"
36
+ New-Item -ItemType Directory -Force -Path $Parent | Out-Null
30
37
  Write-Host "==> uv venv --python $PY_VER $VENV"
31
38
  uv venv --python $PY_VER $VENV
32
39
  }
@@ -35,7 +42,7 @@ if (Test-Path $VENV) {
35
42
  Write-Host "==> uv pip install -r $REQ"
36
43
  uv pip install --python $VENV -r $REQ
37
44
 
38
- # 3) 验证导入(uv run 跨平台,自动用 .venv 里的解释器;requests 必须可导入——传输层缺它会回退 urllib 并丢失 gzip 透明解压,大数据量查询慢 7 倍以上)
45
+ # 3) 验证导入(uv run 跨平台,自动用该 venv 里的解释器;requests 必须可导入——传输层缺它会回退 urllib 并丢失 gzip 透明解压,大数据量查询慢 7 倍以上)
39
46
  Write-Host "==> 验证导入"
40
47
  uv run --python $VENV python -c "import pandas, numpy, plotly, jinja2, requests; print('OK', pandas.__version__, numpy.__version__, plotly.__version__, jinja2.__version__, 'requests', requests.__version__)"
41
48
 
@@ -2,7 +2,8 @@
2
2
  # 苍穹数据分析 preset —— Python 运行时初始化(幂等,macOS/Linux 版)
3
3
  # 用法(macOS/Linux):bash {PRESET}/skills/erp-analysis/scripts/init-python.sh
4
4
  # 用法(Windows):pwsh {PRESET}/skills/erp-analysis/scripts/init-python.ps1
5
- # 行为:.venv 建在 preset 根目录(随 preset 分发、多工作区共享);已存在则跳过创建。
5
+ # 行为:把 .venv 建在 **机器级** 目录 `$DSH_HOME/cache/cosmic/venv`(与 preset 版本解耦,
6
+ # 升级 preset 不重建;多工作区共享);已存在则跳过创建,但仍按 requirements.txt 同步依赖。
6
7
  set -euo pipefail
7
8
 
8
9
  # 国内镜像(可用环境变量覆盖):Python 包走清华源、Python 解释器走 npmmirror
@@ -17,8 +18,11 @@ if ! command -v uv >/dev/null 2>&1; then
17
18
  fi
18
19
 
19
20
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # .../skills/erp-analysis/scripts
20
- ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # preset 根目录
21
- VENV="$ROOT/.venv"
21
+ ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # preset 根目录(run.py 所在)
22
+ # Python 运行时是「机器级」的:放 $DSH_HOME 下,与 preset 包解耦。
23
+ # $DSH_HOME 由 dsh 注入每次 shell;缺省 ~/.dsh(与 dsh 的 resolveDshHome 同规则)。
24
+ DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
25
+ VENV="${COSMIC_VENV:-$DSH_HOME/cache/cosmic/venv}"
22
26
  REQ="$SCRIPT_DIR/requirements.txt"
23
27
  PY_VER="3.14"
24
28
 
@@ -27,8 +31,10 @@ echo "==> venv: $VENV"
27
31
 
28
32
  # 1) 创建 venv(幂等;uv 会自动下载缺失的 3.14)
29
33
  if [ -d "$VENV" ]; then
30
- echo "==> .venv 已存在,跳过创建"
34
+ echo "==> venv 已存在,跳过创建(继续同步依赖)"
31
35
  else
36
+ echo "==> mkdir -p $(dirname "$VENV")"
37
+ mkdir -p "$(dirname "$VENV")"
32
38
  echo "==> uv venv --python $PY_VER $VENV"
33
39
  uv venv --python "$PY_VER" "$VENV"
34
40
  fi
@@ -37,7 +43,7 @@ fi
37
43
  echo "==> uv pip install -r $REQ"
38
44
  uv pip install --python "$VENV" -r "$REQ"
39
45
 
40
- # 3) 验证导入(uv run 跨平台,自动用 .venv 里的解释器;requests 必须可导入——传输层缺它会回退 urllib 并丢失 gzip 透明解压,大数据量查询慢 7 倍以上)
46
+ # 3) 验证导入(uv run 跨平台,自动用该 venv 里的解释器;requests 必须可导入——传输层缺它会回退 urllib 并丢失 gzip 透明解压,大数据量查询慢 7 倍以上)
41
47
  echo "==> 验证导入"
42
48
  uv run --python "$VENV" python -c 'import pandas, numpy, plotly, jinja2, requests; print("OK", pandas.__version__, numpy.__version__, plotly.__version__, jinja2.__version__, "requests", requests.__version__)'
43
49
 
@@ -2,12 +2,22 @@
2
2
 
3
3
  ## Python 运行时(统一约定)
4
4
 
5
- 所有 cosmic-build / erp-analysis 的 .py 脚本,统一用 preset venv 解释器执行,
6
- 不用系统 python3 / 裸 python。依据 run.py 权威定义:
7
-
8
- - `{PRESET}` = `$DSH_HOME/.agent-presets/cosmic`(preset = run.py 所在目录)
9
- - macOS/Linux:`{PRESET}/.venv/bin/python`
10
- - Windows:`{PRESET}/.venv/Scripts/python.exe`
5
+ 所有 cosmic-build / erp-analysis 的 .py 脚本,统一用 cosmic venv 解释器执行,
6
+ 不用系统 python3 / 裸 python
7
+
8
+ - `{PRESET}` = preset 包目录 = **`$DSH_PROFILE_DIR/node_modules/@shundoo-ai/dsh-cosmic`**
9
+ (`$DSH_PROFILE_DIR` 由 dsh 注入每次 shell,即 `<DSH_HOME>/profiles/<profile>`)。
10
+ 权威定义见 `{PRESET}/run.py` 的 `PRESET = os.path.dirname(os.path.abspath(__file__))`
11
+ ——等价说法:本 SKILL.md 所在目录的上两级(布局是 `{PRESET}/skills/<skill名>/`)。
12
+ - `{VENV}` = `$DSH_HOME/cache/cosmic/venv` —— **机器级** Python 运行时,与 preset 版本解耦
13
+ (升级 preset 不会重建它)。`$DSH_HOME` 由 dsh 注入每次 shell,缺省 `~/.dsh`。
14
+ - macOS/Linux:`{VENV}/bin/python`
15
+ - Windows:`{VENV}/Scripts/python.exe`
16
+
17
+ > 首次使用、或 `$DSH_HOME/cache/cosmic/venv` 被删后,需初始化:
18
+ > macOS/Linux `bash {PRESET}/skills/erp-analysis/scripts/init-python.sh`;
19
+ > Windows `pwsh {PRESET}/skills/erp-analysis/scripts/init-python.ps1`。
20
+ > 脚本**幂等**:已存在则跳过创建,但仍会按 `requirements.txt` 同步依赖——升级 preset 后重跑一次即可。
11
21
 
12
22
  ## 公共信息
13
23
 
@@ -30,10 +40,10 @@
30
40
  | cosmic_home | gradle.properties `systemProp.cosmic_home`(优先于环境变量 `COSMIC_HOME`) | 苍穹安装目录;config.gradle 据此定位 `${cosmic_home}/mservice-cosmic/lib/*`(bos/trd/biz/cus),编译插件无需 export |
31
41
  | GRADLE_USER_HOME | `<cwd>/.gradle/user-home` | gradle wrapper 缓存;dist 从系统 `~/.gradle/wrapper/dists` **复制**(勿用软链——wrapper 会删链重解压) |
32
42
  | gradlew / gradlew.bat | POSIX:项目根 `gradlew` 需 `chmod +x`;Windows:需 `gradle/wrapper/`(gradle-wrapper.properties + gradle-wrapper.jar)齐全,`gradlew.bat` 才可运行 | 模板生成时可能缺 x 位;缺 wrapper 元数据时从模板/系统 dist 补齐 |
33
- | 插件编译脚本 | `{PRESET}/.venv/bin/python compile_plugin_source.py` | 脚本只注入 `JAVA_HOME`、**不注入 `GRADLE_USER_HOME`**,调用方必须显式 `export GRADLE_USER_HOME=<cwd>/.gradle/user-home`,否则 Gradle 默认写用户主目录(Windows:`%USERPROFILE%\.gradle`)触发权限拦截;cosmic_home、解释器约定见上 |
43
+ | 插件编译脚本 | `{VENV}/bin/python compile_plugin_source.py` | 脚本只注入 `JAVA_HOME`、**不注入 `GRADLE_USER_HOME`**,调用方必须显式 `export GRADLE_USER_HOME=<cwd>/.gradle/user-home`,否则 Gradle 默认写用户主目录(Windows:`%USERPROFILE%\.gradle`)触发权限拦截;cosmic_home、解释器约定见上 |
34
44
  | 构建权限边界 | 产 jar 走 `:<模块>:build`(写工作区);勿用 `buildJar`/`deployJar`(写 cosmic 安装目录 outputdir,工作区外) | Windows 另有 `CreatePipe` 限制,构建/热部署仍须放开权限;Attacher classpath 分隔符 Windows 用 `;` |
35
45
 
36
46
  > 📌 配置按**工作区**隔离(appconfig / GRADLE_USER_HOME / ERP 凭证均在 `<cwd>` 下),多工作区可并行开展不同项目;bash 每次调用为独立 shell,命令中需显式携带工作区约定值(如 `export GRADLE_USER_HOME=<cwd>/.gradle/user-home`),勿依赖跨调用 export。沙箱写权限仅限工作区(preset 技能脚本 `~/.dsh/...`、`~/kd` 等外部路径修改需授权)。权限放开按平台:**macOS/Linux 无管道限制,构建/热部署无需放开**(缓存入工作区即可);**Windows 禁跨进程管道(`CreatePipe error=5`),构建/热部署每次须单命令级放开 `danger-full-access`**(或会话级放宽;`--no-daemon` 无效)。
37
- > 📌 app-build 脚本(`bootstrap_cosmic_context.py` / `appconfig_load.py` / `prepare_plugin_handoff.py` / `compile_plugin_source.py` / `pipeline_gate.py` / `register_plugins.py` / `render_output_cards.py` 等)同样统一用 `{PRESET}/.venv/bin/python` 执行——app-build SKILL.md 模板里的 `python3 {ROOT_DIR}/scripts/...` 写法与本约定冲突时,以本约定为准。
47
+ > 📌 app-build 脚本(`bootstrap_cosmic_context.py` / `appconfig_load.py` / `prepare_plugin_handoff.py` / `compile_plugin_source.py` / `pipeline_gate.py` / `register_plugins.py` / `render_output_cards.py` 等)同样统一用 `{VENV}/bin/python` 执行——app-build SKILL.md 模板里的 `python3 {ROOT_DIR}/scripts/...` 写法与本约定冲突时,以本约定为准。
38
48
 
39
49
  <!-- 后续按需补充:数据分析、元数据建模等约定 -->