@femtomc/mu-server 26.3.1 → 26.3.2
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/api/heartbeats.js
CHANGED
|
@@ -36,6 +36,39 @@ export async function heartbeatRoutes(request, url, deps, headers) {
|
|
|
36
36
|
if ("prompt" in body && typeof body.prompt !== "string" && body.prompt !== null) {
|
|
37
37
|
return Response.json({ error: "prompt must be string or null" }, { status: 400, headers });
|
|
38
38
|
}
|
|
39
|
+
const parseNullableString = (value) => {
|
|
40
|
+
if (value === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (value === null)
|
|
43
|
+
return null;
|
|
44
|
+
if (typeof value === "string")
|
|
45
|
+
return value;
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
const operatorProvider = parseNullableString(body.operator_provider);
|
|
49
|
+
if ("operator_provider" in body && operatorProvider === undefined) {
|
|
50
|
+
return Response.json({ error: "operator_provider must be string or null" }, { status: 400, headers });
|
|
51
|
+
}
|
|
52
|
+
const operatorModel = parseNullableString(body.operator_model);
|
|
53
|
+
if ("operator_model" in body && operatorModel === undefined) {
|
|
54
|
+
return Response.json({ error: "operator_model must be string or null" }, { status: 400, headers });
|
|
55
|
+
}
|
|
56
|
+
const operatorThinking = parseNullableString(body.operator_thinking);
|
|
57
|
+
if ("operator_thinking" in body && operatorThinking === undefined) {
|
|
58
|
+
return Response.json({ error: "operator_thinking must be string or null" }, { status: 400, headers });
|
|
59
|
+
}
|
|
60
|
+
const contextSessionId = parseNullableString(body.context_session_id);
|
|
61
|
+
if ("context_session_id" in body && contextSessionId === undefined) {
|
|
62
|
+
return Response.json({ error: "context_session_id must be string or null" }, { status: 400, headers });
|
|
63
|
+
}
|
|
64
|
+
const contextSessionFile = parseNullableString(body.context_session_file);
|
|
65
|
+
if ("context_session_file" in body && contextSessionFile === undefined) {
|
|
66
|
+
return Response.json({ error: "context_session_file must be string or null" }, { status: 400, headers });
|
|
67
|
+
}
|
|
68
|
+
const contextSessionDir = parseNullableString(body.context_session_dir);
|
|
69
|
+
if ("context_session_dir" in body && contextSessionDir === undefined) {
|
|
70
|
+
return Response.json({ error: "context_session_dir must be string or null" }, { status: 400, headers });
|
|
71
|
+
}
|
|
39
72
|
const prompt = typeof body.prompt === "string" ? body.prompt : body.prompt === null ? null : undefined;
|
|
40
73
|
const everyMs = typeof body.every_ms === "number" && Number.isFinite(body.every_ms)
|
|
41
74
|
? Math.max(0, Math.trunc(body.every_ms))
|
|
@@ -52,6 +85,12 @@ export async function heartbeatRoutes(request, url, deps, headers) {
|
|
|
52
85
|
metadata: body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata)
|
|
53
86
|
? body.metadata
|
|
54
87
|
: undefined,
|
|
88
|
+
operatorProvider,
|
|
89
|
+
operatorModel,
|
|
90
|
+
operatorThinking,
|
|
91
|
+
contextSessionId,
|
|
92
|
+
contextSessionFile,
|
|
93
|
+
contextSessionDir,
|
|
55
94
|
});
|
|
56
95
|
return Response.json({ ok: true, program }, { status: 201, headers });
|
|
57
96
|
}
|
|
@@ -75,6 +114,15 @@ export async function heartbeatRoutes(request, url, deps, headers) {
|
|
|
75
114
|
return Response.json({ error: "program_id is required" }, { status: 400, headers });
|
|
76
115
|
}
|
|
77
116
|
try {
|
|
117
|
+
const parseNullableString = (value) => {
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
return undefined;
|
|
120
|
+
if (value === null)
|
|
121
|
+
return null;
|
|
122
|
+
if (typeof value === "string")
|
|
123
|
+
return value;
|
|
124
|
+
return undefined;
|
|
125
|
+
};
|
|
78
126
|
const updateOpts = {
|
|
79
127
|
programId,
|
|
80
128
|
title: typeof body.title === "string" ? body.title : undefined,
|
|
@@ -98,6 +146,48 @@ export async function heartbeatRoutes(request, url, deps, headers) {
|
|
|
98
146
|
return Response.json({ error: "prompt must be string or null" }, { status: 400, headers });
|
|
99
147
|
}
|
|
100
148
|
}
|
|
149
|
+
if ("operator_provider" in body) {
|
|
150
|
+
const value = parseNullableString(body.operator_provider);
|
|
151
|
+
if (value === undefined) {
|
|
152
|
+
return Response.json({ error: "operator_provider must be string or null" }, { status: 400, headers });
|
|
153
|
+
}
|
|
154
|
+
updateOpts.operatorProvider = value;
|
|
155
|
+
}
|
|
156
|
+
if ("operator_model" in body) {
|
|
157
|
+
const value = parseNullableString(body.operator_model);
|
|
158
|
+
if (value === undefined) {
|
|
159
|
+
return Response.json({ error: "operator_model must be string or null" }, { status: 400, headers });
|
|
160
|
+
}
|
|
161
|
+
updateOpts.operatorModel = value;
|
|
162
|
+
}
|
|
163
|
+
if ("operator_thinking" in body) {
|
|
164
|
+
const value = parseNullableString(body.operator_thinking);
|
|
165
|
+
if (value === undefined) {
|
|
166
|
+
return Response.json({ error: "operator_thinking must be string or null" }, { status: 400, headers });
|
|
167
|
+
}
|
|
168
|
+
updateOpts.operatorThinking = value;
|
|
169
|
+
}
|
|
170
|
+
if ("context_session_id" in body) {
|
|
171
|
+
const value = parseNullableString(body.context_session_id);
|
|
172
|
+
if (value === undefined) {
|
|
173
|
+
return Response.json({ error: "context_session_id must be string or null" }, { status: 400, headers });
|
|
174
|
+
}
|
|
175
|
+
updateOpts.contextSessionId = value;
|
|
176
|
+
}
|
|
177
|
+
if ("context_session_file" in body) {
|
|
178
|
+
const value = parseNullableString(body.context_session_file);
|
|
179
|
+
if (value === undefined) {
|
|
180
|
+
return Response.json({ error: "context_session_file must be string or null" }, { status: 400, headers });
|
|
181
|
+
}
|
|
182
|
+
updateOpts.contextSessionFile = value;
|
|
183
|
+
}
|
|
184
|
+
if ("context_session_dir" in body) {
|
|
185
|
+
const value = parseNullableString(body.context_session_dir);
|
|
186
|
+
if (value === undefined) {
|
|
187
|
+
return Response.json({ error: "context_session_dir must be string or null" }, { status: 400, headers });
|
|
188
|
+
}
|
|
189
|
+
updateOpts.contextSessionDir = value;
|
|
190
|
+
}
|
|
101
191
|
const result = await deps.heartbeatPrograms.update(updateOpts);
|
|
102
192
|
if (result.ok) {
|
|
103
193
|
return Response.json(result, { headers });
|
|
@@ -9,6 +9,12 @@ export type HeartbeatProgramSnapshot = {
|
|
|
9
9
|
every_ms: number;
|
|
10
10
|
reason: string;
|
|
11
11
|
metadata: Record<string, unknown>;
|
|
12
|
+
operator_provider: string | null;
|
|
13
|
+
operator_model: string | null;
|
|
14
|
+
operator_thinking: string | null;
|
|
15
|
+
context_session_id: string | null;
|
|
16
|
+
context_session_file: string | null;
|
|
17
|
+
context_session_dir: string | null;
|
|
12
18
|
created_at_ms: number;
|
|
13
19
|
updated_at_ms: number;
|
|
14
20
|
last_triggered_at_ms: number | null;
|
|
@@ -67,6 +73,12 @@ export type HeartbeatProgramRegistryOpts = {
|
|
|
67
73
|
prompt: string | null;
|
|
68
74
|
reason: string;
|
|
69
75
|
metadata: Record<string, unknown>;
|
|
76
|
+
operatorProvider: string | null;
|
|
77
|
+
operatorModel: string | null;
|
|
78
|
+
operatorThinking: string | null;
|
|
79
|
+
contextSessionId: string | null;
|
|
80
|
+
contextSessionFile: string | null;
|
|
81
|
+
contextSessionDir: string | null;
|
|
70
82
|
triggeredAtMs: number;
|
|
71
83
|
}) => Promise<HeartbeatProgramDispatchResult>;
|
|
72
84
|
onTickEvent?: (event: HeartbeatProgramTickEvent) => void | Promise<void>;
|
|
@@ -88,6 +100,12 @@ export declare class HeartbeatProgramRegistry {
|
|
|
88
100
|
reason?: string;
|
|
89
101
|
enabled?: boolean;
|
|
90
102
|
metadata?: Record<string, unknown>;
|
|
103
|
+
operatorProvider?: string | null;
|
|
104
|
+
operatorModel?: string | null;
|
|
105
|
+
operatorThinking?: string | null;
|
|
106
|
+
contextSessionId?: string | null;
|
|
107
|
+
contextSessionFile?: string | null;
|
|
108
|
+
contextSessionDir?: string | null;
|
|
91
109
|
}): Promise<HeartbeatProgramSnapshot>;
|
|
92
110
|
update(opts: {
|
|
93
111
|
programId: string;
|
|
@@ -97,6 +115,12 @@ export declare class HeartbeatProgramRegistry {
|
|
|
97
115
|
reason?: string;
|
|
98
116
|
enabled?: boolean;
|
|
99
117
|
metadata?: Record<string, unknown>;
|
|
118
|
+
operatorProvider?: string | null;
|
|
119
|
+
operatorModel?: string | null;
|
|
120
|
+
operatorThinking?: string | null;
|
|
121
|
+
contextSessionId?: string | null;
|
|
122
|
+
contextSessionFile?: string | null;
|
|
123
|
+
contextSessionDir?: string | null;
|
|
100
124
|
}): Promise<HeartbeatProgramOperationResult>;
|
|
101
125
|
remove(programId: string): Promise<HeartbeatProgramOperationResult>;
|
|
102
126
|
trigger(opts: {
|
|
@@ -19,6 +19,13 @@ function normalizePrompt(value) {
|
|
|
19
19
|
}
|
|
20
20
|
return value;
|
|
21
21
|
}
|
|
22
|
+
function normalizeOptionalString(value) {
|
|
23
|
+
if (typeof value !== "string") {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
28
|
+
}
|
|
22
29
|
function normalizeProgram(row) {
|
|
23
30
|
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
24
31
|
return null;
|
|
@@ -43,7 +50,7 @@ function normalizeProgram(row) {
|
|
|
43
50
|
const lastResultRaw = typeof record.last_result === "string" ? record.last_result.trim().toLowerCase() : null;
|
|
44
51
|
const lastResult = lastResultRaw === "ok" || lastResultRaw === "coalesced" || lastResultRaw === "failed" ? lastResultRaw : null;
|
|
45
52
|
const reason = typeof record.reason === "string" && record.reason.trim().length > 0 ? record.reason.trim() : "scheduled";
|
|
46
|
-
|
|
53
|
+
const normalized = {
|
|
47
54
|
v: 1,
|
|
48
55
|
program_id: programId,
|
|
49
56
|
title,
|
|
@@ -52,12 +59,29 @@ function normalizeProgram(row) {
|
|
|
52
59
|
every_ms: everyMs,
|
|
53
60
|
reason,
|
|
54
61
|
metadata: sanitizeMetadata(record.metadata),
|
|
62
|
+
operator_provider: normalizeOptionalString(record.operator_provider),
|
|
63
|
+
operator_model: normalizeOptionalString(record.operator_model),
|
|
64
|
+
operator_thinking: normalizeOptionalString(record.operator_thinking),
|
|
65
|
+
context_session_id: normalizeOptionalString(record.context_session_id),
|
|
66
|
+
context_session_file: normalizeOptionalString(record.context_session_file),
|
|
67
|
+
context_session_dir: normalizeOptionalString(record.context_session_dir),
|
|
55
68
|
created_at_ms: createdAt,
|
|
56
69
|
updated_at_ms: updatedAt,
|
|
57
70
|
last_triggered_at_ms: lastTriggeredAt,
|
|
58
71
|
last_result: lastResult,
|
|
59
72
|
last_error: typeof record.last_error === "string" ? record.last_error : null,
|
|
60
73
|
};
|
|
74
|
+
const hasProvider = Boolean(normalized.operator_provider);
|
|
75
|
+
const hasModel = Boolean(normalized.operator_model);
|
|
76
|
+
if (hasProvider !== hasModel) {
|
|
77
|
+
normalized.operator_provider = null;
|
|
78
|
+
normalized.operator_model = null;
|
|
79
|
+
normalized.operator_thinking = null;
|
|
80
|
+
}
|
|
81
|
+
else if (!hasProvider) {
|
|
82
|
+
normalized.operator_thinking = null;
|
|
83
|
+
}
|
|
84
|
+
return normalized;
|
|
61
85
|
}
|
|
62
86
|
function sortPrograms(programs) {
|
|
63
87
|
return [...programs].sort((a, b) => {
|
|
@@ -67,6 +91,16 @@ function sortPrograms(programs) {
|
|
|
67
91
|
return a.program_id.localeCompare(b.program_id);
|
|
68
92
|
});
|
|
69
93
|
}
|
|
94
|
+
function validateOperatorRouting(program) {
|
|
95
|
+
const hasProvider = typeof program.operator_provider === "string" && program.operator_provider.length > 0;
|
|
96
|
+
const hasModel = typeof program.operator_model === "string" && program.operator_model.length > 0;
|
|
97
|
+
if (hasProvider !== hasModel) {
|
|
98
|
+
throw new Error("heartbeat_program_operator_provider_model_pair_required");
|
|
99
|
+
}
|
|
100
|
+
if (program.operator_thinking && !hasProvider) {
|
|
101
|
+
throw new Error("heartbeat_program_operator_thinking_requires_provider_model");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
70
104
|
export class HeartbeatProgramRegistry {
|
|
71
105
|
#store;
|
|
72
106
|
#heartbeatScheduler;
|
|
@@ -196,6 +230,12 @@ export class HeartbeatProgramRegistry {
|
|
|
196
230
|
prompt: program.prompt,
|
|
197
231
|
reason: heartbeatReason,
|
|
198
232
|
metadata: { ...program.metadata },
|
|
233
|
+
operatorProvider: program.operator_provider,
|
|
234
|
+
operatorModel: program.operator_model,
|
|
235
|
+
operatorThinking: program.operator_thinking,
|
|
236
|
+
contextSessionId: program.context_session_id,
|
|
237
|
+
contextSessionFile: program.context_session_file,
|
|
238
|
+
contextSessionDir: program.context_session_dir,
|
|
199
239
|
triggeredAtMs: nowMs,
|
|
200
240
|
});
|
|
201
241
|
if (result.status === "ok") {
|
|
@@ -297,12 +337,19 @@ export class HeartbeatProgramRegistry {
|
|
|
297
337
|
every_ms: this.#normalizeEveryMs(typeof opts.everyMs === "number" && Number.isFinite(opts.everyMs) ? opts.everyMs : 15_000),
|
|
298
338
|
reason: opts.reason?.trim() || "scheduled",
|
|
299
339
|
metadata: sanitizeMetadata(opts.metadata),
|
|
340
|
+
operator_provider: normalizeOptionalString(opts.operatorProvider),
|
|
341
|
+
operator_model: normalizeOptionalString(opts.operatorModel),
|
|
342
|
+
operator_thinking: normalizeOptionalString(opts.operatorThinking),
|
|
343
|
+
context_session_id: normalizeOptionalString(opts.contextSessionId),
|
|
344
|
+
context_session_file: normalizeOptionalString(opts.contextSessionFile),
|
|
345
|
+
context_session_dir: normalizeOptionalString(opts.contextSessionDir),
|
|
300
346
|
created_at_ms: nowMs,
|
|
301
347
|
updated_at_ms: nowMs,
|
|
302
348
|
last_triggered_at_ms: null,
|
|
303
349
|
last_result: null,
|
|
304
350
|
last_error: null,
|
|
305
351
|
};
|
|
352
|
+
validateOperatorRouting(program);
|
|
306
353
|
this.#programs.set(program.program_id, program);
|
|
307
354
|
this.#applySchedule(program);
|
|
308
355
|
await this.#persist();
|
|
@@ -346,6 +393,25 @@ export class HeartbeatProgramRegistry {
|
|
|
346
393
|
if (opts.metadata) {
|
|
347
394
|
program.metadata = sanitizeMetadata(opts.metadata);
|
|
348
395
|
}
|
|
396
|
+
if ("operatorProvider" in opts) {
|
|
397
|
+
program.operator_provider = normalizeOptionalString(opts.operatorProvider);
|
|
398
|
+
}
|
|
399
|
+
if ("operatorModel" in opts) {
|
|
400
|
+
program.operator_model = normalizeOptionalString(opts.operatorModel);
|
|
401
|
+
}
|
|
402
|
+
if ("operatorThinking" in opts) {
|
|
403
|
+
program.operator_thinking = normalizeOptionalString(opts.operatorThinking);
|
|
404
|
+
}
|
|
405
|
+
if ("contextSessionId" in opts) {
|
|
406
|
+
program.context_session_id = normalizeOptionalString(opts.contextSessionId);
|
|
407
|
+
}
|
|
408
|
+
if ("contextSessionFile" in opts) {
|
|
409
|
+
program.context_session_file = normalizeOptionalString(opts.contextSessionFile);
|
|
410
|
+
}
|
|
411
|
+
if ("contextSessionDir" in opts) {
|
|
412
|
+
program.context_session_dir = normalizeOptionalString(opts.contextSessionDir);
|
|
413
|
+
}
|
|
414
|
+
validateOperatorRouting(program);
|
|
349
415
|
const nowMs = Math.trunc(this.#nowMs());
|
|
350
416
|
program.updated_at_ms = nowMs;
|
|
351
417
|
this.#applySchedule(program);
|
package/dist/server.js
CHANGED
|
@@ -209,16 +209,37 @@ function createServer(options = {}) {
|
|
|
209
209
|
message: opts.message,
|
|
210
210
|
payload: opts.payload,
|
|
211
211
|
});
|
|
212
|
+
const operatorProvider = stringField(opts.payload, "operator_provider");
|
|
213
|
+
const operatorModel = stringField(opts.payload, "operator_model");
|
|
214
|
+
const operatorThinking = stringField(opts.payload, "operator_thinking");
|
|
215
|
+
const contextSessionId = stringField(opts.payload, "context_session_id");
|
|
216
|
+
const contextSessionFile = stringField(opts.payload, "context_session_file");
|
|
217
|
+
const contextSessionDir = stringField(opts.payload, "context_session_dir");
|
|
218
|
+
const defaultHeartbeatSessionId = wakeSource === "heartbeat_program" && programId ? `heartbeat-program:${programId}` : null;
|
|
219
|
+
const operatorSessionId = contextSessionId ?? defaultHeartbeatSessionId;
|
|
220
|
+
const autonomousMetadata = {
|
|
221
|
+
wake_id: wakeId,
|
|
222
|
+
wake_source: wakeSource,
|
|
223
|
+
program_id: programId,
|
|
224
|
+
source_ts_ms: sourceTsMs,
|
|
225
|
+
};
|
|
226
|
+
if (operatorProvider)
|
|
227
|
+
autonomousMetadata.operator_provider = operatorProvider;
|
|
228
|
+
if (operatorModel)
|
|
229
|
+
autonomousMetadata.operator_model = operatorModel;
|
|
230
|
+
if (operatorThinking)
|
|
231
|
+
autonomousMetadata.operator_thinking = operatorThinking;
|
|
232
|
+
if (operatorSessionId)
|
|
233
|
+
autonomousMetadata.operator_session_id = operatorSessionId;
|
|
234
|
+
if (contextSessionFile)
|
|
235
|
+
autonomousMetadata.operator_session_file = contextSessionFile;
|
|
236
|
+
if (contextSessionDir)
|
|
237
|
+
autonomousMetadata.operator_session_dir = contextSessionDir;
|
|
212
238
|
const turnResult = await autonomousIngress({
|
|
213
239
|
text: ingressText,
|
|
214
240
|
repoRoot: context.repoRoot,
|
|
215
241
|
requestId: turnRequestId,
|
|
216
|
-
metadata:
|
|
217
|
-
wake_id: wakeId,
|
|
218
|
-
wake_source: wakeSource,
|
|
219
|
-
program_id: programId,
|
|
220
|
-
source_ts_ms: sourceTsMs,
|
|
221
|
-
},
|
|
242
|
+
metadata: autonomousMetadata,
|
|
222
243
|
});
|
|
223
244
|
if (turnResult.kind === "noop" || turnResult.kind === "invalid") {
|
|
224
245
|
decision = {
|
|
@@ -12,18 +12,31 @@ export function createServerProgramCoordination(opts) {
|
|
|
12
12
|
heartbeatScheduler: opts.heartbeatScheduler,
|
|
13
13
|
dispatchWake: async (wakeOpts) => {
|
|
14
14
|
const prompt = wakeOpts.prompt && wakeOpts.prompt.trim().length > 0 ? wakeOpts.prompt : null;
|
|
15
|
+
const payload = {
|
|
16
|
+
wake_source: "heartbeat_program",
|
|
17
|
+
source_ts_ms: wakeOpts.triggeredAtMs,
|
|
18
|
+
program_id: wakeOpts.programId,
|
|
19
|
+
title: wakeOpts.title,
|
|
20
|
+
prompt,
|
|
21
|
+
reason: wakeOpts.reason,
|
|
22
|
+
metadata: wakeOpts.metadata,
|
|
23
|
+
};
|
|
24
|
+
if (wakeOpts.operatorProvider)
|
|
25
|
+
payload.operator_provider = wakeOpts.operatorProvider;
|
|
26
|
+
if (wakeOpts.operatorModel)
|
|
27
|
+
payload.operator_model = wakeOpts.operatorModel;
|
|
28
|
+
if (wakeOpts.operatorThinking)
|
|
29
|
+
payload.operator_thinking = wakeOpts.operatorThinking;
|
|
30
|
+
if (wakeOpts.contextSessionId)
|
|
31
|
+
payload.context_session_id = wakeOpts.contextSessionId;
|
|
32
|
+
if (wakeOpts.contextSessionFile)
|
|
33
|
+
payload.context_session_file = wakeOpts.contextSessionFile;
|
|
34
|
+
if (wakeOpts.contextSessionDir)
|
|
35
|
+
payload.context_session_dir = wakeOpts.contextSessionDir;
|
|
15
36
|
const wakeResult = await opts.emitOperatorWake({
|
|
16
37
|
dedupeKey: `heartbeat-program:${wakeOpts.programId}`,
|
|
17
38
|
message: prompt ?? `Heartbeat wake: ${wakeOpts.title}`,
|
|
18
|
-
payload
|
|
19
|
-
wake_source: "heartbeat_program",
|
|
20
|
-
source_ts_ms: wakeOpts.triggeredAtMs,
|
|
21
|
-
program_id: wakeOpts.programId,
|
|
22
|
-
title: wakeOpts.title,
|
|
23
|
-
prompt,
|
|
24
|
-
reason: wakeOpts.reason,
|
|
25
|
-
metadata: wakeOpts.metadata,
|
|
26
|
-
},
|
|
39
|
+
payload,
|
|
27
40
|
});
|
|
28
41
|
if (wakeResult.status === "coalesced") {
|
|
29
42
|
return { status: "coalesced", reason: wakeResult.reason };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-server",
|
|
3
|
-
"version": "26.3.
|
|
3
|
+
"version": "26.3.2",
|
|
4
4
|
"description": "HTTP API server for mu control-plane transport/session plus run/activity scheduling coordination.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"start": "bun run dist/cli.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@femtomc/mu-agent": "26.3.
|
|
34
|
-
"@femtomc/mu-control-plane": "26.3.
|
|
35
|
-
"@femtomc/mu-core": "26.3.
|
|
33
|
+
"@femtomc/mu-agent": "26.3.2",
|
|
34
|
+
"@femtomc/mu-control-plane": "26.3.2",
|
|
35
|
+
"@femtomc/mu-core": "26.3.2"
|
|
36
36
|
}
|
|
37
37
|
}
|