@femtomc/mu-server 26.2.107 → 26.2.109

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 CHANGED
@@ -194,11 +194,7 @@ Operational fallbacks:
194
194
  - `telegram_reply_to_message_id` metadata anchors replies when parseable.
195
195
  - Missing Slack/Telegram bot tokens surface capability reason codes (`*_bot_token_missing`) and retry behavior.
196
196
 
197
- Server channel renderers consume canonical `hud_docs` metadata (`HudDoc`) for Slack/Telegram HUD
198
- rendering + actions. Optional HUD presentation hints (`title_style`, `snapshot_style`, chip/item styles)
199
- and metadata presets (`metadata.style_preset`) may be used by richer renderers and safely ignored by
200
- plain-text channels. New features should extend the shared HUD contract path instead of bespoke
201
- channel-specific HUD payload formats.
197
+ Server channel renderers consume canonical `ui_docs` metadata (`UiDoc`) for Slack/Telegram/Discord/Neovim delivery. Interactive actions use callback tokens (`mu-ui:*`) with deterministic text fallbacks when tokens cannot be issued or rendered.
202
198
 
203
199
  ## Running the Server
204
200
 
@@ -99,6 +99,7 @@ const TEXT_ONLY_UI_COMPONENT_SUPPORT = {
99
99
  key_value: false,
100
100
  divider: false,
101
101
  };
