@alipay/alipay-aipay 1.7.6 → 1.7.8
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 +3 -3
- package/skill/VERSION +1 -1
- package/skill/references/normal/alipay-cli-env.md +2 -2
- package/skill/references/onboarding/flow.md +2 -2
- package/skill/references/onboarding/modules/authorization.md +1 -1
- package/skill-pre/VERSION +1 -1
- package/skill-pre/references/integration/flow.md +3 -3
- package/skill-pre/references/integration/modules/checklist.md +2 -2
- package/skill-pre/references/integration/modules/sandbox/a2m-sandbox-test.md +2 -2
- package/skill-pre/references/integration/modules/sandbox/sandbox-setup-guide.md +7 -7
- package/skill-pre/references/integration/modules/scripts/sandbox_config.sh +4 -10
- package/skill-pre/references/normal/alipay-cli-env.md +5 -5
- package/skill-pre/references/normal/conversation-telemetry.md +1 -1
- package/skill-pre/references/normal/full-process-routing.md +2 -1
- package/skill-pre/references/normal/scripts/runtime.mjs +2 -2
- package/skill-pre/references/normal/scripts/telemetry.mjs +4 -4
- package/skill-pre/references/normal/scripts/windows/access-native.mjs +3 -0
- package/skill-pre/references/normal/scripts/windows/architecture.mjs +2 -30
- package/skill-pre/references/normal/scripts/windows/bootstrap-node.ps1 +1 -3
- package/skill-pre/references/normal/scripts/windows/git-discovery.mjs +1 -1
- package/skill-pre/references/normal/scripts/windows/git-install.mjs +1 -1
- package/skill-pre/references/normal/scripts/windows/host.mjs +31 -0
- package/skill-pre/references/normal/scripts/windows/preflight.mjs +14 -16
- package/skill-pre/references/normal/scripts/windows/prepare.mjs +1 -1
- package/skill-pre/references/normal/scripts/windows/python-execution.mjs +6 -6
- package/skill-pre/references/normal/scripts/windows/python-runtime.mjs +4 -4
- package/skill-pre/references/normal/scripts/windows/routing.mjs +7 -6
- package/skill-pre/references/normal/scripts/windows/shell_helpers.sh +2 -2
- package/skill-pre/references/normal/scripts/windows/winget.mjs +1 -1
- package/skill-pre/references/normal/windows-environment.md +12 -12
- package/skill-pre/references/onboarding/flow.md +2 -3
- package/skill-pre/references/onboarding/modules/authorization.md +2 -2
- package/skill-pre/references/onboarding/modules/scripts/app.sh +5 -5
- package/skill-pre/references/onboarding/modules/scripts/ar_sign_apply.sh +1 -1
- package/skill-pre/references/onboarding/modules/scripts/auth.sh +5 -5
- package/skill-pre/references/runtime-entry.md +1 -1
- package/skill-pre/references/normal/scripts/windows/acl-native.mjs +0 -3
- package/skill-pre/references/normal/scripts/windows/acl.mjs +0 -41
- package/skill-pre/references/normal/scripts/windows/architecture.ps1 +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alipay/alipay-aipay",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.8",
|
|
4
4
|
"description": "支付宝 AI 付接入 Skill 安装包 - 优先安装 Agent Skill,并在 macOS/Linux 后台准备用户级 CLI 工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"typescript": "^5.3.3"
|
|
56
56
|
},
|
|
57
57
|
"alipayAipayChannels": {
|
|
58
|
-
"stable": "1.7.
|
|
59
|
-
"pre": "1.7.
|
|
58
|
+
"stable": "1.7.4",
|
|
59
|
+
"pre": "1.7.7-pre"
|
|
60
60
|
}
|
|
61
61
|
}
|
package/skill/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.7.
|
|
1
|
+
1.7.4
|
|
@@ -141,12 +141,12 @@ source ~/.bashrc # Linux/bash
|
|
|
141
141
|
|
|
142
142
|
> ⚠️ **强制规范:检测结果仅供内部使用,禁止向用户输出!**
|
|
143
143
|
|
|
144
|
-
AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM
|
|
144
|
+
AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM`。`run_id` 在根意图与产品首次唯一确定时由 runtime 生成 UUID;同一组合的 active run 复用,组合变化或旧 run 已关闭时生成新 UUID,第三方项目 ID 不参与生成。`common.sh` 为业务 `PLATFORM_ID` 优先使用有效的第三方项目 ID(`COZE_PROJECT_ID`、`MEOO_PROJECT_ID` 或 MIAODA 的 `VITE_APP_ID`/`PLATFORM_ID`),缺失时回退当前 `run_id`;遥测开关不改变该回退规则。脚本还从当前生效 Skill 的 `VERSION` 和同源 `machineUserID` 动态生成 `ALIPAYCLI_BIZ_INFO={"skill":"alipay-aipay","skill_version":"v<当前 VERSION>","skill_user_id":"machine_<32位匿名机器哈希>"}`;极端环境无法取得合法 `machine_` 标识时只省略 `skill_user_id`,不写 `anonymous_uid` 且不阻断业务调用。所有携带 `PLATFORM` 的 Skill 业务 CLI 调用均携带该 JSON,外部同名值会被覆盖;`alipay-cli version` 等不携带 `PLATFORM` 的安装和环境探测不注入该变量。产品确认后,Onboarding 还严格映射 `aipay|webpay|apppay` 为 `PRODUCT=AIPAY|WEBPAY|APPPAY`,其环境检查、登录、查询、写入和上传均携带该值,但 `PRODUCT` 和 `ALIPAYCLI_BIZ_INFO` 都不写入 MCP JSON;Integration 产品确定前的环境检查和快速沙箱不传 `PRODUCT`。
|
|
145
145
|
|
|
146
146
|
```
|
|
147
147
|
❌ 禁止:向用户输出"环境检查完成,检测到您正在使用 Claude Code 环境"等检测结果
|
|
148
148
|
❌ 禁止:向用户透出任何关于 AI 编程工具检测的信息
|
|
149
|
-
✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID`
|
|
149
|
+
✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID` 优先使用有效第三方项目 ID,缺失时回退当前 `run_id`;`ALIPAYCLI_BIZ_INFO` 由当前 Skill 版本和匿名 `skill_user_id` 派生,Onboarding 的 `PRODUCT` 由已确认产品映射
|
|
150
150
|
✅ 正确:静默完成检测,直接进入下一步流程
|
|
151
151
|
```
|
|
152
152
|
|
|
@@ -153,7 +153,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-execute-read
|
|
|
153
153
|
|
|
154
154
|
若组合动作本身不可用,原子 fallback 固定为先执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow onboarding execute --product-type "$PRODUCT_TYPE" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`,再执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" env check --product-type "$PRODUCT_TYPE" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`;fallback 不得重复 stage 或重复环境检查。
|
|
155
155
|
|
|
156
|
-
签约脚本仍通过 `error_handler.sh -> common.sh` 初始化 `DEV_TOOL_NAME`、`PLATFORM
|
|
156
|
+
签约脚本仍通过 `error_handler.sh -> common.sh` 初始化 `DEV_TOOL_NAME`、`PLATFORM`、优先取有效第三方项目 ID、缺失时取当前 `run_id` 的 `PLATFORM_ID`、当前 Skill 版本和匿名 `skill_user_id` 的 `ALIPAYCLI_BIZ_INFO`,以及当前产品 `PRODUCT`。有任务工具时只内部记录实际阶段,不维护任务数量;只保留当前对话最小上下文,不创建业务进度文件。
|
|
157
157
|
|
|
158
158
|
本步骤成功时不输出“环境检查完成”“任务已创建”等对客消息,直接进 Step 3;只有安装失败、权限不足或缺工具时才输出实际问题和恢复动作。工具能力差异不得省略任务、步骤或完成状态。
|
|
159
159
|
|
|
@@ -242,7 +242,7 @@ Step 5 直接使用对应 phase 的完整命令;仅出现脚本异常、需要
|
|
|
242
242
|
2. 服务:仅按量付费由 runner 执行原 `service.sh list`;其他产品 envelope 固定为 `NOT_APPLICABLE`。成功候选只取 envelope 的 `service.candidates`。
|
|
243
243
|
3. 应用:runner 执行原 `app.sh list --product-type "$PRODUCT_TYPE" --sales-code "$SALES_CODE"`。成功候选和 `WEBAPP|MOBILEAPP` 只取 envelope 的 application 分支;不得宣称返回应用已与当前支付产品绑定。
|
|
244
244
|
|
|
245
|
-
> ⚠️ 脚本通过 `error_handler.sh` 间接初始化 `DEV_TOOL_NAME
|
|
245
|
+
> ⚠️ 脚本通过 `error_handler.sh` 间接初始化 `DEV_TOOL_NAME`、优先取有效第三方项目 ID、缺失时取当前 `run_id` 的 `PLATFORM_ID`、当前 Skill 版本和匿名 `skill_user_id` 对应的 `ALIPAYCLI_BIZ_INFO`,以及 CLI `PRODUCT`;签约查询必须传入当前产品的 `--sales-code` 和 `--product-type`(aipay|webpay|apppay)做一致性校验。AI 网页应用收款和 AI 移动应用收款使用不同 salesCode,禁止混用。
|
|
246
246
|
|
|
247
247
|
各适用查询仍是独立 CLI/MCP 调用,runner 只编排现有脚本,必须分别完成原有错误检测,禁止伪造合并 MCP。失败后按错误类型处理:
|
|
248
248
|
|
|
@@ -32,7 +32,7 @@ CLI `login --complete` 成功只表示页面侧确认完成,不代表本机已
|
|
|
32
32
|
|
|
33
33
|
授权页只允许 `https://aipay.alipay.com/cli-auth`,必含唯一 `deviceCode/productCode/mccCode`,只允许可选 `platform`;`mccContextReceipt` 只用于本地上下文校验和短时状态,不进入授权 URL。禁止 CLI `verification_url`、额外参数、重复参数和 fragment。页面 stdout 必须先作为独立对客消息送达,再执行 `auth.sh open` 和 `auth.sh wait`,后续 pending 或收口不得合并进同一回复。
|
|
34
34
|
|
|
35
|
-
`wait` 生产最多查询 12 次,未成功的相邻查询间隔 5
|
|
35
|
+
`wait` 生产最多查询 12 次,未成功的相邻查询间隔 5 秒,成功或过期立即停止。耗尽后,Agent 先按统一入口执行 `count-user-input` 计入这条用户消息,再按 flow 登记命令把原始回复和当前 `USER_INPUT_ID` 交给 `onboarding-recovery auth-confirm`;runner 只复用统一肯定语义判定器,不再计数。接受 `继续`、`好了`、`已完成`、`确认`、`OK` 和兼容输入 `1` 时,每次只执行一次 `auth.sh confirm`,不重启自动轮询。否定、疑问、取消、修改意见或含糊输入不查询。
|
|
36
36
|
|
|
37
37
|
授权对客正文只由 `auth.sh` 调用消息目录生成。初始页面不得提前要求回复完成;pending 的自然语言提示、允许回复和授权页返回提示都不在本模块维护副本。
|
|
38
38
|
|
package/skill-pre/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.7.
|
|
1
|
+
1.7.7-pre
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- ALIPAY_AIPAY_FLOW:INTEGRATION_CORE:START -->
|
|
2
2
|
# 支付产品-代码开发流程
|
|
3
3
|
|
|
4
|
-
本 flow 的 Windows READY/LEGACY/CODE_ONLY 指根入口的唯一兼容环境分流事实,或 Windows 环境文档明确修复后唯一、根意图匹配的新 READY 事实;BLOCKED 停在环境恢复点,不进入本 flow。后续命令附加当前有效的 `--windows-route`。Windows READY 通过 Git Bash 执行登记的快速沙箱脚本;Windows LEGACY/CODE_ONLY 保留原 Node 独立开发和手工沙箱分支。三者都由当前 Agent 串行执行,不启用 Integration Worker。Windows
|
|
4
|
+
本 flow 的 Windows READY/LEGACY/CODE_ONLY 指根入口的唯一兼容环境分流事实,或 Windows 环境文档明确修复后唯一、根意图匹配的新 READY 事实;BLOCKED 停在环境恢复点,不进入本 flow。后续命令附加当前有效的 `--windows-route`。Windows READY 通过 Git Bash 执行登记的快速沙箱脚本;Windows LEGACY/CODE_ONLY 保留原 Node 独立开发和手工沙箱分支。三者都由当前 Agent 串行执行,不启用 Integration Worker。Windows 沙箱项目配置不要求精确 NTFS ACL 白名单;仍校验字段、项目实际加载路径及防误提交。登录凭据与生产配置沿各自动作的保护规则处理。CODE_ONLY 允许 `integration_only/full_process` 在可选工具故障时继续代码开发,沙箱保持 `VERIFY_PENDING`;完整接入完成代码部分后停在开通前,不把 CODE_ONLY 用于 Onboarding。根意图变化必须重跑 Windows 入口。
|
|
5
5
|
|
|
6
6
|
> ⚠️ **前置声明**:本 flow 仅支持 **AI 网页应用收款、AI 移动应用收款、AI 按量付费**三种产品的集成。原有“网站支付”“APP 支付”“按量付费”仍作为输入别名和资源目录名。**其他产品(当面付、订单码支付、JSAPI支付、预授权支付、商家扣款等)暂不支持**,如需集成请前往[支付宝开放平台](https://open.alipay.com/)查阅相关文档。
|
|
7
7
|
|
|
@@ -156,7 +156,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message loc
|
|
|
156
156
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" sandbox ensure "<规范化项目路径>" "<已确认服务端语言>" [--telemetry-run-id "$RUN_ID" --telemetry-history-count "$HISTORY_COUNT"] --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
4. Windows LEGACY/CODE_ONLY 不调用快速沙箱脚本。普通代码开发或完整接入请求没有明确要求当前配置沙箱时,不展示申领消息、不补问,直接记录 `sandboxConfigState=VERIFY_PENDING` 并继续步骤 2
|
|
159
|
+
4. Windows LEGACY/CODE_ONLY 不调用快速沙箱脚本。普通代码开发或完整接入请求没有明确要求当前配置沙箱时,不展示申领消息、不补问,直接记录 `sandboxConfigState=VERIFY_PENDING` 并继续步骤 2。只有用户明确要求现在配置/恢复沙箱时才执行下列既有标准消息 `sandbox.windows.manual_setup`;该消息使用自由资料回复,不要求用户输入 `1` 或自行执行脚本。随后只处理用户针对本轮该消息提供的资料:完整有效时写入目标项目实际使用的本机配置并记录 `READY`,部分或错误时使用既有受控失败分支补问实际缺失/错误字段:
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
162
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message windows-sandbox-setup --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
@@ -328,7 +328,7 @@ curl -sL "<当前契约 sourceUrl 中的官方页面>"
|
|
|
328
328
|
|
|
329
329
|
开始执行代码自检、语法/静态检查或支付配置后置校验前,进入本 flow 的 `verification` 阶段;该阶段不再单独记录 telemetry 开始态,只由最终收口阶段记录结果。
|
|
330
330
|
|
|
331
|
-
**支付配置接入与后置校验**:`sandboxConfigState=READY` 时,Unix/macOS/Linux 或 Windows READY 快速沙箱由商家服务端代码使用当前语言的标准 JSON 解析器,在运行时读取步骤 1/3 已验证的 `.alipay-sandbox.json`,并从 `appIds[0]` 映射 `appId`、`alipayPublicKey` 和当前语言私钥字段;不得先用 `jq`、`cat` 或临时脚本把字段原文打印到工具输出、复制到源码或重复写入 `.env`。项目已有配置抽象时在其服务端加载边界接入该 JSON,不改变字段原值。Windows LEGACY/CODE_ONLY 从步骤 1 由 Agent
|
|
331
|
+
**支付配置接入与后置校验**:`sandboxConfigState=READY` 时,Unix/macOS/Linux 或 Windows READY 快速沙箱由商家服务端代码使用当前语言的标准 JSON 解析器,在运行时读取步骤 1/3 已验证的 `.alipay-sandbox.json`,并从 `appIds[0]` 映射 `appId`、`alipayPublicKey` 和当前语言私钥字段;不得先用 `jq`、`cat` 或临时脚本把字段原文打印到工具输出、复制到源码或重复写入 `.env`。项目已有配置抽象时在其服务端加载边界接入该 JSON,不改变字段原值。Windows LEGACY/CODE_ONLY 从步骤 1 由 Agent 写入并校验的本机沙箱配置读取。后置校验必须检查实际加载路径、字段选择、SDK 初始化和版本控制保护;Java 使用 `appPrivateKey`,其他四种语言使用 `appPrivatePkcsKey`。A2M 的 `seller_signature` 使用原生密码库时,只在调用边界按库要求解析密钥。失败时修正加载器或 SDK 配置,禁止猜测、转换格式或改写原始值。
|
|
332
332
|
|
|
333
333
|
`sandboxConfigState=CREATE_PENDING|VERIFY_PENDING` 时仍完整实现当前产品全部适用接口、安全校验和配置加载边界,但不得读取当前无效配置、写入占位 appId/密钥、把示例值当作运行配置或声称 `INT.CODE.CONFIG_VERIFIED` 通过。加载器在配置缺失或无效时必须明确失败并阻止真实 SDK 调用;本轮所有依赖沙箱值的运行验证保持未完成。
|
|
334
334
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
| 私钥仓库安全 | 私钥不得上传公共仓库 | 私钥泄露将导致应用和支付宝交互的安全性完全丧失 |
|
|
15
15
|
| 沙箱配置后置校验 | 沙箱 appId、应用私钥、支付宝公钥接入项目后必须完成配置准确性校验 | `sandboxConfigState=READY` 时,Unix/macOS/Linux 或 Windows READY 必须确认商家服务端加载器直接读取已验证的 `.alipay-sandbox.json`,从同一 `appIds[0]` 选择当前语言字段且不在源码、普通日志或额外 `.env` 留下密钥副本;Windows LEGACY/CODE_ONLY 必须确认项目实际读取值属于本次手工申领和密钥配置的同一沙箱应用。待配置时本项直接不通过,代码只能保留会在配置缺失时明确失败的加载边界,禁止占位符、示例值或其他来源密钥 |
|
|
16
16
|
| 用户项目文件保护 | 既有项目结构、文件和无关支付能力均保留;新项目只创建在尚不存在或为空的目录 | 检查本轮变更不存在未经独立二次确认的既有文件删除、既有文件整体覆盖,或与已确认目标产品无关的修改;新建文件不属于既有文件整体覆盖。发现违规立即判定不通过并停止后续流程 |
|
|
17
|
-
| 沙箱配置防提交 | Unix/macOS/Linux 或 Windows READY 快速沙箱:配置未被 Git 跟踪,项目根目录 `.gitignore` 包含精确规则 `/.alipay-sandbox.json`,Unix 配置权限为 `0600
|
|
17
|
+
| 沙箱配置防提交 | Unix/macOS/Linux 或 Windows READY 快速沙箱:配置未被 Git 跟踪,项目根目录 `.gitignore` 包含精确规则 `/.alipay-sandbox.json`,Unix 配置权限为 `0600`;Windows LEGACY/CODE_ONLY 手工沙箱:实际沙箱配置未被 Git 跟踪并具有对应忽略保护。Windows 沙箱配置不要求精确 NTFS ACL 白名单 | 避免沙箱配置随项目进入 Git 暂存区或公共仓库;缺少适用防提交保护时判定不通过 |
|
|
18
18
|
| 沙箱配置稳定定位 | Unix/macOS/Linux 或 Windows READY 项目代码从已确认的规范化项目根定位 `.alipay-sandbox.json` | 禁止依赖 `cwd`、`getcwd()`、`Directory.GetCurrentDirectory()`、`user.dir` 或 Agent 启动目录 |
|
|
19
19
|
| SDK 公钥/证书模式一致 | 公钥模式使用普通执行 API;证书执行 API 只在应用公钥证书、支付宝公钥证书和支付宝根证书均已配置时使用 | 禁止公钥配置模式误用证书执行 API |
|
|
20
20
|
| 支付校验无旁路 | 生产源码中不存在 Mock、测试或沙箱开关可触达的跳过验签、固定成功或支付校验旁路 | 沙箱与生产必须共用相同验证控制流 |
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
| 幂等履约 | 同一订单重复携带 `Payment-Proof` 时不得重复发放资源 | 应返回历史履约结果或可重试确认结果 |
|
|
85
85
|
| 履约确认 | 资源生成后必须调用 `alipay.aipay.agent.fulfillment.confirm` | 确认成功后再标记订单 `FULFILLED` 并返回成功交付 |
|
|
86
86
|
| 自动联调成功证据 | 必须同时具备 HTTP 200、非空 `resource_id` / `resourceId`、非空 `content`、无明确业务失败,以及当前五语言示例有效的 `Payment-Validation` | 204、空体、空对象、`VERIFY_FAILED`、履约失败或异常响应头均不得判为通过 |
|
|
87
|
-
| 敏感过程产物 |
|
|
87
|
+
| 敏感过程产物 | 单次联调仅使用本次临时目录,Unix/macOS/Linux 的目录/文件权限不得宽于 `0700/0600`;Windows 检查实际可写和非符号链接,不以 ACL 白名单为完成条件;命令结束时立即清理 | 禁止打印 Payment-Proof 或 Payment-Validation 原始值;失败后重新执行完整联调 |
|
|
88
88
|
| 沙箱 serviceId | 沙箱联调的 `serviceId` 固定为 `api_mock_service_id` | 不向用户索要正式 `serviceId`,不使用其他占位符 |
|
|
89
89
|
| 沙箱空字段边界 | 沙箱联调中字段为空可用于排查定位,生产环境关键字段为空必须按异常处理 | 不得把沙箱容错逻辑直接迁移成生产跳过校验 |
|
|
90
90
|
| 生产实现完整性 | 订单持久化、本地订单匹配、金额一致性、资源防串、幂等履约、待确认状态和确认失败重试均已实际实现 | 任一关键控制仍为 TODO、注释、内存演示或伪代码时只能判定沙箱演示可测试,不得判定生产就绪 |
|
|
@@ -192,7 +192,7 @@ Payment-Proof 参考结构:
|
|
|
192
192
|
|
|
193
193
|
> 当前 `serviceId=api_mock_service_id` 仅用于沙箱联调。正式上线前,必须替换为支付宝服务市场注册或复用服务后实际返回的真实 `serviceId`。
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
脚本在单次临时目录传递敏感中间值;Unix/macOS/Linux 校验目录/文件模式位不宽于 `0700/0600`,Windows 在启动 Python 前确认目录实际可写,不将其解释为 ACL 已安全配置。命令退出前清理,该目录不用于用户进度或后续恢复;Agent 不向用户索要、展示或复用其中的 Payment-Proof。
|
|
196
196
|
|
|
197
197
|
**沙箱支付宝下载红线(Agent 内部约束,不要作为用户提示逐字输出):**
|
|
198
198
|
- 默认只提供上方沙箱支付宝安装直链,复制到网页浏览器打开,并使用支付宝客户端扫码下载。
|
|
@@ -207,7 +207,7 @@ Payment-Proof 参考结构:
|
|
|
207
207
|
|
|
208
208
|
1. **沙箱收银接口**:脚本默认使用 `http://aicashier.dl.alipaydev.com/openclawpay/agent/v1/pay`,此地址已硬编码在脚本中
|
|
209
209
|
2. **付款链接**:前缀为 `https://render.alipay.com/p/yuyan/180020010001290755/pay.html?schema=`,通过 URL 编码的 `payScheme` 拼接
|
|
210
|
-
3.
|
|
210
|
+
3. **过程产物**:只在当前命令的系统临时专用目录中短时存在;Unix/macOS/Linux 校验目录/文件 `0700/0600`,Windows 校验实际可写和非符号链接,不验证 ACL 白名单;命令成功、失败或异常退出都执行清理,不提供自定义目录和跨轮恢复
|
|
211
211
|
4. **买家签名**:默认占位值 `-`,无需修改
|
|
212
212
|
5. **沙箱化 Agent 网络权限**:脚本内 `fetch_payment_needed` 使用 curl,且收银测试会访问沙箱接口;沙箱化 Agent 环境首次执行即申请可联网权限
|
|
213
213
|
6. **沙箱支付宝安装包**:沙箱支付宝当前仅支持安卓系统;默认引导用户复制 `https://mdn.alipayobjects.com/sandboxsys/afts/img/_itHRrdOD9oAAAAAAAAAAAAADgSLAQBr/original` 到网页浏览器打开,并使用支付宝客户端扫码下载。若用户明确反馈该直链、页面二维码无法打开或无法下载,只能引导用户前往支付宝开放平台沙箱工具页 `https://open.alipay.com/develop/sandbox/tool`,按页面指引安装沙箱版支付宝。严禁引导用户使用 iOS 沙箱支付宝、应用市场搜索、`https://sandbox.alipay.com` 或其他自编下载 URL。
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
- ✅ **安全隔离**:测试环境不涉及真实资金,可放心调试
|
|
13
13
|
|
|
14
14
|
### 1.2 Windows LEGACY/CODE_ONLY 用户注意事项
|
|
15
|
-
Windows READY 通过兼容层执行 flow 登记的快速沙箱;下述手工分支适用于 Windows LEGACY/CODE_ONLY;CODE_ONLY
|
|
15
|
+
Windows READY 通过兼容层执行 flow 登记的快速沙箱;下述手工分支适用于 Windows LEGACY/CODE_ONLY;CODE_ONLY 只放行代码开发,不代表站点开通选择。Windows 沙箱项目配置不要求精确 NTFS ACL 白名单,配置是否可用仍由字段校验、实际项目加载路径和防误提交检查决定。
|
|
16
16
|
|
|
17
|
-
Windows LEGACY/CODE_ONLY 普通代码开发或完整接入请求未明确要求当前配置沙箱时,记录 `sandboxConfigState=VERIFY_PENDING` 并继续代码实现,不展示资料收集消息。只有用户明确要求现在配置或恢复 Windows LEGACY/CODE_ONLY 沙箱时,才使用 `../../../normal/json/customer-messages.json` 的既有 `sandbox.windows.manual_setup`,引导用户前往支付宝 AI 付站点的“选择产品 → 一站式接入 → 技术集成”页面获取当前沙箱的 APPID
|
|
17
|
+
Windows LEGACY/CODE_ONLY 普通代码开发或完整接入请求未明确要求当前配置沙箱时,记录 `sandboxConfigState=VERIFY_PENDING` 并继续代码实现,不展示资料收集消息。只有用户明确要求现在配置或恢复 Windows LEGACY/CODE_ONLY 沙箱时,才使用 `../../../normal/json/customer-messages.json` 的既有 `sandbox.windows.manual_setup`,引导用户前往支付宝 AI 付站点的“选择产品 → 一站式接入 → 技术集成”页面获取当前沙箱的 APPID、公私钥和账号信息;随后只处理用户针对本轮该消息提供的资料,完整时校验并写入目标项目实际使用的本地沙箱配置,部分或错误时只补问对应字段。不得要求用户自行配置、理解 `sandbox_config.sh`、执行 Bash 或手工拼接 Skill 的规范化 JSON。该资料输入例外只适用于沙箱,生产环境应用私钥和密码禁止提供给 Agent;Agent 不得在回复、摘要或普通日志中复述收到的值。
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message windows-sandbox-setup --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
@@ -57,7 +57,7 @@ Unix/macOS/Linux 或 Windows READY 快速沙箱由 `sandbox_config.sh` 在内部
|
|
|
57
57
|
1. 提取候选 `data`。
|
|
58
58
|
2. 按第 4 节逐项校验必含字段。
|
|
59
59
|
3. 校验通过后,将候选 `data` 按原始 JSON 结构写入正式本地配置文件。
|
|
60
|
-
4. 确认配置未被 Git 跟踪、项目根目录 `.gitignore` 已包含 `/.alipay-sandbox.json
|
|
60
|
+
4. 确认配置未被 Git 跟踪、项目根目录 `.gitignore` 已包含 `/.alipay-sandbox.json`;Unix/macOS/Linux 确认权限为 `0600`,Windows 确认文件实际可访问且不是符号链接。创建阶段只通过带 `ALIPAY_AIPAY_INTERNAL:` 前缀的 stderr 内部事实返回机器标记和实际配置文件路径,不对客展示关键字段摘要。
|
|
61
61
|
5. 后续由商家服务端代码使用当前语言标准 JSON 解析器在运行时读取该文件并映射到 SDK;Agent 不通过 shell 输出字段原文,也不把沙箱密钥复制到源码或重复写入 `.env`。
|
|
62
62
|
6. 代码开发完成且配置后置校验通过后,再使用第 5.2 节的摘要输出入口对客展示沙箱环境配置。
|
|
63
63
|
|
|
@@ -65,8 +65,8 @@ Windows LEGACY/CODE_ONLY 手工沙箱:
|
|
|
65
65
|
|
|
66
66
|
1. 没有资料且没有明确配置请求时记录 `VERIFY_PENDING` 并继续代码实现,不渲染消息、不探测猜测配置。
|
|
67
67
|
2. 用户明确要求配置且尚未提供资料时,才使用标准消息一次收集当前沙箱应用的 APPID、支付宝公钥、应用公钥、与已确认语言匹配的应用私钥和沙箱账号信息;缺失或校验失败时只补问对应字段。
|
|
68
|
-
3. 按第 4 节校验资料,随后由 Agent
|
|
69
|
-
4.
|
|
68
|
+
3. 按第 4 节校验资料,随后由 Agent 写入目标项目实际使用的本地沙箱配置;不要求采用快速沙箱 `data` 的嵌套结构,不把值复制到源码或普通日志。
|
|
69
|
+
4. 检查沙箱配置未被 Git 跟踪,并已有适用于实际路径的版本控制忽略保护;不因项目目录继承额外 Windows ACL 权限而阻断沙箱配置。
|
|
70
70
|
5. 只有 `READY` 时才输出第 5 节规定的摘要和 Windows LEGACY/CODE_ONLY 提醒并从实际本机配置映射到 SDK;Agent 不得在回复或摘要中复述用户提供的沙箱密钥和密码,生产环境应用私钥和密码禁止提供给 Agent。
|
|
71
71
|
|
|
72
72
|
Unix/macOS/Linux 或 Windows READY 快速沙箱的流程准备必须使用 integration flow 登记的 `sandbox_config.sh ensure` 入口:配置缺失时脚本内部创建,已存在时脚本内部复核;步骤 3 仅在首次准备为 `READY` 时执行 `reverify`,待配置时直接继续代码开发;代码开发完成且配置仍为 `READY` 后才使用 `summary` 展示摘要。`ensure|reverify` 会把既定重试耗尽转换为受控 `CREATE_PENDING|VERIFY_PENDING`,直接渲染统一消息并返回终态标记;Agent 不得直接选择创建分支、重复检查文件存在性或权限、读取字段结构、拼接沙箱 JSON、另写解析逻辑或自制摘要表。Windows LEGACY/CODE_ONLY 手工申领分支检查目标项目实际读取的本地配置,不把平台字段擅自改造成快速沙箱返回结构。
|
|
@@ -128,8 +128,8 @@ Unix/macOS/Linux 或 Windows READY 快速沙箱:
|
|
|
128
128
|
Windows LEGACY/CODE_ONLY 手工沙箱:
|
|
129
129
|
|
|
130
130
|
- 由 Agent 根据目标项目实际结构选择并写入本机敏感配置路径,不要求用户创建 `.alipay-sandbox.json` 或自行配置项目。
|
|
131
|
-
- 配置必须未被 Git
|
|
132
|
-
- 只有实际检查到的字段才能进入后续 SDK
|
|
131
|
+
- 配置必须未被 Git 跟踪,并建立适用于该实际路径的忽略保护;Windows 不以 POSIX `0600` 或精确 NTFS ACL 白名单作为沙箱完成条件。
|
|
132
|
+
- 只有实际检查到的字段才能进入后续 SDK 映射;无法自动核验的平台页面信息标记为人工待验证,项目继承的额外 Windows ACL 权限不构成缺口。
|
|
133
133
|
|
|
134
134
|
Unix/macOS/Linux 或 Windows READY 创建阶段把实际本机配置路径和 `FLOW:SANDBOX_CONFIG_READY` 作为带 `ALIPAY_AIPAY_INTERNAL:` 前缀的 stderr 内部事实,stdout 为空;不展示账号、密码、公钥或摘要。Windows LEGACY/CODE_ONLY 手工分支完成后只在内部保留实际本机配置路径。两条分支在代码开发完成且配置后置校验通过后,才进入摘要展示。
|
|
135
135
|
|
|
@@ -80,11 +80,6 @@ if [ "$MODE" = "summary" ]; then
|
|
|
80
80
|
fi
|
|
81
81
|
case "$PROJECT_PATH" in /*) ;; *) echo "SANDBOX_ERROR:项目路径必须为绝对路径" >&2; exit 2 ;; esac
|
|
82
82
|
[ -d "$PROJECT_PATH" ] && [ ! -L "$PROJECT_PATH" ] || { echo "SANDBOX_ERROR:项目目录不可访问或不安全" >&2; exit 2; }
|
|
83
|
-
if declare -F windows_protect >/dev/null; then
|
|
84
|
-
windows_probe=$(mktemp "${PROJECT_PATH}/.windows-acl-probe.XXXXXX") || exit 78
|
|
85
|
-
windows_protect "$windows_probe" || { rm -f "$windows_probe"; exit 78; }
|
|
86
|
-
rm -f "$windows_probe" || exit 78
|
|
87
|
-
fi
|
|
88
83
|
detected=()
|
|
89
84
|
find "$PROJECT_PATH" -maxdepth 4 -type f \( -name pom.xml -o -name build.gradle -o -name build.gradle.kts \) -print -quit | grep -q . && detected+=(Java)
|
|
90
85
|
find "$PROJECT_PATH" -maxdepth 4 -type f -name package.json -print -quit | grep -q . && detected+=(Node.js)
|
|
@@ -199,7 +194,7 @@ ensure_config_not_tracked() {
|
|
|
199
194
|
}
|
|
200
195
|
|
|
201
196
|
ensure_config_permissions() {
|
|
202
|
-
if declare -F
|
|
197
|
+
if declare -F windows_check_access >/dev/null; then windows_check_access "$CONFIG_PATH"; return $?; fi
|
|
203
198
|
local mode
|
|
204
199
|
chmod 600 "$CONFIG_PATH" 2>/dev/null || return 1
|
|
205
200
|
if mode=$(stat -f '%Lp' "$CONFIG_PATH" 2>/dev/null); then
|
|
@@ -321,24 +316,23 @@ if [ "$MODE" = "create" ]; then
|
|
|
321
316
|
validate_data "$data" || { echo "SANDBOX_ERROR:候选 data 缺少当前语言或测试账号必含字段" >&2; exit 1; }
|
|
322
317
|
temp=$(mktemp "${PROJECT_PATH}/.alipay-sandbox.XXXXXX") || exit 1
|
|
323
318
|
chmod 600 "$temp" 2>/dev/null || true
|
|
324
|
-
if declare -F windows_protect >/dev/null; then windows_protect "$temp" || { rm -f "$temp"; exit 78; }; fi
|
|
325
319
|
printf '%s\n' "$data" >"$temp" || { rm -f "$temp"; exit 1; }
|
|
326
320
|
mv "$temp" "$CONFIG_PATH" || { rm -f "$temp"; exit 1; }
|
|
327
|
-
ensure_config_permissions || { echo "SANDBOX_ERROR
|
|
321
|
+
ensure_config_permissions || { echo "SANDBOX_ERROR:沙箱配置文件不可访问或权限不符合当前系统要求" >&2; exit 1; }
|
|
328
322
|
elif [ "$MODE" = "verify" ]; then
|
|
329
323
|
[ -f "$CONFIG_PATH" ] && [ ! -L "$CONFIG_PATH" ] || { echo "SANDBOX_ERROR:配置文件不存在或不安全" >&2; exit 1; }
|
|
330
324
|
ensure_config_not_tracked || { echo "SANDBOX_ERROR:沙箱配置已被 Git 跟踪,必须先从版本控制中移除" >&2; exit 1; }
|
|
331
325
|
ensure_gitignore_protection || { echo "SANDBOX_ERROR:无法为沙箱配置建立 Git 忽略保护" >&2; exit 1; }
|
|
332
326
|
data=$(jq -c . "$CONFIG_PATH" 2>/dev/null) || { echo "SANDBOX_ERROR:配置不是合法 JSON" >&2; exit 1; }
|
|
333
327
|
validate_data "$data" || { echo "SANDBOX_ERROR:配置缺少当前语言或测试账号必含字段" >&2; exit 1; }
|
|
334
|
-
ensure_config_permissions || { echo "SANDBOX_ERROR
|
|
328
|
+
ensure_config_permissions || { echo "SANDBOX_ERROR:沙箱配置文件不可访问或权限不符合当前系统要求" >&2; exit 1; }
|
|
335
329
|
elif [ "$MODE" = "summary" ]; then
|
|
336
330
|
[ -f "$CONFIG_PATH" ] && [ ! -L "$CONFIG_PATH" ] || { echo "SANDBOX_ERROR:配置文件不存在或不安全" >&2; exit 1; }
|
|
337
331
|
ensure_config_not_tracked || { echo "SANDBOX_ERROR:沙箱配置已被 Git 跟踪,必须先从版本控制中移除" >&2; exit 1; }
|
|
338
332
|
ensure_gitignore_protection || { echo "SANDBOX_ERROR:无法为沙箱配置建立 Git 忽略保护" >&2; exit 1; }
|
|
339
333
|
data=$(jq -c . "$CONFIG_PATH" 2>/dev/null) || { echo "SANDBOX_ERROR:配置不是合法 JSON" >&2; exit 1; }
|
|
340
334
|
validate_data "$data" || { echo "SANDBOX_ERROR:配置缺少当前语言或测试账号必含字段" >&2; exit 1; }
|
|
341
|
-
ensure_config_permissions || { echo "SANDBOX_ERROR
|
|
335
|
+
ensure_config_permissions || { echo "SANDBOX_ERROR:沙箱配置文件不可访问或权限不符合当前系统要求" >&2; exit 1; }
|
|
342
336
|
else
|
|
343
337
|
echo "SANDBOX_ERROR:用法 sandbox_config.sh <ensure|reverify|create|verify> <projectPath> <language> 或 sandbox_config.sh summary <productType> <projectPath> <language>" >&2
|
|
344
338
|
exit 2
|
|
@@ -143,21 +143,21 @@ source ~/.bashrc # Linux/bash
|
|
|
143
143
|
|
|
144
144
|
> ⚠️ **强制规范:检测结果仅供内部使用,禁止向用户输出!**
|
|
145
145
|
|
|
146
|
-
AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM
|
|
146
|
+
AI 工具由 `scripts/detect_dev_tool.sh` 检测;`init_alipay_cli_context` 设置 `DEV_TOOL_NAME`、`PLATFORM`。`run_id` 在根意图与产品首次唯一确定时由 runtime 生成 UUID;同一组合的 active run 复用,组合变化或旧 run 已关闭时生成新 UUID,第三方项目 ID 不参与生成。`common.sh` 为业务 `PLATFORM_ID` 优先使用有效的第三方项目 ID(`COZE_PROJECT_ID`、`MEOO_PROJECT_ID` 或 MIAODA 的 `VITE_APP_ID`/`PLATFORM_ID`),缺失时回退当前 `run_id`;遥测开关不改变该回退规则。脚本还从当前生效 Skill 的 `VERSION` 和同源 `machineUserID` 动态生成 `ALIPAYCLI_BIZ_INFO={"skill":"alipay-aipay","skill_version":"v<当前 VERSION>","skill_user_id":"machine_<32位匿名机器哈希>"}`;极端环境无法取得合法 `machine_` 标识时只省略 `skill_user_id`,不写 `anonymous_uid` 且不阻断业务调用。所有携带 `PLATFORM` 的 Skill 业务 CLI 调用均携带该 JSON,外部同名值会被覆盖;`alipay-cli version` 等不携带 `PLATFORM` 的安装和环境探测不注入该变量。产品确认后,Onboarding 还严格映射 `aipay|webpay|apppay` 为 `PRODUCT=AIPAY|WEBPAY|APPPAY`,其环境检查、登录、查询、写入和上传均携带该值,但 `PRODUCT` 和 `ALIPAYCLI_BIZ_INFO` 都不写入 MCP JSON;Integration 产品确定前的环境检查和快速沙箱不传 `PRODUCT`。
|
|
147
147
|
|
|
148
148
|
```
|
|
149
149
|
❌ 禁止:向用户输出"环境检查完成,检测到您正在使用 Claude Code 环境"等检测结果
|
|
150
150
|
❌ 禁止:向用户透出任何关于 AI 编程工具检测的信息
|
|
151
|
-
✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID`
|
|
151
|
+
✅ 正确:检测结果仅供内部使用,只用于脚本设置 `PLATFORM`;`PLATFORM_ID` 优先使用有效第三方项目 ID,缺失时回退当前 `run_id`;`ALIPAYCLI_BIZ_INFO` 由当前 Skill 版本和匿名 `skill_user_id` 派生,Onboarding 的 `PRODUCT` 由已确认产品映射
|
|
152
152
|
✅ 正确:静默完成检测,直接进入下一步流程
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
### 5.1
|
|
155
|
+
### 5.1 页面打开与密钥工具引导
|
|
156
156
|
|
|
157
157
|
- HTTPS 白名单通过后依次尝试 macOS `open`→`osascript open location`,或 Linux/Unix `xdg-open`→`gio open`→`sensible-browser`;Windows 保留 PowerShell。仅返回 `OPENED|OPEN_FAILED|GUI_UNAVAILABLE|LINK_ONLY`。
|
|
158
158
|
- 系统 opener 失败时,宿主内置浏览器可用则打开已校验、已交付的同一 URL 一次,不搜索、改写或自动操作;仍失败才提示手动打开/保留复制兜底,再有限轮询。不得安装工具或绕过审批;`OPEN_ERROR_KIND|OPEN_ERROR_CODE` 仅内部诊断,不算业务失败。
|
|
159
|
-
-
|
|
160
|
-
-
|
|
159
|
+
- 目标应用缺少用户已准备的应用公钥时,按 Onboarding flow 展示三种获取方式;不自动下载密钥工具。用户选择官方工具引导时,`key-tool` 只展示 `https://opendocs.alipay.com/isv/02kipk` 的手动下载安装说明;选择本地页面时,仅在用户明确选择后执行 `preview open-keygen`,打开失败走登记的手动引导。两种动作均不安装或生成密钥,Agent 只接收用户主动提供的应用公钥。
|
|
160
|
+
- 页面打开能力检测不改变 alipay-cli、MCP、业务确认或网络重试契约;宿主权限提示不是 Skill 业务确认。
|
|
161
161
|
|
|
162
162
|
---
|
|
163
163
|
|
|
@@ -94,7 +94,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry end-conversat
|
|
|
94
94
|
|
|
95
95
|
采集脱敏后的完整正文,不主动截断或摘要。
|
|
96
96
|
|
|
97
|
-
独立 conversation state 只保存随机 ID、会话 hash、去重 hash、消息序号、创建时授权 ID 和 ACTIVE/CLOSED
|
|
97
|
+
独立 conversation state 只保存随机 ID、会话 hash、去重 hash、消息序号、创建时授权 ID 和 ACTIVE/CLOSED 状态;正文仅进入当前用户的 outbox。Unix/macOS/Linux 使用 `0600` 文件模式,Windows 仅检查目录实际可写,不声称已经配置 ACL。对话事件使用 .conversation 后缀保存 JSON,让只识别 .json 的旧版 worker 跳过。对话正文和解析错误不进入 stdout/stderr。code 入队时固定,后续改配置不重标旧事件。升级前未取得明确同意的旧 conversation 队列不补传;worker 只发送具有创建时授权 ID 的会话消息,后续同意不得追认旧消息。
|
|
98
98
|
|
|
99
99
|
对话上报请求不设置脚本级网络超时(timeout=0)。采集命令完成本地状态记录和入队后,尝试启动 detached 后台 worker 并立即返回,不在前台等待网络结果。后台发送也负责失败补传;若网络一直不返回结果,仅后台请求等待,不拖住 Skill 的后续命令。前台 flush-outbox 跳过对话记录,把它们留给后台 worker,避免补传重新引入前台等待。后台 worker 启动失败时保留队列,Skill 继续;后续触发时再尝试启动,不保证最终送达。操作系统、宿主和服务端仍可能中止连接;明确失败时继续按原机制保留记录补传。本地状态写入和入队仍需要少量执行时间,不能把“不等待网络”描述为完全零耗时。
|
|
100
100
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 完整接入编排
|
|
2
2
|
|
|
3
|
-
本文件是 `full_process` 唯一入口。Windows 复用根入口的 READY/LEGACY 分流,或明确修复后唯一、根意图匹配的新 READY 事实;BLOCKED
|
|
3
|
+
本文件是 `full_process` 唯一入口。Windows 复用根入口的 READY/LEGACY/CODE_ONLY 分流,或明确修复后唯一、根意图匹配的新 READY 事实;BLOCKED 先恢复环境,不进入本流程。CODE_ONLY 只完成独立代码开发,沙箱保持 `VERIFY_PENDING`,最终如实列出未完成项并停在 Onboarding 前;不得登录、开通或宣称完整接入完成。从仅开发切换为完整接入时必须先用 `full_process` 重新执行 Windows 入口。命令携带当前有效的 `--windows-route`。READY 执行 Integration 快速沙箱及 Onboarding 本地流程,LEGACY 仅在不支持的平台映射或用户明确选站点时先完成可独立开发内容,再在原衔接点输出站点引导。三者都不重新安装、不创建 Integration Worker。启动方案送达前只复用 Integration 步骤 1 的项目、语言、当前系统分支环境和沙箱准备;不得开发代码、进入 Onboarding 或读取开通信息。
|
|
4
4
|
|
|
5
5
|
目标项目和技术栈确定后执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow integration entry --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`,只加载当前入口阶段并按步骤 1/2 准备及展示方案;代码开发满足衔接条件后执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow onboarding entry --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`。禁止整读两个 flow。所有 Skill 自有运行时动作只用当前 Skill 绝对路径下的统一入口单命令执行,不前置 `cd`、不拼接管道;用户项目命令仍以已确认项目根为 workdir。
|
|
6
6
|
|
|
@@ -86,6 +86,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message sta
|
|
|
86
86
|
|
|
87
87
|
- 启动方案送达后先完成上述 Worker 分流,再由确定的执行者进入 Integration 步骤 3;用户中断后若项目、产品、语言或框架变化,重新澄清和扫描并输出当前方案。
|
|
88
88
|
- Integration 先完整执行和校验。只有沙箱待配置,或 READY 且仅剩非阻塞人工待验证项时,保持“部分通过”进入 Onboarding;其他代码、安全或依据缺口停在 Integration,不提前登录或收集开通材料。
|
|
89
|
+
- Windows CODE_ONLY 时,即使沙箱待配置是代码部分的唯一缺口,也在 Integration 最终检查后停止;恢复可选工具并按 `full_process` 复检得到 READY,才按实际项目状态恢复沙箱和 Onboarding。
|
|
89
90
|
- 阶段间只复用有效的产品、Integration 结果、沙箱状态、人工待验证项和剩余待办,不再询问是否进入 Onboarding;Onboarding 只保留非阻塞方案展示。
|
|
90
91
|
- 授权后分别查询签约、应用和适用服务;分支独立,单分支失败不回滚其他成功,认证/授权不匹配或主体变化全局阻断。一次只收集一个类别,提前材料直接校验且不重复索取。
|
|
91
92
|
- 签约材料通过后直接提交;服务创建摘要后直创、修改仍确认;空应用候选材料完整或非空候选明确新建,重查未变即创建,以用户公钥生成官方确认页,校验成功后直接提审。均不增加额外确认点;按量付费沙箱测试仍属 Integration 默认动作。
|
|
@@ -227,14 +227,14 @@ async function dispatch(domain, args) {
|
|
|
227
227
|
if (domain === 'windows-entry') return windowsRouting.runWindowsEntry(args);
|
|
228
228
|
if (process.env.ALIPAY_AIPAY_WINDOWS_ROUTE === 'CODE_ONLY' &&
|
|
229
229
|
(domain.startsWith('onboarding-') || (domain === 'flow' && args[0] !== 'integration') ||
|
|
230
|
-
(args.includes('--root-intent') && args[args.indexOf('--root-intent') + 1]
|
|
230
|
+
(args.includes('--root-intent') && args[args.indexOf('--root-intent') + 1] === 'onboarding_only'))) {
|
|
231
231
|
windowsRouting.requireBusinessEnvironment();
|
|
232
232
|
}
|
|
233
233
|
if (process.env.ALIPAY_AIPAY_WINDOWS_ROUTE === 'BLOCKED' &&
|
|
234
234
|
['integration-bootstrap','onboarding-bootstrap','flow'].includes(domain)) windowsRouting.requireBusinessEnvironment();
|
|
235
235
|
if (['sandbox', 'mcc', 'auth', 'screenshots', 'special-license', 'sign', 'service', 'app',
|
|
236
236
|
'onboarding-discovery', 'onboarding-recovery', 'onboarding-execute-ready'].includes(domain)) {
|
|
237
|
-
windowsRouting.requireBusinessEnvironment();
|
|
237
|
+
windowsRouting.requireBusinessEnvironment({credentials: domain !== 'sandbox'});
|
|
238
238
|
}
|
|
239
239
|
if (domain === 'env' && ['prepare', 'prepare-jq', 'retry'].includes(args[0])) {
|
|
240
240
|
const { runEnvironment } = await import('./windows/prepare.mjs');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
const
|
|
3
|
-
? (await import('./windows/
|
|
2
|
+
const windowsCheckWritable = process.platform === 'win32'
|
|
3
|
+
? (await import('./windows/host.mjs')).checkWritable
|
|
4
4
|
: null;
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import fs from 'node:fs';
|
|
@@ -368,9 +368,9 @@ export function telemetryStoragePaths(env = process.env) {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
function ensurePrivateDir(dir) {
|
|
371
|
-
if (
|
|
371
|
+
if (windowsCheckWritable) {
|
|
372
372
|
fs.mkdirSync(dir, { recursive: true });
|
|
373
|
-
|
|
373
|
+
windowsCheckWritable(dir);
|
|
374
374
|
return;
|
|
375
375
|
}
|
|
376
376
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {checkWritable} from './host.mjs';
|
|
2
|
+
try { if(process.argv.length!==3)throw new Error('Writable target required');checkWritable(process.argv[2]); }
|
|
3
|
+
catch(e){process.stderr.write('ALIPAY_AIPAY_INTERNAL:WINDOWS_STORAGE_ERROR='+(e.reason||'STORAGE_FAILED')+'\n');process.exitCode=78;}
|
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {fail, run, powershell, psEnv} from './acl.mjs';
|
|
1
|
+
import {fail} from './host.mjs';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
// or environment override can select the probe. No ExecutionPolicy changes.
|
|
6
|
-
const probe = fs.readFileSync(new URL('./architecture.ps1', import.meta.url), 'utf8');
|
|
7
|
-
export const architectureCommand = `$ErrorActionPreference = 'Stop'\n${probe}\nGet-AipayNativeArchitecture`;
|
|
8
|
-
let nativeArchitecture;
|
|
9
|
-
|
|
10
|
-
function readNativeArchitecture() {
|
|
11
|
-
if (nativeArchitecture) return nativeArchitecture;
|
|
12
|
-
let result;
|
|
13
|
-
try {
|
|
14
|
-
result = run(powershell(), ['-NoProfile', '-NonInteractive', '-Command', architectureCommand],
|
|
15
|
-
{env: psEnv(), timeout: 10000});
|
|
16
|
-
} catch { fail('ARCHITECTURE_CHECK_FAILED', 'system architecture'); }
|
|
17
|
-
const value = result.stdout?.trim();
|
|
18
|
-
if (result.error || result.status !== 0 || !['x64', 'arm64', 'ia32', 'arm', 'ia64'].includes(value)) {
|
|
19
|
-
fail('ARCHITECTURE_CHECK_FAILED', 'system architecture');
|
|
20
|
-
}
|
|
21
|
-
nativeArchitecture = value;
|
|
22
|
-
return value;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function requireNativeX64({platform = process.platform, arch = process.arch,
|
|
26
|
-
readArchitecture = readNativeArchitecture} = {}) {
|
|
3
|
+
export function requireWindowsX64Node({platform = process.platform, arch = process.arch} = {}) {
|
|
27
4
|
if (platform !== 'win32') fail('WINDOWS_ADAPTER_REQUIRES_WINDOWS');
|
|
28
5
|
if (arch !== 'x64') fail('NO_COMPATIBLE_EXECUTION_MAPPING', arch);
|
|
29
|
-
let native;
|
|
30
|
-
try { native = readArchitecture(); }
|
|
31
|
-
catch { fail('ARCHITECTURE_CHECK_FAILED', 'system architecture'); }
|
|
32
|
-
if (!['x64', 'arm64', 'ia32', 'arm', 'ia64'].includes(native)) fail('ARCHITECTURE_CHECK_FAILED', 'system architecture');
|
|
33
|
-
if (native !== 'x64') fail('NO_COMPATIBLE_EXECUTION_MAPPING', native);
|
|
34
6
|
}
|
|
@@ -158,15 +158,13 @@ function Invoke-NodeBootstrap([string]$Action, [string]$Root) {
|
|
|
158
158
|
if ($MyInvocation.InvocationName -ne '.') {
|
|
159
159
|
$ErrorActionPreference = 'Stop'
|
|
160
160
|
try {
|
|
161
|
-
. (Join-Path $PSScriptRoot 'architecture.ps1')
|
|
162
|
-
if ((Get-AipayNativeArchitecture) -ne 'x64') { throw 'UNSUPPORTED_PLATFORM' }
|
|
163
161
|
$localData = $env:LOCALAPPDATA
|
|
164
162
|
if (-not $localData) { $localData = [Environment]::GetFolderPath('LocalApplicationData') }
|
|
165
163
|
if (-not $localData -or -not [IO.Path]::IsPathRooted($localData)) { throw 'STORAGE_FAILED' }
|
|
166
164
|
$result = Invoke-NodeBootstrap $Mode (Join-Path $localData 'alipay-aipay-pre/bootstrap')
|
|
167
165
|
} catch {
|
|
168
166
|
$reason = [string]$_.Exception.Message
|
|
169
|
-
if ($reason -notmatch '^(DOWNLOAD_FAILED|NO_LTS_ASSET|INTEGRITY_FAILED|VERIFY_FAILED|UNSAFE_DIRECTORY|ENVIRONMENT_BUSY|ENVIRONMENT_STATE_INVALID|RETRY_BUDGET_EXHAUSTED|
|
|
167
|
+
if ($reason -notmatch '^(DOWNLOAD_FAILED|NO_LTS_ASSET|INTEGRITY_FAILED|VERIFY_FAILED|UNSAFE_DIRECTORY|ENVIRONMENT_BUSY|ENVIRONMENT_STATE_INVALID|RETRY_BUDGET_EXHAUSTED|STORAGE_FAILED)$') { $reason = 'SETUP_FAILED' }
|
|
170
168
|
$result = @{status='BLOCKED'; reason=$reason}
|
|
171
169
|
}
|
|
172
170
|
[Console]::Error.WriteLine('ALIPAY_AIPAY_INTERNAL:NODE_BOOTSTRAP=' + ($result | ConvertTo-Json -Compress))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {run, fail} from './
|
|
3
|
+
import {run, fail} from './host.mjs';
|
|
4
4
|
const managedGitVersion=JSON.parse(fs.readFileSync(new URL('./policy.json',import.meta.url),'utf8')).git.version;
|
|
5
5
|
|
|
6
6
|
export function gitCandidates(env = process.env) {
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import {policy,run,fail} from './preflight.mjs';
|
|
5
5
|
import {findGitBash} from './git-discovery.mjs';
|
|
6
|
-
import {powershell,psEnv} from './
|
|
6
|
+
import {powershell,psEnv} from './host.mjs';
|
|
7
7
|
|
|
8
8
|
export function installGitOfficial({env=process.env,execute=run,exists=fs.existsSync,
|
|
9
9
|
read=fs.readFileSync,remove=fs.rmSync,discover=findGitBash,root,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
export function fail(reason, dependency = '') { throw Object.assign(new Error(reason), {reason, dependency}); }
|
|
7
|
+
export function run(command,args,options={}) {
|
|
8
|
+
return spawnSync(command,args,{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:4*1024*1024,...options});
|
|
9
|
+
}
|
|
10
|
+
export function powershell() {
|
|
11
|
+
const root=process.env.SystemRoot || 'C:\\Windows';
|
|
12
|
+
const command=path.join(root,'System32/WindowsPowerShell/v1.0/powershell.exe');
|
|
13
|
+
if(!fs.existsSync(command)) fail('DEPENDENCY_MISSING','PowerShell');
|
|
14
|
+
return command;
|
|
15
|
+
}
|
|
16
|
+
export function psEnv(env=process.env) {
|
|
17
|
+
return Object.fromEntries(Object.entries(env).filter(([k])=>k.toLowerCase()!=='psmodulepath'));
|
|
18
|
+
}
|
|
19
|
+
export function checkWritable(target) {
|
|
20
|
+
let stat;
|
|
21
|
+
try { stat=fs.lstatSync(target); } catch { fail('STORAGE_FAILED','missing target'); }
|
|
22
|
+
if(stat.isSymbolicLink()||path.resolve(target)===path.parse(target).root)fail('STORAGE_FAILED','unsafe target');
|
|
23
|
+
if(stat.isDirectory()) {
|
|
24
|
+
const probe=path.join(target,'.aipay-probe-'+crypto.randomUUID());
|
|
25
|
+
try { fs.writeFileSync(probe,'',{flag:'wx'});fs.unlinkSync(probe); }
|
|
26
|
+
catch { try { fs.unlinkSync(probe); } catch {} fail('STORAGE_FAILED','directory not writable'); }
|
|
27
|
+
} else if(stat.isFile()) {
|
|
28
|
+
try { fs.accessSync(target,fs.constants.W_OK); }
|
|
29
|
+
catch { fail('STORAGE_FAILED','file not writable'); }
|
|
30
|
+
} else fail('STORAGE_FAILED','unsupported target');
|
|
31
|
+
}
|
|
@@ -8,16 +8,16 @@ export const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
export const policy = JSON.parse(fs.readFileSync(path.join(directory,'policy.json'),'utf8'));
|
|
9
9
|
const slash = p => p.replaceAll('\\','/');
|
|
10
10
|
let invocationContext;
|
|
11
|
-
import {fail, run,
|
|
12
|
-
export {fail, run,
|
|
11
|
+
import {fail, run, checkWritable} from './host.mjs';
|
|
12
|
+
export {fail, run, checkWritable} from './host.mjs';
|
|
13
13
|
import {telemetryStoragePaths} from '../telemetry.mjs';
|
|
14
14
|
import {findGitBash} from './git-discovery.mjs';
|
|
15
|
-
import {
|
|
15
|
+
import {requireWindowsX64Node} from './architecture.mjs';
|
|
16
16
|
|
|
17
17
|
export function stateRoot() {
|
|
18
18
|
const root=path.join(process.env.LOCALAPPDATA || path.join(os.homedir(),'AppData/Local'),'alipay-aipay-pre/windows');
|
|
19
19
|
if(fs.existsSync(root)&&fs.lstatSync(root).isSymbolicLink()) fail('STORAGE_FAILED');
|
|
20
|
-
fs.mkdirSync(root,{recursive:true});
|
|
20
|
+
fs.mkdirSync(root,{recursive:true}); return root;
|
|
21
21
|
}
|
|
22
22
|
export function readState(root) {
|
|
23
23
|
const file=path.join(root,'environment.json');
|
|
@@ -70,8 +70,7 @@ export function locateJq() {
|
|
|
70
70
|
export function locate() {
|
|
71
71
|
if(process.platform!=='win32') fail('WINDOWS_ADAPTER_REQUIRES_WINDOWS');
|
|
72
72
|
if(Number(process.versions.node.split('.')[0])<policy.nodeMinimumMajor) fail('DEPENDENCY_VERSION','Node');
|
|
73
|
-
|
|
74
|
-
requireNativeX64();
|
|
73
|
+
requireWindowsX64Node();
|
|
75
74
|
const foundGit=findGitBash();
|
|
76
75
|
if(!foundGit) fail('DEPENDENCY_MISSING','Git Bash');
|
|
77
76
|
const {bash,git}=foundGit;
|
|
@@ -93,8 +92,7 @@ export function locate() {
|
|
|
93
92
|
env.ALIPAY_WINDOWS_CREDENTIALS_DIR=slash(path.join(os.homedir(),'.alipay-cli/credentials'));
|
|
94
93
|
env.ALIPAY_WINDOWS_LOGS_DIR=slash(path.join(process.env.APPDATA||path.join(os.homedir(),'AppData/Roaming'),'alipay-cli/data/logs'));
|
|
95
94
|
env.ALIPAY_WINDOWS_CLI_ENTRY=slash(path.join(directory,'bin/alipay-cli'));
|
|
96
|
-
env.
|
|
97
|
-
env.ALIPAY_WINDOWS_POWERSHELL=slash(powershell());
|
|
95
|
+
env.ALIPAY_WINDOWS_ACCESS_SCRIPT=slash(path.join(directory,'access-native.mjs'));
|
|
98
96
|
env.NODE_BINARY='node';
|
|
99
97
|
return {bash,git,cli,jq,env};
|
|
100
98
|
}
|
|
@@ -114,12 +112,8 @@ export function privateChildEnvironment(env, temporary) {
|
|
|
114
112
|
|
|
115
113
|
export function inspectLocal(root) {
|
|
116
114
|
const context=locate();
|
|
117
|
-
const temporary=path.join(root,'tmp');fs.mkdirSync(temporary,{recursive:true});
|
|
115
|
+
const temporary=path.join(root,'tmp');fs.mkdirSync(temporary,{recursive:true});
|
|
118
116
|
context.env = privateChildEnvironment(context.env, temporary);
|
|
119
|
-
const home=os.homedir();
|
|
120
|
-
for(const p of [path.join(home,'.alipay-cli/credentials'),path.join(process.env.APPDATA||path.join(home,'AppData/Roaming'),'alipay-cli/data/logs')]) {
|
|
121
|
-
fs.mkdirSync(p,{recursive:true});protect(p);writable(p);
|
|
122
|
-
}
|
|
123
117
|
for(const [exe,args,name] of [[context.cli,['version'],'alipay-cli'],[context.jq,['-n','-e','--arg','v','猫 $HOME "x"','$v == "猫 $HOME \\"x\\""'],'jq']]) {
|
|
124
118
|
// jq round-trip is checked in the Bash probe below; this native check verifies launch/version.
|
|
125
119
|
const r=run(exe,name==='jq'?['--version']:args,{env:context.env});
|
|
@@ -130,6 +124,11 @@ export function inspectLocal(root) {
|
|
|
130
124
|
writable(temporary);
|
|
131
125
|
return context;
|
|
132
126
|
}
|
|
127
|
+
export function checkCredentialsWritable() {
|
|
128
|
+
const target=path.join(os.homedir(),'.alipay-cli/credentials');
|
|
129
|
+
fs.mkdirSync(target,{recursive:true});
|
|
130
|
+
checkWritable(target);
|
|
131
|
+
}
|
|
133
132
|
export function ensureReady({retry=false}={}) {
|
|
134
133
|
// Reuse only within this Node invocation. Each new process performs full checks;
|
|
135
134
|
// persisted failures never prevent a read-only check after the user fixes a dependency.
|
|
@@ -172,14 +171,13 @@ export function resultError(error) {
|
|
|
172
171
|
(error.reason === 'EXECUTION_FAILED' && error.dependency === 'alipay-cli');
|
|
173
172
|
const reason=error.reason || error.code || 'CHECK_FAILED';
|
|
174
173
|
const recovery=reason==='NO_COMPATIBLE_EXECUTION_MAPPING'?'USE_SUPPORTED_PLATFORM_OR_SITE':
|
|
175
|
-
reason==='ARCHITECTURE_CHECK_FAILED'?'RESTORE_SYSTEM_ARCHITECTURE_PROBE_THEN_RECHECK':
|
|
176
174
|
(reason==='DEPENDENCY_MISSING' && error.dependency==='curl')?'RESTORE_WINDOWS_CURL_THEN_RECHECK':
|
|
177
175
|
needsSetup?'PREPARE_MISSING_DEPENDENCIES':
|
|
178
176
|
reason==='DOWNLOAD_FAILED'?'RESTORE_NETWORK_THEN_REPAIR':
|
|
179
177
|
['INTEGRITY_FAILED','SIGNATURE_FAILED'].includes(reason)?'VERIFY_OFFICIAL_ASSET_BEFORE_REPAIR':
|
|
180
178
|
reason==='RETRY_BUDGET_EXHAUSTED'?'MANUAL_REPAIR_THEN_RECHECK':
|
|
181
179
|
reason==='USER_SCOPE_REQUIRED'?'USE_NON_ELEVATED_SESSION':
|
|
182
|
-
['
|
|
180
|
+
['STORAGE_FAILED','ENVIRONMENT_BUSY','ENVIRONMENT_STATE_INVALID'].includes(reason)?'REPAIR_LOCAL_ENVIRONMENT_THEN_RECHECK':
|
|
183
181
|
'INSPECT_FAILURE_THEN_RECHECK';
|
|
184
182
|
return {schemaVersion:1,status:needsSetup?'NEEDS_SETUP':'BLOCKED',reason,dependency:error.dependency||'',recovery,validationCoverage:'NOT_VERIFIED'};
|
|
185
183
|
}
|
|
@@ -194,7 +192,7 @@ if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.ur
|
|
|
194
192
|
if(process.argv[2]==='shell-env') {
|
|
195
193
|
const quote=s=>"'"+s.replaceAll("'","'\\''")+"'";
|
|
196
194
|
// Only explicit adapter-owned values. Business context and credentials never appear here.
|
|
197
|
-
for(const k of ['PATH','TEMP','TMP','TMPDIR','ALIPAY_AIPAY_TELEMETRY_DIR','ALIPAY_WINDOWS_NODE','ALIPAY_WINDOWS_JQ','ALIPAY_WINDOWS_CLI','ALIPAY_WINDOWS_CREDENTIALS_DIR','ALIPAY_WINDOWS_LOGS_DIR','ALIPAY_WINDOWS_CLI_ENTRY','ALIPAY_WINDOWS_CLI_RUNNER','
|
|
195
|
+
for(const k of ['PATH','TEMP','TMP','TMPDIR','ALIPAY_AIPAY_TELEMETRY_DIR','ALIPAY_WINDOWS_NODE','ALIPAY_WINDOWS_JQ','ALIPAY_WINDOWS_CLI','ALIPAY_WINDOWS_CREDENTIALS_DIR','ALIPAY_WINDOWS_LOGS_DIR','ALIPAY_WINDOWS_CLI_ENTRY','ALIPAY_WINDOWS_CLI_RUNNER','ALIPAY_WINDOWS_ACCESS_SCRIPT','NODE_BINARY','MSYS_NO_PATHCONV','MSYS2_ARG_CONV_EXCL']) {
|
|
198
196
|
const v=k==='PATH'?c.env[k].split(';').map(p=>slash(p).replace(/^([a-zA-Z]):/,(_,d)=>'/'+d.toLowerCase())).join(':'):c.env[k];
|
|
199
197
|
console.log('export '+k+'='+quote(v));
|
|
200
198
|
}
|
|
@@ -6,7 +6,7 @@ import {refreshWindowsCli} from './cli-install.mjs';
|
|
|
6
6
|
import {findGitBash} from './git-discovery.mjs';
|
|
7
7
|
import {findWinget} from './winget.mjs';
|
|
8
8
|
import {installGitOfficial} from './git-install.mjs';
|
|
9
|
-
import {powershell} from './
|
|
9
|
+
import {powershell} from './host.mjs';
|
|
10
10
|
import {policy,run,stateRoot,readState,saveState,withLock,ensureReady,locateJq,fail,report} from './preflight.mjs';
|
|
11
11
|
|
|
12
12
|
export function claimPreparation(state,stage,{source='official',repair=false,authorized=false}={}) {
|
|
@@ -2,10 +2,10 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {spawnSync} from 'node:child_process';
|
|
5
|
-
import {
|
|
5
|
+
import {checkWritable} from './host.mjs';
|
|
6
6
|
|
|
7
7
|
// Pin tempfile itself: environment variables alone allow Python to fall back to
|
|
8
|
-
// another directory if the
|
|
8
|
+
// another directory if the writable directory becomes unavailable.
|
|
9
9
|
const bootstrap = [
|
|
10
10
|
'import os, runpy, sys, tempfile',
|
|
11
11
|
'tempfile.tempdir = sys.argv[1]',
|
|
@@ -16,14 +16,14 @@ const bootstrap = [
|
|
|
16
16
|
|
|
17
17
|
// The caller has already validated the single registered A2M script path.
|
|
18
18
|
export function runWindowsPython(python, script, args, {
|
|
19
|
-
env = process.env, tempRoot = os.tmpdir(),
|
|
19
|
+
env = process.env, tempRoot = os.tmpdir(), checkDirectory = checkWritable, spawn = spawnSync,
|
|
20
20
|
cleanup = directory => fs.rmSync(directory, {recursive: true, force: true})
|
|
21
21
|
} = {}) {
|
|
22
22
|
const temporary = fs.mkdtempSync(path.join(path.resolve(tempRoot), 'alipay-aipay-a2m-'));
|
|
23
23
|
try {
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
24
|
+
// Confirm the fresh directory is writable before launching Python. Its
|
|
25
|
+
// inherited permissions remain intact for Python and curl output.
|
|
26
|
+
checkDirectory(temporary);
|
|
27
27
|
const childEnv = Object.fromEntries(Object.entries(env)
|
|
28
28
|
.filter(([key]) => !['TEMP', 'TMP', 'TMPDIR'].includes(key.toUpperCase())));
|
|
29
29
|
Object.assign(childEnv, {TEMP: temporary, TMP: temporary, TMPDIR: temporary});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {fileURLToPath} from 'node:url';
|
|
4
|
-
import {run, fail,
|
|
4
|
+
import {run, fail, checkWritable} from './host.mjs';
|
|
5
5
|
import {findWinget} from './winget.mjs';
|
|
6
6
|
|
|
7
7
|
const script = fileURLToPath(new URL('../../../integration/modules/scripts/local_402_sandbox_pay.py', import.meta.url));
|
|
@@ -34,11 +34,11 @@ export function findWindowsPython({env = process.env, execute = run, exists = fs
|
|
|
34
34
|
// A separate budget: preparing Python for A2M never consumes or resets the
|
|
35
35
|
// Git/jq/CLI environment repair state, or any business/telemetry state.
|
|
36
36
|
export function prepareWindowsPython({authorized = false, repair = false, find = findWindowsPython,
|
|
37
|
-
execute = run, env = process.env, root,
|
|
37
|
+
execute = run, env = process.env, root, checkRoot = checkWritable, lock, exists = fs.existsSync} = {}) {
|
|
38
38
|
const existing = find({env});
|
|
39
39
|
if (existing) return existing;
|
|
40
40
|
if (!authorized) fail('DEPENDENCY_MISSING', 'Python 3');
|
|
41
|
-
|
|
41
|
+
checkRoot(root);
|
|
42
42
|
return lock(root, () => {
|
|
43
43
|
const file = path.join(root, 'python-environment.json');
|
|
44
44
|
let state = {prepareAttempts: 0, repairAttempts: 0};
|
|
@@ -78,7 +78,7 @@ export async function runPythonEnvironment(args) {
|
|
|
78
78
|
if (!found) {
|
|
79
79
|
if (mode === 'check-python' || options['--authorized'] !== 'true') fail('DEPENDENCY_MISSING', 'Python 3');
|
|
80
80
|
prepareWindowsPython({authorized: true, repair: options['--repair-confirmed'] === 'true',
|
|
81
|
-
root: stateRoot(),
|
|
81
|
+
root: stateRoot(), checkRoot: checkWritable, lock: withLock});
|
|
82
82
|
}
|
|
83
83
|
process.stderr.write('ALIPAY_AIPAY_INTERNAL:PYTHON_ENV=READY\n');
|
|
84
84
|
return 0;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {applyReady, report, resultError} from './preflight.mjs';
|
|
1
|
+
import {applyReady, checkCredentialsWritable, report, resultError} from './preflight.mjs';
|
|
2
2
|
import {prepare} from './prepare.mjs';
|
|
3
3
|
|
|
4
4
|
const intents = ['integration_only', 'onboarding_only', 'full_process'];
|
|
5
5
|
const prefix = 'ALIPAY_AIPAY_INTERNAL:';
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
// storage/ACL failures, invalid state and unknown exceptions remain blocking.
|
|
7
|
+
// Optional tool failures do not prevent independent project code development.
|
|
9
8
|
function codeOnlyFailure(error) {
|
|
10
9
|
const tools = ['Git Bash', 'git', 'jq', 'alipay-cli', 'cli', 'curl', 'PowerShell'];
|
|
11
10
|
if (!tools.includes(error.dependency)) return false;
|
|
@@ -36,7 +35,7 @@ export function selectRoute({rootIntent = '', authorized = false, repair = false
|
|
|
36
35
|
}
|
|
37
36
|
report(error);
|
|
38
37
|
const route = error.reason === 'NO_COMPATIBLE_EXECUTION_MAPPING' ? 'LEGACY' :
|
|
39
|
-
rootIntent
|
|
38
|
+
rootIntent !== 'onboarding_only' && codeOnlyFailure(error) ? 'CODE_ONLY' : 'BLOCKED';
|
|
40
39
|
return emit({rootIntent, route, ...resultError(error)});
|
|
41
40
|
}
|
|
42
41
|
|
|
@@ -79,12 +78,14 @@ export function consumeRoute(args) {
|
|
|
79
78
|
|
|
80
79
|
// Native Node messages, telemetry and project inspection do not depend on Bash.
|
|
81
80
|
// READY is never trusted as proof; the actual business executor checks again.
|
|
82
|
-
export function requireBusinessEnvironment() {
|
|
81
|
+
export function requireBusinessEnvironment({credentials = false} = {}) {
|
|
83
82
|
if (['LEGACY','CODE_ONLY','BLOCKED'].includes(process.env.ALIPAY_AIPAY_WINDOWS_ROUTE)) {
|
|
84
83
|
const error = Object.assign(new Error('WINDOWS_ENVIRONMENT_UNAVAILABLE'), {reason: 'WINDOWS_ENVIRONMENT_UNAVAILABLE'});
|
|
85
84
|
throw error;
|
|
86
85
|
}
|
|
87
|
-
|
|
86
|
+
const context=applyReady();
|
|
87
|
+
if (credentials) checkCredentialsWritable();
|
|
88
|
+
return context;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
export function onboardingRoute() {
|
|
@@ -5,8 +5,8 @@ _win_exports=$(command node "$(cygpath -m "$_win_dir/preflight.mjs")" shell-env)
|
|
|
5
5
|
eval "$_win_exports"
|
|
6
6
|
unset _win_exports
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
"$ALIPAY_WINDOWS_NODE" "$
|
|
8
|
+
windows_check_access() {
|
|
9
|
+
"$ALIPAY_WINDOWS_NODE" "$ALIPAY_WINDOWS_ACCESS_SCRIPT" "$(cygpath -m "$1")" >/dev/null
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
git() {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Windows 兼容环境
|
|
2
2
|
|
|
3
|
-
仅真实 Windows
|
|
3
|
+
仅真实 Windows 加载本层。当前适配要求实际运行的 Windows x64 Node >=18(沿用仓库最低版本声明);不通过 OS build 或系统原生架构探测推定能力,不承诺 ARM64、x86 或 WSL。当前移植依据是 Windows 工程验证交付包,尚不能宣称 Windows 10、干净安装或全部真实业务分支已验收。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
只校验当前 Node 进程报告的 `platform=win32`、`arch=x64`、版本与实际可执行性;Git Bash、jq、CLI 仍各自通过实际启动和功能检查。非 x64 Node 走既有 LEGACY,系统信息查询失败不再额外阻断可运行的工具。Node 引导复用或安装 x64 Node 后也以实际启动验证为准。
|
|
6
6
|
|
|
7
|
-
Windows 原生 Node 调度明确选定的 Git for Windows Bash,原 Shell 脚本使用 Windows 原生 jq 与 alipay-cli
|
|
7
|
+
Windows 原生 Node 调度明确选定的 Git for Windows Bash,原 Shell 脚本使用 Windows 原生 jq 与 alipay-cli。兼容层处理依赖、路径和进程;产品映射、CLI/MCP 命令、请求、业务确认、失败恢复和完成条件仍以真实 flow 和原脚本为准。Windows 的 Integration 继续由当前 Agent 串行执行,不启用 Worker。
|
|
8
8
|
|
|
9
9
|
## 启动与分流
|
|
10
10
|
|
|
@@ -16,11 +16,11 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" windows-entry --root-in
|
|
|
16
16
|
|
|
17
17
|
用户明确要求安装并通过 Skill 完成签约/开通或完整接入时附加 `--prepare-authorized true`;仅代码开发且没有依赖准备授权时先只读检查。用户明确选择站点时附加 `--site-selected true`。准备授权只覆盖用户级依赖,不包含登录、签约或应用写入。
|
|
18
18
|
|
|
19
|
-
退出 0 只表示分流完成。必须取得唯一 `ALIPAY_AIPAY_INTERNAL:WINDOWS_ROUTE=<JSON>`,校验 schemaVersion=1、rootIntent 等于当前根意图、route 为 READY、BLOCKED、LEGACY 或 CODE_ONLY;非零、超时、缺失或冲突均按 BLOCKED 停止。READY 才执行本地快速沙箱和签约;BLOCKED 说明内部 `WINDOWS_ENV_RESULT` 的实际 reason/dependency 及恢复动作,等待环境修复,不能输出站点引导;LEGACY 仅用于不支持的执行映射或用户明确选择站点。后续 Windows runtime 命令附加当前 `--windows-route`;BLOCKED 不进入 bootstrap、业务 flow
|
|
19
|
+
退出 0 只表示分流完成。必须取得唯一 `ALIPAY_AIPAY_INTERNAL:WINDOWS_ROUTE=<JSON>`,校验 schemaVersion=1、rootIntent 等于当前根意图、route 为 READY、BLOCKED、LEGACY 或 CODE_ONLY;非零、超时、缺失或冲突均按 BLOCKED 停止。READY 才执行本地快速沙箱和签约;BLOCKED 说明内部 `WINDOWS_ENV_RESULT` 的实际 reason/dependency 及恢复动作,等待环境修复,不能输出站点引导;LEGACY 仅用于不支持的执行映射或用户明确选择站点。后续 Windows runtime 命令附加当前 `--windows-route`;BLOCKED 不进入 bootstrap、业务 flow 或项目写入。CODE_ONLY 只放行独立代码开发,根意图和诊断原因沿用本次入口事实。修复可选工具后重新只读复检,取得唯一 READY 才恢复快速沙箱和开通动作。环境故障不关闭遥测,也不改变 SESSION_ID、run_id、计数或会话采集选择。
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
`integration_only/full_process` 遇到已识别的 Git Bash、jq、CLI 或安装辅助工具缺失/运行失败,以及这些工具的有限安装、下载、哈希/签名校验或预算耗尽失败时,返回 `CODE_ONLY`。失败工具不得继续执行,校验失败的安装包不得运行。以唯一 WINDOWS_ROUTE 的 route 决定是否进入开发;WINDOWS_ENV_RESULT 的失败诊断保留实际原因,不能将其误读为业务能力可用。该分流只允许 Integration 的原生 Node 路径,默认 `VERIFY_PENDING`,跳过快速沙箱和依赖沙箱的测试,最终如实列出未完成项,不自动引导站点开通。完整接入在代码部分检查后停在 Onboarding 前,待工具恢复且复检 READY 才恢复。Node 版本不足、实际存储故障、状态损坏、回滚失败或未知错误仍为 BLOCKED;项目路径和生产配置保护要求不变。下文“失败保持 BLOCKED”对上述可选工具例外按 CODE_ONLY 处理。
|
|
22
22
|
|
|
23
|
-
CODE_ONLY
|
|
23
|
+
CODE_ONLY 后用户已自行修好环境时,按原根意图执行上述 `windows-entry` 只读复检;明确授权准备时附加 `--prepare-authorized true`,只有明确修复后重试才再附加 `--repair-confirmed true`,沿用原有分阶段预算。唯一 READY 才恢复快速沙箱和开通能力;仍为 CODE_ONLY 时只继续独立代码开发,不反复安装。根意图变化必须按新根意图重新执行入口,不把 CODE_ONLY 当成 LEGACY 的站点选择,也不沿用旧能力事实。
|
|
24
24
|
|
|
25
25
|
## Windows CLI 安装契约
|
|
26
26
|
|
|
@@ -32,11 +32,11 @@ stdout 为空、原始安装器输出不对客,只映射根协议的唯一刷
|
|
|
32
32
|
|
|
33
33
|
## 有限环境准备
|
|
34
34
|
|
|
35
|
-
CLI 刷新策略与非 Windows 保持同一会话预算,不锁定 0.1.7 或其哈希;Windows 交由正式 PowerShell 安装器选择版本。jq 优先复用能通过版本和实际 filter 检查的既有原生工具;缺失或不可用时安装固定 1.7.1,官方 HTTPS 资产和 SHA-256 位于 `scripts/windows/policy.json`。Node
|
|
35
|
+
CLI 刷新策略与非 Windows 保持同一会话预算,不锁定 0.1.7 或其哈希;Windows 交由正式 PowerShell 安装器选择版本。jq 优先复用能通过版本和实际 filter 检查的既有原生工具;缺失或不可用时安装固定 1.7.1,官方 HTTPS 资产和 SHA-256 位于 `scripts/windows/policy.json`。Node 优先复用当前可运行的 Windows x64 Node >=18;缺失时由根启动器的 PowerShell 引导准备官方 LTS 用户级 ZIP,SHA-256 验证后返回绝对可执行路径,首次一次、明确修复最多一次。Git Bash 从常见目录和本机 PATH 的 Git 安装布局定位,也可由 `ALIPAY_GIT_BASH` 指定绝对路径;必须验证 Git for Windows 版本、cygpath 和 Bash 的 MINGW/MSYS 平台,不使用 System32/WSL Bash,显式指定错误时停止而不偷偷换工具。
|
|
36
36
|
|
|
37
37
|
初次缺 Git Bash、jq 或本机 CLI 无法启动且当前任务授权准备时,按 Git Bash → jq → 必要的 CLI 修复 → 完整复检执行。CLI 已可用时不重复安装。Git 优先复用已有可运行的 Git for Windows;缺失时优先调用用户级 winget,无法运行或未通过复检时使用 `scripts/windows/policy.json` 固定的 Git for Windows 官方 x64 安装包(当前 2.56.0.2)。官方包下载后先校验固定 SHA-256,再由系统 PowerShell 验证 Authenticode 有效,之后安装到 `%LOCALAPPDATA%/Programs/Git-Aipay-<version>`,并实测 git、bash、cygpath。下载或校验失败不运行安装包。两种来源均不可用时保持 BLOCKED,展示原因及官方手工安装路径,不静默跳站点。
|
|
38
38
|
|
|
39
|
-
jq 使用固定 1.7.1 官方资产和 SHA-256,已有健康版本直接复用;CLI 仍使用正式 PowerShell 安装器选择当前版本。每个依赖和 Git 的两种安装来源分别记录实际开始的安装尝试,确认来源前的只读探测不消耗预算;Git 的 winget
|
|
39
|
+
jq 使用固定 1.7.1 官方资产和 SHA-256,已有健康版本直接复用;CLI 仍使用正式 PowerShell 安装器选择当前版本。每个依赖和 Git 的两种安装来源分别记录实际开始的安装尝试,确认来源前的只读探测不消耗预算;Git 的 winget 失败可在同次准备中尝试官方固定包。初次每个来源最多一次,用户明确修复后每个来源最多再一次;失败阶段可续跑,已健康的阶段不重装。状态记录不含业务材料;复检始终只读且不消耗安装预算。网络、实际权限、签名或完整性失败保持 BLOCKED,不轮换未经验证的镜像、使用 WSL、提权或修改执行策略。
|
|
40
40
|
|
|
41
41
|
环境诊断和次数保存在 `%LOCALAPPDATA%/alipay-aipay-pre/windows/environment.json`;旧版全局次数无法归属具体安装阶段,迁移时保留阻断原因并从新版本的有限分阶段预算重新计数。锁短时占用或状态损坏时,完整工具检查通过仍可 READY;自动安装保持阻断,不删除锁或状态绕过预算。用户修好依赖后,只读复检可再次执行,无论此前安装预算是否用尽:
|
|
42
42
|
|
|
@@ -60,13 +60,13 @@ Skill 自有动作始终执行 `node "<SKILL_DIR>/references/normal/scripts/runt
|
|
|
60
60
|
|
|
61
61
|
只转换登记的项目、配置、上传文件和脚本路径;JSON、URL、scope、公钥和业务上下文保持原样。禁用 MSYS 隐式参数转换后,适配器显式转换 `git -C` 和 native Node/jq/CLI 文件参数。快速沙箱终态的路径由 gateway 归一化为原生盘符绝对路径,脚本内部比较不变。
|
|
62
62
|
|
|
63
|
-
大 jq `--argjson`
|
|
63
|
+
大 jq `--argjson` 值使用当前动作的临时文件并在调用后删除,MCC 目录使用 Node stdin,原 CLI 的 `-d/--data` 不改写;超出原生进程参数预算时明确失败,不能换未经确认的 CLI 参数。同步/异步 Shell 执行共享同一 Git Bash 适配器;CLI 最多 60 秒、业务 Shell 最多 180 秒,超时不等于未发送或业务成功,仍按原只读核验/UNKNOWN 规则处理。
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Windows 沙箱项目配置不要求精确 NTFS ACL 白名单;项目可继承宿主或 Agent 正常工作所需的权限。环境状态、Shell 临时目录、CLI 凭据和日志目录的 ACL 不作为入口门禁。进入登录、签约等需用 CLI 登录凭据的动作前,才验证 `~/.alipay-cli/credentials` 实际可写;失败仅阻断依赖登录凭据的动作,不阻断沙箱和项目代码开发。授权状态和公钥页状态文件只检查实际可写性与非符号链接路径,不改写继承权限;这不表示 ACL 已安全配置,密钥文件访问控制由部署环境管理。生产配置仍遵守各自脚本的保护规则。
|
|
66
66
|
|
|
67
|
-
Shell
|
|
67
|
+
Shell 业务子进程的临时文件放在上述环境目录的专用 tmp 中;gateway 本身不切换 TEMP/TMP,不改变现有遥测目录。Shell 子进程用原 `telemetryStoragePaths()` 明确绑定原来的遥测目录及既有覆盖项,保证声明选择、会话、run、outbox 和上传状态连续。遥测文件的权限失败沿用原静默失败语义,业务计数、采集及上传逻辑不变。
|
|
68
68
|
|
|
69
|
-
402 联调仍由原生 Python 3 执行,不依赖 Git Bash。其 Node
|
|
69
|
+
402 联调仍由原生 Python 3 执行,不依赖 Git Bash。其 Node 启动器在系统临时目录下创建本次命令的独立空目录,确认实际可写后才启动 Python;仅该子进程的 TEMP/TMP/TMPDIR 与 Python `tempfile.tempdir` 指向此目录,禁止不可用时回退到其他目录。过程产物及 curl 响应文件继承用户现有目录权限;实际写入失败时不伪报 ACL 问题。子进程结束或启动异常后尽力清理本次目录,清理失败只输出内部诊断且不覆盖原始执行结果,不重放请求。父进程环境、原 Python 业务脚本、参数和退出码不变;强制终止父进程可能留下该目录,不作为恢复依据。
|
|
70
70
|
|
|
71
71
|
Windows 签约请求的 UUID 使用已有 Node 生成,仅此步骤不要求 python3/uuidgen;Unix/macOS/Linux 保留原依赖检查与生成方式。402 联调仍需 Python 3,此项依赖未被取消。
|
|
72
72
|
|
|
@@ -158,7 +158,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-execute-read
|
|
|
158
158
|
|
|
159
159
|
若组合动作本身不可用,原子 fallback 固定为先执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" flow onboarding execute --product-type "$PRODUCT_TYPE" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`,再执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" env check --product-type "$PRODUCT_TYPE" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"`;fallback 不得重复 stage 或重复环境检查。
|
|
160
160
|
|
|
161
|
-
签约脚本仍通过 `error_handler.sh -> common.sh` 初始化 `DEV_TOOL_NAME`、`PLATFORM
|
|
161
|
+
签约脚本仍通过 `error_handler.sh -> common.sh` 初始化 `DEV_TOOL_NAME`、`PLATFORM`、优先取有效第三方项目 ID、缺失时取当前 `run_id` 的 `PLATFORM_ID`、当前 Skill 版本和匿名 `skill_user_id` 的 `ALIPAYCLI_BIZ_INFO`,以及当前产品 `PRODUCT`。有任务工具时只内部记录实际阶段,不维护任务数量;只保留当前对话最小上下文,不创建业务进度文件。
|
|
162
162
|
|
|
163
163
|
本步骤成功时不输出“环境检查完成”“任务已创建”等对客消息,直接进 Step 3;只有安装失败、权限不足或缺工具时才输出实际问题和恢复动作。工具能力差异不得省略任务、步骤或完成状态。
|
|
164
164
|
|
|
@@ -249,7 +249,7 @@ Step 5 直接使用对应 phase 的完整命令;仅出现脚本异常、需要
|
|
|
249
249
|
2. 服务:仅按量付费由 runner 执行原 `service.sh list`;其他产品 envelope 固定为 `NOT_APPLICABLE`。成功候选只取 envelope 的 `service.candidates`。
|
|
250
250
|
3. 应用:runner 执行原 `app.sh list --product-type "$PRODUCT_TYPE" --sales-code "$SALES_CODE"`。成功候选和 `WEBAPP|MOBILEAPP` 只取 envelope 的 application 分支;不得宣称返回应用已与当前支付产品绑定。
|
|
251
251
|
|
|
252
|
-
> ⚠️ 脚本通过 `error_handler.sh` 间接初始化 `DEV_TOOL_NAME
|
|
252
|
+
> ⚠️ 脚本通过 `error_handler.sh` 间接初始化 `DEV_TOOL_NAME`、优先取有效第三方项目 ID、缺失时取当前 `run_id` 的 `PLATFORM_ID`、当前 Skill 版本和匿名 `skill_user_id` 对应的 `ALIPAYCLI_BIZ_INFO`,以及 CLI `PRODUCT`;签约查询必须传入当前产品的 `--sales-code` 和 `--product-type`(aipay|webpay|apppay)做一致性校验。AI 网页应用收款和 AI 移动应用收款使用不同 salesCode,禁止混用。
|
|
253
253
|
|
|
254
254
|
各适用查询仍是独立 CLI/MCP 调用,runner 只编排现有脚本,必须分别完成原有错误检测,禁止伪造合并 MCP。失败后按错误类型处理:
|
|
255
255
|
|
|
@@ -453,7 +453,6 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" preview key-tool-fallba
|
|
|
453
453
|
`WEBAPP` 需公钥。`MOBILEAPP` 按明确语义归一化:iOS→`IOS+bundleId`,Android→`ANDROID+appPackage+appSign`,两类→`ALL+三项`;示例非口令,枚举兼容,含糊/矛盾补问。脚本只收 `IOS|ANDROID|ALL`。接口无名称/描述。`APP_WEB_INITIAL` 仅限 `aipay|webpay`,`APP_MOBILE_INITIAL` 仅限 `apppay`;`PARTIAL|INVALID` 只补缺项:
|
|
454
454
|
|
|
455
455
|
```bash
|
|
456
|
-
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" key-tool --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
457
456
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message material-collect --category application --state APP_WEB_INITIAL --product-type "$PRODUCT_TYPE" --input-id "$USER_INPUT_ID" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
458
457
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message material-collect --category application --state APP_MOBILE_INITIAL --product-type "$PRODUCT_TYPE" --input-id "$USER_INPUT_ID" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
459
458
|
```
|
|
@@ -32,12 +32,12 @@ CLI `login --complete` 成功只表示页面侧确认完成,不代表本机已
|
|
|
32
32
|
|
|
33
33
|
授权页只允许 `https://aipay.alipay.com/cli-auth`,必含唯一 `deviceCode/productCode/mccCode`,只允许可选 `platform`;`mccContextReceipt` 只用于本地上下文校验和短时状态,不进入授权 URL。禁止 CLI `verification_url`、额外参数、重复参数和 fragment。页面 stdout 必须先作为独立对客消息送达,再执行 `auth.sh open` 和 `auth.sh wait`,后续 pending 或收口不得合并进同一回复。
|
|
34
34
|
|
|
35
|
-
`wait` 生产最多查询 12 次,未成功的相邻查询间隔 5
|
|
35
|
+
`wait` 生产最多查询 12 次,未成功的相邻查询间隔 5 秒,成功或过期立即停止。耗尽后,Agent 先按统一入口执行 `count-user-input` 计入这条用户消息,再按 flow 登记命令把原始回复和当前 `USER_INPUT_ID` 交给 `onboarding-recovery auth-confirm`;runner 只复用统一肯定语义判定器,不再计数。接受 `继续`、`好了`、`已完成`、`确认`、`OK` 和兼容输入 `1` 时,每次只执行一次 `auth.sh confirm`,不重启自动轮询。否定、疑问、取消、修改意见或含糊输入不查询。
|
|
36
36
|
|
|
37
37
|
授权对客正文只由 `auth.sh` 调用消息目录生成。初始页面不得提前要求回复完成;pending 的自然语言提示、允许回复和授权页返回提示都不在本模块维护副本。
|
|
38
38
|
|
|
39
39
|
## 状态保护
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
短时授权上下文使用临时目录和原子文件,拒绝符号链接;Unix/macOS/Linux 校验目录 `0700`、文件 `0600`,Windows 校验路径实际可访问,不声称已配置 ACL。状态可见时显式参数必须逐项一致;状态不可见时仅允许完整显式上下文降级。新 init 只有先通过固定上下文校验才可替换旧状态;上下文改变且 CLI 仍登录时必须进入 mismatch,不得复用旧 deviceCode。
|
|
42
42
|
|
|
43
43
|
任何授权 token、deviceCode、未脱敏 CLI/MCP 输出和临时 URL 都不得进入普通日志、对话摘要、跨会话状态或宽权限临时文件。只有经过脚本白名单和当前上下文校验、且当前动作必须交付的授权 URL 可以进入该动作唯一对客正文。
|
|
@@ -31,7 +31,7 @@ fi
|
|
|
31
31
|
|
|
32
32
|
app_key_secure_mode() {
|
|
33
33
|
local target="$1" expected="$2" mode
|
|
34
|
-
if declare -F
|
|
34
|
+
if declare -F windows_check_access >/dev/null; then windows_check_access "$target"; return $?; fi
|
|
35
35
|
if mode=$(stat -f '%Lp' "$target" 2>/dev/null); then
|
|
36
36
|
:
|
|
37
37
|
elif mode=$(stat -c '%a' "$target" 2>/dev/null); then
|
|
@@ -49,7 +49,7 @@ prepare_app_key_state_dir() {
|
|
|
49
49
|
return 1
|
|
50
50
|
fi
|
|
51
51
|
mkdir -p "$APP_KEY_STATE_DIR" || return 1
|
|
52
|
-
chmod 700 "$APP_KEY_STATE_DIR" 2>/dev/null || return 1
|
|
52
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 700 "$APP_KEY_STATE_DIR" 2>/dev/null || return 1; fi
|
|
53
53
|
app_key_secure_mode "$APP_KEY_STATE_DIR" 700 || return 1
|
|
54
54
|
else
|
|
55
55
|
[ -d "$APP_KEY_STATE_DIR" ] && [ ! -L "$APP_KEY_STATE_DIR" ] || {
|
|
@@ -61,7 +61,7 @@ prepare_app_key_state_dir() {
|
|
|
61
61
|
|
|
62
62
|
validate_app_key_state_file() {
|
|
63
63
|
[ -f "$APP_KEY_STATE_FILE" ] && [ ! -L "$APP_KEY_STATE_FILE" ] || return 1
|
|
64
|
-
chmod 600 "$APP_KEY_STATE_FILE" 2>/dev/null || return 1
|
|
64
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 600 "$APP_KEY_STATE_FILE" 2>/dev/null || return 1; fi
|
|
65
65
|
app_key_secure_mode "$APP_KEY_STATE_FILE" 600
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -74,7 +74,7 @@ save_app_key_page_state() {
|
|
|
74
74
|
return 1
|
|
75
75
|
fi
|
|
76
76
|
temp_state=$(mktemp "${APP_KEY_STATE_DIR}/.key-page-state.XXXXXX") || return 1
|
|
77
|
-
chmod 600 "$temp_state" 2>/dev/null || { rm -f "$temp_state"; return 1; }
|
|
77
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 600 "$temp_state" 2>/dev/null || { rm -f "$temp_state"; return 1; }; fi
|
|
78
78
|
jq -n \
|
|
79
79
|
--arg appId "$APP_ID" \
|
|
80
80
|
--arg confirmPageUrl "$CONFIRM_PAGE_URL" \
|
|
@@ -82,7 +82,7 @@ save_app_key_page_state() {
|
|
|
82
82
|
mv "$temp_state" "$APP_KEY_STATE_FILE" || { rm -f "$temp_state"; return 1; }
|
|
83
83
|
validate_app_key_state_file || {
|
|
84
84
|
rm -f "$APP_KEY_STATE_FILE"
|
|
85
|
-
echo "❌
|
|
85
|
+
echo "❌ 公钥确认页状态文件不可访问,已停止" >&2
|
|
86
86
|
return 1
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -28,7 +28,7 @@ require_command jq || exit 1
|
|
|
28
28
|
require_command alipay-cli || exit 1
|
|
29
29
|
|
|
30
30
|
# 生成 UUID 作为 bizRequestNo
|
|
31
|
-
if declare -F
|
|
31
|
+
if declare -F windows_check_access >/dev/null; then
|
|
32
32
|
require_command node || exit 1
|
|
33
33
|
BIZ_REQUEST_NO=$(node -e 'console.log(require("node:crypto").randomUUID())') || exit 78
|
|
34
34
|
else
|
|
@@ -78,7 +78,7 @@ fi
|
|
|
78
78
|
|
|
79
79
|
secure_mode() {
|
|
80
80
|
local target="$1" expected="$2" mode
|
|
81
|
-
if declare -F
|
|
81
|
+
if declare -F windows_check_access >/dev/null; then windows_check_access "$target"; return $?; fi
|
|
82
82
|
if mode=$(stat -f '%Lp' "$target" 2>/dev/null); then
|
|
83
83
|
:
|
|
84
84
|
elif mode=$(stat -c '%a' "$target" 2>/dev/null); then
|
|
@@ -96,7 +96,7 @@ prepare_auth_state_dir() {
|
|
|
96
96
|
return 1
|
|
97
97
|
fi
|
|
98
98
|
mkdir -p "$AUTH_STATE_DIR" || return 1
|
|
99
|
-
chmod 700 "$AUTH_STATE_DIR" 2>/dev/null || return 1
|
|
99
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 700 "$AUTH_STATE_DIR" 2>/dev/null || return 1; fi
|
|
100
100
|
secure_mode "$AUTH_STATE_DIR" 700 || return 1
|
|
101
101
|
else
|
|
102
102
|
[ -d "$AUTH_STATE_DIR" ] && [ ! -L "$AUTH_STATE_DIR" ] || {
|
|
@@ -108,7 +108,7 @@ prepare_auth_state_dir() {
|
|
|
108
108
|
|
|
109
109
|
validate_auth_state_file() {
|
|
110
110
|
[ -f "$AUTH_STATE_FILE" ] && [ ! -L "$AUTH_STATE_FILE" ] || return 1
|
|
111
|
-
chmod 600 "$AUTH_STATE_FILE" 2>/dev/null || return 1
|
|
111
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 600 "$AUTH_STATE_FILE" 2>/dev/null || return 1; fi
|
|
112
112
|
secure_mode "$AUTH_STATE_FILE" 600
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -160,7 +160,7 @@ save_auth_state() {
|
|
|
160
160
|
return 1
|
|
161
161
|
fi
|
|
162
162
|
temp_state=$(mktemp "${AUTH_STATE_DIR}/.auth-state.XXXXXX") || return 1
|
|
163
|
-
chmod 600 "$temp_state" 2>/dev/null || { rm -f "$temp_state"; return 1; }
|
|
163
|
+
if ! declare -F windows_check_access >/dev/null; then chmod 600 "$temp_state" 2>/dev/null || { rm -f "$temp_state"; return 1; }; fi
|
|
164
164
|
jq -n \
|
|
165
165
|
--arg salesCode "$SALES_CODE" \
|
|
166
166
|
--arg mccCode "$MCC_CODE" \
|
|
@@ -183,7 +183,7 @@ save_auth_state() {
|
|
|
183
183
|
mv "$temp_state" "$AUTH_STATE_FILE" || { rm -f "$temp_state"; return 1; }
|
|
184
184
|
validate_auth_state_file || {
|
|
185
185
|
cleanup_auth_state
|
|
186
|
-
echo "❌
|
|
186
|
+
echo "❌ 授权状态文件不可访问,已停止" >&2
|
|
187
187
|
return 1
|
|
188
188
|
}
|
|
189
189
|
}
|
|
@@ -70,7 +70,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" telemetry resolve-sessi
|
|
|
70
70
|
node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" windows-entry --root-intent "<ROOT_INTENT>" --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
用户明确要求安装并通过 Skill 完成签约/开通或完整接入时(包括正式版保留的原任务与用户随后明确同意安装内测版的选择),当前任务已授权准备所需用户级依赖,附加 `--prepare-authorized true`;仅代码开发且未授权准备依赖时先只读检查。用户明确选择站点时才附加 `--site-selected true`。这不构成登录、签约或应用写入授权。只接受退出 0、唯一 `ALIPAY_AIPAY_INTERNAL:WINDOWS_ROUTE=<JSON>` 且 rootIntent 匹配、route 为 `READY|BLOCKED|LEGACY|CODE_ONLY`。非零、缺失或冲突按 `BLOCKED` 停止,不推定站点分支。`BLOCKED` 须说明内部结果给出的实际缺项/失败原因和有限恢复动作;用户修好依赖后先只读复检,必要且获授权时才按环境文档继续准备。只有不支持的执行映射或用户明确选择站点才进入 `LEGACY`。`integration_only`
|
|
73
|
+
用户明确要求安装并通过 Skill 完成签约/开通或完整接入时(包括正式版保留的原任务与用户随后明确同意安装内测版的选择),当前任务已授权准备所需用户级依赖,附加 `--prepare-authorized true`;仅代码开发且未授权准备依赖时先只读检查。用户明确选择站点时才附加 `--site-selected true`。这不构成登录、签约或应用写入授权。只接受退出 0、唯一 `ALIPAY_AIPAY_INTERNAL:WINDOWS_ROUTE=<JSON>` 且 rootIntent 匹配、route 为 `READY|BLOCKED|LEGACY|CODE_ONLY`。非零、缺失或冲突按 `BLOCKED` 停止,不推定站点分支。`BLOCKED` 须说明内部结果给出的实际缺项/失败原因和有限恢复动作;用户修好依赖后先只读复检,必要且获授权时才按环境文档继续准备。只有不支持的执行映射或用户明确选择站点才进入 `LEGACY`。`integration_only/full_process` 因 Git Bash、jq、CLI 等可选工具缺失、不可运行或有限准备失败时可返回 `CODE_ONLY`,继续原生 Node 代码开发,沙箱记 `VERIFY_PENDING`;完整接入的代码部分完成后停在开通前,不宣称完整接入完成。Windows 沙箱项目配置不要求精确 ACL 白名单;CLI 登录凭据目录仅在依赖登录的动作前检查实际可写性,失败只阻断该动作。Node 不足、实际存储故障、状态异常及未知错误仍阻断,不由 Agent 自行降级。所有后续 Windows runtime 命令追加 `--windows-route "<READY|BLOCKED|LEGACY|CODE_ONLY>"`;`BLOCKED` 不进入业务 bootstrap/flow。修复后只有唯一、根意图匹配的 `WINDOWS_ROUTE=READY` 才恢复快速沙箱与本地开通;`LEGACY` 保留原独立开发/手工沙箱及站点开通分支。`CODE_ONLY` 绑定本次根意图;根意图变化必须重新执行 `windows-entry`,不得继承旧分流。用户修好依赖或明确授权准备后,按 Windows 环境文档重新检查;未取得 READY 时继续独立开发,不循环安装。实际业务执行前仍复检能力。环境、沙箱准备或业务失败不修改采集选择、SESSION_ID、run_id、用户输入计数或阶段责任方。
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
`integration_only` 的 `integration-bootstrap` 在产品已唯一确定时必须携带 `--product-type`,并串行执行 `telemetry start-run` 与 `flow integration entry`;产品尚未唯一确定时省略产品且不创建正式 run,后续由 `integration-message start-confirm` 首次开 run并生成 `clarify_intent`。`onboarding_only` 的 `onboarding-bootstrap` 同理:产品已唯一时携带产品并先启动 run,尚未唯一时由 `mcc resolve-plan` 或 `onboarding-message plan-confirm` 首次启动。两者均为既有无业务写入动作,执行后不得重复原子命令。Windows READY 返回本地 entry 契约;Windows LEGACY 引导渲染依次使用现有平台项目 ID、正式 run_id;两者均不存在时使用已绑定 `SESSION_ID` 的稳定匿名哈希,不提前创建 run。非 Windows 返回 entry 契约。`full_process` 不使用 `integration-bootstrap`;衔接 Onboarding 也不使用带 `start-run` 的 bootstrap,仍执行 `node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" onboarding-message platform-entry --agent-platform "<AGENT_PLATFORM>" --session-id "<SESSION_ID>"` 后按需加载 entry,避免重复 `clarify_intent`。
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import {spawnSync} from 'node:child_process';
|
|
4
|
-
|
|
5
|
-
export function fail(reason, dependency = '') { throw Object.assign(new Error(reason), {reason, dependency}); }
|
|
6
|
-
export function run(command,args,options={}) {
|
|
7
|
-
return spawnSync(command,args,{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:4*1024*1024,...options});
|
|
8
|
-
}
|
|
9
|
-
export function powershell() {
|
|
10
|
-
const root=process.env.SystemRoot || 'C:\\Windows';
|
|
11
|
-
const command=path.join(root,'System32/WindowsPowerShell/v1.0/powershell.exe');
|
|
12
|
-
if(!fs.existsSync(command)) fail('DEPENDENCY_MISSING','PowerShell');
|
|
13
|
-
return command;
|
|
14
|
-
}
|
|
15
|
-
export function psEnv(env=process.env) {
|
|
16
|
-
return Object.fromEntries(Object.entries(env).filter(([k])=>k.toLowerCase()!=='psmodulepath'));
|
|
17
|
-
}
|
|
18
|
-
export function protect(target) {
|
|
19
|
-
if(fs.lstatSync(target).isSymbolicLink()||path.resolve(target)===path.parse(target).root)fail('ACL_FAILED','unsafe target');
|
|
20
|
-
const system=path.join(process.env.SystemRoot||'C:/Windows','System32');
|
|
21
|
-
const who=run(path.join(system,'whoami.exe'),['/user','/fo','csv','/nh']);
|
|
22
|
-
const sid=who.stdout?.match(/S-1-5-[0-9-]+/)?.[0];if(who.status!==0||!sid)fail('ACL_FAILED');
|
|
23
|
-
const env={...psEnv(),ALIPAY_WINDOWS_ACL_TARGET:path.resolve(target)};
|
|
24
|
-
// Native icacls changes permissions; PowerShell only reads ACL metadata.
|
|
25
|
-
// No .ps1 execution, ExecutionPolicy override, elevation, or policy mutation.
|
|
26
|
-
const inspect=()=>{
|
|
27
|
-
const r=run(powershell(),['-NoProfile','-NonInteractive','-Command',
|
|
28
|
-
'$ErrorActionPreference="Stop"; $p=$env:ALIPAY_WINDOWS_ACL_TARGET; $d=New-Object IO.DriveInfo([IO.Path]::GetPathRoot($p)); if($d.DriveFormat -ne "NTFS"){exit 2}; $a=Get-Acl -LiteralPath $p; @{protected=$a.AreAccessRulesProtected;rules=@($a.GetAccessRules($true,$true,[Security.Principal.SecurityIdentifier]) | ForEach-Object { @{sid=$_.IdentityReference.Value;type=[string]$_.AccessControlType;rights=[int]$_.FileSystemRights} })} | ConvertTo-Json -Depth 4 -Compress'],{env});
|
|
29
|
-
if(r.status!==0)fail('ACL_FAILED','NTFS');try{return JSON.parse(r.stdout);}catch{fail('ACL_FAILED');}
|
|
30
|
-
};
|
|
31
|
-
const allowed=[sid,'S-1-5-18','S-1-5-32-544'];inspect();
|
|
32
|
-
const inherited=fs.statSync(target).isDirectory()?'(OI)(CI)F':'F';
|
|
33
|
-
const args=[target,'/inheritance:r','/grant:r',...allowed.map(s=>'*'+s+':'+inherited)];
|
|
34
|
-
if(run(path.join(system,'icacls.exe'),args).status!==0)fail('ACL_FAILED','grant');
|
|
35
|
-
const remove=[...new Set(inspect().rules.filter(r=>!allowed.includes(r.sid)||r.type!=='Allow').map(r=>r.sid))];
|
|
36
|
-
for(const s of remove)if(run(path.join(system,'icacls.exe'),[target,'/remove','*'+s]).status!==0)fail('ACL_FAILED','remove');
|
|
37
|
-
// Reapply after removing any explicit deny ACE for an allowed identity.
|
|
38
|
-
if(remove.length&&run(path.join(system,'icacls.exe'),args).status!==0)fail('ACL_FAILED','regrant');
|
|
39
|
-
const after=inspect();
|
|
40
|
-
if(!after.protected||after.rules.some(r=>!allowed.includes(r.sid)||r.type!=='Allow')||!after.rules.some(r=>r.sid===sid&&(r.rights&2032127)===2032127))fail('ACL_FAILED','readback');
|
|
41
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Read OS facts instead of inherited process environment variables. The native
|
|
2
|
-
# machine from IsWow64Process2 distinguishes ARM64 even under x64 emulation.
|
|
3
|
-
function Get-AipayApiArchitecture {
|
|
4
|
-
if (-not ('AipayArchitecture.Native' -as [type])) {
|
|
5
|
-
Add-Type -TypeDefinition @'
|
|
6
|
-
using System;
|
|
7
|
-
using System.Runtime.InteropServices;
|
|
8
|
-
namespace AipayArchitecture {
|
|
9
|
-
public static class Native {
|
|
10
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
11
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
12
|
-
public static extern bool IsWow64Process2(IntPtr process, out ushort processMachine, out ushort nativeMachine);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
'@ -ErrorAction Stop
|
|
16
|
-
}
|
|
17
|
-
[UInt16]$processMachine = 0
|
|
18
|
-
[UInt16]$nativeMachine = 0
|
|
19
|
-
if (-not [AipayArchitecture.Native]::IsWow64Process2([IntPtr](-1), [ref]$processMachine, [ref]$nativeMachine)) {
|
|
20
|
-
throw 'ARCHITECTURE_CHECK_FAILED'
|
|
21
|
-
}
|
|
22
|
-
switch ($nativeMachine) {
|
|
23
|
-
0x8664 { return 'x64' }
|
|
24
|
-
0xAA64 { return 'arm64' }
|
|
25
|
-
0x014C { return 'ia32' }
|
|
26
|
-
0x01C4 { return 'arm' }
|
|
27
|
-
0x0200 { return 'ia64' }
|
|
28
|
-
default { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function Get-AipayNativeArchitecture {
|
|
33
|
-
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { throw 'UNSUPPORTED_PLATFORM' }
|
|
34
|
-
try { return Get-AipayApiArchitecture } catch {
|
|
35
|
-
# Older Windows or policy-restricted Add-Type: use the built-in CIM
|
|
36
|
-
# provider once. Never relax policy, install a tool, or infer from env.
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
$processors = @(Get-CimInstance -ClassName Win32_Processor -OperationTimeoutSec 5 -ErrorAction Stop)
|
|
40
|
-
if ($processors.Count -eq 0) { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
41
|
-
$values = @($processors | ForEach-Object {
|
|
42
|
-
if ($null -eq $_.Architecture) { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
43
|
-
[string]$_.Architecture
|
|
44
|
-
} | Select-Object -Unique)
|
|
45
|
-
if ($values.Count -ne 1) { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
46
|
-
switch ($values[0]) {
|
|
47
|
-
'9' { return 'x64' }
|
|
48
|
-
'12' { return 'arm64' }
|
|
49
|
-
'0' { return 'ia32' }
|
|
50
|
-
'5' { return 'arm' }
|
|
51
|
-
'6' { return 'ia64' }
|
|
52
|
-
default { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
53
|
-
}
|
|
54
|
-
} catch { throw 'ARCHITECTURE_CHECK_FAILED' }
|
|
55
|
-
}
|