@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.
Files changed (37) hide show
  1. package/bin/alphaclaw.js +1 -31
  2. package/lib/public/assets/icons/google_icon.svg +8 -0
  3. package/lib/public/css/explorer.css +53 -0
  4. package/lib/public/js/app.js +126 -105
  5. package/lib/public/js/components/credentials-modal.js +36 -8
  6. package/lib/public/js/components/file-tree.js +212 -22
  7. package/lib/public/js/components/file-viewer/index.js +44 -6
  8. package/lib/public/js/components/file-viewer/status-banners.js +11 -6
  9. package/lib/public/js/components/file-viewer/toolbar.js +43 -1
  10. package/lib/public/js/components/file-viewer/use-editor-selection-restore.js +6 -0
  11. package/lib/public/js/components/file-viewer/use-file-diff.js +11 -0
  12. package/lib/public/js/components/file-viewer/use-file-loader.js +12 -2
  13. package/lib/public/js/components/file-viewer/use-file-viewer.js +94 -2
  14. package/lib/public/js/components/google/account-row.js +98 -0
  15. package/lib/public/js/components/google/add-account-modal.js +93 -0
  16. package/lib/public/js/components/google/index.js +439 -0
  17. package/lib/public/js/components/google/use-google-accounts.js +41 -0
  18. package/lib/public/js/components/icons.js +26 -0
  19. package/lib/public/js/components/sidebar-git-panel.js +43 -14
  20. package/lib/public/js/components/sidebar.js +91 -75
  21. package/lib/public/js/lib/api.js +72 -8
  22. package/lib/public/js/lib/browse-file-policies.js +29 -11
  23. package/lib/public/js/lib/syntax-highlighters/index.js +6 -5
  24. package/lib/public/shared/browse-file-policies.json +13 -0
  25. package/lib/server/constants.js +19 -7
  26. package/lib/server/google-state.js +187 -0
  27. package/lib/server/helpers.js +12 -4
  28. package/lib/server/onboarding/github.js +21 -2
  29. package/lib/server/onboarding/index.js +1 -3
  30. package/lib/server/onboarding/openclaw.js +3 -0
  31. package/lib/server/onboarding/workspace.js +40 -0
  32. package/lib/server/routes/browse/index.js +90 -2
  33. package/lib/server/routes/google.js +414 -213
  34. package/lib/setup/gitignore +3 -0
  35. package/lib/setup/hourly-git-sync.sh +28 -1
  36. package/package.json +1 -1
  37. package/lib/public/js/components/google.js +0 -228
@@ -6,6 +6,9 @@
6
6
  !workspace/**
7
7
  workspace/.openclaw/
8
8
  workspace/.openclaw/**
9
+ !gogcli/
10
+ gogcli/*
11
+ !gogcli/state.json
9
12
  db/
10
13
  db/**
11
14
  !skills/
@@ -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
- alphaclaw git-sync -m "$msg"
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.3.5-beta.1",
3
+ "version": "0.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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
- }