@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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useState, useCallback } from "https://esm.sh/preact/hooks";
|
|
3
|
+
import htm from "https://esm.sh/htm";
|
|
4
|
+
import { ActionButton } from "../action-button.js";
|
|
5
|
+
import { LoadingSpinner } from "../loading-spinner.js";
|
|
6
|
+
|
|
7
|
+
const html = htm.bind(h);
|
|
8
|
+
|
|
9
|
+
const SecretRow = ({ secret, selected, onToggle, envVarName, onEnvVarChange }) =>
|
|
10
|
+
html`
|
|
11
|
+
<div
|
|
12
|
+
class="border border-border rounded-lg p-3 space-y-2 ${selected
|
|
13
|
+
? "bg-cyan-900/10 border-cyan-800/40"
|
|
14
|
+
: ""}"
|
|
15
|
+
>
|
|
16
|
+
<div class="flex items-start gap-2">
|
|
17
|
+
<input
|
|
18
|
+
type="checkbox"
|
|
19
|
+
checked=${selected}
|
|
20
|
+
onChange=${onToggle}
|
|
21
|
+
class="mt-0.5 rounded"
|
|
22
|
+
/>
|
|
23
|
+
<div class="flex-1 min-w-0">
|
|
24
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
25
|
+
<span class="text-xs font-mono text-gray-300 truncate"
|
|
26
|
+
>${secret.maskedValue}</span
|
|
27
|
+
>
|
|
28
|
+
${secret.confidence === "high"
|
|
29
|
+
? html`<span
|
|
30
|
+
class="text-xs px-1.5 py-0.5 rounded-full bg-red-900/40 text-red-300"
|
|
31
|
+
>high confidence</span
|
|
32
|
+
>`
|
|
33
|
+
: html`<span
|
|
34
|
+
class="text-xs px-1.5 py-0.5 rounded-full bg-yellow-900/40 text-yellow-300"
|
|
35
|
+
>possible</span
|
|
36
|
+
>`}
|
|
37
|
+
</div>
|
|
38
|
+
<div class="text-xs text-gray-500 mt-1">
|
|
39
|
+
Found in${" "}
|
|
40
|
+
<span class="font-mono">${secret.file || "config"}</span>
|
|
41
|
+
${secret.configPath
|
|
42
|
+
? html` at <span class="font-mono">${secret.configPath}</span>`
|
|
43
|
+
: null}
|
|
44
|
+
</div>
|
|
45
|
+
${secret.duplicateIn &&
|
|
46
|
+
html`
|
|
47
|
+
<div class="text-xs text-yellow-400 mt-1">
|
|
48
|
+
Also found in${" "}<span class="font-mono"
|
|
49
|
+
>${secret.duplicateIn}</span
|
|
50
|
+
>
|
|
51
|
+
</div>
|
|
52
|
+
`}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
${selected &&
|
|
56
|
+
html`
|
|
57
|
+
<div class="pl-6">
|
|
58
|
+
<label class="text-xs text-gray-500">Extract as env var:</label>
|
|
59
|
+
<input
|
|
60
|
+
type="text"
|
|
61
|
+
value=${envVarName}
|
|
62
|
+
onInput=${(e) => onEnvVarChange(e.target.value)}
|
|
63
|
+
class="w-full mt-1 bg-black/30 border border-border rounded-lg px-3 py-1.5 text-xs text-gray-200 outline-none focus:border-gray-500 font-mono"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
`}
|
|
67
|
+
</div>
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
export const WelcomeSecretReviewStep = ({
|
|
71
|
+
secrets = [],
|
|
72
|
+
onApprove,
|
|
73
|
+
onBack,
|
|
74
|
+
loading,
|
|
75
|
+
error,
|
|
76
|
+
}) => {
|
|
77
|
+
const [selections, setSelections] = useState(() => {
|
|
78
|
+
const initial = {};
|
|
79
|
+
for (const secret of secrets) {
|
|
80
|
+
initial[secret.configPath] = {
|
|
81
|
+
selected: secret.confidence === "high",
|
|
82
|
+
envVarName: secret.suggestedEnvVar || "",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return initial;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const toggleSecret = useCallback(
|
|
89
|
+
(configPath) => {
|
|
90
|
+
setSelections((prev) => ({
|
|
91
|
+
...prev,
|
|
92
|
+
[configPath]: {
|
|
93
|
+
...prev[configPath],
|
|
94
|
+
selected: !prev[configPath]?.selected,
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
},
|
|
98
|
+
[],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const updateEnvVarName = useCallback(
|
|
102
|
+
(configPath, name) => {
|
|
103
|
+
setSelections((prev) => ({
|
|
104
|
+
...prev,
|
|
105
|
+
[configPath]: {
|
|
106
|
+
...prev[configPath],
|
|
107
|
+
envVarName: name,
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
},
|
|
111
|
+
[],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const selectedCount = Object.values(selections).filter(
|
|
115
|
+
(s) => s.selected,
|
|
116
|
+
).length;
|
|
117
|
+
|
|
118
|
+
const handleExtract = () => {
|
|
119
|
+
const approved = secrets
|
|
120
|
+
.filter((s) => selections[s.configPath]?.selected)
|
|
121
|
+
.map((s) => ({
|
|
122
|
+
...s,
|
|
123
|
+
suggestedEnvVar:
|
|
124
|
+
selections[s.configPath]?.envVarName || s.suggestedEnvVar,
|
|
125
|
+
}));
|
|
126
|
+
onApprove(approved);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (loading) {
|
|
130
|
+
return html`
|
|
131
|
+
<div class="flex flex-col items-center justify-center py-8 gap-3">
|
|
132
|
+
<${LoadingSpinner} />
|
|
133
|
+
<p class="text-sm text-gray-400">Applying import...</p>
|
|
134
|
+
</div>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return html`
|
|
139
|
+
<div class="space-y-3">
|
|
140
|
+
<div>
|
|
141
|
+
<h2 class="text-sm font-medium text-gray-200">Review Secrets</h2>
|
|
142
|
+
<p class="text-xs text-gray-500">
|
|
143
|
+
Select secrets to extract into environment variables. Inline values in
|
|
144
|
+
config will be replaced with ${"`"}${"${"}ENV_VAR_NAME${"}"}${"`"} references.
|
|
145
|
+
</p>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
${error &&
|
|
149
|
+
html`
|
|
150
|
+
<div
|
|
151
|
+
class="bg-red-900/30 border border-red-800 rounded-xl p-3 text-red-300 text-sm"
|
|
152
|
+
>
|
|
153
|
+
${error}
|
|
154
|
+
</div>
|
|
155
|
+
`}
|
|
156
|
+
|
|
157
|
+
<div class="space-y-2 max-h-80 overflow-y-auto">
|
|
158
|
+
${secrets.map(
|
|
159
|
+
(secret) => html`
|
|
160
|
+
<${SecretRow}
|
|
161
|
+
key=${secret.configPath}
|
|
162
|
+
secret=${secret}
|
|
163
|
+
selected=${selections[secret.configPath]?.selected || false}
|
|
164
|
+
envVarName=${selections[secret.configPath]?.envVarName || ""}
|
|
165
|
+
onToggle=${() => toggleSecret(secret.configPath)}
|
|
166
|
+
onEnvVarChange=${(name) =>
|
|
167
|
+
updateEnvVarName(secret.configPath, name)}
|
|
168
|
+
/>
|
|
169
|
+
`,
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div class="grid grid-cols-2 gap-2 pt-1">
|
|
174
|
+
<${ActionButton}
|
|
175
|
+
onClick=${onBack}
|
|
176
|
+
tone="secondary"
|
|
177
|
+
idleLabel="Back"
|
|
178
|
+
className="w-full"
|
|
179
|
+
/>
|
|
180
|
+
<${ActionButton}
|
|
181
|
+
onClick=${handleExtract}
|
|
182
|
+
tone="primary"
|
|
183
|
+
idleLabel=${selectedCount > 0
|
|
184
|
+
? `Extract ${selectedCount} Secret${selectedCount === 1 ? "" : "s"}`
|
|
185
|
+
: "Skip All"}
|
|
186
|
+
className="w-full"
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
`;
|
|
191
|
+
};
|
|
@@ -11,14 +11,20 @@ const html = htm.bind(h);
|
|
|
11
11
|
* @param {*} props.value Currently selected value.
|
|
12
12
|
* @param {Function} props.onChange Called with the new value on click.
|
|
13
13
|
* @param {string} [props.className] Extra classes on the wrapper.
|
|
14
|
+
* @param {"sm"|"lg"} [props.size] Visual size variant.
|
|
15
|
+
* @param {boolean} [props.fullWidth] Stretch wrapper and options to 100%.
|
|
14
16
|
*/
|
|
15
17
|
export const SegmentedControl = ({
|
|
16
18
|
options = [],
|
|
17
19
|
value,
|
|
18
20
|
onChange = () => {},
|
|
19
21
|
className = "",
|
|
22
|
+
size = "sm",
|
|
23
|
+
fullWidth = false,
|
|
20
24
|
}) => html`
|
|
21
|
-
<div
|
|
25
|
+
<div
|
|
26
|
+
class=${`ac-segmented-control ${size === "lg" ? "ac-segmented-control-lg" : ""} ${fullWidth ? "ac-segmented-control-full" : ""} ${className}`.trim()}
|
|
27
|
+
>
|
|
22
28
|
${options.map(
|
|
23
29
|
(option) => html`
|
|
24
30
|
<button
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { kWelcomeGroups } from "../onboarding/welcome-config.js";
|
|
4
|
+
import { WelcomeImportStep } from "../onboarding/welcome-import-step.js";
|
|
5
|
+
import { WelcomePlaceholderReviewStep } from "../onboarding/welcome-placeholder-review-step.js";
|
|
6
|
+
import { WelcomeSecretReviewStep } from "../onboarding/welcome-secret-review-step.js";
|
|
7
|
+
import { WelcomeHeader } from "../onboarding/welcome-header.js";
|
|
8
|
+
import { WelcomeSetupStep } from "../onboarding/welcome-setup-step.js";
|
|
9
|
+
import { WelcomeFormStep } from "../onboarding/welcome-form-step.js";
|
|
10
|
+
import { WelcomePairingStep } from "../onboarding/welcome-pairing-step.js";
|
|
11
|
+
import { useWelcome } from "./use-welcome.js";
|
|
12
|
+
|
|
13
|
+
const html = htm.bind(h);
|
|
14
|
+
|
|
15
|
+
export const Welcome = ({ onComplete }) => {
|
|
16
|
+
const { state, actions } = useWelcome({ onComplete });
|
|
17
|
+
|
|
18
|
+
return html`
|
|
19
|
+
<div class="max-w-lg w-full space-y-5">
|
|
20
|
+
<${WelcomeHeader}
|
|
21
|
+
groups=${kWelcomeGroups}
|
|
22
|
+
step=${state.step}
|
|
23
|
+
isSetupStep=${state.isSetupStep}
|
|
24
|
+
isPairingStep=${state.isPairingStep}
|
|
25
|
+
stepNumber=${state.stepNumber}
|
|
26
|
+
activeStepLabel=${state.activeStepLabel}
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
<div class="bg-surface border border-border rounded-xl p-4 space-y-3">
|
|
30
|
+
${state.isImportStep
|
|
31
|
+
? html`<${WelcomeImportStep}
|
|
32
|
+
scanResult=${state.importScanResult}
|
|
33
|
+
scanning=${state.importScanning}
|
|
34
|
+
error=${state.importError}
|
|
35
|
+
onApprove=${actions.handleImportApprove}
|
|
36
|
+
onShowSecretReview=${actions.handleShowSecretReview}
|
|
37
|
+
onBack=${actions.handleImportBack}
|
|
38
|
+
/>`
|
|
39
|
+
: state.isSecretReviewStep
|
|
40
|
+
? html`<${WelcomeSecretReviewStep}
|
|
41
|
+
secrets=${state.importScanResult?.secrets || []}
|
|
42
|
+
onApprove=${actions.handleImportApprove}
|
|
43
|
+
onBack=${actions.handleSecretReviewBack}
|
|
44
|
+
loading=${state.importScanning}
|
|
45
|
+
error=${state.importError}
|
|
46
|
+
/>`
|
|
47
|
+
: state.isPlaceholderReviewStep
|
|
48
|
+
? html`<${WelcomePlaceholderReviewStep}
|
|
49
|
+
placeholderReview=${state.placeholderReview}
|
|
50
|
+
vals=${state.vals}
|
|
51
|
+
setValue=${actions.setValue}
|
|
52
|
+
onContinue=${actions.handlePlaceholderReviewContinue}
|
|
53
|
+
/>`
|
|
54
|
+
: state.isSetupStep
|
|
55
|
+
? html`<${WelcomeSetupStep}
|
|
56
|
+
error=${state.setupError}
|
|
57
|
+
loading=${state.loading}
|
|
58
|
+
onRetry=${actions.handleSubmit}
|
|
59
|
+
onBack=${actions.goBackFromSetupError}
|
|
60
|
+
/>`
|
|
61
|
+
: state.isPairingStep
|
|
62
|
+
? html`<${WelcomePairingStep}
|
|
63
|
+
channel=${state.selectedPairingChannel}
|
|
64
|
+
pairings=${state.pairingRequestsPoll.data || []}
|
|
65
|
+
channels=${state.pairingChannels}
|
|
66
|
+
loading=${!state.pairingStatusPoll.data}
|
|
67
|
+
error=${state.pairingError}
|
|
68
|
+
onApprove=${actions.handlePairingApprove}
|
|
69
|
+
onReject=${actions.handlePairingReject}
|
|
70
|
+
canFinish=${state.pairingComplete || state.canFinishPairing}
|
|
71
|
+
onContinue=${actions.finishOnboarding}
|
|
72
|
+
/>`
|
|
73
|
+
: html`
|
|
74
|
+
<${WelcomeFormStep}
|
|
75
|
+
activeGroup=${state.activeGroup}
|
|
76
|
+
vals=${state.vals}
|
|
77
|
+
hasAi=${state.hasAi}
|
|
78
|
+
setValue=${actions.setValue}
|
|
79
|
+
modelOptions=${state.modelOptions}
|
|
80
|
+
modelsLoading=${state.modelsLoading}
|
|
81
|
+
modelsError=${state.modelsError}
|
|
82
|
+
canToggleFullCatalog=${state.canToggleFullCatalog}
|
|
83
|
+
showAllModels=${state.showAllModels}
|
|
84
|
+
setShowAllModels=${actions.setShowAllModels}
|
|
85
|
+
selectedProvider=${state.selectedProvider}
|
|
86
|
+
codexLoading=${state.codexLoading}
|
|
87
|
+
codexStatus=${state.codexStatus}
|
|
88
|
+
startCodexAuth=${actions.startCodexAuth}
|
|
89
|
+
handleCodexDisconnect=${actions.handleCodexDisconnect}
|
|
90
|
+
codexAuthStarted=${state.codexAuthStarted}
|
|
91
|
+
codexAuthWaiting=${state.codexAuthWaiting}
|
|
92
|
+
codexManualInput=${state.codexManualInput}
|
|
93
|
+
setCodexManualInput=${actions.setCodexManualInput}
|
|
94
|
+
completeCodexAuth=${actions.completeCodexAuth}
|
|
95
|
+
codexExchanging=${state.codexExchanging}
|
|
96
|
+
visibleAiFieldKeys=${state.visibleAiFieldKeys}
|
|
97
|
+
error=${state.formError}
|
|
98
|
+
step=${state.step}
|
|
99
|
+
totalGroups=${kWelcomeGroups.length}
|
|
100
|
+
currentGroupValid=${state.currentGroupValid}
|
|
101
|
+
goBack=${actions.goBack}
|
|
102
|
+
goNext=${actions.goNext}
|
|
103
|
+
loading=${state.loading}
|
|
104
|
+
githubStepLoading=${state.githubStepLoading}
|
|
105
|
+
allValid=${state.allValid}
|
|
106
|
+
handleSubmit=${actions.handleSubmit}
|
|
107
|
+
/>
|
|
108
|
+
`}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
`;
|
|
112
|
+
};
|