支持云下载继承版本参数并调整回滚请求格式
- 新增 PARENT_VERSION_NUMBER 可选配置,默认空值不传 - create-download-task 非空时透传 parentVersionNumber - 支持 LLM/规则从自然语言和 key=value 中抽取继承版本参数 - 将 rollback 接口参数从表单 body 改为 URL query 拼接 - 同步 README、打包说明和 Skill 文档 - 增加 MCP 参数透传、配置写入和 rollback query 调用测试
This commit is contained in:
parent
4250a7b221
commit
039a3e1bdc
@ -91,7 +91,7 @@ packaging/
|
|||||||
- 支持通过 `--llm-action-analysis-prompt-file`、`PAM_LLM_ACTION_ANALYSIS_PROMPT_FILE` 或 chat 内 `llm config action_analysis_prompt_file=...` 自定义 action 审核提示词。
|
- 支持通过 `--llm-action-analysis-prompt-file`、`PAM_LLM_ACTION_ANALYSIS_PROMPT_FILE` 或 chat 内 `llm config action_analysis_prompt_file=...` 自定义 action 审核提示词。
|
||||||
- 增加统一运行日志,默认写入 `logs/pam_deploy_agent.log`,覆盖 CLI/chat、LLM 调用、action 路由、脚本/MCP 调用、LangGraph、checkpoint 等关键流程。
|
- 增加统一运行日志,默认写入 `logs/pam_deploy_agent.log`,覆盖 CLI/chat、LLM 调用、action 路由、脚本/MCP 调用、LangGraph、checkpoint 等关键流程。
|
||||||
- chat 支持 `llm test [文本]`,可用当前 LLM client 做一次轻量调用,确认真实 LLM 或规则 fallback 是否正常加载。
|
- chat 支持 `llm test [文本]`,可用当前 LLM client 做一次轻量调用,确认真实 LLM 或规则 fallback 是否正常加载。
|
||||||
- 添加基础测试,当前本地结果为 `67 passed, 3 skipped`。
|
- 添加基础测试,当前本地结果为 `72 passed, 3 skipped`。
|
||||||
|
|
||||||
未完成:
|
未完成:
|
||||||
|
|
||||||
@ -300,7 +300,11 @@ PAM> resume
|
|||||||
PAM> exit
|
PAM> exit
|
||||||
```
|
```
|
||||||
|
|
||||||
`chat` 默认仍要求在会话内显式输入 `run`,并确认参数、目标 IP 范围和最终执行后才会执行 action。输入 `你好`、`hello` 这类问候不会触发 LLM/结构化分析;需要分析部署需求时可直接描述部署任务,或显式使用 `analyze <需求>`。每个 action 完成后都会自动进入一次 LLM/规则审核,并播报审核开始/结束;审核输入只包含当前 action 的结构化结果和必要诊断日志,不会把完整运行态 `state_summary` 交给大模型,避免跨步骤状态干扰判断;只有审核通过才会把 action 记为 completed;如果审核建议停止或审核本身失败,流程会暂停并输出建议,等待用户决定是否 `resume` 重试当前 action。`poll-download-progress` 和 `poll-upgrade-progress` 每次只查询一次进度,workflow 会按 `POLL_INTERVAL_SEC`、`DOWNLOAD_POLL_MAX_ATTEMPTS`、`UPGRADE_POLL_MAX_ATTEMPTS` 重复调用,并在每次返回后让 LLM/规则判断是否完成、播报进度;未完成时不会跳到下一个 action。`verify-ip` 用于应用启动后的健康检查,失败时 workflow 会按 `VERIFY_INTERVAL_SEC` 重试,最多 `VERIFY_MAX_ATTEMPTS` 次;默认约每 10 秒一次、最多 12 次,仍未通过才暂停。逐 IP action 失败时也会暂停,修复外部环境后输入 `resume` 会从当前 action 重试;如果确实需要回滚,使用 `rollback [IP]` 显式执行。`llm test [文本]` 可测试当前 LLM client 是否可用。`--analyze-actions` 仅控制详细审核结果是否写入 `events`。执行中可按 `Ctrl+C` 中断,chat 会保存当前 checkpoint 并把流程标记为 `user_interrupted`。`set KEY=VALUE` 和 `load params <路径>` 会把更新同步到当前运行 state、`config.txt` 和 checkpoint。`chat` 也支持 `--llm-base-url` / `--llm-api-key` / `--llm-model` / `--llm-action-analysis-prompt-file`、`--mcp-config` 和 `--analyze-actions`。
|
`chat` 默认仍要求在会话内显式输入 `run`,并确认参数、目标 IP 范围和最终执行后才会执行 action。输入 `你好`、`hello` 这类问候不会触发 LLM/结构化分析;需要分析部署需求时可直接描述部署任务,或显式使用 `analyze <需求>`。每个 action 完成后都会自动进入一次 LLM/规则审核,并播报审核开始/结束;审核输入只包含当前 action 的结构化结果和必要诊断日志,不会把完整运行态 `state_summary` 交给大模型,避免跨步骤状态干扰判断;只有审核通过才会把 action 记为 completed;如果审核建议停止或审核本身失败,流程会暂停并输出建议,等待用户决定是否 `resume` 重试当前 action。`create-download-task` 支持可选 `PARENT_VERSION_NUMBER`,非空时会在云下载接口中传入 `parentVersionNumber`,用于指定继承哪个版本的规则;默认空值不传,沿用正在使用的版本规则。`poll-download-progress` 和 `poll-upgrade-progress` 每次只查询一次进度,workflow 会按 `POLL_INTERVAL_SEC`、`DOWNLOAD_POLL_MAX_ATTEMPTS`、`UPGRADE_POLL_MAX_ATTEMPTS` 重复调用,并在每次返回后让 LLM/规则判断是否完成、播报进度;未完成时不会跳到下一个 action。`verify-ip` 用于应用启动后的健康检查,失败时 workflow 会按 `VERIFY_INTERVAL_SEC` 重试,最多 `VERIFY_MAX_ATTEMPTS` 次;默认约每 10 秒一次、最多 12 次,仍未通过才暂停。逐 IP action 失败时也会暂停,修复外部环境后输入 `resume` 会从当前 action 重试;如果确实需要回滚,使用 `rollback [IP]` 显式执行。`llm test [文本]` 可测试当前 LLM client 是否可用。`--analyze-actions` 仅控制详细审核结果是否写入 `events`。执行中可按 `Ctrl+C` 中断,chat 会保存当前 checkpoint 并把流程标记为 `user_interrupted`。`set KEY=VALUE` 和 `load params <路径>` 会把更新同步到当前运行 state、`config.txt` 和 checkpoint。`chat` 也支持 `--llm-base-url` / `--llm-api-key` / `--llm-model` / `--llm-action-analysis-prompt-file`、`--mcp-config` 和 `--analyze-actions`。
|
||||||
|
|
||||||
|
云下载相关参数:
|
||||||
|
|
||||||
|
- `PARENT_VERSION_NUMBER`:可选,创建云下载任务时映射为接口参数 `parentVersionNumber`;默认空值不发送,表示继承正在使用的版本规则。
|
||||||
|
|
||||||
重试和进度查询相关参数:
|
重试和进度查询相关参数:
|
||||||
|
|
||||||
|
|||||||
@ -68,6 +68,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
| `actionType` | `ACTION_TYPE` | 否 | 升级类型,默认 `FULL` |
|
| `actionType` | `ACTION_TYPE` | 否 | 升级类型,默认 `FULL` |
|
||||||
| `timeOut` | `TIMEOUT` | 否 | 接口级超时参数,默认 `120` |
|
| `timeOut` | `TIMEOUT` | 否 | 接口级超时参数,默认 `120` |
|
||||||
| `logName` | `LOG_NAME` | 否 | 日志文件名,默认 `app.log` |
|
| `logName` | `LOG_NAME` | 否 | 日志文件名,默认 `app.log` |
|
||||||
|
| `parentVersionNumber` | `PARENT_VERSION_NUMBER` | 否 | 云下载时指定继承哪个版本的规则;默认空值不传,继承正在使用的版本规则 |
|
||||||
| `pollIntervalSec` | `POLL_INTERVAL_SEC` | 否 | 两次进度查询间隔,默认 `2` 秒 |
|
| `pollIntervalSec` | `POLL_INTERVAL_SEC` | 否 | 两次进度查询间隔,默认 `2` 秒 |
|
||||||
| `downloadPollMaxAttempts` | `DOWNLOAD_POLL_MAX_ATTEMPTS` | 否 | 云下载进度最大查询次数,默认 `60` |
|
| `downloadPollMaxAttempts` | `DOWNLOAD_POLL_MAX_ATTEMPTS` | 否 | 云下载进度最大查询次数,默认 `60` |
|
||||||
| `upgradePollMaxAttempts` | `UPGRADE_POLL_MAX_ATTEMPTS` | 否 | 单 IP 推送进度最大查询次数,默认 `600` |
|
| `upgradePollMaxAttempts` | `UPGRADE_POLL_MAX_ATTEMPTS` | 否 | 单 IP 推送进度最大查询次数,默认 `600` |
|
||||||
@ -114,6 +115,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
- `actionType`
|
- `actionType`
|
||||||
- `timeOut`
|
- `timeOut`
|
||||||
- `logName`
|
- `logName`
|
||||||
|
- `parentVersionNumber`(可选;空值表示不传)
|
||||||
- 用户指定 IP 子集(如有)
|
- 用户指定 IP 子集(如有)
|
||||||
|
|
||||||
确认规则:
|
确认规则:
|
||||||
@ -138,6 +140,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
- ACTION_TYPE: FULL
|
- ACTION_TYPE: FULL
|
||||||
- TIMEOUT: 120
|
- TIMEOUT: 120
|
||||||
- LOG_NAME: app.log
|
- LOG_NAME: app.log
|
||||||
|
- PARENT_VERSION_NUMBER: -
|
||||||
- 指定IP: 192.168.1.10, 192.168.1.11
|
- 指定IP: 192.168.1.10, 192.168.1.11
|
||||||
- CLIENT_ID: 已提供
|
- CLIENT_ID: 已提供
|
||||||
- CLIENT_SECRET: 已提供
|
- CLIENT_SECRET: 已提供
|
||||||
@ -163,6 +166,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
- `ACTION_TYPE`
|
- `ACTION_TYPE`
|
||||||
- `TIMEOUT`
|
- `TIMEOUT`
|
||||||
- `LOG_NAME`
|
- `LOG_NAME`
|
||||||
|
- `PARENT_VERSION_NUMBER`
|
||||||
- `POLL_INTERVAL_SEC`
|
- `POLL_INTERVAL_SEC`
|
||||||
- `DOWNLOAD_POLL_MAX_ATTEMPTS`
|
- `DOWNLOAD_POLL_MAX_ATTEMPTS`
|
||||||
- `UPGRADE_POLL_MAX_ATTEMPTS`
|
- `UPGRADE_POLL_MAX_ATTEMPTS`
|
||||||
@ -177,6 +181,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
- `client_secret` 等敏感字段不得通过命令行透传。
|
- `client_secret` 等敏感字段不得通过命令行透传。
|
||||||
- 如果用户明确要求“不落地配置文件”,则本 Skill 不执行真实部署,只说明限制和原因。
|
- 如果用户明确要求“不落地配置文件”,则本 Skill 不执行真实部署,只说明限制和原因。
|
||||||
- `traceFilePath` 不写入 `config.txt`,由 Agent 在运行时持有并应用。
|
- `traceFilePath` 不写入 `config.txt`,由 Agent 在运行时持有并应用。
|
||||||
|
- `PARENT_VERSION_NUMBER` 写入 `config.txt` 但默认可为空;只有非空时,`create-download-task` 才把它作为云下载接口参数 `parentVersionNumber` 发送。
|
||||||
- 进度查询和健康检查重试参数写入 `config.txt`,由 Agent workflow 和脚本调试流程共同读取。
|
- 进度查询和健康检查重试参数写入 `config.txt`,由 Agent workflow 和脚本调试流程共同读取。
|
||||||
|
|
||||||
## 4. 主流程(硬约束)
|
## 4. 主流程(硬约束)
|
||||||
@ -202,7 +207,7 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
11. 调用 `get-node-url`。
|
11. 调用 `get-node-url`。
|
||||||
12. 调用 `get-online-ips`。
|
12. 调用 `get-online-ips`。
|
||||||
13. 若用户指定了目标 IP,则基于在线 IP 列表做过滤。
|
13. 若用户指定了目标 IP,则基于在线 IP 列表做过滤。
|
||||||
14. 调用 `create-download-task`。
|
14. 调用 `create-download-task`;如 `PARENT_VERSION_NUMBER` 非空,则云下载接口携带 `parentVersionNumber`,否则不传该参数。
|
||||||
15. 重复调用 `poll-download-progress` 单次查询进度;每次返回后交给 LLM/规则判断,直到下载完成、失败或达到最大查询次数。
|
15. 重复调用 `poll-download-progress` 单次查询进度;每次返回后交给 LLM/规则判断,直到下载完成、失败或达到最大查询次数。
|
||||||
16. 按在线 IP 或过滤后的目标 IP 列表逐台执行:
|
16. 按在线 IP 或过滤后的目标 IP 列表逐台执行:
|
||||||
- `upgrade-ip`
|
- `upgrade-ip`
|
||||||
@ -219,17 +224,18 @@ description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解
|
|||||||
1. 一次完整部署中的所有 action 调用,应复用同一个 `traceFilePath`,禁止每个 action 各自新建独立 trace 文件。
|
1. 一次完整部署中的所有 action 调用,应复用同一个 `traceFilePath`,禁止每个 action 各自新建独立 trace 文件。
|
||||||
2. 全局 action 与下一 action 之间,按 `stepIntervalSec` 等待。
|
2. 全局 action 与下一 action 之间,按 `stepIntervalSec` 等待。
|
||||||
3. `create-download-task` 成功后,直接进入 `poll-download-progress`;未完成时按 `POLL_INTERVAL_SEC` 等待后再次查询当前 action。
|
3. `create-download-task` 成功后,直接进入 `poll-download-progress`;未完成时按 `POLL_INTERVAL_SEC` 等待后再次查询当前 action。
|
||||||
4. 同一台 IP 内部:
|
4. `PARENT_VERSION_NUMBER` 只影响 `create-download-task` / `download-cloud`,不得透传到推送、启动、校验或日志下载 action。
|
||||||
|
5. 同一台 IP 内部:
|
||||||
- `upgrade-ip -> poll-upgrade-progress`
|
- `upgrade-ip -> poll-upgrade-progress`
|
||||||
- `poll-upgrade-progress -> start-ip`
|
- `poll-upgrade-progress -> start-ip`
|
||||||
- `start-ip -> verify-ip`
|
- `start-ip -> verify-ip`
|
||||||
- `verify-ip -> download-log`
|
- `verify-ip -> download-log`
|
||||||
之间按 `perIpStepIntervalSec` 等待。
|
之间按 `perIpStepIntervalSec` 等待。
|
||||||
5. 当前一台 IP 处理完成后,到下一台 IP 开始前,按 `perIpIntervalSec` 等待。
|
6. 当前一台 IP 处理完成后,到下一台 IP 开始前,按 `perIpIntervalSec` 等待。
|
||||||
6. 若某步骤失败后需要进入提示、确认或分支流程,可按 `failurePauseSec` 等待。
|
7. 若某步骤失败后需要进入提示、确认或分支流程,可按 `failurePauseSec` 等待。
|
||||||
7. 若某个间隔值为 `0`,表示该层级不等待,直接进入下一动作。
|
8. 若某个间隔值为 `0`,表示该层级不等待,直接进入下一动作。
|
||||||
8. `poll-download-progress` 和 `poll-upgrade-progress` 的脚本 action 只执行一次进度查询;正式 workflow 的循环、checkpoint、LLM 判断和进度播报由 Agent Runtime 负责。
|
9. `poll-download-progress` 和 `poll-upgrade-progress` 的脚本 action 只执行一次进度查询;正式 workflow 的循环、checkpoint、LLM 判断和进度播报由 Agent Runtime 负责。
|
||||||
9. `verify-ip` 失败但未达到 `VERIFY_MAX_ATTEMPTS` 时,不进入 `download-log`,也不把当前 action 记为 completed;正式 workflow 会播报健康检查进度、保存 checkpoint,并按 `VERIFY_INTERVAL_SEC` 重试当前 action。
|
10. `verify-ip` 失败但未达到 `VERIFY_MAX_ATTEMPTS` 时,不进入 `download-log`,也不把当前 action 记为 completed;正式 workflow 会播报健康检查进度、保存 checkpoint,并按 `VERIFY_INTERVAL_SEC` 重试当前 action。
|
||||||
|
|
||||||
### 4.2 主流程中的强制确认点
|
### 4.2 主流程中的强制确认点
|
||||||
|
|
||||||
@ -506,7 +512,7 @@ powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action <ActionName> [-Ip
|
|||||||
| `stop-ip` | 停止指定 IP 应用 | `--ip` / `-Ip` |
|
| `stop-ip` | 停止指定 IP 应用 | `--ip` / `-Ip` |
|
||||||
| `verify-ip` | 校验指定 IP | `--ip` / `-Ip` |
|
| `verify-ip` | 校验指定 IP | `--ip` / `-Ip` |
|
||||||
| `download-log` | 下载指定 IP 日志压缩包,返回 zip 文件路径 | `--ip` / `-Ip` |
|
| `download-log` | 下载指定 IP 日志压缩包,返回 zip 文件路径 | `--ip` / `-Ip` |
|
||||||
| `rollback-ip` | 执行指定 IP 回滚 | `--ip` / `-Ip`,可带 `--stop-first` / `-RollbackStopFirst` |
|
| `rollback-ip` | 执行指定 IP 回滚;接口参数使用 URL query,不使用表单 body | `--ip` / `-Ip`,可带 `--stop-first` / `-RollbackStopFirst` |
|
||||||
|
|
||||||
### 6.4 action 输出约定
|
### 6.4 action 输出约定
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ ZIP_FILE_PATH=C:\path\to\pam-2.0.5.zip
|
|||||||
ACTION_TYPE=FULL
|
ACTION_TYPE=FULL
|
||||||
TIMEOUT=120
|
TIMEOUT=120
|
||||||
LOG_NAME=app.log
|
LOG_NAME=app.log
|
||||||
|
PARENT_VERSION_NUMBER=
|
||||||
POLL_INTERVAL_SEC=2
|
POLL_INTERVAL_SEC=2
|
||||||
DOWNLOAD_POLL_MAX_ATTEMPTS=60
|
DOWNLOAD_POLL_MAX_ATTEMPTS=60
|
||||||
UPGRADE_POLL_MAX_ATTEMPTS=600
|
UPGRADE_POLL_MAX_ATTEMPTS=600
|
||||||
|
|||||||
@ -368,6 +368,7 @@ function Get-PamConfig {
|
|||||||
'ACTION_TYPE' { $config[$key] = $value }
|
'ACTION_TYPE' { $config[$key] = $value }
|
||||||
'TIMEOUT' { $config[$key] = $value }
|
'TIMEOUT' { $config[$key] = $value }
|
||||||
'LOG_NAME' { $config[$key] = $value }
|
'LOG_NAME' { $config[$key] = $value }
|
||||||
|
'PARENT_VERSION_NUMBER' { $config[$key] = $value }
|
||||||
'POLL_INTERVAL_SEC' { $config[$key] = $value }
|
'POLL_INTERVAL_SEC' { $config[$key] = $value }
|
||||||
'DOWNLOAD_POLL_MAX_ATTEMPTS' { $config[$key] = $value }
|
'DOWNLOAD_POLL_MAX_ATTEMPTS' { $config[$key] = $value }
|
||||||
'UPGRADE_POLL_MAX_ATTEMPTS' { $config[$key] = $value }
|
'UPGRADE_POLL_MAX_ATTEMPTS' { $config[$key] = $value }
|
||||||
@ -391,6 +392,7 @@ function Get-PamConfig {
|
|||||||
ACTION_TYPE = 'FULL'
|
ACTION_TYPE = 'FULL'
|
||||||
TIMEOUT = '120'
|
TIMEOUT = '120'
|
||||||
LOG_NAME = 'app.log'
|
LOG_NAME = 'app.log'
|
||||||
|
PARENT_VERSION_NUMBER = ''
|
||||||
POLL_INTERVAL_SEC = '2'
|
POLL_INTERVAL_SEC = '2'
|
||||||
DOWNLOAD_POLL_MAX_ATTEMPTS = '60'
|
DOWNLOAD_POLL_MAX_ATTEMPTS = '60'
|
||||||
UPGRADE_POLL_MAX_ATTEMPTS = '600'
|
UPGRADE_POLL_MAX_ATTEMPTS = '600'
|
||||||
@ -780,6 +782,11 @@ function Create-DownloadTask {
|
|||||||
moduleName = $Config.MODULE_NAME
|
moduleName = $Config.MODULE_NAME
|
||||||
timeOut = '0'
|
timeOut = '0'
|
||||||
})
|
})
|
||||||
|
if ($Config.PARENT_VERSION_NUMBER) {
|
||||||
|
$query += '&' + (Join-RequestPairs ([ordered]@{
|
||||||
|
parentVersionNumber = $Config.PARENT_VERSION_NUMBER
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
[void](Invoke-PamWebRequest -Method GET -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/download-cloud?$query" -Token $Token -Headers @{
|
[void](Invoke-PamWebRequest -Method GET -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/download-cloud?$query" -Token $Token -Headers @{
|
||||||
'Target-Node' = $NodeUrl
|
'Target-Node' = $NodeUrl
|
||||||
@ -1097,16 +1104,16 @@ function Invoke-Rollback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$body = Join-RequestPairs ([ordered]@{
|
$query = Join-RequestPairs ([ordered]@{
|
||||||
airportCode = $Config.AIRPORT_CODE
|
airportCode = $Config.AIRPORT_CODE
|
||||||
targetIp = $Ip
|
targetIp = $Ip
|
||||||
applicationName = $Config.APP_NAME
|
applicationName = $Config.APP_NAME
|
||||||
moduleName = $Config.MODULE_NAME
|
moduleName = $Config.MODULE_NAME
|
||||||
timeOut = $Config.TIMEOUT
|
timeOut = $Config.TIMEOUT
|
||||||
})
|
})
|
||||||
$response = Invoke-PamWebRequest -Method POST -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/rollback" -Token $Token -Headers @{
|
$response = Invoke-PamWebRequest -Method POST -Url "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/rollback?$query" -Token $Token -Headers @{
|
||||||
'Target-Node' = $NodeUrl
|
'Target-Node' = $NodeUrl
|
||||||
} -Body $body -ContentType 'application/x-www-form-urlencoded'
|
}
|
||||||
|
|
||||||
$rollbackSuccess = Get-ResponseValue -Response $response -Candidates @('success')
|
$rollbackSuccess = Get-ResponseValue -Response $response -Candidates @('success')
|
||||||
if ($rollbackSuccess -and $rollbackSuccess -ne 'true') {
|
if ($rollbackSuccess -and $rollbackSuccess -ne 'true') {
|
||||||
|
|||||||
@ -57,6 +57,7 @@ usage() {
|
|||||||
ACTION_TYPE
|
ACTION_TYPE
|
||||||
TIMEOUT
|
TIMEOUT
|
||||||
LOG_NAME
|
LOG_NAME
|
||||||
|
PARENT_VERSION_NUMBER
|
||||||
POLL_INTERVAL_SEC
|
POLL_INTERVAL_SEC
|
||||||
DOWNLOAD_POLL_MAX_ATTEMPTS
|
DOWNLOAD_POLL_MAX_ATTEMPTS
|
||||||
UPGRADE_POLL_MAX_ATTEMPTS
|
UPGRADE_POLL_MAX_ATTEMPTS
|
||||||
@ -351,6 +352,7 @@ set_defaults() {
|
|||||||
: "${ACTION_TYPE:=FULL}"
|
: "${ACTION_TYPE:=FULL}"
|
||||||
: "${TIMEOUT:=120}"
|
: "${TIMEOUT:=120}"
|
||||||
: "${LOG_NAME:=app.log}"
|
: "${LOG_NAME:=app.log}"
|
||||||
|
: "${PARENT_VERSION_NUMBER:=}"
|
||||||
: "${POLL_INTERVAL_SEC:=2}"
|
: "${POLL_INTERVAL_SEC:=2}"
|
||||||
: "${DOWNLOAD_POLL_MAX_ATTEMPTS:=60}"
|
: "${DOWNLOAD_POLL_MAX_ATTEMPTS:=60}"
|
||||||
: "${UPGRADE_POLL_MAX_ATTEMPTS:=600}"
|
: "${UPGRADE_POLL_MAX_ATTEMPTS:=600}"
|
||||||
@ -380,7 +382,7 @@ load_config() {
|
|||||||
value="$(strip_inline_comment "$value")"
|
value="$(strip_inline_comment "$value")"
|
||||||
|
|
||||||
case "$key" in
|
case "$key" in
|
||||||
HOME_BASE_URL|CLIENT_ID|CLIENT_SECRET|AIRPORT_CODE|APP_NAME|MODULE_NAME|VERSION_NUMBER|ZIP_FILE_PATH|ACTION_TYPE|TIMEOUT|LOG_NAME|POLL_INTERVAL_SEC|DOWNLOAD_POLL_MAX_ATTEMPTS|UPGRADE_POLL_MAX_ATTEMPTS|VERIFY_INTERVAL_SEC|VERIFY_MAX_ATTEMPTS)
|
HOME_BASE_URL|CLIENT_ID|CLIENT_SECRET|AIRPORT_CODE|APP_NAME|MODULE_NAME|VERSION_NUMBER|ZIP_FILE_PATH|ACTION_TYPE|TIMEOUT|LOG_NAME|PARENT_VERSION_NUMBER|POLL_INTERVAL_SEC|DOWNLOAD_POLL_MAX_ATTEMPTS|UPGRADE_POLL_MAX_ATTEMPTS|VERIFY_INTERVAL_SEC|VERIFY_MAX_ATTEMPTS)
|
||||||
printf -v "$key" '%s' "$value"
|
printf -v "$key" '%s' "$value"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -1065,8 +1067,13 @@ wait_download_progress() {
|
|||||||
|
|
||||||
create_download_task() {
|
create_download_task() {
|
||||||
log_info "Step 3.3: 下载软件包到 Node..."
|
log_info "Step 3.3: 下载软件包到 Node..."
|
||||||
|
local download_query="versionNumber=${VERSION_NUMBER}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&timeOut=0"
|
||||||
|
if [[ -n "${PARENT_VERSION_NUMBER:-}" ]]; then
|
||||||
|
download_query="${download_query}&parentVersionNumber=${PARENT_VERSION_NUMBER}"
|
||||||
|
fi
|
||||||
|
|
||||||
http_request "GET" \
|
http_request "GET" \
|
||||||
"${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/download-cloud?versionNumber=${VERSION_NUMBER}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&timeOut=0" \
|
"${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/download-cloud?${download_query}" \
|
||||||
"" \
|
"" \
|
||||||
"" \
|
"" \
|
||||||
"Target-Node: ${NODE_URL}" \
|
"Target-Node: ${NODE_URL}" \
|
||||||
@ -1294,10 +1301,11 @@ rollback_ip() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local response
|
local response
|
||||||
|
local rollback_query="airportCode=${AIRPORT_CODE}&targetIp=${ip}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&timeOut=${TIMEOUT}"
|
||||||
if ! response=$(http_request "POST" \
|
if ! response=$(http_request "POST" \
|
||||||
"${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/rollback" \
|
"${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/rollback?${rollback_query}" \
|
||||||
"airportCode=${AIRPORT_CODE}&targetIp=${ip}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&timeOut=${TIMEOUT}" \
|
"" \
|
||||||
"application/x-www-form-urlencoded" \
|
"" \
|
||||||
"Target-Node: ${NODE_URL}"); then
|
"Target-Node: ${NODE_URL}"); then
|
||||||
printf '%s' "ROLLBACK_REQUEST_FAILED"
|
printf '%s' "ROLLBACK_REQUEST_FAILED"
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@ -72,6 +72,7 @@ cd pam-deploy-agent-linux-x86_64
|
|||||||
|
|
||||||
- 每个 action 完成后都会自动执行一次 LLM/规则审核,只有审核通过才会把 action 记为 completed。
|
- 每个 action 完成后都会自动执行一次 LLM/规则审核,只有审核通过才会把 action 记为 completed。
|
||||||
- action 审核输入不包含完整运行态 `state_summary`,只包含当前 action 的结构化结果和必要诊断日志,避免历史状态干扰大模型判断。
|
- action 审核输入不包含完整运行态 `state_summary`,只包含当前 action 的结构化结果和必要诊断日志,避免历史状态干扰大模型判断。
|
||||||
|
- `create-download-task` 支持可选 `PARENT_VERSION_NUMBER`,非空时会传给云下载接口的 `parentVersionNumber`;默认空值不发送,继承正在使用的版本规则。
|
||||||
- `poll-download-progress` 和 `poll-upgrade-progress` 是单次进度查询 action;Agent workflow 会按配置重复调用,每次返回后交给 LLM/规则判断是否完成并播报进度。
|
- `poll-download-progress` 和 `poll-upgrade-progress` 是单次进度查询 action;Agent workflow 会按配置重复调用,每次返回后交给 LLM/规则判断是否完成并播报进度。
|
||||||
- `verify-ip` 会按 `VERIFY_INTERVAL_SEC` / `VERIFY_MAX_ATTEMPTS` 做应用健康检查重试,默认每 10 秒一次、最多 12 次,仍未通过才暂停。
|
- `verify-ip` 会按 `VERIFY_INTERVAL_SEC` / `VERIFY_MAX_ATTEMPTS` 做应用健康检查重试,默认每 10 秒一次、最多 12 次,仍未通过才暂停。
|
||||||
- `--analyze-actions` 只控制是否把详细审核结果写入 `events`。
|
- `--analyze-actions` 只控制是否把详细审核结果写入 `events`。
|
||||||
|
|||||||
@ -36,7 +36,7 @@ pam-deploy-agent-linux-x86_64/
|
|||||||
|
|
||||||
发布包默认会优先使用 `prompt_toolkit` 增强输入,支持更稳定的退格、历史记录和补全;如果增强输入初始化失败,会自动降级到普通 `input()`。输出仍会在可用时使用 `rich` 做更清晰的文本展示。
|
发布包默认会优先使用 `prompt_toolkit` 增强输入,支持更稳定的退格、历史记录和补全;如果增强输入初始化失败,会自动降级到普通 `input()`。输出仍会在可用时使用 `rich` 做更清晰的文本展示。
|
||||||
action 失败或审核阻断后会保存 checkpoint 并暂停;修复外部环境后输入 `resume` 会从当前 action 重试。回滚不再属于主 workflow 自动分支,需要时在 chat 内输入 `rollback [IP]` 显式执行。
|
action 失败或审核阻断后会保存 checkpoint 并暂停;修复外部环境后输入 `resume` 会从当前 action 重试。回滚不再属于主 workflow 自动分支,需要时在 chat 内输入 `rollback [IP]` 显式执行。
|
||||||
chat 会在执行前归一化并展示实际写入脚本配置的参数;`script_only` / `hybrid_node_mcp` 会先检查 `ZIP_FILE_PATH` 是否存在,避免脚本运行后才用默认路径失败。执行过程中每个 action 都会输出开始、完成或失败状态;每个 action 完成后还会自动进入一次 LLM/规则审核,并播报审核开始和审核结果;审核输入只包含当前 action 的结构化结果和必要诊断日志,不会把完整运行态 `state_summary` 交给大模型;只有审核通过才会把 action 记为 completed。
|
chat 会在执行前归一化并展示实际写入脚本配置的参数;`script_only` / `hybrid_node_mcp` 会先检查 `ZIP_FILE_PATH` 是否存在,避免脚本运行后才用默认路径失败。执行过程中每个 action 都会输出开始、完成或失败状态;每个 action 完成后还会自动进入一次 LLM/规则审核,并播报审核开始和审核结果;审核输入只包含当前 action 的结构化结果和必要诊断日志,不会把完整运行态 `state_summary` 交给大模型;只有审核通过才会把 action 记为 completed。`create-download-task` 支持可选 `PARENT_VERSION_NUMBER`,非空时会作为云下载接口参数 `parentVersionNumber` 传入;默认空值不发送,表示继承正在使用的版本规则。
|
||||||
`poll-download-progress` 和 `poll-upgrade-progress` 每次只查询一次进度,Agent workflow 会按 `POLL_INTERVAL_SEC`、`DOWNLOAD_POLL_MAX_ATTEMPTS`、`UPGRADE_POLL_MAX_ATTEMPTS` 重复调用,并在每次返回后交给 LLM/规则判断是否完成、向 chat 播报进度。`verify-ip` 健康检查失败时,Agent workflow 会按 `VERIFY_INTERVAL_SEC` 重试,最多 `VERIFY_MAX_ATTEMPTS` 次;默认每 10 秒一次、最多 12 次,仍未通过才暂停。
|
`poll-download-progress` 和 `poll-upgrade-progress` 每次只查询一次进度,Agent workflow 会按 `POLL_INTERVAL_SEC`、`DOWNLOAD_POLL_MAX_ATTEMPTS`、`UPGRADE_POLL_MAX_ATTEMPTS` 重复调用,并在每次返回后交给 LLM/规则判断是否完成、向 chat 播报进度。`verify-ip` 健康检查失败时,Agent workflow 会按 `VERIFY_INTERVAL_SEC` 重试,最多 `VERIFY_MAX_ATTEMPTS` 次;默认每 10 秒一次、最多 12 次,仍未通过才暂停。
|
||||||
|
|
||||||
## 交互式使用
|
## 交互式使用
|
||||||
@ -242,6 +242,7 @@ MCP token 获取方式与 HOME 一致,默认按 `client_credentials` POST 到
|
|||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- 执行真实 action 前请确认配置文件中的 `HOME_BASE_URL`、`CLIENT_ID`、`CLIENT_SECRET`、`AIRPORT_CODE`、`APP_NAME`、`MODULE_NAME`、`VERSION_NUMBER`、`ZIP_FILE_PATH`。
|
- 执行真实 action 前请确认配置文件中的 `HOME_BASE_URL`、`CLIENT_ID`、`CLIENT_SECRET`、`AIRPORT_CODE`、`APP_NAME`、`MODULE_NAME`、`VERSION_NUMBER`、`ZIP_FILE_PATH`。
|
||||||
|
- `PARENT_VERSION_NUMBER` 是云下载可选参数;非空时会传给 `download-cloud` 的 `parentVersionNumber`,空值不会发送。
|
||||||
- `chat` 中输入 `你好`、`hello` 这类问候不会触发 LLM/结构化分析;需要分析部署需求时请直接描述部署任务,或显式使用 `analyze <需求>`。
|
- `chat` 中输入 `你好`、`hello` 这类问候不会触发 LLM/结构化分析;需要分析部署需求时请直接描述部署任务,或显式使用 `analyze <需求>`。
|
||||||
- 每个 action 完成后都会自动执行一次 LLM/规则审核;`--analyze-actions` 和 `llm action-analysis on` 只控制是否把详细审核结果写入 `events`。
|
- 每个 action 完成后都会自动执行一次 LLM/规则审核;`--analyze-actions` 和 `llm action-analysis on` 只控制是否把详细审核结果写入 `events`。
|
||||||
- action 审核输入不包含完整运行态 `state_summary`,只包含当前 action 的结构化结果和必要诊断日志。
|
- action 审核输入不包含完整运行态 `state_summary`,只包含当前 action 的结构化结果和必要诊断日志。
|
||||||
|
|||||||
@ -194,10 +194,11 @@ LLM 环境变量:
|
|||||||
4. mcp_client.example.json 是 MCP server URL + 独立鉴权配置示例,需要按真实 MCP server 修改。
|
4. mcp_client.example.json 是 MCP server URL + 独立鉴权配置示例,需要按真实 MCP server 修改。
|
||||||
5. action 失败或审核阻断后会暂停;修复后用 resume 从当前 action 重试,需要回滚时用 rollback 显式执行。
|
5. action 失败或审核阻断后会暂停;修复后用 resume 从当前 action 重试,需要回滚时用 rollback 显式执行。
|
||||||
6. chat 会在执行前归一化并展示实际写入脚本配置的参数;script_only / hybrid_node_mcp 会先检查 ZIP_FILE_PATH 是否存在。
|
6. chat 会在执行前归一化并展示实际写入脚本配置的参数;script_only / hybrid_node_mcp 会先检查 ZIP_FILE_PATH 是否存在。
|
||||||
7. chat 执行过程中会播报每个 action 的开始、完成或失败;普通问候不会触发 LLM/结构化分析。
|
7. PARENT_VERSION_NUMBER 是云下载可选参数;空值不发送,非空时传给 parentVersionNumber。
|
||||||
8. chat 内可使用 params、events、rollback、list checkpoints、load checkpoint、load params、llm config、llm test、mcp config 等命令。
|
8. chat 执行过程中会播报每个 action 的开始、完成或失败;普通问候不会触发 LLM/结构化分析。
|
||||||
9. 日志默认写入 logs/pam_deploy_agent.log,并会脱敏 token、secret、api_key、Authorization 等字段。
|
9. chat 内可使用 params、events、rollback、list checkpoints、load checkpoint、load params、llm config、llm test、mcp config 等命令。
|
||||||
10. checkpoint 会保存完整运行参数,请放在受控目录。
|
10. 日志默认写入 logs/pam_deploy_agent.log,并会脱敏 token、secret、api_key、Authorization 等字段。
|
||||||
|
11. checkpoint 会保存完整运行参数,请放在受控目录。
|
||||||
HELP_TEXT
|
HELP_TEXT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@ CONFIG_KEYS = (
|
|||||||
"ACTION_TYPE",
|
"ACTION_TYPE",
|
||||||
"TIMEOUT",
|
"TIMEOUT",
|
||||||
"LOG_NAME",
|
"LOG_NAME",
|
||||||
|
"PARENT_VERSION_NUMBER",
|
||||||
"POLL_INTERVAL_SEC",
|
"POLL_INTERVAL_SEC",
|
||||||
"DOWNLOAD_POLL_MAX_ATTEMPTS",
|
"DOWNLOAD_POLL_MAX_ATTEMPTS",
|
||||||
"UPGRADE_POLL_MAX_ATTEMPTS",
|
"UPGRADE_POLL_MAX_ATTEMPTS",
|
||||||
|
|||||||
@ -64,6 +64,7 @@ DEFAULT_PARAMS = {
|
|||||||
"ACTION_TYPE": "FULL",
|
"ACTION_TYPE": "FULL",
|
||||||
"TIMEOUT": 120,
|
"TIMEOUT": 120,
|
||||||
"LOG_NAME": "app.log",
|
"LOG_NAME": "app.log",
|
||||||
|
"PARENT_VERSION_NUMBER": "",
|
||||||
"POLL_INTERVAL_SEC": 2,
|
"POLL_INTERVAL_SEC": 2,
|
||||||
"DOWNLOAD_POLL_MAX_ATTEMPTS": 60,
|
"DOWNLOAD_POLL_MAX_ATTEMPTS": 60,
|
||||||
"UPGRADE_POLL_MAX_ATTEMPTS": 600,
|
"UPGRADE_POLL_MAX_ATTEMPTS": 600,
|
||||||
|
|||||||
@ -1046,6 +1046,7 @@ def _looks_like_deploy_request(text: str) -> bool:
|
|||||||
"APP_NAME",
|
"APP_NAME",
|
||||||
"MODULE_NAME",
|
"MODULE_NAME",
|
||||||
"VERSION_NUMBER",
|
"VERSION_NUMBER",
|
||||||
|
"PARENT_VERSION_NUMBER",
|
||||||
"ZIP_FILE_PATH",
|
"ZIP_FILE_PATH",
|
||||||
)
|
)
|
||||||
return any(keyword in lowered for keyword in deploy_keywords) or any(marker in text for marker in param_markers)
|
return any(keyword in lowered for keyword in deploy_keywords) or any(marker in text for marker in param_markers)
|
||||||
|
|||||||
@ -39,6 +39,7 @@ PARAM_PROMPT = """从用户输入中抽取 PAM 部署参数和控制信息。
|
|||||||
"ACTION_TYPE": "...",
|
"ACTION_TYPE": "...",
|
||||||
"TIMEOUT": "...",
|
"TIMEOUT": "...",
|
||||||
"LOG_NAME": "...",
|
"LOG_NAME": "...",
|
||||||
|
"PARENT_VERSION_NUMBER": "...",
|
||||||
"POLL_INTERVAL_SEC": "...",
|
"POLL_INTERVAL_SEC": "...",
|
||||||
"DOWNLOAD_POLL_MAX_ATTEMPTS": "...",
|
"DOWNLOAD_POLL_MAX_ATTEMPTS": "...",
|
||||||
"UPGRADE_POLL_MAX_ATTEMPTS": "...",
|
"UPGRADE_POLL_MAX_ATTEMPTS": "...",
|
||||||
|
|||||||
@ -39,6 +39,9 @@ KEY_ALIASES = {
|
|||||||
"MODULE_NAME": "MODULE_NAME",
|
"MODULE_NAME": "MODULE_NAME",
|
||||||
"versionNumber": "VERSION_NUMBER",
|
"versionNumber": "VERSION_NUMBER",
|
||||||
"VERSION_NUMBER": "VERSION_NUMBER",
|
"VERSION_NUMBER": "VERSION_NUMBER",
|
||||||
|
"parentVersionNumber": "PARENT_VERSION_NUMBER",
|
||||||
|
"PARENT_VERSION_NUMBER": "PARENT_VERSION_NUMBER",
|
||||||
|
"parent_version_number": "PARENT_VERSION_NUMBER",
|
||||||
"zipFilePath": "ZIP_FILE_PATH",
|
"zipFilePath": "ZIP_FILE_PATH",
|
||||||
"ZIP_FILE_PATH": "ZIP_FILE_PATH",
|
"ZIP_FILE_PATH": "ZIP_FILE_PATH",
|
||||||
"actionType": "ACTION_TYPE",
|
"actionType": "ACTION_TYPE",
|
||||||
@ -276,6 +279,7 @@ class RuleBasedLlmClient:
|
|||||||
"APP_NAME": r"(?:应用|应用名)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
"APP_NAME": r"(?:应用|应用名)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
||||||
"MODULE_NAME": r"(?:模块|模块名)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
"MODULE_NAME": r"(?:模块|模块名)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
||||||
"VERSION_NUMBER": r"(?:版本|版本号)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
"VERSION_NUMBER": r"(?:版本|版本号)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
||||||
|
"PARENT_VERSION_NUMBER": r"(?:继承版本|父版本|规则版本|继承哪个版本的规则)\s*[::]?\s*([A-Za-z0-9_.-]+)",
|
||||||
"ZIP_FILE_PATH": r"(?:包|软件包|zip)\s*[::]?\s*([A-Za-z]:[\\/][^\s,;]+|/[^\s,;]+)",
|
"ZIP_FILE_PATH": r"(?:包|软件包|zip)\s*[::]?\s*([A-Za-z]:[\\/][^\s,;]+|/[^\s,;]+)",
|
||||||
}
|
}
|
||||||
params: dict[str, str] = {}
|
params: dict[str, str] = {}
|
||||||
|
|||||||
@ -169,6 +169,8 @@ class McpActionRunner:
|
|||||||
arguments["hashCode"] = hash_code
|
arguments["hashCode"] = hash_code
|
||||||
if node_url:
|
if node_url:
|
||||||
arguments["nodeUrl"] = node_url
|
arguments["nodeUrl"] = node_url
|
||||||
|
if action == "create-download-task" and params.get("PARENT_VERSION_NUMBER"):
|
||||||
|
arguments["parentVersionNumber"] = params.get("PARENT_VERSION_NUMBER")
|
||||||
if action == "rollback-ip":
|
if action == "rollback-ip":
|
||||||
arguments["stopFirst"] = stop_first
|
arguments["stopFirst"] = stop_first
|
||||||
return {key: value for key, value in arguments.items() if value not in (None, "")}
|
return {key: value for key, value in arguments.items() if value not in (None, "")}
|
||||||
|
|||||||
@ -223,6 +223,7 @@ def test_create_state_writes_absolute_script_config_path_and_normalized_zip(tmp_
|
|||||||
assert Path(state.trace_file_path).is_absolute()
|
assert Path(state.trace_file_path).is_absolute()
|
||||||
config_text = Path(state.config_path).read_text(encoding="utf-8")
|
config_text = Path(state.config_path).read_text(encoding="utf-8")
|
||||||
assert f"ZIP_FILE_PATH={package_path.resolve()}" in config_text
|
assert f"ZIP_FILE_PATH={package_path.resolve()}" in config_text
|
||||||
|
assert "PARENT_VERSION_NUMBER=\n" in config_text
|
||||||
|
|
||||||
|
|
||||||
def test_global_action_requires_hash_code_from_upload_package(tmp_path: Path):
|
def test_global_action_requires_hash_code_from_upload_package(tmp_path: Path):
|
||||||
|
|||||||
@ -21,13 +21,22 @@ def test_understand_request_prefers_hybrid_for_mcp():
|
|||||||
def test_extract_params_from_key_value_text():
|
def test_extract_params_from_key_value_text():
|
||||||
result = RuleBasedLlmClient().extract_params(
|
result = RuleBasedLlmClient().extract_params(
|
||||||
"HOME_BASE_URL=https://x CLIENT_ID=id CLIENT_SECRET=s AIRPORT_CODE=HET "
|
"HOME_BASE_URL=https://x CLIENT_ID=id CLIENT_SECRET=s AIRPORT_CODE=HET "
|
||||||
"APP_NAME=PAM MODULE_NAME=Node VERSION_NUMBER=2.0.5 ZIP_FILE_PATH=C:/pkg.zip"
|
"APP_NAME=PAM MODULE_NAME=Node VERSION_NUMBER=2.0.5 "
|
||||||
|
"parentVersionNumber=2.0.4 ZIP_FILE_PATH=C:/pkg.zip"
|
||||||
)
|
)
|
||||||
assert result.extracted_params["AIRPORT_CODE"] == "HET"
|
assert result.extracted_params["AIRPORT_CODE"] == "HET"
|
||||||
|
assert result.extracted_params["PARENT_VERSION_NUMBER"] == "2.0.4"
|
||||||
assert result.missing_required_params == []
|
assert result.missing_required_params == []
|
||||||
assert "CLIENT_SECRET" in result.sensitive_fields_present
|
assert "CLIENT_SECRET" in result.sensitive_fields_present
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_parent_version_from_chinese_text():
|
||||||
|
result = RuleBasedLlmClient().extract_params("请部署版本 2.0.5,云下载继承版本 2.0.4 的规则")
|
||||||
|
|
||||||
|
assert result.extracted_params["VERSION_NUMBER"] == "2.0.5"
|
||||||
|
assert result.extracted_params["PARENT_VERSION_NUMBER"] == "2.0.4"
|
||||||
|
|
||||||
|
|
||||||
def test_analyze_request_returns_structured_objects():
|
def test_analyze_request_returns_structured_objects():
|
||||||
agent = PamDeployAgent()
|
agent = PamDeployAgent()
|
||||||
result = agent.analyze_request(
|
result = agent.analyze_request(
|
||||||
|
|||||||
@ -215,6 +215,51 @@ def test_mcp_runner_passes_hash_code_and_node_url():
|
|||||||
assert calls[0][1]["nodeUrl"] == "https://pam.node"
|
assert calls[0][1]["nodeUrl"] == "https://pam.node"
|
||||||
|
|
||||||
|
|
||||||
|
def test_mcp_runner_passes_parent_version_only_for_download_task():
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def call_tool(self, tool_name, arguments):
|
||||||
|
calls.append((tool_name, arguments))
|
||||||
|
return {"ACTION": "create-download-task", "SUCCESS": "true"}
|
||||||
|
|
||||||
|
runner = McpActionRunner(client=Client())
|
||||||
|
|
||||||
|
result = runner.run(
|
||||||
|
"create-download-task",
|
||||||
|
params={
|
||||||
|
"VERSION_NUMBER": "2.0.5",
|
||||||
|
"PARENT_VERSION_NUMBER": "2.0.4",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.ok is True
|
||||||
|
assert calls[0][1]["versionNumber"] == "2.0.5"
|
||||||
|
assert calls[0][1]["parentVersionNumber"] == "2.0.4"
|
||||||
|
|
||||||
|
|
||||||
|
def test_mcp_runner_omits_blank_parent_version():
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def call_tool(self, tool_name, arguments):
|
||||||
|
calls.append((tool_name, arguments))
|
||||||
|
return {"ACTION": "create-download-task", "SUCCESS": "true"}
|
||||||
|
|
||||||
|
runner = McpActionRunner(client=Client())
|
||||||
|
|
||||||
|
result = runner.run(
|
||||||
|
"create-download-task",
|
||||||
|
params={
|
||||||
|
"VERSION_NUMBER": "2.0.5",
|
||||||
|
"PARENT_VERSION_NUMBER": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.ok is True
|
||||||
|
assert "parentVersionNumber" not in calls[0][1]
|
||||||
|
|
||||||
|
|
||||||
def _write_json_config(tmpdir, payload):
|
def _write_json_config(tmpdir, payload):
|
||||||
path = tmpdir / "mcp.json"
|
path = tmpdir / "mcp.json"
|
||||||
path.write_text(__import__("json").dumps(payload), encoding="utf-8")
|
path.write_text(__import__("json").dumps(payload), encoding="utf-8")
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from pam_deploy_graph.script_runner import ScriptActionRunner
|
from pam_deploy_graph.script_runner import ScriptActionRunner
|
||||||
|
|
||||||
|
|
||||||
@ -46,3 +48,23 @@ def test_build_powershell_action_command():
|
|||||||
"-RollbackStopFirst",
|
"-RollbackStopFirst",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_shell_rollback_uses_query_parameters_not_form_body():
|
||||||
|
text = Path("doc_scripts/deploy.sh").read_text(encoding="utf-8")
|
||||||
|
start = text.index("rollback_ip()")
|
||||||
|
end = text.index("run_manual_rollback()", start)
|
||||||
|
rollback_block = text[start:end]
|
||||||
|
|
||||||
|
assert "/api/mcp/version/upgrade/rollback?${rollback_query}" in rollback_block
|
||||||
|
assert "application/x-www-form-urlencoded" not in rollback_block
|
||||||
|
|
||||||
|
|
||||||
|
def test_powershell_rollback_uses_query_parameters_not_form_body():
|
||||||
|
text = Path("doc_scripts/deploy.ps1").read_text(encoding="utf-8")
|
||||||
|
start = text.index("function Invoke-Rollback")
|
||||||
|
end = text.index("function Invoke-IpDeploy", start)
|
||||||
|
rollback_block = text[start:end]
|
||||||
|
|
||||||
|
assert "/api/mcp/version/upgrade/rollback?$query" in rollback_block
|
||||||
|
assert "-Body $body" not in rollback_block
|
||||||
|
assert "application/x-www-form-urlencoded" not in rollback_block
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user