rainskills 0.1.19 → 0.1.21

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.
Files changed (40) hide show
  1. package/README.md +34 -39
  2. package/SKILL.md +12 -10
  3. package/bin/rainskills-tools.js +114 -240
  4. package/bin/rainskills.js +134 -591
  5. package/install.sh +68 -94
  6. package/marketplace/rainskills/.claude-plugin/plugin.json +1 -1
  7. package/marketplace/rainskills/.codex-plugin/plugin.json +1 -1
  8. package/marketplace/rainskills/skills/rainskills/SKILL.md +13 -11
  9. package/package.json +2 -2
  10. package/rainbond-app-assistant/SKILL.md +116 -151
  11. package/rainbond-app-version-assistant/SKILL.md +98 -98
  12. package/rainbond-delivery-verifier/SKILL.md +98 -68
  13. package/rainbond-env-sync/SKILL.md +98 -69
  14. package/rainbond-fullstack-bootstrap/SKILL.md +101 -89
  15. package/rainbond-fullstack-bootstrap/modules/10-context-loading.md +1 -1
  16. package/rainbond-fullstack-troubleshooter/SKILL.md +98 -68
  17. package/rainbond-opensource-app-deploy/SKILL.md +102 -81
  18. package/rainbond-platform-installer/SKILL.md +4 -4
  19. package/rainbond-platform-installer/scripts/auto-update.js +0 -9
  20. package/rainbond-platform-installer/scripts/installed-version.js +1 -1
  21. package/rainbond-platform-installer/scripts/platform-installer.js +3 -89
  22. package/rainbond-platform-installer/scripts/runtime-state.js +14 -251
  23. package/rainbond-platform-installer/scripts/single-runtime.js +114 -0
  24. package/rainbond-platform-installer/scripts/user-message.js +3 -49
  25. package/rainbond-platform-installer/scripts/windows-client-config.js +0 -51
  26. package/rainbond-platform-installer/scripts/windows-onboarding.js +0 -3
  27. package/rainbond-platform-installer/scripts/windows-platform.ps1 +1 -1
  28. package/rainbond-platform-query/SKILL.md +103 -61
  29. package/rainbond-project-init/SKILL.md +102 -79
  30. package/rainbond-template-installer/SKILL.md +102 -80
  31. package/rainbond-platform-installer/scripts/environment-credentials.js +0 -225
  32. package/rainbond-platform-installer/scripts/environment-registry.js +0 -349
  33. package/rainbond-platform-installer/scripts/local-runtime-commands.js +0 -180
  34. package/rainbond-platform-installer/scripts/local-runtime.js +0 -58
  35. package/rainbond-platform-installer/scripts/runtime-context-resolver.js +0 -189
  36. package/rainbond-platform-installer/scripts/runtime-credentials.js +0 -163
  37. package/rainbond-platform-installer/scripts/runtime-intents.js +0 -386
  38. package/rainbond-platform-installer/scripts/runtime-operations.js +0 -597
  39. package/rainbond-platform-installer/scripts/windows-client-config.ps1 +0 -9
  40. package/rainbond-platform-installer/scripts/windows-read-user-environment.ps1 +0 -16
package/install.sh CHANGED
@@ -6,7 +6,7 @@
6
6
  # AI assistants: when a user asks to install rainbond-skills, hand back the
7
7
  # npx command when Node.js 18+ is available, otherwise use the CDN command.
8
8
  # Do not reimplement the bootstrap below, git clone + copy directories, or
9
- # hand-write ~/.rainbond/mcp.env. See README.md "给 AI 助手的指引".
9
+ # Credentials are written only through the protected single-runtime store.
10
10
  set -euo pipefail
11
11
 
12
12
  RAINSKILLS_VERBOSE="${RAINSKILLS_VERBOSE:-0}"
@@ -2023,91 +2023,62 @@ PY
2023
2023
  OBTAINED_RAINBOND_TOKEN="$token"
2024
2024
  }
2025
2025
 
