@phronesis-io/openclaw-eigenflux 0.0.4 → 0.0.6
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/README.md +15 -161
- package/dist/agent-prompt-templates.d.ts +14 -12
- package/dist/agent-prompt-templates.d.ts.map +1 -1
- package/dist/agent-prompt-templates.js +27 -35
- package/dist/agent-prompt-templates.js.map +1 -1
- package/dist/cli-executor.d.ts +32 -0
- package/dist/cli-executor.d.ts.map +1 -0
- package/dist/cli-executor.js +75 -0
- package/dist/cli-executor.js.map +1 -0
- package/dist/config.d.ts +41 -126
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +94 -229
- package/dist/config.js.map +1 -1
- package/dist/credentials-loader.d.ts +6 -5
- package/dist/credentials-loader.d.ts.map +1 -1
- package/dist/credentials-loader.js +17 -21
- package/dist/credentials-loader.js.map +1 -1
- package/dist/index.d.ts +3 -73
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -277
- package/dist/index.js.map +1 -1
- package/dist/notification-route-resolver.d.ts +24 -2
- package/dist/notification-route-resolver.d.ts.map +1 -1
- package/dist/notification-route-resolver.js +257 -43
- package/dist/notification-route-resolver.js.map +1 -1
- package/dist/notifier.d.ts +9 -17
- package/dist/notifier.d.ts.map +1 -1
- package/dist/notifier.js +133 -66
- package/dist/notifier.js.map +1 -1
- package/dist/polling-client.d.ts +31 -19
- package/dist/polling-client.d.ts.map +1 -1
- package/dist/polling-client.js +102 -127
- package/dist/polling-client.js.map +1 -1
- package/dist/reply-target.d.ts +8 -0
- package/dist/reply-target.d.ts.map +1 -0
- package/dist/reply-target.js +104 -0
- package/dist/reply-target.js.map +1 -0
- package/dist/session-route-memory.d.ts +12 -3
- package/dist/session-route-memory.d.ts.map +1 -1
- package/dist/session-route-memory.js +83 -80
- package/dist/session-route-memory.js.map +1 -1
- package/dist/stream-client.d.ts +48 -0
- package/dist/stream-client.d.ts.map +1 -0
- package/dist/stream-client.js +168 -0
- package/dist/stream-client.js.map +1 -0
- package/openclaw.plugin.json +5 -75
- package/package.json +6 -8
- package/skills/ef-broadcast/SKILL.md +84 -0
- package/skills/ef-broadcast/references/feed.md +127 -0
- package/skills/ef-broadcast/references/publish.md +119 -0
- package/skills/ef-communication/SKILL.md +95 -0
- package/skills/ef-communication/references/message.md +132 -0
- package/skills/ef-communication/references/relations.md +215 -0
- package/skills/ef-communication/references/stream.md +66 -0
- package/skills/ef-profile/SKILL.md +138 -0
- package/skills/ef-profile/references/auth.md +103 -0
- package/skills/ef-profile/references/config.md +54 -0
- package/skills/ef-profile/references/onboarding.md +172 -0
- package/skills/ef-profile/references/server-management.md +67 -0
- package/dist/gateway-rpc-client.d.ts +0 -26
- package/dist/gateway-rpc-client.d.ts.map +0 -1
- package/dist/gateway-rpc-client.js +0 -288
- package/dist/gateway-rpc-client.js.map +0 -1
- package/dist/pm-polling-client.d.ts +0 -52
- package/dist/pm-polling-client.d.ts.map +0 -1
- package/dist/pm-polling-client.js +0 -182
- package/dist/pm-polling-client.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,131 +1,179 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const polling_client_1 = require("./polling-client");
|
|
4
|
-
const
|
|
4
|
+
const stream_client_1 = require("./stream-client");
|
|
5
|
+
const cli_executor_1 = require("./cli-executor");
|
|
5
6
|
const logger_1 = require("./logger");
|
|
6
7
|
const credentials_loader_1 = require("./credentials-loader");
|
|
7
8
|
const config_1 = require("./config");
|
|
8
9
|
const notification_route_resolver_1 = require("./notification-route-resolver");
|
|
9
10
|
const agent_prompt_templates_1 = require("./agent-prompt-templates");
|
|
10
11
|
const notifier_1 = require("./notifier");
|
|
12
|
+
const reply_target_1 = require("./reply-target");
|
|
11
13
|
const session_route_memory_1 = require("./session-route-memory");
|
|
12
14
|
const COMMAND_NAMES = ['auth', 'profile', 'servers', 'feed', 'pm', 'here'];
|
|
13
15
|
const COMMAND_NAME_SET = new Set(COMMAND_NAMES);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const DEFAULT_ROUTING = {
|
|
17
|
+
sessionKey: config_1.PLUGIN_CONFIG.DEFAULT_SESSION_KEY,
|
|
18
|
+
agentId: config_1.PLUGIN_CONFIG.DEFAULT_AGENT_ID,
|
|
19
|
+
routeOverrides: {
|
|
20
|
+
sessionKey: false,
|
|
21
|
+
agentId: false,
|
|
22
|
+
replyChannel: false,
|
|
23
|
+
replyTo: false,
|
|
24
|
+
replyAccountId: false,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
18
27
|
function register(api) {
|
|
19
|
-
const logger = new logger_1.Logger(api
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const logger = new logger_1.Logger(resolvePluginLogger(api));
|
|
29
|
+
const pluginConfig = (0, config_1.resolvePluginConfig)(api.pluginConfig, logger);
|
|
30
|
+
const eigenfluxHome = (0, config_1.resolveEigenfluxHome)();
|
|
31
|
+
let runtimes = [];
|
|
32
|
+
let notInstalledPromptDelivered = false;
|
|
33
|
+
// Register a single meta-service that discovers servers on start
|
|
34
|
+
api.registerService({
|
|
35
|
+
id: 'eigenflux:discovery',
|
|
36
|
+
start: async () => {
|
|
37
|
+
logger.info('Starting EigenFlux discovery service...');
|
|
38
|
+
const discovery = await (0, config_1.discoverServers)(pluginConfig.eigenfluxBin, logger);
|
|
39
|
+
if (discovery.kind === 'not_installed') {
|
|
40
|
+
logger.warn(`EigenFlux CLI not installed (bin=${discovery.bin}); delivering install prompt to user`);
|
|
41
|
+
if (!notInstalledPromptDelivered) {
|
|
42
|
+
notInstalledPromptDelivered = true;
|
|
43
|
+
await deliverNotInstalledPrompt(api, logger, pluginConfig, eigenfluxHome, discovery.bin);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const servers = discovery.servers;
|
|
48
|
+
if (servers.length === 0) {
|
|
49
|
+
logger.warn('No EigenFlux servers discovered; services will not start');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
logger.info(`Discovered ${servers.length} server(s): ${servers.map((s) => s.name).join(', ')}`);
|
|
53
|
+
runtimes = servers.map((server) => createServerRuntime(api, logger, pluginConfig, server, eigenfluxHome));
|
|
54
|
+
for (const runtime of runtimes) {
|
|
55
|
+
logger.info(`Starting services for server=${runtime.server.name}`);
|
|
56
|
+
await runtime.feedPoller.start();
|
|
57
|
+
await runtime.streamClient.start();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
stop: async () => {
|
|
61
|
+
logger.info('Stopping EigenFlux discovery service...');
|
|
62
|
+
for (const runtime of runtimes) {
|
|
63
|
+
logger.info(`Stopping services for server=${runtime.server.name}`);
|
|
64
|
+
runtime.feedPoller.stop();
|
|
65
|
+
await runtime.streamClient.stop();
|
|
66
|
+
}
|
|
67
|
+
runtimes = [];
|
|
68
|
+
notInstalledPromptDelivered = false;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
registerCommand(api, logger, pluginConfig, eigenfluxHome, () => runtimes);
|
|
72
|
+
}
|
|
73
|
+
function resolvePluginLogger(api) {
|
|
74
|
+
const runtimeLogging = api.runtime?.logging;
|
|
75
|
+
if (runtimeLogging && typeof runtimeLogging.getChildLogger === 'function') {
|
|
76
|
+
try {
|
|
77
|
+
const child = runtimeLogging.getChildLogger({ plugin: 'eigenflux' });
|
|
78
|
+
if (child) {
|
|
79
|
+
return child;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// fall through to api.logger
|
|
84
|
+
}
|
|
29
85
|
}
|
|
30
|
-
|
|
31
|
-
registerCommand(api, logger, runtimes);
|
|
32
|
-
logger.info(`EigenFlux activated with ${enabledRuntimes.length}/${runtimes.length} enabled server(s)`);
|
|
86
|
+
return api.logger;
|
|
33
87
|
}
|
|
34
88
|
const plugin = {
|
|
35
89
|
id: 'openclaw-eigenflux',
|
|
36
90
|
name: 'EigenFlux',
|
|
37
|
-
description: 'OpenClaw extension for EigenFlux
|
|
91
|
+
description: 'OpenClaw extension for EigenFlux with CLI-based feed polling and PM streaming',
|
|
38
92
|
configSchema: config_1.PLUGIN_CONFIG_SCHEMA,
|
|
39
93
|
register,
|
|
40
94
|
};
|
|
41
95
|
exports.default = plugin;
|
|
42
|
-
|
|
43
|
-
|
|
96
|
+
const INSTALL_COMMAND = 'curl -fsSL https://eigenflux.ai/install.sh | bash';
|
|
97
|
+
async function deliverNotInstalledPrompt(api, logger, pluginConfig, _eigenfluxHome, bin) {
|
|
98
|
+
// Intentionally no workdir: the bootstrap notifier must not read or persist
|
|
99
|
+
// any remembered session route under <eigenfluxHome>/bootstrap.
|
|
100
|
+
const notifier = new notifier_1.EigenFluxNotifier(api, logger, {
|
|
101
|
+
sessionKey: DEFAULT_ROUTING.sessionKey,
|
|
102
|
+
agentId: DEFAULT_ROUTING.agentId,
|
|
103
|
+
replyChannel: DEFAULT_ROUTING.replyChannel,
|
|
104
|
+
replyTo: DEFAULT_ROUTING.replyTo,
|
|
105
|
+
replyAccountId: DEFAULT_ROUTING.replyAccountId,
|
|
106
|
+
openclawCliBin: pluginConfig.openclawCliBin,
|
|
107
|
+
routeOverrides: DEFAULT_ROUTING.routeOverrides,
|
|
108
|
+
});
|
|
109
|
+
await notifier.deliver((0, agent_prompt_templates_1.buildNotInstalledPromptTemplate)({ bin, installCommand: INSTALL_COMMAND }));
|
|
110
|
+
}
|
|
111
|
+
function createServerRuntime(api, logger, pluginConfig, server, eigenfluxHome) {
|
|
112
|
+
const routing = pluginConfig.serverRouting[server.name] ?? DEFAULT_ROUTING;
|
|
113
|
+
const credentialsLoader = new credentials_loader_1.CredentialsLoader(logger, eigenfluxHome, server.name);
|
|
44
114
|
const notifier = new notifier_1.EigenFluxNotifier(api, logger, {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
replyAccountId: server.replyAccountId,
|
|
115
|
+
eigenfluxBin: pluginConfig.eigenfluxBin,
|
|
116
|
+
serverName: server.name,
|
|
117
|
+
sessionKey: routing.sessionKey,
|
|
118
|
+
agentId: routing.agentId,
|
|
119
|
+
replyChannel: routing.replyChannel,
|
|
120
|
+
replyTo: routing.replyTo,
|
|
121
|
+
replyAccountId: routing.replyAccountId,
|
|
53
122
|
openclawCliBin: pluginConfig.openclawCliBin,
|
|
54
|
-
|
|
55
|
-
routeOverrides: server.routeOverrides,
|
|
123
|
+
routeOverrides: routing.routeOverrides,
|
|
56
124
|
});
|
|
57
125
|
const getPromptContext = () => ({
|
|
58
126
|
serverName: server.name,
|
|
59
|
-
|
|
60
|
-
workdir: server.workdir,
|
|
61
|
-
skillPath: (0, config_1.resolveServerSkillPath)(server),
|
|
127
|
+
eigenfluxHome,
|
|
62
128
|
});
|
|
63
129
|
let lastAuthPromptKey = null;
|
|
64
130
|
const resetAuthPromptGate = () => {
|
|
65
131
|
lastAuthPromptKey = null;
|
|
66
132
|
};
|
|
67
133
|
const notifyAuthRequired = async (authEvent) => {
|
|
68
|
-
const promptKey =
|
|
134
|
+
const promptKey = `auth_required:${server.name}`;
|
|
69
135
|
if (lastAuthPromptKey === promptKey) {
|
|
70
|
-
logger.debug(`Skipping duplicate auth prompt for server=${server.name}
|
|
136
|
+
logger.debug(`Skipping duplicate auth prompt for server=${server.name}`);
|
|
71
137
|
return;
|
|
72
138
|
}
|
|
73
139
|
lastAuthPromptKey = promptKey;
|
|
74
|
-
|
|
75
|
-
await notifier.deliver(buildAuthRequiredMessage(getPromptContext(), {
|
|
76
|
-
authEvent,
|
|
77
|
-
authState,
|
|
78
|
-
}));
|
|
140
|
+
await notifier.deliver((0, agent_prompt_templates_1.buildAuthRequiredPromptTemplate)({ context: getPromptContext() }));
|
|
79
141
|
};
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
142
|
+
const feedPoller = new polling_client_1.EigenFluxPollingClient({
|
|
143
|
+
serverName: server.name,
|
|
144
|
+
eigenfluxBin: pluginConfig.eigenfluxBin,
|
|
145
|
+
resolvePollIntervalSec: () => (0, polling_client_1.readPollIntervalSec)(pluginConfig.eigenfluxBin, server.name, logger),
|
|
84
146
|
logger,
|
|
85
147
|
onFeedPolled: async (payload) => {
|
|
86
148
|
resetAuthPromptGate();
|
|
87
|
-
await notifier.deliver(
|
|
149
|
+
await notifier.deliver((0, agent_prompt_templates_1.buildFeedPayloadPromptTemplate)(payload, getPromptContext()));
|
|
88
150
|
},
|
|
89
151
|
onAuthRequired: notifyAuthRequired,
|
|
90
152
|
});
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
pollIntervalSec: server.pmPollIntervalSec,
|
|
153
|
+
const streamClient = new stream_client_1.EigenFluxStreamClient({
|
|
154
|
+
serverName: server.name,
|
|
155
|
+
eigenfluxBin: pluginConfig.eigenfluxBin,
|
|
95
156
|
logger,
|
|
96
|
-
|
|
157
|
+
onPmEvent: async (event) => {
|
|
97
158
|
resetAuthPromptGate();
|
|
98
|
-
await notifier.deliver(
|
|
159
|
+
await notifier.deliver((0, agent_prompt_templates_1.buildPmStreamEventPromptTemplate)(event, getPromptContext()));
|
|
160
|
+
},
|
|
161
|
+
onAuthRequired: async () => {
|
|
162
|
+
await notifyAuthRequired({ reason: 'auth_required' });
|
|
99
163
|
},
|
|
100
|
-
onAuthRequired: notifyAuthRequired,
|
|
101
164
|
});
|
|
102
165
|
return {
|
|
103
166
|
server,
|
|
167
|
+
routing,
|
|
104
168
|
credentialsLoader,
|
|
105
169
|
notifier,
|
|
106
|
-
|
|
107
|
-
|
|
170
|
+
feedPoller,
|
|
171
|
+
streamClient,
|
|
108
172
|
getPromptContext,
|
|
109
173
|
};
|
|
110
174
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
api.registerService({
|
|
114
|
-
id: `eigenflux:${toServiceIdSegment(runtime.server.name)}`,
|
|
115
|
-
start: async () => {
|
|
116
|
-
logger.info(`Starting EigenFlux polling services for server=${runtime.server.name}`);
|
|
117
|
-
await runtime.pollingClient.start();
|
|
118
|
-
await runtime.pmPollingClient.start();
|
|
119
|
-
},
|
|
120
|
-
stop: async () => {
|
|
121
|
-
logger.info(`Stopping EigenFlux polling services for server=${runtime.server.name}`);
|
|
122
|
-
runtime.pollingClient.stop();
|
|
123
|
-
runtime.pmPollingClient.stop();
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function registerCommand(api, logger, runtimes) {
|
|
175
|
+
// ─── Command Handler ────────────────────────────────────────────────────────
|
|
176
|
+
function registerCommand(api, logger, pluginConfig, eigenfluxHome, getRuntimes) {
|
|
129
177
|
if (!api.registerCommand) {
|
|
130
178
|
logger.warn('registerCommand API unavailable; skipping /eigenflux command registration');
|
|
131
179
|
return;
|
|
@@ -136,6 +184,7 @@ function registerCommand(api, logger, runtimes) {
|
|
|
136
184
|
acceptsArgs: true,
|
|
137
185
|
handler: async (ctx) => {
|
|
138
186
|
const parsed = parseCommandArgs(ctx.args);
|
|
187
|
+
const runtimes = getRuntimes();
|
|
139
188
|
if (parsed.command === 'servers') {
|
|
140
189
|
return {
|
|
141
190
|
text: buildServersText(runtimes),
|
|
@@ -148,27 +197,27 @@ function registerCommand(api, logger, runtimes) {
|
|
|
148
197
|
};
|
|
149
198
|
}
|
|
150
199
|
const runtime = selection.runtime;
|
|
151
|
-
await rememberCurrentCommandRouteIfPossible(ctx, runtime.
|
|
200
|
+
await rememberCurrentCommandRouteIfPossible(ctx, runtime, pluginConfig.eigenfluxBin, logger);
|
|
152
201
|
switch (parsed.command) {
|
|
153
202
|
case 'auth':
|
|
154
203
|
return {
|
|
155
|
-
text: buildAuthStatusText(runtime
|
|
204
|
+
text: buildAuthStatusText(runtime),
|
|
156
205
|
};
|
|
157
206
|
case 'profile':
|
|
158
207
|
return {
|
|
159
|
-
text: await buildProfileText(runtime,
|
|
208
|
+
text: await buildProfileText(runtime, pluginConfig.eigenfluxBin),
|
|
160
209
|
};
|
|
161
210
|
case 'feed':
|
|
162
211
|
return {
|
|
163
|
-
text: await buildFeedText(runtime
|
|
212
|
+
text: await buildFeedText(runtime),
|
|
164
213
|
};
|
|
165
214
|
case 'pm':
|
|
166
215
|
return {
|
|
167
|
-
text:
|
|
216
|
+
text: buildPmStatusText(runtime),
|
|
168
217
|
};
|
|
169
218
|
case 'here':
|
|
170
219
|
return {
|
|
171
|
-
text: await buildHereText(ctx, runtime.
|
|
220
|
+
text: await buildHereText(ctx, runtime, pluginConfig.eigenfluxBin, logger),
|
|
172
221
|
};
|
|
173
222
|
default:
|
|
174
223
|
return {
|
|
@@ -200,7 +249,7 @@ function parseCommandArgs(args) {
|
|
|
200
249
|
function selectServerRuntime(runtimes, requestedServerName) {
|
|
201
250
|
if (runtimes.length === 0) {
|
|
202
251
|
return {
|
|
203
|
-
error: 'No EigenFlux servers
|
|
252
|
+
error: 'No EigenFlux servers discovered. Ensure eigenflux CLI is configured with at least one server.',
|
|
204
253
|
};
|
|
205
254
|
}
|
|
206
255
|
if (!requestedServerName) {
|
|
@@ -222,19 +271,19 @@ function selectServerRuntime(runtimes, requestedServerName) {
|
|
|
222
271
|
}
|
|
223
272
|
function buildServersText(runtimes) {
|
|
224
273
|
if (runtimes.length === 0) {
|
|
225
|
-
return 'No EigenFlux servers
|
|
274
|
+
return 'No EigenFlux servers discovered.';
|
|
226
275
|
}
|
|
227
|
-
const defaultRuntime = runtimes[0];
|
|
228
276
|
return [
|
|
229
|
-
'EigenFlux servers:',
|
|
277
|
+
'EigenFlux servers (discovered via CLI):',
|
|
230
278
|
...runtimes.map((runtime) => {
|
|
231
279
|
const flags = [
|
|
232
|
-
runtime.server.
|
|
233
|
-
|
|
280
|
+
runtime.server.current ? 'default' : null,
|
|
281
|
+
runtime.streamClient.isRunning() ? 'streaming' : null,
|
|
234
282
|
]
|
|
235
283
|
.filter(Boolean)
|
|
236
284
|
.join(', ');
|
|
237
|
-
|
|
285
|
+
const suffix = flags ? ` (${flags})` : '';
|
|
286
|
+
return `- ${runtime.server.name}: endpoint=${runtime.server.endpoint}${suffix}`;
|
|
238
287
|
}),
|
|
239
288
|
].join('\n');
|
|
240
289
|
}
|
|
@@ -248,23 +297,12 @@ function buildHelpText(runtimes) {
|
|
|
248
297
|
? `Available servers: ${runtimes.map((runtime) => runtime.server.name).join(', ')}`
|
|
249
298
|
: undefined,
|
|
250
299
|
'',
|
|
251
|
-
'/eigenflux auth',
|
|
252
|
-
'
|
|
253
|
-
'',
|
|
254
|
-
'/eigenflux
|
|
255
|
-
'
|
|
256
|
-
'',
|
|
257
|
-
'/eigenflux servers',
|
|
258
|
-
'List configured EigenFlux servers.',
|
|
259
|
-
'',
|
|
260
|
-
'/eigenflux feed',
|
|
261
|
-
'Run one feed refresh and return the raw feed payload.',
|
|
262
|
-
'',
|
|
263
|
-
'/eigenflux pm',
|
|
264
|
-
'Run one PM fetch and return the raw PM payload.',
|
|
265
|
-
'',
|
|
266
|
-
'/eigenflux here',
|
|
267
|
-
'Remember the current conversation as the default delivery route for the selected server.',
|
|
300
|
+
'/eigenflux auth — Show credential status',
|
|
301
|
+
'/eigenflux profile — Fetch agent profile',
|
|
302
|
+
'/eigenflux servers — List discovered servers',
|
|
303
|
+
'/eigenflux feed — Run one feed refresh',
|
|
304
|
+
'/eigenflux pm — Show PM stream status',
|
|
305
|
+
'/eigenflux here — Remember current conversation as delivery route',
|
|
268
306
|
]
|
|
269
307
|
.filter(Boolean)
|
|
270
308
|
.join('\n');
|
|
@@ -287,98 +325,51 @@ function isInternalAgentSessionKey(value) {
|
|
|
287
325
|
const parts = trimmed.split(':').filter((part) => part.length > 0);
|
|
288
326
|
return parts[0]?.toLowerCase() === 'agent' && parts[2]?.toLowerCase() === 'main';
|
|
289
327
|
}
|
|
290
|
-
function
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (!trimmed) {
|
|
296
|
-
return undefined;
|
|
297
|
-
}
|
|
298
|
-
if (isNormalizedConversationTarget(trimmed)) {
|
|
299
|
-
return trimmed;
|
|
300
|
-
}
|
|
301
|
-
if (channel && trimmed.startsWith(`${channel}:`)) {
|
|
302
|
-
const inner = trimmed.slice(channel.length + 1).trim();
|
|
303
|
-
if (isNormalizedConversationTarget(inner)) {
|
|
304
|
-
return inner;
|
|
305
|
-
}
|
|
306
|
-
return fallbackKind ? `${fallbackKind}:${inner}` : inner;
|
|
307
|
-
}
|
|
308
|
-
return fallbackKind ? `${fallbackKind}:${trimmed}` : trimmed;
|
|
309
|
-
}
|
|
310
|
-
async function resolveCurrentCommandRoute(ctx, serverConfig, logger) {
|
|
311
|
-
const channel = normalizeChannel(ctx.channel);
|
|
312
|
-
const accountId = readNonEmptyString(ctx.accountId);
|
|
313
|
-
let replyChannel = channel;
|
|
314
|
-
let replyTo = normalizeReplyTarget(ctx.to, channel) ?? normalizeReplyTarget(ctx.from, channel, 'user');
|
|
315
|
-
let replyAccountId = accountId;
|
|
328
|
+
async function resolveCurrentCommandRoute(ctx, runtime, logger) {
|
|
329
|
+
let channel = normalizeChannel(ctx.channel);
|
|
330
|
+
let to = (0, reply_target_1.normalizeReplyTarget)(ctx.to, { channel }) ??
|
|
331
|
+
(0, reply_target_1.normalizeReplyTarget)(ctx.from, { channel, fallbackKind: 'user' });
|
|
332
|
+
let accountId = readNonEmptyString(ctx.accountId);
|
|
316
333
|
if (typeof ctx.getCurrentConversationBinding === 'function') {
|
|
317
334
|
try {
|
|
318
335
|
const binding = await ctx.getCurrentConversationBinding();
|
|
319
336
|
if (binding) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
normalizeReplyTarget(binding.conversationId,
|
|
323
|
-
normalizeReplyTarget(binding.parentConversationId,
|
|
324
|
-
|
|
325
|
-
|
|
337
|
+
channel = normalizeChannel(binding.channel) ?? channel;
|
|
338
|
+
to =
|
|
339
|
+
(0, reply_target_1.normalizeReplyTarget)(binding.conversationId, { channel }) ??
|
|
340
|
+
(0, reply_target_1.normalizeReplyTarget)(binding.parentConversationId, { channel }) ??
|
|
341
|
+
to;
|
|
342
|
+
accountId = readNonEmptyString(binding.accountId) ?? accountId;
|
|
326
343
|
}
|
|
327
344
|
}
|
|
328
345
|
catch (error) {
|
|
329
346
|
logger.debug(`Failed to read current conversation binding: ${error instanceof Error ? error.message : String(error)}`);
|
|
330
347
|
}
|
|
331
348
|
}
|
|
332
|
-
if (!
|
|
349
|
+
if (!channel || !to) {
|
|
333
350
|
return undefined;
|
|
334
351
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
replyAccountId,
|
|
341
|
-
sessionStorePath: readServerSessionStorePath(serverConfig),
|
|
342
|
-
workdir: serverConfig.workdir,
|
|
343
|
-
routeOverrides: {
|
|
344
|
-
sessionKey: false,
|
|
345
|
-
agentId: false,
|
|
346
|
-
replyChannel: true,
|
|
347
|
-
replyTo: true,
|
|
348
|
-
replyAccountId: replyAccountId !== undefined,
|
|
349
|
-
},
|
|
352
|
+
return (0, notification_route_resolver_1.findSessionRouteForBinding)({
|
|
353
|
+
agentId: runtime.routing.agentId,
|
|
354
|
+
channel,
|
|
355
|
+
to,
|
|
356
|
+
accountId,
|
|
350
357
|
}, logger);
|
|
351
|
-
if (!route.replyChannel || !route.replyTo) {
|
|
352
|
-
return undefined;
|
|
353
|
-
}
|
|
354
|
-
if (isInternalAgentSessionKey(route.sessionKey)) {
|
|
355
|
-
const configuredSessionKey = readNonEmptyString(serverConfig.sessionKey);
|
|
356
|
-
if (configuredSessionKey && !isInternalAgentSessionKey(configuredSessionKey)) {
|
|
357
|
-
return {
|
|
358
|
-
sessionKey: configuredSessionKey,
|
|
359
|
-
agentId: readNonEmptyString(serverConfig.agentId) ?? route.agentId,
|
|
360
|
-
replyChannel: route.replyChannel,
|
|
361
|
-
replyTo: route.replyTo,
|
|
362
|
-
replyAccountId: route.replyAccountId,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return route;
|
|
367
358
|
}
|
|
368
|
-
async function buildHereText(ctx,
|
|
369
|
-
const route = await resolveCurrentCommandRoute(ctx,
|
|
359
|
+
async function buildHereText(ctx, runtime, eigenfluxBin, logger) {
|
|
360
|
+
const route = await resolveCurrentCommandRoute(ctx, runtime, logger);
|
|
370
361
|
if (!route || route.sessionKey === 'main' || route.sessionKey.endsWith(':main')) {
|
|
371
362
|
return [
|
|
372
|
-
`Unable to resolve the current external session for server=${
|
|
363
|
+
`Unable to resolve the current external session for server=${runtime.server.name}.`,
|
|
373
364
|
'Run `/eigenflux here` inside the target conversation after OpenClaw has already created a session for it.',
|
|
374
365
|
].join('\n');
|
|
375
366
|
}
|
|
376
|
-
const saved = (0, session_route_memory_1.writeStoredNotificationRoute)(
|
|
367
|
+
const saved = await (0, session_route_memory_1.writeStoredNotificationRoute)(eigenfluxBin, runtime.server.name, route, logger);
|
|
377
368
|
if (!saved) {
|
|
378
|
-
return `Failed to persist the current EigenFlux route for server=${
|
|
369
|
+
return `Failed to persist the current EigenFlux route for server=${runtime.server.name}; check plugin logs for details.`;
|
|
379
370
|
}
|
|
380
371
|
return [
|
|
381
|
-
`EigenFlux server ${
|
|
372
|
+
`EigenFlux server ${runtime.server.name} will deliver to this conversation by default:`,
|
|
382
373
|
`sessionKey: ${route.sessionKey}`,
|
|
383
374
|
`agentId: ${route.agentId}`,
|
|
384
375
|
`channel: ${route.replyChannel ?? 'unknown'}`,
|
|
@@ -388,42 +379,52 @@ async function buildHereText(ctx, serverConfig, logger) {
|
|
|
388
379
|
.filter(Boolean)
|
|
389
380
|
.join('\n');
|
|
390
381
|
}
|
|
391
|
-
async function rememberCurrentCommandRouteIfPossible(ctx,
|
|
392
|
-
const route = await resolveCurrentCommandRoute(ctx,
|
|
382
|
+
async function rememberCurrentCommandRouteIfPossible(ctx, runtime, eigenfluxBin, logger) {
|
|
383
|
+
const route = await resolveCurrentCommandRoute(ctx, runtime, logger);
|
|
393
384
|
if (!route || route.sessionKey === 'main' || route.sessionKey.endsWith(':main')) {
|
|
394
385
|
return;
|
|
395
386
|
}
|
|
396
|
-
if ((0, session_route_memory_1.writeStoredNotificationRoute)(
|
|
397
|
-
logger.debug(`Remembered current command route for server=${
|
|
387
|
+
if (await (0, session_route_memory_1.writeStoredNotificationRoute)(eigenfluxBin, runtime.server.name, route, logger)) {
|
|
388
|
+
logger.debug(`Remembered current command route for server=${runtime.server.name}: session_key=${route.sessionKey}, channel=${route.replyChannel ?? 'unknown'}, to=${route.replyTo ?? 'unknown'}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// ─── Command Handlers ───────────────────────────────────────────────────────
|
|
392
|
+
function buildAuthStatusText(runtime) {
|
|
393
|
+
const authState = runtime.credentialsLoader.loadAuthState();
|
|
394
|
+
const lines = [`EigenFlux auth status (server=${runtime.server.name}):`];
|
|
395
|
+
lines.push(`- credentials_path: ${authState.credentialsPath}`);
|
|
396
|
+
lines.push(`- status: ${authState.status}`);
|
|
397
|
+
if (authState.expiresAt) {
|
|
398
|
+
lines.push(`- expires_at: ${authState.expiresAt}`);
|
|
399
|
+
}
|
|
400
|
+
if (authState.status === 'available') {
|
|
401
|
+
lines.push(`- token: ${maskToken(authState.accessToken)}`);
|
|
398
402
|
}
|
|
403
|
+
else {
|
|
404
|
+
lines.push('- token: unavailable');
|
|
405
|
+
}
|
|
406
|
+
return lines.join('\n');
|
|
399
407
|
}
|
|
400
|
-
async function buildProfileText(runtime,
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
reason: authState.status === 'expired' ? 'expired_token' : 'missing_token',
|
|
405
|
-
credentialsPath: authState.credentialsPath,
|
|
406
|
-
source: authState.source,
|
|
407
|
-
expiresAt: authState.expiresAt,
|
|
408
|
-
},
|
|
409
|
-
authState,
|
|
410
|
-
});
|
|
408
|
+
async function buildProfileText(runtime, eigenfluxBin) {
|
|
409
|
+
const result = await (0, cli_executor_1.execEigenflux)(eigenfluxBin, ['profile', 'show', '-s', runtime.server.name, '-f', 'json']);
|
|
410
|
+
if (result.kind === 'auth_required') {
|
|
411
|
+
return (0, agent_prompt_templates_1.buildAuthRequiredPromptTemplate)({ context: runtime.getPromptContext() });
|
|
411
412
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
return [
|
|
415
|
-
`EigenFlux profile (server=${runtime.server.name}):`,
|
|
416
|
-
'```json',
|
|
417
|
-
safeJsonStringify(payload),
|
|
418
|
-
'```',
|
|
419
|
-
].join('\n');
|
|
413
|
+
if (result.kind === 'not_installed') {
|
|
414
|
+
return `EigenFlux CLI not installed (bin=${result.bin}). Install with: ${INSTALL_COMMAND}`;
|
|
420
415
|
}
|
|
421
|
-
|
|
422
|
-
return `Failed to fetch profile for server ${runtime.server.name}: ${error
|
|
416
|
+
if (result.kind === 'error') {
|
|
417
|
+
return `Failed to fetch profile for server ${runtime.server.name}: ${result.error.message}`;
|
|
423
418
|
}
|
|
419
|
+
return [
|
|
420
|
+
`EigenFlux profile (server=${runtime.server.name}):`,
|
|
421
|
+
'```json',
|
|
422
|
+
safeJsonStringify(result.data),
|
|
423
|
+
'```',
|
|
424
|
+
].join('\n');
|
|
424
425
|
}
|
|
425
|
-
async function buildFeedText(runtime
|
|
426
|
-
const result = await runtime.
|
|
426
|
+
async function buildFeedText(runtime) {
|
|
427
|
+
const result = await runtime.feedPoller.pollOnce({
|
|
427
428
|
notifyFeed: false,
|
|
428
429
|
notifyAuthRequired: false,
|
|
429
430
|
});
|
|
@@ -436,89 +437,27 @@ async function buildFeedText(runtime, authState) {
|
|
|
436
437
|
'```',
|
|
437
438
|
].join('\n');
|
|
438
439
|
case 'auth_required':
|
|
439
|
-
return
|
|
440
|
-
authEvent: result.authEvent,
|
|
441
|
-
authState,
|
|
442
|
-
});
|
|
440
|
+
return (0, agent_prompt_templates_1.buildAuthRequiredPromptTemplate)({ context: runtime.getPromptContext() });
|
|
443
441
|
case 'error':
|
|
444
442
|
return `EigenFlux feed failed for server ${runtime.server.name}: ${result.error.message}`;
|
|
445
443
|
default:
|
|
446
444
|
return `EigenFlux feed finished with an unknown result for server ${runtime.server.name}.`;
|
|
447
445
|
}
|
|
448
446
|
}
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return [
|
|
457
|
-
`EigenFlux PM poll result (server=${runtime.server.name}):`,
|
|
458
|
-
'```json',
|
|
459
|
-
safeJsonStringify(result.payload),
|
|
460
|
-
'```',
|
|
461
|
-
].join('\n');
|
|
462
|
-
case 'auth_required':
|
|
463
|
-
return buildAuthRequiredMessage(runtime.getPromptContext(), {
|
|
464
|
-
authEvent: result.authEvent,
|
|
465
|
-
authState,
|
|
466
|
-
});
|
|
467
|
-
case 'error':
|
|
468
|
-
return `EigenFlux PM poll failed for server ${runtime.server.name}: ${result.error.message}`;
|
|
469
|
-
default:
|
|
470
|
-
return `EigenFlux PM poll finished with an unknown result for server ${runtime.server.name}.`;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
async function fetchJson(url, accessToken) {
|
|
474
|
-
const response = await fetch(url, {
|
|
475
|
-
method: 'GET',
|
|
476
|
-
headers: (0, config_1.buildEigenFluxRequestHeaders)(accessToken),
|
|
477
|
-
});
|
|
478
|
-
if (response.status === 401) {
|
|
479
|
-
throw new Error('HTTP 401: unauthorized');
|
|
480
|
-
}
|
|
481
|
-
if (!response.ok) {
|
|
482
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
483
|
-
}
|
|
484
|
-
const payload = (await response.json());
|
|
485
|
-
if (payload.code !== 0) {
|
|
486
|
-
throw new Error(`API error: ${payload.msg}`);
|
|
487
|
-
}
|
|
488
|
-
return payload;
|
|
489
|
-
}
|
|
490
|
-
function buildAuthStatusText(serverConfig, authState) {
|
|
491
|
-
const lines = [`EigenFlux auth status (server=${serverConfig.name}):`];
|
|
492
|
-
lines.push(`- workdir: ${serverConfig.workdir}`);
|
|
493
|
-
lines.push(`- credentials_path: ${authState.credentialsPath}`);
|
|
494
|
-
lines.push(`- status: ${authState.status}`);
|
|
495
|
-
if (authState.source) {
|
|
496
|
-
lines.push(`- source: ${authState.source}`);
|
|
497
|
-
}
|
|
498
|
-
if (authState.expiresAt) {
|
|
499
|
-
lines.push(`- expires_at: ${authState.expiresAt}`);
|
|
500
|
-
}
|
|
501
|
-
if (authState.status === 'available') {
|
|
502
|
-
lines.push(`- token: ${maskToken(authState.accessToken)}`);
|
|
447
|
+
function buildPmStatusText(runtime) {
|
|
448
|
+
const running = runtime.streamClient.isRunning();
|
|
449
|
+
const cursor = runtime.streamClient.getLastCursor();
|
|
450
|
+
const lines = [`EigenFlux PM stream status (server=${runtime.server.name}):`];
|
|
451
|
+
lines.push(`- streaming: ${running ? 'active' : 'inactive'}`);
|
|
452
|
+
if (cursor) {
|
|
453
|
+
lines.push(`- last_cursor: ${cursor}`);
|
|
503
454
|
}
|
|
504
|
-
|
|
505
|
-
lines.push('
|
|
455
|
+
if (!running) {
|
|
456
|
+
lines.push('PM stream is not running. Check auth status or restart the service.');
|
|
506
457
|
}
|
|
507
458
|
return lines.join('\n');
|
|
508
459
|
}
|
|
509
|
-
|
|
510
|
-
return (0, agent_prompt_templates_1.buildAuthRequiredPromptTemplate)({
|
|
511
|
-
...promptContext,
|
|
512
|
-
authEvent,
|
|
513
|
-
maskedToken: authState?.status === 'available' ? maskToken(authState.accessToken) : undefined,
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
function buildFeedPayloadMessage(promptContext, payload) {
|
|
517
|
-
return (0, agent_prompt_templates_1.buildFeedPayloadPromptTemplate)(payload, promptContext);
|
|
518
|
-
}
|
|
519
|
-
function buildPmPayloadMessage(promptContext, payload) {
|
|
520
|
-
return (0, agent_prompt_templates_1.buildPmPayloadPromptTemplate)(payload, promptContext);
|
|
521
|
-
}
|
|
460
|
+
// ─── Utilities ──────────────────────────────────────────────────────────────
|
|
522
461
|
function maskToken(token) {
|
|
523
462
|
const trimmed = token.trim();
|
|
524
463
|
if (trimmed.length <= 10) {
|
|
@@ -534,8 +473,4 @@ function safeJsonStringify(value) {
|
|
|
534
473
|
return String(value);
|
|
535
474
|
}
|
|
536
475
|
}
|
|
537
|
-
function toServiceIdSegment(name) {
|
|
538
|
-
const sanitized = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu, '-');
|
|
539
|
-
return sanitized || 'default';
|
|
540
|
-
}
|
|
541
476
|
//# sourceMappingURL=index.js.map
|