@mutirolabs/openclaw-brain 0.2.0 → 0.2.1
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/CHANGELOG.md +20 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/src/actions.js +40 -0
- package/dist/src/actions.js.map +1 -0
- package/dist/src/agent-tools.js +547 -0
- package/dist/src/agent-tools.js.map +1 -0
- package/dist/src/bridge-client.js +172 -0
- package/dist/src/bridge-client.js.map +1 -0
- package/dist/src/bridge-messages.js +226 -0
- package/dist/src/bridge-messages.js.map +1 -0
- package/dist/src/bridge-protocol.js +42 -0
- package/dist/src/bridge-protocol.js.map +1 -0
- package/dist/src/bridge-session.js +279 -0
- package/dist/src/bridge-session.js.map +1 -0
- package/dist/src/channel.js +167 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/channel.runtime.js +422 -0
- package/dist/src/channel.runtime.js.map +1 -0
- package/dist/src/config.js +61 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/inbound.js +92 -0
- package/dist/src/inbound.js.map +1 -0
- package/dist/src/live-snapshot.js +151 -0
- package/dist/src/live-snapshot.js.map +1 -0
- package/dist/src/outbound.js +205 -0
- package/dist/src/outbound.js.map +1 -0
- package/dist/src/setup-surface.js +252 -0
- package/dist/src/setup-surface.js.map +1 -0
- package/dist/src/signal-forwarder.js +119 -0
- package/dist/src/signal-forwarder.js.map +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Builds the live-handoff snapshot the host requests when a voice call
|
|
2
|
+
// starts. This is the brain's chance to hand OpenClaw's agent persona,
|
|
3
|
+
// the real session transcript, and channel-owned tool hints to Mutiro's
|
|
4
|
+
// live voice model so the call doesn't start with a generic LLM.
|
|
5
|
+
//
|
|
6
|
+
// The three fields returned map 1:1 to ChatBridgeSessionSnapshotResult:
|
|
7
|
+
// system_instruction ← agent's systemPromptOverride (or a minimal header)
|
|
8
|
+
// recent_messages ← recent turns parsed from OpenClaw's session jsonl
|
|
9
|
+
// tool_hints ← channel-owned agent tools advertised by the plugin
|
|
10
|
+
import { promises as fs } from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
13
|
+
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
14
|
+
const MAX_RECENT_TRANSCRIPT_TURNS = 30;
|
|
15
|
+
const resolveAgentConfig = (cfg, agentId) => {
|
|
16
|
+
const agents = cfg.agents;
|
|
17
|
+
return agents?.agents?.[agentId];
|
|
18
|
+
};
|
|
19
|
+
const buildSystemInstruction = (params) => {
|
|
20
|
+
const override = params.agentConfig?.systemPromptOverride?.trim();
|
|
21
|
+
if (override) {
|
|
22
|
+
return override;
|
|
23
|
+
}
|
|
24
|
+
const displayName = params.agentConfig?.name?.trim() || params.agentId;
|
|
25
|
+
const callerSuffix = params.callerUsername ? ` Caller: @${params.callerUsername}.` : "";
|
|
26
|
+
return [
|
|
27
|
+
`You are ${displayName}, the same agent this user speaks with in chat.`,
|
|
28
|
+
"You are now speaking live over voice.",
|
|
29
|
+
"Stay concise, conversational, and in the agent's established persona.",
|
|
30
|
+
`Agent identity: @${params.agentUsername}.${callerSuffix}`,
|
|
31
|
+
].join(" ");
|
|
32
|
+
};
|
|
33
|
+
const extractTextFromContent = (content) => {
|
|
34
|
+
if (!Array.isArray(content))
|
|
35
|
+
return "";
|
|
36
|
+
const parts = [];
|
|
37
|
+
for (const entry of content) {
|
|
38
|
+
if (!entry || typeof entry !== "object")
|
|
39
|
+
continue;
|
|
40
|
+
if (entry.type === "text" && typeof entry.text === "string") {
|
|
41
|
+
parts.push(entry.text);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return parts.join("").trim();
|
|
45
|
+
};
|
|
46
|
+
const readRecentTranscriptTurns = async (sessionFile, conversationId, agentUsername) => {
|
|
47
|
+
const raw = await fs.readFile(sessionFile, "utf8");
|
|
48
|
+
const lines = raw.split(/\r?\n/);
|
|
49
|
+
const turns = [];
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const trimmed = line.trim();
|
|
52
|
+
if (!trimmed)
|
|
53
|
+
continue;
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(trimmed);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (parsed.type !== "message")
|
|
62
|
+
continue;
|
|
63
|
+
const role = parsed.message?.role;
|
|
64
|
+
if (role !== "user" && role !== "assistant")
|
|
65
|
+
continue;
|
|
66
|
+
const text = extractTextFromContent(parsed.message?.content);
|
|
67
|
+
if (!text)
|
|
68
|
+
continue;
|
|
69
|
+
const fromUsername = role === "assistant" ? agentUsername : "user";
|
|
70
|
+
turns.push({
|
|
71
|
+
id: `transcript-${turns.length}`,
|
|
72
|
+
conversation_id: conversationId,
|
|
73
|
+
from: { username: fromUsername },
|
|
74
|
+
text,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return turns.slice(-MAX_RECENT_TRANSCRIPT_TURNS);
|
|
78
|
+
};
|
|
79
|
+
const readRecentMessagesFromStore = async (params) => {
|
|
80
|
+
try {
|
|
81
|
+
const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
|
|
82
|
+
const { loadSessionStore } = await import("openclaw/plugin-sdk/config-runtime");
|
|
83
|
+
const store = loadSessionStore(storePath);
|
|
84
|
+
const entry = store[params.sessionKey];
|
|
85
|
+
if (!entry?.sessionId)
|
|
86
|
+
return undefined;
|
|
87
|
+
const sessionFile = entry.sessionFile && entry.sessionFile.trim()
|
|
88
|
+
? entry.sessionFile
|
|
89
|
+
: path.join(path.dirname(storePath), `${entry.sessionId}.jsonl`);
|
|
90
|
+
return await readRecentTranscriptTurns(sessionFile, params.conversationId, params.agentUsername);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const buildToolHints = async () => {
|
|
97
|
+
// Lazy import so the light startup path stays clean.
|
|
98
|
+
const { mutiroAgentTools } = await import("./agent-tools.js");
|
|
99
|
+
return mutiroAgentTools().map((tool) => {
|
|
100
|
+
const rawDescription = tool.description;
|
|
101
|
+
const description = typeof rawDescription === "string" ? rawDescription.trim() : "";
|
|
102
|
+
return {
|
|
103
|
+
name: tool.name,
|
|
104
|
+
description,
|
|
105
|
+
metadata: {},
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Assemble everything the live voice model needs to speak as OpenClaw's
|
|
111
|
+
* agent. Safe to call even when config/session state is incomplete: each
|
|
112
|
+
* section degrades independently (missing agent config → minimal header,
|
|
113
|
+
* missing transcript → undefined recent_messages, tool enumeration always
|
|
114
|
+
* succeeds).
|
|
115
|
+
*/
|
|
116
|
+
export const buildLiveSnapshot = async (ctx) => {
|
|
117
|
+
const route = resolveAgentRoute({
|
|
118
|
+
cfg: ctx.cfg,
|
|
119
|
+
channel: "mutiro",
|
|
120
|
+
accountId: ctx.accountId,
|
|
121
|
+
peer: { kind: "direct", id: ctx.callerUsername ?? "unknown" },
|
|
122
|
+
});
|
|
123
|
+
const agentConfig = resolveAgentConfig(ctx.cfg, route.agentId);
|
|
124
|
+
const systemInstruction = buildSystemInstruction({
|
|
125
|
+
agentId: route.agentId,
|
|
126
|
+
agentConfig,
|
|
127
|
+
agentUsername: ctx.agentUsername,
|
|
128
|
+
callerUsername: ctx.callerUsername,
|
|
129
|
+
});
|
|
130
|
+
const recentMessages = await readRecentMessagesFromStore({
|
|
131
|
+
cfg: ctx.cfg,
|
|
132
|
+
agentId: route.agentId,
|
|
133
|
+
sessionKey: route.sessionKey,
|
|
134
|
+
conversationId: ctx.conversationId,
|
|
135
|
+
agentUsername: ctx.agentUsername,
|
|
136
|
+
});
|
|
137
|
+
const toolHints = await buildToolHints();
|
|
138
|
+
const metadata = {
|
|
139
|
+
agent_id: route.agentId,
|
|
140
|
+
session_key: route.sessionKey,
|
|
141
|
+
};
|
|
142
|
+
if (ctx.callId)
|
|
143
|
+
metadata.call_id = ctx.callId;
|
|
144
|
+
return {
|
|
145
|
+
systemInstruction,
|
|
146
|
+
...(recentMessages ? { recentMessages } : {}),
|
|
147
|
+
toolHints,
|
|
148
|
+
metadata,
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=live-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-snapshot.js","sourceRoot":"","sources":["../../src/live-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Outbound adapter that translates OpenClaw reply-dispatch calls into
|
|
2
|
+
// bridge-local commands: send_message, send_voice_message, send_card,
|
|
3
|
+
// react_to_message, send_file_message, forward_message, recall,
|
|
4
|
+
// recall_get. Shaped around OpenClaw's ChannelOutboundAdapter contract.
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { applyVoiceLanguage, normalizeOutputText } from "./bridge-messages.js";
|
|
7
|
+
import { TYPE_URLS } from "./bridge-protocol.js";
|
|
8
|
+
const DEFAULT_VOICE = "en-US-Chirp3-HD-Orus";
|
|
9
|
+
const buildCardJson = (components, data, cardId) => {
|
|
10
|
+
let rootId = components[0]?.id || "root";
|
|
11
|
+
for (const component of components) {
|
|
12
|
+
const c = component;
|
|
13
|
+
if (!c.parentId && !c.parent_id) {
|
|
14
|
+
rootId = c.id || rootId;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const lines = [
|
|
19
|
+
JSON.stringify({
|
|
20
|
+
surfaceUpdate: {
|
|
21
|
+
surfaceId: "main",
|
|
22
|
+
components,
|
|
23
|
+
clearBefore: true,
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
];
|
|
27
|
+
if (data) {
|
|
28
|
+
const contents = Object.keys(data).map((key) => ({
|
|
29
|
+
key,
|
|
30
|
+
valueString: typeof data[key] === "object" ? JSON.stringify(data[key]) : String(data[key]),
|
|
31
|
+
}));
|
|
32
|
+
lines.push(JSON.stringify({
|
|
33
|
+
dataModelUpdate: {
|
|
34
|
+
surfaceId: "main",
|
|
35
|
+
contents,
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
lines.push(JSON.stringify({
|
|
40
|
+
beginRendering: {
|
|
41
|
+
surfaceId: "main",
|
|
42
|
+
root: rootId,
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
return {
|
|
46
|
+
// Field names must match Mutiro's CardPart protobuf schema (see
|
|
47
|
+
// spec/protobuf/shared/messaging.proto). The host's strict JSON-to-proto
|
|
48
|
+
// decoder rejects unknown field names like `json_data` / `version`.
|
|
49
|
+
a2ui_json: lines.join("\n"),
|
|
50
|
+
schema_version: "0.8",
|
|
51
|
+
card_id: cardId || `openclaw-card-${Math.random().toString(36).slice(2, 10)}`,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export const createMutiroOutbound = (bridge) => {
|
|
55
|
+
const extras = (target) => ({
|
|
56
|
+
conversation_id: target.conversationId,
|
|
57
|
+
reply_to_message_id: target.replyToMessageId,
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
sendText: async (target, text) => {
|
|
61
|
+
const normalized = normalizeOutputText(text);
|
|
62
|
+
if (!normalized)
|
|
63
|
+
return { ok: false, reason: "noop" };
|
|
64
|
+
return bridge.request("message.send", {
|
|
65
|
+
"@type": TYPE_URLS.bridgeSendMessageCommand,
|
|
66
|
+
conversation_id: target.conversationId,
|
|
67
|
+
reply_to_message_id: target.replyToMessageId,
|
|
68
|
+
text: { text: normalized },
|
|
69
|
+
}, extras(target));
|
|
70
|
+
},
|
|
71
|
+
sendVoice: async (target, params) => {
|
|
72
|
+
const normalized = normalizeOutputText(params.speech);
|
|
73
|
+
if (!normalized)
|
|
74
|
+
return { ok: false, reason: "noop" };
|
|
75
|
+
const voiceName = params.language
|
|
76
|
+
? applyVoiceLanguage(DEFAULT_VOICE, params.language)
|
|
77
|
+
: DEFAULT_VOICE;
|
|
78
|
+
return bridge.request("message.send_voice", {
|
|
79
|
+
"@type": TYPE_URLS.bridgeSendVoiceMessageCommand,
|
|
80
|
+
to_username: params.toUsername.replace(/^@/, ""),
|
|
81
|
+
speech: normalized,
|
|
82
|
+
voice_name: voiceName,
|
|
83
|
+
reply_to_message_id: target.replyToMessageId,
|
|
84
|
+
}, extras(target));
|
|
85
|
+
},
|
|
86
|
+
sendCard: async (target, params) => {
|
|
87
|
+
return bridge.request("message.send", {
|
|
88
|
+
"@type": TYPE_URLS.bridgeSendMessageCommand,
|
|
89
|
+
conversation_id: target.conversationId,
|
|
90
|
+
reply_to_message_id: target.replyToMessageId,
|
|
91
|
+
parts: {
|
|
92
|
+
parts: [
|
|
93
|
+
{
|
|
94
|
+
card: buildCardJson(params.components, params.data, params.cardId),
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
}, extras(target));
|
|
99
|
+
},
|
|
100
|
+
// Ship pre-built A2UI JSONL (same format the canvas tool's a2ui_push
|
|
101
|
+
// action emits). Skips the components-to-JSONL conversion entirely, so
|
|
102
|
+
// the agent can reuse its canvas mental model verbatim.
|
|
103
|
+
sendCardJsonl: async (target, params) => {
|
|
104
|
+
const jsonl = (params.jsonl ?? "").trim();
|
|
105
|
+
if (!jsonl)
|
|
106
|
+
return { ok: false, reason: "empty_jsonl" };
|
|
107
|
+
return bridge.request("message.send", {
|
|
108
|
+
"@type": TYPE_URLS.bridgeSendMessageCommand,
|
|
109
|
+
conversation_id: target.conversationId,
|
|
110
|
+
reply_to_message_id: target.replyToMessageId,
|
|
111
|
+
parts: {
|
|
112
|
+
parts: [
|
|
113
|
+
{
|
|
114
|
+
card: {
|
|
115
|
+
a2ui_json: jsonl,
|
|
116
|
+
schema_version: params.version ?? "0.8",
|
|
117
|
+
card_id: params.cardId || `openclaw-card-${Math.random().toString(36).slice(2, 10)}`,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
}, extras(target));
|
|
123
|
+
},
|
|
124
|
+
sendFile: async (target, params) => {
|
|
125
|
+
const uploadRes = (await bridge.request("media.upload", {
|
|
126
|
+
"@type": TYPE_URLS.bridgeMediaUploadCommand,
|
|
127
|
+
local_path: params.filePath,
|
|
128
|
+
filename: path.basename(params.filePath),
|
|
129
|
+
mime_type: "application/octet-stream",
|
|
130
|
+
}, extras(target)));
|
|
131
|
+
if (!uploadRes?.media) {
|
|
132
|
+
throw new Error(`failed to upload media: ${JSON.stringify(uploadRes)}`);
|
|
133
|
+
}
|
|
134
|
+
return bridge.request("message.send", {
|
|
135
|
+
"@type": TYPE_URLS.bridgeSendMessageCommand,
|
|
136
|
+
conversation_id: target.conversationId,
|
|
137
|
+
reply_to_message_id: target.replyToMessageId,
|
|
138
|
+
parts: {
|
|
139
|
+
parts: [
|
|
140
|
+
{
|
|
141
|
+
file: uploadRes.media,
|
|
142
|
+
...(params.caption ? { metadata: { caption: params.caption } } : {}),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
}, extras(target));
|
|
147
|
+
},
|
|
148
|
+
react: async (params) => {
|
|
149
|
+
return bridge.request("message.react", {
|
|
150
|
+
"@type": TYPE_URLS.addReactionRequest,
|
|
151
|
+
message_id: params.messageId,
|
|
152
|
+
emoji: params.emoji,
|
|
153
|
+
}, { message_id: params.messageId });
|
|
154
|
+
},
|
|
155
|
+
forward: async (params) => {
|
|
156
|
+
const toUsername = params.toUsername?.trim().replace(/^@/, "");
|
|
157
|
+
const targetConversationId = params.targetConversationId?.trim();
|
|
158
|
+
if (!toUsername && !targetConversationId) {
|
|
159
|
+
throw new Error("forward requires exactly one of { toUsername, targetConversationId }");
|
|
160
|
+
}
|
|
161
|
+
// `destination` is a proto `oneof`; set exactly one of the two fields.
|
|
162
|
+
return bridge.request("message.forward", {
|
|
163
|
+
"@type": TYPE_URLS.forwardMessageRequest,
|
|
164
|
+
message_id: params.messageId,
|
|
165
|
+
...(toUsername
|
|
166
|
+
? { to_username: toUsername }
|
|
167
|
+
: { conversation_id: targetConversationId }),
|
|
168
|
+
comment: params.comment || "",
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
recallSearch: async (params) => {
|
|
172
|
+
return bridge.request("recall.search", {
|
|
173
|
+
"@type": TYPE_URLS.recallSearchRequest,
|
|
174
|
+
query: params.query,
|
|
175
|
+
conversation_id: params.conversationId,
|
|
176
|
+
limit: params.maxResults,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
recallGet: async (params) => {
|
|
180
|
+
return bridge.request("recall.get", {
|
|
181
|
+
"@type": TYPE_URLS.recallGetRequest,
|
|
182
|
+
entry_id: params.entryId,
|
|
183
|
+
conversation_id: params.conversationId,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
endTurn: (target) => {
|
|
187
|
+
bridge.send("turn.end", {
|
|
188
|
+
"@type": TYPE_URLS.bridgeTurnEndCommand,
|
|
189
|
+
status: "completed",
|
|
190
|
+
}, extras(target));
|
|
191
|
+
},
|
|
192
|
+
emitSignal: (target, signalType, detailText = "") => {
|
|
193
|
+
if (!target.conversationId)
|
|
194
|
+
return;
|
|
195
|
+
bridge.send("signal.emit", {
|
|
196
|
+
"@type": TYPE_URLS.sendSignalRequest,
|
|
197
|
+
conversation_id: target.conversationId,
|
|
198
|
+
signal_type: signalType,
|
|
199
|
+
detail_text: detailText,
|
|
200
|
+
in_reply_to: target.replyToMessageId,
|
|
201
|
+
}, extras(target));
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=outbound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound.js","sourceRoot":"","sources":["../../src/outbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Setup wizard + adapter. The wizard runs when the user invokes `openclaw
|
|
2
|
+
// channels add` with no flags and picks `mutiro` from the selection list
|
|
3
|
+
// (passing `--channel mutiro` falls through to the non-interactive adapter
|
|
4
|
+
// path, not the wizard). The wizard detects the `mutiro` CLI,
|
|
5
|
+
// collects the agent directory path, validates that it looks like a real
|
|
6
|
+
// Mutiro agent workspace, and checks that auth is configured before writing
|
|
7
|
+
// `channels.mutiro.accounts.<id>.agentDir` into the OpenClaw config.
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { createStandardChannelSetupStatus, DEFAULT_ACCOUNT_ID, formatDocsLink, normalizeAccountId, setSetupChannelEnabled, } from "openclaw/plugin-sdk/setup";
|
|
11
|
+
import { runPluginCommandWithTimeout } from "openclaw/plugin-sdk/run-command";
|
|
12
|
+
import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
|
|
13
|
+
import { listMutiroAccountIds, resolveMutiroAccount } from "./config.js";
|
|
14
|
+
const channel = "mutiro";
|
|
15
|
+
const INSTALL_URL = "https://mutiro.com/downloads/install.sh";
|
|
16
|
+
const CREATE_AGENT_GUIDE = "https://www.mutiro.com/docs/guides/create-agent";
|
|
17
|
+
const MUTIRO_INTRO_LINES = [
|
|
18
|
+
"Point OpenClaw at an existing Mutiro agent directory.",
|
|
19
|
+
"Mutiro stays the messaging surface; OpenClaw becomes the brain.",
|
|
20
|
+
"",
|
|
21
|
+
"Before continuing:",
|
|
22
|
+
` 1. Create a Mutiro agent: ${CREATE_AGENT_GUIDE}`,
|
|
23
|
+
" 2. Stop the built-in Mutiro brain for that agent — do not run two brains.",
|
|
24
|
+
"",
|
|
25
|
+
`Docs: ${formatDocsLink("/channels/mutiro", "channels/mutiro")}`,
|
|
26
|
+
];
|
|
27
|
+
function getSection(cfg) {
|
|
28
|
+
const channels = cfg.channels;
|
|
29
|
+
return channels?.mutiro ?? {};
|
|
30
|
+
}
|
|
31
|
+
function isConfigured(cfg, accountId) {
|
|
32
|
+
const dir = resolveMutiroAccount(cfg, accountId).config.agentDir;
|
|
33
|
+
if (!dir)
|
|
34
|
+
return false;
|
|
35
|
+
try {
|
|
36
|
+
return fs.statSync(dir).isDirectory();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function patchAccount(params) {
|
|
43
|
+
const section = getSection(params.cfg);
|
|
44
|
+
// Single-account shorthand: keep `agentDir` at the top-level section as long
|
|
45
|
+
// as no named accounts exist. This matches the plugin's config shape.
|
|
46
|
+
if (params.accountId === DEFAULT_ACCOUNT_ID && !section.accounts) {
|
|
47
|
+
return {
|
|
48
|
+
...params.cfg,
|
|
49
|
+
channels: {
|
|
50
|
+
...params.cfg.channels,
|
|
51
|
+
[channel]: {
|
|
52
|
+
...section,
|
|
53
|
+
...(params.enabled ? { enabled: true } : {}),
|
|
54
|
+
...params.patch,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const accounts = { ...(section.accounts ?? {}) };
|
|
60
|
+
accounts[params.accountId] = {
|
|
61
|
+
...(accounts[params.accountId] ?? {}),
|
|
62
|
+
...params.patch,
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
...params.cfg,
|
|
66
|
+
channels: {
|
|
67
|
+
...params.cfg.channels,
|
|
68
|
+
[channel]: {
|
|
69
|
+
...section,
|
|
70
|
+
...(params.enabled ? { enabled: true } : {}),
|
|
71
|
+
accounts,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function validateAgentDir(raw) {
|
|
77
|
+
const trimmed = raw.trim();
|
|
78
|
+
if (!trimmed)
|
|
79
|
+
return "Agent directory is required.";
|
|
80
|
+
const resolved = path.resolve(trimmed);
|
|
81
|
+
try {
|
|
82
|
+
const stat = fs.statSync(resolved);
|
|
83
|
+
if (!stat.isDirectory())
|
|
84
|
+
return "Path is not a directory.";
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return "Directory does not exist. Create the Mutiro agent first.";
|
|
88
|
+
}
|
|
89
|
+
const manifest = path.join(resolved, ".mutiro-agent.yaml");
|
|
90
|
+
if (!fs.existsSync(manifest)) {
|
|
91
|
+
return ".mutiro-agent.yaml not found. Is this the correct agent directory?";
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
export const mutiroSetupAdapter = {
|
|
96
|
+
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID,
|
|
97
|
+
validateInput: ({ input }) => {
|
|
98
|
+
const raw = input.authDir?.trim();
|
|
99
|
+
if (!raw) {
|
|
100
|
+
return "Mutiro requires --auth-dir (path to the Mutiro agent directory).";
|
|
101
|
+
}
|
|
102
|
+
return validateAgentDir(raw) ?? null;
|
|
103
|
+
},
|
|
104
|
+
applyAccountConfig: ({ cfg, accountId, input }) => patchAccount({
|
|
105
|
+
cfg,
|
|
106
|
+
accountId,
|
|
107
|
+
enabled: true,
|
|
108
|
+
patch: { agentDir: path.resolve((input.authDir ?? "").trim()) },
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
export const mutiroSetupWizard = {
|
|
112
|
+
channel,
|
|
113
|
+
status: createStandardChannelSetupStatus({
|
|
114
|
+
channelLabel: "Mutiro",
|
|
115
|
+
configuredLabel: "configured",
|
|
116
|
+
unconfiguredLabel: "needs agent directory",
|
|
117
|
+
configuredHint: "agent directory set",
|
|
118
|
+
unconfiguredHint: "point at a Mutiro agent dir",
|
|
119
|
+
configuredScore: 1,
|
|
120
|
+
unconfiguredScore: 0,
|
|
121
|
+
includeStatusLine: true,
|
|
122
|
+
resolveConfigured: ({ cfg, accountId }) => accountId
|
|
123
|
+
? isConfigured(cfg, accountId)
|
|
124
|
+
: listMutiroAccountIds(cfg).some((id) => isConfigured(cfg, id)),
|
|
125
|
+
resolveExtraStatusLines: ({ cfg }) => [`Accounts: ${listMutiroAccountIds(cfg).length || 0}`],
|
|
126
|
+
}),
|
|
127
|
+
introNote: {
|
|
128
|
+
title: "Mutiro Channel setup",
|
|
129
|
+
lines: MUTIRO_INTRO_LINES,
|
|
130
|
+
},
|
|
131
|
+
prepare: async ({ prompter }) => {
|
|
132
|
+
const cliDetected = await detectBinary("mutiro");
|
|
133
|
+
if (cliDetected)
|
|
134
|
+
return undefined;
|
|
135
|
+
await prompter.note([
|
|
136
|
+
"mutiro CLI not found on PATH.",
|
|
137
|
+
"",
|
|
138
|
+
"Install it:",
|
|
139
|
+
` curl -sSL ${INSTALL_URL} | bash`,
|
|
140
|
+
"",
|
|
141
|
+
"Then log in and create (or pick) an agent:",
|
|
142
|
+
" mutiro auth login <email>",
|
|
143
|
+
' mutiro agents create <username> "<Display Name>" --engine genie --bio "<bio>" --badge lobster',
|
|
144
|
+
"",
|
|
145
|
+
`Guide: ${CREATE_AGENT_GUIDE}`,
|
|
146
|
+
].join("\n"), "Mutiro CLI required");
|
|
147
|
+
return undefined;
|
|
148
|
+
},
|
|
149
|
+
credentials: [],
|
|
150
|
+
textInputs: [
|
|
151
|
+
{
|
|
152
|
+
inputKey: "authDir",
|
|
153
|
+
message: "Path to your Mutiro agent directory",
|
|
154
|
+
placeholder: "/Users/you/agents/my-agent",
|
|
155
|
+
required: true,
|
|
156
|
+
helpTitle: "Mutiro agent directory",
|
|
157
|
+
helpLines: [
|
|
158
|
+
"The folder that contains `.mutiro-agent.yaml` for the agent OpenClaw",
|
|
159
|
+
"should drive. Each configured account points to one agent directory.",
|
|
160
|
+
"",
|
|
161
|
+
"If you don't have one yet:",
|
|
162
|
+
' mutiro agents create <username> "<Display Name>" --engine genie --bio "<bio>" --badge lobster',
|
|
163
|
+
`Guide: ${CREATE_AGENT_GUIDE}`,
|
|
164
|
+
],
|
|
165
|
+
currentValue: ({ cfg, accountId }) => resolveMutiroAccount(cfg, accountId).config.agentDir,
|
|
166
|
+
keepPrompt: (value) => `Agent directory set (${value}). Keep it?`,
|
|
167
|
+
validate: ({ value }) => validateAgentDir(value),
|
|
168
|
+
normalizeValue: ({ value }) => path.resolve(value.trim()),
|
|
169
|
+
applySet: async ({ cfg, accountId, value }) => patchAccount({
|
|
170
|
+
cfg,
|
|
171
|
+
accountId,
|
|
172
|
+
enabled: true,
|
|
173
|
+
patch: { agentDir: path.resolve(value.trim()) },
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
finalize: async ({ cfg, accountId, prompter }) => {
|
|
178
|
+
const dir = resolveMutiroAccount(cfg, accountId).config.agentDir;
|
|
179
|
+
if (!dir)
|
|
180
|
+
return undefined;
|
|
181
|
+
const issues = [];
|
|
182
|
+
const whoami = await runPluginCommandWithTimeout({
|
|
183
|
+
argv: ["mutiro", "auth", "whoami"],
|
|
184
|
+
timeoutMs: 5_000,
|
|
185
|
+
cwd: dir,
|
|
186
|
+
});
|
|
187
|
+
if (whoami.code !== 0) {
|
|
188
|
+
issues.push([
|
|
189
|
+
"Mutiro auth not confirmed. Log in before starting the gateway:",
|
|
190
|
+
` cd ${dir}`,
|
|
191
|
+
" mutiro auth login <email>",
|
|
192
|
+
].join("\n"));
|
|
193
|
+
}
|
|
194
|
+
// Built-in brain check: `mutiro agent host status` exits 0 when a host
|
|
195
|
+
// process is already running for this agent. Starting OpenClaw's gateway
|
|
196
|
+
// on top of a running host would put two brains on one agent and make
|
|
197
|
+
// them race on every turn. `host doctor` (setup validation) is separate
|
|
198
|
+
// from `host status` (runtime liveness) — we want the latter here.
|
|
199
|
+
const hostStatus = await runPluginCommandWithTimeout({
|
|
200
|
+
argv: ["mutiro", "agent", "host", "status"],
|
|
201
|
+
timeoutMs: 5_000,
|
|
202
|
+
cwd: dir,
|
|
203
|
+
});
|
|
204
|
+
if (hostStatus.code === 0) {
|
|
205
|
+
issues.push([
|
|
206
|
+
"A Mutiro agent host is already running for this agent.",
|
|
207
|
+
"Stop it before starting OpenClaw — two brains on one agent will",
|
|
208
|
+
"race on every turn:",
|
|
209
|
+
" pkill -f 'mutiro agent host' # or stop whichever process started it",
|
|
210
|
+
].join("\n"));
|
|
211
|
+
}
|
|
212
|
+
if (issues.length > 0) {
|
|
213
|
+
await prompter.note([
|
|
214
|
+
"Readiness checks flagged the following:",
|
|
215
|
+
"",
|
|
216
|
+
...issues.flatMap((issue) => [issue, ""]),
|
|
217
|
+
]
|
|
218
|
+
.join("\n")
|
|
219
|
+
.trimEnd(), "Mutiro agent readiness");
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
},
|
|
223
|
+
completionNote: {
|
|
224
|
+
title: "Mutiro bridge configured",
|
|
225
|
+
lines: [
|
|
226
|
+
"Next steps:",
|
|
227
|
+
" 1. Stop the built-in Mutiro brain for this agent (don't run two brains).",
|
|
228
|
+
" 2. Allow Mutiro tools on the OpenClaw agent by adding `mutiro*` to",
|
|
229
|
+
" `tools.alsoAllow` (or enable individually: mutiro_send_voice_message,",
|
|
230
|
+
" mutiro_send_card, mutiro_forward_message).",
|
|
231
|
+
" 3. Start the gateway: openclaw gateway run",
|
|
232
|
+
"",
|
|
233
|
+
"Once the gateway is running, talk to your agent:",
|
|
234
|
+
" Web: https://app.mutiro.com",
|
|
235
|
+
" CLI: mutiro chat",
|
|
236
|
+
" Mobile: Mutiro app (iOS / Android)",
|
|
237
|
+
" Desktop: Mutiro desktop app (macOS / Windows / Linux)",
|
|
238
|
+
"",
|
|
239
|
+
"Sharing the agent with other users:",
|
|
240
|
+
" Mutiro has its own server-side allowlist — denied users are blocked",
|
|
241
|
+
" before their messages ever reach OpenClaw. Manage it with:",
|
|
242
|
+
" mutiro agents allowlist get <agent-username>",
|
|
243
|
+
" mutiro agents allow <agent-username> <username>",
|
|
244
|
+
" Full guide (paste into your AI assistant):",
|
|
245
|
+
" https://github.com/mutirolabs/openclaw-brain/blob/main/docs/guides/manage-allowlist.md",
|
|
246
|
+
"",
|
|
247
|
+
`Docs: ${formatDocsLink("/channels/mutiro", "channels/mutiro")}`,
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=setup-surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-surface.js","sourceRoot":"","sources":["../../src/setup-surface.ts"],"names":[],"mappings":"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"}
|