rainskills 0.1.34 → 0.1.36
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 +1 -1
- package/SKILL.md +1 -1
- package/bin/rainskills-tools.js +6 -1
- package/bin/rainskills.js +45 -0
- package/install.sh +39 -2
- package/marketplace/rainskills/.claude-plugin/plugin.json +1 -1
- package/marketplace/rainskills/.codex-plugin/plugin.json +1 -1
- package/marketplace/rainskills/skills/rainskills/SKILL.md +2 -2
- package/package.json +1 -1
- package/rainbond-app-assistant/SKILL.md +1 -1
- package/rainbond-app-assistant/evals/04-delivered-stop-without-promotion.response.md +7 -0
- package/rainbond-app-assistant/evals/11-delivered-with-proxy-and-local-binding.response.md +7 -0
- package/rainbond-app-assistant/evals/16-source-delivered-manual-validation.response.md +7 -0
- package/rainbond-app-assistant/references/output-contract.md +17 -0
- package/rainbond-app-assistant/references/runtime-gate.md +2 -2
- package/rainbond-app-assistant/references/workflow-rules.md +10 -1
- package/rainbond-app-version-assistant/SKILL.md +1 -1
- package/rainbond-delivery-verifier/SKILL.md +8 -1
- package/rainbond-env-sync/SKILL.md +1 -1
- package/rainbond-fullstack-bootstrap/SKILL.md +2 -2
- package/rainbond-fullstack-troubleshooter/SKILL.md +1 -1
- package/rainbond-opensource-app-deploy/references/runtime-gate.md +1 -1
- package/rainbond-platform-installer/SKILL.md +1 -1
- package/rainbond-platform-installer/scripts/installed-version.js +1 -1
- package/rainbond-platform-installer/scripts/result-telemetry.js +33 -4
- package/rainbond-platform-query/SKILL.md +1 -1
- package/rainbond-project-init/SKILL.md +1 -1
- package/rainbond-template-installer/SKILL.md +1 -1
- package/scripts/validate_customer_output.py +19 -0
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ node ~/.rainbond/lib/rainskills/bin/rainskills.js platform install --onboarding-
|
|
|
247
247
|
|
|
248
248
|
Rainskills 会在用户下一次发起业务动作时,由本地运行时立即返回环境查询结果,并另行启动后台任务静默检查更新。更新只跟随 npm `latest` 指向的正式版。当前版本是 RC 或其他预发布版本时不会查询、不会自动升级;npm 上的新 RC 版本也不参与正式版自动升级。
|
|
249
249
|
|
|
250
|
-
发现更高的正式版后,后台任务只委托到经过校验的精确版本,例如 `rainskills@0.1.
|
|
250
|
+
发现更高的正式版后,后台任务只委托到经过校验的精确版本,例如 `rainskills@0.1.36`,不会执行浮动的 `@latest` 业务代码。新版本原子刷新已经安装的 Rainskills Skills;当前业务继续使用启动时已经加载的版本,最迟从下一条新任务开始使用新版。npm 超时、版本检查失败、安装位置不安全或文件迁移失败时会保留旧版本,且不会阻塞或改变当前操作。
|
|
251
251
|
|
|
252
252
|
升级只更新 Rainskills 自身,不触发 Rainbond 安装、运行环境选择、登录授权或重新对接,也不会新增 Agent MCP 配置。更新内容仅包括 Skills 和本地 CLI。原始业务操作会继续执行;只有该业务操作本身需要运行环境时,才按既有门禁检查当前连接。可用连接直接复用,401 只重新授权一次,403 立即停止,从未连接过运行环境时才进入环境选择。
|
|
253
253
|
|
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.
|
|
57
|
+
Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.36`):
|
|
58
58
|
|
|
59
59
|
- 状态:执行 `runtime status --json`。
|
|
60
60
|
- 首次连接:执行 `runtime connect <target> --saas` 或 `runtime connect <target> --rainbond-url <Console origin>`。
|
package/bin/rainskills-tools.js
CHANGED
|
@@ -474,7 +474,7 @@ function createBridgeTelemetry(config) {
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
async function executeWithUsageTelemetry(command, config) {
|
|
477
|
-
const tracksUsage = command
|
|
477
|
+
const tracksUsage = commandTracksBusinessUsage(command);
|
|
478
478
|
const telemetry = tracksUsage ? createBridgeTelemetry(config) : null;
|
|
479
479
|
if (telemetry) await telemetry.flushPending(3).catch(() => {});
|
|
480
480
|
try {
|
|
@@ -499,6 +499,10 @@ async function executeWithUsageTelemetry(command, config) {
|
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
function commandTracksBusinessUsage(command) {
|
|
503
|
+
return command?.command === "read" || command?.command === "call";
|
|
504
|
+
}
|
|
505
|
+
|
|
502
506
|
function loadSkillBinding(config, skillId, rootSkillId) {
|
|
503
507
|
const target = skillManifestPath(config);
|
|
504
508
|
let manifest;
|
|
@@ -1451,6 +1455,7 @@ module.exports = {
|
|
|
1451
1455
|
CATALOG_TTL_MS,
|
|
1452
1456
|
CLI_VERSION,
|
|
1453
1457
|
loadConfig,
|
|
1458
|
+
commandTracksBusinessUsage,
|
|
1454
1459
|
executePackageUpload,
|
|
1455
1460
|
packageUploadEnvironment,
|
|
1456
1461
|
parseCommand,
|
package/bin/rainskills.js
CHANGED
|
@@ -252,6 +252,7 @@ async function runBuiltin(args, {
|
|
|
252
252
|
credentialEnvironment = process.env,
|
|
253
253
|
credentialPersister,
|
|
254
254
|
connectedCredentialReader,
|
|
255
|
+
resultTelemetryFactory,
|
|
255
256
|
} = {}) {
|
|
256
257
|
const getSingleRuntimeStore = () => singleRuntimeStore || require(
|
|
257
258
|
"../rainbond-platform-installer/scripts/single-runtime.js"
|
|
@@ -445,6 +446,27 @@ async function runBuiltin(args, {
|
|
|
445
446
|
intent: null,
|
|
446
447
|
operation_id: operationId,
|
|
447
448
|
};
|
|
449
|
+
const resultTelemetry = (() => {
|
|
450
|
+
try {
|
|
451
|
+
const factory = resultTelemetryFactory || require(
|
|
452
|
+
"../rainbond-platform-installer/scripts/result-telemetry.js"
|
|
453
|
+
).createResultTelemetry;
|
|
454
|
+
return factory({
|
|
455
|
+
agentType: options.targetClient,
|
|
456
|
+
packageVersion: require("../package.json").version,
|
|
457
|
+
});
|
|
458
|
+
} catch {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
})();
|
|
462
|
+
const reportRuntimeConnection = async (status, details) => {
|
|
463
|
+
try {
|
|
464
|
+
const result = resultTelemetry?.recordRuntimeConnect?.(status, details);
|
|
465
|
+
await result?.delivery?.catch(() => false);
|
|
466
|
+
} catch {
|
|
467
|
+
// Telemetry must never change authorization or connection behavior.
|
|
468
|
+
}
|
|
469
|
+
};
|
|
448
470
|
manager.startConnecting(connection, { replaceExisting: true });
|
|
449
471
|
try {
|
|
450
472
|
const invocation = runtimeConnectionInvocation(options, inspection.origin);
|
|
@@ -490,15 +512,30 @@ async function runBuiltin(args, {
|
|
|
490
512
|
manager.abortConnecting(connection);
|
|
491
513
|
}
|
|
492
514
|
write(`${JSON.stringify(runtimeConnectRetryAction(options, inspection.origin))}\n`);
|
|
515
|
+
await reportRuntimeConnection("failed", {
|
|
516
|
+
environment_kind: connection.environment_kind,
|
|
517
|
+
error_stage: "authorization",
|
|
518
|
+
error_code: "authorization_failed",
|
|
519
|
+
});
|
|
493
520
|
throw error;
|
|
494
521
|
}
|
|
495
522
|
let storedRuntime = getSingleRuntimeStore().read();
|
|
496
523
|
if (!storedRuntime || storedRuntime.console_origin !== inspection.origin) {
|
|
497
524
|
if (!connectedCredentialReader) {
|
|
525
|
+
await reportRuntimeConnection("failed", {
|
|
526
|
+
environment_kind: connection.environment_kind,
|
|
527
|
+
error_stage: "verification",
|
|
528
|
+
error_code: "verification_failed",
|
|
529
|
+
});
|
|
498
530
|
throw new Error("runtime connect 未写入唯一运行环境凭据");
|
|
499
531
|
}
|
|
500
532
|
const credential = await connectedCredentialReader(inspection.origin);
|
|
501
533
|
if (!credential || credential.origin !== inspection.origin) {
|
|
534
|
+
await reportRuntimeConnection("failed", {
|
|
535
|
+
environment_kind: connection.environment_kind,
|
|
536
|
+
error_stage: "verification",
|
|
537
|
+
error_code: "verification_failed",
|
|
538
|
+
});
|
|
502
539
|
throw new Error("运行环境凭据与已验证 Console origin 不匹配");
|
|
503
540
|
}
|
|
504
541
|
getSingleRuntimeStore().write({
|
|
@@ -510,8 +547,16 @@ async function runBuiltin(args, {
|
|
|
510
547
|
storedRuntime = getSingleRuntimeStore().read();
|
|
511
548
|
}
|
|
512
549
|
if (!storedRuntime || storedRuntime.console_origin !== inspection.origin) {
|
|
550
|
+
await reportRuntimeConnection("failed", {
|
|
551
|
+
environment_kind: connection.environment_kind,
|
|
552
|
+
error_stage: "verification",
|
|
553
|
+
error_code: "verification_failed",
|
|
554
|
+
});
|
|
513
555
|
throw new Error("唯一运行环境凭据与已连接 Console origin 不匹配");
|
|
514
556
|
}
|
|
557
|
+
await reportRuntimeConnection("success", {
|
|
558
|
+
environment_kind: connection.environment_kind,
|
|
559
|
+
});
|
|
515
560
|
write(`${JSON.stringify({
|
|
516
561
|
schema: "rainskills.runtime-connect-result.v1",
|
|
517
562
|
state: "connected",
|
package/install.sh
CHANGED
|
@@ -423,6 +423,14 @@ rainskills_v2_execution_environment() {
|
|
|
423
423
|
fi
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
rainskills_v2_environment_kind() {
|
|
427
|
+
case "$DEPLOYMENT_MODE_INPUT" in
|
|
428
|
+
saas) printf 'saas\n' ;;
|
|
429
|
+
self-hosted) printf 'private\n' ;;
|
|
430
|
+
*) return 1 ;;
|
|
431
|
+
esac
|
|
432
|
+
}
|
|
433
|
+
|
|
426
434
|
rainskills_v2_error_stage() {
|
|
427
435
|
case "${RAINSKILLS_INSTALL_FAILURE_STAGE:-install}" in
|
|
428
436
|
bootstrap) printf 'preflight\n' ;;
|
|
@@ -453,6 +461,7 @@ report_rainskills_v2_event() {
|
|
|
453
461
|
local agent="${3:-}"
|
|
454
462
|
local error_stage="${4:-}"
|
|
455
463
|
local error_code="${5:-}"
|
|
464
|
+
local environment_kind="${6:-}"
|
|
456
465
|
rainskills_v2_enabled || return 0
|
|
457
466
|
[[ -n "${RAINSKILLS_TELEMETRY_INSTALLATION_ID:-}" ]] || return 0
|
|
458
467
|
command -v curl >/dev/null 2>&1 || return 0
|
|
@@ -474,6 +483,7 @@ report_rainskills_v2_event() {
|
|
|
474
483
|
"$status" \
|
|
475
484
|
"$error_stage" \
|
|
476
485
|
"$error_code" \
|
|
486
|
+
"$environment_kind" \
|
|
477
487
|
"$telemetry_dir" <<'PY'
|
|
478
488
|
import datetime
|
|
479
489
|
import json
|
|
@@ -483,10 +493,10 @@ import time
|
|
|
483
493
|
import uuid
|
|
484
494
|
|
|
485
495
|
(event_type, installation_id, attempt_id, package_version, action, agent,
|
|
486
|
-
os_type, os_arch, execution_environment, status, error_stage, error_code,
|
|
496
|
+
os_type, os_arch, execution_environment, status, error_stage, error_code, environment_kind,
|
|
487
497
|
telemetry_dir) = sys.argv[1:]
|
|
488
498
|
event = {
|
|
489
|
-
"schema": "rainskills.telemetry-event.v2",
|
|
499
|
+
"schema": "rainskills.telemetry-event.v3" if event_type == "runtime_connect_result" else "rainskills.telemetry-event.v2",
|
|
490
500
|
"event_id": str(uuid.uuid4()),
|
|
491
501
|
"event_type": event_type,
|
|
492
502
|
"installation_id": installation_id,
|
|
@@ -505,6 +515,8 @@ if event_type == "install_result":
|
|
|
505
515
|
})
|
|
506
516
|
elif event_type == "agent_config_result":
|
|
507
517
|
event.update({"agent_type": agent, "status": status})
|
|
518
|
+
elif event_type == "runtime_connect_result":
|
|
519
|
+
event.update({"agent_type": agent, "environment_kind": environment_kind, "status": status})
|
|
508
520
|
if status == "failed":
|
|
509
521
|
event["error_stage"] = error_stage
|
|
510
522
|
event["error_code"] = error_code
|
|
@@ -555,6 +567,27 @@ PY
|
|
|
555
567
|
) &
|
|
556
568
|
}
|
|
557
569
|
|
|
570
|
+
report_rainskills_runtime_connect_result() {
|
|
571
|
+
local status="$1"
|
|
572
|
+
local environment_kind
|
|
573
|
+
environment_kind="$(rainskills_v2_environment_kind 2>/dev/null || true)"
|
|
574
|
+
[[ -n "$environment_kind" ]] || return 0
|
|
575
|
+
report_rainskills_v2_event \
|
|
576
|
+
"runtime_connect_result" \
|
|
577
|
+
"$status" \
|
|
578
|
+
"$RAINSKILLS_INSTALL_CLIENT" \
|
|
579
|
+
"$([[ "$status" == "failed" ]] && rainskills_v2_error_stage || true)" \
|
|
580
|
+
"$([[ "$status" == "failed" ]] && rainskills_v2_error_code || true)" \
|
|
581
|
+
"$environment_kind"
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
report_rainskills_runtime_connect_failure_if_applicable() {
|
|
585
|
+
[[ "$ACTION" != "connect" ]] || return 0
|
|
586
|
+
case "$RAINSKILLS_INSTALL_FAILURE_STAGE" in
|
|
587
|
+
authorization|verification) report_rainskills_runtime_connect_result "failed" ;;
|
|
588
|
+
esac
|
|
589
|
+
}
|
|
590
|
+
|
|
558
591
|
record_rainskills_v2_configured_agent() {
|
|
559
592
|
local agent
|
|
560
593
|
agent="$(rainskills_v2_agent "$1")"
|
|
@@ -938,6 +971,7 @@ report_unhandled_rainskills_installation_failure() {
|
|
|
938
971
|
"${BASH_SUBSHELL:-0}" -eq 0 && \
|
|
939
972
|
"$RAINSKILLS_V2_TERMINAL_REPORTED" -eq 0 ]]; then
|
|
940
973
|
RAINSKILLS_V2_TERMINAL_REPORTED=1
|
|
974
|
+
report_rainskills_runtime_connect_failure_if_applicable
|
|
941
975
|
if [[ -n "$RAINSKILLS_V2_CURRENT_AGENT" ]]; then
|
|
942
976
|
report_rainskills_v2_event \
|
|
943
977
|
"agent_config_result" \
|
|
@@ -1037,6 +1071,7 @@ handle_installer_exit() {
|
|
|
1037
1071
|
die() {
|
|
1038
1072
|
if [[ "${BASH_SUBSHELL:-0}" -eq 0 && "$RAINSKILLS_V2_TERMINAL_REPORTED" -eq 0 ]]; then
|
|
1039
1073
|
RAINSKILLS_V2_TERMINAL_REPORTED=1
|
|
1074
|
+
report_rainskills_runtime_connect_failure_if_applicable
|
|
1040
1075
|
if [[ -n "$RAINSKILLS_V2_CURRENT_AGENT" ]]; then
|
|
1041
1076
|
report_rainskills_v2_event \
|
|
1042
1077
|
"agent_config_result" \
|
|
@@ -2914,6 +2949,7 @@ do_refresh() {
|
|
|
2914
2949
|
|
|
2915
2950
|
export RAINBOND_JWT="$token"
|
|
2916
2951
|
write_token_file "$token" "$base_url"
|
|
2952
|
+
report_rainskills_runtime_connect_result "success"
|
|
2917
2953
|
set_rainskills_failure_context "configuration" "cli_configuration_failed"
|
|
2918
2954
|
report_rainskills_lifecycle_event "configure_cli" "install_cli" "configure_cli" "started"
|
|
2919
2955
|
install_local_cli
|
|
@@ -2999,6 +3035,7 @@ configure_runtime_connection() {
|
|
|
2999
3035
|
|| die "runtime connect 凭据安全写入失败。"
|
|
3000
3036
|
else
|
|
3001
3037
|
write_token_file "$token" "$base_url"
|
|
3038
|
+
report_rainskills_runtime_connect_result "success"
|
|
3002
3039
|
fi
|
|
3003
3040
|
|
|
3004
3041
|
set_rainskills_failure_context "configuration" "cli_configuration_failed"
|
|
@@ -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.
|
|
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.36 <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.
|
|
57
|
+
Rainskills 只保存一个全局运行环境,不维护环境列表、名称、默认值或环境 ID。使用固定 launcher `node <home>/.rainbond/lib/rainskills/bin/rainskills.js`(运行包版本 `rainskills@0.1.36`):
|
|
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.
|
|
3
|
+
"version": "0.1.36",
|
|
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",
|
|
@@ -56,7 +56,7 @@ workspace context 包含 `enterprise_id`、`team_id`、`team_name` 和 `region_n
|
|
|
56
56
|
|
|
57
57
|
普通请求只给用户可用的中文结果:
|
|
58
58
|
|
|
59
|
-
-
|
|
59
|
+
- 成功:只报真实交付结果;加载 [output contract](references/output-contract.md),以“部署成功后的固定动作块”收尾。
|
|
60
60
|
- 未完成:输出“部署失败。”、一句直接原因;仅在确有安全可执行方案时增加“解决办法”。
|
|
61
61
|
- 默认不得展示内部对象、状态枚举、Skill/工具名、YAML、JSON 或英文编排标题。
|
|
62
62
|
- 过程消息和最终结果中的工作空间一律用 `team_name` 展示,可连同 `region_name` 写成“工作空间 / 集群”;除非用户明确要求调试原始数据,不展示 `team_id`。
|
|
@@ -46,6 +46,16 @@
|
|
|
46
46
|
- when browser confirmation remains, add at most one short validation note
|
|
47
47
|
- include proxy/mirror usage when it affected the deployment
|
|
48
48
|
- include warnings that matter after delivery, such as development-only database auth or missing production persistence
|
|
49
|
+
- every reply whose result line is `部署成功。` or `部署成功,待浏览器访问确认。` must append the following fixed block exactly once after all other sections, with no prose after it:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
你接下来可以:
|
|
53
|
+
|
|
54
|
+
1. 修改代码并重新部署
|
|
55
|
+
2. 将当前应用创建快照发布版本,用于部署到生产环境
|
|
56
|
+
3. 查看运行日志
|
|
57
|
+
4. 将应用迁移到自己的 Rainbond
|
|
58
|
+
```
|
|
49
59
|
- do not expose orchestration enums, lower-level skill names, `Blocking Issue: none`, or the internal action ledger
|
|
50
60
|
|
|
51
61
|
Default concise section order:
|
|
@@ -75,6 +85,13 @@
|
|
|
75
85
|
|
|
76
86
|
### 处理记录
|
|
77
87
|
- 使用镜像代理完成依赖拉取
|
|
88
|
+
|
|
89
|
+
你接下来可以:
|
|
90
|
+
|
|
91
|
+
1. 修改代码并重新部署
|
|
92
|
+
2. 将当前应用创建快照发布版本,用于部署到生产环境
|
|
93
|
+
3. 查看运行日志
|
|
94
|
+
4. 将应用迁移到自己的 Rainbond
|
|
78
95
|
```
|
|
79
96
|
|
|
80
97
|
### Structured contract mode
|
|
@@ -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.
|
|
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.36` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 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.
|
|
29
|
+
"package_version": "rainskills@0.1.36",
|
|
30
30
|
"runtime_status": [
|
|
31
31
|
"node",
|
|
32
32
|
"<home>/.rainbond/lib/rainskills/bin/rainskills.js",
|
|
@@ -423,7 +423,16 @@
|
|
|
423
423
|
具体的 provider 命名约定、connection env 变量名(`DB_*` / `REDIS_*` / `KAFKA_*` 等是示例,按 provider 文档实际名字为准)、以及依赖 alias 细节,详见 bootstrap modules/30-creation-rules.md 的相关章节。
|
|
424
424
|
24. 不要自动拉起本地 Docker Desktop/OrbStack、执行本地 Docker build/push、或推送临时镜像作为兜底;这属于 delivery-mode 策略切换,必须先得到用户明确确认。
|
|
425
425
|
25. 每次运行内部仍必须形成 `AppAssistantResult` 结果对象,但默认用户答复不一定暴露 YAML。
|
|
426
|
-
当 `source_app_delivery` 的 runtime healthy、没有 blocker、控制台部署位置和公网访问地址都已确定,且 delivery 已 `delivered` 或只剩浏览器人工确认时,默认使用简洁中文交付报告,不追加 `### Structured Output
|
|
426
|
+
当 `source_app_delivery` 的 runtime healthy、没有 blocker、控制台部署位置和公网访问地址都已确定,且 delivery 已 `delivered` 或只剩浏览器人工确认时,默认使用简洁中文交付报告,不追加 `### Structured Output`。报告的全部现有内容结束后,必须以以下固定动作块收尾;动作块只出现一次,后面不得再追加文字:
|
|
427
|
+
|
|
428
|
+
```text
|
|
429
|
+
你接下来可以:
|
|
430
|
+
|
|
431
|
+
1. 修改代码并重新部署
|
|
432
|
+
2. 将当前应用创建快照发布版本,用于部署到生产环境
|
|
433
|
+
3. 查看运行日志
|
|
434
|
+
4. 将应用迁移到自己的 Rainbond
|
|
435
|
+
```
|
|
427
436
|
26. 只有在自动化/评测明确要求结构化契约,或用户明确要求 YAML、JSON、调试详情、内部状态对象时,才把 `AppAssistantResult` 渲染为最终 fenced `yaml`。部署失败、仍在构建、存在 blocker/handoff/身份歧义或进入 dev-to-test promotion 都不是默认暴露 YAML 的理由。
|
|
428
437
|
27. 如果本次使用了 Git、镜像仓库或其他传输代理,必须在默认交付报告的处理记录或注意事项中说明;在结构化模式下也必须写入 `actions_performed[].details`。
|
|
429
438
|
代理事实属于执行记录,不是强制暴露 YAML 的理由。
|
|
@@ -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.
|
|
41
|
+
"package_version": "rainskills@0.1.36",
|
|
42
42
|
"runtime_status": [
|
|
43
43
|
"node",
|
|
44
44
|
"<home>/.rainbond/lib/rainskills/bin/rainskills.js",
|
|
@@ -22,6 +22,13 @@ description: "Use only when the user explicitly asks for final delivery or acces
|
|
|
22
22
|
- 运行环境地址:<Rainbond Console 或应用管理页面地址>
|
|
23
23
|
- 应用访问地址:<部署完成后真实可访问的应用地址>
|
|
24
24
|
- 已完成操作:<用一句话概括本轮实际完成的项目识别、应用创建、组件构建、启动和访问验证;只列真实执行过的操作>
|
|
25
|
+
|
|
26
|
+
你接下来可以:
|
|
27
|
+
|
|
28
|
+
1. 修改代码并重新部署
|
|
29
|
+
2. 将当前应用创建快照发布版本,用于部署到生产环境
|
|
30
|
+
3. 查看运行日志
|
|
31
|
+
4. 将应用迁移到自己的 Rainbond
|
|
25
32
|
```
|
|
26
33
|
|
|
27
34
|
部署失败或未完成时只输出:
|
|
@@ -61,7 +68,7 @@ Hermes Agent 执行带 `--input -` 的一次性业务命令时,使用 `termina
|
|
|
61
68
|
```json
|
|
62
69
|
{
|
|
63
70
|
"schema": "rainskills.single-runtime-contract.v1",
|
|
64
|
-
"package_version": "rainskills@0.1.
|
|
71
|
+
"package_version": "rainskills@0.1.36",
|
|
65
72
|
"runtime_status": [
|
|
66
73
|
"node",
|
|
67
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.
|
|
41
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
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.36` 的更新/修复流程并重新取得一致状态;不得在版本错配时继续业务调用。不得配置或直接调用客户端 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.
|
|
41
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
64
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
29
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
35
|
+
2. Use the installed local launcher `["node", "<home>/.rainbond/lib/rainskills/bin/rainskills.js"]`; its protected runtime package marker is `rainskills@0.1.36` 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 连接或修改服务器。
|
|
@@ -6,9 +6,13 @@ const path = require("node:path");
|
|
|
6
6
|
const { randomUUID: defaultRandomUUID } = require("node:crypto");
|
|
7
7
|
|
|
8
8
|
const SCHEMA = "rainskills.telemetry-event.v2";
|
|
9
|
+
const RUNTIME_CONNECT_SCHEMA = "rainskills.telemetry-event.v3";
|
|
9
10
|
const DEFAULT_REPORT_URL = "https://log.rainbond.com/api/rainskills/events";
|
|
10
11
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11
12
|
const AGENTS = new Set(["codex", "pi", "claude_code", "deepseek", "workbuddy", "hermes_agent", "other", "unknown"]);
|
|
13
|
+
const RUNTIME_CONNECT_ERROR_CODES = new Set([
|
|
14
|
+
"authorization_failed", "verification_failed", "network_unreachable", "user_cancelled", "unknown",
|
|
15
|
+
]);
|
|
12
16
|
const MAX_PENDING_EVENTS = 100;
|
|
13
17
|
const MAX_PENDING_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
14
18
|
|
|
@@ -135,7 +139,7 @@ function prunePending(directory, nowMs) {
|
|
|
135
139
|
|
|
136
140
|
function buildEvent({ installationId, packageVersion, agentType, randomUUID, now }, input) {
|
|
137
141
|
const event = {
|
|
138
|
-
schema: SCHEMA,
|
|
142
|
+
schema: input.event_type === "runtime_connect_result" ? RUNTIME_CONNECT_SCHEMA : SCHEMA,
|
|
139
143
|
event_id: randomUUID(),
|
|
140
144
|
event_type: input.event_type,
|
|
141
145
|
installation_id: installationId,
|
|
@@ -144,10 +148,10 @@ function buildEvent({ installationId, packageVersion, agentType, randomUUID, now
|
|
|
144
148
|
};
|
|
145
149
|
const fields = [
|
|
146
150
|
"install_attempt_id", "action", "agent_type", "os_type", "os_arch",
|
|
147
|
-
"execution_environment", "status", "error_stage", "error_code",
|
|
151
|
+
"execution_environment", "environment_kind", "status", "error_stage", "error_code",
|
|
148
152
|
];
|
|
149
153
|
for (const field of fields) {
|
|
150
|
-
const usesDefaultAgent = ["first_use_result", "active_daily"].includes(input.event_type);
|
|
154
|
+
const usesDefaultAgent = ["runtime_connect_result", "first_use_result", "active_daily"].includes(input.event_type);
|
|
151
155
|
const value = field === "agent_type"
|
|
152
156
|
? (input[field] || (usesDefaultAgent ? agentType : ""))
|
|
153
157
|
: input[field];
|
|
@@ -239,10 +243,28 @@ function createResultTelemetry({
|
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
function recordActiveDaily() {
|
|
242
|
-
const activeDate = now()
|
|
246
|
+
const activeDate = formatBeijingDate(now());
|
|
243
247
|
return record({ event_type: "active_daily" }, `active:${resolvedAgent}:${activeDate}`);
|
|
244
248
|
}
|
|
245
249
|
|
|
250
|
+
function recordRuntimeConnect(status, details = {}) {
|
|
251
|
+
if (!["success", "failed"].includes(status)) return skippedResult();
|
|
252
|
+
if (!["saas", "private"].includes(details.environment_kind)) return skippedResult();
|
|
253
|
+
if (status === "failed" && ![
|
|
254
|
+
"authorization", "verification",
|
|
255
|
+
].includes(details.error_stage)) return skippedResult();
|
|
256
|
+
if (status === "failed" && !RUNTIME_CONNECT_ERROR_CODES.has(details.error_code)) return skippedResult();
|
|
257
|
+
return record({
|
|
258
|
+
event_type: "runtime_connect_result",
|
|
259
|
+
environment_kind: details.environment_kind,
|
|
260
|
+
status,
|
|
261
|
+
...(status === "failed" ? {
|
|
262
|
+
error_stage: details.error_stage,
|
|
263
|
+
error_code: details.error_code,
|
|
264
|
+
} : {}),
|
|
265
|
+
}, `runtime-connect:${resolvedAgent}:${details.environment_kind}:${status}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
246
268
|
async function flushPending(limit = MAX_PENDING_EVENTS) {
|
|
247
269
|
if (disabled) return;
|
|
248
270
|
let files;
|
|
@@ -270,14 +292,21 @@ function createResultTelemetry({
|
|
|
270
292
|
installationId: resolvedInstallationId,
|
|
271
293
|
agentType: resolvedAgent,
|
|
272
294
|
record,
|
|
295
|
+
recordRuntimeConnect,
|
|
273
296
|
recordFirstUse,
|
|
274
297
|
recordActiveDaily,
|
|
275
298
|
flushPending,
|
|
276
299
|
};
|
|
277
300
|
}
|
|
278
301
|
|
|
302
|
+
function formatBeijingDate(value) {
|
|
303
|
+
const shifted = new Date(value.getTime() + 8 * 60 * 60 * 1000);
|
|
304
|
+
return shifted.toISOString().slice(0, 10);
|
|
305
|
+
}
|
|
306
|
+
|
|
279
307
|
module.exports = {
|
|
280
308
|
DEFAULT_REPORT_URL,
|
|
309
|
+
RUNTIME_CONNECT_SCHEMA,
|
|
281
310
|
SCHEMA,
|
|
282
311
|
createResultTelemetry,
|
|
283
312
|
ensureInstallationId,
|
|
@@ -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.
|
|
32
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
41
|
+
"package_version": "rainskills@0.1.36",
|
|
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.
|
|
41
|
+
"package_version": "rainskills@0.1.36",
|
|
42
42
|
"runtime_status": [
|
|
43
43
|
"node",
|
|
44
44
|
"<home>/.rainbond/lib/rainskills/bin/rainskills.js",
|
|
@@ -28,6 +28,17 @@ SECRET_ASSIGNMENT = re.compile(
|
|
|
28
28
|
|
|
29
29
|
MASKED_VALUES = {"***", "[masked]", "<masked>", "redacted", "<redacted>", "null"}
|
|
30
30
|
|
|
31
|
+
SUCCESSFUL_DEPLOYMENT_NEXT_ACTIONS = """你接下来可以:
|
|
32
|
+
|
|
33
|
+
1. 修改代码并重新部署
|
|
34
|
+
2. 将当前应用创建快照发布版本,用于部署到生产环境
|
|
35
|
+
3. 查看运行日志
|
|
36
|
+
4. 将应用迁移到自己的 Rainbond"""
|
|
37
|
+
|
|
38
|
+
SUCCESSFUL_DEPLOYMENT_PATTERN = re.compile(
|
|
39
|
+
r"(?m)^部署成功(?:,待浏览器访问确认)?。$"
|
|
40
|
+
)
|
|
41
|
+
|
|
31
42
|
|
|
32
43
|
def validate_customer_output(response_text: str, expected: dict[str, Any] | None = None) -> list[str]:
|
|
33
44
|
errors: list[str] = []
|
|
@@ -48,6 +59,14 @@ def validate_customer_output(response_text: str, expected: dict[str, Any] | None
|
|
|
48
59
|
errors.append("customer response appears to expose secret material")
|
|
49
60
|
break
|
|
50
61
|
|
|
62
|
+
if SUCCESSFUL_DEPLOYMENT_PATTERN.search(response_text):
|
|
63
|
+
if response_text.count(SUCCESSFUL_DEPLOYMENT_NEXT_ACTIONS) != 1:
|
|
64
|
+
errors.append(
|
|
65
|
+
"successful deployment response must contain the four customer next actions exactly once"
|
|
66
|
+
)
|
|
67
|
+
if not response_text.rstrip().endswith(SUCCESSFUL_DEPLOYMENT_NEXT_ACTIONS):
|
|
68
|
+
errors.append("successful deployment response must end with the customer next actions")
|
|
69
|
+
|
|
51
70
|
assertions = (expected or {}).get("assert", {})
|
|
52
71
|
for needle in assertions.get("prose_contains", []):
|
|
53
72
|
if needle not in response_text:
|