rainskills 0.1.39 → 0.1.41

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/README.md CHANGED
@@ -67,6 +67,10 @@ npx --yes rainskills hermes
67
67
 
68
68
  安装流程支持 Codex、Claude Code、Pi Agent、DeepSeek Harness、WorkBuddy 和 Hermes Agent;所有 Agent 共用同一组 Skills、CLI profile 和本地 Rainskills CLI,不提供单独的客户端 MCP adapter。macOS、Linux 和 WSL 不支持 OpenClaw 安装。不要手工复制 Skill、拼接凭据或修改 Agent 配置。
69
69
 
70
+ 测试或发布烟测可在安装命令末尾追加 `--no-telemetry`,仅关闭当前进程的匿名安装遥测;普通安装不传该参数时保持默认遥测行为。
71
+
72
+ 非交互安装必须明确指定 `codex`、`claude`、`pi`、`dsh`、`workbuddy`、`hermes` 或 `all`;`all` 不再作为无 target 时的默认值。发布后验证统一执行 `npm run verify:published -- <version>`,该脚本会在临时 HOME 中显式安装全部目标并强制关闭遥测。
73
+
70
74
  ## 安装完成时用户会看到什么
71
75
 
72
76
  安装完成后只完成 Skills 和受保护本地 CLI 的安装并展示能力列表,不选择运行环境、不登录 Rainbond,也不配置 Agent MCP:
@@ -247,7 +251,7 @@ node ~/.rainbond/lib/rainskills/bin/rainskills.js platform install --onboarding-
247
251
 
248
252
  Rainskills 会在用户下一次发起业务动作时,由本地运行时立即返回环境查询结果,并另行启动后台任务静默检查更新。更新只跟随 npm `latest` 指向的正式版。当前版本是 RC 或其他预发布版本时不会查询、不会自动升级;npm 上的新 RC 版本也不参与正式版自动升级。
249
253
 
250
- 发现更高的正式版后,后台任务只委托到经过校验的精确版本,例如 `rainskills@0.1.39`,不会执行浮动的 `@latest` 业务代码。新版本原子刷新已经安装的 Rainskills Skills;当前业务继续使用启动时已经加载的版本,最迟从下一条新任务开始使用新版。npm 超时、版本检查失败、安装位置不安全或文件迁移失败时会保留旧版本,且不会阻塞或改变当前操作。
254
+ 发现更高的正式版后,后台任务只委托到经过校验的精确版本,例如 `rainskills@0.1.41`,不会执行浮动的 `@latest` 业务代码。新版本原子刷新已经安装的 Rainskills Skills;当前业务继续使用启动时已经加载的版本,最迟从下一条新任务开始使用新版。npm 超时、版本检查失败、安装位置不安全或文件迁移失败时会保留旧版本,且不会阻塞或改变当前操作。
251
255
 
252
256
  升级只更新 Rainskills 自身,不触发 Rainbond 安装、运行环境选择、登录授权或重新对接,也不会新增 Agent MCP 配置。更新内容仅包括 Skills 和本地 CLI。原始业务操作会继续执行;只有该业务操作本身需要运行环境时,才按既有门禁检查当前连接。可用连接直接复用,401 只重新授权一次,403 立即停止,从未连接过运行环境时才进入环境选择。
253
257
 
package/SKILL.md CHANGED
@@ -54,7 +54,7 @@ Hermes Agent 中必须使用 `terminal` 以 `background=true` 启动授权命令
54
54
 
55
55
  DeepSeek Harness 和 WorkBuddy 也必须保持原命令附着直到退出;如果宿主把长命令转成持久终端或后台任务,只能轮询该原始命令句柄,不得另起 `runtime status` 推测授权结果。WorkBuddy 安装后若当前任务的 Skill 清单尚未刷新,在下一条业务动作前先刷新 Skill 列表或新建任务,不得退回内置 Sites 代替 Rainbond 部署。
56
56
 
57
- Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.39`):
57
+ Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.41`):
58
58
 
59
59
  - 状态:执行 `runtime status --json`。
60
60
  - 首次连接:执行 `runtime connect <target> --saas` 或 `runtime connect <target> --rainbond-url <Console origin>`。
