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
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const path = require("node:path");
|
|
4
|
-
|
|
5
|
-
const CONTROL_PATTERN = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
6
|
-
const CREDENTIAL_KEY_PATTERN = /(?:^|_)(?:auth|authorization|cookie|credential|jwt|password|secret|token)(?:_|$)|(?:api|private)[_-]?key/i;
|
|
7
|
-
const IDENTIFIER_FIELDS = new Set(["enterprise_id", "team_id", "app_id", "service_id", "template_id", "snapshot_id", "market_id"]);
|
|
8
|
-
const MAX_IDENTIFIER_LENGTH = 128;
|
|
9
|
-
const MAX_PATH_LENGTH = 2048;
|
|
10
|
-
const MAX_VERSION_LENGTH = 128;
|
|
11
|
-
const MAX_IMAGE_REFERENCE_LENGTH = 512;
|
|
12
|
-
const MAX_IMAGE_REFERENCES = 64;
|
|
13
|
-
|
|
14
|
-
function deepFreeze(value) {
|
|
15
|
-
if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
16
|
-
for (const child of Object.values(value)) deepFreeze(child);
|
|
17
|
-
return Object.freeze(value);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const INTENT_DEFINITIONS = deepFreeze({
|
|
21
|
-
"environment-add": {
|
|
22
|
-
skillId: "rainskills",
|
|
23
|
-
required: [],
|
|
24
|
-
optional: [],
|
|
25
|
-
enums: {},
|
|
26
|
-
steps: ["connect"],
|
|
27
|
-
},
|
|
28
|
-
deploy: {
|
|
29
|
-
skillId: "rainbond-app-assistant",
|
|
30
|
-
required: [],
|
|
31
|
-
optional: ["project_root", "source_kind", "source_url", "image_ref", "service_id"],
|
|
32
|
-
enums: { source_kind: ["local", "git", "image", "package"] },
|
|
33
|
-
steps: ["project-analysis", "topology", "build", "runtime", "access-verification"],
|
|
34
|
-
},
|
|
35
|
-
create: {
|
|
36
|
-
skillId: "rainbond-app-assistant",
|
|
37
|
-
required: [],
|
|
38
|
-
optional: ["project_root", "source_kind", "source_url", "image_ref", "service_id"],
|
|
39
|
-
enums: { source_kind: ["local", "git", "image", "package"] },
|
|
40
|
-
steps: ["project-analysis", "topology", "build", "runtime", "access-verification"],
|
|
41
|
-
},
|
|
42
|
-
"template-install": {
|
|
43
|
-
skillId: "rainbond-template-installer",
|
|
44
|
-
required: ["template_id", "install_scope"],
|
|
45
|
-
optional: ["team_id", "app_id"],
|
|
46
|
-
enums: { install_scope: ["new-app", "existing-app"] },
|
|
47
|
-
steps: ["lookup", "install", "verify"],
|
|
48
|
-
},
|
|
49
|
-
query: {
|
|
50
|
-
skillId: "rainbond-app-assistant",
|
|
51
|
-
required: ["operation"],
|
|
52
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
53
|
-
enums: { operation: ["summary", "components", "events", "logs", "access"] },
|
|
54
|
-
steps: ["resolve-target", "read"],
|
|
55
|
-
},
|
|
56
|
-
"platform-query": {
|
|
57
|
-
skillId: "rainbond-platform-query",
|
|
58
|
-
required: ["resource"],
|
|
59
|
-
optional: ["enterprise_id", "team_id", "app_id"],
|
|
60
|
-
enums: { resource: ["current-user", "current-enterprise", "teams", "regions", "apps", "team-apps", "components"] },
|
|
61
|
-
steps: ["resolve-context", "read"],
|
|
62
|
-
},
|
|
63
|
-
troubleshoot: {
|
|
64
|
-
skillId: "rainbond-app-assistant",
|
|
65
|
-
required: ["operation"],
|
|
66
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
67
|
-
enums: { operation: ["auto", "build", "runtime", "access"] },
|
|
68
|
-
steps: ["resolve-target", "diagnose", "repair", "verify"],
|
|
69
|
-
},
|
|
70
|
-
modify: {
|
|
71
|
-
skillId: "rainbond-app-assistant",
|
|
72
|
-
required: ["team_id", "app_id", "operation"],
|
|
73
|
-
optional: ["service_id"],
|
|
74
|
-
enums: { operation: ["component-config", "build-source", "ports", "env", "storage", "dependency"] },
|
|
75
|
-
steps: ["resolve-target", "apply", "verify"],
|
|
76
|
-
},
|
|
77
|
-
"delivery-verify": {
|
|
78
|
-
skillId: "rainbond-delivery-verifier",
|
|
79
|
-
required: ["operation"],
|
|
80
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
81
|
-
enums: { operation: ["full", "runtime", "access"] },
|
|
82
|
-
steps: ["resolve-target", "runtime", "access"],
|
|
83
|
-
},
|
|
84
|
-
snapshot: {
|
|
85
|
-
skillId: "rainbond-app-version-assistant",
|
|
86
|
-
required: ["team_id", "app_id", "operation"],
|
|
87
|
-
optional: ["snapshot_id"],
|
|
88
|
-
enums: { operation: ["create", "inspect"] },
|
|
89
|
-
steps: ["resolve-target", "prepare", "apply", "verify"],
|
|
90
|
-
},
|
|
91
|
-
publish: {
|
|
92
|
-
skillId: "rainbond-app-version-assistant",
|
|
93
|
-
required: ["team_id", "app_id", "destination"],
|
|
94
|
-
optional: ["snapshot_id", "market_id", "version"],
|
|
95
|
-
enums: { destination: ["local-library", "cloud-market"] },
|
|
96
|
-
steps: ["resolve-target", "prepare", "apply", "verify"],
|
|
97
|
-
},
|
|
98
|
-
rollback: {
|
|
99
|
-
skillId: "rainbond-app-version-assistant",
|
|
100
|
-
required: ["team_id", "app_id", "snapshot_id", "operation"],
|
|
101
|
-
optional: [],
|
|
102
|
-
enums: { operation: ["preview", "apply"] },
|
|
103
|
-
steps: ["resolve-target", "prepare", "apply", "verify"],
|
|
104
|
-
},
|
|
105
|
-
"env-sync": {
|
|
106
|
-
skillId: "rainbond-env-sync",
|
|
107
|
-
required: ["project_root", "environment"],
|
|
108
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
109
|
-
enums: { environment: ["preview", "production"] },
|
|
110
|
-
steps: ["resolve-target", "sync", "verify"],
|
|
111
|
-
},
|
|
112
|
-
"project-init": {
|
|
113
|
-
skillId: "rainbond-project-init",
|
|
114
|
-
required: ["project_root"],
|
|
115
|
-
optional: ["source_kind", "source_url", "image_ref"],
|
|
116
|
-
enums: { source_kind: ["local", "git", "image", "package"] },
|
|
117
|
-
steps: ["project-analysis", "manifest", "link"],
|
|
118
|
-
},
|
|
119
|
-
bootstrap: {
|
|
120
|
-
skillId: "rainbond-fullstack-bootstrap",
|
|
121
|
-
required: ["project_root"],
|
|
122
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
123
|
-
enums: {},
|
|
124
|
-
steps: ["resolve-target", "topology", "build"],
|
|
125
|
-
},
|
|
126
|
-
"troubleshoot-phase": {
|
|
127
|
-
skillId: "rainbond-fullstack-troubleshooter",
|
|
128
|
-
required: ["operation"],
|
|
129
|
-
optional: ["team_id", "app_id", "service_id"],
|
|
130
|
-
enums: { operation: ["auto", "build", "runtime", "access"] },
|
|
131
|
-
steps: ["resolve-target", "diagnose", "repair", "verify"],
|
|
132
|
-
},
|
|
133
|
-
"opensource-deploy": {
|
|
134
|
-
skillId: "rainbond-opensource-app-deploy",
|
|
135
|
-
required: ["source_kind"],
|
|
136
|
-
optional: ["project_root", "source_url", "image_refs"],
|
|
137
|
-
enums: { source_kind: ["compose", "helm", "images"] },
|
|
138
|
-
steps: ["topology", "model", "deploy", "verify"],
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const INTENT_EXAMPLES = deepFreeze({
|
|
143
|
-
"environment-add": { type: "environment-add" },
|
|
144
|
-
deploy: { type: "deploy" },
|
|
145
|
-
create: { type: "create" },
|
|
146
|
-
"template-install": {
|
|
147
|
-
type: "template-install",
|
|
148
|
-
template_id: "template",
|
|
149
|
-
install_scope: "new-app",
|
|
150
|
-
},
|
|
151
|
-
query: { type: "query", operation: "summary", app_id: "app" },
|
|
152
|
-
"platform-query": { type: "platform-query", resource: "components", app_id: "app" },
|
|
153
|
-
troubleshoot: { type: "troubleshoot", operation: "build", app_id: "app" },
|
|
154
|
-
modify: { type: "modify", team_id: "team", app_id: "app", operation: "env" },
|
|
155
|
-
"delivery-verify": { type: "delivery-verify", operation: "full", app_id: "app" },
|
|
156
|
-
snapshot: { type: "snapshot", team_id: "team", app_id: "app", operation: "create" },
|
|
157
|
-
publish: { type: "publish", team_id: "team", app_id: "app", destination: "local-library" },
|
|
158
|
-
rollback: {
|
|
159
|
-
type: "rollback",
|
|
160
|
-
team_id: "team",
|
|
161
|
-
app_id: "app",
|
|
162
|
-
snapshot_id: "snapshot",
|
|
163
|
-
operation: "preview",
|
|
164
|
-
},
|
|
165
|
-
"env-sync": {
|
|
166
|
-
type: "env-sync",
|
|
167
|
-
project_root: "/workspace/app",
|
|
168
|
-
environment: "production",
|
|
169
|
-
app_id: "app",
|
|
170
|
-
},
|
|
171
|
-
"project-init": { type: "project-init", project_root: "/workspace/app", source_kind: "local" },
|
|
172
|
-
bootstrap: { type: "bootstrap", project_root: "/workspace/app" },
|
|
173
|
-
"troubleshoot-phase": { type: "troubleshoot-phase", operation: "build", app_id: "app" },
|
|
174
|
-
"opensource-deploy": {
|
|
175
|
-
type: "opensource-deploy",
|
|
176
|
-
source_kind: "images",
|
|
177
|
-
image_refs: ["nginx:1.27"],
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
function assertBoundedString(value, field, maximum) {
|
|
182
|
-
if (typeof value !== "string" || !value || value.length > maximum) {
|
|
183
|
-
throw new Error(`${field} 必须是长度不超过 ${maximum} 的非空字符串`);
|
|
184
|
-
}
|
|
185
|
-
if (CONTROL_PATTERN.test(value)) throw new Error(`${field} 不能包含控制字符`);
|
|
186
|
-
return value;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function canonicalHttpsSourceUrl(value) {
|
|
190
|
-
assertBoundedString(value, "source_url", MAX_PATH_LENGTH);
|
|
191
|
-
let parsed;
|
|
192
|
-
try {
|
|
193
|
-
parsed = new URL(value);
|
|
194
|
-
} catch {
|
|
195
|
-
throw new Error("source_url 必须是规范的 HTTPS URL");
|
|
196
|
-
}
|
|
197
|
-
if (
|
|
198
|
-
parsed.protocol !== "https:"
|
|
199
|
-
|| parsed.username
|
|
200
|
-
|| parsed.password
|
|
201
|
-
|| parsed.search
|
|
202
|
-
|| parsed.hash
|
|
203
|
-
|| value.includes("?")
|
|
204
|
-
|| value.includes("#")
|
|
205
|
-
) {
|
|
206
|
-
throw new Error("source_url 必须是无 userinfo、query 和 fragment 的 HTTPS URL");
|
|
207
|
-
}
|
|
208
|
-
return parsed.toString();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function canonicalImageReference(value, field = "image_ref") {
|
|
212
|
-
const reference = assertBoundedString(value, field, MAX_IMAGE_REFERENCE_LENGTH);
|
|
213
|
-
if (reference.trim() !== reference || /\s|[;`$\\]/u.test(reference)) {
|
|
214
|
-
throw new Error(`${field} 必须是安全的 OCI 镜像引用`);
|
|
215
|
-
}
|
|
216
|
-
if (reference.includes("://") || reference.includes("?") || reference.includes("#")) {
|
|
217
|
-
throw new Error(`${field} 不能是 URL`);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const digestParts = reference.split("@");
|
|
221
|
-
if (digestParts.length > 2) throw new Error(`${field} digest 无效`);
|
|
222
|
-
const [taggedName, digest] = digestParts;
|
|
223
|
-
if (digest && !/^sha256:[a-f0-9]{64}$/u.test(digest)) {
|
|
224
|
-
throw new Error(`${field} digest 无效`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const lastSlash = taggedName.lastIndexOf("/");
|
|
228
|
-
const lastColon = taggedName.lastIndexOf(":");
|
|
229
|
-
let imageName = taggedName;
|
|
230
|
-
if (lastColon > lastSlash) {
|
|
231
|
-
const tag = taggedName.slice(lastColon + 1);
|
|
232
|
-
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/u.test(tag)) {
|
|
233
|
-
throw new Error(`${field} tag 无效`);
|
|
234
|
-
}
|
|
235
|
-
imageName = taggedName.slice(0, lastColon);
|
|
236
|
-
}
|
|
237
|
-
if (!imageName || imageName.startsWith("/") || imageName.endsWith("/") || imageName.includes("//")) {
|
|
238
|
-
throw new Error(`${field} 镜像名称无效`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const components = imageName.split("/");
|
|
242
|
-
const first = components[0];
|
|
243
|
-
if (components.length > 1 && first.includes(":")) {
|
|
244
|
-
const separator = first.lastIndexOf(":");
|
|
245
|
-
const host = first.slice(0, separator);
|
|
246
|
-
const port = first.slice(separator + 1);
|
|
247
|
-
if (!host || !/^[0-9]{1,5}$/u.test(port) || Number(port) < 1 || Number(port) > 65535) {
|
|
248
|
-
throw new Error(`${field} registry 端口无效`);
|
|
249
|
-
}
|
|
250
|
-
components[0] = host;
|
|
251
|
-
}
|
|
252
|
-
if (components.some((component) => (
|
|
253
|
-
!/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u.test(component)
|
|
254
|
-
))) {
|
|
255
|
-
throw new Error(`${field} 镜像名称无效`);
|
|
256
|
-
}
|
|
257
|
-
return reference;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function canonicalImageReferences(value) {
|
|
261
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > MAX_IMAGE_REFERENCES) {
|
|
262
|
-
throw new Error(`image_refs 必须是包含 1-${MAX_IMAGE_REFERENCES} 个镜像引用的数组`);
|
|
263
|
-
}
|
|
264
|
-
const references = value.map((item) => canonicalImageReference(item, "image_refs"));
|
|
265
|
-
if (new Set(references).size !== references.length) throw new Error("image_refs 不能包含重复项");
|
|
266
|
-
return references;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function validateSourceFields(result) {
|
|
270
|
-
const kind = result.source_kind;
|
|
271
|
-
if (result.source_url && !kind) {
|
|
272
|
-
throw new Error("source_url 必须同时指定 source_kind");
|
|
273
|
-
}
|
|
274
|
-
if (result.source_url && ["local", "image", "images"].includes(kind)) {
|
|
275
|
-
throw new Error(`source_kind=${kind} 不能使用 source_url`);
|
|
276
|
-
}
|
|
277
|
-
if (result.image_ref && kind !== "image") {
|
|
278
|
-
throw new Error("image_ref 只允许用于 source_kind=image");
|
|
279
|
-
}
|
|
280
|
-
if (result.image_refs && kind !== "images") {
|
|
281
|
-
throw new Error("image_refs 只允许用于 source_kind=images");
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function validateIntent(input, { pathApi = path } = {}) {
|
|
286
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
287
|
-
throw new Error("intent 必须是对象");
|
|
288
|
-
}
|
|
289
|
-
for (const key of Object.keys(input)) {
|
|
290
|
-
if (CREDENTIAL_KEY_PATTERN.test(key)) throw new Error(`intent 不能包含凭据字段:${key}`);
|
|
291
|
-
}
|
|
292
|
-
const type = input.type;
|
|
293
|
-
if (type === undefined || type === null || type === "") {
|
|
294
|
-
throw new Error("intent 缺少必填字段:type");
|
|
295
|
-
}
|
|
296
|
-
if (!Object.hasOwn(INTENT_DEFINITIONS, type)) throw new Error(`未知 intent type:${String(type)}`);
|
|
297
|
-
const definition = INTENT_DEFINITIONS[type];
|
|
298
|
-
const allowed = new Set(["type", ...definition.required, ...definition.optional]);
|
|
299
|
-
for (const key of Object.keys(input)) {
|
|
300
|
-
if (!allowed.has(key)) throw new Error(`intent 包含未知字段:${key}`);
|
|
301
|
-
}
|
|
302
|
-
if (input.source_url !== undefined && input.source_kind === "image") {
|
|
303
|
-
throw new Error("source_kind=image 必须使用 image_ref,不能使用 source_url");
|
|
304
|
-
}
|
|
305
|
-
if (input.source_url !== undefined && input.source_kind === "images") {
|
|
306
|
-
throw new Error("source_kind=images 必须使用 image_refs,不能使用 source_url");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const result = { type };
|
|
310
|
-
for (const field of definition.required) {
|
|
311
|
-
if (input[field] === undefined || input[field] === null || input[field] === "") {
|
|
312
|
-
throw new Error(`intent 缺少必填字段:${field}`);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
for (const field of [...definition.required, ...definition.optional]) {
|
|
316
|
-
const value = input[field];
|
|
317
|
-
if (value === undefined) continue;
|
|
318
|
-
if (definition.enums[field]) {
|
|
319
|
-
if (!definition.enums[field].includes(value)) {
|
|
320
|
-
throw new Error(`${field} 不是允许的固定值`);
|
|
321
|
-
}
|
|
322
|
-
result[field] = value;
|
|
323
|
-
} else if (field === "source_url") {
|
|
324
|
-
result[field] = canonicalHttpsSourceUrl(value);
|
|
325
|
-
} else if (field === "image_ref") {
|
|
326
|
-
result[field] = canonicalImageReference(value);
|
|
327
|
-
} else if (field === "image_refs") {
|
|
328
|
-
result[field] = canonicalImageReferences(value);
|
|
329
|
-
} else if (field === "project_root") {
|
|
330
|
-
const projectRoot = assertBoundedString(value, field, MAX_PATH_LENGTH);
|
|
331
|
-
if (!projectRoot.trim() || projectRoot.trim() !== projectRoot) {
|
|
332
|
-
throw new Error("project_root 不能是纯空白或包含前后空白");
|
|
333
|
-
}
|
|
334
|
-
if (!pathApi || typeof pathApi.resolve !== "function") throw new Error("project_root path API 无效");
|
|
335
|
-
result[field] = assertBoundedString(pathApi.resolve(projectRoot), field, MAX_PATH_LENGTH);
|
|
336
|
-
} else if (field === "version") {
|
|
337
|
-
result[field] = assertBoundedString(value, field, MAX_VERSION_LENGTH);
|
|
338
|
-
} else if (IDENTIFIER_FIELDS.has(field)) {
|
|
339
|
-
result[field] = assertBoundedString(value, field, MAX_IDENTIFIER_LENGTH);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
validateSourceFields(result);
|
|
343
|
-
return result;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function isExistingAppIntent(intent) {
|
|
347
|
-
if (["query", "platform-query", "troubleshoot", "troubleshoot-phase", "env-sync", "modify", "delivery-verify", "snapshot", "publish", "rollback"].includes(intent.type)) {
|
|
348
|
-
return true;
|
|
349
|
-
}
|
|
350
|
-
if (intent.type === "template-install") return intent.install_scope === "existing-app";
|
|
351
|
-
if (intent.type === "bootstrap") return Boolean(intent.app_id || intent.service_id);
|
|
352
|
-
return ["deploy", "create"].includes(intent.type) && Boolean(intent.service_id);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function assertIntentCanInstallNewPlatform(input) {
|
|
356
|
-
const intent = validateIntent(input);
|
|
357
|
-
if (isExistingAppIntent(intent)) {
|
|
358
|
-
throw new Error("existing-app intent 不能进入新平台安装分支");
|
|
359
|
-
}
|
|
360
|
-
return intent;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function createIntentContinuation(input, resumeStep) {
|
|
364
|
-
const intent = validateIntent(input);
|
|
365
|
-
const definition = INTENT_DEFINITIONS[intent.type];
|
|
366
|
-
const fixedStep = resumeStep || definition.steps[0];
|
|
367
|
-
if (!definition.steps.includes(fixedStep)) throw new Error("resume step 不是该 intent 的固定步骤");
|
|
368
|
-
return {
|
|
369
|
-
schema: "rainskills.intent-continuation.v1",
|
|
370
|
-
skill_id: definition.skillId,
|
|
371
|
-
intent,
|
|
372
|
-
resume_step: fixedStep,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
module.exports = {
|
|
377
|
-
INTENT_DEFINITIONS,
|
|
378
|
-
INTENT_EXAMPLES,
|
|
379
|
-
assertIntentCanInstallNewPlatform,
|
|
380
|
-
canonicalHttpsSourceUrl,
|
|
381
|
-
createIntentContinuation,
|
|
382
|
-
deepFreeze,
|
|
383
|
-
isExistingAppIntent,
|
|
384
|
-
validateIntent,
|
|
385
|
-
canonicalImageReference,
|
|
386
|
-
};
|