@chrysb/alphaclaw 0.3.5-beta.1 → 0.4.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 +1 -31
- package/lib/public/assets/icons/google_icon.svg +8 -0
- package/lib/public/css/explorer.css +53 -0
- package/lib/public/js/app.js +126 -105
- package/lib/public/js/components/credentials-modal.js +36 -8
- package/lib/public/js/components/file-tree.js +212 -22
- package/lib/public/js/components/file-viewer/index.js +44 -6
- package/lib/public/js/components/file-viewer/status-banners.js +11 -6
- package/lib/public/js/components/file-viewer/toolbar.js +43 -1
- package/lib/public/js/components/file-viewer/use-editor-selection-restore.js +6 -0
- package/lib/public/js/components/file-viewer/use-file-diff.js +11 -0
- package/lib/public/js/components/file-viewer/use-file-loader.js +12 -2
- package/lib/public/js/components/file-viewer/use-file-viewer.js +94 -2
- package/lib/public/js/components/google/account-row.js +98 -0
- package/lib/public/js/components/google/add-account-modal.js +93 -0
- package/lib/public/js/components/google/index.js +439 -0
- package/lib/public/js/components/google/use-google-accounts.js +41 -0
- package/lib/public/js/components/icons.js +26 -0
- package/lib/public/js/components/sidebar-git-panel.js +43 -14
- package/lib/public/js/components/sidebar.js +91 -75
- package/lib/public/js/lib/api.js +72 -8
- package/lib/public/js/lib/browse-file-policies.js +29 -11
- package/lib/public/js/lib/syntax-highlighters/index.js +6 -5
- package/lib/public/shared/browse-file-policies.json +13 -0
- package/lib/server/constants.js +19 -7
- package/lib/server/google-state.js +187 -0
- package/lib/server/helpers.js +12 -4
- package/lib/server/onboarding/github.js +21 -2
- package/lib/server/onboarding/index.js +1 -3
- package/lib/server/onboarding/openclaw.js +3 -0
- package/lib/server/onboarding/workspace.js +40 -0
- package/lib/server/routes/browse/index.js +90 -2
- package/lib/server/routes/google.js +414 -213
- package/lib/setup/gitignore +3 -0
- package/lib/setup/hourly-git-sync.sh +28 -1
- package/package.json +1 -1
- package/lib/public/js/components/google.js +0 -228
package/lib/setup/gitignore
CHANGED
|
@@ -81,5 +81,32 @@ NODE
|
|
|
81
81
|
maybe_restore_if_runtime_only "cron/jobs.json"
|
|
82
82
|
maybe_restore_if_runtime_only "crons.json"
|
|
83
83
|
|
|
84
|
+
resolve_alphaclaw_cmd() {
|
|
85
|
+
if command -v alphaclaw >/dev/null 2>&1; then
|
|
86
|
+
command -v alphaclaw
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
local candidate_paths=(
|
|
91
|
+
"/app/node_modules/.bin/alphaclaw"
|
|
92
|
+
"$REPO/node_modules/.bin/alphaclaw"
|
|
93
|
+
"$REPO/../node_modules/.bin/alphaclaw"
|
|
94
|
+
)
|
|
95
|
+
local candidate
|
|
96
|
+
for candidate in "${candidate_paths[@]}"; do
|
|
97
|
+
if [[ -x "$candidate" ]]; then
|
|
98
|
+
echo "$candidate"
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
done
|
|
102
|
+
|
|
103
|
+
return 1
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
msg="Auto-commit hourly sync $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
|
85
|
-
|
|
107
|
+
alphaclaw_cmd="$(resolve_alphaclaw_cmd || true)"
|
|
108
|
+
if [[ -z "${alphaclaw_cmd:-}" ]]; then
|
|
109
|
+
echo "hourly-git-sync: alphaclaw CLI not found in PATH or known install paths" >&2
|
|
110
|
+
exit 127
|
|
111
|
+
fi
|
|
112
|
+
"$alphaclaw_cmd" git-sync -m "$msg"
|
package/package.json
CHANGED
|
@@ -1,228 +0,0 @@
|
|
|
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 { ConfirmDialog } from "./confirm-dialog.js";
|
|
16
|
-
import { showToast } from "./toast.js";
|
|
17
|
-
const html = htm.bind(h);
|
|
18
|
-
|
|
19
|
-
export function Google({ gatewayStatus }) {
|
|
20
|
-
const [google, setGoogle] = useState(null);
|
|
21
|
-
const [scopes, setScopes] = useState(getDefaultScopes());
|
|
22
|
-
const [savedScopes, setSavedScopes] = useState(null);
|
|
23
|
-
const [apiStatus, setApiStatus] = useState({});
|
|
24
|
-
const [checkingApis, setCheckingApis] = useState(false);
|
|
25
|
-
const [modalOpen, setModalOpen] = useState(false);
|
|
26
|
-
const [disconnectDialogOpen, setDisconnectDialogOpen] = useState(false);
|
|
27
|
-
|
|
28
|
-
const runApiCheck = useCallback(async () => {
|
|
29
|
-
setApiStatus({});
|
|
30
|
-
setCheckingApis(true);
|
|
31
|
-
try {
|
|
32
|
-
const check = await checkApis();
|
|
33
|
-
if (check.results) setApiStatus(check.results);
|
|
34
|
-
} finally {
|
|
35
|
-
setCheckingApis(false);
|
|
36
|
-
}
|
|
37
|
-
}, []);
|
|
38
|
-
|
|
39
|
-
const refresh = useCallback(async () => {
|
|
40
|
-
try {
|
|
41
|
-
const data = await fetchGoogleStatus();
|
|
42
|
-
setGoogle(data);
|
|
43
|
-
if (data.activeScopes && data.activeScopes.length > 0) {
|
|
44
|
-
setScopes(data.activeScopes);
|
|
45
|
-
setSavedScopes(data.activeScopes);
|
|
46
|
-
}
|
|
47
|
-
if (data.authenticated) {
|
|
48
|
-
await runApiCheck();
|
|
49
|
-
}
|
|
50
|
-
} catch {}
|
|
51
|
-
}, [runApiCheck]);
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
refresh();
|
|
55
|
-
}, [refresh]);
|
|
56
|
-
|
|
57
|
-
// First load can race gateway startup; refresh when gateway becomes healthy.
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (gatewayStatus === "running") {
|
|
60
|
-
refresh();
|
|
61
|
-
}
|
|
62
|
-
}, [gatewayStatus, refresh]);
|
|
63
|
-
|
|
64
|
-
// Listen for OAuth popup postMessage
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
const handler = async (e) => {
|
|
67
|
-
if (e.data?.google === "success") {
|
|
68
|
-
showToast("✓ Google account connected", "success");
|
|
69
|
-
setApiStatus({});
|
|
70
|
-
await refresh();
|
|
71
|
-
} else if (e.data?.google === "error") {
|
|
72
|
-
showToast(
|
|
73
|
-
"✗ Google auth failed: " + (e.data.message || "unknown"),
|
|
74
|
-
"error",
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
window.addEventListener("message", handler);
|
|
79
|
-
return () => window.removeEventListener("message", handler);
|
|
80
|
-
}, [refresh]);
|
|
81
|
-
|
|
82
|
-
const handleToggle = (scope) => {
|
|
83
|
-
setScopes((prev) => toggleScopeLogic(prev, scope));
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const startAuth = (email) => {
|
|
87
|
-
if (scopes.length === 0) {
|
|
88
|
-
alert("Select at least one service");
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const authUrl = `/auth/google/start?email=${encodeURIComponent(email)}&services=${scopes.join(",")}&_ts=${Date.now()}`;
|
|
92
|
-
const popup = window.open(
|
|
93
|
-
authUrl,
|
|
94
|
-
"google-auth",
|
|
95
|
-
"popup=yes,width=500,height=700",
|
|
96
|
-
);
|
|
97
|
-
if (!popup || popup.closed) window.location.href = authUrl;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const handleCheckApis = () => runApiCheck();
|
|
101
|
-
|
|
102
|
-
const handleDisconnect = async () => {
|
|
103
|
-
const data = await apiDisconnect();
|
|
104
|
-
if (data.ok) {
|
|
105
|
-
setGoogle({
|
|
106
|
-
hasCredentials: false,
|
|
107
|
-
authenticated: false,
|
|
108
|
-
email: "",
|
|
109
|
-
services: "",
|
|
110
|
-
activeScopes: [],
|
|
111
|
-
});
|
|
112
|
-
setApiStatus({});
|
|
113
|
-
setScopes(getDefaultScopes());
|
|
114
|
-
showToast("Google account disconnected", "success");
|
|
115
|
-
} else {
|
|
116
|
-
alert("Failed to disconnect: " + (data.error || "unknown"));
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
if (!google) {
|
|
121
|
-
return html` <div class="bg-surface border border-border rounded-xl p-4">
|
|
122
|
-
<h2 class="card-label mb-3">Google Workspace</h2>
|
|
123
|
-
<div class="text-gray-500 text-sm text-center py-2">Loading...</div>
|
|
124
|
-
</div>`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const hasCredentials = google.authenticated || google.hasCredentials;
|
|
128
|
-
const isAuthed = google.authenticated;
|
|
129
|
-
const email = google.email || "";
|
|
130
|
-
const scopesChanged =
|
|
131
|
-
!savedScopes ||
|
|
132
|
-
scopes.length !== savedScopes.length ||
|
|
133
|
-
scopes.some((s) => !savedScopes.includes(s));
|
|
134
|
-
|
|
135
|
-
return html`
|
|
136
|
-
<div class="bg-surface border border-border rounded-xl p-4">
|
|
137
|
-
<h2 class="card-label mb-3">Google Workspace</h2>
|
|
138
|
-
${hasCredentials
|
|
139
|
-
? html`
|
|
140
|
-
<div class="space-y-3">
|
|
141
|
-
<div class="flex justify-between items-center">
|
|
142
|
-
<div class="text-sm font-medium">${email}</div>
|
|
143
|
-
${isAuthed
|
|
144
|
-
? html`<span
|
|
145
|
-
class="text-xs px-2 py-0.5 rounded-full font-medium bg-green-500/10 text-green-500"
|
|
146
|
-
>Connected</span
|
|
147
|
-
>`
|
|
148
|
-
: html`<span
|
|
149
|
-
class="text-xs px-2 py-0.5 rounded-full font-medium bg-yellow-500/10 text-yellow-500"
|
|
150
|
-
>Awaiting sign-in</span
|
|
151
|
-
>`}
|
|
152
|
-
</div>
|
|
153
|
-
<div class="flex justify-between items-center">
|
|
154
|
-
<span class="text-sm text-gray-400">Select permissions</span>
|
|
155
|
-
${isAuthed &&
|
|
156
|
-
html`<button
|
|
157
|
-
onclick=${handleCheckApis}
|
|
158
|
-
class="text-xs px-2 py-1 rounded-lg ac-btn-ghost"
|
|
159
|
-
>
|
|
160
|
-
↻ Check APIs
|
|
161
|
-
</button>`}
|
|
162
|
-
</div>
|
|
163
|
-
<${ScopePicker}
|
|
164
|
-
scopes=${scopes}
|
|
165
|
-
onToggle=${handleToggle}
|
|
166
|
-
apiStatus=${isAuthed ? apiStatus : {}}
|
|
167
|
-
loading=${isAuthed && checkingApis}
|
|
168
|
-
/>
|
|
169
|
-
<div class="pt-1 space-y-2 sm:space-y-0 sm:flex sm:justify-between sm:items-center">
|
|
170
|
-
<div class="grid grid-cols-2 gap-2 w-full sm:w-auto sm:flex sm:items-center">
|
|
171
|
-
<button
|
|
172
|
-
onclick=${() => startAuth(email)}
|
|
173
|
-
disabled=${isAuthed && !scopesChanged}
|
|
174
|
-
class="w-full sm:w-auto text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
|
|
175
|
-
>
|
|
176
|
-
${isAuthed ? "Update Permissions" : "Sign in with Google"}
|
|
177
|
-
</button>
|
|
178
|
-
<button
|
|
179
|
-
type="button"
|
|
180
|
-
onclick=${() => setModalOpen(true)}
|
|
181
|
-
class="w-full sm:w-auto text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary"
|
|
182
|
-
>
|
|
183
|
-
Edit credentials
|
|
184
|
-
</button>
|
|
185
|
-
</div>
|
|
186
|
-
<button
|
|
187
|
-
onclick=${() => setDisconnectDialogOpen(true)}
|
|
188
|
-
class="text-xs px-2 py-1 rounded-lg ac-btn-ghost w-full sm:w-auto"
|
|
189
|
-
>
|
|
190
|
-
Disconnect
|
|
191
|
-
</button>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
`
|
|
195
|
-
: html`
|
|
196
|
-
<div class="text-center space-y-2 py-1">
|
|
197
|
-
<p class="text-xs text-gray-500">
|
|
198
|
-
Connect Gmail, Calendar, Contacts, Drive, Sheets, Tasks, Docs,
|
|
199
|
-
and Meet.
|
|
200
|
-
</p>
|
|
201
|
-
<button
|
|
202
|
-
onclick=${() => setModalOpen(true)}
|
|
203
|
-
class="text-sm font-medium px-4 py-2 rounded-lg ac-btn-cyan"
|
|
204
|
-
>
|
|
205
|
-
Set up Google
|
|
206
|
-
</button>
|
|
207
|
-
</div>
|
|
208
|
-
`}
|
|
209
|
-
</div>
|
|
210
|
-
<${CredentialsModal}
|
|
211
|
-
visible=${modalOpen}
|
|
212
|
-
onClose=${() => setModalOpen(false)}
|
|
213
|
-
onSaved=${refresh}
|
|
214
|
-
/>
|
|
215
|
-
<${ConfirmDialog}
|
|
216
|
-
visible=${disconnectDialogOpen}
|
|
217
|
-
title="Disconnect Google account?"
|
|
218
|
-
message="Your agent will lose access to Gmail, Calendar, and other Google Workspace services until you reconnect."
|
|
219
|
-
confirmLabel="Disconnect"
|
|
220
|
-
cancelLabel="Cancel"
|
|
221
|
-
onCancel=${() => setDisconnectDialogOpen(false)}
|
|
222
|
-
onConfirm=${async () => {
|
|
223
|
-
setDisconnectDialogOpen(false);
|
|
224
|
-
await handleDisconnect();
|
|
225
|
-
}}
|
|
226
|
-
/>
|
|
227
|
-
`;
|
|
228
|
-
}
|