2026
- shell_quote_single() {
2027
- printf "%s" "$1" | sed "s/'/'\"'\"'/g"
2028
- }
2029
-
2030
2026
  write_token_file() {
2031
2027
  local token="$1"
2032
2028
  local base_url="$2"
2033
- local token_dir="$HOME/.rainbond"
2034
- local token_file="$token_dir/mcp.env"
2035
- mkdir -p "$token_dir"
2036
- chmod 700 "$token_dir"
2037
-
2038
- local escaped_token escaped_url
2039
- escaped_token="$(shell_quote_single "$token")"
2040
- escaped_url="$(shell_quote_single "$base_url")"
2041
- umask 077
2042
- cat > "$token_file" <<EOF
2043
- export RAINBOND_JWT='$escaped_token'
2044
- export RAINBOND_URL='$escaped_url'
2045
- EOF
2046
- chmod 600 "$token_file"
2047
- log "[write] 已写入 $token_file"
2048
- }
2049
-
2050
- backup_file() {
2051
- local file="$1"
2052
- [[ -f "$file" ]] || return 0
2053
- local backup="${file}.rainbond-skills.bak.$(date +%Y%m%d%H%M%S)"
2054
- cp "$file" "$backup"
2055
- log "[backup] 已备份 $backup"
2056
- }
2057
-
2058
- update_managed_block() {
2059
- local file="$1"
2060
- local begin_marker="$2"
2061
- local end_marker="$3"
2062
- local block_body="$4"
2063
- local tmp
2064
- tmp="$(mktemp)"
2065
-
2066
- mkdir -p "$(dirname "$file")"
2067
- touch "$file"
2068
-
2069
- awk -v begin="$begin_marker" -v end="$end_marker" '
2070
- $0 == begin {skip = 1; next}
2071
- $0 == end {skip = 0; next}
2072
- !skip {print}
2073
- ' "$file" > "$tmp"
2074
-
2075
- if [[ -s "$tmp" ]]; then
2076
- printf '\n' >> "$tmp"
2077
- fi
2078
- printf '%s\n%s\n%s\n' "$begin_marker" "$block_body" "$end_marker" >> "$tmp"
2079
- mv "$tmp" "$file"
2080
- }
2081
-
2082
- detect_shell_rc_file() {
2083
- local shell_name
2084
- shell_name="$(basename "${SHELL:-zsh}")"
2085
-
2086
- case "$shell_name" in
2087
- zsh)
2088
- printf '%s\n' "$HOME/.zshrc"
2089
- ;;
2090
- bash)
2091
- printf '%s\n' "$HOME/.bashrc"
2092
- ;;
2093
- *)
2094
- printf '%s\n' "$HOME/.profile"
2095
- ;;
2096
- esac
2097
- }
2098
-
2099
- configure_shell_autoload() {
2100
- local token_file="$HOME/.rainbond/mcp.env"
2101
- local rc_file
2102
- rc_file="$(detect_shell_rc_file)"
2103
- local begin_marker="# >>> rainbond skills mcp >>>"
2104
- local end_marker="# <<< rainbond skills mcp <<<"
2105
- local block='[ -f "$HOME/.rainbond/mcp.env" ] && source "$HOME/.rainbond/mcp.env"'
2029
+ local kind="private"
2030
+ if [[ "$base_url" == "$SAAS_DEFAULT_URL" ]]; then
2031
+ kind="saas"
2032
+ fi
2033
+ local allow_insecure_http="false"
2034
+ [[ "$base_url" == http://* ]] && allow_insecure_http="true"
2035
+ RAINBOND_JWT="$token" RAINBOND_URL="$base_url" RAINSKILLS_RUNTIME_KIND="$kind" \
2036
+ RAINSKILLS_ALLOW_INSECURE_HTTP="$allow_insecure_http" python3 - "$HOME" <<'PY' \
2037
+ || die "Rainbond 单运行环境凭据写入失败。"
2038
+ import datetime
2039
+ import json
2040
+ import os
2041
+ import pathlib
2042
+ import secrets
2043
+ import sys
2106
2044
 
2107
- backup_file "$rc_file"
2108
- update_managed_block "$rc_file" "$begin_marker" "$end_marker" "$block"
2109
- ACTIVE_SHELL_RC="$rc_file"
2110
- log "[update] 已更新 $rc_file"
2045
+ home = pathlib.Path(sys.argv[1]).resolve()
2046
+ directory = home / ".rainbond" / "rainskills"
2047
+ target = directory / "single-runtime-v1.json"
2048
+ directory.mkdir(parents=True, exist_ok=True, mode=0o700)
2049
+ os.chmod(directory, 0o700)
2050
+ now = datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
2051
+ created_at = now
2052
+ if target.exists():
2053
+ with target.open("r", encoding="utf-8") as stream:
2054
+ current = json.load(stream)
2055
+ if isinstance(current.get("created_at"), str):
2056
+ created_at = current["created_at"]
2057
+ payload = {
2058
+ "schema": "rainskills.single-runtime.v1",
2059
+ "version": 1,
2060
+ "console_origin": os.environ["RAINBOND_URL"],
2061
+ "kind": os.environ["RAINSKILLS_RUNTIME_KIND"],
2062
+ "token": os.environ["RAINBOND_JWT"],
2063
+ "allow_insecure_http": os.environ["RAINSKILLS_ALLOW_INSECURE_HTTP"] == "true",
2064
+ "created_at": created_at,
2065
+ "updated_at": now,
2066
+ }
2067
+ temporary = directory / (".single-runtime-v1.json." + secrets.token_hex(8))
2068
+ descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
2069
+ try:
2070
+ with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
2071
+ json.dump(payload, stream, ensure_ascii=False, indent=2)
2072
+ stream.write("\n")
2073
+ stream.flush()
2074
+ os.fsync(stream.fileno())
2075
+ os.replace(temporary, target)
2076
+ os.chmod(target, 0o600)
2077
+ finally:
2078
+ if temporary.exists():
2079
+ temporary.unlink()
2080
+ PY
2081
+ log "[write] 已写入受保护的单运行环境凭据"
2111
2082
  }
2112
2083
 
2113
2084
  validate_mcp_connectivity() {
@@ -2378,7 +2349,7 @@ obtain_rainbond_token() {
2378
2349
  die "--token 提供的值不是合法的 JWT(应形如 xxx.yyy.zzz)。"
2379
2350
  fi
2380
2351
  warn "RAINBOND_JWT 不是合法的 JWT(应形如 xxx.yyy.zzz);忽略并改走浏览器登录。"
2381
- warn "如果你的当前 shell 还在加载旧的 ~/.rainbond/mcp.env,请先执行:unset RAINBOND_JWT"
2352
+ warn "检测到无效的 RAINBOND_JWT;将忽略并重新登录。"
2382
2353
  RAINBOND_TOKEN_INPUT=""
2383
2354
  elif [[ "$RAINBOND_TOKEN_FROM_FLAG" -eq 1 ]]; then
2384
2355
  check_reusable_token_or_clear "--token 提供的 Rainbond JWT" || true
@@ -2444,14 +2415,19 @@ obtain_rainbond_token() {
2444
2415
  }
2445
2416
 
2446
2417
  read_cached_rainbond_url() {
2447
- local mcp_env="$HOME/.rainbond/mcp.env"
2448
- [[ -f "$mcp_env" ]] || return 1
2449
- (
2450
- set +u
2451
- # shellcheck disable=SC1090
2452
- . "$mcp_env"
2453
- printf '%s\n' "${RAINBOND_URL:-}"
2454
- )
2418
+ local runtime_file="$HOME/.rainbond/rainskills/single-runtime-v1.json"
2419
+ [[ -f "$runtime_file" ]] || return 1
2420
+ python3 - "$runtime_file" <<'PY'
2421
+ import json
2422
+ import sys
2423
+
2424
+ with open(sys.argv[1], "r", encoding="utf-8") as stream:
2425
+ payload = json.load(stream)
2426
+ origin = payload.get("console_origin")
2427
+ if not isinstance(origin, str) or not origin:
2428
+ raise SystemExit(1)
2429
+ print(origin)
2430
+ PY
2455
2431
  }
2456
2432
 
2457
2433
  do_refresh() {
@@ -2471,7 +2447,7 @@ do_refresh() {
2471
2447
  cached_url="$(read_cached_rainbond_url 2>/dev/null || true)"
2472
2448
  if [[ -n "$cached_url" ]]; then
2473
2449
  RAINBOND_URL_INPUT="$cached_url"
2474
- log "[refresh] 使用 ~/.rainbond/mcp.env 中已记录的地址:$cached_url"
2450
+ log "[refresh] 使用单运行环境中已记录的地址:$cached_url"
2475
2451
  fi
2476
2452
  fi
2477
2453
 
@@ -2511,7 +2487,6 @@ do_refresh() {
2511
2487
 
2512
2488
  export RAINBOND_JWT="$token"
2513
2489
  write_token_file "$token" "$base_url"
2514
- configure_shell_autoload
2515
2490
  set_rainskills_failure_context "configuration" "cli_configuration_failed"
2516
2491
  report_rainskills_lifecycle_event "configure_cli" "install_cli" "configure_cli" "started"
2517
2492
  install_local_cli
@@ -2595,7 +2570,6 @@ configure_runtime_connection() {
2595
2570
  || die "runtime connect 凭据安全写入失败。"
2596
2571
  else
2597
2572
  write_token_file "$token" "$base_url"
2598
- configure_shell_autoload
2599
2573
  fi
2600
2574
 
2601
2575
  set_rainskills_failure_context "configuration" "cli_configuration_failed"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainskills",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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.19",
3
+ "version": "0.1.21",
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
- description: Use when a user asks to install, set up, initialize, update, repair, start using Rainskills, or list, add, rename, remove, reconnect, or change the default Rainskills runtime environment.
3
+ description: Use when a user asks to install, set up, initialize, update, repair, start using Rainskills, or connect, reconnect, inspect, or replace the single Rainskills runtime.
4
4
  ---
5
5
 
6
6
  # Rainskills
@@ -14,11 +14,11 @@ 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.19 <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.21 <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
 
21
- 执行优化:同一会话内只检查一次 Node.js(首次使用本地 CLI 前);仅在 Node.jsRainskills 安装、升级,或 PATH 变更后失效。固定 launcher 和 argv 已在本 Skill 中,禁止读取、搜索或探测 `rainskills.js`,也禁止执行 `npm root -g`。每个新的业务操作仍需要刷新一次环境列表;带已有 `operation_id` 或 `onboarding-id` 的续接复用已绑定的环境 ID,不重复枚举环境。
21
+ 执行优化:同一会话内只检查一次 Node.js 和运行环境状态;仅在 Node.jsRainskills 或唯一运行环境发生变化后失效。固定 launcher 和 argv 已在本 Skill 中,禁止读取、搜索或探测 `rainskills.js`,也禁止执行 `npm root -g`。
22
22
 
23
23
  ## Completion Message
24
24
 
@@ -38,18 +38,20 @@ Rainskills 安装完成,下一条消息即可直接使用。
38
38
  也可以直接告诉我你想部署什么应用。
39
39
  ```
40
40
 
41
- ## Manage Runtime Environments
41
+ ## Manage the Single Runtime
42
42
 
43
43
  受限沙箱(包括 Codex)执行本地状态命令时,必须申请用户级受保护目录访问权限;在 Codex 中使用 `require_escalated`。不得修改 `~/.rainbond` 权限、复制受保护状态到工作区,或因沙箱权限错误建议重装。
44
44
 
45
45
  涉及浏览器或设备授权的 `runtime connect`,以及恢复/安装场景中的 `rainskills <target> --self-hosted`,必须在附加交互终端(TTY)中运行;在 Codex 中设置 `tty: true` 并保持进程附着直到授权完成。禁止通过非交互命令要求用户粘贴 JWT;非交互模式只可复用已存在的受保护凭据。
46
46
 
47
- 环境是全局列表,不是项目绑定。本地状态命令必须定位已安装的同级 `rainbond-platform-installer/scripts/local-runtime.js`,解析为绝对路径后通过 `node` 以 argv 数组执行;不得为了查询本地环境访问 npm。只有连接或安装环境时才使用与当前技能包一致的固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.19`)。
47
+ Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.21`):
48
48
 
49
- - 列表:用本地 launcher 执行 `environment list --json`,只展示名称、类型、状态、是否默认和最近验证时间。
50
- - 重命名:用本地 launcher 执行 `environment rename --environment-id <uuid> --name <name>`。
51
- - 设为默认:用本地 launcher 执行 `environment set-default --environment-id <uuid>`。只影响之后未指定环境的新操作。
52
- - 删除:用本地 launcher 执行 `environment remove --environment-id <uuid>`;默认环境或活动操作使用中的环境必须先阻断。
53
- - 添加:新增环境时直接生成 operation UUID,以 `{"type":"environment-add"}` 执行 runtime connect。先原样展示 `runtime message --id add-environment-location`。选择云端环境时直接执行 `--saas` route;选择私有环境时立即原样展示 `runtime message --id private-deployment-location`,并与首次部署保持完全相同的路径:选择 1(部署到本机)执行 `--install-private --location local`,选择 2(部署到独立服务器)执行 `--install-private --location server`,选择 3(部署到已有 Rainbond)原样展示 `runtime message --id private-console-origin`,收到地址后执行 `--rainbond-url <已验证 Console origin>`。不得展示任何额外的接入方式中间步骤。授权成功后必须把 launcher 返回的 `user_message` 中完整环境列表原样展示给用户,再按用户要求重命名并执行 `operation complete --operation-id <uuid>`。第二个环境不得自动改成默认环境。不得先执行 `runtime status`,不得检查、迁移、备份、删除或修复旧的 `runtime-connection-v1.json`;旧 runtime 状态不是新增环境的前置条件,连接失败时只执行 launcher 返回的固定恢复 argv,不得自行设计恢复流程。
49
+ - 状态:执行 `runtime status --json`。
50
+ - 首次连接:执行 `runtime connect <target> --saas` `runtime connect <target> --rainbond-url <Console origin>`。
51
+ - 安装私有环境:执行 `runtime connect <target> --install-private --location local|server`。
52
+ - 重新授权:执行 `runtime reconnect <target>`;必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT。
53
+ - 更换环境:明确告知用户会替换当前唯一环境;新授权和 live probe 成功后再覆盖凭据。
54
54
 
55
- 用户在一次业务请求中明确指定运行环境时,只把该环境 ID 传给本次 `operation begin`;同一项目可以部署到任意多个环境和团队。明确“团队”表示环境内团队;明确“运行环境/平台”表示环境;裸名称同时匹配两者时必须询问,不能靠名称中是否含“环境”二字猜测。
55
+ 所有 Rainbond 查询和变更继续通过 `~/.rainbond/bin/rainskills-tools.js`。不得配置客户端 MCP,也不得恢复环境枚举、业务 operation 生命周期、环境 ID 或 intent 恢复协议。
56
+
57
+ 业务 CLI 默认只读取受保护的唯一运行环境文件,忽略 Shell 中遗留的 `RAINBOND_URL` / `RAINBOND_JWT`。CI 若要显式使用环境变量凭据,必须同时设置 `RAINSKILLS_CREDENTIAL_SOURCE=environment`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainskills",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Interactive Rainbond skill installer for Codex, Claude Code, and Pi Agent",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/goodrain/rainskills#readme",
@@ -64,7 +64,7 @@
64
64
  "test:runtime-version": "node --test tests/runtime-version-sync.test.js",
65
65
  "test:runtime-contracts": "node scripts/sync-runtime-contracts.mjs --check",
66
66
  "test:auto-update": "node --test tests/auto-update.test.js",
67
- "test:launcher": "node --test tests/console-origin.test.js tests/environment-credentials.test.js tests/environment-registry.test.js tests/local-runtime-cli.test.js tests/local-cli-installer.test.js tests/npx-launcher.test.js tests/runtime-credentials.test.js tests/runtime-intents.test.js tests/runtime-operations.test.js tests/runtime-state.test.js tests/cli-only-transport.test.js tests/skill-manifest-builder.test.js",
67
+ "test:launcher": "node --test tests/console-origin.test.js tests/local-cli-installer.test.js tests/npx-launcher.test.js tests/runtime-state.test.js tests/single-runtime.test.js tests/single-runtime-cli.test.js tests/single-runtime-launcher.test.js tests/skill-manifest-builder.test.js",
68
68
  "test:api-bridge": "node --test tests/api-bridge.test.js",
69
69
  "test:console-contract": "python3 tests/console_tool_contract_test.py",
70
70
  "test:skill-profile": "node --test tests/skill-profile-builder.test.js tests/markdown-link-integrity.test.js",
@@ -88,153 +88,124 @@ description: "Use whenever a user asks to deploy, run, deliver, publish, inspect
88
88
  <!-- rainskills-user-result:end -->
89
89
 
90
90
  <!-- rainskills-runtime-gate:start -->
91
- ## 运行环境门禁(最高优先级)
92
-
93
- ### CLI 调用格式(强制)
94
-
95
- 所有可变 `call` 都必须使用完整 argv:`call <tool> --input - --operation-id <uuid> --skill-id rainbond-app-assistant`。CLI 返回确认 ID 后,只能在同一 argv 末尾加入 `--confirm <confirmation-id>`;不得省略 `--skill-id`、猜测参数,或根据泛化错误反复重试。
96
-
97
- ### 多运行环境操作契约
98
-
99
- Node.js 前置检查通过后,每次用户业务请求都创建独立操作,不把项目绑定到任何环境、团队或应用。先执行本地 launcher + `["environment", "list", "--json"]`:用户明确说“运行环境/平台”时按环境名选择;明确说“团队”时表示默认环境中的团队;裸名称同时匹配环境和团队时必须让用户确认。未指定环境时使用全局默认环境;默认环境不可用时停止,禁止自动切换到其他环境。
100
-
101
- 生成新的 UUID,并执行本地 launcher + `["operation", "begin", "--operation-id", "<uuid>", "--intent-json", "<intent-json>"]`;用户明确指定已有环境时在 `--intent-json` 前加入 `["--environment-id", "<immutable-environment-id>"]`。保存返回的 `operation_id`,此后每个 Rainbond MCP 工具调用都必须在参数中加入 `rainskills_operation_id`。环境改名或修改全局默认值不能改变已开始操作的目标。同一项目可以在多个环境和团队分别部署,禁止写入或推断项目级默认环境。
102
-
103
- 每个 intent JSON 都必须显式包含顶层 `type`,下方 `required` 只表示除 `type` 外的业务字段要求,绝不表示可以传 `{}`。用户明确要求部署新应用时,最小 intent 是 `{"type":"deploy"}`;用户只给出 Harbor 这类应用名称、尚未提供来源描述符时,也必须先保留 `{"type":"deploy"}`,不得省略或延后补充 `type`。从首次构造开始复用同一份完整 intent JSON,依次传给 `operation begin`、`runtime connect` 和后续恢复;只在用户已明确提供且契约允许时增加字段,不得重新构造为不含 `type` 的对象。
104
-
105
- 第一步检查 Node.js 是否存在且主版本不低于 18。Node.js 缺失或低于 18 时,只说明“Rainskills 执行组件需要 Node.js 18 或更高版本”并停止:不选择运行环境,不调用 MCP,不猜测替代命令。只有用户或 agent 明确同意后才安装或升级 Node.js,再从同一原始 intent 继续。
106
-
107
- 固定 launcher 是 `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`;运行包版本标记为 `rainskills@0.1.19`,且必须与本技能包 `package.json` 一致。把 launcher 与参数拼成 argv 数组直接执行,禁止使用 `rainskills@latest`,禁止拼接或执行 shell 字符串。
108
-
109
- 本地 launcher 必须从当前 Skill 所在目录的同级目录定位 `rainbond-platform-installer/scripts/local-runtime.js`,解析为绝对路径后使用 `["node", "<绝对路径>"]` 执行。`environment list`、`operation begin`、`operation complete` 和 `runtime message` 只能使用本地 launcher;本地 launcher 只读取已安装文件和本机受保护状态,不得访问 npm 或其它网络。只有用户选定连接或安装运行环境后,才使用上面的固定本地 launcher。
110
-
111
- 所有 Rainbond 查询和变更必须通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行,并绑定本次 operation ID。禁止 Agent 直接调用 Rainbond MCP,也不得启动本地 Rainskills MCP 服务;只允许执行 CLI 返回的结构化结果与确认续接 argv。
112
-
113
- 业务操作开始后,必须使用 runtime contract 的 `input_commands.context-resolve` 完整 argv 和配套 stdin。返回 `resolved` 时只使用该 operation 已保存的企业、工作空间和集群上下文;返回 `needs-selection` 时,把 CLI 返回的“工作空间 + 集群”组合选项一次性列给用户,不得逐项询问,也不得让用户提供 enterprise_id。用户选择后必须使用 `input_commands.context-select` 的完整 argv 和配套 stdin;选择成功后继续原操作,同一 operation 不再重复查询上下文。
114
-
115
- 只有 CLI 返回并通过校验的 `rainskills.next-action.v1` argv 才能执行续接。普通失败一律禁止自动重试:不得再次执行原命令,不得执行 `--help`、`sleep`、`rg`、`grep`,不得搜索 Rainskills 源码;同一 `operation complete` 最多执行一次。
116
-
117
- <!-- rainskills-runtime-contract:start -->
118
- ```json
119
- {
120
- "schema": "rainskills.skill-runtime-contract.v1",
121
- "package_version": "rainskills@0.1.19",
122
- "launcher": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"],
123
- "local_launcher": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js"],
124
- "local_argv": {
125
- "environment-list": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "environment", "list", "--json"],
126
- "operation-begin-default": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "operation", "begin", "--operation-id", "<uuid>", "--intent-json", "<intent-json>"],
127
- "operation-begin-selected": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "operation", "begin", "--operation-id", "<uuid>", "--environment-id", "<environment-id>", "--intent-json", "<intent-json>"],
128
- "operation-complete": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "operation", "complete", "--operation-id", "<uuid>"],
129
- "runtime-message": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "runtime", "message", "--id", "<message-id>"]
130
- },
131
- "intents": {
132
- "deploy": {
133
- "type": "deploy",
134
- "required": ["type"],
135
- "optional": ["project_root", "source_kind", "source_url", "image_ref", "service_id"],
136
- "enums": {
137
- "source_kind": ["local", "git", "image", "package"]
138
- },
139
- "example": {
140
- "type": "deploy"
141
- }
142
- },
143
- "create": {
144
- "type": "create",
145
- "required": ["type"],
146
- "optional": ["project_root", "source_kind", "source_url", "image_ref", "service_id"],
147
- "enums": {
148
- "source_kind": ["local", "git", "image", "package"]
149
- },
150
- "example": {
151
- "type": "create"
152
- }
153
- },
154
- "query": {
155
- "type": "query",
156
- "required": ["type", "operation"],
157
- "optional": ["team_id", "app_id", "service_id"],
158
- "enums": {
159
- "operation": ["summary", "components", "events", "logs", "access"]
160
- },
161
- "example": {
162
- "type": "query",
163
- "operation": "summary",
164
- "app_id": "app"
165
- }
166
- },
167
- "troubleshoot": {
168
- "type": "troubleshoot",
169
- "required": ["type", "operation"],
170
- "optional": ["team_id", "app_id", "service_id"],
171
- "enums": {
172
- "operation": ["auto", "build", "runtime", "access"]
173
- },
174
- "example": {
175
- "type": "troubleshoot",
176
- "operation": "build",
177
- "app_id": "app"
178
- }
179
- },
180
- "modify": {
181
- "type": "modify",
182
- "required": ["type", "team_id", "app_id", "operation"],
183
- "optional": ["service_id"],
184
- "enums": {
185
- "operation": ["component-config", "build-source", "ports", "env", "storage", "dependency"]
186
- },
187
- "example": {
188
- "type": "modify",
189
- "team_id": "team",
190
- "app_id": "app",
191
- "operation": "env"
192
- }
91
+ ## 单运行环境 CLI 门禁(最高优先级)
92
+
93
+ 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true` 后,所有查询和变更直接通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
94
+
95
+ 没有运行环境时,让用户选择 Rainbond Cloud 或一个已有/新建的私有 Rainbond,并执行对应的 `runtime connect`。连接和重新授权必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT;新凭据通过 live probe 后才覆盖唯一运行环境。CLI 返回 401 时,只读调用可在 `runtime reconnect` 成功后重试一次;写调用不得自动重放,必须先查询平台真实状态。403 直接停止,不重新授权。
96
+
97
+ `context resolve` 是无状态调用:单一工作空间直接返回上下文,多个候选返回组合选项;用户选择后由当前任务直接携带 team/region 参数,不执行 `context select`,不写本地 operation。所有可变 `call` 仍需先取得 confirmation ID,再以完全相同的输入追加 `--confirm` 执行一次。
98
+
99
+ ```json
100
+ {
101
+ "schema": "rainskills.single-runtime-contract.v1",
102
+ "package_version": "rainskills@0.1.21",
103
+ "runtime_status": [
104
+ "node",
105
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
106
+ "runtime",
107
+ "status",
108
+ "--json"
109
+ ],
110
+ "runtime_connect": {
111
+ "saas": [
112
+ "node",
113
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
114
+ "runtime",
115
+ "connect",
116
+ "<target>",
117
+ "--saas"
118
+ ],
119
+ "private_existing": [
120
+ "node",
121
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
122
+ "runtime",
123
+ "connect",
124
+ "<target>",
125
+ "--rainbond-url",
126
+ "<console-origin>"
127
+ ],
128
+ "install_private": [
129
+ "node",
130
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
131
+ "runtime",
132
+ "connect",
133
+ "<target>",
134
+ "--install-private",
135
+ "--location",
136
+ "<local-or-server>"
137
+ ],
138
+ "reconnect": [
139
+ "node",
140
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
141
+ "runtime",
142
+ "reconnect",
143
+ "<target>"
144
+ ]
145
+ },
146
+ "input_commands": {
147
+ "context_resolve": {
148
+ "argv": [
149
+ "node",
150
+ "<home>/.rainbond/bin/rainskills-tools.js",
151
+ "context",
152
+ "resolve",
153
+ "--input",
154
+ "-",
155
+ "--skill-id",
156
+ "rainbond-app-assistant"
157
+ ],
158
+ "stdin": {
159
+ "required": [
160
+ "enterprise",
161
+ "workspace"
162
+ ]
193
163
  }
194
164
  },
195
- "routes": {
196
- "new": ["saas", "private-existing", "install-private"],
197
- "existing": ["saas", "private-existing"]
165
+ "read": {
166
+ "argv": [
167
+ "node",
168
+ "<home>/.rainbond/bin/rainskills-tools.js",
169
+ "read",
170
+ "<tool>",
171
+ "--input",
172
+ "-",
173
+ "--skill-id",
174
+ "rainbond-app-assistant"
175
+ ],
176
+ "stdin_schema_source": "tool-catalog"
198
177
  },
199
- "connect_argv": {
200
- "saas": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--saas", "--intent-json", "<intent-json>"],
201
- "private-existing": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--rainbond-url", "<rainbond-url>", "--intent-json", "<intent-json>"],
202
- "install-private": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--install-private", "--location", "<private-location>", "--intent-json", "<intent-json>"]
178
+ "call": {
179
+ "argv": [
180
+ "node",
181
+ "<home>/.rainbond/bin/rainskills-tools.js",
182
+ "call",
183
+ "<tool>",
184
+ "--input",
185
+ "-",
186
+ "--skill-id",
187
+ "rainbond-app-assistant"
188
+ ],
189
+ "stdin_schema_source": "tool-catalog"
203
190
  },
204
- "input_commands": {
205
- "context-resolve": {
206
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "context", "resolve", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-app-assistant"],
207
- "stdin": {
208
- "required": ["enterprise", "workspace"]
209
- }
210
- },
211
- "context-select": {
212
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "context", "select", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-app-assistant"],
213
- "stdin": {
214
- "selection_id": "<selection-id>",
215
- "option_id": "<option-id>"
216
- }
217
- },
218
- "read": {
219
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "read", "<tool>", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-app-assistant"],
220
- "stdin_schema_source": "tool-catalog"
221
- },
222
- "call": {
223
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "call", "<tool>", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-app-assistant"],
224
- "stdin_schema_source": "tool-catalog"
225
- }
191
+ "call_confirm": {
192
+ "argv": [
193
+ "node",
194
+ "<home>/.rainbond/bin/rainskills-tools.js",
195
+ "call",
196
+ "<tool>",
197
+ "--input",
198
+ "-",
199
+ "--skill-id",
200
+ "rainbond-app-assistant",
201
+ "--confirm",
202
+ "<confirmation-id>"
203
+ ],
204
+ "stdin_schema_source": "same-confirmed-input"
226
205
  }
227
206
  }
228
- ```
229
- <!-- rainskills-runtime-contract:end -->
230
-
231
- `deploy`/`create` 含 `service_id` 时属于 existing,否则属于 new;`query`、`troubleshoot`、`modify` 始终属于 existing。意图不明确时先澄清,不执行 connect。target 只允许 `codex`、`claude`、`pi`、`all`。连接前校验 intent,只执行其 scope 在契约中允许的完整 argv。只消费 schema 为 `rainskills.next-action.v1` 且完成字段校验后的 `argv` 数组。
232
-
233
- 连接或平台安装完成后,用固定 `onboarding-id` 执行 launcher + `["intent", "resume", "--onboarding-id", "<同一 onboarding-id>"]`,恢复原始 intent 和 `resume_step`,不得重新猜测动作。
234
-
235
- 同一条附着命令先输出 `runtime.device-authorization`、随后输出 schema 为 `rainskills.runtime-connect-result.v1` 且 `state=connected` 时,最终 connected 结果覆盖前面的等待授权消息。不得再次展示旧授权消息、不得等待用户回复“已授权”;必须在同一轮立即使用同一 `onboarding-id` 执行 `intent resume`。
236
-
237
- 业务 MCP 返回 401 时,先执行 launcher + `["runtime", "record-failure", "--onboarding-id", "<同一 onboarding-id>", "--step", "<当前固定步骤>", "--reason", "credential-expired"]`,再仅一次执行 launcher + `["runtime", "reconnect", "--onboarding-id", "<同一 onboarding-id>"]`,然后 resume 并只重试该步骤;第二次 401 立即停止。403 时执行 launcher + `["runtime", "record-failure", "--onboarding-id", "<同一 onboarding-id>", "--step", "<当前固定步骤>", "--reason", "permission-denied"]` 后停止,不得 reconnect、重新授权或自动重试,只说明缺少的权限。
207
+ }
208
+ ```
238
209
  <!-- rainskills-runtime-gate:end -->
239
210
 
240
211
  受限沙箱(包括 Codex)执行本地状态命令时,必须申请用户级受保护目录访问权限;在 Codex 中使用 `require_escalated`。不得修改 `~/.rainbond` 权限、复制受保护状态到工作区,或因沙箱权限错误建议重装。
@@ -269,17 +240,11 @@ description: "Use whenever a user asks to deploy, run, deliver, publish, inspect
269
240
  请提示“请选择应用要运行的环境:”,并只显示:
270
241
 
271
242
  1) 云端环境(免费体验)
272
- 2) 私有环境(去对接)
273
-
274
- 用户选择私有环境后,立即执行本地 launcher + `["runtime", "message", "--id", "private-deployment-location"]`,并按同一消息协议原样输出块内正文:
275
-
276
- 请选择部署位置:
277
-
278
- 1、部署到本机
279
- 2、部署到独立服务器
280
- 3、部署到已有 Rainbond
243
+ 2) 本机环境
244
+ 3) 独立服务器
245
+ 4) 已有 Rainbond
281
246
 
282
- 选择 1 时执行 `install-private` route,并在完整 argv 中使用 `["--location", "local"]`;选择 2 时执行 `install-private` route,并使用 `["--location", "server"]`;选择 3 时执行本地 launcher + `["runtime", "message", "--id", "private-console-origin"]`,收到地址后执行 `private-existing`。不得显示额外的接入方式中间步骤,不得在平台安装器中重复询问部署位置,也不得在环境准备完成前询问应用来源。
247
+ 选择 1 时执行 `saas` route;选择 2 时执行 `install-private` route,并在完整 argv 中使用 `["--location", "local"]`;选择 3 时执行 `install-private` route,并使用 `["--location", "server"]`;选择 4 时执行本地 launcher + `["runtime", "message", "--id", "private-console-origin"]`,收到地址后执行 `private-existing`。不得显示“私有环境”或部署位置中间层,不得在平台安装器中重复询问部署位置,也不得在环境准备完成前询问应用来源。
283
248
 
284
249
  ### 已有应用
285
250