@femtomc/mu-agent 26.2.35 → 26.2.37
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 +52 -0
- package/dist/extensions/branding.d.ts +13 -0
- package/dist/extensions/branding.d.ts.map +1 -0
- package/dist/extensions/branding.js +253 -0
- package/dist/extensions/event-log.d.ts +11 -0
- package/dist/extensions/event-log.d.ts.map +1 -0
- package/dist/extensions/event-log.js +134 -0
- package/dist/extensions/index.d.ts +21 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +33 -0
- package/dist/extensions/messaging-setup.d.ts +12 -0
- package/dist/extensions/messaging-setup.d.ts.map +1 -0
- package/dist/extensions/messaging-setup.js +1073 -0
- package/dist/extensions/server-tools.d.ts +9 -0
- package/dist/extensions/server-tools.d.ts.map +1 -0
- package/dist/extensions/server-tools.js +318 -0
- package/dist/extensions/shared.d.ts +31 -0
- package/dist/extensions/shared.d.ts.map +1 -0
- package/dist/extensions/shared.js +53 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/meta_agent.d.ts +2 -0
- package/dist/meta_agent.d.ts.map +1 -1
- package/dist/meta_agent.js +29 -66
- package/dist/pi_sdk_backend.d.ts +2 -0
- package/dist/pi_sdk_backend.d.ts.map +1 -1
- package/dist/pi_sdk_backend.js +2 -0
- package/dist/session_factory.d.ts +23 -0
- package/dist/session_factory.d.ts.map +1 -0
- package/dist/session_factory.js +31 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -26,3 +26,55 @@ From repo root (`mu/`):
|
|
|
26
26
|
bun run build
|
|
27
27
|
bun test packages/orchestrator packages/control-plane
|
|
28
28
|
```
|
|
29
|
+
|
|
30
|
+
## Serve-mode extensions (`mu serve`)
|
|
31
|
+
|
|
32
|
+
When `mu serve` starts the interactive assistant, it loads
|
|
33
|
+
`serveExtensionPaths` from `src/extensions/index.ts` (path-based extensions,
|
|
34
|
+
not anonymous inline factories).
|
|
35
|
+
|
|
36
|
+
Current stack:
|
|
37
|
+
|
|
38
|
+
- `brandingExtension` — mu header/footer/widgets
|
|
39
|
+
- `serverToolsExtension` — status + issues/forum/events/control-plane tools
|
|
40
|
+
- `eventLogExtension` — event tail + watch widget
|
|
41
|
+
- `messagingSetupExtension` — adapter diagnostics and setup guidance
|
|
42
|
+
|
|
43
|
+
`mu serve` sets `MU_SERVER_URL` automatically for these extensions.
|
|
44
|
+
|
|
45
|
+
## Slash commands (operator-facing)
|
|
46
|
+
|
|
47
|
+
- `/mu-status` — concise server status
|
|
48
|
+
- `/mu-control` — active control-plane adapters and webhook routes
|
|
49
|
+
- `/mu-setup` — adapter preflight
|
|
50
|
+
- `/mu-setup plan <adapter>` — actionable wiring plan
|
|
51
|
+
- `/mu-setup apply <adapter>` — guided config apply + control-plane reload
|
|
52
|
+
- `/mu-setup verify [adapter]` — runtime verification for mounted routes
|
|
53
|
+
- `/mu-setup <adapter>` — sends adapter setup brief to mu agent (`--no-agent` prints local guide)
|
|
54
|
+
- `/mu-events [n]` / `/mu-events tail [n]` — event log tail
|
|
55
|
+
- `/mu-events watch on|off` — toggle event watch widget
|
|
56
|
+
- `/mu-brand on|off|toggle` — enable/disable UI branding
|
|
57
|
+
|
|
58
|
+
## Tools (agent/meta-agent-facing)
|
|
59
|
+
|
|
60
|
+
- `mu_status()`
|
|
61
|
+
- High-level server status.
|
|
62
|
+
- `mu_control_plane({ action })`
|
|
63
|
+
- `action`: `status | adapters | routes`
|
|
64
|
+
- `mu_issues({ action, ... })`
|
|
65
|
+
- `action`: `list | get | ready`
|
|
66
|
+
- `mu_forum({ action, ... })`
|
|
67
|
+
- `action`: `read | post | topics`
|
|
68
|
+
- `mu_events({ action, ... })`
|
|
69
|
+
- `action`: `tail | query`
|
|
70
|
+
- `mu_messaging_setup({ action, adapter?, public_base_url? })`
|
|
71
|
+
- `action`: `check | preflight | guide | plan | apply | verify`
|
|
72
|
+
- `adapter`: `slack | discord | telegram | gmail`
|
|
73
|
+
|
|
74
|
+
## Messaging setup notes
|
|
75
|
+
|
|
76
|
+
- Runtime setup state comes from `GET /api/config` and `.mu/config.json`.
|
|
77
|
+
- `slack`, `discord`, `telegram` are currently modeled as available adapters.
|
|
78
|
+
- `gmail` is modeled as planned guidance (not mounted by runtime yet).
|
|
79
|
+
- `mu_messaging_setup(action=preflight)` is the quickest health check during
|
|
80
|
+
onboarding.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu-branding — Custom serve-mode TUI chrome for mu.
|
|
3
|
+
*
|
|
4
|
+
* Adds:
|
|
5
|
+
* - Custom header + footer
|
|
6
|
+
* - Quick command widget above the editor
|
|
7
|
+
* - Terminal title and working message branding
|
|
8
|
+
* - Lightweight periodic status refresh (open/ready/control-plane)
|
|
9
|
+
*/
|
|
10
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
11
|
+
export declare function brandingExtension(pi: ExtensionAPI): void;
|
|
12
|
+
export default brandingExtension;
|
|
13
|
+
//# sourceMappingURL=branding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/extensions/branding.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA+CpF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,QA4NjD;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu-branding — Custom serve-mode TUI chrome for mu.
|
|
3
|
+
*
|
|
4
|
+
* Adds:
|
|
5
|
+
* - Custom header + footer
|
|
6
|
+
* - Quick command widget above the editor
|
|
7
|
+
* - Terminal title and working message branding
|
|
8
|
+
* - Lightweight periodic status refresh (open/ready/control-plane)
|
|
9
|
+
*/
|
|
10
|
+
import { basename } from "node:path";
|
|
11
|
+
import { fetchMuStatus, muServerUrl } from "./shared.js";
|
|
12
|
+
const EMPTY_SNAPSHOT = {
|
|
13
|
+
openCount: 0,
|
|
14
|
+
readyCount: 0,
|
|
15
|
+
controlPlaneActive: false,
|
|
16
|
+
adapters: [],
|
|
17
|
+
error: null,
|
|
18
|
+
};
|
|
19
|
+
const ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
20
|
+
function visibleWidth(text) {
|
|
21
|
+
return text.replace(ANSI_RE, "").length;
|
|
22
|
+
}
|
|
23
|
+
function truncateToWidth(text, width) {
|
|
24
|
+
if (width <= 0)
|
|
25
|
+
return "";
|
|
26
|
+
if (visibleWidth(text) <= width)
|
|
27
|
+
return text;
|
|
28
|
+
const plain = text.replace(ANSI_RE, "");
|
|
29
|
+
if (width === 1)
|
|
30
|
+
return plain.slice(0, 1);
|
|
31
|
+
return `${plain.slice(0, width - 1)}…`;
|
|
32
|
+
}
|
|
33
|
+
function routesFromStatus(adapters, routes) {
|
|
34
|
+
if (routes && routes.length > 0)
|
|
35
|
+
return routes;
|
|
36
|
+
return adapters.map((name) => ({ name, route: `/webhooks/${name}` }));
|
|
37
|
+
}
|
|
38
|
+
function modelLabelFromContext(ctx) {
|
|
39
|
+
if (!ctx.model)
|
|
40
|
+
return "model:unknown";
|
|
41
|
+
return `${ctx.model.provider}/${ctx.model.id}`;
|
|
42
|
+
}
|
|
43
|
+
function modelLabelFromEventModel(model) {
|
|
44
|
+
return `${model.provider}/${model.id}`;
|
|
45
|
+
}
|
|
46
|
+
export function brandingExtension(pi) {
|
|
47
|
+
let enabled = true;
|
|
48
|
+
let repoName = "mu";
|
|
49
|
+
let currentModel = "model:unknown";
|
|
50
|
+
let snapshot = { ...EMPTY_SNAPSHOT };
|
|
51
|
+
let activeCtx = null;
|
|
52
|
+
let pollTimer = null;
|
|
53
|
+
let footerRequestRender = null;
|
|
54
|
+
function refreshWidget(ctx) {
|
|
55
|
+
if (!ctx.hasUI)
|
|
56
|
+
return;
|
|
57
|
+
ctx.ui.setWidget("mu-quick-actions", (_tui, theme) => ({
|
|
58
|
+
render(width) {
|
|
59
|
+
const cpState = snapshot.error
|
|
60
|
+
? theme.fg("warning", "cp unavailable")
|
|
61
|
+
: snapshot.controlPlaneActive
|
|
62
|
+
? theme.fg("success", `cp ${snapshot.adapters.join(",") || "on"}`)
|
|
63
|
+
: theme.fg("muted", "cp off");
|
|
64
|
+
const line1 = `${theme.fg("accent", "μ")}${theme.fg("dim", " quick actions")}: ${theme.fg("muted", "/mu-status /mu-control /mu-setup /mu-events")}`;
|
|
65
|
+
const line2 = `${theme.fg("dim", `open ${snapshot.openCount} · ready ${snapshot.readyCount}`)} · ${cpState}`;
|
|
66
|
+
return [truncateToWidth(line1, width), truncateToWidth(line2, width)];
|
|
67
|
+
},
|
|
68
|
+
invalidate() { },
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
function applyChrome(ctx) {
|
|
72
|
+
if (!ctx.hasUI || !enabled)
|
|
73
|
+
return;
|
|
74
|
+
ctx.ui.setTitle(`mu • ${repoName}`);
|
|
75
|
+
ctx.ui.setWorkingMessage("μ working...");
|
|
76
|
+
ctx.ui.setHeader((_tui, theme) => ({
|
|
77
|
+
render(width) {
|
|
78
|
+
const line1 = `${theme.fg("accent", theme.bold("μ"))} ${theme.bold("mu")} ${theme.fg("muted", "serve console")}`;
|
|
79
|
+
const line2 = theme.fg("dim", `repo: ${repoName}`);
|
|
80
|
+
const line3 = theme.fg("muted", "status via: mu_status · mu_control_plane · mu_messaging_setup");
|
|
81
|
+
return [
|
|
82
|
+
"",
|
|
83
|
+
truncateToWidth(line1, width),
|
|
84
|
+
truncateToWidth(line2, width),
|
|
85
|
+
truncateToWidth(line3, width),
|
|
86
|
+
"",
|
|
87
|
+
];
|
|
88
|
+
},
|
|
89
|
+
invalidate() { },
|
|
90
|
+
}));
|
|
91
|
+
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
92
|
+
const requestRender = () => tui.requestRender();
|
|
93
|
+
const unsubscribeBranch = footerData.onBranchChange(requestRender);
|
|
94
|
+
footerRequestRender = requestRender;
|
|
95
|
+
return {
|
|
96
|
+
dispose() {
|
|
97
|
+
if (footerRequestRender === requestRender) {
|
|
98
|
+
footerRequestRender = null;
|
|
99
|
+
}
|
|
100
|
+
unsubscribeBranch();
|
|
101
|
+
},
|
|
102
|
+
invalidate() { },
|
|
103
|
+
render(width) {
|
|
104
|
+
const cpLabel = snapshot.error
|
|
105
|
+
? theme.fg("warning", "cp:unavailable")
|
|
106
|
+
: snapshot.controlPlaneActive
|
|
107
|
+
? theme.fg("success", `cp:${snapshot.adapters.join(",") || "on"}`)
|
|
108
|
+
: theme.fg("muted", "cp:off");
|
|
109
|
+
const left = [
|
|
110
|
+
theme.fg("accent", "μ"),
|
|
111
|
+
theme.fg("dim", repoName),
|
|
112
|
+
theme.fg("muted", "|"),
|
|
113
|
+
theme.fg("dim", `open ${snapshot.openCount} ready ${snapshot.readyCount}`),
|
|
114
|
+
theme.fg("muted", "|"),
|
|
115
|
+
cpLabel,
|
|
116
|
+
].join(" ");
|
|
117
|
+
const branch = footerData.getGitBranch();
|
|
118
|
+
const right = theme.fg("dim", branch ? `${currentModel} · branch:${branch}` : currentModel);
|
|
119
|
+
const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
120
|
+
return [truncateToWidth(`${left}${pad}${right}`, width)];
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
refreshWidget(ctx);
|
|
125
|
+
}
|
|
126
|
+
function clearChrome(ctx) {
|
|
127
|
+
if (!ctx.hasUI)
|
|
128
|
+
return;
|
|
129
|
+
ctx.ui.setHeader(undefined);
|
|
130
|
+
ctx.ui.setFooter(undefined);
|
|
131
|
+
ctx.ui.setWidget("mu-quick-actions", undefined);
|
|
132
|
+
ctx.ui.setWorkingMessage();
|
|
133
|
+
ctx.ui.setStatus("mu-overview", undefined);
|
|
134
|
+
}
|
|
135
|
+
async function refreshStatus(ctx) {
|
|
136
|
+
if (!ctx.hasUI || !enabled)
|
|
137
|
+
return;
|
|
138
|
+
if (!muServerUrl()) {
|
|
139
|
+
snapshot = {
|
|
140
|
+
...EMPTY_SNAPSHOT,
|
|
141
|
+
error: "MU_SERVER_URL not set",
|
|
142
|
+
};
|
|
143
|
+
ctx.ui.setStatus("mu-overview", ctx.ui.theme.fg("warning", "μ server unavailable"));
|
|
144
|
+
refreshWidget(ctx);
|
|
145
|
+
footerRequestRender?.();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const status = await fetchMuStatus(4_000);
|
|
150
|
+
const cp = status.control_plane ?? {
|
|
151
|
+
active: false,
|
|
152
|
+
adapters: [],
|
|
153
|
+
routes: [],
|
|
154
|
+
};
|
|
155
|
+
snapshot = {
|
|
156
|
+
openCount: status.open_count,
|
|
157
|
+
readyCount: status.ready_count,
|
|
158
|
+
controlPlaneActive: cp.active,
|
|
159
|
+
adapters: routesFromStatus(cp.adapters, cp.routes).map((entry) => entry.name),
|
|
160
|
+
error: null,
|
|
161
|
+
};
|
|
162
|
+
ctx.ui.setStatus("mu-overview", ctx.ui.theme.fg("dim", `μ open ${snapshot.openCount} · ready ${snapshot.readyCount} · cp ${snapshot.controlPlaneActive ? "on" : "off"}`));
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
snapshot = {
|
|
166
|
+
...EMPTY_SNAPSHOT,
|
|
167
|
+
error: err instanceof Error ? err.message : String(err),
|
|
168
|
+
};
|
|
169
|
+
ctx.ui.setStatus("mu-overview", ctx.ui.theme.fg("warning", "μ status refresh failed"));
|
|
170
|
+
}
|
|
171
|
+
refreshWidget(ctx);
|
|
172
|
+
footerRequestRender?.();
|
|
173
|
+
}
|
|
174
|
+
function ensurePolling() {
|
|
175
|
+
if (pollTimer)
|
|
176
|
+
return;
|
|
177
|
+
pollTimer = setInterval(() => {
|
|
178
|
+
if (!activeCtx)
|
|
179
|
+
return;
|
|
180
|
+
void refreshStatus(activeCtx);
|
|
181
|
+
}, 12_000);
|
|
182
|
+
}
|
|
183
|
+
function stopPolling() {
|
|
184
|
+
if (!pollTimer)
|
|
185
|
+
return;
|
|
186
|
+
clearInterval(pollTimer);
|
|
187
|
+
pollTimer = null;
|
|
188
|
+
}
|
|
189
|
+
async function initialize(ctx) {
|
|
190
|
+
activeCtx = ctx;
|
|
191
|
+
repoName = basename(ctx.cwd);
|
|
192
|
+
currentModel = modelLabelFromContext(ctx);
|
|
193
|
+
if (!ctx.hasUI)
|
|
194
|
+
return;
|
|
195
|
+
if (enabled) {
|
|
196
|
+
applyChrome(ctx);
|
|
197
|
+
await refreshStatus(ctx);
|
|
198
|
+
ensurePolling();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
clearChrome(ctx);
|
|
202
|
+
stopPolling();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
206
|
+
await initialize(ctx);
|
|
207
|
+
});
|
|
208
|
+
pi.on("session_switch", async (_event, ctx) => {
|
|
209
|
+
await initialize(ctx);
|
|
210
|
+
});
|
|
211
|
+
pi.on("model_select", async (event, ctx) => {
|
|
212
|
+
currentModel = modelLabelFromEventModel(event.model);
|
|
213
|
+
if (!ctx.hasUI || !enabled)
|
|
214
|
+
return;
|
|
215
|
+
ctx.ui.setStatus("mu-model", ctx.ui.theme.fg("dim", currentModel));
|
|
216
|
+
footerRequestRender?.();
|
|
217
|
+
});
|
|
218
|
+
pi.on("session_shutdown", async () => {
|
|
219
|
+
stopPolling();
|
|
220
|
+
footerRequestRender = null;
|
|
221
|
+
activeCtx = null;
|
|
222
|
+
});
|
|
223
|
+
pi.registerCommand("mu-brand", {
|
|
224
|
+
description: "Toggle mu TUI branding (`/mu-brand on|off|toggle`)",
|
|
225
|
+
handler: async (args, ctx) => {
|
|
226
|
+
const mode = args.trim().toLowerCase();
|
|
227
|
+
if (mode === "on") {
|
|
228
|
+
enabled = true;
|
|
229
|
+
}
|
|
230
|
+
else if (mode === "off") {
|
|
231
|
+
enabled = false;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
enabled = !enabled;
|
|
235
|
+
}
|
|
236
|
+
if (!ctx.hasUI) {
|
|
237
|
+
ctx.ui.notify(`mu branding ${enabled ? "enabled" : "disabled"}.`, "info");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (enabled) {
|
|
241
|
+
applyChrome(ctx);
|
|
242
|
+
await refreshStatus(ctx);
|
|
243
|
+
ensurePolling();
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
clearChrome(ctx);
|
|
247
|
+
stopPolling();
|
|
248
|
+
}
|
|
249
|
+
ctx.ui.notify(`mu branding ${enabled ? "enabled" : "disabled"}.`, "info");
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
export default brandingExtension;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu-event-log — Event stream helper for mu serve.
|
|
3
|
+
*
|
|
4
|
+
* - Status line with last event type and tail count
|
|
5
|
+
* - Optional watch widget below editor (`/mu-events watch on|off`)
|
|
6
|
+
* - Command for quick tail inspection (`/mu-events [n]` or `/mu-events tail [n]`)
|
|
7
|
+
*/
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
export declare function eventLogExtension(pi: ExtensionAPI): void;
|
|
10
|
+
export default eventLogExtension;
|
|
11
|
+
//# sourceMappingURL=event-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.d.ts","sourceRoot":"","sources":["../../src/extensions/event-log.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAgCpF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,QAgHjD;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu-event-log — Event stream helper for mu serve.
|
|
3
|
+
*
|
|
4
|
+
* - Status line with last event type and tail count
|
|
5
|
+
* - Optional watch widget below editor (`/mu-events watch on|off`)
|
|
6
|
+
* - Command for quick tail inspection (`/mu-events [n]` or `/mu-events tail [n]`)
|
|
7
|
+
*/
|
|
8
|
+
import { clampInt, fetchMuJson, muServerUrl } from "./shared.js";
|
|
9
|
+
function eventTime(tsMs) {
|
|
10
|
+
return new Date(tsMs).toLocaleTimeString();
|
|
11
|
+
}
|
|
12
|
+
function formatEventLine(event) {
|
|
13
|
+
const ts = eventTime(event.ts_ms);
|
|
14
|
+
const payloadHint = event.issue_id ? ` issue:${event.issue_id}` : "";
|
|
15
|
+
return `${ts} ${event.type} (${event.source})${payloadHint}`;
|
|
16
|
+
}
|
|
17
|
+
async function fetchTail(n) {
|
|
18
|
+
if (!muServerUrl())
|
|
19
|
+
return [];
|
|
20
|
+
try {
|
|
21
|
+
return await fetchMuJson(`/api/events/tail?n=${n}`, { timeoutMs: 6_000 });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function eventLogExtension(pi) {
|
|
28
|
+
let watchEnabled = false;
|
|
29
|
+
let pollTimer = null;
|
|
30
|
+
let activeCtx = null;
|
|
31
|
+
async function refresh(ctx, opts = {}) {
|
|
32
|
+
if (!ctx.hasUI)
|
|
33
|
+
return;
|
|
34
|
+
const tail = clampInt(opts.tail, 8, 1, 50);
|
|
35
|
+
const events = await fetchTail(tail);
|
|
36
|
+
if (events.length === 0) {
|
|
37
|
+
ctx.ui.setStatus("mu-events", ctx.ui.theme.fg("dim", "events: none"));
|
|
38
|
+
if (watchEnabled) {
|
|
39
|
+
ctx.ui.setWidget("mu-events", [ctx.ui.theme.fg("dim", "(no events yet)")], { placement: "belowEditor" });
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const last = events[events.length - 1];
|
|
44
|
+
ctx.ui.setStatus("mu-events", ctx.ui.theme.fg("dim", `events: ${events.length} · last ${last.type}`));
|
|
45
|
+
if (watchEnabled) {
|
|
46
|
+
const lines = events.slice(-5).map((event) => ` ${formatEventLine(event)}`);
|
|
47
|
+
ctx.ui.setWidget("mu-events", lines, { placement: "belowEditor" });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function stopPolling() {
|
|
51
|
+
if (!pollTimer)
|
|
52
|
+
return;
|
|
53
|
+
clearInterval(pollTimer);
|
|
54
|
+
pollTimer = null;
|
|
55
|
+
}
|
|
56
|
+
function ensurePolling() {
|
|
57
|
+
if (pollTimer)
|
|
58
|
+
return;
|
|
59
|
+
pollTimer = setInterval(() => {
|
|
60
|
+
if (!activeCtx)
|
|
61
|
+
return;
|
|
62
|
+
void refresh(activeCtx);
|
|
63
|
+
}, 8_000);
|
|
64
|
+
}
|
|
65
|
+
function setWatchEnabled(next) {
|
|
66
|
+
watchEnabled = next;
|
|
67
|
+
if (watchEnabled) {
|
|
68
|
+
ensurePolling();
|
|
69
|
+
if (activeCtx) {
|
|
70
|
+
void refresh(activeCtx);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (activeCtx?.hasUI) {
|
|
75
|
+
activeCtx.ui.setWidget("mu-events", undefined);
|
|
76
|
+
}
|
|
77
|
+
stopPolling();
|
|
78
|
+
}
|
|
79
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
80
|
+
activeCtx = ctx;
|
|
81
|
+
if (!ctx.hasUI)
|
|
82
|
+
return;
|
|
83
|
+
await refresh(ctx);
|
|
84
|
+
if (watchEnabled)
|
|
85
|
+
ensurePolling();
|
|
86
|
+
});
|
|
87
|
+
pi.on("session_switch", async (_event, ctx) => {
|
|
88
|
+
activeCtx = ctx;
|
|
89
|
+
if (!ctx.hasUI)
|
|
90
|
+
return;
|
|
91
|
+
await refresh(ctx);
|
|
92
|
+
if (watchEnabled)
|
|
93
|
+
ensurePolling();
|
|
94
|
+
});
|
|
95
|
+
pi.on("session_shutdown", async () => {
|
|
96
|
+
stopPolling();
|
|
97
|
+
activeCtx = null;
|
|
98
|
+
});
|
|
99
|
+
pi.registerCommand("mu-events", {
|
|
100
|
+
description: "Inspect events (`/mu-events [n]`, `/mu-events tail [n]`, `/mu-events watch on|off`)",
|
|
101
|
+
handler: async (args, ctx) => {
|
|
102
|
+
const tokens = args
|
|
103
|
+
.trim()
|
|
104
|
+
.split(/\s+/)
|
|
105
|
+
.filter((token) => token.length > 0);
|
|
106
|
+
if (tokens[0] === "watch") {
|
|
107
|
+
const mode = (tokens[1] ?? "toggle").toLowerCase();
|
|
108
|
+
if (mode === "on") {
|
|
109
|
+
setWatchEnabled(true);
|
|
110
|
+
ctx.ui.notify("Event watch enabled.", "info");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (mode === "off") {
|
|
114
|
+
setWatchEnabled(false);
|
|
115
|
+
ctx.ui.notify("Event watch disabled.", "info");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
setWatchEnabled(!watchEnabled);
|
|
119
|
+
ctx.ui.notify(`Event watch ${watchEnabled ? "enabled" : "disabled"}.`, "info");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const requestedLimit = tokens[0] === "tail" ? Number.parseInt(tokens[1] ?? "20", 10) : Number.parseInt(tokens[0] ?? "20", 10);
|
|
123
|
+
const limit = clampInt(Number.isFinite(requestedLimit) ? requestedLimit : undefined, 20, 1, 100);
|
|
124
|
+
const events = await fetchTail(limit);
|
|
125
|
+
if (events.length === 0) {
|
|
126
|
+
ctx.ui.notify("No events found.", "info");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const lines = events.map((event) => formatEventLine(event));
|
|
130
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
export default eventLogExtension;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { brandingExtension } from "./branding.js";
|
|
2
|
+
export { eventLogExtension } from "./event-log.js";
|
|
3
|
+
export { messagingSetupExtension } from "./messaging-setup.js";
|
|
4
|
+
export { serverToolsExtension } from "./server-tools.js";
|
|
5
|
+
import { brandingExtension } from "./branding.js";
|
|
6
|
+
/**
|
|
7
|
+
* Serve-mode extension module paths.
|
|
8
|
+
*
|
|
9
|
+
* Prefer this for session creation so extensions are loaded through pi's
|
|
10
|
+
* normal path-based loader (discoverable and visible by file path), not as
|
|
11
|
+
* anonymous inline factories.
|
|
12
|
+
*/
|
|
13
|
+
export declare const serveExtensionPaths: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Legacy serve-mode extension factories.
|
|
16
|
+
*
|
|
17
|
+
* Kept for compatibility in cases where callers need programmatic inline
|
|
18
|
+
* factories, but path-based loading is preferred.
|
|
19
|
+
*/
|
|
20
|
+
export declare const serveExtensionFactories: (typeof brandingExtension)[];
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAYlD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,UAE/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,8BAKnC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { brandingExtension } from "./branding.js";
|
|
2
|
+
export { eventLogExtension } from "./event-log.js";
|
|
3
|
+
export { messagingSetupExtension } from "./messaging-setup.js";
|
|
4
|
+
export { serverToolsExtension } from "./server-tools.js";
|
|
5
|
+
import { brandingExtension } from "./branding.js";
|
|
6
|
+
import { eventLogExtension } from "./event-log.js";
|
|
7
|
+
import { messagingSetupExtension } from "./messaging-setup.js";
|
|
8
|
+
import { serverToolsExtension } from "./server-tools.js";
|
|
9
|
+
const SERVE_EXTENSION_MODULE_BASENAMES = ["branding", "server-tools", "event-log", "messaging-setup"];
|
|
10
|
+
const RUNTIME_EXTENSION = import.meta.url.endsWith(".ts") ? "ts" : "js";
|
|
11
|
+
function resolveBundledExtensionPath(moduleBasename) {
|
|
12
|
+
return new URL(`./${moduleBasename}.${RUNTIME_EXTENSION}`, import.meta.url).pathname;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Serve-mode extension module paths.
|
|
16
|
+
*
|
|
17
|
+
* Prefer this for session creation so extensions are loaded through pi's
|
|
18
|
+
* normal path-based loader (discoverable and visible by file path), not as
|
|
19
|
+
* anonymous inline factories.
|
|
20
|
+
*/
|
|
21
|
+
export const serveExtensionPaths = SERVE_EXTENSION_MODULE_BASENAMES.map((moduleBasename) => resolveBundledExtensionPath(moduleBasename));
|
|
22
|
+
/**
|
|
23
|
+
* Legacy serve-mode extension factories.
|
|
24
|
+
*
|
|
25
|
+
* Kept for compatibility in cases where callers need programmatic inline
|
|
26
|
+
* factories, but path-based loading is preferred.
|
|
27
|
+
*/
|
|
28
|
+
export const serveExtensionFactories = [
|
|
29
|
+
brandingExtension,
|
|
30
|
+
serverToolsExtension,
|
|
31
|
+
eventLogExtension,
|
|
32
|
+
messagingSetupExtension,
|
|
33
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mu-messaging-setup — Adapter configuration diagnostics + guided setup.
|
|
3
|
+
*
|
|
4
|
+
* Goals:
|
|
5
|
+
* - Make `/mu-setup <adapter>` hand setup context to the active mu agent.
|
|
6
|
+
* - Keep configuration in `.mu/config.json` (no process.env mutations).
|
|
7
|
+
* - Support plan/apply/verify workflow with in-process control-plane reload.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
export declare function messagingSetupExtension(pi: ExtensionAPI): void;
|
|
11
|
+
export default messagingSetupExtension;
|
|
12
|
+
//# sourceMappingURL=messaging-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-setup.d.ts","sourceRoot":"","sources":["../../src/extensions/messaging-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAA6C,MAAM,+BAA+B,CAAC;AAgjC7G,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,YAAY,QAqPvD;AAED,eAAe,uBAAuB,CAAC"}
|