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.
- package/README.md +34 -39
- package/SKILL.md +12 -10
- package/bin/rainskills-tools.js +114 -240
- package/bin/rainskills.js +134 -591
- package/install.sh +68 -94
- 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 +13 -11
- package/package.json +2 -2
- package/rainbond-app-assistant/SKILL.md +116 -151
- package/rainbond-app-version-assistant/SKILL.md +98 -98
- package/rainbond-delivery-verifier/SKILL.md +98 -68
- package/rainbond-env-sync/SKILL.md +98 -69
- package/rainbond-fullstack-bootstrap/SKILL.md +101 -89
- package/rainbond-fullstack-bootstrap/modules/10-context-loading.md +1 -1
- package/rainbond-fullstack-troubleshooter/SKILL.md +98 -68
- package/rainbond-opensource-app-deploy/SKILL.md +102 -81
- package/rainbond-platform-installer/SKILL.md +4 -4
- package/rainbond-platform-installer/scripts/auto-update.js +0 -9
- package/rainbond-platform-installer/scripts/installed-version.js +1 -1
- package/rainbond-platform-installer/scripts/platform-installer.js +3 -89
- package/rainbond-platform-installer/scripts/runtime-state.js +14 -251
- package/rainbond-platform-installer/scripts/single-runtime.js +114 -0
- package/rainbond-platform-installer/scripts/user-message.js +3 -49
- package/rainbond-platform-installer/scripts/windows-client-config.js +0 -51
- package/rainbond-platform-installer/scripts/windows-onboarding.js +0 -3
- package/rainbond-platform-installer/scripts/windows-platform.ps1 +1 -1
- package/rainbond-platform-query/SKILL.md +103 -61
- package/rainbond-project-init/SKILL.md +102 -79
- package/rainbond-template-installer/SKILL.md +102 -80
- package/rainbond-platform-installer/scripts/environment-credentials.js +0 -225
- package/rainbond-platform-installer/scripts/environment-registry.js +0 -349
- package/rainbond-platform-installer/scripts/local-runtime-commands.js +0 -180
- package/rainbond-platform-installer/scripts/local-runtime.js +0 -58
- package/rainbond-platform-installer/scripts/runtime-context-resolver.js +0 -189
- package/rainbond-platform-installer/scripts/runtime-credentials.js +0 -163
- package/rainbond-platform-installer/scripts/runtime-intents.js +0 -386
- package/rainbond-platform-installer/scripts/runtime-operations.js +0 -597
- package/rainbond-platform-installer/scripts/windows-client-config.ps1 +0 -9
- package/rainbond-platform-installer/scripts/windows-read-user-environment.ps1 +0 -16
package/bin/rainskills.js
CHANGED
|
@@ -4,7 +4,6 @@ const path = require("node:path");
|
|
|
4
4
|
const crypto = require("node:crypto");
|
|
5
5
|
const os = require("node:os");
|
|
6
6
|
const { spawn } = require("node:child_process");
|
|
7
|
-
const { isDeepStrictEqual } = require("node:util");
|
|
8
7
|
const {
|
|
9
8
|
detectControlEnvironment,
|
|
10
9
|
} = require("../rainbond-platform-installer/scripts/control-environment.js");
|
|
@@ -30,32 +29,17 @@ const RUNTIME_CHILD_ENVIRONMENT_KEYS = Object.freeze([
|
|
|
30
29
|
"RAINSKILLS_INSTALL_REPORT_URL", "RAINSKILLS_LIFECYCLE_REPORT_URL",
|
|
31
30
|
]);
|
|
32
31
|
|
|
33
|
-
function runtimeChildEnvironment(source = process.env, extra = {}
|
|
32
|
+
function runtimeChildEnvironment(source = process.env, extra = {}) {
|
|
34
33
|
const environment = {};
|
|
35
34
|
for (const key of RUNTIME_CHILD_ENVIRONMENT_KEYS) {
|
|
36
35
|
if (typeof source[key] === "string") environment[key] = source[key];
|
|
37
36
|
}
|
|
38
37
|
for (const key of Object.keys(extra)) {
|
|
39
|
-
if (!["RAINSKILLS_RUNTIME_CONNECT_COMPLETION", "RAINSKILLS_RUNTIME_OPERATION_ID"
|
|
38
|
+
if (!["RAINSKILLS_RUNTIME_CONNECT_COMPLETION", "RAINSKILLS_RUNTIME_OPERATION_ID"].includes(key)) {
|
|
40
39
|
throw new Error("runtime child environment 包含未知字段");
|
|
41
40
|
}
|
|
42
41
|
environment[key] = extra[key];
|
|
43
42
|
}
|
|
44
|
-
if (
|
|
45
|
-
expectedOrigin
|
|
46
|
-
&& typeof source.RAINBOND_JWT === "string"
|
|
47
|
-
&& /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(source.RAINBOND_JWT)
|
|
48
|
-
&& typeof source.RAINBOND_URL === "string"
|
|
49
|
-
) {
|
|
50
|
-
try {
|
|
51
|
-
if (normalizeConsoleOrigin(source.RAINBOND_URL) === normalizeConsoleOrigin(expectedOrigin)) {
|
|
52
|
-
environment.RAINBOND_JWT = source.RAINBOND_JWT;
|
|
53
|
-
environment.RAINBOND_URL = normalizeConsoleOrigin(expectedOrigin);
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
// An inherited credential is optional. Invalid or origin-drifted pairs are discarded.
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
43
|
return environment;
|
|
60
44
|
}
|
|
61
45
|
|
|
@@ -103,20 +87,6 @@ function assertConnectingState(current, expected) {
|
|
|
103
87
|
}
|
|
104
88
|
}
|
|
105
89
|
|
|
106
|
-
function assertExactConnectedState(current, expected) {
|
|
107
|
-
if (
|
|
108
|
-
!current
|
|
109
|
-
|| current.state !== "connected"
|
|
110
|
-
|| current.operation_id !== expected.operation_id
|
|
111
|
-
|| current.target_client !== expected.target_client
|
|
112
|
-
|| current.environment_kind !== expected.environment_kind
|
|
113
|
-
|| current.console_origin !== expected.console_origin
|
|
114
|
-
|| !isDeepStrictEqual(current.intent, expected.intent)
|
|
115
|
-
) {
|
|
116
|
-
throw new Error("runtime reconnect 完成状态与 protected connection 不匹配");
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
90
|
function parseRuntimeConnectArgs(args) {
|
|
121
91
|
if (args[0] !== "runtime" || args[1] !== "connect") {
|
|
122
92
|
throw new Error("不是 runtime connect 命令");
|
|
@@ -128,8 +98,6 @@ function parseRuntimeConnectArgs(args) {
|
|
|
128
98
|
let environmentChoice = "";
|
|
129
99
|
let rainbondUrl = "";
|
|
130
100
|
let allowInsecureHttp = false;
|
|
131
|
-
let onboardingId = "";
|
|
132
|
-
let intentInput = "";
|
|
133
101
|
let privateLocation = "";
|
|
134
102
|
for (let index = 3; index < args.length; index += 1) {
|
|
135
103
|
const argument = args[index];
|
|
@@ -149,31 +117,13 @@ function parseRuntimeConnectArgs(args) {
|
|
|
149
117
|
throw new Error("runtime connect 私有部署位置只支持 local 或 server");
|
|
150
118
|
}
|
|
151
119
|
index += 1;
|
|
152
|
-
} else if (
|
|
153
|
-
|
|
154
|
-
index += 1;
|
|
155
|
-
} else if (argument === "--intent-json") {
|
|
156
|
-
intentInput = requireFixedValue(args, index, argument);
|
|
157
|
-
index += 1;
|
|
120
|
+
} else if (["--intent-json", "--onboarding-id"].includes(argument)) {
|
|
121
|
+
throw new Error(`runtime connect 不再支持 ${argument}`);
|
|
158
122
|
} else {
|
|
159
123
|
throw new Error("runtime connect 包含未知参数");
|
|
160
124
|
}
|
|
161
125
|
}
|
|
162
126
|
if (!environmentChoice) throw new Error("runtime connect 必须选择一个应用运行环境");
|
|
163
|
-
if (!intentInput || intentInput.length > 16384) throw new Error("runtime connect 需要受限的 intent JSON");
|
|
164
|
-
let rawIntent;
|
|
165
|
-
try {
|
|
166
|
-
rawIntent = JSON.parse(intentInput);
|
|
167
|
-
} catch {
|
|
168
|
-
throw new Error("intent JSON 无效");
|
|
169
|
-
}
|
|
170
|
-
const { assertIntentCanInstallNewPlatform, validateIntent } = require(
|
|
171
|
-
"../rainbond-platform-installer/scripts/runtime-intents.js"
|
|
172
|
-
);
|
|
173
|
-
const intent = environmentChoice === "install-private"
|
|
174
|
-
? assertIntentCanInstallNewPlatform(rawIntent)
|
|
175
|
-
: validateIntent(rawIntent);
|
|
176
|
-
if (onboardingId && !UUID_PATTERN.test(onboardingId)) throw new Error("onboarding id 无效");
|
|
177
127
|
if (allowInsecureHttp && environmentChoice !== "private-existing") {
|
|
178
128
|
throw new Error("--allow-insecure-http 只适用于明确的私有 Console 地址");
|
|
179
129
|
}
|
|
@@ -185,9 +135,7 @@ function parseRuntimeConnectArgs(args) {
|
|
|
185
135
|
environmentChoice,
|
|
186
136
|
rainbondUrl,
|
|
187
137
|
allowInsecureHttp,
|
|
188
|
-
|
|
189
|
-
intent,
|
|
190
|
-
...(privateLocation ? { privateLocation } : {}),
|
|
138
|
+
privateLocation: privateLocation || undefined,
|
|
191
139
|
};
|
|
192
140
|
}
|
|
193
141
|
|
|
@@ -197,141 +145,25 @@ function runtimeConnectionInvocation(options, origin) {
|
|
|
197
145
|
if (options.environmentChoice === "saas") args.push("--saas");
|
|
198
146
|
else args.push("--self-hosted", "--rainbond-url", origin);
|
|
199
147
|
if (options.allowInsecureHttp) args.push("--allow-insecure-http");
|
|
148
|
+
args.push("--no-cached-token");
|
|
200
149
|
return {
|
|
201
150
|
executable: "bash",
|
|
202
151
|
args,
|
|
203
152
|
};
|
|
204
153
|
}
|
|
205
154
|
|
|
206
|
-
function runtimeConnectRetryAction(options, origin
|
|
155
|
+
function runtimeConnectRetryAction(options, origin) {
|
|
207
156
|
const argv = ["runtime", "connect", options.targetClient];
|
|
208
157
|
if (options.environmentChoice === "saas") argv.push("--saas");
|
|
209
158
|
else argv.push("--rainbond-url", origin);
|
|
210
159
|
if (options.allowInsecureHttp) argv.push("--allow-insecure-http");
|
|
211
|
-
argv.push("--onboarding-id", operationId, "--intent-json", JSON.stringify(options.intent));
|
|
212
160
|
return {
|
|
213
161
|
schema: "rainskills.next-action.v1",
|
|
214
162
|
action: "retry-runtime-connect",
|
|
215
|
-
onboarding_id: operationId,
|
|
216
163
|
argv,
|
|
217
164
|
};
|
|
218
165
|
}
|
|
219
166
|
|
|
220
|
-
function runtimeReconnectRetryAction(operationId) {
|
|
221
|
-
return {
|
|
222
|
-
schema: "rainskills.next-action.v1",
|
|
223
|
-
action: "retry-runtime-reconnect",
|
|
224
|
-
onboarding_id: operationId,
|
|
225
|
-
argv: ["runtime", "reconnect", "--onboarding-id", operationId],
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function parseRuntimeFailureArgs(args) {
|
|
230
|
-
if (
|
|
231
|
-
args.length !== 8
|
|
232
|
-
|| args[0] !== "runtime"
|
|
233
|
-
|| args[1] !== "record-failure"
|
|
234
|
-
|| args[2] !== "--onboarding-id"
|
|
235
|
-
|| args[4] !== "--step"
|
|
236
|
-
|| args[6] !== "--reason"
|
|
237
|
-
) {
|
|
238
|
-
throw new Error("runtime record-failure 参数无效");
|
|
239
|
-
}
|
|
240
|
-
if (!UUID_PATTERN.test(args[3] || "")) throw new Error("runtime failure onboarding operation 无效");
|
|
241
|
-
if (!args[5] || args[5].startsWith("--")) throw new Error("runtime failure step 无效");
|
|
242
|
-
if (!["credential-expired", "permission-denied"].includes(args[7])) {
|
|
243
|
-
throw new Error("runtime failure reason 无效");
|
|
244
|
-
}
|
|
245
|
-
return { operationId: args[3], step: args[5], reason: args[7] };
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function parseRuntimeReconnectArgs(args) {
|
|
249
|
-
if (
|
|
250
|
-
args.length !== 4
|
|
251
|
-
|| args[0] !== "runtime"
|
|
252
|
-
|| args[1] !== "reconnect"
|
|
253
|
-
|| args[2] !== "--onboarding-id"
|
|
254
|
-
|| !UUID_PATTERN.test(args[3] || "")
|
|
255
|
-
) {
|
|
256
|
-
throw new Error("runtime reconnect 参数无效");
|
|
257
|
-
}
|
|
258
|
-
return { operationId: args[3] };
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function createEnvironmentRuntimeServices() {
|
|
262
|
-
const { createEnvironmentRegistry } = require(
|
|
263
|
-
"../rainbond-platform-installer/scripts/environment-registry.js"
|
|
264
|
-
);
|
|
265
|
-
const { createRuntimeOperationStore } = require(
|
|
266
|
-
"../rainbond-platform-installer/scripts/runtime-operations.js"
|
|
267
|
-
);
|
|
268
|
-
const { createEnvironmentCredentialStore } = require(
|
|
269
|
-
"../rainbond-platform-installer/scripts/environment-credentials.js"
|
|
270
|
-
);
|
|
271
|
-
let operations;
|
|
272
|
-
const registry = createEnvironmentRegistry({
|
|
273
|
-
activeOperationIds: (environmentId) => operations?.activeOperationIds(environmentId) || [],
|
|
274
|
-
});
|
|
275
|
-
operations = createRuntimeOperationStore({ registry });
|
|
276
|
-
return {
|
|
277
|
-
environmentCredentialStore: createEnvironmentCredentialStore(),
|
|
278
|
-
environmentRegistry: registry,
|
|
279
|
-
operationStore: operations,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function parseEnvironmentMutationArgs(args) {
|
|
284
|
-
const action = args[1];
|
|
285
|
-
if (!new Set(["rename", "set-default", "remove"]).has(action)) {
|
|
286
|
-
throw new Error("environment action 无效");
|
|
287
|
-
}
|
|
288
|
-
if (args[2] !== "--environment-id" || !UUID_PATTERN.test(args[3] || "")) {
|
|
289
|
-
throw new Error("环境 ID 无效");
|
|
290
|
-
}
|
|
291
|
-
if (action === "rename") {
|
|
292
|
-
if (args.length !== 6 || args[4] !== "--name" || !args[5] || args[5].startsWith("--")) {
|
|
293
|
-
throw new Error("environment rename 参数无效");
|
|
294
|
-
}
|
|
295
|
-
return { action, environmentId: args[3], name: args[5] };
|
|
296
|
-
}
|
|
297
|
-
if (args.length !== 4) throw new Error(`environment ${action} 参数无效`);
|
|
298
|
-
return { action, environmentId: args[3] };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function parseOperationBeginArgs(args) {
|
|
302
|
-
if (args[0] !== "operation" || args[1] !== "begin") {
|
|
303
|
-
throw new Error("operation begin 参数无效");
|
|
304
|
-
}
|
|
305
|
-
let operationId = "";
|
|
306
|
-
let environmentId = "";
|
|
307
|
-
let intentInput = "";
|
|
308
|
-
for (let index = 2; index < args.length; index += 1) {
|
|
309
|
-
const argument = args[index];
|
|
310
|
-
if (argument === "--operation-id") {
|
|
311
|
-
operationId = requireFixedValue(args, index, argument);
|
|
312
|
-
index += 1;
|
|
313
|
-
} else if (argument === "--environment-id") {
|
|
314
|
-
environmentId = requireFixedValue(args, index, argument);
|
|
315
|
-
index += 1;
|
|
316
|
-
} else if (argument === "--intent-json") {
|
|
317
|
-
intentInput = requireFixedValue(args, index, argument);
|
|
318
|
-
index += 1;
|
|
319
|
-
} else {
|
|
320
|
-
throw new Error("operation begin 包含未知参数");
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (!UUID_PATTERN.test(operationId)) throw new Error("operation ID 无效");
|
|
324
|
-
if (environmentId && !UUID_PATTERN.test(environmentId)) throw new Error("环境 ID 无效");
|
|
325
|
-
if (!intentInput || intentInput.length > 16384) throw new Error("operation begin 缺少 intent JSON");
|
|
326
|
-
let intent;
|
|
327
|
-
try {
|
|
328
|
-
intent = JSON.parse(intentInput);
|
|
329
|
-
} catch {
|
|
330
|
-
throw new Error("operation begin intent JSON 无效");
|
|
331
|
-
}
|
|
332
|
-
return { operationId, environmentId: environmentId || undefined, intent };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
167
|
function runAttached(executable, args, { env = process.env } = {}) {
|
|
336
168
|
return new Promise((resolve, reject) => {
|
|
337
169
|
const child = spawn(executable, args, { env, stdio: "inherit" });
|
|
@@ -362,16 +194,13 @@ async function defaultConnectionRunner(invocation, {
|
|
|
362
194
|
RAINSKILLS_RUNTIME_CONNECT_COMPLETION: "1",
|
|
363
195
|
RAINSKILLS_RUNTIME_OPERATION_ID: operationId,
|
|
364
196
|
};
|
|
365
|
-
if (options.intent?.type === "environment-add") {
|
|
366
|
-
childEnvironment.RAINSKILLS_RUNTIME_STATE_SCOPE = "operation";
|
|
367
|
-
}
|
|
368
197
|
const result = await runAttached(invocation.executable, invocation.args, {
|
|
369
198
|
env: runtimeChildEnvironment(process.env, childEnvironment, origin),
|
|
370
199
|
});
|
|
371
200
|
return { ...result, completesRuntimeState: true };
|
|
372
201
|
}
|
|
373
202
|
|
|
374
|
-
function defaultPrivateInstallerScheduler({ control,
|
|
203
|
+
function defaultPrivateInstallerScheduler({ control, operationId, target, privateLocation }) {
|
|
375
204
|
const {
|
|
376
205
|
createNextAction,
|
|
377
206
|
createOnboardingCheckpoint,
|
|
@@ -383,16 +212,13 @@ function defaultPrivateInstallerScheduler({ control, intent, operationId, target
|
|
|
383
212
|
packageVersion,
|
|
384
213
|
control,
|
|
385
214
|
operationId,
|
|
386
|
-
intent,
|
|
387
215
|
});
|
|
388
216
|
return createNextAction(operationId, privateLocation);
|
|
389
217
|
}
|
|
390
218
|
|
|
391
219
|
async function runBuiltin(args, {
|
|
392
220
|
runtimeStateManager,
|
|
393
|
-
|
|
394
|
-
environmentRegistry,
|
|
395
|
-
operationStore,
|
|
221
|
+
singleRuntimeStore,
|
|
396
222
|
write = (value) => process.stdout.write(value),
|
|
397
223
|
control = detectControlEnvironment(),
|
|
398
224
|
originInspector,
|
|
@@ -402,120 +228,16 @@ async function runBuiltin(args, {
|
|
|
402
228
|
credentialPersister,
|
|
403
229
|
connectedCredentialReader,
|
|
404
230
|
} = {}) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
};
|
|
410
|
-
const getEnvironmentRegistry = () => environmentRegistry
|
|
411
|
-
|| getEnvironmentServices().environmentRegistry;
|
|
412
|
-
const getOperationStore = () => operationStore || getEnvironmentServices().operationStore;
|
|
413
|
-
const getEnvironmentCredentialStore = () => environmentCredentialStore
|
|
414
|
-
|| getEnvironmentServices().environmentCredentialStore;
|
|
415
|
-
const getRuntimeStateManager = (operationId, {
|
|
416
|
-
scoped = false,
|
|
417
|
-
protectedOperation = null,
|
|
418
|
-
} = {}) => {
|
|
231
|
+
const getSingleRuntimeStore = () => singleRuntimeStore || require(
|
|
232
|
+
"../rainbond-platform-installer/scripts/single-runtime.js"
|
|
233
|
+
).createSingleRuntimeStore();
|
|
234
|
+
const getRuntimeStateManager = () => {
|
|
419
235
|
if (runtimeStateManager) return runtimeStateManager;
|
|
420
|
-
const operationScoped = scoped || (
|
|
421
|
-
credentialEnvironment.RAINSKILLS_RUNTIME_STATE_SCOPE === "operation"
|
|
422
|
-
&& credentialEnvironment.RAINSKILLS_RUNTIME_OPERATION_ID === operationId
|
|
423
|
-
);
|
|
424
|
-
const managerOptions = operationScoped ? { operationId } : {};
|
|
425
|
-
if (protectedOperation?.environment_id) {
|
|
426
|
-
const environment = getEnvironmentRegistry().get(protectedOperation.environment_id);
|
|
427
|
-
if (!environment || environment.connection_state !== "connected") {
|
|
428
|
-
throw new Error("runtime operation 绑定的运行环境当前不可用");
|
|
429
|
-
}
|
|
430
|
-
const credentialStore = getEnvironmentCredentialStore();
|
|
431
|
-
const credential = credentialStore.read({
|
|
432
|
-
environmentId: environment.id,
|
|
433
|
-
expectedOrigin: environment.console_origin,
|
|
434
|
-
});
|
|
435
|
-
managerOptions.env = {
|
|
436
|
-
RAINBOND_JWT: credential.token,
|
|
437
|
-
RAINBOND_URL: credential.origin,
|
|
438
|
-
};
|
|
439
|
-
managerOptions.credentialWriter = ({ token, baseUrl }) => {
|
|
440
|
-
if (baseUrl !== environment.console_origin) {
|
|
441
|
-
throw new Error("runtime renewed credential origin 不匹配");
|
|
442
|
-
}
|
|
443
|
-
credentialStore.write({
|
|
444
|
-
environmentId: environment.id,
|
|
445
|
-
origin: baseUrl,
|
|
446
|
-
token,
|
|
447
|
-
});
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
236
|
return require(
|
|
451
237
|
"../rainbond-platform-installer/scripts/runtime-state.js"
|
|
452
|
-
).createRuntimeStateManager(
|
|
238
|
+
).createRuntimeStateManager();
|
|
453
239
|
};
|
|
454
240
|
|
|
455
|
-
if (args[0] === "environment" && args[1] === "list") {
|
|
456
|
-
if (args.length !== 3 || args[2] !== "--json") {
|
|
457
|
-
throw new Error("environment list 只支持固定参数 --json");
|
|
458
|
-
}
|
|
459
|
-
const current = getEnvironmentRegistry().read();
|
|
460
|
-
write(`${JSON.stringify({
|
|
461
|
-
schema: "rainskills.environment-list.v1",
|
|
462
|
-
default_environment_id: current.default_environment_id,
|
|
463
|
-
environments: current.environments,
|
|
464
|
-
})}\n`);
|
|
465
|
-
return true;
|
|
466
|
-
}
|
|
467
|
-
if (args[0] === "environment" && ["rename", "set-default", "remove"].includes(args[1])) {
|
|
468
|
-
const input = parseEnvironmentMutationArgs(args);
|
|
469
|
-
const registry = getEnvironmentRegistry();
|
|
470
|
-
let environment;
|
|
471
|
-
let action;
|
|
472
|
-
if (input.action === "rename") {
|
|
473
|
-
environment = registry.rename(input.environmentId, input.name);
|
|
474
|
-
action = "renamed";
|
|
475
|
-
} else if (input.action === "set-default") {
|
|
476
|
-
environment = registry.setDefault(input.environmentId);
|
|
477
|
-
action = "default-changed";
|
|
478
|
-
} else {
|
|
479
|
-
environment = registry.remove(input.environmentId);
|
|
480
|
-
getEnvironmentCredentialStore().remove(input.environmentId);
|
|
481
|
-
action = "removed";
|
|
482
|
-
}
|
|
483
|
-
write(`${JSON.stringify({
|
|
484
|
-
schema: "rainskills.environment-result.v1",
|
|
485
|
-
action,
|
|
486
|
-
environment,
|
|
487
|
-
})}\n`);
|
|
488
|
-
return true;
|
|
489
|
-
}
|
|
490
|
-
if (args[0] === "operation" && args[1] === "begin") {
|
|
491
|
-
const input = parseOperationBeginArgs(args);
|
|
492
|
-
const operation = getOperationStore().begin(input);
|
|
493
|
-
write(`${JSON.stringify({
|
|
494
|
-
schema: "rainskills.operation-begin-result.v1",
|
|
495
|
-
operation_id: operation.operation_id,
|
|
496
|
-
environment_id: operation.environment_id,
|
|
497
|
-
intent: operation.intent,
|
|
498
|
-
stage: operation.stage,
|
|
499
|
-
})}\n`);
|
|
500
|
-
return true;
|
|
501
|
-
}
|
|
502
|
-
if (args[0] === "operation" && args[1] === "complete") {
|
|
503
|
-
if (
|
|
504
|
-
args.length !== 4
|
|
505
|
-
|| args[2] !== "--operation-id"
|
|
506
|
-
|| !UUID_PATTERN.test(args[3] || "")
|
|
507
|
-
) {
|
|
508
|
-
throw new Error("operation complete 参数无效");
|
|
509
|
-
}
|
|
510
|
-
const operation = getOperationStore().complete(args[3]);
|
|
511
|
-
write(`${JSON.stringify({
|
|
512
|
-
schema: "rainskills.operation-complete-result.v1",
|
|
513
|
-
operation_id: operation.operation_id,
|
|
514
|
-
environment_id: operation.environment_id,
|
|
515
|
-
stage: operation.stage,
|
|
516
|
-
})}\n`);
|
|
517
|
-
return true;
|
|
518
|
-
}
|
|
519
241
|
if (args[0] === "runtime" && args[1] === "message") {
|
|
520
242
|
if (args.length !== 4 || args[2] !== "--id") {
|
|
521
243
|
throw new Error("runtime message 只支持固定参数 --id");
|
|
@@ -529,165 +251,104 @@ async function runBuiltin(args, {
|
|
|
529
251
|
if (args.length !== 3 || args[2] !== "--json") {
|
|
530
252
|
throw new Error("runtime status 只支持固定参数 --json");
|
|
531
253
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|| !runtimeStateManager
|
|
559
|
-
);
|
|
560
|
-
let reconnectedEnvironment = null;
|
|
561
|
-
const manager = runtimeStateManager || require(
|
|
562
|
-
"../rainbond-platform-installer/scripts/runtime-state.js"
|
|
563
|
-
).createRuntimeStateManager();
|
|
564
|
-
const reconnectResult = await manager.withReconnectLease(operationId, async (connection) => {
|
|
565
|
-
try {
|
|
566
|
-
const inspect = originInspector || require(
|
|
567
|
-
"../rainbond-platform-installer/scripts/console-origin.js"
|
|
568
|
-
).inspectConsoleOrigin;
|
|
569
|
-
const inspection = await inspect(connection.console_origin);
|
|
570
|
-
if (inspection.pendingRedirectOrigin || inspection.origin !== connection.console_origin) {
|
|
571
|
-
throw new Error("protected Console origin 在重连检查中发生变化");
|
|
572
|
-
}
|
|
573
|
-
const options = {
|
|
574
|
-
targetClient: connection.target_client,
|
|
575
|
-
environmentChoice: connection.environment_kind === "saas" ? "saas" : "private-existing",
|
|
576
|
-
rainbondUrl: connection.console_origin,
|
|
577
|
-
allowInsecureHttp: connection.console_origin.startsWith("http://"),
|
|
578
|
-
onboardingId: operationId,
|
|
579
|
-
intent: connection.intent,
|
|
580
|
-
};
|
|
581
|
-
const invocation = runtimeConnectionInvocation(options, connection.console_origin);
|
|
582
|
-
let completedWithCredential = false;
|
|
583
|
-
const completeWithCredential = async (credential) => {
|
|
584
|
-
const priorToken = process.env.RAINBOND_JWT;
|
|
585
|
-
try {
|
|
586
|
-
process.env.RAINBOND_JWT = credential;
|
|
587
|
-
await manager.markConnected(connection);
|
|
588
|
-
if (multiEnvironmentEnabled) {
|
|
589
|
-
const protectedOperation = getOperationStore().read(operationId);
|
|
590
|
-
if (!protectedOperation?.environment_id) {
|
|
591
|
-
throw new Error("runtime reconnect 缺少已锁定的运行环境");
|
|
592
|
-
}
|
|
593
|
-
const registration = require(
|
|
594
|
-
"../rainbond-platform-installer/scripts/environment-credentials.js"
|
|
595
|
-
).registerConnectedEnvironment({
|
|
596
|
-
registry: getEnvironmentRegistry(),
|
|
597
|
-
credentialStore: getEnvironmentCredentialStore(),
|
|
598
|
-
origin: connection.console_origin,
|
|
599
|
-
token: credential,
|
|
600
|
-
kind: connection.environment_kind,
|
|
601
|
-
});
|
|
602
|
-
if (registration.environment.id !== protectedOperation.environment_id) {
|
|
603
|
-
throw new Error("runtime reconnect 不能切换已锁定的运行环境");
|
|
604
|
-
}
|
|
605
|
-
reconnectedEnvironment = registration.environment;
|
|
606
|
-
}
|
|
607
|
-
completedWithCredential = true;
|
|
608
|
-
} finally {
|
|
609
|
-
if (priorToken === undefined) delete process.env.RAINBOND_JWT;
|
|
610
|
-
else process.env.RAINBOND_JWT = priorToken;
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
const result = await connectionRunner(invocation, {
|
|
614
|
-
completeWithCredential,
|
|
615
|
-
control,
|
|
616
|
-
options,
|
|
617
|
-
origin: connection.console_origin,
|
|
618
|
-
operationId,
|
|
254
|
+
if (runtimeStateManager) {
|
|
255
|
+
write(`${JSON.stringify(await runtimeStateManager.status())}\n`);
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
const runtime = getSingleRuntimeStore().read();
|
|
259
|
+
if (!runtime) {
|
|
260
|
+
write(`${JSON.stringify({
|
|
261
|
+
schema: "rainskills.runtime-status.v1",
|
|
262
|
+
state: "not_started",
|
|
263
|
+
usable: false,
|
|
264
|
+
})}\n`);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const validation = await require(
|
|
269
|
+
"../rainbond-platform-installer/scripts/windows-client-config.js"
|
|
270
|
+
).validateMcp({
|
|
271
|
+
url: `${runtime.console_origin}/console/mcp/rainskills/api/query`,
|
|
272
|
+
token: runtime.token,
|
|
273
|
+
});
|
|
274
|
+
if (validation.token !== runtime.token) {
|
|
275
|
+
getSingleRuntimeStore().write({
|
|
276
|
+
consoleOrigin: runtime.console_origin,
|
|
277
|
+
kind: runtime.kind,
|
|
278
|
+
token: validation.token,
|
|
279
|
+
allowInsecureHttp: runtime.allow_insecure_http,
|
|
619
280
|
});
|
|
620
|
-
if (result.signal || result.code !== 0) {
|
|
621
|
-
throw new Error("RainSkills 运行环境重新授权未完成");
|
|
622
|
-
}
|
|
623
|
-
if (result.completesRuntimeState) {
|
|
624
|
-
assertExactConnectedState(manager.read(), connection);
|
|
625
|
-
} else {
|
|
626
|
-
if (completedWithCredential) throw new Error("运行环境重连器返回了矛盾状态");
|
|
627
|
-
await manager.markConnected(connection);
|
|
628
|
-
assertExactConnectedState(manager.read(), connection);
|
|
629
|
-
}
|
|
630
|
-
return {
|
|
631
|
-
environmentKind: connection.environment_kind,
|
|
632
|
-
consoleOrigin: connection.console_origin,
|
|
633
|
-
};
|
|
634
|
-
} catch {
|
|
635
|
-
write(`${JSON.stringify(runtimeReconnectRetryAction(operationId))}\n`);
|
|
636
|
-
throw new Error("RainSkills 运行环境重新授权失败");
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
if (multiEnvironmentEnabled) {
|
|
640
|
-
const operations = getOperationStore();
|
|
641
|
-
const protectedOperation = operations.read(operationId);
|
|
642
|
-
if (!protectedOperation || !protectedOperation.environment_id) {
|
|
643
|
-
throw new Error("runtime reconnect 缺少已锁定的运行环境");
|
|
644
|
-
}
|
|
645
|
-
if (!reconnectedEnvironment) {
|
|
646
|
-
if (connectedCredentialReader) {
|
|
647
|
-
const credential = await connectedCredentialReader(reconnectResult.consoleOrigin);
|
|
648
|
-
reconnectedEnvironment = require(
|
|
649
|
-
"../rainbond-platform-installer/scripts/environment-credentials.js"
|
|
650
|
-
).registerConnectedEnvironment({
|
|
651
|
-
registry: getEnvironmentRegistry(),
|
|
652
|
-
credentialStore: getEnvironmentCredentialStore(),
|
|
653
|
-
origin: reconnectResult.consoleOrigin,
|
|
654
|
-
token: credential.token,
|
|
655
|
-
kind: reconnectResult.environmentKind,
|
|
656
|
-
}).environment;
|
|
657
|
-
} else {
|
|
658
|
-
const existing = getEnvironmentRegistry().findByOrigin(reconnectResult.consoleOrigin);
|
|
659
|
-
if (!existing || !getEnvironmentCredentialStore().has(existing.id)) {
|
|
660
|
-
throw new Error("runtime reconnect 未写入目标环境凭据");
|
|
661
|
-
}
|
|
662
|
-
getEnvironmentCredentialStore().read({
|
|
663
|
-
environmentId: existing.id,
|
|
664
|
-
expectedOrigin: reconnectResult.consoleOrigin,
|
|
665
|
-
});
|
|
666
|
-
reconnectedEnvironment = existing;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
if (reconnectedEnvironment.id !== protectedOperation.environment_id) {
|
|
670
|
-
throw new Error("runtime reconnect 不能切换已锁定的运行环境");
|
|
671
281
|
}
|
|
282
|
+
write(`${JSON.stringify({
|
|
283
|
+
schema: "rainskills.runtime-status.v1",
|
|
284
|
+
state: "connected",
|
|
285
|
+
usable: true,
|
|
286
|
+
console_origin: runtime.console_origin,
|
|
287
|
+
environment_kind: runtime.kind,
|
|
288
|
+
})}\n`);
|
|
289
|
+
} catch {
|
|
290
|
+
write(`${JSON.stringify({
|
|
291
|
+
schema: "rainskills.runtime-status.v1",
|
|
292
|
+
state: "needs_reconnect",
|
|
293
|
+
usable: false,
|
|
294
|
+
console_origin: runtime.console_origin,
|
|
295
|
+
environment_kind: runtime.kind,
|
|
296
|
+
})}\n`);
|
|
672
297
|
}
|
|
673
|
-
write(`${JSON.stringify({
|
|
674
|
-
schema: "rainskills.runtime-reconnect-result.v1",
|
|
675
|
-
state: "connected",
|
|
676
|
-
onboarding_id: operationId,
|
|
677
|
-
environment_kind: reconnectResult.environmentKind,
|
|
678
|
-
...(reconnectedEnvironment ? {
|
|
679
|
-
environment_id: reconnectedEnvironment.id,
|
|
680
|
-
environment_name: reconnectedEnvironment.name,
|
|
681
|
-
} : {}),
|
|
682
|
-
})}\n`);
|
|
683
298
|
return true;
|
|
684
299
|
}
|
|
300
|
+
if (args[0] === "runtime" && args[1] === "reconnect") {
|
|
301
|
+
if (args.length !== 3 || !["codex", "claude", "pi", "all"].includes(args[2])) {
|
|
302
|
+
throw new Error("runtime reconnect 参数无效");
|
|
303
|
+
}
|
|
304
|
+
const current = getSingleRuntimeStore().read();
|
|
305
|
+
if (!current) throw new Error("目前还没有已连接的 Rainbond 运行环境");
|
|
306
|
+
const reconnectArgs = ["runtime", "connect", args[2]];
|
|
307
|
+
if (current.kind === "saas") reconnectArgs.push("--saas");
|
|
308
|
+
else reconnectArgs.push("--rainbond-url", current.console_origin);
|
|
309
|
+
if (current.allow_insecure_http) reconnectArgs.push("--allow-insecure-http");
|
|
310
|
+
return runBuiltin(reconnectArgs, {
|
|
311
|
+
runtimeStateManager,
|
|
312
|
+
singleRuntimeStore: getSingleRuntimeStore(),
|
|
313
|
+
write,
|
|
314
|
+
control,
|
|
315
|
+
originInspector,
|
|
316
|
+
connectionRunner,
|
|
317
|
+
privateInstallerScheduler,
|
|
318
|
+
credentialEnvironment,
|
|
319
|
+
credentialPersister,
|
|
320
|
+
connectedCredentialReader,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
685
323
|
if (args[0] === "runtime" && args[1] === "assert-connect") {
|
|
686
324
|
const expected = parseRuntimeAssertConnectArgs(args);
|
|
687
325
|
const manager = getRuntimeStateManager(expected.operationId);
|
|
688
326
|
assertConnectingState(manager.read(), expected);
|
|
689
327
|
return true;
|
|
690
328
|
}
|
|
329
|
+
if (args[0] === "runtime" && args[1] === "store-credential") {
|
|
330
|
+
if (
|
|
331
|
+
args.length < 6
|
|
332
|
+
|| args[2] !== "--console-origin"
|
|
333
|
+
|| args[4] !== "--kind"
|
|
334
|
+
|| !["saas", "private"].includes(args[5])
|
|
335
|
+
|| (args.length === 7 && args[6] !== "--allow-insecure-http")
|
|
336
|
+
|| args.length > 7
|
|
337
|
+
) {
|
|
338
|
+
throw new Error("runtime store-credential 参数无效");
|
|
339
|
+
}
|
|
340
|
+
const token = credentialEnvironment.RAINBOND_JWT;
|
|
341
|
+
if (typeof token !== "string" || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) {
|
|
342
|
+
throw new Error("runtime store-credential 缺少有效凭据");
|
|
343
|
+
}
|
|
344
|
+
getSingleRuntimeStore().write({
|
|
345
|
+
consoleOrigin: args[3],
|
|
346
|
+
kind: args[5],
|
|
347
|
+
token,
|
|
348
|
+
allowInsecureHttp: args[6] === "--allow-insecure-http",
|
|
349
|
+
});
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
691
352
|
if (args[0] === "runtime" && args[1] === "persist-connect-credential") {
|
|
692
353
|
if (
|
|
693
354
|
args.length !== 4
|
|
@@ -707,63 +368,22 @@ async function runBuiltin(args, {
|
|
|
707
368
|
}
|
|
708
369
|
if (credentialPersister) {
|
|
709
370
|
await credentialPersister({ token, baseUrl: current.console_origin });
|
|
710
|
-
} else
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|| environmentCredentialStore
|
|
714
|
-
|| !runtimeStateManager
|
|
715
|
-
) {
|
|
716
|
-
const registration = require(
|
|
717
|
-
"../rainbond-platform-installer/scripts/environment-credentials.js"
|
|
718
|
-
).registerConnectedEnvironment({
|
|
719
|
-
registry: getEnvironmentRegistry(),
|
|
720
|
-
credentialStore: getEnvironmentCredentialStore(),
|
|
721
|
-
origin: current.console_origin,
|
|
722
|
-
token,
|
|
371
|
+
} else {
|
|
372
|
+
getSingleRuntimeStore().write({
|
|
373
|
+
consoleOrigin: current.console_origin,
|
|
723
374
|
kind: current.environment_kind,
|
|
375
|
+
token,
|
|
376
|
+
allowInsecureHttp: current.console_origin.startsWith("http://"),
|
|
724
377
|
});
|
|
725
|
-
const operations = getOperationStore();
|
|
726
|
-
const operation = operations.read(args[3]);
|
|
727
|
-
if (!operation) throw new Error("runtime connect operation 不存在");
|
|
728
|
-
if (operation.environment_id === null) {
|
|
729
|
-
operations.bindEnvironment(args[3], registration.environment.id);
|
|
730
|
-
} else if (operation.environment_id !== registration.environment.id) {
|
|
731
|
-
throw new Error("runtime connect 不能改变已锁定的运行环境");
|
|
732
|
-
}
|
|
733
|
-
} else {
|
|
734
|
-
if (typeof manager.persistConnectingCredential !== "function") {
|
|
735
|
-
throw new Error("runtime connect credential writer 不可用");
|
|
736
|
-
}
|
|
737
|
-
await manager.persistConnectingCredential({ operationId: args[3], token });
|
|
738
378
|
}
|
|
739
379
|
return true;
|
|
740
380
|
}
|
|
741
381
|
if (args[0] === "runtime" && args[1] === "connect") {
|
|
742
382
|
const options = parseRuntimeConnectArgs(args);
|
|
743
|
-
const operationId =
|
|
744
|
-
const multiEnvironmentEnabled = Boolean(
|
|
745
|
-
environmentRegistry
|
|
746
|
-
|| operationStore
|
|
747
|
-
|| environmentCredentialStore
|
|
748
|
-
|| !runtimeStateManager
|
|
749
|
-
);
|
|
750
|
-
let operations;
|
|
751
|
-
if (multiEnvironmentEnabled) {
|
|
752
|
-
operations = getOperationStore();
|
|
753
|
-
const existingOperation = operations.read(operationId);
|
|
754
|
-
if (!existingOperation) {
|
|
755
|
-
operations.createPending({ operationId, intent: options.intent });
|
|
756
|
-
} else if (
|
|
757
|
-
existingOperation.stage !== "awaiting-environment"
|
|
758
|
-
|| !isDeepStrictEqual(existingOperation.intent, options.intent)
|
|
759
|
-
) {
|
|
760
|
-
throw new Error("runtime connect operation 与受保护的原始 intent 不匹配");
|
|
761
|
-
}
|
|
762
|
-
}
|
|
383
|
+
const operationId = crypto.randomUUID();
|
|
763
384
|
if (options.environmentChoice === "install-private") {
|
|
764
385
|
const nextAction = privateInstallerScheduler({
|
|
765
386
|
control,
|
|
766
|
-
intent: options.intent,
|
|
767
387
|
operationId,
|
|
768
388
|
target: options.targetClient,
|
|
769
389
|
privateLocation: options.privateLocation,
|
|
@@ -785,17 +405,14 @@ async function runBuiltin(args, {
|
|
|
785
405
|
if (inspection.httpConfirmationRequired && !options.allowInsecureHttp) {
|
|
786
406
|
throw new Error("明文 HTTP 需要单独显式确认;确认可信内网后使用 --allow-insecure-http");
|
|
787
407
|
}
|
|
788
|
-
const manager = getRuntimeStateManager(operationId
|
|
789
|
-
scoped: options.intent.type === "environment-add",
|
|
790
|
-
});
|
|
408
|
+
const manager = getRuntimeStateManager(operationId);
|
|
791
409
|
const connection = {
|
|
792
410
|
target_client: options.targetClient,
|
|
793
411
|
environment_kind: options.environmentChoice === "saas" ? "saas" : "private",
|
|
794
412
|
console_origin: inspection.origin,
|
|
795
|
-
intent:
|
|
413
|
+
intent: null,
|
|
796
414
|
operation_id: operationId,
|
|
797
415
|
};
|
|
798
|
-
let registeredEnvironment = null;
|
|
799
416
|
manager.startConnecting(connection);
|
|
800
417
|
try {
|
|
801
418
|
const invocation = runtimeConnectionInvocation(options, inspection.origin);
|
|
@@ -805,23 +422,12 @@ async function runBuiltin(args, {
|
|
|
805
422
|
try {
|
|
806
423
|
process.env.RAINBOND_JWT = credential;
|
|
807
424
|
await manager.markConnected(connection);
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
origin: inspection.origin,
|
|
815
|
-
token: credential,
|
|
816
|
-
kind: connection.environment_kind,
|
|
817
|
-
}).environment;
|
|
818
|
-
const pending = operations.read(operationId);
|
|
819
|
-
if (pending.environment_id === null) {
|
|
820
|
-
operations.bindEnvironment(operationId, registeredEnvironment.id);
|
|
821
|
-
} else if (pending.environment_id !== registeredEnvironment.id) {
|
|
822
|
-
throw new Error("runtime connect 不能改变已锁定的运行环境");
|
|
823
|
-
}
|
|
824
|
-
}
|
|
425
|
+
getSingleRuntimeStore().write({
|
|
426
|
+
consoleOrigin: inspection.origin,
|
|
427
|
+
kind: connection.environment_kind,
|
|
428
|
+
token: credential,
|
|
429
|
+
allowInsecureHttp: inspection.origin.startsWith("http://"),
|
|
430
|
+
});
|
|
825
431
|
completedWithCredential = true;
|
|
826
432
|
} finally {
|
|
827
433
|
if (priorToken === undefined) delete process.env.RAINBOND_JWT;
|
|
@@ -848,67 +454,34 @@ async function runBuiltin(args, {
|
|
|
848
454
|
await manager.markConnected(connection);
|
|
849
455
|
}
|
|
850
456
|
} catch (error) {
|
|
851
|
-
write(`${JSON.stringify(runtimeConnectRetryAction(options, inspection.origin
|
|
457
|
+
write(`${JSON.stringify(runtimeConnectRetryAction(options, inspection.origin))}\n`);
|
|
852
458
|
throw error;
|
|
853
459
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
if (!credential || credential.origin !== inspection.origin) {
|
|
859
|
-
throw new Error("运行环境凭据与已验证 Console origin 不匹配");
|
|
860
|
-
}
|
|
861
|
-
registeredEnvironment = require(
|
|
862
|
-
"../rainbond-platform-installer/scripts/environment-credentials.js"
|
|
863
|
-
).registerConnectedEnvironment({
|
|
864
|
-
registry: getEnvironmentRegistry(),
|
|
865
|
-
credentialStore: getEnvironmentCredentialStore(),
|
|
866
|
-
origin: inspection.origin,
|
|
867
|
-
token: credential.token,
|
|
868
|
-
kind: connection.environment_kind,
|
|
869
|
-
}).environment;
|
|
870
|
-
} else {
|
|
871
|
-
const existing = getEnvironmentRegistry().findByOrigin(inspection.origin);
|
|
872
|
-
if (!existing || !getEnvironmentCredentialStore().has(existing.id)) {
|
|
873
|
-
throw new Error("runtime connect 未写入目标环境凭据");
|
|
874
|
-
}
|
|
875
|
-
getEnvironmentCredentialStore().read({
|
|
876
|
-
environmentId: existing.id,
|
|
877
|
-
expectedOrigin: inspection.origin,
|
|
878
|
-
});
|
|
879
|
-
registeredEnvironment = existing;
|
|
880
|
-
}
|
|
460
|
+
let storedRuntime = getSingleRuntimeStore().read();
|
|
461
|
+
if (!storedRuntime || storedRuntime.console_origin !== inspection.origin) {
|
|
462
|
+
if (!connectedCredentialReader) {
|
|
463
|
+
throw new Error("runtime connect 未写入唯一运行环境凭据");
|
|
881
464
|
}
|
|
882
|
-
const
|
|
883
|
-
if (
|
|
884
|
-
|
|
885
|
-
} else if (protectedOperation.environment_id !== registeredEnvironment.id) {
|
|
886
|
-
throw new Error("runtime connect 不能改变已锁定的运行环境");
|
|
465
|
+
const credential = await connectedCredentialReader(inspection.origin);
|
|
466
|
+
if (!credential || credential.origin !== inspection.origin) {
|
|
467
|
+
throw new Error("运行环境凭据与已验证 Console origin 不匹配");
|
|
887
468
|
}
|
|
469
|
+
getSingleRuntimeStore().write({
|
|
470
|
+
consoleOrigin: inspection.origin,
|
|
471
|
+
kind: connection.environment_kind,
|
|
472
|
+
token: credential.token,
|
|
473
|
+
allowInsecureHttp: inspection.origin.startsWith("http://"),
|
|
474
|
+
});
|
|
475
|
+
storedRuntime = getSingleRuntimeStore().read();
|
|
476
|
+
}
|
|
477
|
+
if (!storedRuntime || storedRuntime.console_origin !== inspection.origin) {
|
|
478
|
+
throw new Error("唯一运行环境凭据与已连接 Console origin 不匹配");
|
|
888
479
|
}
|
|
889
|
-
const environmentSnapshot = registeredEnvironment && options.intent.type === "environment-add"
|
|
890
|
-
? getEnvironmentRegistry().read()
|
|
891
|
-
: null;
|
|
892
480
|
write(`${JSON.stringify({
|
|
893
481
|
schema: "rainskills.runtime-connect-result.v1",
|
|
894
482
|
state: "connected",
|
|
895
|
-
|
|
483
|
+
console_origin: inspection.origin,
|
|
896
484
|
environment_kind: connection.environment_kind,
|
|
897
|
-
...(registeredEnvironment ? {
|
|
898
|
-
environment_id: registeredEnvironment.id,
|
|
899
|
-
environment_name: registeredEnvironment.name,
|
|
900
|
-
} : {}),
|
|
901
|
-
...(environmentSnapshot ? {
|
|
902
|
-
default_environment_id: environmentSnapshot.default_environment_id,
|
|
903
|
-
environments: environmentSnapshot.environments,
|
|
904
|
-
user_message: require(
|
|
905
|
-
"../rainbond-platform-installer/scripts/user-message.js"
|
|
906
|
-
).renderEnvironmentConnectedList({
|
|
907
|
-
environments: environmentSnapshot.environments,
|
|
908
|
-
defaultEnvironmentId: environmentSnapshot.default_environment_id,
|
|
909
|
-
addedEnvironmentId: registeredEnvironment.id,
|
|
910
|
-
}),
|
|
911
|
-
} : {}),
|
|
912
485
|
})}\n`);
|
|
913
486
|
return true;
|
|
914
487
|
}
|
|
@@ -930,33 +503,6 @@ async function runBuiltin(args, {
|
|
|
930
503
|
});
|
|
931
504
|
return true;
|
|
932
505
|
}
|
|
933
|
-
if (args[0] === "intent" && args[1] === "resume") {
|
|
934
|
-
if (args.length !== 4 || args[2] !== "--onboarding-id" || !args[3]) {
|
|
935
|
-
throw new Error("intent resume 需要固定参数 --onboarding-id <uuid>");
|
|
936
|
-
}
|
|
937
|
-
const protectedOperation = runtimeStateManager ? null : getOperationStore().read(args[3]);
|
|
938
|
-
const manager = getRuntimeStateManager(args[3], {
|
|
939
|
-
scoped: protectedOperation?.intent?.type === "environment-add",
|
|
940
|
-
protectedOperation,
|
|
941
|
-
});
|
|
942
|
-
const eligible = manager.assertContinuationEligible(args[3]);
|
|
943
|
-
if (protectedOperation?.environment_id) {
|
|
944
|
-
const environment = getEnvironmentRegistry().get(protectedOperation.environment_id);
|
|
945
|
-
if (!environment || eligible.console_origin !== environment.console_origin) {
|
|
946
|
-
throw new Error("runtime operation 与已锁定运行环境不匹配");
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
const status = await manager.status();
|
|
950
|
-
if (status.state !== "connected" || status.usable !== true) {
|
|
951
|
-
throw new Error("runtime 尚未 connected 且通过 live probe,不能恢复 intent");
|
|
952
|
-
}
|
|
953
|
-
const runtime = manager.prepareContinuation(args[3]);
|
|
954
|
-
const { createIntentContinuation } = require(
|
|
955
|
-
"../rainbond-platform-installer/scripts/runtime-intents.js"
|
|
956
|
-
);
|
|
957
|
-
write(`${JSON.stringify(createIntentContinuation(runtime.intent, runtime.failed_step || undefined))}\n`);
|
|
958
|
-
return true;
|
|
959
|
-
}
|
|
960
506
|
return false;
|
|
961
507
|
}
|
|
962
508
|
|
|
@@ -1220,8 +766,6 @@ async function run() {
|
|
|
1220
766
|
module.exports = {
|
|
1221
767
|
AUTO_UPDATE_FALLBACK_EXIT_CODE,
|
|
1222
768
|
classifyNodeMajor,
|
|
1223
|
-
parseRuntimeFailureArgs,
|
|
1224
|
-
parseRuntimeReconnectArgs,
|
|
1225
769
|
parseRuntimeConnectArgs,
|
|
1226
770
|
resolveInvocation,
|
|
1227
771
|
runAutoUpdatePhase,
|
|
@@ -1229,7 +773,6 @@ module.exports = {
|
|
|
1229
773
|
runtimeChildEnvironment,
|
|
1230
774
|
runtimeConnectRetryAction,
|
|
1231
775
|
runtimeConnectionInvocation,
|
|
1232
|
-
runtimeReconnectRetryAction,
|
|
1233
776
|
};
|
|
1234
777
|
|
|
1235
778
|
if (require.main === module) {
|