@chrysb/alphaclaw 0.4.6-beta.8 → 0.4.6-beta.9
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/bin/alphaclaw.js +2 -32
- package/lib/public/css/theme.css +19 -0
- package/lib/public/js/app.js +1 -1
- package/lib/public/js/components/envars.js +0 -1
- package/lib/public/js/components/onboarding/welcome-config.js +39 -17
- package/lib/public/js/components/onboarding/welcome-form-step.js +142 -47
- package/lib/public/js/components/onboarding/welcome-import-step.js +306 -0
- package/lib/public/js/components/onboarding/welcome-placeholder-review-step.js +99 -0
- package/lib/public/js/components/onboarding/welcome-secret-review-step.js +191 -0
- package/lib/public/js/components/segmented-control.js +7 -1
- package/lib/public/js/components/welcome/index.js +112 -0
- package/lib/public/js/components/welcome/use-welcome.js +561 -0
- package/lib/public/js/lib/api.js +221 -161
- package/lib/server/commands.js +1 -0
- package/lib/server/constants.js +0 -1
- package/lib/server/gateway.js +15 -40
- package/lib/server/onboarding/github.js +120 -19
- package/lib/server/onboarding/import/import-applier.js +321 -0
- package/lib/server/onboarding/import/import-config.js +69 -0
- package/lib/server/onboarding/import/import-scanner.js +469 -0
- package/lib/server/onboarding/import/import-temp.js +63 -0
- package/lib/server/onboarding/import/secret-detector.js +289 -0
- package/lib/server/onboarding/index.js +256 -29
- package/lib/server/onboarding/workspace.js +38 -6
- package/lib/server/routes/onboarding.js +281 -12
- package/lib/server.js +11 -2
- package/package.json +1 -1
- package/lib/public/js/components/welcome.js +0 -318
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
kSetupDir,
|
|
4
|
+
OPENCLAW_DIR,
|
|
5
|
+
ENV_FILE_PATH,
|
|
6
|
+
} = require("../constants");
|
|
3
7
|
const { renderTopicRegistryMarkdown } = require("../topic-registry");
|
|
4
8
|
const { readGoogleState } = require("../google-state");
|
|
5
9
|
|
|
@@ -71,11 +75,19 @@ const renderGoogleAccountsMarkdown = (fs) => {
|
|
|
71
75
|
const syncBootstrapPromptFiles = ({ fs, workspaceDir, baseUrl }) => {
|
|
72
76
|
try {
|
|
73
77
|
const setupUiUrl = resolveSetupUiUrl(baseUrl);
|
|
74
|
-
const bootstrapDir =
|
|
78
|
+
const bootstrapDir = path.join(workspaceDir, "hooks", "bootstrap");
|
|
75
79
|
fs.mkdirSync(bootstrapDir, { recursive: true });
|
|
76
|
-
fs.copyFileSync(path.join(kSetupDir, "core-prompts", "AGENTS.md"), `${bootstrapDir}/AGENTS.md`);
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
// AlphaClaw-managed files are always overwritten (even during import)
|
|
82
|
+
fs.copyFileSync(
|
|
83
|
+
path.join(kSetupDir, "core-prompts", "AGENTS.md"),
|
|
84
|
+
path.join(bootstrapDir, "AGENTS.md"),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const toolsTemplate = fs.readFileSync(
|
|
88
|
+
path.join(kSetupDir, "core-prompts", "TOOLS.md"),
|
|
89
|
+
"utf8",
|
|
90
|
+
);
|
|
79
91
|
let toolsContent = toolsTemplate.replace(
|
|
80
92
|
/\{\{SETUP_UI_URL\}\}/g,
|
|
81
93
|
setupUiUrl,
|
|
@@ -92,7 +104,7 @@ const syncBootstrapPromptFiles = ({ fs, workspaceDir, baseUrl }) => {
|
|
|
92
104
|
toolsContent += googleAccountsSection;
|
|
93
105
|
}
|
|
94
106
|
|
|
95
|
-
fs.writeFileSync(
|
|
107
|
+
fs.writeFileSync(path.join(bootstrapDir, "TOOLS.md"), toolsContent);
|
|
96
108
|
console.log("[onboard] Bootstrap prompt files synced");
|
|
97
109
|
} catch (e) {
|
|
98
110
|
console.error("[onboard] Bootstrap prompt sync error:", e.message);
|
|
@@ -113,4 +125,24 @@ const installControlUiSkill = ({ fs, openclawDir, baseUrl }) => {
|
|
|
113
125
|
}
|
|
114
126
|
};
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
const ensureOpenclawRuntimeArtifacts = ({
|
|
129
|
+
fs,
|
|
130
|
+
openclawDir,
|
|
131
|
+
envFilePath = ENV_FILE_PATH,
|
|
132
|
+
}) => {
|
|
133
|
+
try {
|
|
134
|
+
const openclawEnvLink = path.join(openclawDir, ".env");
|
|
135
|
+
if (!fs.existsSync(openclawEnvLink) && fs.existsSync(envFilePath)) {
|
|
136
|
+
fs.symlinkSync(envFilePath, openclawEnvLink);
|
|
137
|
+
console.log(`[alphaclaw] Symlinked ${openclawEnvLink} -> ${envFilePath}`);
|
|
138
|
+
}
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.log(`[alphaclaw] .env symlink skipped: ${e.message}`);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
ensureOpenclawRuntimeArtifacts,
|
|
146
|
+
installControlUiSkill,
|
|
147
|
+
syncBootstrapPromptFiles,
|
|
148
|
+
};
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
createOnboardingService,
|
|
3
|
+
getImportedPlaceholderReview,
|
|
4
|
+
} = require("../onboarding");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { scanWorkspace } = require("../onboarding/import/import-scanner");
|
|
7
|
+
const {
|
|
8
|
+
detectSecrets,
|
|
9
|
+
extractPreFillValues,
|
|
10
|
+
} = require("../onboarding/import/secret-detector");
|
|
11
|
+
const {
|
|
12
|
+
promoteCloneToTarget,
|
|
13
|
+
alignHookTransforms,
|
|
14
|
+
applySecretExtraction,
|
|
15
|
+
isValidTempDir,
|
|
16
|
+
} = require("../onboarding/import/import-applier");
|
|
17
|
+
const { cleanupTempClone } = require("../onboarding/github");
|
|
2
18
|
|
|
3
19
|
const sanitizeOnboardingError = (error) => {
|
|
4
20
|
const raw = [error?.stderr, error?.stdout, error?.message]
|
|
@@ -7,6 +23,7 @@ const sanitizeOnboardingError = (error) => {
|
|
|
7
23
|
const redacted = String(raw || "Onboarding failed")
|
|
8
24
|
.replace(/sk-[^\s"]+/g, "***")
|
|
9
25
|
.replace(/ghp_[^\s"]+/g, "***")
|
|
26
|
+
.replace(/github_pat_[^\s"]+/g, "***")
|
|
10
27
|
.replace(/(?:token|api[_-]?key)["'\s:=]+[^\s"']+/gi, (match) =>
|
|
11
28
|
match.replace(/[^\s"':=]+$/g, "***"),
|
|
12
29
|
);
|
|
@@ -65,6 +82,7 @@ const registerOnboardingRoutes = ({
|
|
|
65
82
|
constants,
|
|
66
83
|
shellCmd,
|
|
67
84
|
gatewayEnv,
|
|
85
|
+
readEnvFile,
|
|
68
86
|
writeEnvFile,
|
|
69
87
|
reloadEnv,
|
|
70
88
|
isOnboarded,
|
|
@@ -76,11 +94,17 @@ const registerOnboardingRoutes = ({
|
|
|
76
94
|
getBaseUrl,
|
|
77
95
|
startGateway,
|
|
78
96
|
}) => {
|
|
97
|
+
// Keep mutating onboarding routes marker-gated so in-progress imports
|
|
98
|
+
// can promote files before the final completion marker is written.
|
|
99
|
+
const hasExplicitOnboardingMarker = () =>
|
|
100
|
+
fs.existsSync(constants.kOnboardingMarkerPath);
|
|
101
|
+
|
|
79
102
|
const onboardingService = createOnboardingService({
|
|
80
103
|
fs,
|
|
81
104
|
constants,
|
|
82
105
|
shellCmd,
|
|
83
106
|
gatewayEnv,
|
|
107
|
+
readEnvFile,
|
|
84
108
|
writeEnvFile,
|
|
85
109
|
reloadEnv,
|
|
86
110
|
resolveGithubRepoUrl,
|
|
@@ -92,20 +116,57 @@ const registerOnboardingRoutes = ({
|
|
|
92
116
|
startGateway,
|
|
93
117
|
});
|
|
94
118
|
|
|
119
|
+
const kEnvVarNamePattern = /^[A-Z_][A-Z0-9_]*$/;
|
|
120
|
+
const validateApprovedSecrets = ({ approvedSecrets = [], scannedSecrets = [] }) => {
|
|
121
|
+
if (!Array.isArray(approvedSecrets)) return { ok: true, secrets: [] };
|
|
122
|
+
const scannedByFingerprint = new Map(
|
|
123
|
+
scannedSecrets.map((secret) => [
|
|
124
|
+
[
|
|
125
|
+
String(secret?.configPath || ""),
|
|
126
|
+
String(secret?.file || ""),
|
|
127
|
+
String(secret?.value || ""),
|
|
128
|
+
].join("\u0000"),
|
|
129
|
+
secret,
|
|
130
|
+
]),
|
|
131
|
+
);
|
|
132
|
+
const secrets = [];
|
|
133
|
+
for (const approvedSecret of approvedSecrets) {
|
|
134
|
+
const fingerprint = [
|
|
135
|
+
String(approvedSecret?.configPath || ""),
|
|
136
|
+
String(approvedSecret?.file || ""),
|
|
137
|
+
String(approvedSecret?.value || ""),
|
|
138
|
+
].join("\u0000");
|
|
139
|
+
const scannedSecret = scannedByFingerprint.get(fingerprint);
|
|
140
|
+
const envVarName = String(approvedSecret?.suggestedEnvVar || "").trim();
|
|
141
|
+
if (!scannedSecret || !envVarName || !kEnvVarNamePattern.test(envVarName)) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
error: "Invalid approved secrets payload",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
secrets.push({
|
|
148
|
+
...scannedSecret,
|
|
149
|
+
suggestedEnvVar: envVarName,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return { ok: true, secrets };
|
|
153
|
+
};
|
|
154
|
+
|
|
95
155
|
app.get("/api/onboard/status", (req, res) => {
|
|
96
|
-
res.json({ onboarded:
|
|
156
|
+
res.json({ onboarded: hasExplicitOnboardingMarker() });
|
|
97
157
|
});
|
|
98
158
|
|
|
99
159
|
app.post("/api/onboard", async (req, res) => {
|
|
100
|
-
if (
|
|
160
|
+
if (hasExplicitOnboardingMarker())
|
|
101
161
|
return res.json({ ok: false, error: "Already onboarded" });
|
|
102
162
|
|
|
103
163
|
try {
|
|
104
|
-
const { vars, modelKey } = req.body;
|
|
164
|
+
const { vars, modelKey, importMode } = req.body;
|
|
105
165
|
const result = await onboardingService.completeOnboarding({
|
|
106
166
|
req,
|
|
107
167
|
vars,
|
|
108
168
|
modelKey,
|
|
169
|
+
importMode: !!importMode,
|
|
109
170
|
});
|
|
110
171
|
res.status(result.status).json(result.body);
|
|
111
172
|
} catch (err) {
|
|
@@ -115,25 +176,25 @@ const registerOnboardingRoutes = ({
|
|
|
115
176
|
});
|
|
116
177
|
|
|
117
178
|
app.post("/api/onboard/github/verify", async (req, res) => {
|
|
118
|
-
if (
|
|
179
|
+
if (hasExplicitOnboardingMarker()) {
|
|
119
180
|
return res.json({ ok: false, error: "Already onboarded" });
|
|
120
181
|
}
|
|
121
182
|
|
|
122
183
|
try {
|
|
123
184
|
const githubRepoInput = String(req.body?.repo || "").trim();
|
|
124
185
|
const githubToken = String(req.body?.token || "").trim();
|
|
186
|
+
const mode = String(req.body?.mode || "new").trim();
|
|
125
187
|
if (!githubRepoInput || !githubToken) {
|
|
126
|
-
return res
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
error: "GitHub token and workspace repo are required",
|
|
131
|
-
});
|
|
188
|
+
return res.status(400).json({
|
|
189
|
+
ok: false,
|
|
190
|
+
error: "GitHub token and workspace repo are required",
|
|
191
|
+
});
|
|
132
192
|
}
|
|
133
193
|
|
|
134
194
|
const result = await onboardingService.verifyGithubSetup({
|
|
135
195
|
githubRepoInput,
|
|
136
196
|
githubToken,
|
|
197
|
+
mode,
|
|
137
198
|
resolveGithubRepoUrl,
|
|
138
199
|
});
|
|
139
200
|
if (!result.ok) {
|
|
@@ -141,7 +202,12 @@ const registerOnboardingRoutes = ({
|
|
|
141
202
|
.status(result.status || 400)
|
|
142
203
|
.json({ ok: false, error: result.error });
|
|
143
204
|
}
|
|
144
|
-
return res.json({
|
|
205
|
+
return res.json({
|
|
206
|
+
ok: true,
|
|
207
|
+
repoExists: result.repoExists || false,
|
|
208
|
+
repoIsEmpty: result.repoIsEmpty || false,
|
|
209
|
+
tempDir: result.tempDir || null,
|
|
210
|
+
});
|
|
145
211
|
} catch (err) {
|
|
146
212
|
console.error("[onboard] GitHub verify error:", err);
|
|
147
213
|
return res
|
|
@@ -149,6 +215,209 @@ const registerOnboardingRoutes = ({
|
|
|
149
215
|
.json({ ok: false, error: sanitizeOnboardingError(err) });
|
|
150
216
|
}
|
|
151
217
|
});
|
|
218
|
+
app.post("/api/onboard/import/scan", async (req, res) => {
|
|
219
|
+
if (hasExplicitOnboardingMarker()) {
|
|
220
|
+
return res.json({ ok: false, error: "Already onboarded" });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const tempDir = String(req.body?.tempDir || "").trim();
|
|
225
|
+
if (!tempDir || !isValidTempDir(tempDir)) {
|
|
226
|
+
return res
|
|
227
|
+
.status(400)
|
|
228
|
+
.json({ ok: false, error: "Invalid temp directory" });
|
|
229
|
+
}
|
|
230
|
+
if (!fs.existsSync(tempDir)) {
|
|
231
|
+
return res
|
|
232
|
+
.status(400)
|
|
233
|
+
.json({ ok: false, error: "Temp directory not found" });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const scan = scanWorkspace({ fs, baseDir: tempDir });
|
|
237
|
+
if (!scan.sourceLayout?.supported) {
|
|
238
|
+
cleanupTempClone(tempDir);
|
|
239
|
+
return res.status(400).json({
|
|
240
|
+
ok: false,
|
|
241
|
+
error: scan.sourceLayout?.error || "Unsupported import source layout",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const secrets = detectSecrets({
|
|
246
|
+
fs,
|
|
247
|
+
baseDir: tempDir,
|
|
248
|
+
configFiles: scan.gatewayConfig.files,
|
|
249
|
+
envFiles: scan.envFiles.files,
|
|
250
|
+
});
|
|
251
|
+
const preFill = extractPreFillValues({
|
|
252
|
+
fs,
|
|
253
|
+
baseDir: tempDir,
|
|
254
|
+
configFiles: scan.gatewayConfig.files,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
return res.json({ ok: true, ...scan, secrets, preFill });
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error("[onboard] Import scan error:", err);
|
|
260
|
+
return res
|
|
261
|
+
.status(500)
|
|
262
|
+
.json({ ok: false, error: sanitizeOnboardingError(err) });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
app.post("/api/onboard/import/apply", async (req, res) => {
|
|
267
|
+
if (hasExplicitOnboardingMarker()) {
|
|
268
|
+
return res.json({ ok: false, error: "Already onboarded" });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const tempDir = String(req.body?.tempDir || "").trim();
|
|
273
|
+
const approvedSecrets = Array.isArray(req.body?.approvedSecrets)
|
|
274
|
+
? req.body.approvedSecrets
|
|
275
|
+
: [];
|
|
276
|
+
const skipSecretExtraction = !!req.body?.skipSecretExtraction;
|
|
277
|
+
const githubToken = String(req.body?.githubToken || "").trim();
|
|
278
|
+
const githubRepoInput = String(req.body?.githubRepo || "").trim();
|
|
279
|
+
|
|
280
|
+
if (!tempDir || !isValidTempDir(tempDir)) {
|
|
281
|
+
return res
|
|
282
|
+
.status(400)
|
|
283
|
+
.json({ ok: false, error: "Invalid temp directory" });
|
|
284
|
+
}
|
|
285
|
+
if (!fs.existsSync(tempDir)) {
|
|
286
|
+
return res
|
|
287
|
+
.status(400)
|
|
288
|
+
.json({ ok: false, error: "Temp directory not found" });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const scan = scanWorkspace({ fs, baseDir: tempDir });
|
|
292
|
+
if (!scan.sourceLayout?.supported) {
|
|
293
|
+
cleanupTempClone(tempDir);
|
|
294
|
+
return res.status(400).json({
|
|
295
|
+
ok: false,
|
|
296
|
+
error: scan.sourceLayout?.error || "Unsupported import source layout",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let envVars = [];
|
|
301
|
+
const scannedSecrets = detectSecrets({
|
|
302
|
+
fs,
|
|
303
|
+
baseDir: tempDir,
|
|
304
|
+
configFiles: scan.gatewayConfig.files,
|
|
305
|
+
envFiles: scan.envFiles.files,
|
|
306
|
+
});
|
|
307
|
+
const approvedSecretValidation = validateApprovedSecrets({
|
|
308
|
+
approvedSecrets,
|
|
309
|
+
scannedSecrets,
|
|
310
|
+
});
|
|
311
|
+
if (!approvedSecretValidation.ok) {
|
|
312
|
+
return res.status(400).json({
|
|
313
|
+
ok: false,
|
|
314
|
+
error: approvedSecretValidation.error,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
if (!skipSecretExtraction && approvedSecrets.length > 0) {
|
|
318
|
+
const extraction = applySecretExtraction({
|
|
319
|
+
fs,
|
|
320
|
+
baseDir: tempDir,
|
|
321
|
+
approvedSecrets: approvedSecretValidation.secrets,
|
|
322
|
+
});
|
|
323
|
+
envVars = extraction.envVars;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const configFiles = ["openclaw.json"].filter((f) =>
|
|
327
|
+
fs.existsSync(path.join(tempDir, f)),
|
|
328
|
+
);
|
|
329
|
+
const transformAlignment = alignHookTransforms({
|
|
330
|
+
fs,
|
|
331
|
+
baseDir: tempDir,
|
|
332
|
+
configFiles,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const preFill = extractPreFillValues({
|
|
336
|
+
fs,
|
|
337
|
+
baseDir: tempDir,
|
|
338
|
+
configFiles,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const promoteTargetDir =
|
|
342
|
+
scan.sourceLayout.kind === "workspace-only"
|
|
343
|
+
? constants.WORKSPACE_DIR
|
|
344
|
+
: constants.OPENCLAW_DIR;
|
|
345
|
+
const promoteResult = promoteCloneToTarget({
|
|
346
|
+
fs,
|
|
347
|
+
tempDir,
|
|
348
|
+
targetDir: promoteTargetDir,
|
|
349
|
+
sourceSubdir: scan.sourceLayout.promoteSourceSubdir || "",
|
|
350
|
+
cleanupBootstrap: scan.sourceLayout.kind === "full-openclaw-root",
|
|
351
|
+
});
|
|
352
|
+
if (!promoteResult.ok) {
|
|
353
|
+
return res.status(500).json({ ok: false, error: promoteResult.error });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const existing = typeof readEnvFile === "function" ? readEnvFile() : [];
|
|
357
|
+
const merged = [...existing];
|
|
358
|
+
if (githubToken) {
|
|
359
|
+
const tokenIdx = merged.findIndex((v) => v.key === "GITHUB_TOKEN");
|
|
360
|
+
if (tokenIdx >= 0) {
|
|
361
|
+
merged[tokenIdx] = { key: "GITHUB_TOKEN", value: githubToken };
|
|
362
|
+
} else {
|
|
363
|
+
merged.push({ key: "GITHUB_TOKEN", value: githubToken });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (githubRepoInput) {
|
|
367
|
+
const normalizedRepo = resolveGithubRepoUrl(githubRepoInput);
|
|
368
|
+
const repoIdx = merged.findIndex(
|
|
369
|
+
(v) => v.key === "GITHUB_WORKSPACE_REPO",
|
|
370
|
+
);
|
|
371
|
+
if (repoIdx >= 0) {
|
|
372
|
+
merged[repoIdx] = {
|
|
373
|
+
key: "GITHUB_WORKSPACE_REPO",
|
|
374
|
+
value: normalizedRepo,
|
|
375
|
+
};
|
|
376
|
+
} else {
|
|
377
|
+
merged.push({
|
|
378
|
+
key: "GITHUB_WORKSPACE_REPO",
|
|
379
|
+
value: normalizedRepo,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
for (const newVar of envVars) {
|
|
384
|
+
const idx = merged.findIndex((v) => v.key === newVar.key);
|
|
385
|
+
if (idx >= 0) {
|
|
386
|
+
merged[idx] = newVar;
|
|
387
|
+
} else {
|
|
388
|
+
merged.push(newVar);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (githubToken || githubRepoInput || envVars.length > 0) {
|
|
392
|
+
writeEnvFile(merged);
|
|
393
|
+
reloadEnv();
|
|
394
|
+
}
|
|
395
|
+
const systemVars =
|
|
396
|
+
constants.kSystemVars instanceof Set ? constants.kSystemVars : new Set();
|
|
397
|
+
const placeholderReview = getImportedPlaceholderReview({
|
|
398
|
+
fs,
|
|
399
|
+
openclawDir: constants.OPENCLAW_DIR,
|
|
400
|
+
envVars: merged,
|
|
401
|
+
systemVars,
|
|
402
|
+
normalizeConfig: true,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return res.json({
|
|
406
|
+
ok: true,
|
|
407
|
+
preFill,
|
|
408
|
+
placeholderReview,
|
|
409
|
+
sourceLayout: scan.sourceLayout,
|
|
410
|
+
envVarsImported: envVars.length,
|
|
411
|
+
transformsAligned: transformAlignment.alignedCount,
|
|
412
|
+
});
|
|
413
|
+
} catch (err) {
|
|
414
|
+
console.error("[onboard] Import apply error:", err);
|
|
415
|
+
cleanupTempClone(req.body?.tempDir);
|
|
416
|
+
return res
|
|
417
|
+
.status(500)
|
|
418
|
+
.json({ ok: false, error: sanitizeOnboardingError(err) });
|
|
419
|
+
}
|
|
420
|
+
});
|
|
152
421
|
};
|
|
153
422
|
|
|
154
423
|
module.exports = { registerOnboardingRoutes };
|
package/lib/server.js
CHANGED
|
@@ -88,9 +88,13 @@ const {
|
|
|
88
88
|
createRestartRequiredState,
|
|
89
89
|
} = require("./server/restart-required-state");
|
|
90
90
|
const {
|
|
91
|
+
ensureOpenclawRuntimeArtifacts,
|
|
91
92
|
installControlUiSkill,
|
|
92
93
|
syncBootstrapPromptFiles,
|
|
93
94
|
} = require("./server/onboarding/workspace");
|
|
95
|
+
const {
|
|
96
|
+
cleanupStaleImportTempDirs,
|
|
97
|
+
} = require("./server/onboarding/import/import-temp");
|
|
94
98
|
const {
|
|
95
99
|
migrateManagedInternalFiles,
|
|
96
100
|
} = require("./server/internal-files-migration");
|
|
@@ -122,6 +126,7 @@ const { PORT, GATEWAY_URL, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
|
|
|
122
126
|
|
|
123
127
|
startEnvWatcher();
|
|
124
128
|
attachGatewaySignalHandlers();
|
|
129
|
+
cleanupStaleImportTempDirs();
|
|
125
130
|
migrateManagedInternalFiles({
|
|
126
131
|
fs,
|
|
127
132
|
openclawDir: constants.OPENCLAW_DIR,
|
|
@@ -214,6 +219,7 @@ registerOnboardingRoutes({
|
|
|
214
219
|
constants,
|
|
215
220
|
shellCmd,
|
|
216
221
|
gatewayEnv,
|
|
222
|
+
readEnvFile,
|
|
217
223
|
writeEnvFile,
|
|
218
224
|
reloadEnv,
|
|
219
225
|
isOnboarded,
|
|
@@ -317,6 +323,10 @@ setGatewayExitHandler((payload) => watchdog.onGatewayExit(payload));
|
|
|
317
323
|
setGatewayLaunchHandler((payload) => watchdog.onGatewayLaunch(payload));
|
|
318
324
|
const doSyncPromptFiles = () => {
|
|
319
325
|
const setupUiUrl = resolveSetupUrl();
|
|
326
|
+
ensureOpenclawRuntimeArtifacts({
|
|
327
|
+
fs,
|
|
328
|
+
openclawDir: constants.OPENCLAW_DIR,
|
|
329
|
+
});
|
|
320
330
|
syncBootstrapPromptFiles({
|
|
321
331
|
fs,
|
|
322
332
|
workspaceDir: constants.WORKSPACE_DIR,
|
|
@@ -329,7 +339,6 @@ const doSyncPromptFiles = () => {
|
|
|
329
339
|
});
|
|
330
340
|
installGogCliSkill({ fs, openclawDir: constants.OPENCLAW_DIR });
|
|
331
341
|
};
|
|
332
|
-
doSyncPromptFiles();
|
|
333
342
|
registerTelegramRoutes({
|
|
334
343
|
app,
|
|
335
344
|
telegramApi,
|
|
@@ -403,8 +412,8 @@ server.on("upgrade", (req, socket, head) => {
|
|
|
403
412
|
|
|
404
413
|
server.listen(PORT, "0.0.0.0", () => {
|
|
405
414
|
console.log(`[alphaclaw] Express listening on :${PORT}`);
|
|
406
|
-
doSyncPromptFiles();
|
|
407
415
|
if (isOnboarded()) {
|
|
416
|
+
doSyncPromptFiles();
|
|
408
417
|
reloadEnv();
|
|
409
418
|
syncChannelConfig(readEnvFile());
|
|
410
419
|
ensureGatewayProxyConfig(null);
|