@chrysb/alphaclaw 0.5.6 → 0.6.0-beta.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 +6 -1
- package/lib/public/css/agents.css +92 -0
- package/lib/public/css/explorer.css +101 -0
- package/lib/public/css/shell.css +15 -4
- package/lib/public/js/app.js +69 -3
- package/lib/public/js/components/action-button.js +5 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/helpers.js +76 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/index.js +490 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/use-agent-bindings.js +256 -0
- package/lib/public/js/components/agents-tab/agent-detail-panel.js +74 -0
- package/lib/public/js/components/agents-tab/agent-identity-section.js +175 -0
- package/lib/public/js/components/agents-tab/agent-overview/index.js +53 -0
- package/lib/public/js/components/agents-tab/agent-overview/manage-card.js +44 -0
- package/lib/public/js/components/agents-tab/agent-overview/model-card.js +158 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +169 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-workspace-card.js +45 -0
- package/lib/public/js/components/agents-tab/agent-overview/workspace-card.js +47 -0
- package/lib/public/js/components/agents-tab/agent-pairing-section.js +265 -0
- package/lib/public/js/components/agents-tab/create-agent-modal.js +189 -0
- package/lib/public/js/components/agents-tab/create-channel-modal.js +323 -0
- package/lib/public/js/components/agents-tab/delete-agent-dialog.js +50 -0
- package/lib/public/js/components/agents-tab/edit-agent-modal.js +109 -0
- package/lib/public/js/components/agents-tab/index.js +148 -0
- package/lib/public/js/components/agents-tab/use-agents.js +89 -0
- package/lib/public/js/components/channel-account-status-badge.js +35 -0
- package/lib/public/js/components/channel-operations-panel.js +33 -0
- package/lib/public/js/components/channels.js +545 -60
- package/lib/public/js/components/envars.js +25 -4
- package/lib/public/js/components/general/index.js +21 -11
- package/lib/public/js/components/general/use-general-tab.js +78 -16
- package/lib/public/js/components/google/gmail-setup-wizard.js +1 -3
- package/lib/public/js/components/google/index.js +28 -30
- package/lib/public/js/components/icons.js +37 -0
- package/lib/public/js/components/models-tab/index.js +58 -224
- package/lib/public/js/components/models-tab/model-picker.js +212 -0
- package/lib/public/js/components/models-tab/use-models.js +17 -14
- package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -4
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
- package/lib/public/js/components/overflow-menu.js +122 -0
- package/lib/public/js/components/pairings.js +36 -8
- package/lib/public/js/components/routes/agents-route.js +27 -0
- package/lib/public/js/components/routes/general-route.js +2 -0
- package/lib/public/js/components/routes/index.js +1 -0
- package/lib/public/js/components/routes/telegram-route.js +2 -2
- package/lib/public/js/components/secret-input.js +8 -1
- package/lib/public/js/components/sidebar.js +64 -26
- package/lib/public/js/components/telegram-workspace/index.js +175 -74
- package/lib/public/js/components/telegram-workspace/manage.js +83 -10
- package/lib/public/js/components/telegram-workspace/onboarding.js +9 -8
- package/lib/public/js/components/webhooks.js +43 -18
- package/lib/public/js/hooks/use-app-shell-controller.js +7 -0
- package/lib/public/js/hooks/use-browse-navigation.js +8 -5
- package/lib/public/js/hooks/use-destination-session-selection.js +8 -1
- package/lib/public/js/lib/api.js +163 -9
- package/lib/public/js/lib/app-navigation.js +2 -1
- package/lib/public/js/lib/channel-create-operation.js +102 -0
- package/lib/public/js/lib/format.js +14 -0
- package/lib/public/js/lib/sse.js +51 -0
- package/lib/public/js/lib/telegram-api.js +38 -18
- package/lib/public/setup.html +1 -0
- package/lib/public/shared/browse-file-policies.json +0 -1
- package/lib/server/agents/service.js +1478 -0
- package/lib/server/constants.js +2 -2
- package/lib/server/env.js +3 -1
- package/lib/server/gateway.js +104 -20
- package/lib/server/gmail-watch.js +29 -2
- package/lib/server/onboarding/import/import-applier.js +0 -1
- package/lib/server/onboarding/index.js +0 -6
- package/lib/server/onboarding/workspace.js +73 -38
- package/lib/server/openclaw-config.js +23 -0
- package/lib/server/operation-events.js +141 -0
- package/lib/server/routes/agents.js +266 -0
- package/lib/server/routes/pairings.js +135 -25
- package/lib/server/routes/system.js +90 -10
- package/lib/server/routes/telegram.js +247 -51
- package/lib/server/telegram-workspace.js +61 -10
- package/lib/server/topic-registry.js +66 -7
- package/lib/server/watchdog.js +39 -1
- package/lib/server/webhooks.js +60 -12
- package/lib/server.js +21 -7
- package/lib/setup/core-prompts/AGENTS.md +6 -5
- package/lib/setup/core-prompts/TOOLS.md +1 -8
- package/package.json +1 -1
- package/lib/setup/skills/control-ui/SKILL.md +0 -62
|
@@ -2,6 +2,14 @@ const fs = require("fs");
|
|
|
2
2
|
const { WORKSPACE_DIR } = require("./constants");
|
|
3
3
|
|
|
4
4
|
const kRegistryPath = `${WORKSPACE_DIR}/topic-registry.json`;
|
|
5
|
+
const kDefaultAccountId = "default";
|
|
6
|
+
const kDefaultAgentId = "default";
|
|
7
|
+
|
|
8
|
+
const normalizeAccountId = (value) =>
|
|
9
|
+
String(value || "").trim() || kDefaultAccountId;
|
|
10
|
+
|
|
11
|
+
const normalizeGroupAgentId = (value) =>
|
|
12
|
+
String(value || "").trim() || kDefaultAgentId;
|
|
5
13
|
|
|
6
14
|
const readRegistry = () => {
|
|
7
15
|
try {
|
|
@@ -36,6 +44,20 @@ const setGroup = (groupId, groupData) => {
|
|
|
36
44
|
return registry;
|
|
37
45
|
};
|
|
38
46
|
|
|
47
|
+
const getGroupsForAccount = (accountId) => {
|
|
48
|
+
const registry = readRegistry();
|
|
49
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
50
|
+
const groups = registry.groups && typeof registry.groups === "object"
|
|
51
|
+
? registry.groups
|
|
52
|
+
: {};
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
Object.entries(groups).filter(([, group]) => {
|
|
55
|
+
const groupAccountId = normalizeAccountId(group?.accountId);
|
|
56
|
+
return groupAccountId === normalizedAccountId;
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
39
61
|
const addTopic = (groupId, threadId, topicData) => {
|
|
40
62
|
const registry = readRegistry();
|
|
41
63
|
if (!registry.groups[groupId]) {
|
|
@@ -90,21 +112,56 @@ const getTotalTopicCount = () => {
|
|
|
90
112
|
return count;
|
|
91
113
|
};
|
|
92
114
|
|
|
93
|
-
|
|
94
|
-
const renderTopicRegistryMarkdown = ({ includeSyncGuidance = false } = {}) => {
|
|
115
|
+
const getTopicsForAgent = (agentId) => {
|
|
95
116
|
const registry = readRegistry();
|
|
117
|
+
const groups = registry.groups && typeof registry.groups === "object"
|
|
118
|
+
? registry.groups
|
|
119
|
+
: {};
|
|
120
|
+
const normalizedAgentId = normalizeGroupAgentId(agentId);
|
|
96
121
|
const rows = [];
|
|
97
|
-
for (const [groupId, group] of Object.entries(
|
|
98
|
-
|
|
122
|
+
for (const [groupId, group] of Object.entries(groups)) {
|
|
123
|
+
const groupAgentId = normalizeGroupAgentId(group?.agentId);
|
|
124
|
+
const groupName = String(group?.name || "").trim() || groupId;
|
|
125
|
+
const topics = group?.topics && typeof group.topics === "object"
|
|
126
|
+
? group.topics
|
|
127
|
+
: {};
|
|
128
|
+
const isGroupOwner = groupAgentId === normalizedAgentId;
|
|
129
|
+
for (const [threadId, topic] of Object.entries(topics)) {
|
|
130
|
+
const topicAgentId = String(topic?.agentId || "").trim();
|
|
131
|
+
if (!isGroupOwner && topicAgentId !== normalizedAgentId) continue;
|
|
99
132
|
rows.push({
|
|
100
|
-
groupName
|
|
133
|
+
groupName,
|
|
101
134
|
groupId,
|
|
102
|
-
topicName: topic
|
|
135
|
+
topicName: topic?.name,
|
|
103
136
|
threadId,
|
|
137
|
+
groupAgentId,
|
|
138
|
+
topicAgentId,
|
|
104
139
|
});
|
|
105
140
|
}
|
|
106
141
|
}
|
|
107
|
-
|
|
142
|
+
return rows;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Render the topic registry as a markdown section for TOOLS.md
|
|
146
|
+
const renderTopicRegistryMarkdown = ({
|
|
147
|
+
includeSyncGuidance = false,
|
|
148
|
+
agentId = "",
|
|
149
|
+
} = {}) => {
|
|
150
|
+
const registry = readRegistry();
|
|
151
|
+
const groups = registry.groups && typeof registry.groups === "object"
|
|
152
|
+
? registry.groups
|
|
153
|
+
: {};
|
|
154
|
+
const normalizedAgentId = String(agentId || "").trim();
|
|
155
|
+
const rows = normalizedAgentId
|
|
156
|
+
? getTopicsForAgent(normalizedAgentId)
|
|
157
|
+
: Object.entries(groups).flatMap(([groupId, group]) =>
|
|
158
|
+
Object.entries(group.topics || {}).map(([threadId, topic]) => ({
|
|
159
|
+
groupName: group.name || groupId,
|
|
160
|
+
groupId,
|
|
161
|
+
topicName: topic.name,
|
|
162
|
+
threadId,
|
|
163
|
+
})));
|
|
164
|
+
if (rows.length === 0) return "";
|
|
108
165
|
|
|
109
166
|
const lines = [
|
|
110
167
|
"",
|
|
@@ -144,9 +201,11 @@ module.exports = {
|
|
|
144
201
|
writeRegistry,
|
|
145
202
|
getGroup,
|
|
146
203
|
setGroup,
|
|
204
|
+
getGroupsForAccount,
|
|
147
205
|
addTopic,
|
|
148
206
|
updateTopic,
|
|
149
207
|
removeTopic,
|
|
150
208
|
getTotalTopicCount,
|
|
209
|
+
getTopicsForAgent,
|
|
151
210
|
renderTopicRegistryMarkdown,
|
|
152
211
|
};
|
package/lib/server/watchdog.js
CHANGED
|
@@ -44,6 +44,18 @@ const parseHealthResult = (result) => {
|
|
|
44
44
|
}
|
|
45
45
|
return { ok: true };
|
|
46
46
|
};
|
|
47
|
+
const isDuplicateGatewayLaunchExit = ({ code, stderrTail = [] } = {}) => {
|
|
48
|
+
if (code !== 1) return false;
|
|
49
|
+
const stderrText = (Array.isArray(stderrTail) ? stderrTail : [])
|
|
50
|
+
.map((entry) => String(entry || ""))
|
|
51
|
+
.join("\n")
|
|
52
|
+
.toLowerCase();
|
|
53
|
+
if (!stderrText) return false;
|
|
54
|
+
return (
|
|
55
|
+
stderrText.includes("another gateway instance is already listening") ||
|
|
56
|
+
(stderrText.includes("port") && stderrText.includes("already in use"))
|
|
57
|
+
);
|
|
58
|
+
};
|
|
47
59
|
|
|
48
60
|
const createWatchdog = ({
|
|
49
61
|
clawCmd,
|
|
@@ -593,6 +605,7 @@ const createWatchdog = ({
|
|
|
593
605
|
if (expectedExit && (code == null || code === 0)) {
|
|
594
606
|
state.lifecycle = "restarting";
|
|
595
607
|
state.health = "unknown";
|
|
608
|
+
state.uptimeStartedAt = null;
|
|
596
609
|
state.crashRecoveryActive = false;
|
|
597
610
|
markExpectedRestartWindow();
|
|
598
611
|
startBootstrapHealthChecks();
|
|
@@ -605,9 +618,33 @@ const createWatchdog = ({
|
|
|
605
618
|
);
|
|
606
619
|
return;
|
|
607
620
|
}
|
|
621
|
+
if (isDuplicateGatewayLaunchExit({ code, stderrTail })) {
|
|
622
|
+
state.lifecycle = "running";
|
|
623
|
+
state.health = "unknown";
|
|
624
|
+
state.crashRecoveryActive = false;
|
|
625
|
+
state.startupConsecutiveHealthFailures = 0;
|
|
626
|
+
if (!state.uptimeStartedAt) {
|
|
627
|
+
state.uptimeStartedAt = Date.now();
|
|
628
|
+
}
|
|
629
|
+
startBootstrapHealthChecks();
|
|
630
|
+
logEvent(
|
|
631
|
+
"restart",
|
|
632
|
+
"exit_event",
|
|
633
|
+
"ok",
|
|
634
|
+
{
|
|
635
|
+
duplicateLaunch: true,
|
|
636
|
+
code: code ?? null,
|
|
637
|
+
signal: signal ?? null,
|
|
638
|
+
stderrTail,
|
|
639
|
+
},
|
|
640
|
+
correlationId,
|
|
641
|
+
);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
608
644
|
|
|
609
645
|
state.lifecycle = "crashed";
|
|
610
646
|
state.health = "unhealthy";
|
|
647
|
+
state.uptimeStartedAt = null;
|
|
611
648
|
state.crashRecoveryActive = true;
|
|
612
649
|
state.crashTimestamps.push(Date.now());
|
|
613
650
|
trimCrashWindow();
|
|
@@ -677,6 +714,7 @@ const createWatchdog = ({
|
|
|
677
714
|
clearDegradedHealthCheckTimer();
|
|
678
715
|
state.lifecycle = "restarting";
|
|
679
716
|
state.health = "unknown";
|
|
717
|
+
state.uptimeStartedAt = null;
|
|
680
718
|
state.startupConsecutiveHealthFailures = 0;
|
|
681
719
|
state.crashRecoveryActive = false;
|
|
682
720
|
markExpectedRestartWindow();
|
|
@@ -698,7 +736,6 @@ const createWatchdog = ({
|
|
|
698
736
|
state.lifecycle = "running";
|
|
699
737
|
state.health = "unknown";
|
|
700
738
|
state.startupConsecutiveHealthFailures = 0;
|
|
701
|
-
state.uptimeStartedAt = Date.now();
|
|
702
739
|
state.gatewayStartedAt = Date.now();
|
|
703
740
|
startBootstrapHealthChecks();
|
|
704
741
|
};
|
|
@@ -714,6 +751,7 @@ const createWatchdog = ({
|
|
|
714
751
|
healthTimer = null;
|
|
715
752
|
}
|
|
716
753
|
state.lifecycle = "stopped";
|
|
754
|
+
state.uptimeStartedAt = null;
|
|
717
755
|
state.startupConsecutiveHealthFailures = 0;
|
|
718
756
|
};
|
|
719
757
|
|
package/lib/server/webhooks.js
CHANGED
|
@@ -99,11 +99,16 @@ const normalizeDestination = (destination = null) => {
|
|
|
99
99
|
if (!destination || typeof destination !== "object") return null;
|
|
100
100
|
const channel = String(destination?.channel || "").trim();
|
|
101
101
|
const to = String(destination?.to || "").trim();
|
|
102
|
+
const agentId = String(destination?.agentId || "").trim();
|
|
102
103
|
if (!channel && !to) return null;
|
|
103
104
|
if (!channel || !to) {
|
|
104
105
|
throw new Error("destination.channel and destination.to are required");
|
|
105
106
|
}
|
|
106
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
channel,
|
|
109
|
+
to,
|
|
110
|
+
...(agentId ? { agentId } : {}),
|
|
111
|
+
};
|
|
107
112
|
};
|
|
108
113
|
|
|
109
114
|
const resolveTransformPathFromMapping = (name, mapping) => {
|
|
@@ -137,8 +142,7 @@ const normalizeMappingTransformModules = (mappings) => {
|
|
|
137
142
|
return changed;
|
|
138
143
|
};
|
|
139
144
|
|
|
140
|
-
const buildDefaultTransformSource = (name
|
|
141
|
-
const normalizedDestination = normalizeDestination(destination);
|
|
145
|
+
const buildDefaultTransformSource = (name) => {
|
|
142
146
|
return [
|
|
143
147
|
"export default async function transform(payload, context) {",
|
|
144
148
|
" const data = payload.payload || payload;",
|
|
@@ -146,12 +150,6 @@ const buildDefaultTransformSource = (name, destination = null) => {
|
|
|
146
150
|
" message: data.message,",
|
|
147
151
|
` name: data.name || "${name}",`,
|
|
148
152
|
' wakeMode: data.wakeMode || "now",',
|
|
149
|
-
...(normalizedDestination
|
|
150
|
-
? [
|
|
151
|
-
` channel: ${JSON.stringify(normalizedDestination.channel)},`,
|
|
152
|
-
` to: ${JSON.stringify(normalizedDestination.to)},`,
|
|
153
|
-
]
|
|
154
|
-
: []),
|
|
155
153
|
" };",
|
|
156
154
|
"}",
|
|
157
155
|
"",
|
|
@@ -164,6 +162,7 @@ const ensureWebhookTransform = ({
|
|
|
164
162
|
name,
|
|
165
163
|
source = "",
|
|
166
164
|
destination = null,
|
|
165
|
+
forceWrite = false,
|
|
167
166
|
}) => {
|
|
168
167
|
const webhookName = validateWebhookName(name);
|
|
169
168
|
const transformAbsolutePath = getTransformAbsolutePath(
|
|
@@ -171,14 +170,14 @@ const ensureWebhookTransform = ({
|
|
|
171
170
|
webhookName,
|
|
172
171
|
);
|
|
173
172
|
fs.mkdirSync(path.dirname(transformAbsolutePath), { recursive: true });
|
|
174
|
-
if (fs.existsSync(transformAbsolutePath)) {
|
|
173
|
+
if (fs.existsSync(transformAbsolutePath) && !forceWrite) {
|
|
175
174
|
return { changed: false, path: transformAbsolutePath };
|
|
176
175
|
}
|
|
177
176
|
fs.writeFileSync(
|
|
178
177
|
transformAbsolutePath,
|
|
179
178
|
String(source || "").trim()
|
|
180
179
|
? `${String(source).replace(/\s+$/, "")}\n`
|
|
181
|
-
: buildDefaultTransformSource(webhookName
|
|
180
|
+
: buildDefaultTransformSource(webhookName),
|
|
182
181
|
);
|
|
183
182
|
return { changed: true, path: transformAbsolutePath };
|
|
184
183
|
};
|
|
@@ -235,6 +234,27 @@ const ensureWebhookMapping = ({ cfg, name, mapping = {} }) => {
|
|
|
235
234
|
};
|
|
236
235
|
};
|
|
237
236
|
|
|
237
|
+
const resolveDefaultAgentId = (cfg) => {
|
|
238
|
+
const agents = Array.isArray(cfg?.agents?.list) ? cfg.agents.list : [];
|
|
239
|
+
const explicitDefault = agents.find((entry) => !!entry?.default);
|
|
240
|
+
const defaultId = String(explicitDefault?.id || "").trim();
|
|
241
|
+
if (defaultId) return defaultId;
|
|
242
|
+
const firstId = String(agents[0]?.id || "").trim();
|
|
243
|
+
return firstId || "main";
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const resolveWebhookAgentId = ({ cfg, requestedAgentId = "" }) => {
|
|
247
|
+
const normalizedRequested = String(requestedAgentId || "").trim();
|
|
248
|
+
const agents = Array.isArray(cfg?.agents?.list) ? cfg.agents.list : [];
|
|
249
|
+
if (
|
|
250
|
+
normalizedRequested &&
|
|
251
|
+
agents.some((entry) => String(entry?.id || "").trim() === normalizedRequested)
|
|
252
|
+
) {
|
|
253
|
+
return normalizedRequested;
|
|
254
|
+
}
|
|
255
|
+
return resolveDefaultAgentId(cfg);
|
|
256
|
+
};
|
|
257
|
+
|
|
238
258
|
const listManagedWebhooksFromConfig = ({ cfg }) => {
|
|
239
259
|
const presets = Array.isArray(cfg?.hooks?.presets) ? cfg.hooks.presets : [];
|
|
240
260
|
return kManagedWebhookConfigs
|
|
@@ -289,6 +309,10 @@ const listWebhooks = ({ fs, constants }) => {
|
|
|
289
309
|
path: `/hooks/${name}`,
|
|
290
310
|
transformPath,
|
|
291
311
|
transformExists: fs.existsSync(transformAbsolutePath),
|
|
312
|
+
deliver: Boolean(mapping?.deliver),
|
|
313
|
+
channel: String(mapping?.channel || "").trim(),
|
|
314
|
+
to: String(mapping?.to || "").trim(),
|
|
315
|
+
agentId: String(mapping?.agentId || "").trim(),
|
|
292
316
|
managed: Boolean(managed),
|
|
293
317
|
managedReason: managed?.managedReason || "",
|
|
294
318
|
};
|
|
@@ -332,6 +356,7 @@ const createWebhook = ({
|
|
|
332
356
|
mapping = {},
|
|
333
357
|
transformSource = "",
|
|
334
358
|
destination = null,
|
|
359
|
+
overwriteTransform = false,
|
|
335
360
|
}) => {
|
|
336
361
|
const webhookName = validateWebhookName(name);
|
|
337
362
|
const normalizedDestination = normalizeDestination(destination);
|
|
@@ -346,10 +371,32 @@ const createWebhook = ({
|
|
|
346
371
|
if (exists && !upsert) {
|
|
347
372
|
throw new Error(`Webhook "${webhookName}" already exists`);
|
|
348
373
|
}
|
|
374
|
+
const agentId = resolveWebhookAgentId({
|
|
375
|
+
cfg,
|
|
376
|
+
requestedAgentId:
|
|
377
|
+
String(mapping?.agentId || "").trim() ||
|
|
378
|
+
String(normalizedDestination?.agentId || "").trim(),
|
|
379
|
+
});
|
|
380
|
+
const resolvedMapping = {
|
|
381
|
+
...mapping,
|
|
382
|
+
deliver: true,
|
|
383
|
+
channel:
|
|
384
|
+
String(mapping?.channel || "").trim() ||
|
|
385
|
+
String(normalizedDestination?.channel || "").trim() ||
|
|
386
|
+
"last",
|
|
387
|
+
...(String(mapping?.to || "").trim() || String(normalizedDestination?.to || "").trim()
|
|
388
|
+
? {
|
|
389
|
+
to:
|
|
390
|
+
String(mapping?.to || "").trim() ||
|
|
391
|
+
String(normalizedDestination?.to || "").trim(),
|
|
392
|
+
}
|
|
393
|
+
: {}),
|
|
394
|
+
agentId,
|
|
395
|
+
};
|
|
349
396
|
const ensuredMapping = ensureWebhookMapping({
|
|
350
397
|
cfg,
|
|
351
398
|
name: webhookName,
|
|
352
|
-
mapping,
|
|
399
|
+
mapping: resolvedMapping,
|
|
353
400
|
});
|
|
354
401
|
const ensuredTransform = ensureWebhookTransform({
|
|
355
402
|
fs,
|
|
@@ -357,6 +404,7 @@ const createWebhook = ({
|
|
|
357
404
|
name: webhookName,
|
|
358
405
|
source: transformSource,
|
|
359
406
|
destination: normalizedDestination,
|
|
407
|
+
forceWrite: overwriteTransform,
|
|
360
408
|
});
|
|
361
409
|
if (ensuredMapping.changed || ensuredTransform.changed || !exists) {
|
|
362
410
|
writeConfig({ fs, configPath, cfg });
|
package/lib/server.js
CHANGED
|
@@ -72,6 +72,7 @@ const {
|
|
|
72
72
|
isGatewayRunning,
|
|
73
73
|
startGateway,
|
|
74
74
|
restartGateway: restartGatewayWithReload,
|
|
75
|
+
restartGatewayLight: restartGatewayLightWithReload,
|
|
75
76
|
attachGatewaySignalHandlers,
|
|
76
77
|
ensureGatewayProxyConfig,
|
|
77
78
|
syncChannelConfig,
|
|
@@ -90,7 +91,6 @@ const {
|
|
|
90
91
|
} = require("./server/restart-required-state");
|
|
91
92
|
const {
|
|
92
93
|
ensureOpenclawRuntimeArtifacts,
|
|
93
|
-
installControlUiSkill,
|
|
94
94
|
resolveSetupUiUrl,
|
|
95
95
|
syncBootstrapPromptFiles,
|
|
96
96
|
} = require("./server/onboarding/workspace");
|
|
@@ -106,6 +106,8 @@ const { createDiscordApi } = require("./server/discord-api");
|
|
|
106
106
|
const { createWatchdogNotifier } = require("./server/watchdog-notify");
|
|
107
107
|
const { createWatchdog } = require("./server/watchdog");
|
|
108
108
|
const { createDoctorService } = require("./server/doctor/service");
|
|
109
|
+
const { createAgentsService } = require("./server/agents/service");
|
|
110
|
+
const { createOperationEventsService } = require("./server/operation-events");
|
|
109
111
|
const { runOnboardedBootSequence } = require("./server/startup");
|
|
110
112
|
|
|
111
113
|
const { registerAuthRoutes } = require("./server/routes/auth");
|
|
@@ -124,6 +126,7 @@ const { registerWatchdogRoutes } = require("./server/routes/watchdog");
|
|
|
124
126
|
const { registerUsageRoutes } = require("./server/routes/usage");
|
|
125
127
|
const { registerGmailRoutes } = require("./server/routes/gmail");
|
|
126
128
|
const { registerDoctorRoutes } = require("./server/routes/doctor");
|
|
129
|
+
const { registerAgentRoutes } = require("./server/routes/agents");
|
|
127
130
|
|
|
128
131
|
const { PORT, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
|
|
129
132
|
|
|
@@ -154,8 +157,17 @@ proxy.on("error", (err, req, res) => {
|
|
|
154
157
|
});
|
|
155
158
|
|
|
156
159
|
const authProfiles = createAuthProfiles();
|
|
157
|
-
const loginThrottle = { ...createLoginThrottle(), getClientKey };
|
|
158
160
|
const { shellCmd, clawCmd, gogCmd } = createCommands({ gatewayEnv });
|
|
161
|
+
const agentsService = createAgentsService({
|
|
162
|
+
fs,
|
|
163
|
+
OPENCLAW_DIR: constants.OPENCLAW_DIR,
|
|
164
|
+
readEnvFile,
|
|
165
|
+
writeEnvFile,
|
|
166
|
+
reloadEnv,
|
|
167
|
+
restartGateway: () => restartGatewayWithReload(reloadEnv),
|
|
168
|
+
clawCmd,
|
|
169
|
+
});
|
|
170
|
+
const loginThrottle = { ...createLoginThrottle(), getClientKey };
|
|
159
171
|
const resolveSetupUrl = () =>
|
|
160
172
|
resolveSetupUiUrl(
|
|
161
173
|
process.env.ALPHACLAW_SETUP_URL ||
|
|
@@ -171,6 +183,7 @@ const openclawVersionService = createOpenclawVersionService({
|
|
|
171
183
|
});
|
|
172
184
|
const alphaclawVersionService = createAlphaclawVersionService();
|
|
173
185
|
const restartRequiredState = createRestartRequiredState({ isGatewayRunning });
|
|
186
|
+
const operationEvents = createOperationEventsService();
|
|
174
187
|
|
|
175
188
|
const { requireAuth, isAuthorizedRequest } = registerAuthRoutes({
|
|
176
189
|
app,
|
|
@@ -328,11 +341,6 @@ const doSyncPromptFiles = () => {
|
|
|
328
341
|
workspaceDir: constants.WORKSPACE_DIR,
|
|
329
342
|
baseUrl: setupUiUrl,
|
|
330
343
|
});
|
|
331
|
-
installControlUiSkill({
|
|
332
|
-
fs,
|
|
333
|
-
openclawDir: constants.OPENCLAW_DIR,
|
|
334
|
-
baseUrl: setupUiUrl,
|
|
335
|
-
});
|
|
336
344
|
installGogCliSkill({ fs, openclawDir: constants.OPENCLAW_DIR });
|
|
337
345
|
};
|
|
338
346
|
registerTelegramRoutes({
|
|
@@ -375,6 +383,12 @@ registerDoctorRoutes({
|
|
|
375
383
|
requireAuth,
|
|
376
384
|
doctorService,
|
|
377
385
|
});
|
|
386
|
+
registerAgentRoutes({
|
|
387
|
+
app,
|
|
388
|
+
agentsService,
|
|
389
|
+
restartRequiredState,
|
|
390
|
+
operationEvents,
|
|
391
|
+
});
|
|
378
392
|
registerProxyRoutes({
|
|
379
393
|
app,
|
|
380
394
|
proxy,
|
|
@@ -21,14 +21,15 @@ Before diving into implementation, share your plan when the work is **significan
|
|
|
21
21
|
|
|
22
22
|
If any of these apply, outline your approach first — what you intend to do, in what order, and any trade-offs you see — then **wait for the user's sign-off** before proceeding. For straightforward, low-risk tasks, just get it done.
|
|
23
23
|
|
|
24
|
-
### Show Your Work (IMPORTANT)
|
|
24
|
+
### Save and Show Your Work (IMPORTANT)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Your `.openclaw` directory is version-controlled and this is how work survives container restarts.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Anytime you add, edit, or remove workspace files, openclaw.json, cron.json, skills, or external resources (third-party pages, databases, integrations), **commit and push your changes to git**. Never force push; always pull first if there might be remote changes.
|
|
29
|
+
|
|
30
|
+
Whenever you do this, end your message with a **Changes committed** summary. Use workspace-relative paths for local files.
|
|
29
31
|
|
|
30
32
|
```
|
|
31
|
-
Changes committed ([abc1234](commit url)): <-- linked
|
|
33
|
+
Changes committed ([abc1234](commit url)): <-- linked abbreviated hash, no backticks
|
|
32
34
|
• path/or/resource (new|edit|delete) — brief description
|
|
33
35
|
```
|
|
34
|
-
|
|
@@ -23,14 +23,7 @@ Changes to env vars are made through the **Envars** tab (`{{SETUP_UI_URL}}#envar
|
|
|
23
23
|
|
|
24
24
|
### Google Workspace
|
|
25
25
|
|
|
26
|
-
Google Workspace is connected via the **General** tab (`{{SETUP_UI_URL}}#general`). The user provides OAuth client credentials from Google Cloud Console, then authorizes access to the services they need (Gmail, Calendar, Drive, Sheets, Docs, Tasks, Contacts, Meet).
|
|
27
|
-
|
|
28
|
-
## Git Discipline
|
|
29
|
-
|
|
30
|
-
**Commit and push after every set of changes.** Your entire .openclaw directory (config, cron, workspace) is version controlled. This is how your work survives container restarts.
|
|
31
|
-
|
|
32
|
-
Never force push. Always pull before pushing if there might be remote changes.
|
|
33
|
-
After pushing, include a link to the commit using the abbreviated hash: [abc1234](https://github.com/owner/repo/commit/abc1234) format. No backticks.
|
|
26
|
+
Google Workspace is connected via the **General** tab (`{{SETUP_UI_URL}}#general`). The user provides OAuth client credentials from Google Cloud Console, then authorizes access to the services they need (Gmail, Calendar, Drive, Sheets, Docs, Tasks, Contacts, Meet). Connected accounts and `gog` CLI usage are covered by the gog-cli skill.
|
|
34
27
|
|
|
35
28
|
## Telegram Formatting
|
|
36
29
|
|
package/package.json
CHANGED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: alphaclaw
|
|
3
|
-
description: Know when and how to direct the user to the AlphaClaw UI for configuration tasks.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# AlphaClaw UI
|
|
7
|
-
|
|
8
|
-
There is a web-based Setup UI at `{{BASE_URL}}`. The **user** manages runtime configuration through it. You should NOT call these API endpoints yourself or write config files directly — instead, tell the user what they need to do and where to do it.
|
|
9
|
-
|
|
10
|
-
## When to direct the user to the UI
|
|
11
|
-
|
|
12
|
-
### Adding or changing environment variables
|
|
13
|
-
|
|
14
|
-
When the user needs to add a new API key, token, or any env var:
|
|
15
|
-
|
|
16
|
-
> You can add that in your Setup UI → **Envars** tab: {{BASE_URL}}#envars
|
|
17
|
-
|
|
18
|
-
### Connecting a new channel (Telegram, Discord)
|
|
19
|
-
|
|
20
|
-
> Add your bot token in the Setup UI → **Envars** tab ({{BASE_URL}}#envars), then approve the pairing request in the **General** tab ({{BASE_URL}}#general).
|
|
21
|
-
|
|
22
|
-
### Approving or rejecting pairings
|
|
23
|
-
|
|
24
|
-
When a user asks about pairing their Telegram or Discord account:
|
|
25
|
-
|
|
26
|
-
> Open the Setup UI → **General** tab ({{BASE_URL}}#general). Pending pairing requests appear automatically — click **Approve** or **Reject**.
|
|
27
|
-
|
|
28
|
-
### Connecting OpenAI Codex OAuth
|
|
29
|
-
|
|
30
|
-
> Connect or reconnect Codex OAuth from the Setup UI → **Providers** tab ({{BASE_URL}}#providers). Click **Connect Codex OAuth** and follow the popup flow.
|
|
31
|
-
|
|
32
|
-
### Connecting Google Workspace
|
|
33
|
-
|
|
34
|
-
> Set up Google Workspace from the Setup UI → **General** tab ({{BASE_URL}}#general, Google section). You'll need your OAuth client credentials from Google Cloud Console.
|
|
35
|
-
|
|
36
|
-
Supported Google services (user selects which to enable during OAuth):
|
|
37
|
-
|
|
38
|
-
| Service | Read | Write | Google API |
|
|
39
|
-
| -------- | --------------- | ---------------- | ------------------------------ |
|
|
40
|
-
| Gmail | `gmail:read` | `gmail:write` | `gmail.googleapis.com` |
|
|
41
|
-
| Calendar | `calendar:read` | `calendar:write` | `calendar-json.googleapis.com` |
|
|
42
|
-
| Drive | `drive:read` | `drive:write` | `drive.googleapis.com` |
|
|
43
|
-
| Sheets | `sheets:read` | `sheets:write` | `sheets.googleapis.com` |
|
|
44
|
-
| Docs | `docs:read` | `docs:write` | `docs.googleapis.com` |
|
|
45
|
-
| Tasks | `tasks:read` | `tasks:write` | `tasks.googleapis.com` |
|
|
46
|
-
| Contacts | `contacts:read` | `contacts:write` | `people.googleapis.com` |
|
|
47
|
-
| Meet | `meet:read` | `meet:write` | `meet.googleapis.com` |
|
|
48
|
-
|
|
49
|
-
Default enabled: Gmail (read), Calendar (read+write), Drive (read), Sheets (read), Docs (read).
|
|
50
|
-
|
|
51
|
-
The `gog` CLI is available to verify Google API access:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
gog auth list --plain
|
|
55
|
-
gog gmail labels list --account user@gmail.com
|
|
56
|
-
gog calendar calendars --account user@gmail.com
|
|
57
|
-
gog drive ls --account user@gmail.com
|
|
58
|
-
gog sheets metadata SPREADSHEET_ID --account user@gmail.com
|
|
59
|
-
gog contacts list --account user@gmail.com
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Config lives at `/data/.openclaw/gogcli/`.
|