@chrysb/alphaclaw 0.4.6-beta.7 → 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/doctor/helpers.js +71 -5
- package/lib/public/js/components/doctor/index.js +89 -28
- 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/doctor/bootstrap-context.js +191 -0
- package/lib/server/doctor/prompt.js +20 -4
- package/lib/server/doctor/service.js +18 -4
- 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 +12 -3
- package/package.json +1 -1
- package/lib/public/js/components/welcome.js +0 -318
package/bin/alphaclaw.js
CHANGED
|
@@ -216,21 +216,7 @@ try {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// ---------------------------------------------------------------------------
|
|
219
|
-
// 5.
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
|
|
222
|
-
const openclawEnvLink = path.join(openclawDir, ".env");
|
|
223
|
-
try {
|
|
224
|
-
if (!fs.existsSync(openclawEnvLink)) {
|
|
225
|
-
fs.symlinkSync(envFilePath, openclawEnvLink);
|
|
226
|
-
console.log(`[alphaclaw] Symlinked ${openclawEnvLink} -> ${envFilePath}`);
|
|
227
|
-
}
|
|
228
|
-
} catch (e) {
|
|
229
|
-
console.log(`[alphaclaw] .env symlink skipped: ${e.message}`);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ---------------------------------------------------------------------------
|
|
233
|
-
// 6. Load .env into process.env
|
|
219
|
+
// 5. Load .env into process.env
|
|
234
220
|
// ---------------------------------------------------------------------------
|
|
235
221
|
|
|
236
222
|
if (fs.existsSync(envFilePath)) {
|
|
@@ -530,23 +516,7 @@ if (!gogInstalled) {
|
|
|
530
516
|
}
|
|
531
517
|
|
|
532
518
|
// ---------------------------------------------------------------------------
|
|
533
|
-
// 7.
|
|
534
|
-
// ---------------------------------------------------------------------------
|
|
535
|
-
|
|
536
|
-
process.env.GOG_KEYRING_PASSWORD =
|
|
537
|
-
process.env.GOG_KEYRING_PASSWORD || "alphaclaw";
|
|
538
|
-
const gogConfigFile = path.join(openclawDir, "gogcli", "config.json");
|
|
539
|
-
|
|
540
|
-
if (!fs.existsSync(gogConfigFile)) {
|
|
541
|
-
fs.mkdirSync(path.join(openclawDir, "gogcli"), { recursive: true });
|
|
542
|
-
try {
|
|
543
|
-
execSync("gog auth keyring file", { stdio: "ignore" });
|
|
544
|
-
console.log("[alphaclaw] gog keyring configured (file backend)");
|
|
545
|
-
} catch {}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// ---------------------------------------------------------------------------
|
|
549
|
-
// 8. Install/reconcile system cron entry
|
|
519
|
+
// 7. Install/reconcile system cron entry
|
|
550
520
|
// ---------------------------------------------------------------------------
|
|
551
521
|
|
|
552
522
|
const packagedHourlyGitSyncPath = path.join(setupDir, "hourly-git-sync.sh");
|
package/lib/public/css/theme.css
CHANGED
|
@@ -513,6 +513,11 @@ textarea:focus {
|
|
|
513
513
|
height: 28px;
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
+
.ac-segmented-control-full {
|
|
517
|
+
display: flex;
|
|
518
|
+
width: 100%;
|
|
519
|
+
}
|
|
520
|
+
|
|
516
521
|
.ac-segmented-control-button {
|
|
517
522
|
border: 0;
|
|
518
523
|
background: transparent;
|
|
@@ -527,6 +532,20 @@ textarea:focus {
|
|
|
527
532
|
transition: color 0.12s, background 0.12s;
|
|
528
533
|
}
|
|
529
534
|
|
|
535
|
+
.ac-segmented-control-full .ac-segmented-control-button {
|
|
536
|
+
flex: 1 1 0%;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.ac-segmented-control-lg {
|
|
540
|
+
height: 36px;
|
|
541
|
+
border-radius: 12px;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.ac-segmented-control-lg .ac-segmented-control-button {
|
|
545
|
+
font-size: 14px;
|
|
546
|
+
padding: 0 16px;
|
|
547
|
+
}
|
|
548
|
+
|
|
530
549
|
.ac-segmented-control-button:hover {
|
|
531
550
|
color: var(--text);
|
|
532
551
|
background: rgba(255, 255, 255, 0.03);
|
package/lib/public/js/app.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useState, useEffect } from "https://esm.sh/preact/hooks";
|
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
4
|
import { Router, Route, Switch, useLocation } from "https://esm.sh/wouter-preact";
|
|
5
5
|
import { logout } from "./lib/api.js";
|
|
6
|
-
import { Welcome } from "./components/welcome.js";
|
|
6
|
+
import { Welcome } from "./components/welcome/index.js";
|
|
7
7
|
import { ToastContainer } from "./components/toast.js";
|
|
8
8
|
import { GlobalRestartBanner } from "./components/global-restart-banner.js";
|
|
9
9
|
import { LoadingSpinner } from "./components/loading-spinner.js";
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export const getDoctorPriorityTone = (priority = "") => {
|
|
2
|
-
const normalized = String(priority || "")
|
|
2
|
+
const normalized = String(priority || "")
|
|
3
|
+
.trim()
|
|
4
|
+
.toUpperCase();
|
|
3
5
|
if (normalized === "P0") return "danger";
|
|
4
6
|
if (normalized === "P1") return "warning";
|
|
5
7
|
return "neutral";
|
|
6
8
|
};
|
|
7
9
|
|
|
8
10
|
export const getDoctorStatusTone = (status = "") => {
|
|
9
|
-
const normalized = String(status || "")
|
|
11
|
+
const normalized = String(status || "")
|
|
12
|
+
.trim()
|
|
13
|
+
.toLowerCase();
|
|
10
14
|
if (normalized === "fixed") return "success";
|
|
11
15
|
if (normalized === "dismissed") return "neutral";
|
|
12
16
|
return "warning";
|
|
@@ -35,7 +39,9 @@ export const formatDoctorCategory = (category = "") => {
|
|
|
35
39
|
export const buildDoctorPriorityCounts = (cards = []) =>
|
|
36
40
|
cards.reduce(
|
|
37
41
|
(totals, card) => {
|
|
38
|
-
const priority = String(card?.priority || "")
|
|
42
|
+
const priority = String(card?.priority || "")
|
|
43
|
+
.trim()
|
|
44
|
+
.toUpperCase();
|
|
39
45
|
if (priority === "P0" || priority === "P1" || priority === "P2") {
|
|
40
46
|
totals[priority] += 1;
|
|
41
47
|
}
|
|
@@ -47,7 +53,9 @@ export const buildDoctorPriorityCounts = (cards = []) =>
|
|
|
47
53
|
export const groupDoctorCardsByStatus = (cards = []) =>
|
|
48
54
|
cards.reduce(
|
|
49
55
|
(groups, card) => {
|
|
50
|
-
const status = String(card?.status || "open")
|
|
56
|
+
const status = String(card?.status || "open")
|
|
57
|
+
.trim()
|
|
58
|
+
.toLowerCase();
|
|
51
59
|
if (status === "fixed") {
|
|
52
60
|
groups.fixed.push(card);
|
|
53
61
|
return groups;
|
|
@@ -74,13 +82,71 @@ export const shouldShowDoctorWarning = (
|
|
|
74
82
|
|
|
75
83
|
export const getDoctorWarningMessage = (doctorStatus = null) => {
|
|
76
84
|
if (!doctorStatus) return "";
|
|
77
|
-
const changedFilesCount = Number(
|
|
85
|
+
const changedFilesCount = Number(
|
|
86
|
+
doctorStatus.changeSummary?.changedFilesCount || 0,
|
|
87
|
+
);
|
|
78
88
|
if (changedFilesCount > 0) {
|
|
79
89
|
return `Drift Doctor has not been run in the last week and ${changedFilesCount} file${changedFilesCount === 1 ? "" : "s"} changed since the last review.`;
|
|
80
90
|
}
|
|
81
91
|
return "Doctor has not been run in the last week.";
|
|
82
92
|
};
|
|
83
93
|
|
|
94
|
+
export const formatDoctorCharCount = (value = 0) =>
|
|
95
|
+
`${Number(value || 0).toLocaleString()} chars`;
|
|
96
|
+
|
|
97
|
+
const isManagedBootstrapContextPath = (filePath = "") =>
|
|
98
|
+
String(filePath || "").startsWith("hooks/bootstrap/");
|
|
99
|
+
|
|
100
|
+
export const getDoctorBootstrapTruncationItems = (doctorStatus = null) => {
|
|
101
|
+
const bootstrapContext = doctorStatus?.bootstrapContext;
|
|
102
|
+
const truncatedFiles = (bootstrapContext?.activeTruncatedFiles || []).filter(
|
|
103
|
+
(file) => !isManagedBootstrapContextPath(file?.path),
|
|
104
|
+
);
|
|
105
|
+
const nearLimitFiles = (bootstrapContext?.activeNearLimitFiles || []).filter(
|
|
106
|
+
(file) => !isManagedBootstrapContextPath(file?.path),
|
|
107
|
+
);
|
|
108
|
+
return [
|
|
109
|
+
...truncatedFiles.map((file) => ({
|
|
110
|
+
path: file.path,
|
|
111
|
+
size: formatDoctorCharCount(file.rawChars),
|
|
112
|
+
statusText: `-${Number(
|
|
113
|
+
Math.max(
|
|
114
|
+
0,
|
|
115
|
+
Number(file.rawChars || 0) - Number(file.injectedChars || 0),
|
|
116
|
+
),
|
|
117
|
+
).toLocaleString()} cut`,
|
|
118
|
+
statusTone: "danger",
|
|
119
|
+
})),
|
|
120
|
+
...nearLimitFiles.map((file) => ({
|
|
121
|
+
path: file.path,
|
|
122
|
+
size: formatDoctorCharCount(file.rawChars),
|
|
123
|
+
statusText: "Near limit",
|
|
124
|
+
statusTone: "warning",
|
|
125
|
+
})),
|
|
126
|
+
];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const hasDoctorBootstrapWarnings = (doctorStatus = null) =>
|
|
130
|
+
getDoctorBootstrapTruncationItems(doctorStatus).length > 0;
|
|
131
|
+
|
|
132
|
+
export const getDoctorBootstrapWarningTitle = (doctorStatus = null) => {
|
|
133
|
+
const items = getDoctorBootstrapTruncationItems(doctorStatus);
|
|
134
|
+
if (!items.length) return "";
|
|
135
|
+
const hasTruncatedItems = items.some((item) => item.statusTone === "danger");
|
|
136
|
+
const hasNearLimitItems = items.some((item) => item.statusTone === "warning");
|
|
137
|
+
if (hasTruncatedItems && hasNearLimitItems) {
|
|
138
|
+
return "Some of your main files are being truncated or nearing the limit:";
|
|
139
|
+
}
|
|
140
|
+
if (hasNearLimitItems) {
|
|
141
|
+
return items.length === 1
|
|
142
|
+
? "One of your main files is nearing the limit:"
|
|
143
|
+
: "Some of your main files are nearing the limit:";
|
|
144
|
+
}
|
|
145
|
+
return items.length === 1
|
|
146
|
+
? "One of your main files is being truncated:"
|
|
147
|
+
: "Some of your main files are being truncated:";
|
|
148
|
+
};
|
|
149
|
+
|
|
84
150
|
export const getDoctorChangeLabel = (changeSummary = null) => {
|
|
85
151
|
const changedFilesCount = Number(changeSummary?.changedFilesCount || 0);
|
|
86
152
|
if (changedFilesCount === 0) return "No changes since last run";
|
|
@@ -20,8 +20,11 @@ import { DoctorFixCardModal } from "./fix-card-modal.js";
|
|
|
20
20
|
import {
|
|
21
21
|
buildDoctorRunMarkers,
|
|
22
22
|
buildDoctorStatusFilterOptions,
|
|
23
|
+
getDoctorBootstrapTruncationItems,
|
|
24
|
+
getDoctorBootstrapWarningTitle,
|
|
23
25
|
getDoctorChangeLabel,
|
|
24
26
|
getDoctorRunPillDetail,
|
|
27
|
+
hasDoctorBootstrapWarnings,
|
|
25
28
|
shouldShowDoctorWarning,
|
|
26
29
|
} from "./helpers.js";
|
|
27
30
|
|
|
@@ -182,6 +185,18 @@ export const DoctorTab = ({ isActive = false, onOpenFile = () => {} }) => {
|
|
|
182
185
|
() => shouldShowDoctorWarning(doctorStatus, 0),
|
|
183
186
|
[doctorStatus],
|
|
184
187
|
);
|
|
188
|
+
const showBootstrapTruncationBanner = useMemo(
|
|
189
|
+
() => hasDoctorBootstrapWarnings(doctorStatus),
|
|
190
|
+
[doctorStatus],
|
|
191
|
+
);
|
|
192
|
+
const bootstrapTruncationMessage = useMemo(
|
|
193
|
+
() => getDoctorBootstrapWarningTitle(doctorStatus),
|
|
194
|
+
[doctorStatus],
|
|
195
|
+
);
|
|
196
|
+
const bootstrapTruncationItems = useMemo(
|
|
197
|
+
() => getDoctorBootstrapTruncationItems(doctorStatus),
|
|
198
|
+
[doctorStatus],
|
|
199
|
+
);
|
|
185
200
|
const hasCompletedDoctorRun = !!doctorStatus?.lastRunAt;
|
|
186
201
|
const hasRuns = runs.length > 0;
|
|
187
202
|
const hasLoadedRuns = runsPoll.data !== null || runsPoll.error !== null;
|
|
@@ -312,29 +327,76 @@ export const DoctorTab = ({ isActive = false, onOpenFile = () => {} }) => {
|
|
|
312
327
|
: null}
|
|
313
328
|
${!showInitialLoadingState && hasRuns
|
|
314
329
|
? html`
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
<div class="space-y-3">
|
|
331
|
+
<${DoctorSummaryCards} cards=${openCards} />
|
|
332
|
+
<div class="space-y-3">
|
|
333
|
+
${hasCompletedDoctorRun
|
|
334
|
+
? html`
|
|
335
|
+
<div
|
|
336
|
+
class="bg-surface border border-border rounded-xl p-4 flex flex-wrap items-center justify-between gap-3"
|
|
337
|
+
>
|
|
338
|
+
<span class="text-xs text-gray-500">
|
|
339
|
+
Last run ·${" "}
|
|
340
|
+
<span class="text-gray-300">
|
|
341
|
+
${formatLocaleDateTime(doctorStatus?.lastRunAt, {
|
|
342
|
+
fallback: "Never",
|
|
343
|
+
})}
|
|
344
|
+
</span>
|
|
328
345
|
</span>
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
</
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
<span class="text-xs text-gray-500">
|
|
347
|
+
${changeLabel}
|
|
348
|
+
</span>
|
|
349
|
+
</div>
|
|
350
|
+
${showBootstrapTruncationBanner
|
|
351
|
+
? html`
|
|
352
|
+
<div
|
|
353
|
+
class="bg-surface border border-border rounded-xl p-4 space-y-3"
|
|
354
|
+
>
|
|
355
|
+
<div class="text-xs text-gray-400">
|
|
356
|
+
⚠️ ${bootstrapTruncationMessage}
|
|
357
|
+
</div>
|
|
358
|
+
<div class="space-y-2">
|
|
359
|
+
${bootstrapTruncationItems.map(
|
|
360
|
+
(item) => html`
|
|
361
|
+
<div
|
|
362
|
+
class="flex items-center justify-between gap-3 text-xs"
|
|
363
|
+
>
|
|
364
|
+
<button
|
|
365
|
+
type="button"
|
|
366
|
+
class="font-mono text-gray-200 ac-tip-link hover:underline text-left cursor-pointer"
|
|
367
|
+
onClick=${() => onOpenFile(String(item.path || ""))}
|
|
368
|
+
>
|
|
369
|
+
${item.path}
|
|
370
|
+
</button>
|
|
371
|
+
<span
|
|
372
|
+
class="flex items-center gap-3 whitespace-nowrap"
|
|
373
|
+
>
|
|
374
|
+
<span class="text-gray-500">
|
|
375
|
+
${item.size}
|
|
376
|
+
</span>
|
|
377
|
+
<span
|
|
378
|
+
class=${item.statusTone === "warning"
|
|
379
|
+
? "text-yellow-300"
|
|
380
|
+
: "text-red-300"}
|
|
381
|
+
>
|
|
382
|
+
${item.statusText}
|
|
383
|
+
</span>
|
|
384
|
+
</span>
|
|
385
|
+
</div>
|
|
386
|
+
`,
|
|
387
|
+
)}
|
|
388
|
+
</div>
|
|
389
|
+
<div class="border-t border-border"></div>
|
|
390
|
+
<p class="text-xs text-gray-500 leading-5">
|
|
391
|
+
Truncated files become partially hidden from
|
|
392
|
+
your agent and could cause drift.
|
|
393
|
+
</p>
|
|
394
|
+
</div>
|
|
395
|
+
`
|
|
396
|
+
: null}
|
|
397
|
+
`
|
|
398
|
+
: null}
|
|
399
|
+
${showDoctorStaleBanner
|
|
338
400
|
? html`
|
|
339
401
|
<div
|
|
340
402
|
class="text-xs text-yellow-300 bg-yellow-500/10 border border-yellow-500/35 rounded-lg px-3 py-2"
|
|
@@ -344,8 +406,8 @@ export const DoctorTab = ({ isActive = false, onOpenFile = () => {} }) => {
|
|
|
344
406
|
changed.
|
|
345
407
|
</div>
|
|
346
408
|
`
|
|
347
|
-
: null
|
|
348
|
-
|
|
409
|
+
: null}
|
|
410
|
+
</div>
|
|
349
411
|
</div>
|
|
350
412
|
`
|
|
351
413
|
: null}
|
|
@@ -461,9 +523,7 @@ export const DoctorTab = ({ isActive = false, onOpenFile = () => {} }) => {
|
|
|
461
523
|
${selectedRunIsInProgress
|
|
462
524
|
? html`
|
|
463
525
|
<div class="ac-surface-inset rounded-xl p-4">
|
|
464
|
-
<div
|
|
465
|
-
class="text-xs leading-5 text-gray-400"
|
|
466
|
-
>
|
|
526
|
+
<div class="text-xs leading-5 text-gray-400">
|
|
467
527
|
<span
|
|
468
528
|
>Run in progress. Findings will appear when analysis
|
|
469
529
|
completes.</span
|
|
@@ -479,7 +539,8 @@ export const DoctorTab = ({ isActive = false, onOpenFile = () => {} }) => {
|
|
|
479
539
|
onAskAgentFix=${setFixCard}
|
|
480
540
|
onUpdateStatus=${handleUpdateStatus}
|
|
481
541
|
onOpenFile=${onOpenFile}
|
|
482
|
-
changedPaths=${doctorStatus?.changeSummary?.changedPaths ||
|
|
542
|
+
changedPaths=${doctorStatus?.changeSummary?.changedPaths ||
|
|
543
|
+
[]}
|
|
483
544
|
showRunMeta=${selectedRunFilter === "all"}
|
|
484
545
|
hideEmptyState=${selectedRunIsInProgress}
|
|
485
546
|
/>
|
|
@@ -86,7 +86,6 @@ const kHintByKey = {
|
|
|
86
86
|
OPENAI_API_KEY: html`from <a href="https://platform.openai.com" target="_blank" class="hover:underline" style="color: var(--accent-link)">platform.openai.com</a>`,
|
|
87
87
|
GEMINI_API_KEY: html`from <a href="https://aistudio.google.com" target="_blank" class="hover:underline" style="color: var(--accent-link)">aistudio.google.com</a>`,
|
|
88
88
|
ELEVENLABS_API_KEY: html`from <a href="https://elevenlabs.io" target="_blank" class="hover:underline" style="color: var(--accent-link)">elevenlabs.io</a> · <code class="text-xs bg-black/30 px-1 rounded">XI_API_KEY</code> also supported`,
|
|
89
|
-
GITHUB_TOKEN: html`classic PAT · <code class="text-xs bg-black/30 px-1 rounded">repo</code> scope · <a href="https://github.com/settings/tokens" target="_blank" class="hover:underline" style="color: var(--accent-link)">github settings</a>`,
|
|
90
89
|
GITHUB_WORKSPACE_REPO: html`use <code class="text-xs bg-black/30 px-1 rounded">owner/repo</code> or <code class="text-xs bg-black/30 px-1 rounded">https://github.com/owner/repo</code>`,
|
|
91
90
|
TELEGRAM_BOT_TOKEN: html`from <a href="https://t.me/BotFather" target="_blank" class="hover:underline" style="color: var(--accent-link)">@BotFather</a> · <a href="https://docs.openclaw.ai/channels/telegram" target="_blank" class="hover:underline" style="color: var(--accent-link)">full guide</a>`,
|
|
92
91
|
DISCORD_BOT_TOKEN: html`from <a href="https://discord.com/developers/applications" target="_blank" class="hover:underline" style="color: var(--accent-link)">developer portal</a> · <a href="https://docs.openclaw.ai/channels/discord" target="_blank" class="hover:underline" style="color: var(--accent-link)">full guide</a>`,
|
|
@@ -4,6 +4,13 @@ import { kAllAiAuthFields } from "../../lib/model-config.js";
|
|
|
4
4
|
|
|
5
5
|
const html = htm.bind(h);
|
|
6
6
|
|
|
7
|
+
export const kRepoModeNew = "new";
|
|
8
|
+
export const kRepoModeExisting = "existing";
|
|
9
|
+
export const kGithubFlowFresh = "fresh";
|
|
10
|
+
export const kGithubFlowImport = "import";
|
|
11
|
+
export const kGithubTargetRepoModeCreate = "create";
|
|
12
|
+
export const kGithubTargetRepoModeExistingEmpty = "existing-empty";
|
|
13
|
+
|
|
7
14
|
export const normalizeGithubRepoInput = (repoInput) =>
|
|
8
15
|
String(repoInput || "")
|
|
9
16
|
.trim()
|
|
@@ -19,44 +26,59 @@ export const isValidGithubRepoInput = (repoInput) => {
|
|
|
19
26
|
};
|
|
20
27
|
|
|
21
28
|
export const kWelcomeGroups = [
|
|
22
|
-
{
|
|
23
|
-
id: "ai",
|
|
24
|
-
title: "Primary Agent Model",
|
|
25
|
-
description: "Choose your main model and authenticate its provider",
|
|
26
|
-
fields: kAllAiAuthFields,
|
|
27
|
-
validate: (vals, ctx = {}) => !!(vals.MODEL_KEY && ctx.hasAi),
|
|
28
|
-
},
|
|
29
29
|
{
|
|
30
30
|
id: "github",
|
|
31
31
|
title: "GitHub",
|
|
32
32
|
description: "Backs up your agent's config and workspace",
|
|
33
33
|
fields: [
|
|
34
|
+
{
|
|
35
|
+
key: "_GITHUB_SOURCE_REPO",
|
|
36
|
+
label: "Source Repo",
|
|
37
|
+
placeholder: "username/existing-openclaw",
|
|
38
|
+
isText: true,
|
|
39
|
+
},
|
|
34
40
|
{
|
|
35
41
|
key: "GITHUB_WORKSPACE_REPO",
|
|
36
|
-
label: "Workspace Repo",
|
|
37
|
-
hint: "A new private repo will be created for you",
|
|
42
|
+
label: "New Workspace Repo",
|
|
38
43
|
placeholder: "username/my-agent",
|
|
39
44
|
isText: true,
|
|
40
45
|
},
|
|
41
46
|
{
|
|
42
47
|
key: "GITHUB_TOKEN",
|
|
43
48
|
label: "Personal Access Token",
|
|
44
|
-
hint: html`Create a
|
|
49
|
+
hint: html`Create a${" "}<a
|
|
45
50
|
href="https://github.com/settings/tokens"
|
|
46
51
|
target="_blank"
|
|
47
52
|
class="hover:underline"
|
|
48
53
|
style="color: var(--accent-link)"
|
|
49
|
-
>
|
|
54
|
+
>classic PAT</a
|
|
50
55
|
>${" "}with${" "}<code class="text-xs bg-black/30 px-1 rounded"
|
|
51
56
|
>repo</code
|
|
52
|
-
>${" "}scope
|
|
53
|
-
|
|
57
|
+
>${" "}scope, or a${" "}<a
|
|
58
|
+
href="https://github.com/settings/personal-access-tokens/new"
|
|
59
|
+
target="_blank"
|
|
60
|
+
class="hover:underline"
|
|
61
|
+
style="color: var(--accent-link)"
|
|
62
|
+
>fine-grained token</a
|
|
63
|
+
>${" "}with Contents + Metadata access`,
|
|
64
|
+
placeholder: "ghp_... or github_pat_...",
|
|
54
65
|
},
|
|
55
66
|
],
|
|
56
|
-
validate: (vals) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
validate: (vals) => {
|
|
68
|
+
const githubFlow = vals._GITHUB_FLOW || kGithubFlowFresh;
|
|
69
|
+
const hasTarget = isValidGithubRepoInput(vals.GITHUB_WORKSPACE_REPO);
|
|
70
|
+
const hasSource =
|
|
71
|
+
githubFlow !== kGithubFlowImport ||
|
|
72
|
+
isValidGithubRepoInput(vals._GITHUB_SOURCE_REPO);
|
|
73
|
+
return !!(vals.GITHUB_TOKEN && hasTarget && hasSource);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "ai",
|
|
78
|
+
title: "Primary Agent Model",
|
|
79
|
+
description: "Choose your main model and authenticate its provider",
|
|
80
|
+
fields: kAllAiAuthFields,
|
|
81
|
+
validate: (vals, ctx = {}) => !!(vals.MODEL_KEY && ctx.hasAi),
|
|
60
82
|
},
|
|
61
83
|
{
|
|
62
84
|
id: "channels",
|