102
+ const STATUS_PROFILE_ACTIONS_FALLBACK_REASON = "status_profile_actions_degrade_to_text";
102
103
  const CHANNEL_UI_CAPABILITIES = {
103
104
  slack: {
104
105
  supported: true,
@@ -106,7 +107,7 @@ const CHANNEL_UI_CAPABILITIES = {
106
107
  components: UI_COMPONENT_SUPPORT,
107
108
  actions: {
108
109
  supported: true,
109
- reason: null,
110
+ reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
110
111
  },
111
112
  },
112
113
  discord: {
@@ -115,7 +116,7 @@ const CHANNEL_UI_CAPABILITIES = {
115
116
  components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
116
117
  actions: {
117
118
  supported: true,
118
- reason: null,
119
+ reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
119
120
  },
120
121
  },
121
122
  telegram: {
@@ -124,7 +125,7 @@ const CHANNEL_UI_CAPABILITIES = {
124
125
  components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
125
126
  actions: {
126
127
  supported: true,
127
- reason: null,
128
+ reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
128
129
  },
129
130
  },
130
131
  neovim: {
@@ -133,7 +134,7 @@ const CHANNEL_UI_CAPABILITIES = {
133
134
  components: TEXT_ONLY_UI_COMPONENT_SUPPORT,
134
135
  actions: {
135
136
  supported: true,
136
- reason: null,
137
+ reason: STATUS_PROFILE_ACTIONS_FALLBACK_REASON,
137
138
  },
138
139
  },
139
140
  terminal: {
@@ -1,5 +1,5 @@
1
- import { applyHudStylePreset, normalizeHudDocs, normalizeUiDocs, } from "@femtomc/mu-core";
2
- import { ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneRuntime, buildSanitizedUiEventForAction, buildSlackHudActionId, getControlPlanePaths, issueUiDocActionPayloads, TelegramControlPlaneAdapterSpec, uiActionPayloadContextFromOutboxRecord, uiDocActionPayloadKey, UiCallbackTokenStore, } from "@femtomc/mu-control-plane";
1
+ import { normalizeUiDocs, resolveUiStatusProfileName, } from "@femtomc/mu-core";
2
+ import { ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneRuntime, buildSanitizedUiEventForAction, buildSlackUiActionId, getControlPlanePaths, issueUiDocActionPayloads, TelegramControlPlaneAdapterSpec, uiActionPayloadContextFromOutboxRecord, uiDocActionPayloadKey, UiCallbackTokenStore, } from "@femtomc/mu-control-plane";
3
3
  import { DEFAULT_MU_CONFIG } from "./config.js";
4
4
  import { buildMessagingOperatorRuntime, createOutboxDrainLoop } from "./control_plane_bootstrap_helpers.js";
5
5
  import { buildWakeOutboundEnvelope, resolveWakeFanoutCapability, wakeDeliveryMetadataFromOutboxRecord, wakeDispatchReasonCode, wakeFanoutDedupeKey, } from "./control_plane_wake_delivery.js";
@@ -147,9 +147,7 @@ const SLACK_BLOCKS_MAX = 50;
147
147
  const SLACK_ACTIONS_MAX_PER_BLOCK = 5;
148
148
  const SLACK_ACTIONS_MAX_TOTAL = 20;
149
149
  const SLACK_ACTION_VALUE_MAX_CHARS = 2_000;
150
- const SLACK_UI_EVENT_ACTION_ID = buildSlackHudActionId("ui_event");
151
- const SLACK_DOCS_MAX = 3;
152
- const SLACK_SECTION_LINE_MAX = 8;
150
+ const SLACK_UI_EVENT_ACTION_ID = buildSlackUiActionId("ui_event");
153
151
  const UI_DOCS_MAX = 3;
154
152
  const UI_CALLBACK_TOKEN_TTL_MS = 15 * 60_000;
155
153
  export const UI_COMPONENT_SUPPORT = {
@@ -159,9 +157,6 @@ export const UI_COMPONENT_SUPPORT = {
159
157
  divider: true,
160
158
  };
161
159
  export const UI_ACTIONS_UNSUPPORTED_REASON = "ui_actions_not_implemented";
162
- function hudDocsForPresentation(input, maxDocs) {
163
- return normalizeHudDocs(input, { maxDocs }).map((doc) => applyHudStylePreset(doc) ?? doc);
164
- }
165
160
  function truncateSlackText(text, maxLen = SLACK_BLOCK_TEXT_MAX_LEN) {
166
161
  if (text.length <= maxLen) {
167
162
  return text;
@@ -171,103 +166,6 @@ function truncateSlackText(text, maxLen = SLACK_BLOCK_TEXT_MAX_LEN) {
171
166
  }
172
167
  return `${text.slice(0, maxLen - 1)}…`;
173
168
  }
174
- function hudTonePrefix(tone) {
175
- switch (tone) {
176
- case "success":
177
- return "✅";
178
- case "warning":
179
- return "⚠️";
180
- case "error":
181
- return "⛔";
182
- case "accent":
183
- return "🔹";
184
- case "muted":
185
- return "▫️";
186
- case "dim":
187
- return "·";
188
- case "info":
189
- default:
190
- return "ℹ️";
191
- }
192
- }
193
- function applySlackHudTextStyle(text, style, opts = {}) {
194
- const weight = style?.weight ?? opts.defaultWeight;
195
- const italic = style?.italic ?? opts.defaultItalic ?? false;
196
- const code = style?.code ?? opts.defaultCode ?? false;
197
- let out = text;
198
- if (code) {
199
- out = `\`${out}\``;
200
- }
201
- if (italic) {
202
- out = `_${out}_`;
203
- }
204
- if (weight === "strong") {
205
- out = `*${out}*`;
206
- }
207
- return out;
208
- }
209
- function stripSlackMrkdwn(text) {
210
- return text.replace(/[*_`~]/g, "");
211
- }
212
- function hudDocSectionLines(doc) {
213
- const lines = [];
214
- const chipsLine = doc.chips
215
- .slice(0, 8)
216
- .map((chip) => {
217
- const label = applySlackHudTextStyle(chip.label, chip.style, { defaultWeight: "strong" });
218
- return `${hudTonePrefix(chip.tone)} ${label}`;
219
- })
220
- .join(" · ");
221
- if (chipsLine.length > 0) {
222
- lines.push(chipsLine);
223
- }
224
- for (const section of doc.sections) {
225
- switch (section.kind) {
226
- case "kv": {
227
- const title = applySlackHudTextStyle(section.title ?? "Details", section.title_style, { defaultWeight: "strong" });
228
- const items = section.items.slice(0, SLACK_SECTION_LINE_MAX).map((item) => {
229
- const value = applySlackHudTextStyle(item.value, item.value_style);
230
- return `• *${item.label}:* ${value}`;
231
- });
232
- if (items.length > 0) {
233
- lines.push([title, ...items].join("\n"));
234
- }
235
- break;
236
- }
237
- case "checklist": {
238
- const title = applySlackHudTextStyle(section.title ?? "Checklist", section.title_style, { defaultWeight: "strong" });
239
- const items = section.items
240
- .slice(0, SLACK_SECTION_LINE_MAX)
241
- .map((item) => `${item.done ? "✅" : "⬜"} ${applySlackHudTextStyle(item.label, item.style)}`);
242
- if (items.length > 0) {
243
- lines.push([title, ...items].join("\n"));
244
- }
245
- break;
246
- }
247
- case "activity": {
248
- const title = applySlackHudTextStyle(section.title ?? "Activity", section.title_style, { defaultWeight: "strong" });
249
- const items = section.lines.slice(0, SLACK_SECTION_LINE_MAX).map((line) => `• ${line}`);
250
- if (items.length > 0) {
251
- lines.push([title, ...items].join("\n"));
252
- }
253
- break;
254
- }
255
- case "text": {
256
- const title = section.title
257
- ? `${applySlackHudTextStyle(section.title, section.title_style, { defaultWeight: "strong" })}\n`
258
- : "";
259
- const text = applySlackHudTextStyle(section.text, section.style);
260
- lines.push(`${title}${text}`);
261
- break;
262
- }
263
- }
264
- }
265
- if (lines.length === 0) {
266
- const snapshot = applySlackHudTextStyle(doc.snapshot_compact, doc.snapshot_style);
267
- lines.push(`${applySlackHudTextStyle("Snapshot", undefined, { defaultWeight: "strong" })}\n${snapshot}`);
268
- }
269
- return lines;
270
- }
271
169
  function uiDocComponentLines(doc) {
272
170
  const lines = [];
273
171
  const components = [...doc.components].sort((a, b) => a.id.localeCompare(b.id));
@@ -350,17 +248,6 @@ function appendUiDocText(body, uiDocs) {
350
248
  }
351
249
  return `${trimmed}\n\n${fallback}`;
352
250
  }
353
- function hudActionButtons(doc) {
354
- return doc.actions.slice(0, SLACK_ACTIONS_MAX_TOTAL).map((action) => ({
355
- type: "button",
356
- text: {
357
- type: "plain_text",
358
- text: truncateSlackText(action.label, 75),
359
- },
360
- value: truncateSlackText(action.command_text, SLACK_ACTION_VALUE_MAX_CHARS),
361
- action_id: buildSlackHudActionId(action.id),
362
- }));
363
- }
364
251
  function uiDocActionTextLine(action, opts = {}) {
365
252
  const parts = [`• ${action.label}`];
366
253
  if (action.description) {
@@ -369,11 +256,36 @@ function uiDocActionTextLine(action, opts = {}) {
369
256
  parts.push(`(id=${action.id}${opts.suffix ? `; ${opts.suffix}` : ""})`);
370
257
  return parts.join(" ");
371
258
  }
259
+ function statusProfileVariant(doc) {
260
+ const profile = doc.metadata.profile;
261
+ if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
262
+ return "status";
263
+ }
264
+ const rawVariant = typeof profile.variant === "string"
265
+ ? profile.variant.trim().toLowerCase()
266
+ : "";
267
+ return rawVariant.length > 0 ? rawVariant : "status";
268
+ }
269
+ function isStatusProfileStatusVariant(doc) {
270
+ return resolveUiStatusProfileName(doc) !== null && statusProfileVariant(doc) === "status";
271
+ }
272
+ function uiDocDeterministicActionFallbackLine(action) {
273
+ const commandText = commandTextForUiDocAction(action);
274
+ if (commandText) {
275
+ return `• ${action.label}: ${commandText}`;
276
+ }
277
+ return `• ${action.label}: interactive unavailable (missing command_text)`;
278
+ }
372
279
  function uiDocActionButtons(doc, actionPayloadsByKey) {
373
280
  const buttons = [];
374
281
  const fallbackLines = [];
282
+ const statusProfile = isStatusProfileStatusVariant(doc);
375
283
  const actions = [...doc.actions].sort((a, b) => a.id.localeCompare(b.id)).slice(0, SLACK_ACTIONS_MAX_TOTAL);
376
284
  for (const action of actions) {
285
+ if (statusProfile) {
286
+ fallbackLines.push(uiDocDeterministicActionFallbackLine(action));
287
+ continue;
288
+ }
377
289
  const payload = actionPayloadsByKey.get(uiDocActionPayloadKey(doc.ui_id, action.id));
378
290
  if (!payload) {
379
291
  fallbackLines.push(uiDocActionTextLine(action, { suffix: "interactive unavailable" }));
@@ -415,9 +327,8 @@ export function splitSlackMessageText(text, maxLen = SLACK_MESSAGE_MAX_LEN) {
415
327
  }
416
328
  return chunks;
417
329
  }
418
- function slackBlocksForOutboxRecord(record, body, uiDocs, opts = {}) {
419
- const hudDocs = hudDocsForPresentation(record.envelope.metadata?.hud_docs, SLACK_DOCS_MAX);
420
- if (hudDocs.length === 0 && uiDocs.length === 0) {
330
+ function slackBlocksForOutboxRecord(body, uiDocs, opts = {}) {
331
+ if (uiDocs.length === 0) {
421
332
  return undefined;
422
333
  }
423
334
  const uiDocActionPayloadsByKey = opts.uiDocActionPayloadsByKey ?? new Map();
@@ -427,35 +338,6 @@ function slackBlocksForOutboxRecord(record, body, uiDocs, opts = {}) {
427
338
  type: "section",
428
339
  text: { type: "mrkdwn", text: truncateSlackText(headerText) },
429
340
  });
430
- for (const doc of hudDocs) {
431
- if (blocks.length >= SLACK_BLOCKS_MAX) {
432
- break;
433
- }
434
- const styledTitle = applySlackHudTextStyle(doc.title, doc.title_style, { defaultWeight: "strong" });
435
- blocks.push({
436
- type: "context",
437
- elements: [{ type: "mrkdwn", text: truncateSlackText(`*HUD* · ${styledTitle}`) }],
438
- });
439
- for (const line of hudDocSectionLines(doc)) {
440
- if (blocks.length >= SLACK_BLOCKS_MAX) {
441
- break;
442
- }
443
- blocks.push({
444
- type: "section",
445
- text: { type: "mrkdwn", text: truncateSlackText(line) },
446
- });
447
- }
448
- const buttons = hudActionButtons(doc);
449
- for (let idx = 0; idx < buttons.length; idx += SLACK_ACTIONS_MAX_PER_BLOCK) {
450
- if (blocks.length >= SLACK_BLOCKS_MAX) {
451
- break;
452
- }
453
- blocks.push({
454
- type: "actions",
455
- elements: buttons.slice(idx, idx + SLACK_ACTIONS_MAX_PER_BLOCK),
456
- });
457
- }
458
- }
459
341
  for (const doc of uiDocs) {
460
342
  if (blocks.length >= SLACK_BLOCKS_MAX) {
461
343
  break;
@@ -515,21 +397,11 @@ function slackStatusMessageTsFromMetadata(metadata) {
515
397
  const TELEGRAM_CALLBACK_DATA_MAX_BYTES = 64;
516
398
  const TELEGRAM_ACTIONS_MAX_TOTAL = 20;
517
399
  const TELEGRAM_ACTIONS_PER_ROW = 3;
518
- const TELEGRAM_DOCS_MAX = 2;
519
400
  function utf8ByteLength(value) {
520
401
  return new TextEncoder().encode(value).length;
521
402
  }
522
- function telegramTextForOutboxRecord(record, body, uiDocs) {
523
- const hudDocs = hudDocsForPresentation(record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
403
+ function telegramTextForOutboxRecord(body, uiDocs) {
524
404
  const lines = [body.trim()];
525
- if (hudDocs.length > 0) {
526
- for (const doc of hudDocs) {
527
- lines.push("", `HUD · ${doc.title}`);
528
- for (const sectionLine of hudDocSectionLines(doc)) {
529
- lines.push(stripSlackMrkdwn(sectionLine));
530
- }
531
- }
532
- }
533
405
  const uiFallback = uiDocsTextFallback(uiDocs);
534
406
  if (uiFallback) {
535
407
  lines.push("", uiFallback);
@@ -552,43 +424,6 @@ function telegramOverflowText(lines) {
552
424
  }
553
425
  return `\n\nActions:\n${lines.join("\n")}`;
554
426
  }
555
- async function compileTelegramHudActions(opts) {
556
- const hudDocs = hudDocsForPresentation(opts.record.envelope.metadata?.hud_docs, TELEGRAM_DOCS_MAX);
557
- if (hudDocs.length === 0) {
558
- return { buttonEntries: [], overflowLines: [] };
559
- }
560
- const buttonEntries = [];
561
- const overflowLines = [];
562
- const actions = hudDocs.flatMap((doc) => doc.actions).slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
563
- for (const action of actions) {
564
- const fallbackLine = `• ${action.label}: ${action.command_text}`;
565
- let callbackData = action.command_text;
566
- if (opts.encodeCallbackData) {
567
- try {
568
- callbackData = await opts.encodeCallbackData(action.command_text, { record: opts.record });
569
- }
570
- catch {
571
- overflowLines.push(fallbackLine);
572
- continue;
573
- }
574
- }
575
- if (utf8ByteLength(callbackData) > TELEGRAM_CALLBACK_DATA_MAX_BYTES) {
576
- overflowLines.push(fallbackLine);
577
- continue;
578
- }
579
- buttonEntries.push({
580
- button: {
581
- text: action.label.slice(0, 64),
582
- callback_data: callbackData,
583
- },
584
- fallbackLine,
585
- });
586
- }
587
- return {
588
- buttonEntries,
589
- overflowLines,
590
- };
591
- }
592
427
  function commandTextForUiDocAction(action) {
593
428
  const fromMetadata = typeof action.metadata.command_text === "string" ? action.metadata.command_text.trim() : "";
594
429
  if (fromMetadata.length === 0) {
@@ -603,17 +438,22 @@ async function compileTelegramUiDocActions(opts) {
603
438
  const buttonEntries = [];
604
439
  const overflowLines = [];
605
440
  for (const doc of opts.uiDocs) {
441
+ const statusProfile = isStatusProfileStatusVariant(doc);
606
442
  const actions = [...doc.actions].sort((a, b) => a.id.localeCompare(b.id));
607
443
  for (const action of actions) {
444
+ const commandText = commandTextForUiDocAction(action);
445
+ const fallbackLine = commandText
446
+ ? `• ${action.label}: ${commandText}`
447
+ : `• ${action.label}: interactive unavailable (missing command_text)`;
448
+ if (statusProfile) {
449
+ overflowLines.push(fallbackLine);
450
+ continue;
451
+ }
608
452
  const uiEvent = buildSanitizedUiEventForAction({
609
453
  doc,
610
454
  action,
611
455
  createdAtMs: opts.nowMs,
612
456
  });
613
- const commandText = commandTextForUiDocAction(action);
614
- const fallbackLine = commandText
615
- ? `• ${action.label}: ${commandText}`
616
- : `• ${action.label}: interactive unavailable (missing command_text)`;
617
457
  if (!commandText || !uiEvent) {
618
458
  overflowLines.push(fallbackLine);
619
459
  continue;
@@ -736,28 +576,22 @@ async function sendTelegramTextChunks(opts) {
736
576
  }
737
577
  export async function deliverTelegramOutboxRecord(opts) {
738
578
  const { botToken, record } = opts;
739
- const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs: TELEGRAM_DOCS_MAX });
579
+ const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs: UI_DOCS_MAX });
740
580
  const nowMs = Math.trunc(Date.now());
741
- const hudActions = await compileTelegramHudActions({
742
- record,
743
- encodeCallbackData: opts.encodeCallbackData,
744
- });
745
581
  const uiActions = await compileTelegramUiDocActions({
746
582
  record,
747
583
  uiDocs,
748
584
  nowMs,
749
585
  encodeCallbackData: opts.encodeCallbackData,
750
586
  });
751
- const combinedEntries = [...hudActions.buttonEntries, ...uiActions.buttonEntries];
752
- const visibleEntries = combinedEntries.slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
587
+ const visibleEntries = uiActions.buttonEntries.slice(0, TELEGRAM_ACTIONS_MAX_TOTAL);
753
588
  const overflowLines = [
754
- ...hudActions.overflowLines,
755
589
  ...uiActions.overflowLines,
756
- ...combinedEntries.slice(TELEGRAM_ACTIONS_MAX_TOTAL).map((entry) => entry.fallbackLine),
590
+ ...uiActions.buttonEntries.slice(TELEGRAM_ACTIONS_MAX_TOTAL).map((entry) => entry.fallbackLine),
757
591
  ];
758
592
  const replyMarkup = telegramReplyMarkupFromButtons(visibleEntries.map((entry) => entry.button));
759
593
  const replyToMessageId = maybeParseTelegramMessageId(record.envelope.metadata?.telegram_reply_to_message_id);
760
- const telegramText = `${telegramTextForOutboxRecord(record, record.envelope.body, uiDocs)}${telegramOverflowText(overflowLines)}`.trim();
594
+ const telegramText = `${telegramTextForOutboxRecord(record.envelope.body, uiDocs)}${telegramOverflowText(overflowLines)}`.trim();
761
595
  const fallbackMessagePayload = {
762
596
  ...buildTelegramSendMessagePayload({
763
597
  chatId: record.envelope.channel_conversation_id,
@@ -916,10 +750,11 @@ export async function deliverSlackOutboxRecord(opts) {
916
750
  const fetchImpl = opts.fetchImpl ?? fetch;
917
751
  const attachments = record.envelope.attachments ?? [];
918
752
  const uiDocs = normalizeUiDocs(record.envelope.metadata?.ui_docs, { maxDocs: UI_DOCS_MAX });
753
+ const interactiveUiDocs = uiDocs.filter((doc) => !isStatusProfileStatusVariant(doc));
919
754
  let uiDocActionPayloadsByKey = new Map();
920
- if (opts.uiCallbackTokenStore && uiDocs.some((doc) => doc.actions.length > 0)) {
755
+ if (opts.uiCallbackTokenStore && interactiveUiDocs.some((doc) => doc.actions.length > 0)) {
921
756
  const issued = await issueUiDocActionPayloads({
922
- uiDocs,
757
+ uiDocs: interactiveUiDocs,
923
758
  tokenStore: opts.uiCallbackTokenStore,
924
759
  context: uiActionPayloadContextFromOutboxRecord(record),
925
760
  ttlMs: UI_CALLBACK_TOKEN_TTL_MS,
@@ -928,7 +763,7 @@ export async function deliverSlackOutboxRecord(opts) {
928
763
  uiDocActionPayloadsByKey = new Map(issued.map((entry) => [entry.key, entry.payload_json]));
929
764
  }
930
765
  const renderedBodyForBlocks = renderSlackMarkdown(record.envelope.body);
931
- const blocks = slackBlocksForOutboxRecord(record, renderedBodyForBlocks, uiDocs, {
766
+ const blocks = slackBlocksForOutboxRecord(renderedBodyForBlocks, uiDocs, {
932
767
  uiDocActionPayloadsByKey,
933
768
  });
934
769
  const bodyForText = blocks
package/package.json CHANGED
@@ -1,37 +1,37 @@
1
1
  {
2
- "name": "@femtomc/mu-server",
3
- "version": "26.2.107",
4
- "description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
5
- "keywords": [
6
- "mu",
7
- "server",
8
- "api",
9
- "web",
10
- "automation"
11
- ],
12
- "type": "module",
13
- "main": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
- "bin": {
16
- "mu-server": "./dist/cli.js"
17
- },
18
- "exports": {
19
- ".": {
20
- "types": "./dist/index.d.ts",
21
- "default": "./dist/index.js"
22
- }
23
- },
24
- "files": [
25
- "dist/**"
26
- ],
27
- "scripts": {
28
- "build": "tsc -p tsconfig.build.json",
29
- "test": "bun test",
30
- "start": "bun run dist/cli.js"
31
- },
32
- "dependencies": {
33
- "@femtomc/mu-agent": "26.2.107",
34
- "@femtomc/mu-control-plane": "26.2.107",
35
- "@femtomc/mu-core": "26.2.107"
36
- }
2
+ "name": "@femtomc/mu-server",
3
+ "version": "26.2.109",
4
+ "description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
5
+ "keywords": [
6
+ "mu",
7
+ "server",
8
+ "api",
9
+ "web",
10
+ "automation"
11
+ ],
12
+ "type": "module",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "bin": {
16
+ "mu-server": "./dist/cli.js"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/**"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "test": "bun test",
30
+ "start": "bun run dist/cli.js"
31
+ },
32
+ "dependencies": {
33
+ "@femtomc/mu-agent": "26.2.109",
34
+ "@femtomc/mu-control-plane": "26.2.109",
35
+ "@femtomc/mu-core": "26.2.109"
36
+ }
37
37
  }