package/install.sh CHANGED
@@ -142,7 +142,6 @@ else
142
142
  bootstrap_download_if_needed "$@"
143
143
  fi
144
144
 
145
- DEFAULT_TARGET="all"
146
145
  ACTION=""
147
146
  TARGET=""
148
147
  FORCE=0
@@ -238,6 +237,7 @@ Options:
238
237
  --token JWT Use an existing Rainbond JWT, skip browser login
239
238
  --no-browser Do not open a local browser; print the authorization URL
240
239
  --no-cached-token Ignore RAINBOND_JWT inherited from the shell and re-login
240
+ --no-telemetry Disable anonymous installation telemetry for this run
241
241
  --username NAME Legacy: Rainbond login username (self-hosted only)
242
242
  --allow-insecure-http Allow plain HTTP for internal trial environments
243
243
  -h, --help Show this help message
@@ -877,7 +877,7 @@ rainskills_should_report_invocation() {
877
877
  local arg
878
878
  for arg in "$@"; do
879
879
  case "$arg" in
880
- -h|--help)
880
+ -h|--help|--no-telemetry)
881
881
  return 1
882
882
  ;;
883
883
  esac
@@ -1260,6 +1260,10 @@ parse_args() {
1260
1260
  NO_BROWSER=1
1261
1261
  shift
1262
1262
  ;;
1263
+ --no-telemetry)
1264
+ export RAINSKILLS_TELEMETRY_DISABLED=1
1265
+ shift
1266
+ ;;
1263
1267
  --saas)
1264
1268
  DEPLOYMENT_MODE_INPUT="saas"
1265
1269
  shift
@@ -1413,8 +1417,7 @@ resolve_target() {
1413
1417
  fi
1414
1418
 
1415
1419
  if [[ "$NON_INTERACTIVE" -eq 1 || ! -t 0 ]]; then
1416
- TARGET="$DEFAULT_TARGET"
1417
- return 0
1420
+ die "非交互安装必须明确指定 codex、claude、pi、dsh、workbuddy、hermes 或 all"
1418
1421
  fi
1419
1422
 
1420
1423
  log "请选择要安装和配置的平台:"
@@ -1454,7 +1457,7 @@ resolve_target() {
1454
1457
  TARGET="hermes"
1455
1458
  return 0
1456
1459
  ;;
1457
- 7|"")
1460
+ 7)
1458
1461
  TARGET="all"
1459
1462
  return 0
1460
1463
  ;;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainskills",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Install the complete Rainskills AI deployment skill suite as one product.",
