@femtomc/mu-agent 26.2.54 → 26.2.56
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/dist/extensions/messaging-setup.d.ts.map +1 -1
- package/dist/extensions/messaging-setup.js +90 -13
- package/dist/extensions/server-tools.d.ts.map +1 -1
- package/dist/extensions/server-tools.js +108 -17
- package/dist/extensions/shared.d.ts +38 -5
- package/dist/extensions/shared.d.ts.map +1 -1
- package/dist/extensions/shared.js +23 -1
- package/package.json +1 -1
- package/prompts/skills/messaging-setup-brief.md +3 -1
|
@@ -1 +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;
|
|
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;AAwpC7G,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,YAAY,QAwPvD;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -452,6 +452,62 @@ function planText(plan) {
|
|
|
452
452
|
function planSummary(plans) {
|
|
453
453
|
return plans.map((plan) => planText(plan)).join("\n\n");
|
|
454
454
|
}
|
|
455
|
+
function isRecord(value) {
|
|
456
|
+
return typeof value === "object" && value !== null;
|
|
457
|
+
}
|
|
458
|
+
function isControlPlaneGenerationIdentity(value) {
|
|
459
|
+
if (!isRecord(value))
|
|
460
|
+
return false;
|
|
461
|
+
return typeof value.generation_id === "string" && typeof value.generation_seq === "number";
|
|
462
|
+
}
|
|
463
|
+
function isControlPlaneReloadGenerationSummary(value) {
|
|
464
|
+
if (!isRecord(value))
|
|
465
|
+
return false;
|
|
466
|
+
if (typeof value.attempt_id !== "string")
|
|
467
|
+
return false;
|
|
468
|
+
if (typeof value.coalesced !== "boolean")
|
|
469
|
+
return false;
|
|
470
|
+
if (value.from_generation !== null && !isControlPlaneGenerationIdentity(value.from_generation))
|
|
471
|
+
return false;
|
|
472
|
+
if (!isControlPlaneGenerationIdentity(value.to_generation))
|
|
473
|
+
return false;
|
|
474
|
+
if (value.active_generation !== null && !isControlPlaneGenerationIdentity(value.active_generation))
|
|
475
|
+
return false;
|
|
476
|
+
return value.outcome === "success" || value.outcome === "failure";
|
|
477
|
+
}
|
|
478
|
+
function parseControlPlaneReloadApiResponse(raw) {
|
|
479
|
+
let parsed;
|
|
480
|
+
try {
|
|
481
|
+
parsed = JSON.parse(raw);
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
return {
|
|
485
|
+
response: null,
|
|
486
|
+
error: "control-plane reload returned invalid JSON response",
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
if (!isRecord(parsed)) {
|
|
490
|
+
return {
|
|
491
|
+
response: null,
|
|
492
|
+
error: "control-plane reload returned non-object payload",
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
if (!isControlPlaneReloadGenerationSummary(parsed.generation)) {
|
|
496
|
+
return {
|
|
497
|
+
response: null,
|
|
498
|
+
error: "control-plane reload response missing generation metadata (expected generation-scoped contract)",
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const parsedRecord = parsed;
|
|
502
|
+
const response = {
|
|
503
|
+
...parsed,
|
|
504
|
+
telegram_generation: parsedRecord.telegram_generation ?? null,
|
|
505
|
+
};
|
|
506
|
+
return {
|
|
507
|
+
response,
|
|
508
|
+
error: null,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
455
511
|
async function reloadControlPlaneInProcess(reason) {
|
|
456
512
|
const base = muServerUrl();
|
|
457
513
|
if (!base) {
|
|
@@ -468,18 +524,27 @@ async function reloadControlPlaneInProcess(reason) {
|
|
|
468
524
|
body: JSON.stringify({ reason }),
|
|
469
525
|
});
|
|
470
526
|
const raw = await response.text();
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
527
|
+
const parsedResult = parseControlPlaneReloadApiResponse(raw);
|
|
528
|
+
const parsed = parsedResult.response;
|
|
529
|
+
if (parsedResult.error) {
|
|
530
|
+
return {
|
|
531
|
+
ok: false,
|
|
532
|
+
response: null,
|
|
533
|
+
error: parsedResult.error,
|
|
534
|
+
};
|
|
474
535
|
}
|
|
475
|
-
|
|
476
|
-
|
|
536
|
+
if (!parsed) {
|
|
537
|
+
return {
|
|
538
|
+
ok: false,
|
|
539
|
+
response: null,
|
|
540
|
+
error: "control-plane reload response missing payload",
|
|
541
|
+
};
|
|
477
542
|
}
|
|
478
|
-
if (!response.ok || !parsed
|
|
543
|
+
if (!response.ok || !parsed.ok) {
|
|
479
544
|
return {
|
|
480
545
|
ok: false,
|
|
481
546
|
response: parsed,
|
|
482
|
-
error: parsed
|
|
547
|
+
error: parsed.error ?? `control-plane reload failed (${response.status})`,
|
|
483
548
|
};
|
|
484
549
|
}
|
|
485
550
|
return {
|
|
@@ -496,6 +561,22 @@ async function reloadControlPlaneInProcess(reason) {
|
|
|
496
561
|
};
|
|
497
562
|
}
|
|
498
563
|
}
|
|
564
|
+
function reloadOutcomeSummary(reload) {
|
|
565
|
+
if (!reload.ok) {
|
|
566
|
+
return `Control-plane reload failed: ${reload.error ?? "unknown error"}.`;
|
|
567
|
+
}
|
|
568
|
+
const response = reload.response;
|
|
569
|
+
if (!response) {
|
|
570
|
+
return "Control-plane reload failed: missing reload response payload.";
|
|
571
|
+
}
|
|
572
|
+
const adapters = response.control_plane?.adapters.join(", ") || "(none)";
|
|
573
|
+
const generationSummary = `${response.generation.outcome} (${response.generation.active_generation?.generation_id ?? response.generation.to_generation.generation_id})`;
|
|
574
|
+
const telegramRollbackTrigger = response.telegram_generation?.rollback.trigger;
|
|
575
|
+
const telegramNote = response.telegram_generation?.handled && telegramRollbackTrigger
|
|
576
|
+
? ` rollback_trigger=${telegramRollbackTrigger}`
|
|
577
|
+
: "";
|
|
578
|
+
return `Control-plane reloaded in-process. Active adapters: ${adapters}. Generation: ${generationSummary}.${telegramNote}`;
|
|
579
|
+
}
|
|
499
580
|
function patchForAdapterValues(adapterId, values) {
|
|
500
581
|
switch (adapterId) {
|
|
501
582
|
case "slack":
|
|
@@ -846,9 +927,7 @@ async function runInteractiveApply(ctx, adapterId) {
|
|
|
846
927
|
const lines = [
|
|
847
928
|
`Updated config fields: ${outcome.updated_fields.join(", ") || "(none)"}`,
|
|
848
929
|
`Config path: ${outcome.config_path ?? runtime.configPath ?? "(unknown)"}`,
|
|
849
|
-
outcome.reload
|
|
850
|
-
? `Control-plane reloaded in-process. Active adapters: ${outcome.reload.response?.control_plane?.adapters.join(", ") || "(none)"}.`
|
|
851
|
-
: `Control-plane reload failed: ${outcome.reload.error ?? "unknown error"}.`,
|
|
930
|
+
reloadOutcomeSummary(outcome.reload),
|
|
852
931
|
"",
|
|
853
932
|
verifyText(verify),
|
|
854
933
|
];
|
|
@@ -959,9 +1038,7 @@ export function messagingSetupExtension(pi) {
|
|
|
959
1038
|
const lines = [
|
|
960
1039
|
`Updated config fields: ${outcome.updated_fields.join(", ") || "(none)"}`,
|
|
961
1040
|
`Config path: ${outcome.config_path ?? runtime.configPath ?? "(unknown)"}`,
|
|
962
|
-
outcome.reload
|
|
963
|
-
? `Control-plane reloaded in-process. Active adapters: ${outcome.reload.response?.control_plane?.adapters.join(", ") || "(none)"}.`
|
|
964
|
-
: `Control-plane reload failed: ${outcome.reload.error ?? "unknown error"}.`,
|
|
1041
|
+
reloadOutcomeSummary(outcome.reload),
|
|
965
1042
|
"",
|
|
966
1043
|
verifyText(verify),
|
|
967
1044
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-tools.d.ts","sourceRoot":"","sources":["../../src/extensions/server-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"server-tools.d.ts","sourceRoot":"","sources":["../../src/extensions/server-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAoFlE,MAAM,MAAM,wBAAwB,GAAG;IACtC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC,CAAC;AAuXF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,GAAE,wBAA6B,QAQzF;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,YAAY,QAS5D;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -21,17 +21,33 @@ function cpRoutesFromStatus(routes, adapters) {
|
|
|
21
21
|
route: `/webhooks/${name}`,
|
|
22
22
|
}));
|
|
23
23
|
}
|
|
24
|
+
function generationSummary(generation) {
|
|
25
|
+
const active = generation.active_generation?.generation_id ?? "(none)";
|
|
26
|
+
const pending = generation.pending_reload
|
|
27
|
+
? `${generation.pending_reload.attempt_id}:${generation.pending_reload.state}`
|
|
28
|
+
: "(none)";
|
|
29
|
+
const last = generation.last_reload
|
|
30
|
+
? `${generation.last_reload.attempt_id}:${generation.last_reload.state}`
|
|
31
|
+
: "(none)";
|
|
32
|
+
return `generation: active=${active} pending=${pending} last=${last}`;
|
|
33
|
+
}
|
|
34
|
+
function observabilitySummary(counters) {
|
|
35
|
+
return `observability: reload_success=${counters.reload_success_total} reload_failure=${counters.reload_failure_total} duplicate=${counters.duplicate_signal_total} drop=${counters.drop_signal_total}`;
|
|
36
|
+
}
|
|
24
37
|
function summarizeStatus(status) {
|
|
25
|
-
const cp = status.control_plane
|
|
38
|
+
const cp = status.control_plane;
|
|
26
39
|
const routes = cpRoutesFromStatus(cp.routes, cp.adapters);
|
|
27
40
|
const routeText = routes.length > 0 ? routes.map((entry) => `${entry.name}:${entry.route}`).join(", ") : "(none)";
|
|
28
|
-
|
|
41
|
+
const lines = [
|
|
29
42
|
`repo: ${status.repo_root}`,
|
|
30
43
|
`issues: open=${status.open_count} ready=${status.ready_count}`,
|
|
31
44
|
`control_plane: ${cp.active ? "active" : "inactive"}`,
|
|
32
45
|
`adapters: ${cp.adapters.length > 0 ? cp.adapters.join(", ") : "(none)"}`,
|
|
33
46
|
`routes: ${routeText}`,
|
|
34
|
-
|
|
47
|
+
generationSummary(cp.generation),
|
|
48
|
+
observabilitySummary(cp.observability.counters),
|
|
49
|
+
];
|
|
50
|
+
return lines.join("\n");
|
|
35
51
|
}
|
|
36
52
|
function sliceWithLimit(items, limitRaw, fallback = 50) {
|
|
37
53
|
const limit = clampInt(limitRaw, fallback, 1, 200);
|
|
@@ -68,7 +84,7 @@ function registerServerTools(pi, opts) {
|
|
|
68
84
|
ctx.ui.setStatus("mu-server", ctx.ui.theme.fg("dim", `μ server ${url}`));
|
|
69
85
|
try {
|
|
70
86
|
const status = await fetchMuStatus(4_000);
|
|
71
|
-
ctx.ui.setStatus("mu-status", ctx.ui.theme.fg("dim", `open ${status.open_count} · ready ${status.ready_count} · cp ${status.control_plane
|
|
87
|
+
ctx.ui.setStatus("mu-status", ctx.ui.theme.fg("dim", `open ${status.open_count} · ready ${status.ready_count} · cp ${status.control_plane.active ? "on" : "off"}`));
|
|
72
88
|
}
|
|
73
89
|
catch {
|
|
74
90
|
ctx.ui.setStatus("mu-status", ctx.ui.theme.fg("warning", "μ status unavailable"));
|
|
@@ -96,19 +112,19 @@ function registerServerTools(pi, opts) {
|
|
|
96
112
|
parameters: ControlPlaneParams,
|
|
97
113
|
async execute(_toolCallId, params) {
|
|
98
114
|
const status = await fetchMuStatus();
|
|
99
|
-
const cp = status.control_plane
|
|
100
|
-
active: false,
|
|
101
|
-
adapters: [],
|
|
102
|
-
routes: [],
|
|
103
|
-
};
|
|
115
|
+
const cp = status.control_plane;
|
|
104
116
|
const routes = cpRoutesFromStatus(cp.routes, cp.adapters);
|
|
117
|
+
const generation = cp.generation;
|
|
118
|
+
const observability = cp.observability.counters;
|
|
105
119
|
switch (params.action) {
|
|
106
120
|
case "status":
|
|
107
121
|
return textResult(toJsonText({
|
|
108
122
|
active: cp.active,
|
|
109
123
|
adapters: cp.adapters,
|
|
110
124
|
routes,
|
|
111
|
-
|
|
125
|
+
generation,
|
|
126
|
+
observability,
|
|
127
|
+
}), { control_plane: cp, routes, generation, observability });
|
|
112
128
|
case "adapters":
|
|
113
129
|
return textResult(toJsonText(cp.adapters), { adapters: cp.adapters });
|
|
114
130
|
case "routes":
|
|
@@ -283,6 +299,83 @@ function registerServerTools(pi, opts) {
|
|
|
283
299
|
}
|
|
284
300
|
},
|
|
285
301
|
});
|
|
302
|
+
const IdentityParams = Type.Object({
|
|
303
|
+
action: StringEnum(["list", "link", "unlink"]),
|
|
304
|
+
channel: Type.Optional(Type.String({ description: "Channel: slack, discord, telegram (for link)" })),
|
|
305
|
+
actor_id: Type.Optional(Type.String({ description: "Channel actor ID (for link)" })),
|
|
306
|
+
tenant_id: Type.Optional(Type.String({ description: "Channel tenant ID (for link)" })),
|
|
307
|
+
role: Type.Optional(Type.String({ description: "Role: operator, contributor, viewer (for link, default operator)" })),
|
|
308
|
+
binding_id: Type.Optional(Type.String({ description: "Binding ID (for link/unlink)" })),
|
|
309
|
+
actor_binding_id: Type.Optional(Type.String({ description: "Actor binding ID (for unlink, usually same as binding_id)" })),
|
|
310
|
+
reason: Type.Optional(Type.String({ description: "Unlink reason (for unlink)" })),
|
|
311
|
+
include_inactive: Type.Optional(Type.Boolean({ description: "Include inactive bindings (for list)" })),
|
|
312
|
+
});
|
|
313
|
+
pi.registerTool({
|
|
314
|
+
name: "mu_identity",
|
|
315
|
+
label: "Identity",
|
|
316
|
+
description: "Manage identity bindings. Actions: list (enumerate bindings), link (create binding), unlink (self-unlink).",
|
|
317
|
+
parameters: IdentityParams,
|
|
318
|
+
async execute(_toolCallId, params) {
|
|
319
|
+
switch (params.action) {
|
|
320
|
+
case "list": {
|
|
321
|
+
const query = new URLSearchParams();
|
|
322
|
+
if (params.include_inactive)
|
|
323
|
+
query.set("include_inactive", "true");
|
|
324
|
+
const data = await fetchMuJson(`/api/identities${query.size > 0 ? `?${query.toString()}` : ""}`);
|
|
325
|
+
return textResult(toJsonText(data), { count: data.count });
|
|
326
|
+
}
|
|
327
|
+
case "link": {
|
|
328
|
+
const channel = trimOrNull(params.channel);
|
|
329
|
+
const actorId = trimOrNull(params.actor_id);
|
|
330
|
+
const tenantId = trimOrNull(params.tenant_id);
|
|
331
|
+
if (!channel)
|
|
332
|
+
return textResult("Error: channel required for link");
|
|
333
|
+
if (!actorId)
|
|
334
|
+
return textResult("Error: actor_id required for link");
|
|
335
|
+
if (!tenantId)
|
|
336
|
+
return textResult("Error: tenant_id required for link");
|
|
337
|
+
const body = {
|
|
338
|
+
channel,
|
|
339
|
+
actor_id: actorId,
|
|
340
|
+
tenant_id: tenantId,
|
|
341
|
+
};
|
|
342
|
+
const role = trimOrNull(params.role);
|
|
343
|
+
if (role)
|
|
344
|
+
body.role = role;
|
|
345
|
+
const bindingId = trimOrNull(params.binding_id);
|
|
346
|
+
if (bindingId)
|
|
347
|
+
body.binding_id = bindingId;
|
|
348
|
+
const result = await fetchMuJson("/api/identities/link", {
|
|
349
|
+
method: "POST",
|
|
350
|
+
body,
|
|
351
|
+
});
|
|
352
|
+
return textResult(toJsonText(result), result);
|
|
353
|
+
}
|
|
354
|
+
case "unlink": {
|
|
355
|
+
const bindingId = trimOrNull(params.binding_id);
|
|
356
|
+
const actorBindingId = trimOrNull(params.actor_binding_id);
|
|
357
|
+
if (!bindingId)
|
|
358
|
+
return textResult("Error: binding_id required for unlink");
|
|
359
|
+
if (!actorBindingId)
|
|
360
|
+
return textResult("Error: actor_binding_id required for unlink");
|
|
361
|
+
const body = {
|
|
362
|
+
binding_id: bindingId,
|
|
363
|
+
actor_binding_id: actorBindingId,
|
|
364
|
+
};
|
|
365
|
+
const reason = trimOrNull(params.reason);
|
|
366
|
+
if (reason)
|
|
367
|
+
body.reason = reason;
|
|
368
|
+
const result = await fetchMuJson("/api/identities/unlink", {
|
|
369
|
+
method: "POST",
|
|
370
|
+
body,
|
|
371
|
+
});
|
|
372
|
+
return textResult(toJsonText(result), result);
|
|
373
|
+
}
|
|
374
|
+
default:
|
|
375
|
+
return textResult(`Unknown action: ${params.action}`);
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
});
|
|
286
379
|
pi.registerCommand("mu-status", {
|
|
287
380
|
description: "Show concise mu server status",
|
|
288
381
|
handler: async (_args, ctx) => {
|
|
@@ -300,16 +393,14 @@ function registerServerTools(pi, opts) {
|
|
|
300
393
|
handler: async (_args, ctx) => {
|
|
301
394
|
try {
|
|
302
395
|
const status = await fetchMuStatus();
|
|
303
|
-
const cp = status.control_plane
|
|
304
|
-
active: false,
|
|
305
|
-
adapters: [],
|
|
306
|
-
routes: [],
|
|
307
|
-
};
|
|
396
|
+
const cp = status.control_plane;
|
|
308
397
|
const routes = cpRoutesFromStatus(cp.routes, cp.adapters);
|
|
309
398
|
const lines = [
|
|
310
399
|
`control_plane: ${cp.active ? "active" : "inactive"}`,
|
|
311
400
|
`adapters: ${cp.adapters.length > 0 ? cp.adapters.join(", ") : "(none)"}`,
|
|
312
401
|
`routes: ${routes.length > 0 ? routes.map((entry) => `${entry.name}:${entry.route}`).join(", ") : "(none)"}`,
|
|
402
|
+
generationSummary(cp.generation),
|
|
403
|
+
observabilitySummary(cp.observability.counters),
|
|
313
404
|
];
|
|
314
405
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
315
406
|
}
|
|
@@ -323,14 +414,14 @@ export function serverToolsExtension(pi, opts = {}) {
|
|
|
323
414
|
registerServerTools(pi, {
|
|
324
415
|
allowForumPost: opts.allowForumPost ?? true,
|
|
325
416
|
toolIntroLine: opts.toolIntroLine ??
|
|
326
|
-
"Tools: mu_status, mu_control_plane, mu_issues, mu_forum, mu_events, mu_runs, mu_activities, mu_heartbeats.",
|
|
417
|
+
"Tools: mu_status, mu_control_plane, mu_issues, mu_forum, mu_events, mu_runs, mu_activities, mu_heartbeats, mu_identity.",
|
|
327
418
|
extraSystemPromptLines: opts.extraSystemPromptLines ?? [],
|
|
328
419
|
});
|
|
329
420
|
}
|
|
330
421
|
export function serverToolsReadOnlyExtension(pi) {
|
|
331
422
|
registerServerTools(pi, {
|
|
332
423
|
allowForumPost: false,
|
|
333
|
-
toolIntroLine: "Tools: mu_status, mu_control_plane, mu_issues, mu_forum(read/topics), mu_events, mu_runs(read), mu_messaging_setup.",
|
|
424
|
+
toolIntroLine: "Tools: mu_status, mu_control_plane, mu_issues, mu_forum(read/topics), mu_events, mu_runs(read), mu_messaging_setup, mu_identity.",
|
|
334
425
|
extraSystemPromptLines: [
|
|
335
426
|
"You have Bash, Read, Write, and Edit tools. Use them to run mu CLI commands, edit config files, and complete tasks directly.",
|
|
336
427
|
],
|
|
@@ -2,15 +2,48 @@ export type MuControlPlaneRoute = {
|
|
|
2
2
|
name: string;
|
|
3
3
|
route: string;
|
|
4
4
|
};
|
|
5
|
+
export type MuGenerationIdentity = {
|
|
6
|
+
generation_id: string;
|
|
7
|
+
generation_seq: number;
|
|
8
|
+
};
|
|
9
|
+
export type MuGenerationReloadAttempt = {
|
|
10
|
+
attempt_id: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
state: "planned" | "swapped" | "completed" | "failed";
|
|
13
|
+
requested_at_ms: number;
|
|
14
|
+
swapped_at_ms: number | null;
|
|
15
|
+
finished_at_ms: number | null;
|
|
16
|
+
from_generation: MuGenerationIdentity | null;
|
|
17
|
+
to_generation: MuGenerationIdentity;
|
|
18
|
+
};
|
|
19
|
+
export type MuGenerationSupervisorSnapshot = {
|
|
20
|
+
supervisor_id: string;
|
|
21
|
+
active_generation: MuGenerationIdentity | null;
|
|
22
|
+
pending_reload: MuGenerationReloadAttempt | null;
|
|
23
|
+
last_reload: MuGenerationReloadAttempt | null;
|
|
24
|
+
};
|
|
25
|
+
export type MuGenerationObservabilityCounters = {
|
|
26
|
+
reload_success_total: number;
|
|
27
|
+
reload_failure_total: number;
|
|
28
|
+
reload_drain_duration_ms_total: number;
|
|
29
|
+
reload_drain_duration_samples_total: number;
|
|
30
|
+
duplicate_signal_total: number;
|
|
31
|
+
drop_signal_total: number;
|
|
32
|
+
};
|
|
33
|
+
export type MuControlPlaneStatus = {
|
|
34
|
+
active: boolean;
|
|
35
|
+
adapters: string[];
|
|
36
|
+
routes?: MuControlPlaneRoute[];
|
|
37
|
+
generation: MuGenerationSupervisorSnapshot;
|
|
38
|
+
observability: {
|
|
39
|
+
counters: MuGenerationObservabilityCounters;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
5
42
|
export type MuStatusResponse = {
|
|
6
43
|
repo_root: string;
|
|
7
44
|
open_count: number;
|
|
8
45
|
ready_count: number;
|
|
9
|
-
control_plane
|
|
10
|
-
active: boolean;
|
|
11
|
-
adapters: string[];
|
|
12
|
-
routes?: MuControlPlaneRoute[];
|
|
13
|
-
};
|
|
46
|
+
control_plane: MuControlPlaneStatus;
|
|
14
47
|
};
|
|
15
48
|
export declare function muServerUrl(): string | null;
|
|
16
49
|
export declare function clampInt(value: number | undefined, fallback: number, min: number, max: number): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/extensions/shared.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/extensions/shared.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,oBAAoB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC/C,cAAc,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACjD,WAAW,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC/C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8BAA8B,EAAE,MAAM,CAAC;IACvC,mCAAmC,EAAE,MAAM,CAAC;IAC5C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC/B,UAAU,EAAE,8BAA8B,CAAC;IAC3C,aAAa,EAAE;QACd,QAAQ,EAAE,iCAAiC,CAAC;KAC5C,CAAC;CACF,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,oBAAoB,CAAC;CACpC,CAAC;AAEF,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAG3C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAKtG;AAED,wBAAsB,WAAW,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAChE,OAAO,CAAC,CAAC,CAAC,CA4BZ;AAiCD,wBAAsB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGjF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;EAK7E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD"}
|
|
@@ -39,8 +39,30 @@ export async function fetchMuJson(path, opts = {}) {
|
|
|
39
39
|
clearTimeout(timeout);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
function ensureGenerationScopedStatus(status) {
|
|
43
|
+
const controlPlane = status.control_plane;
|
|
44
|
+
if (!controlPlane || typeof controlPlane !== "object") {
|
|
45
|
+
throw new Error("mu server /api/status missing control_plane payload (expected generation-scoped contract)");
|
|
46
|
+
}
|
|
47
|
+
const controlPlaneRecord = controlPlane;
|
|
48
|
+
if (!("generation" in controlPlaneRecord) || !controlPlaneRecord.generation) {
|
|
49
|
+
throw new Error("mu server /api/status missing control_plane.generation (expected generation-scoped contract)");
|
|
50
|
+
}
|
|
51
|
+
if (!("observability" in controlPlaneRecord) || !controlPlaneRecord.observability) {
|
|
52
|
+
throw new Error("mu server /api/status missing control_plane.observability (expected generation-scoped contract)");
|
|
53
|
+
}
|
|
54
|
+
const observability = controlPlaneRecord.observability;
|
|
55
|
+
if (typeof observability !== "object" ||
|
|
56
|
+
observability == null ||
|
|
57
|
+
!("counters" in observability) ||
|
|
58
|
+
!observability.counters) {
|
|
59
|
+
throw new Error("mu server /api/status missing control_plane.observability.counters (expected generation-scoped contract)");
|
|
60
|
+
}
|
|
61
|
+
return status;
|
|
62
|
+
}
|
|
42
63
|
export async function fetchMuStatus(timeoutMs) {
|
|
43
|
-
|
|
64
|
+
const status = await fetchMuJson("/api/status", { timeoutMs });
|
|
65
|
+
return ensureGenerationScopedStatus(status);
|
|
44
66
|
}
|
|
45
67
|
export function textResult(text, details = {}) {
|
|
46
68
|
return {
|
package/package.json
CHANGED
|
@@ -18,6 +18,8 @@ missing fields: {{missing_fields}}
|
|
|
18
18
|
2) Generate values you CAN create yourself (e.g. webhook_secret — run `openssl rand -hex 32` via Bash).
|
|
19
19
|
3) Write config via mu_messaging_setup tool: call mu_messaging_setup(action="apply", adapter="{{adapter_id}}", fields={...}) with ALL missing field values. This writes config and reloads the control plane in one step.
|
|
20
20
|
4) After config is applied, call provider APIs (e.g. Telegram setWebhook) via Bash/curl.
|
|
21
|
-
5)
|
|
21
|
+
5) Link an identity binding via mu_identity tool: call mu_identity(action="link", channel="{{adapter_id}}", actor_id="<actor>", tenant_id="<tenant>") — do NOT use `mu control link` CLI.
|
|
22
|
+
6) Run {{verify_command}} to confirm everything works.
|
|
22
23
|
|
|
23
24
|
Do NOT give the user copy-paste commands or tutorials. Do the work yourself.
|
|
25
|
+
For identity operations, always use the mu_identity tool (not `mu control link` or direct .mu file access).
|