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
@@ -15,16 +15,9 @@ const USER_MESSAGES = Object.freeze({
15
15
  "请选择应用要运行的环境:",
16
16
  "",
17
17
  "1) 云端环境(免费体验)",
18
- "2) 私有环境(去对接)",
19
- ].join("\n"),
20
- },
21
- "add-environment-location": {
22
- messageId: "runtime.add-environment-location",
23
- message: [
24
- "请选择要添加的运行环境:",
25
- "",
26
- "1) 云端环境(免费体验)",
27
- "2) 私有环境(去对接)",
18
+ "2) 本机环境",
19
+ "3) 独立服务器",
20
+ "4) 已有 Rainbond",
28
21
  ].join("\n"),
29
22
  },
30
23
  "private-console-origin": {
@@ -78,48 +71,9 @@ function renderCatalogUserMessage(id, { controlPlatform = process.platform } = {
78
71
  return renderUserMessage(entry.messageId, entry.message);
79
72
  }
80
73
 
81
- function renderEnvironmentConnectedList({
82
- environments,
83
- defaultEnvironmentId,
84
- addedEnvironmentId,
85
- }) {
86
- if (!Array.isArray(environments) || environments.length === 0) {
87
- throw new Error("运行环境列表无效");
88
- }
89
- const defaultEnvironment = environments.find((entry) => entry.id === defaultEnvironmentId);
90
- const addedEnvironment = environments.find((entry) => entry.id === addedEnvironmentId);
91
- if (!defaultEnvironment || !addedEnvironment) {
92
- throw new Error("运行环境列表缺少默认环境或新增环境");
93
- }
94
- const kindLabel = (kind) => kind === "saas" ? "在线环境" : "私有环境";
95
- const stateLabel = (state) => ({
96
- connected: "已连接",
97
- "needs-reconnect": "需要重新授权",
98
- unavailable: "不可用",
99
- })[state] || "状态未知";
100
- const lines = environments.map((environment) => {
101
- const labels = [kindLabel(environment.kind), stateLabel(environment.connection_state)];
102
- if (environment.id === defaultEnvironmentId) labels.push("默认");
103
- if (environment.id === addedEnvironmentId) labels.push("刚新增");
104
- return `- ${environment.name}(${labels.join(",")})`;
105
- });
106
- return renderUserMessage("runtime.environment-connected-list", [
107
- "新环境已对接成功。",
108
- "",
109
- "当前运行环境:",
110
- "",
111
- ...lines,
112
- "",
113
- defaultEnvironmentId === addedEnvironmentId
114
- ? `全局默认环境是:${defaultEnvironment.name}`
115
- : `全局默认环境仍是:${defaultEnvironment.name}`,
116
- ].join("\n"));
117
- }
118
-
119
74
  module.exports = {
120
75
  privateDeploymentLocationMessage,
121
76
  renderUserMessage,
122
77
  renderCatalogUserMessage,
123
- renderEnvironmentConnectedList,
124
78
  writeUserMessage,
125
79
  };
@@ -1,23 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  const os = require("node:os");
4
- const path = require("node:path");
5
- const { spawnSync } = require("node:child_process");
6
4
  const { looksLikeJwt } = require("./windows-auth.js");
7
5
 
8
6
  const MAX_MISSING_ROUTE_BODY_BYTES = 64 * 1024;
9
7
 
10
- function normalizeBaseUrl(value) {
11
- const url = new URL(value);
12
- if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
13
- throw new Error("Rainbond Console 地址必须是无凭据的 HTTP(S) URL");
14
- }
15
- url.search = "";
16
- url.hash = "";
17
- url.pathname = url.pathname.replace(/\/$/, "");
18
- return url.toString().replace(/\/$/, "");
19
- }
20
-
21
8
  async function readBoundedResponseText(response) {
22
9
  const declaredLength = Number(response.headers?.get?.("content-length") || 0);
23
10
  if (Number.isFinite(declaredLength) && declaredLength > MAX_MISSING_ROUTE_BODY_BYTES) return null;
@@ -121,45 +108,7 @@ async function validateMcp({ url, token, fetchImpl = globalThis.fetch }) {
121
108
  return { token: looksLikeJwt(renewed) ? renewed : token };
122
109
  }
123
110
 
124
- function checkedSpawn(spawnImpl, command, args, options) {
125
- const result = spawnImpl(command, args, options);
126
- if (result.error) {
127
- if (result.error.code === "ENOENT") throw new Error(`未找到所选客户端命令:${command}`);
128
- throw result.error;
129
- }
130
- if (result.signal) throw new Error(`${command} 被信号 ${result.signal} 中断`);
131
- if (result.status !== 0) throw new Error(`${command} 配置失败,退出码 ${result.status}`);
132
- }
133
-
134
- function persistWindowsEnvironment({
135
- token,
136
- baseUrl,
137
- spawnImpl = spawnSync,
138
- helperPath = path.join(__dirname, "windows-client-config.ps1"),
139
- }) {
140
- if (!looksLikeJwt(token)) throw new Error("Rainbond JWT 格式无效");
141
- const normalizedBase = normalizeBaseUrl(baseUrl);
142
- const environment = {
143
- ...process.env,
144
- RAINSKILLS_RAINBOND_JWT: token,
145
- RAINSKILLS_RAINBOND_URL: normalizedBase,
146
- };
147
- checkedSpawn(spawnImpl, "powershell.exe", [
148
- "-NoProfile",
149
- "-NonInteractive",
150
- "-File",
151
- helperPath,
152
- ], {
153
- encoding: "utf8",
154
- env: environment,
155
- windowsHide: true,
156
- });
157
- process.env.RAINBOND_JWT = token;
158
- process.env.RAINBOND_URL = normalizedBase;
159
- }
160
-
161
111
  module.exports = {
162
112
  isVerifiedMissingMcpRoute,
163
- persistWindowsEnvironment,
164
113
  validateMcp,
165
114
  };
@@ -10,7 +10,6 @@ const readline = require("node:readline/promises");
10
10
  const { stdin, stdout } = require("node:process");
11
11
  const { pathToFileURL } = require("node:url");
12
12
  const { createSecureStateStore } = require("./secure-state.js");
13
- const { assertIntentCanInstallNewPlatform } = require("./runtime-intents.js");
14
13
  const { createWindowsSecureStateStore } = require("./windows-platform.js");
15
14
  const {
16
15
  authorizeWithDeviceFlow,
@@ -293,7 +292,6 @@ function createOnboardingCheckpoint({
293
292
  operationId = crypto.randomUUID(),
294
293
  now = () => new Date().toISOString(),
295
294
  stateStore,
296
- intent,
297
295
  }) {
298
296
  if (!UUID_PATTERN.test(operationId)) throw new Error("operation id 不是有效的 UUID");
299
297
  if (!["codex", "claude", "pi", "all"].includes(target)) throw new Error("安装目标无效");
@@ -324,7 +322,6 @@ function createOnboardingCheckpoint({
324
322
  ),
325
323
  console_url: null,
326
324
  };
327
- if (intent !== undefined) state.intent = assertIntentCanInstallNewPlatform(intent);
328
325
  store.atomicWriteJson(checkpointPath, state);
329
326
  return { path: checkpointPath, state };
330
327
  }
@@ -303,7 +303,7 @@ function Get-StateAclFacts([string]$PathValue, [string]$Kind, [string]$HomeValue
303
303
  } finally {
304
304
  $hasher.Dispose()
305
305
  }
306
- $fileIdentity = "sha256:$digest:$($stream.Length)"
306
+ $fileIdentity = "sha256:${digest}:$($stream.Length)"
307
307
  }
308
308
  return [ordered]@{
309
309
  ownerSid = Convert-IdentityToSid $acl.Owner
@@ -6,82 +6,124 @@ description: Use for a user-requested, read-only Rainbond platform query about t
6
6
  # Rainbond Platform Query
7
7
 
8
8
  <!-- rainskills-runtime-gate:start -->
9
- ## 运行环境门禁(最高优先级)
9
+ ## 单运行环境 CLI 门禁(最高优先级)
10
10
 
11
- ### CLI 调用格式(强制)
11
+ 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true` 后,所有查询和变更直接通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
12
12
 
13
- 所有可变 `call` 都必须使用完整 argv:`call <tool> --input - --operation-id <uuid> --skill-id rainbond-platform-query`。CLI 返回确认 ID 后,只能在同一 argv 末尾加入 `--confirm <confirmation-id>`;不得省略 `--skill-id`、猜测参数,或根据泛化错误反复重试。
13
+ 没有运行环境时,让用户选择 Rainbond Cloud 或一个已有/新建的私有 Rainbond,并执行对应的 `runtime connect`。连接和重新授权必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT;新凭据通过 live probe 后才覆盖唯一运行环境。CLI 返回 401 时,只读调用可在 `runtime reconnect` 成功后重试一次;写调用不得自动重放,必须先查询平台真实状态。403 直接停止,不重新授权。
14
14
 
15
- ### 多运行环境操作契约
15
+ `context resolve` 是无状态调用:单一工作空间直接返回上下文,多个候选返回组合选项;用户选择后由当前任务直接携带 team/region 参数,不执行 `context select`,不写本地 operation。所有可变 `call` 仍需先取得 confirmation ID,再以完全相同的输入追加 `--confirm` 执行一次。
16
16
 
17
- Node.js 前置检查通过后,平台只读查询只执行一次本地 `rainskills-tools.js query`。用户未指定运行环境时,不得先执行 `environment list`,由 CLI 直接使用全局默认环境;默认环境缺失或不可用时停止且不回退。用户明确指定运行环境时,才允许执行一次本地 launcher + `["environment", "list", "--json"]` 解析不可变环境 ID,随后执行带 `--environment-id` 的单次查询。CLI 直接从受保护环境注册表读取环境与凭据,校验完整 `platform-query` intent 后调用 Rainbond;只读查询不创建 `operation_id` 或 operation 记录。Agent 禁止生成 UUID、禁止传 `--intent-json`,也禁止调用 `operation begin` 或 `operation complete`。环境、团队和应用只属于本次查询,禁止保存项目级默认环境或绑定;同一项目可以在多个环境中查询。明确“团队”表示环境内团队;明确“运行环境/平台”表示环境;裸名称同时匹配环境和团队时必须询问。
18
-
19
- 查询入口只接受固定 Tool 映射:`rainbond_get_current_user`、`rainbond_query_enterprises`、`rainbond_query_teams`、`rainbond_query_regions`、`rainbond_query_apps`、`rainbond_get_team_apps`、`rainbond_query_components`。resource 和 intent 由 CLI 从 Tool 与输入参数构造并校验,Agent 不得自行拼接。CLI 参数校验失败表示命令尚未发起 Rainbond 查询,必须如实报告本地参数错误,不得表述为授权被拒绝。
20
-
21
- 对 `rainbond_query_teams({})` 等企业范围查询,CLI 会在同一条 `query` 命令内部自动调用 `rainbond_get_current_user`,从当前身份取得 `enterprise_id`,再调用 `rainbond_query_teams` 等目标 Tool。Agent 不得要求用户提供或输入 `enterprise_id`,也不得为了取得该字段额外执行一条身份查询命令。
22
-
23
- 第一步检查 Node.js 是否存在且主版本不低于 18。Node.js 缺失或低于 18 时,只说明“Rainskills 执行组件需要 Node.js 18 或更高版本”并停止:不选择运行环境,不调用 MCP,不猜测替代命令。只有用户或 agent 明确同意后才安装或升级 Node.js,再从同一原始 intent 继续。
24
-
25
- 固定 launcher 是 `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`;运行包版本标记为 `rainskills@0.1.19`,且必须与本技能包 `package.json` 一致。把 launcher 与参数拼成 argv 数组直接执行,禁止 `rainskills@latest` 或执行 shell 字符串。
26
-
27
- 本地 launcher 必须从当前 Skill 所在目录的同级目录定位 `rainbond-platform-installer/scripts/local-runtime.js`,解析为绝对路径后使用 `["node", "<绝对路径>"]` 执行。仅在用户明确指定环境时使用 `environment list`;`runtime message` 仍使用本地 launcher。本地 launcher 只读取已安装文件和本机受保护状态,不得访问 npm 或其它网络。
28
-
29
- 所有 Rainbond 查询必须通过本地 `~/.rainbond/bin/rainskills-tools.js` 的单次 `query` 命令执行。默认环境 argv 固定为 `["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--input", "-"]`;明确环境 argv 固定为 `["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--environment-id", "<environment-id>", "--input", "-"]`。JSON 只经 stdin 输入。禁止 Agent 直接调用 Rainbond MCP,也不得启动本地 Rainskills MCP 服务;不得把一次查询拆成 `environment list`、`operation begin`、`read`、`operation complete` 四次执行。
30
-
31
- 只有 CLI 返回并通过校验的 `rainskills.next-action.v1` argv 才能执行续接。普通失败一律禁止自动重试:不得再次执行原命令,不得执行 `--help`、`sleep`、`rg`、`grep`,不得搜索 Rainskills 源码;只读查询不得手工补跑 `operation begin` 或 `operation complete`。
32
-
33
- <!-- rainskills-runtime-contract:start -->
34
17
  ```json
35
18
  {
36
- "schema": "rainskills.skill-runtime-contract.v1",
37
- "package_version": "rainskills@0.1.19",
38
- "launcher": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"],
39
- "local_launcher": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js"],
40
- "local_argv": {
41
- "environment-list": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "environment", "list", "--json"],
42
- "runtime-message": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "runtime", "message", "--id", "<message-id>"],
43
- "platform-query-default": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--input", "-"],
44
- "platform-query-selected": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--environment-id", "<environment-id>", "--input", "-"]
45
- },
46
- "intents": {
47
- "platform-query": {
48
- "type": "platform-query",
49
- "required": ["type", "resource"],
50
- "optional": ["enterprise_id", "team_id", "app_id"],
51
- "enums": {
52
- "resource": ["current-user", "current-enterprise", "teams", "regions", "apps", "team-apps", "components"]
53
- },
54
- "example": {
55
- "type": "platform-query",
56
- "resource": "components",
57
- "app_id": "app"
58
- }
59
- }
60
- },
61
- "routes": {
62
- "existing": ["saas", "private-existing"]
63
- },
64
- "connect_argv": {
65
- "saas": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--saas", "--intent-json", "<intent-json>"],
66
- "private-existing": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--rainbond-url", "<rainbond-url>", "--intent-json", "<intent-json>"]
19
+ "schema": "rainskills.single-runtime-contract.v1",
20
+ "package_version": "rainskills@0.1.21",
21
+ "runtime_status": [
22
+ "node",
23
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
24
+ "runtime",
25
+ "status",
26
+ "--json"
27
+ ],
28
+ "runtime_connect": {
29
+ "saas": [
30
+ "node",
31
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
32
+ "runtime",
33
+ "connect",
34
+ "<target>",
35
+ "--saas"
36
+ ],
37
+ "private_existing": [
38
+ "node",
39
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
40
+ "runtime",
41
+ "connect",
42
+ "<target>",
43
+ "--rainbond-url",
44
+ "<console-origin>"
45
+ ],
46
+ "install_private": [
47
+ "node",
48
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
49
+ "runtime",
50
+ "connect",
51
+ "<target>",
52
+ "--install-private",
53
+ "--location",
54
+ "<local-or-server>"
55
+ ],
56
+ "reconnect": [
57
+ "node",
58
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
59
+ "runtime",
60
+ "reconnect",
61
+ "<target>"
62
+ ]
67
63
  },
68
64
  "input_commands": {
69
- "query-default": {
70
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--input", "-"],
65
+ "context_resolve": {
66
+ "argv": [
67
+ "node",
68
+ "<home>/.rainbond/bin/rainskills-tools.js",
69
+ "context",
70
+ "resolve",
71
+ "--input",
72
+ "-",
73
+ "--skill-id",
74
+ "rainbond-platform-query"
75
+ ],
76
+ "stdin": {
77
+ "required": [
78
+ "enterprise",
79
+ "workspace"
80
+ ]
81
+ }
82
+ },
83
+ "read": {
84
+ "argv": [
85
+ "node",
86
+ "<home>/.rainbond/bin/rainskills-tools.js",
87
+ "read",
88
+ "<tool>",
89
+ "--input",
90
+ "-",
91
+ "--skill-id",
92
+ "rainbond-platform-query"
93
+ ],
71
94
  "stdin_schema_source": "tool-catalog"
72
95
  },
73
- "query-selected": {
74
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "query", "<tool>", "--environment-id", "<environment-id>", "--input", "-"],
96
+ "call": {
97
+ "argv": [
98
+ "node",
99
+ "<home>/.rainbond/bin/rainskills-tools.js",
100
+ "call",
101
+ "<tool>",
102
+ "--input",
103
+ "-",
104
+ "--skill-id",
105
+ "rainbond-platform-query"
106
+ ],
75
107
  "stdin_schema_source": "tool-catalog"
108
+ },
109
+ "call_confirm": {
110
+ "argv": [
111
+ "node",
112
+ "<home>/.rainbond/bin/rainskills-tools.js",
113
+ "call",
114
+ "<tool>",
115
+ "--input",
116
+ "-",
117
+ "--skill-id",
118
+ "rainbond-platform-query",
119
+ "--confirm",
120
+ "<confirmation-id>"
121
+ ],
122
+ "stdin_schema_source": "same-confirmed-input"
76
123
  }
77
124
  }
78
125
  }
79
126
  ```
80
- <!-- rainskills-runtime-contract:end -->
81
-
82
- target 只允许 `codex`、`claude`、`pi`、`all`。校验 intent 后只执行 existing scope 的完整 argv;只消费 schema 为 `rainskills.next-action.v1` 且校验后的 `argv` 数组。
83
-
84
- 连接完成后用固定 `onboarding-id` 执行 launcher + `["intent", "resume", "--onboarding-id", "<同一 onboarding-id>"]`,恢复原始 intent 和 `resume_step`。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、重新授权或自动重试。
85
127
  <!-- rainskills-runtime-gate:end -->
86
128
 
87
129
  受限沙箱(包括 Codex)执行本地状态命令时,必须申请用户级受保护目录访问权限;在 Codex 中使用 `require_escalated`。不得修改 `~/.rainbond` 权限、复制受保护状态到工作区,或因沙箱权限错误建议重装。
@@ -6,95 +6,124 @@ description: "Use only when the user explicitly asks to initialize or link a loc
6
6
  # Rainbond Project Init
7
7
 
8
8
  <!-- rainskills-runtime-gate:start -->
9
- ## 运行环境门禁(最高优先级)
9
+ ## 单运行环境 CLI 门禁(最高优先级)
10
10
 
11
- ### CLI 调用格式(强制)
11
+ 本机只允许连接一个 Rainbond 运行环境。当前 Skill 在本会话第一次调用 Rainbond 前,执行固定 launcher 的 `runtime status --json`。返回 `connected` 且 `usable=true` 后,所有查询和变更直接通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行。不得配置或直接调用客户端 MCP,不得执行环境枚举或业务 operation 生命周期命令,也不得生成或传递运行环境 ID、业务 operation ID 或 intent JSON。
12
12
 
13
- 所有可变 `call` 都必须使用完整 argv:`call <tool> --input - --operation-id <uuid> --skill-id rainbond-project-init`。CLI 返回确认 ID 后,只能在同一 argv 末尾加入 `--confirm <confirmation-id>`;不得省略 `--skill-id`、猜测参数,或根据泛化错误反复重试。
13
+ 没有运行环境时,让用户选择 Rainbond Cloud 或一个已有/新建的私有 Rainbond,并执行对应的 `runtime connect`。连接和重新授权必须进入浏览器 Device Flow,不复用 Shell 中缓存的 JWT;新凭据通过 live probe 后才覆盖唯一运行环境。CLI 返回 401 时,只读调用可在 `runtime reconnect` 成功后重试一次;写调用不得自动重放,必须先查询平台真实状态。403 直接停止,不重新授权。
14
14
 
15
- ### 多运行环境操作契约
15
+ `context resolve` 是无状态调用:单一工作空间直接返回上下文,多个候选返回组合选项;用户选择后由当前任务直接携带 team/region 参数,不执行 `context select`,不写本地 operation。所有可变 `call` 仍需先取得 confirmation ID,再以完全相同的输入追加 `--confirm` 执行一次。
16
16
 
17
- Node.js 前置检查通过后,每次请求先执行本地 launcher + `["environment", "list", "--json"]`,按用户明确指定的运行环境选择不可变环境 ID;未指定时只用全局默认环境,默认不可用时停止且不回退。生成 UUID 后执行本地 launcher + `["operation", "begin", "--operation-id", "<uuid>", "--environment-id", "<id>", "--intent-json", "<intent-json>"]`,并在之后每个 Rainbond MCP 调用中加入 `rainskills_operation_id`。环境、团队和应用只属于本次操作,禁止保存项目绑定;同一项目可以部署到多个环境。明确“团队”表示环境内团队;明确“运行环境/平台”表示环境;裸名称同时匹配环境和团队时必须询问。
18
-
19
- 第一步检查 Node.js 是否存在且主版本不低于 18。Node.js 缺失或低于 18 时,只说明“Rainskills 执行组件需要 Node.js 18 或更高版本”并停止:不选择运行环境,不调用 MCP,不猜测替代命令。只有用户或 agent 明确同意后才安装或升级 Node.js,再从同一原始 intent 继续。
20
-
21
- 固定 launcher 是 `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`;运行包版本标记为 `rainskills@0.1.19`,且必须与本技能包 `package.json` 一致。把 launcher 与参数拼成 argv 数组直接执行,禁止 `rainskills@latest` 或执行 shell 字符串。
22
-
23
- 本地 launcher 必须从当前 Skill 所在目录的同级目录定位 `rainbond-platform-installer/scripts/local-runtime.js`,解析为绝对路径后使用 `["node", "<绝对路径>"]` 执行。`environment list`、`operation begin`、`operation complete` 和 `runtime message` 只能使用本地 launcher;本地 launcher 只读取已安装文件和本机受保护状态,不得访问 npm 或其它网络。只有用户选定连接或安装运行环境后,才使用上面的固定本地 launcher。
24
-
25
- 所有 Rainbond 查询和变更必须通过本地 `~/.rainbond/bin/rainskills-tools.js` 执行,并绑定本次 operation ID。禁止 Agent 直接调用 Rainbond MCP,也不得启动本地 Rainskills MCP 服务;只允许执行 CLI 返回的结构化结果与确认续接 argv。
26
-
27
- 业务操作开始后,必须使用 runtime contract 的 `input_commands.context-resolve` 完整 argv 和配套 stdin。返回 `resolved` 时只使用该 operation 已保存的企业、工作空间和集群上下文;返回 `needs-selection` 时,把 CLI 返回的“工作空间 + 集群”组合选项一次性列给用户,不得逐项询问,也不得让用户提供 enterprise_id。用户选择后必须使用 `input_commands.context-select` 的完整 argv 和配套 stdin;选择成功后继续原操作,同一 operation 不再重复查询上下文。
28
-
29
- 只有 CLI 返回并通过校验的 `rainskills.next-action.v1` argv 才能执行续接。普通失败一律禁止自动重试:不得再次执行原命令,不得执行 `--help`、`sleep`、`rg`、`grep`,不得搜索 Rainskills 源码;同一 `operation complete` 最多执行一次。
30
-
31
- <!-- rainskills-runtime-contract:start -->
32
17
  ```json
33
18
  {
34
- "schema": "rainskills.skill-runtime-contract.v1",
35
- "package_version": "rainskills@0.1.19",
36
- "launcher": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"],
37
- "local_launcher": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js"],
38
- "local_argv": {
39
- "environment-list": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "environment", "list", "--json"],
40
- "operation-begin-default": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "operation", "begin", "--operation-id", "<uuid>", "--intent-json", "<intent-json>"],
41
- "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>"],
42
- "operation-complete": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "operation", "complete", "--operation-id", "<uuid>"],
43
- "runtime-message": ["node", "<installed-skills-root>/rainbond-platform-installer/scripts/local-runtime.js", "runtime", "message", "--id", "<message-id>"]
44
- },
45
- "intents": {
46
- "project-init": {
47
- "type": "project-init",
48
- "required": ["type", "project_root"],
49
- "optional": ["source_kind", "source_url", "image_ref"],
50
- "enums": {
51
- "source_kind": ["local", "git", "image", "package"]
52
- },
53
- "example": {
54
- "type": "project-init",
55
- "project_root": "/workspace/app",
56
- "source_kind": "local"
57
- }
58
- }
59
- },
60
- "routes": {
61
- "new": ["saas", "private-existing", "install-private"]
62
- },
63
- "connect_argv": {
64
- "saas": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--saas", "--intent-json", "<intent-json>"],
65
- "private-existing": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--rainbond-url", "<rainbond-url>", "--intent-json", "<intent-json>"],
66
- "install-private": ["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js", "runtime", "connect", "<target>", "--install-private", "--location", "<private-location>", "--intent-json", "<intent-json>"]
19
+ "schema": "rainskills.single-runtime-contract.v1",
20
+ "package_version": "rainskills@0.1.21",
21
+ "runtime_status": [
22
+ "node",
23
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
24
+ "runtime",
25
+ "status",
26
+ "--json"
27
+ ],
28
+ "runtime_connect": {
29
+ "saas": [
30
+ "node",
31
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
32
+ "runtime",
33
+ "connect",
34
+ "<target>",
35
+ "--saas"
36
+ ],
37
+ "private_existing": [
38
+ "node",
39
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
40
+ "runtime",
41
+ "connect",
42
+ "<target>",
43
+ "--rainbond-url",
44
+ "<console-origin>"
45
+ ],
46
+ "install_private": [
47
+ "node",
48
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
49
+ "runtime",
50
+ "connect",
51
+ "<target>",
52
+ "--install-private",
53
+ "--location",
54
+ "<local-or-server>"
55
+ ],
56
+ "reconnect": [
57
+ "node",
58
+ "<home>/.rainbond/lib/rainskills/bin/rainskills.js",
59
+ "runtime",
60
+ "reconnect",
61
+ "<target>"
62
+ ]
67
63
  },
68
64
  "input_commands": {
69
- "context-resolve": {
70
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "context", "resolve", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-project-init"],
71
- "stdin": {
72
- "required": ["enterprise", "workspace"]
73
- }
74
- },
75
- "context-select": {
76
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "context", "select", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-project-init"],
65
+ "context_resolve": {
66
+ "argv": [
67
+ "node",
68
+ "<home>/.rainbond/bin/rainskills-tools.js",
69
+ "context",
70
+ "resolve",
71
+ "--input",
72
+ "-",
73
+ "--skill-id",
74
+ "rainbond-project-init"
75
+ ],
77
76
  "stdin": {
78
- "selection_id": "<selection-id>",
79
- "option_id": "<option-id>"
77
+ "required": [
78
+ "enterprise",
79
+ "workspace"
80
+ ]
80
81
  }
81
82
  },
82
83
  "read": {
83
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "read", "<tool>", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-project-init"],
84
+ "argv": [
85
+ "node",
86
+ "<home>/.rainbond/bin/rainskills-tools.js",
87
+ "read",
88
+ "<tool>",
89
+ "--input",
90
+ "-",
91
+ "--skill-id",
92
+ "rainbond-project-init"
93
+ ],
84
94
  "stdin_schema_source": "tool-catalog"
85
95
  },
86
96
  "call": {
87
- "argv": ["node", "<home>/.rainbond/bin/rainskills-tools.js", "call", "<tool>", "--input", "-", "--operation-id", "<uuid>", "--skill-id", "rainbond-project-init"],
97
+ "argv": [
98
+ "node",
99
+ "<home>/.rainbond/bin/rainskills-tools.js",
100
+ "call",
101
+ "<tool>",
102
+ "--input",
103
+ "-",
104
+ "--skill-id",
105
+ "rainbond-project-init"
106
+ ],
88
107
  "stdin_schema_source": "tool-catalog"
108
+ },
109
+ "call_confirm": {
110
+ "argv": [
111
+ "node",
112
+ "<home>/.rainbond/bin/rainskills-tools.js",
113
+ "call",
114
+ "<tool>",
115
+ "--input",
116
+ "-",
117
+ "--skill-id",
118
+ "rainbond-project-init",
119
+ "--confirm",
120
+ "<confirmation-id>"
121
+ ],
122
+ "stdin_schema_source": "same-confirmed-input"
89
123
  }
90
124
  }
91
125
  }
92
126
  ```
93
- <!-- rainskills-runtime-contract:end -->
94
-
95
- target 只允许 `codex`、`claude`、`pi`、`all`。校验 intent 后只执行 new scope 的完整 argv;只消费 schema 为 `rainskills.next-action.v1` 且校验后的 `argv` 数组。
96
-
97
- 连接完成后用固定 `onboarding-id` 执行 launcher + `["intent", "resume", "--onboarding-id", "<同一 onboarding-id>"]`,恢复原始 intent 和 `resume_step`。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、重新授权或自动重试。
98
127
  <!-- rainskills-runtime-gate:end -->
99
128
 
100
129
  受限沙箱(包括 Codex)执行本地状态命令时,必须申请用户级受保护目录访问权限;在 Codex 中使用 `require_escalated`。不得修改 `~/.rainbond` 权限、复制受保护状态到工作区,或因沙箱权限错误建议重装。
@@ -113,17 +142,11 @@ target 只允许 `codex`、`claude`、`pi`、`all`。校验 intent 后只执行
113
142
  请提示“请选择应用要运行的环境:”,并只显示:
114
143
 
115
144
  1) 云端环境(免费体验)
116
- 2) 私有环境(去对接)
117
-
118
- 用户选择私有环境后,立即执行本地 launcher + `["runtime", "message", "--id", "private-deployment-location"]`,并原样输出固定消息:
119
-
120
- 请选择部署位置:
121
-
122
- 1、部署到本机
123
- 2、部署到独立服务器
124
- 3、部署到已有 Rainbond
145
+ 2) 本机环境
146
+ 3) 独立服务器
147
+ 4) 已有 Rainbond
125
148
 
126
- 选择 1 时执行 `install-private` route,并使用 `["--location", "local"]`;选择 2 时执行 `install-private` route,并使用 `["--location", "server"]`;选择 3 时执行本地 launcher + `["runtime", "message", "--id", "private-console-origin"]`,收到地址后执行 `private-existing`。不得显示额外的接入方式中间步骤,不得重复询问部署位置,也不得在环境准备完成前询问应用来源。
149
+ 选择 1 时执行 `saas` route;选择 2 时执行 `install-private` route,并使用 `["--location", "local"]`;选择 3 时执行 `install-private` route,并使用 `["--location", "server"]`;选择 4 时执行本地 launcher + `["runtime", "message", "--id", "private-console-origin"]`,收到地址后执行 `private-existing`。不得显示“私有环境”或部署位置中间层,不得重复询问部署位置,也不得在环境准备完成前询问应用来源。
127
150
  <!-- rainskills-runtime-routing:end -->
128
151
 
129
152
  ## Overview