5
5
  "author": {
6
6
  "name": "Goodrain",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainskills",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Install the complete Rainskills AI deployment skill suite as one product.",
5
5
  "author": {
6
6
  "name": "Goodrain",
@@ -14,7 +14,7 @@ This is the single marketplace entry for the complete Rainskills product. The in
14
14
  3. Keep stdin, stdout, and stderr attached. When `RAINSKILLS_USER_INPUT_REQUIRED` appears, pause for that installer choice. If the installer emits `rainskills.next-action.v1`, execute only its fixed `argv` through the same launcher; never evaluate output as a shell command. If the adjacent `bin/rainskills.js` exists, use it for fixed next actions; otherwise use the same versioned npm package fallback described below.
15
15
  4. Stay attached until every independent Skill is installed. Do not select, connect, or configure an application runtime during installation. In the user-facing response, output only the fixed completion message below.
16
16
 
17
- If the adjacent installer is missing, check the local Node.js version before choosing the fallback. With `npx` and Node.js 18 or newer, use `npx --yes rainskills@0.1.39 <target>`. With no Node.js or a version below 18, use `bash <(curl -fsSL https://get.rainbond.com/rainskills/install.sh) <target>` instead. Omit `<target>` only when the host cannot be determined reliably. Keep either command attached to the interactive terminal. For an update or repair, refresh this marketplace Skill first, then run the installer again; it compares and updates every independent internal Skill.
17
+ If the adjacent installer is missing, check the local Node.js version before choosing the fallback. With `npx` and Node.js 18 or newer, use `npx --yes rainskills@0.1.41 <target>`. With no Node.js or a version below 18, use `bash <(curl -fsSL https://get.rainbond.com/rainskills/install.sh) <target>` instead. Omit `<target>` only when the host cannot be determined reliably. Keep either command attached to the interactive terminal. For an update or repair, refresh this marketplace Skill first, then run the installer again; it compares and updates every independent internal Skill.
18
18
 
19
19
  Skills-only 安装不需要 Node.js;CDN fallback 只负责安装 Skill 文件,不代表运行环境连接、应用部署或平台安装已经可执行。用户首次提出需要运行环境的动作时,对应业务 Skill 才检查 Node.js;固定 Rainskills launcher 需要 Node.js 18 或更高版本。缺失或版本过低时保留原始 intent 并停止,等待用户或 agent 明确同意安装或升级 Node.js,安装完成消息不得提前提示 Node.js。
20
20
 
@@ -54,7 +54,7 @@ Hermes Agent 中必须使用 `terminal` 以 `background=true` 启动授权命令
54
54
 
55
55
  DeepSeek Harness 和 WorkBuddy 也必须保持原命令附着直到退出;如果宿主把长命令转成持久终端或后台任务,只能轮询该原始命令句柄,不得另起 `runtime status` 推测授权结果。WorkBuddy 安装后若当前任务的 Skill 清单尚未刷新,在下一条业务动作前先刷新 Skill 列表或新建任务,不得退回内置 Sites 代替 Rainbond 部署。
56
56
 
57
- Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.39`):
57
+ Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.41`):
58
58
 
59
59
  - 状态:执行 `runtime status --json`。
60
60
  - 首次连接:执行 `runtime connect <target> --saas` 或 `runtime connect <target> --rainbond-url <Console origin>`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainskills",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Interactive Rainbond skill installer for Codex, Claude Code, Pi Agent, DeepSeek Harness, WorkBuddy, and Hermes Agent",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/goodrain/rainskills#readme",
@@ -64,7 +64,10 @@
64
64
  "check:runtime-contracts": "node scripts/sync-runtime-contracts.mjs --check",
65
65
  "version": "npm run sync:runtime-version && npm run sync:runtime-contracts && npm run build:marketplace",
66
66
  "prepack": "node scripts/sync-runtime-version.mjs --check && node scripts/sync-runtime-contracts.mjs --check && node scripts/build-marketplace-package.mjs --check --quiet",
67
- "test": "npm run test:runtime-version && npm run test:runtime-contracts && npm run test:auto-update && npm run test:launcher && npm run test:api-bridge && npm run test:console-contract && npm run test:skill-profile && npm run test:marketplace && npm run test:runtime-routing && npm run test:telemetry && npm run test:platform && npm run test:windows && npm run test:package-upload && npm run test:package && npm run test:installer && npm run test:signal && npm run test:npx-pty",
67
+ "test": "node scripts/run-test-suite.js",
68
+ "test:test-runner": "node --test tests/test-suite-runner.test.js",
69
+ "test:published-verifier": "node --test tests/published-package-verifier.test.js",
70
+ "verify:published": "node scripts/verify-published-package.js",
68
71
  "test:runtime-version": "node --test tests/runtime-version-sync.test.js",
69
72
  "test:runtime-contracts": "node scripts/sync-runtime-contracts.mjs --check",
70
73
  "test:auto-update": "node --test tests/auto-update.test.js",
@@ -5,7 +5,7 @@ Canonical progressive-loading contract: `rainskills.skill-runtime-contract.v1`.
5
5
  <!-- rainskills-runtime-gate:start -->
6
6
  ## 单运行环境 CLI 门禁(最高优先级)
7
7
 
8
- 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true`,并且 `package_version` 与本 Skill JSON contract 的 `package_version` 完全一致后,所有查询和变更才可通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。缺少或不一致时必须先执行当前固定版本 `rainskills@0.1.39` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
8
+ 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true`,并且 `package_version` 与本 Skill JSON contract 的 `package_version` 完全一致后,所有查询和变更才可通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。缺少或不一致时必须先执行当前固定版本 `rainskills@0.1.41` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
9
9
 
10
10
  没有运行环境时,让用户选择 Rainbond Cloud 或一个已有/新建的私有 Rainbond,并执行对应的 `runtime connect`。连接和重新授权必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT;新凭据通过 live probe 后才覆盖唯一运行环境。CLI 返回 401 时,只读调用可在 `runtime reconnect` 成功后重试一次;写调用不得自动重放,必须先查询平台真实状态。403 直接停止,不重新授权。
11
11
 
@@ -26,7 +26,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
26
26
  ```json
27
27
  {
28
28
  "schema": "rainskills.single-runtime-contract.v1",
29
- "package_version": "rainskills@0.1.39",
29
+ "package_version": "rainskills@0.1.41",
30
30
  "runtime_status": [
31
31
  "node",
32
32
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -38,7 +38,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
38
38
  ```json
39
39
  {
40
40
  "schema": "rainskills.single-runtime-contract.v1",
41
- "package_version": "rainskills@0.1.39",
41
+ "package_version": "rainskills@0.1.41",
42
42
  "runtime_status": [
43
43
  "node",
44
44
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -68,7 +68,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
68
68
  ```json
69
69
  {
70
70
  "schema": "rainskills.single-runtime-contract.v1",
71
- "package_version": "rainskills@0.1.39",
71
+ "package_version": "rainskills@0.1.41",
72
72
  "runtime_status": [
73
73
  "node",
74
74
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -38,7 +38,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
38
38
  ```json
39
39
  {
40
40
  "schema": "rainskills.single-runtime-contract.v1",
41
- "package_version": "rainskills@0.1.39",
41
+ "package_version": "rainskills@0.1.41",
42
42
  "runtime_status": [
43
43
  "node",
44
44
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -17,7 +17,7 @@ description: "Use only when the user explicitly asks to create the Rainbond app
17
17
  <!-- rainskills-runtime-gate:start -->
18
18
  ## 单运行环境 CLI 门禁(最高优先级)
19
19
 
20
- 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true`,并且 `package_version` 与本 Skill JSON contract 的 `package_version` 完全一致后,所有查询和变更才可通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。缺少或不一致时必须先执行当前固定版本 `rainskills@0.1.39` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
20
+ 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true`,并且 `package_version` 与本 Skill JSON contract 的 `package_version` 完全一致后,所有查询和变更才可通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。缺少或不一致时必须先执行当前固定版本 `rainskills@0.1.41` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
21
21
 
22
22
  没有运行环境时,让用户选择 Rainbond Cloud 或一个已有/新建的私有 Rainbond,并执行对应的 `runtime connect`。连接和重新授权必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT;新凭据通过 live probe 后才覆盖唯一运行环境。CLI 返回 401 时,只读调用可在 `runtime reconnect` 成功后重试一次;写调用不得自动重放,必须先查询平台真实状态。403 直接停止,不重新授权。
23
23
 
@@ -38,7 +38,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
38
38
  ```json
39
39
  {
40
40
  "schema": "rainskills.single-runtime-contract.v1",
41
- "package_version": "rainskills@0.1.39",
41
+ "package_version": "rainskills@0.1.41",
42
42
  "runtime_status": [
43
43
  "node",
44
44
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -61,7 +61,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
61
61
  ```json
62
62
  {
63
63
  "schema": "rainskills.single-runtime-contract.v1",
64
- "package_version": "rainskills@0.1.39",
64
+ "package_version": "rainskills@0.1.41",
65
65
  "runtime_status": [
66
66
  "node",
67
67
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -26,7 +26,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
26
26
  ```json
27
27
  {
28
28
  "schema": "rainskills.single-runtime-contract.v1",
29
- "package_version": "rainskills@0.1.39",
29
+ "package_version": "rainskills@0.1.41",
30
30
  "runtime_status": [
31
31
  "node",
32
32
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -32,7 +32,7 @@ Do not use it to deploy an application to an existing Rainbond. Route those requ
32
32
  ## Workflow
33
33
 
34
34
  1. Read [installation-policy.md](references/installation-policy.md).
35
- 2. Use the installed local launcher `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`; its protected runtime package marker is `rainskills@0.1.39` and must equal this package's `package.json`. For a Rainskills marker, first validate schema `rainskills.next-action.v1`, action, onboarding id, and the bounded `argv` array, then append that array to the launcher. Never use `latest` or evaluate a shell string from output.
35
+ 2. Use the installed local launcher `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`; its protected runtime package marker is `rainskills@0.1.41` and must equal this package's `package.json`. For a Rainskills marker, first validate schema `rainskills.next-action.v1`, action, onboarding id, and the bounded `argv` array, then append that array to the launcher. Never use `latest` or evaluate a shell string from output.
36
36
  3. 业务 Skill 的四项运行环境菜单会把本机或独立服务器选择写入 `rainskills.next-action.v1` 的显式 `--location`;收到这类 next-action 后直接执行固定 argv,不得再次调用 `private-deployment-location`。只有用户直接要求安装 Rainbond 平台且尚未选择部署位置时,才执行 launcher + `["runtime", "message", "--id", "private-deployment-location"]` 并原样输出固定的三项部署位置消息:选择 1 后执行带 `["--location", "local", "--mode", "single-node"]` 的 `platform install`;选择 2 后执行带 `["--location", "server"]` 的 `platform install`,由 helper 继续显示固定的服务器类型消息;选择 3 后执行 launcher + `["runtime", "message", "--id", "private-console-origin"]` 并进入已有环境连接,不得执行 `platform install`。平台安装 onboarding 只保存安装断点,不保存或恢复业务 intent。
37
37
  4. Let the helper perform one read-only preflight against the already selected local or remote target, then show resources, blockers, and applicable host changes. Never invoke `platform install` without an explicit `--location`; the helper must not ask for the deployment location again.
38
38
  5. 主机集群开始前必须获得 explicit confirmation,并使用受限 AI 交接:首次调用在固定安装 argv 后追加 `--agent-handoff`,记录该子进程会话;用户确认后,使用相同固定 argv 追加 `--agent-handoff --yes` 一次。不得向等待进程写入 `y`、不得启动第二个竞争安装、不得 `kill` 安装进程,也不得让用户复制完整安装或恢复命令。用户取消时,仅使用同一 argv 追加 `--agent-handoff --cancel`;它只能清除匹配的待确认状态,不能建立 SSH 连接或修改服务器。
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
 
3
3
  module.exports = Object.freeze({
4
- version: "0.1.39",
4
+ version: "0.1.41",
5
5
  });
@@ -8,6 +8,12 @@ const { spawn } = require("node:child_process");
8
8
  const JWT_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
9
9
  const USER_CODE_PATTERN = /^[23456789BCDFGHJKMNPQRTVWXY]{4}-[23456789BCDFGHJKMNPQRTVWXY]{4}$/;
10
10
 
11
+ function authorizationError(message, code) {
12
+ const error = new Error(message);
13
+ error.code = code;
14
+ return error;
15
+ }
16
+
11
17
  function looksLikeJwt(value) {
12
18
  return JWT_PATTERN.test(String(value || ""));
13
19
  }
@@ -56,7 +62,7 @@ function authorizeWithLoopback({
56
62
  }
57
63
 
58
64
  function abort() {
59
- settle(new Error("Rainbond 浏览器授权已取消"));
65
+ settle(authorizationError("Rainbond 浏览器授权已取消", "DEVICE_AUTHORIZATION_CANCELLED"));
60
66
  }
61
67
 
62
68
  function writeResponse(request, response, status, success) {
@@ -134,7 +140,9 @@ function authorizeWithLoopback({
134
140
  return;
135
141
  }
136
142
  if (signal) signal.addEventListener("abort", abort, { once: true });
137
- timer = setTimeout(() => settle(new Error("Rainbond 浏览器授权超时")), timeoutMs);
143
+ timer = setTimeout(() => settle(
144
+ authorizationError("Rainbond 浏览器授权超时", "DEVICE_AUTHORIZATION_EXPIRED")
145
+ ), timeoutMs);
138
146
  server.listen(0, "127.0.0.1", async () => {
139
147
  const address = server.address();
140
148
  const callback = `http://127.0.0.1:${address.port}/cli-callback`;
@@ -151,7 +159,9 @@ function authorizeWithLoopback({
151
159
  }
152
160
 
153
161
  function abortIfNeeded(signal) {
154
- if (signal?.aborted) throw new Error("Rainbond 设备授权已取消");
162
+ if (signal?.aborted) {
163
+ throw authorizationError("Rainbond 设备授权已取消", "DEVICE_AUTHORIZATION_CANCELLED");
164
+ }
155
165
  }
156
166
 
157
167
  async function jsonResponse(response) {
@@ -214,7 +224,9 @@ async function authorizeWithDeviceFlow({
214
224
  abortIfNeeded(signal);
215
225
  await sleep(interval);
216
226
  abortIfNeeded(signal);
217
- if (now() >= deadline) throw new Error("Rainbond 设备授权超时");
227
+ if (now() >= deadline) {
228
+ throw authorizationError("Rainbond 设备授权超时", "DEVICE_AUTHORIZATION_EXPIRED");
229
+ }
218
230
  let response;
219
231
  try {
220
232
  response = await fetchImpl(`${normalizedBase}/console/mcp/device/token`, {
@@ -254,8 +266,12 @@ async function authorizeWithDeviceFlow({
254
266
  interval += 5;
255
267
  continue;
256
268
  }
257
- if (payload.error === "access_denied") throw new Error("你已拒绝 Rainbond 授权");
258
- if (payload.error === "expired_token") throw new Error("Rainbond 设备授权码已过期");
269
+ if (payload.error === "access_denied") {
270
+ throw authorizationError("你已拒绝 Rainbond 授权", "DEVICE_AUTHORIZATION_DENIED");
271
+ }
272
+ if (payload.error === "expired_token") {
273
+ throw authorizationError("Rainbond 设备授权码已过期", "DEVICE_AUTHORIZATION_EXPIRED");
274
+ }
259
275
  throw new Error(`Rainbond Device Flow 轮询失败(HTTP ${response.status})`);
260
276
  }
261
277
  }
@@ -88,6 +88,19 @@ function requireValue(argv, index, option) {
88
88
  return argv[index + 1];
89
89
  }
90
90
 
91
+ function authorizationTelemetryCode(error) {
92
+ switch (error?.code) {
93
+ case "DEVICE_AUTHORIZATION_DENIED":
94
+ return "device_authorization_denied";
95
+ case "DEVICE_AUTHORIZATION_EXPIRED":
96
+ return "device_authorization_expired";
97
+ case "DEVICE_AUTHORIZATION_CANCELLED":
98
+ return "user_cancelled";
99
+ default:
100
+ return "authorization_failed";
101
+ }
102
+ }
103
+
91
104
  function parseWindowsInstallerArgs(argv) {
92
105
  const options = {
93
106
  target: "",
@@ -100,6 +113,7 @@ function parseWindowsInstallerArgs(argv) {
100
113
  deploymentMode: "",
101
114
  allowInsecureHttp: false,
102
115
  noBrowser: false,
116
+ noTelemetry: false,
103
117
  help: false,
104
118
  };
105
119
 
@@ -131,6 +145,8 @@ function parseWindowsInstallerArgs(argv) {
131
145
  options.allowInsecureHttp = true;
132
146
  } else if (argument === "--no-browser") {
133
147
  options.noBrowser = true;
148
+ } else if (argument === "--no-telemetry") {
149
+ options.noTelemetry = true;
134
150
  } else if (argument === "-h" || argument === "--help") {
135
151
  options.help = true;
136
152
  } else {
@@ -407,14 +423,15 @@ async function authorizeAndConfigure({
407
423
  });
408
424
  } catch (error) {
409
425
  if (error.code !== "DEVICE_FLOW_UNSUPPORTED") {
426
+ const failureCode = authorizationTelemetryCode(error);
410
427
  telemetry.record({
411
428
  lifecycle_phase: "authorize_device_flow",
412
429
  step: "device_code",
413
430
  lifecycle_action: "authorize",
414
431
  lifecycle_status: "failed",
415
- error_code: "authorization_failed",
432
+ error_code: failureCode,
416
433
  error_stage: "authorize_device_flow",
417
- reason_code: "authorization_failed",
434
+ reason_code: failureCode,
418
435
  auth_method: "device_flow",
419
436
  transport: "powershell",
420
437
  });
@@ -542,7 +559,7 @@ async function promptTarget() {
542
559
  if (answer === "4") return "dsh";
543
560
  if (answer === "5") return "workbuddy";
544
561
  if (answer === "6") return "hermes";
545
- if (answer === "" || answer === "7") return "all";
562
+ if (answer === "7") return "all";
546
563
  stdout.write("请输入 1、2、3、4、5、6 或 7。\n");
547
564
  }
548
565
  } finally {
@@ -632,11 +649,13 @@ async function main(argv, dependencies = {}) {
632
649
  || environment.HERMES_AGENT === "true"
633
650
  ? "hermes"
634
651
  : "";
635
- const target = options.target || detectedTarget || (
636
- options.nonInteractive || !stdin.isTTY
637
- ? "all"
638
- : await (dependencies.promptTarget || promptTarget)()
639
- );
652
+ let target = options.target || detectedTarget;
653
+ if (!target && !options.customDest) {
654
+ if (options.nonInteractive || !stdin.isTTY) {
655
+ throw new Error("非交互安装必须明确指定 codex、claude、pi、dsh、workbuddy、hermes 或 all");
656
+ }
657
+ target = await (dependencies.promptTarget || promptTarget)();
658
+ }
640
659
  const destinations = options.customDest
641
660
  ? [path.resolve(options.customDest)]
642
661
  : destinationsForTarget(target, home, environment);
@@ -656,13 +675,14 @@ async function main(argv, dependencies = {}) {
656
675
  ? environment.RAINSKILLS_INSTALL_ATTEMPT_ID
657
676
  : crypto.randomUUID();
658
677
  const osArch = os.arch() === "x64" ? "amd64" : os.arch() === "arm64" ? "arm64" : "other";
678
+ const telemetryDisabled = options.noTelemetry || environment.RAINSKILLS_TELEMETRY_DISABLED === "1";
659
679
  const resultTelemetry = (() => {
660
680
  try {
661
681
  return (dependencies.resultTelemetryFactory || createResultTelemetry)({
662
682
  directory: telemetryDirectory,
663
683
  packageVersion,
664
684
  agentType: "unknown",
665
- disabled: environment.RAINSKILLS_TELEMETRY_DISABLED === "1",
685
+ disabled: telemetryDisabled,
666
686
  installAttemptId,
667
687
  osArch,
668
688
  });
@@ -707,7 +727,7 @@ async function main(argv, dependencies = {}) {
707
727
  status: "success",
708
728
  });
709
729
  deliveries.push(installResult.delivery);
710
- if (environment.RAINSKILLS_TELEMETRY_DISABLED !== "1") {
730
+ if (!telemetryDisabled) {
711
731
  try {
712
732
  (dependencies.configuredAgentsWriter || writeConfiguredAgents)(
713
733
  telemetryDirectory,
@@ -29,7 +29,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
29
29
  ```json
30
30
  {
31
31
  "schema": "rainskills.single-runtime-contract.v1",
32
- "package_version": "rainskills@0.1.39",
32
+ "package_version": "rainskills@0.1.41",
33
33
  "runtime_status": [
34
34
  "node",
35
35
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -38,7 +38,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
38
38
  ```json
39
39
  {
40
40
  "schema": "rainskills.single-runtime-contract.v1",
41
- "package_version": "rainskills@0.1.39",
41
+ "package_version": "rainskills@0.1.41",
42
42
  "runtime_status": [
43
43
  "node",
44
44
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
@@ -38,7 +38,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
38
38
  ```json
39
39
  {
40
40
  "schema": "rainskills.single-runtime-contract.v1",
41
- "package_version": "rainskills@0.1.39",
41
+ "package_version": "rainskills@0.1.41",
42
42
  "runtime_status": [
43
43
  "node",
44
44
  "<home>/.rainbond/lib/rainskills/bin/rainskills.js",