@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.
Files changed (53) hide show
  1. package/bin/alphaclaw.js +338 -0
  2. package/lib/public/icons/chevron-down.svg +9 -0
  3. package/lib/public/js/app.js +325 -0
  4. package/lib/public/js/components/badge.js +16 -0
  5. package/lib/public/js/components/channels.js +36 -0
  6. package/lib/public/js/components/credentials-modal.js +336 -0
  7. package/lib/public/js/components/device-pairings.js +72 -0
  8. package/lib/public/js/components/envars.js +354 -0
  9. package/lib/public/js/components/gateway.js +163 -0
  10. package/lib/public/js/components/google.js +223 -0
  11. package/lib/public/js/components/icons.js +23 -0
  12. package/lib/public/js/components/models.js +461 -0
  13. package/lib/public/js/components/pairings.js +74 -0
  14. package/lib/public/js/components/scope-picker.js +106 -0
  15. package/lib/public/js/components/toast.js +31 -0
  16. package/lib/public/js/components/welcome.js +541 -0
  17. package/lib/public/js/hooks/usePolling.js +29 -0
  18. package/lib/public/js/lib/api.js +196 -0
  19. package/lib/public/js/lib/model-config.js +88 -0
  20. package/lib/public/login.html +90 -0
  21. package/lib/public/setup.html +33 -0
  22. package/lib/scripts/systemctl +56 -0
  23. package/lib/server/auth-profiles.js +101 -0
  24. package/lib/server/commands.js +84 -0
  25. package/lib/server/constants.js +282 -0
  26. package/lib/server/env.js +78 -0
  27. package/lib/server/gateway.js +262 -0
  28. package/lib/server/helpers.js +192 -0
  29. package/lib/server/login-throttle.js +86 -0
  30. package/lib/server/onboarding/cron.js +51 -0
  31. package/lib/server/onboarding/github.js +49 -0
  32. package/lib/server/onboarding/index.js +127 -0
  33. package/lib/server/onboarding/openclaw.js +171 -0
  34. package/lib/server/onboarding/validation.js +107 -0
  35. package/lib/server/onboarding/workspace.js +52 -0
  36. package/lib/server/openclaw-version.js +179 -0
  37. package/lib/server/routes/auth.js +80 -0
  38. package/lib/server/routes/codex.js +204 -0
  39. package/lib/server/routes/google.js +390 -0
  40. package/lib/server/routes/models.js +68 -0
  41. package/lib/server/routes/onboarding.js +116 -0
  42. package/lib/server/routes/pages.js +21 -0
  43. package/lib/server/routes/pairings.js +134 -0
  44. package/lib/server/routes/proxy.js +29 -0
  45. package/lib/server/routes/system.js +213 -0
  46. package/lib/server.js +161 -0
  47. package/lib/setup/core-prompts/AGENTS.md +22 -0
  48. package/lib/setup/core-prompts/TOOLS.md +18 -0
  49. package/lib/setup/env.template +19 -0
  50. package/lib/setup/gitignore +12 -0
  51. package/lib/setup/hourly-git-sync.sh +86 -0
  52. package/lib/setup/skills/control-ui/SKILL.md +70 -0
  53. 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
+ `;