@chrysb/alphaclaw 0.1.0
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 +338 -0
- package/lib/public/icons/chevron-down.svg +9 -0
- package/lib/public/js/app.js +325 -0
- package/lib/public/js/components/badge.js +16 -0
- package/lib/public/js/components/channels.js +36 -0
- package/lib/public/js/components/credentials-modal.js +336 -0
- package/lib/public/js/components/device-pairings.js +72 -0
- package/lib/public/js/components/envars.js +354 -0
- package/lib/public/js/components/gateway.js +163 -0
- package/lib/public/js/components/google.js +223 -0
- package/lib/public/js/components/icons.js +23 -0
- package/lib/public/js/components/models.js +461 -0
- package/lib/public/js/components/pairings.js +74 -0
- package/lib/public/js/components/scope-picker.js +106 -0
- package/lib/public/js/components/toast.js +31 -0
- package/lib/public/js/components/welcome.js +541 -0
- package/lib/public/js/hooks/usePolling.js +29 -0
- package/lib/public/js/lib/api.js +196 -0
- package/lib/public/js/lib/model-config.js +88 -0
- package/lib/public/login.html +90 -0
- package/lib/public/setup.html +33 -0
- package/lib/scripts/systemctl +56 -0
- package/lib/server/auth-profiles.js +101 -0
- package/lib/server/commands.js +84 -0
- package/lib/server/constants.js +282 -0
- package/lib/server/env.js +78 -0
- package/lib/server/gateway.js +262 -0
- package/lib/server/helpers.js +192 -0
- package/lib/server/login-throttle.js +86 -0
- package/lib/server/onboarding/cron.js +51 -0
- package/lib/server/onboarding/github.js +49 -0
- package/lib/server/onboarding/index.js +127 -0
- package/lib/server/onboarding/openclaw.js +171 -0
- package/lib/server/onboarding/validation.js +107 -0
- package/lib/server/onboarding/workspace.js +52 -0
- package/lib/server/openclaw-version.js +179 -0
- package/lib/server/routes/auth.js +80 -0
- package/lib/server/routes/codex.js +204 -0
- package/lib/server/routes/google.js +390 -0
- package/lib/server/routes/models.js +68 -0
- package/lib/server/routes/onboarding.js +116 -0
- package/lib/server/routes/pages.js +21 -0
- package/lib/server/routes/pairings.js +134 -0
- package/lib/server/routes/proxy.js +29 -0
- package/lib/server/routes/system.js +213 -0
- package/lib/server.js +161 -0
- package/lib/setup/core-prompts/AGENTS.md +22 -0
- package/lib/setup/core-prompts/TOOLS.md +18 -0
- package/lib/setup/env.template +19 -0
- package/lib/setup/gitignore +12 -0
- package/lib/setup/hourly-git-sync.sh +86 -0
- package/lib/setup/skills/control-ui/SKILL.md +70 -0
- package/package.json +34 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useState, useEffect, useCallback } from "https://esm.sh/preact/hooks";
|
|
3
|
+
import htm from "https://esm.sh/htm";
|
|
4
|
+
import {
|
|
5
|
+
fetchGoogleStatus,
|
|
6
|
+
checkGoogleApis as checkApis,
|
|
7
|
+
disconnectGoogle as apiDisconnect,
|
|
8
|
+
} from "../lib/api.js";
|
|
9
|
+
import {
|
|
10
|
+
ScopePicker,
|
|
11
|
+
toggleScopeLogic,
|
|
12
|
+
getDefaultScopes,
|
|
13
|
+
} from "./scope-picker.js";
|
|
14
|
+
import { CredentialsModal } from "./credentials-modal.js";
|
|
15
|
+
import { showToast } from "./toast.js";
|
|
16
|
+
const html = htm.bind(h);
|
|
17
|
+
|
|
18
|
+
export function Google({ gatewayStatus }) {
|
|
19
|
+
const [google, setGoogle] = useState(null);
|
|
20
|
+
const [scopes, setScopes] = useState(getDefaultScopes());
|
|
21
|
+
const [savedScopes, setSavedScopes] = useState(null);
|
|
22
|
+
const [apiStatus, setApiStatus] = useState({});
|
|
23
|
+
const [checkingApis, setCheckingApis] = useState(false);
|
|
24
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
25
|
+
|
|
26
|
+
const runApiCheck = useCallback(async () => {
|
|
27
|
+
setApiStatus({});
|
|
28
|
+
setCheckingApis(true);
|
|
29
|
+
try {
|
|
30
|
+
const check = await checkApis();
|
|
31
|
+
if (check.results) setApiStatus(check.results);
|
|
32
|
+
} finally {
|
|
33
|
+
setCheckingApis(false);
|
|
34
|
+
}
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const refresh = useCallback(async () => {
|
|
38
|
+
try {
|
|
39
|
+
const data = await fetchGoogleStatus();
|
|
40
|
+
setGoogle(data);
|
|
41
|
+
if (data.activeScopes && data.activeScopes.length > 0) {
|
|
42
|
+
setScopes(data.activeScopes);
|
|
43
|
+
setSavedScopes(data.activeScopes);
|
|
44
|
+
}
|
|
45
|
+
if (data.authenticated) {
|
|
46
|
+
await runApiCheck();
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
}, [runApiCheck]);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
refresh();
|
|
53
|
+
}, [refresh]);
|
|
54
|
+
|
|
55
|
+
// First load can race gateway startup; refresh when gateway becomes healthy.
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (gatewayStatus === "running") {
|
|
58
|
+
refresh();
|
|
59
|
+
}
|
|
60
|
+
}, [gatewayStatus, refresh]);
|
|
61
|
+
|
|
62
|
+
// Listen for OAuth popup postMessage
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const handler = async (e) => {
|
|
65
|
+
if (e.data?.google === "success") {
|
|
66
|
+
showToast("✓ Google account connected", "green");
|
|
67
|
+
setApiStatus({});
|
|
68
|
+
await refresh();
|
|
69
|
+
} else if (e.data?.google === "error") {
|
|
70
|
+
showToast(
|
|
71
|
+
"✗ Google auth failed: " + (e.data.message || "unknown"),
|
|
72
|
+
"red",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
window.addEventListener("message", handler);
|
|
77
|
+
return () => window.removeEventListener("message", handler);
|
|
78
|
+
}, [refresh]);
|
|
79
|
+
|
|
80
|
+
const handleToggle = (scope) => {
|
|
81
|
+
setScopes((prev) => toggleScopeLogic(prev, scope));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const startAuth = (email) => {
|
|
85
|
+
if (scopes.length === 0) {
|
|
86
|
+
alert("Select at least one service");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const authUrl = `/auth/google/start?email=${encodeURIComponent(email)}&services=${scopes.join(",")}&_ts=${Date.now()}`;
|
|
90
|
+
const popup = window.open(
|
|
91
|
+
authUrl,
|
|
92
|
+
"google-auth",
|
|
93
|
+
"popup=yes,width=500,height=700",
|
|
94
|
+
);
|
|
95
|
+
if (!popup || popup.closed) window.location.href = authUrl;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleCheckApis = () => runApiCheck();
|
|
99
|
+
|
|
100
|
+
const handleDisconnect = async () => {
|
|
101
|
+
if (
|
|
102
|
+
!confirm(
|
|
103
|
+
"Disconnect Google account? Your agent will lose access to Gmail, Calendar, etc.",
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
return;
|
|
107
|
+
const data = await apiDisconnect();
|
|
108
|
+
if (data.ok) {
|
|
109
|
+
setGoogle({
|
|
110
|
+
hasCredentials: false,
|
|
111
|
+
authenticated: false,
|
|
112
|
+
email: "",
|
|
113
|
+
services: "",
|
|
114
|
+
activeScopes: [],
|
|
115
|
+
});
|
|
116
|
+
setApiStatus({});
|
|
117
|
+
setScopes(getDefaultScopes());
|
|
118
|
+
showToast("Google account disconnected", "green");
|
|
119
|
+
} else {
|
|
120
|
+
alert("Failed to disconnect: " + (data.error || "unknown"));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (!google) {
|
|
125
|
+
return html` <div class="bg-surface border border-border rounded-xl p-4">
|
|
126
|
+
<h2 class="font-semibold mb-3">Google Workspace</h2>
|
|
127
|
+
<div class="text-gray-500 text-sm text-center py-2">Loading...</div>
|
|
128
|
+
</div>`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const hasCredentials = google.authenticated || google.hasCredentials;
|
|
132
|
+
const isAuthed = google.authenticated;
|
|
133
|
+
const email = google.email || "";
|
|
134
|
+
const scopesChanged =
|
|
135
|
+
!savedScopes ||
|
|
136
|
+
scopes.length !== savedScopes.length ||
|
|
137
|
+
scopes.some((s) => !savedScopes.includes(s));
|
|
138
|
+
|
|
139
|
+
return html`
|
|
140
|
+
<div class="bg-surface border border-border rounded-xl p-4">
|
|
141
|
+
<h2 class="font-semibold mb-3">Google Workspace</h2>
|
|
142
|
+
${hasCredentials
|
|
143
|
+
? html`
|
|
144
|
+
<div class="space-y-3">
|
|
145
|
+
<div class="flex justify-between items-center">
|
|
146
|
+
<div class="text-sm font-medium">${email}</div>
|
|
147
|
+
${isAuthed
|
|
148
|
+
? html`<span
|
|
149
|
+
class="text-xs px-2 py-0.5 rounded-full font-medium bg-green-500/10 text-green-500"
|
|
150
|
+
>Connected</span
|
|
151
|
+
>`
|
|
152
|
+
: html`<span
|
|
153
|
+
class="text-xs px-2 py-0.5 rounded-full font-medium bg-yellow-500/10 text-yellow-500"
|
|
154
|
+
>Awaiting sign-in</span
|
|
155
|
+
>`}
|
|
156
|
+
</div>
|
|
157
|
+
<div class="flex justify-between items-center">
|
|
158
|
+
<span class="text-sm text-gray-400">Select permissions</span>
|
|
159
|
+
${isAuthed &&
|
|
160
|
+
html`<button
|
|
161
|
+
onclick=${handleCheckApis}
|
|
162
|
+
class="text-xs text-gray-500 hover:text-gray-300"
|
|
163
|
+
>
|
|
164
|
+
↻ Check APIs
|
|
165
|
+
</button>`}
|
|
166
|
+
</div>
|
|
167
|
+
<${ScopePicker}
|
|
168
|
+
scopes=${scopes}
|
|
169
|
+
onToggle=${handleToggle}
|
|
170
|
+
apiStatus=${isAuthed ? apiStatus : {}}
|
|
171
|
+
loading=${isAuthed && checkingApis}
|
|
172
|
+
/>
|
|
173
|
+
<div class="flex justify-between items-center pt-1">
|
|
174
|
+
<div class="flex items-center gap-2">
|
|
175
|
+
<button
|
|
176
|
+
onclick=${() => startAuth(email)}
|
|
177
|
+
disabled=${isAuthed && !scopesChanged}
|
|
178
|
+
class="text-sm font-medium px-4 py-2 rounded-lg ${isAuthed &&
|
|
179
|
+
!scopesChanged
|
|
180
|
+
? "bg-gray-600 text-gray-400 cursor-not-allowed"
|
|
181
|
+
: "bg-white text-black hover:opacity-85"}"
|
|
182
|
+
>
|
|
183
|
+
${isAuthed ? "Update Permissions" : "Sign in with Google"}
|
|
184
|
+
</button>
|
|
185
|
+
<button
|
|
186
|
+
type="button"
|
|
187
|
+
onclick=${() => setModalOpen(true)}
|
|
188
|
+
class="text-xs font-medium px-3 py-2 rounded-lg border border-border text-gray-300 hover:border-gray-500"
|
|
189
|
+
>
|
|
190
|
+
Edit credentials
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
<button
|
|
194
|
+
onclick=${handleDisconnect}
|
|
195
|
+
class="text-xs text-red-400/60 hover:text-red-400"
|
|
196
|
+
>
|
|
197
|
+
Disconnect
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
`
|
|
202
|
+
: html`
|
|
203
|
+
<div class="text-center space-y-2 py-1">
|
|
204
|
+
<p class="text-xs text-gray-500">
|
|
205
|
+
Connect Gmail, Calendar, Contacts, Drive, Sheets, Tasks, Docs,
|
|
206
|
+
and Meet.
|
|
207
|
+
</p>
|
|
208
|
+
<button
|
|
209
|
+
onclick=${() => setModalOpen(true)}
|
|
210
|
+
class="bg-white text-black text-sm font-medium px-4 py-2 rounded-lg hover:opacity-85"
|
|
211
|
+
>
|
|
212
|
+
Set up Google
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
`}
|
|
216
|
+
</div>
|
|
217
|
+
<${CredentialsModal}
|
|
218
|
+
visible=${modalOpen}
|
|
219
|
+
onClose=${() => setModalOpen(false)}
|
|
220
|
+
onSaved=${refresh}
|
|
221
|
+
/>
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
export const ChevronDownIcon = ({ className = "" }) => html`
|
|
7
|
+
<svg
|
|
8
|
+
class=${className}
|
|
9
|
+
width="12"
|
|
10
|
+
height="12"
|
|
11
|
+
viewBox="0 0 20 20"
|
|
12
|
+
fill="none"
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d="M5 7.5L10 12.5L15 7.5"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
stroke-width="1.8"
|
|
19
|
+
stroke-linecap="round"
|
|
20
|
+
stroke-linejoin="round"
|
|
21
|
+
/>
|
|
22
|
+
</svg>
|
|
23
|
+
`;
|