@femtomc/mu-server 26.2.75 → 26.2.76
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 +24 -35
- package/dist/api/control_plane.js +35 -0
- package/dist/api/events.js +7 -3
- package/dist/api/heartbeats.js +19 -2
- package/dist/api/identities.js +3 -3
- package/dist/api/runs.js +6 -6
- package/dist/api/session_turn.d.ts +0 -36
- package/dist/api/session_turn.js +32 -372
- package/dist/cli.js +4 -4
- package/dist/config.d.ts +15 -0
- package/dist/config.js +70 -1
- package/dist/control_plane.js +1 -1
- package/dist/control_plane_bootstrap_helpers.js +3 -2
- package/dist/control_plane_contract.d.ts +1 -1
- package/dist/cron_programs.js +3 -2
- package/dist/heartbeat_programs.d.ts +4 -0
- package/dist/heartbeat_programs.js +17 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/dist/memory_index_maintainer.d.ts +15 -0
- package/dist/memory_index_maintainer.js +165 -0
- package/dist/run_queue.js +2 -2
- package/dist/run_supervisor.d.ts +4 -4
- package/dist/run_supervisor.js +6 -5
- package/dist/server.d.ts +0 -3
- package/dist/server.js +26 -23
- package/dist/server_program_orchestration.js +6 -1
- package/dist/server_routing.d.ts +0 -2
- package/dist/server_routing.js +1 -43
- package/package.json +4 -4
- package/dist/activity_supervisor.d.ts +0 -81
- package/dist/activity_supervisor.js +0 -306
- package/dist/api/activities.d.ts +0 -2
- package/dist/api/activities.js +0 -160
- package/dist/api/session_flash.d.ts +0 -60
- package/dist/api/session_flash.js +0 -326
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
|
|
2
|
-
export type ControlPlaneActivityStatus = "running" | "completed" | "failed" | "cancelled";
|
|
3
|
-
export type ControlPlaneActivitySnapshot = {
|
|
4
|
-
activity_id: string;
|
|
5
|
-
kind: string;
|
|
6
|
-
title: string;
|
|
7
|
-
status: ControlPlaneActivityStatus;
|
|
8
|
-
heartbeat_every_ms: number;
|
|
9
|
-
heartbeat_count: number;
|
|
10
|
-
last_heartbeat_at_ms: number | null;
|
|
11
|
-
last_heartbeat_reason: string | null;
|
|
12
|
-
last_progress: string | null;
|
|
13
|
-
final_message: string | null;
|
|
14
|
-
metadata: Record<string, unknown>;
|
|
15
|
-
source: "api" | "command" | "system";
|
|
16
|
-
started_at_ms: number;
|
|
17
|
-
updated_at_ms: number;
|
|
18
|
-
finished_at_ms: number | null;
|
|
19
|
-
};
|
|
20
|
-
export type ControlPlaneActivityEventKind = "activity_started" | "activity_progress" | "activity_heartbeat" | "activity_completed" | "activity_failed" | "activity_cancelled";
|
|
21
|
-
export type ControlPlaneActivityEvent = {
|
|
22
|
-
seq: number;
|
|
23
|
-
ts_ms: number;
|
|
24
|
-
kind: ControlPlaneActivityEventKind;
|
|
25
|
-
message: string;
|
|
26
|
-
activity: ControlPlaneActivitySnapshot;
|
|
27
|
-
};
|
|
28
|
-
export type ControlPlaneActivityMutationResult = {
|
|
29
|
-
ok: boolean;
|
|
30
|
-
reason: "not_found" | "not_running" | "missing_target" | null;
|
|
31
|
-
activity: ControlPlaneActivitySnapshot | null;
|
|
32
|
-
};
|
|
33
|
-
export type ControlPlaneActivitySupervisorOpts = {
|
|
34
|
-
nowMs?: () => number;
|
|
35
|
-
heartbeatScheduler?: ActivityHeartbeatScheduler;
|
|
36
|
-
defaultHeartbeatEveryMs?: number;
|
|
37
|
-
maxHistory?: number;
|
|
38
|
-
maxEventsPerActivity?: number;
|
|
39
|
-
onEvent?: (event: ControlPlaneActivityEvent) => void | Promise<void>;
|
|
40
|
-
};
|
|
41
|
-
export declare class ControlPlaneActivitySupervisor {
|
|
42
|
-
#private;
|
|
43
|
-
constructor(opts?: ControlPlaneActivitySupervisorOpts);
|
|
44
|
-
start(opts: {
|
|
45
|
-
title: string;
|
|
46
|
-
kind?: string;
|
|
47
|
-
heartbeatEveryMs?: number;
|
|
48
|
-
metadata?: Record<string, unknown>;
|
|
49
|
-
source?: "api" | "command" | "system";
|
|
50
|
-
}): ControlPlaneActivitySnapshot;
|
|
51
|
-
list(opts?: {
|
|
52
|
-
status?: ControlPlaneActivityStatus;
|
|
53
|
-
kind?: string;
|
|
54
|
-
limit?: number;
|
|
55
|
-
}): ControlPlaneActivitySnapshot[];
|
|
56
|
-
get(activityId: string): ControlPlaneActivitySnapshot | null;
|
|
57
|
-
events(activityId: string, opts?: {
|
|
58
|
-
limit?: number;
|
|
59
|
-
}): ControlPlaneActivityEvent[] | null;
|
|
60
|
-
progress(opts: {
|
|
61
|
-
activityId?: string | null;
|
|
62
|
-
message?: string | null;
|
|
63
|
-
}): ControlPlaneActivityMutationResult;
|
|
64
|
-
heartbeat(opts: {
|
|
65
|
-
activityId?: string | null;
|
|
66
|
-
reason?: string | null;
|
|
67
|
-
}): ControlPlaneActivityMutationResult;
|
|
68
|
-
complete(opts: {
|
|
69
|
-
activityId?: string | null;
|
|
70
|
-
message?: string | null;
|
|
71
|
-
}): ControlPlaneActivityMutationResult;
|
|
72
|
-
fail(opts: {
|
|
73
|
-
activityId?: string | null;
|
|
74
|
-
message?: string | null;
|
|
75
|
-
}): ControlPlaneActivityMutationResult;
|
|
76
|
-
cancel(opts: {
|
|
77
|
-
activityId?: string | null;
|
|
78
|
-
message?: string | null;
|
|
79
|
-
}): ControlPlaneActivityMutationResult;
|
|
80
|
-
stop(): void;
|
|
81
|
-
}
|
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
import { ActivityHeartbeatScheduler } from "./heartbeat_scheduler.js";
|
|
2
|
-
const DEFAULT_HEARTBEAT_EVERY_MS = 15_000;
|
|
3
|
-
function defaultNowMs() {
|
|
4
|
-
return Date.now();
|
|
5
|
-
}
|
|
6
|
-
function normalizeKind(value) {
|
|
7
|
-
const trimmed = value?.trim();
|
|
8
|
-
if (!trimmed) {
|
|
9
|
-
return "generic";
|
|
10
|
-
}
|
|
11
|
-
return trimmed.toLowerCase();
|
|
12
|
-
}
|
|
13
|
-
function normalizeTitle(value) {
|
|
14
|
-
const trimmed = value.trim();
|
|
15
|
-
if (trimmed.length === 0) {
|
|
16
|
-
throw new Error("activity_title_required");
|
|
17
|
-
}
|
|
18
|
-
return trimmed;
|
|
19
|
-
}
|
|
20
|
-
function normalizeHeartbeatEveryMs(value, fallback) {
|
|
21
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
22
|
-
return Math.max(0, Math.trunc(value));
|
|
23
|
-
}
|
|
24
|
-
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
25
|
-
return Math.max(0, Number.parseInt(value.trim(), 10));
|
|
26
|
-
}
|
|
27
|
-
return fallback;
|
|
28
|
-
}
|
|
29
|
-
function toSafeMetadata(value) {
|
|
30
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
33
|
-
return { ...value };
|
|
34
|
-
}
|
|
35
|
-
function pushBounded(items, value, max) {
|
|
36
|
-
items.push(value);
|
|
37
|
-
if (items.length <= max) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
items.splice(0, items.length - max);
|
|
41
|
-
}
|
|
42
|
-
function elapsedSeconds(snapshot, nowMs) {
|
|
43
|
-
return Math.max(0, Math.trunc((nowMs - snapshot.started_at_ms) / 1_000));
|
|
44
|
-
}
|
|
45
|
-
export class ControlPlaneActivitySupervisor {
|
|
46
|
-
#nowMs;
|
|
47
|
-
#heartbeatScheduler;
|
|
48
|
-
#ownsHeartbeatScheduler;
|
|
49
|
-
#defaultHeartbeatEveryMs;
|
|
50
|
-
#maxHistory;
|
|
51
|
-
#maxEventsPerActivity;
|
|
52
|
-
#onEvent;
|
|
53
|
-
#activities = new Map();
|
|
54
|
-
#seq = 0;
|
|
55
|
-
#counter = 0;
|
|
56
|
-
constructor(opts = {}) {
|
|
57
|
-
this.#nowMs = opts.nowMs ?? defaultNowMs;
|
|
58
|
-
this.#heartbeatScheduler = opts.heartbeatScheduler ?? new ActivityHeartbeatScheduler();
|
|
59
|
-
this.#ownsHeartbeatScheduler = !opts.heartbeatScheduler;
|
|
60
|
-
this.#defaultHeartbeatEveryMs = Math.max(0, Math.trunc(opts.defaultHeartbeatEveryMs ?? DEFAULT_HEARTBEAT_EVERY_MS));
|
|
61
|
-
this.#maxHistory = Math.max(20, Math.trunc(opts.maxHistory ?? 200));
|
|
62
|
-
this.#maxEventsPerActivity = Math.max(20, Math.trunc(opts.maxEventsPerActivity ?? 400));
|
|
63
|
-
this.#onEvent = opts.onEvent ?? null;
|
|
64
|
-
}
|
|
65
|
-
#nextActivityId() {
|
|
66
|
-
this.#counter += 1;
|
|
67
|
-
return `activity-${this.#counter.toString(36)}-${crypto.randomUUID().slice(0, 8)}`;
|
|
68
|
-
}
|
|
69
|
-
#snapshot(activity) {
|
|
70
|
-
return {
|
|
71
|
-
...activity.snapshot,
|
|
72
|
-
metadata: { ...activity.snapshot.metadata },
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
#touch(activity) {
|
|
76
|
-
activity.snapshot.updated_at_ms = Math.trunc(this.#nowMs());
|
|
77
|
-
}
|
|
78
|
-
#emit(kind, activity, message) {
|
|
79
|
-
const event = {
|
|
80
|
-
seq: ++this.#seq,
|
|
81
|
-
ts_ms: Math.trunc(this.#nowMs()),
|
|
82
|
-
kind,
|
|
83
|
-
message,
|
|
84
|
-
activity: this.#snapshot(activity),
|
|
85
|
-
};
|
|
86
|
-
pushBounded(activity.events, event, this.#maxEventsPerActivity);
|
|
87
|
-
if (!this.#onEvent) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
void Promise.resolve(this.#onEvent(event)).catch(() => {
|
|
91
|
-
// Do not crash on notifier failures.
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
#pruneHistory() {
|
|
95
|
-
const rows = [...this.#activities.values()].sort((a, b) => {
|
|
96
|
-
if (a.snapshot.started_at_ms !== b.snapshot.started_at_ms) {
|
|
97
|
-
return b.snapshot.started_at_ms - a.snapshot.started_at_ms;
|
|
98
|
-
}
|
|
99
|
-
return a.snapshot.activity_id.localeCompare(b.snapshot.activity_id);
|
|
100
|
-
});
|
|
101
|
-
let kept = 0;
|
|
102
|
-
for (const row of rows) {
|
|
103
|
-
if (row.snapshot.status === "running") {
|
|
104
|
-
kept += 1;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
kept += 1;
|
|
108
|
-
if (kept <= this.#maxHistory) {
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
this.#heartbeatScheduler.unregister(row.snapshot.activity_id);
|
|
112
|
-
this.#activities.delete(row.snapshot.activity_id);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
#resolveActivity(activityIdRaw) {
|
|
116
|
-
const id = activityIdRaw?.trim() ?? "";
|
|
117
|
-
if (id.length === 0) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
return this.#activities.get(id) ?? null;
|
|
121
|
-
}
|
|
122
|
-
#heartbeatMessage(activity) {
|
|
123
|
-
const nowMs = Math.trunc(this.#nowMs());
|
|
124
|
-
const elapsed = elapsedSeconds(activity.snapshot, nowMs);
|
|
125
|
-
const progress = activity.snapshot.last_progress ? ` · ${activity.snapshot.last_progress}` : "";
|
|
126
|
-
return `⏱ ${activity.snapshot.title} running for ${elapsed}s${progress}`;
|
|
127
|
-
}
|
|
128
|
-
#emitHeartbeat(activity, reason) {
|
|
129
|
-
activity.snapshot.heartbeat_count += 1;
|
|
130
|
-
activity.snapshot.last_heartbeat_at_ms = Math.trunc(this.#nowMs());
|
|
131
|
-
activity.snapshot.last_heartbeat_reason = reason?.trim() || "requested";
|
|
132
|
-
this.#touch(activity);
|
|
133
|
-
this.#emit("activity_heartbeat", activity, this.#heartbeatMessage(activity));
|
|
134
|
-
}
|
|
135
|
-
#registerHeartbeat(activity) {
|
|
136
|
-
if (activity.snapshot.heartbeat_every_ms <= 0) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
this.#heartbeatScheduler.register({
|
|
140
|
-
activityId: activity.snapshot.activity_id,
|
|
141
|
-
everyMs: activity.snapshot.heartbeat_every_ms,
|
|
142
|
-
handler: async ({ reason }) => {
|
|
143
|
-
if (activity.snapshot.status !== "running") {
|
|
144
|
-
return { status: "skipped", reason: "not_running" };
|
|
145
|
-
}
|
|
146
|
-
this.#emitHeartbeat(activity, reason);
|
|
147
|
-
return { status: "ran" };
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
start(opts) {
|
|
152
|
-
const nowMs = Math.trunc(this.#nowMs());
|
|
153
|
-
const snapshot = {
|
|
154
|
-
activity_id: this.#nextActivityId(),
|
|
155
|
-
kind: normalizeKind(opts.kind),
|
|
156
|
-
title: normalizeTitle(opts.title),
|
|
157
|
-
status: "running",
|
|
158
|
-
heartbeat_every_ms: normalizeHeartbeatEveryMs(opts.heartbeatEveryMs, this.#defaultHeartbeatEveryMs),
|
|
159
|
-
heartbeat_count: 0,
|
|
160
|
-
last_heartbeat_at_ms: null,
|
|
161
|
-
last_heartbeat_reason: null,
|
|
162
|
-
last_progress: null,
|
|
163
|
-
final_message: null,
|
|
164
|
-
metadata: toSafeMetadata(opts.metadata),
|
|
165
|
-
source: opts.source ?? "api",
|
|
166
|
-
started_at_ms: nowMs,
|
|
167
|
-
updated_at_ms: nowMs,
|
|
168
|
-
finished_at_ms: null,
|
|
169
|
-
};
|
|
170
|
-
const activity = {
|
|
171
|
-
snapshot,
|
|
172
|
-
events: [],
|
|
173
|
-
};
|
|
174
|
-
this.#activities.set(snapshot.activity_id, activity);
|
|
175
|
-
this.#registerHeartbeat(activity);
|
|
176
|
-
this.#emit("activity_started", activity, `🚀 Started activity ${snapshot.title} (${snapshot.activity_id}).`);
|
|
177
|
-
return this.#snapshot(activity);
|
|
178
|
-
}
|
|
179
|
-
list(opts = {}) {
|
|
180
|
-
const limit = Math.max(1, Math.min(500, Math.trunc(opts.limit ?? 100)));
|
|
181
|
-
const kind = opts.kind?.trim().toLowerCase() || null;
|
|
182
|
-
return [...this.#activities.values()]
|
|
183
|
-
.filter((activity) => {
|
|
184
|
-
if (opts.status && activity.snapshot.status !== opts.status) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
if (kind && activity.snapshot.kind !== kind) {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
return true;
|
|
191
|
-
})
|
|
192
|
-
.sort((a, b) => {
|
|
193
|
-
if (a.snapshot.started_at_ms !== b.snapshot.started_at_ms) {
|
|
194
|
-
return b.snapshot.started_at_ms - a.snapshot.started_at_ms;
|
|
195
|
-
}
|
|
196
|
-
return a.snapshot.activity_id.localeCompare(b.snapshot.activity_id);
|
|
197
|
-
})
|
|
198
|
-
.slice(0, limit)
|
|
199
|
-
.map((activity) => this.#snapshot(activity));
|
|
200
|
-
}
|
|
201
|
-
get(activityId) {
|
|
202
|
-
const activity = this.#resolveActivity(activityId);
|
|
203
|
-
return activity ? this.#snapshot(activity) : null;
|
|
204
|
-
}
|
|
205
|
-
events(activityId, opts = {}) {
|
|
206
|
-
const activity = this.#resolveActivity(activityId);
|
|
207
|
-
if (!activity) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
const limit = Math.max(1, Math.min(2_000, Math.trunc(opts.limit ?? 200)));
|
|
211
|
-
return activity.events.slice(-limit).map((event) => ({
|
|
212
|
-
...event,
|
|
213
|
-
activity: {
|
|
214
|
-
...event.activity,
|
|
215
|
-
metadata: { ...event.activity.metadata },
|
|
216
|
-
},
|
|
217
|
-
}));
|
|
218
|
-
}
|
|
219
|
-
progress(opts) {
|
|
220
|
-
const activity = this.#resolveActivity(opts.activityId);
|
|
221
|
-
if (!opts.activityId?.trim()) {
|
|
222
|
-
return { ok: false, reason: "missing_target", activity: null };
|
|
223
|
-
}
|
|
224
|
-
if (!activity) {
|
|
225
|
-
return { ok: false, reason: "not_found", activity: null };
|
|
226
|
-
}
|
|
227
|
-
if (activity.snapshot.status !== "running") {
|
|
228
|
-
return { ok: false, reason: "not_running", activity: this.#snapshot(activity) };
|
|
229
|
-
}
|
|
230
|
-
const message = opts.message?.trim() || "progress updated";
|
|
231
|
-
activity.snapshot.last_progress = message;
|
|
232
|
-
this.#touch(activity);
|
|
233
|
-
this.#emit("activity_progress", activity, `📈 ${message}`);
|
|
234
|
-
return { ok: true, reason: null, activity: this.#snapshot(activity) };
|
|
235
|
-
}
|
|
236
|
-
heartbeat(opts) {
|
|
237
|
-
const activityId = opts.activityId?.trim() || "";
|
|
238
|
-
if (activityId.length === 0) {
|
|
239
|
-
return { ok: false, reason: "missing_target", activity: null };
|
|
240
|
-
}
|
|
241
|
-
const activity = this.#activities.get(activityId) ?? null;
|
|
242
|
-
if (!activity) {
|
|
243
|
-
return { ok: false, reason: "not_found", activity: null };
|
|
244
|
-
}
|
|
245
|
-
if (activity.snapshot.status !== "running") {
|
|
246
|
-
return { ok: false, reason: "not_running", activity: this.#snapshot(activity) };
|
|
247
|
-
}
|
|
248
|
-
const reason = opts.reason?.trim() || "manual";
|
|
249
|
-
if (this.#heartbeatScheduler.has(activityId)) {
|
|
250
|
-
this.#heartbeatScheduler.requestNow(activityId, { reason, coalesceMs: 0 });
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
this.#emitHeartbeat(activity, reason);
|
|
254
|
-
}
|
|
255
|
-
return { ok: true, reason: null, activity: this.#snapshot(activity) };
|
|
256
|
-
}
|
|
257
|
-
#finish(opts) {
|
|
258
|
-
const activityId = opts.activityId?.trim() || "";
|
|
259
|
-
if (activityId.length === 0) {
|
|
260
|
-
return { ok: false, reason: "missing_target", activity: null };
|
|
261
|
-
}
|
|
262
|
-
const activity = this.#activities.get(activityId) ?? null;
|
|
263
|
-
if (!activity) {
|
|
264
|
-
return { ok: false, reason: "not_found", activity: null };
|
|
265
|
-
}
|
|
266
|
-
if (activity.snapshot.status !== "running") {
|
|
267
|
-
return { ok: false, reason: "not_running", activity: this.#snapshot(activity) };
|
|
268
|
-
}
|
|
269
|
-
activity.snapshot.status = opts.status;
|
|
270
|
-
activity.snapshot.final_message = opts.message?.trim() || null;
|
|
271
|
-
activity.snapshot.finished_at_ms = Math.trunc(this.#nowMs());
|
|
272
|
-
activity.snapshot.updated_at_ms = activity.snapshot.finished_at_ms;
|
|
273
|
-
this.#heartbeatScheduler.unregister(activity.snapshot.activity_id);
|
|
274
|
-
switch (opts.status) {
|
|
275
|
-
case "completed":
|
|
276
|
-
this.#emit("activity_completed", activity, `✅ Activity completed: ${activity.snapshot.title}${activity.snapshot.final_message ? ` · ${activity.snapshot.final_message}` : ""}`);
|
|
277
|
-
break;
|
|
278
|
-
case "failed":
|
|
279
|
-
this.#emit("activity_failed", activity, `❌ Activity failed: ${activity.snapshot.title}${activity.snapshot.final_message ? ` · ${activity.snapshot.final_message}` : ""}`);
|
|
280
|
-
break;
|
|
281
|
-
case "cancelled":
|
|
282
|
-
this.#emit("activity_cancelled", activity, `🛑 Activity cancelled: ${activity.snapshot.title}${activity.snapshot.final_message ? ` · ${activity.snapshot.final_message}` : ""}`);
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
this.#pruneHistory();
|
|
286
|
-
return { ok: true, reason: null, activity: this.#snapshot(activity) };
|
|
287
|
-
}
|
|
288
|
-
complete(opts) {
|
|
289
|
-
return this.#finish({ ...opts, status: "completed" });
|
|
290
|
-
}
|
|
291
|
-
fail(opts) {
|
|
292
|
-
return this.#finish({ ...opts, status: "failed" });
|
|
293
|
-
}
|
|
294
|
-
cancel(opts) {
|
|
295
|
-
return this.#finish({ ...opts, status: "cancelled" });
|
|
296
|
-
}
|
|
297
|
-
stop() {
|
|
298
|
-
for (const activity of this.#activities.values()) {
|
|
299
|
-
this.#heartbeatScheduler.unregister(activity.snapshot.activity_id);
|
|
300
|
-
}
|
|
301
|
-
if (this.#ownsHeartbeatScheduler) {
|
|
302
|
-
this.#heartbeatScheduler.stop();
|
|
303
|
-
}
|
|
304
|
-
this.#activities.clear();
|
|
305
|
-
}
|
|
306
|
-
}
|
package/dist/api/activities.d.ts
DELETED
package/dist/api/activities.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
export async function activityRoutes(request, url, deps, headers) {
|
|
2
|
-
const path = url.pathname;
|
|
3
|
-
if (path === "/api/activities") {
|
|
4
|
-
if (request.method !== "GET") {
|
|
5
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
6
|
-
}
|
|
7
|
-
const statusRaw = url.searchParams.get("status")?.trim().toLowerCase();
|
|
8
|
-
const status = statusRaw === "running" || statusRaw === "completed" || statusRaw === "failed" || statusRaw === "cancelled"
|
|
9
|
-
? statusRaw
|
|
10
|
-
: undefined;
|
|
11
|
-
const kind = url.searchParams.get("kind")?.trim() || undefined;
|
|
12
|
-
const limitRaw = url.searchParams.get("limit");
|
|
13
|
-
const limit = limitRaw && /^\d+$/.test(limitRaw) ? Math.max(1, Math.min(500, Number.parseInt(limitRaw, 10))) : undefined;
|
|
14
|
-
const activities = deps.activitySupervisor.list({ status, kind, limit });
|
|
15
|
-
return Response.json({ count: activities.length, activities }, { headers });
|
|
16
|
-
}
|
|
17
|
-
if (path === "/api/activities/start") {
|
|
18
|
-
if (request.method !== "POST") {
|
|
19
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
20
|
-
}
|
|
21
|
-
let body;
|
|
22
|
-
try {
|
|
23
|
-
body = (await request.json());
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return Response.json({ error: "invalid json body" }, { status: 400, headers });
|
|
27
|
-
}
|
|
28
|
-
const title = typeof body.title === "string" ? body.title.trim() : "";
|
|
29
|
-
if (!title) {
|
|
30
|
-
return Response.json({ error: "title is required" }, { status: 400, headers });
|
|
31
|
-
}
|
|
32
|
-
const kind = typeof body.kind === "string" ? body.kind.trim() : undefined;
|
|
33
|
-
const heartbeatEveryMs = typeof body.heartbeat_every_ms === "number" && Number.isFinite(body.heartbeat_every_ms)
|
|
34
|
-
? Math.max(0, Math.trunc(body.heartbeat_every_ms))
|
|
35
|
-
: undefined;
|
|
36
|
-
const source = body.source === "api" || body.source === "command" || body.source === "system" ? body.source : "api";
|
|
37
|
-
try {
|
|
38
|
-
const activity = deps.activitySupervisor.start({
|
|
39
|
-
title,
|
|
40
|
-
kind,
|
|
41
|
-
heartbeatEveryMs,
|
|
42
|
-
metadata: body.metadata ?? undefined,
|
|
43
|
-
source,
|
|
44
|
-
});
|
|
45
|
-
return Response.json({ ok: true, activity }, { status: 201, headers });
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
return Response.json({ error: deps.describeError(err) }, { status: 400, headers });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (path === "/api/activities/progress") {
|
|
52
|
-
if (request.method !== "POST") {
|
|
53
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
54
|
-
}
|
|
55
|
-
let body;
|
|
56
|
-
try {
|
|
57
|
-
body = (await request.json());
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return Response.json({ error: "invalid json body" }, { status: 400, headers });
|
|
61
|
-
}
|
|
62
|
-
const result = deps.activitySupervisor.progress({
|
|
63
|
-
activityId: typeof body.activity_id === "string" ? body.activity_id : null,
|
|
64
|
-
message: typeof body.message === "string" ? body.message : null,
|
|
65
|
-
});
|
|
66
|
-
if (result.ok) {
|
|
67
|
-
return Response.json(result, { headers });
|
|
68
|
-
}
|
|
69
|
-
if (result.reason === "missing_target") {
|
|
70
|
-
return Response.json(result, { status: 400, headers });
|
|
71
|
-
}
|
|
72
|
-
if (result.reason === "not_running") {
|
|
73
|
-
return Response.json(result, { status: 409, headers });
|
|
74
|
-
}
|
|
75
|
-
return Response.json(result, { status: 404, headers });
|
|
76
|
-
}
|
|
77
|
-
if (path === "/api/activities/heartbeat") {
|
|
78
|
-
if (request.method !== "POST") {
|
|
79
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
80
|
-
}
|
|
81
|
-
let body;
|
|
82
|
-
try {
|
|
83
|
-
body = (await request.json());
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return Response.json({ error: "invalid json body" }, { status: 400, headers });
|
|
87
|
-
}
|
|
88
|
-
const result = deps.activitySupervisor.heartbeat({
|
|
89
|
-
activityId: typeof body.activity_id === "string" ? body.activity_id : null,
|
|
90
|
-
reason: typeof body.reason === "string" ? body.reason : null,
|
|
91
|
-
});
|
|
92
|
-
if (result.ok) {
|
|
93
|
-
return Response.json(result, { headers });
|
|
94
|
-
}
|
|
95
|
-
if (result.reason === "missing_target") {
|
|
96
|
-
return Response.json(result, { status: 400, headers });
|
|
97
|
-
}
|
|
98
|
-
if (result.reason === "not_running") {
|
|
99
|
-
return Response.json(result, { status: 409, headers });
|
|
100
|
-
}
|
|
101
|
-
return Response.json(result, { status: 404, headers });
|
|
102
|
-
}
|
|
103
|
-
if (path === "/api/activities/complete" || path === "/api/activities/fail" || path === "/api/activities/cancel") {
|
|
104
|
-
if (request.method !== "POST") {
|
|
105
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
106
|
-
}
|
|
107
|
-
let body;
|
|
108
|
-
try {
|
|
109
|
-
body = (await request.json());
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return Response.json({ error: "invalid json body" }, { status: 400, headers });
|
|
113
|
-
}
|
|
114
|
-
const activityId = typeof body.activity_id === "string" ? body.activity_id : null;
|
|
115
|
-
const message = typeof body.message === "string" ? body.message : null;
|
|
116
|
-
const result = path === "/api/activities/complete"
|
|
117
|
-
? deps.activitySupervisor.complete({ activityId, message })
|
|
118
|
-
: path === "/api/activities/fail"
|
|
119
|
-
? deps.activitySupervisor.fail({ activityId, message })
|
|
120
|
-
: deps.activitySupervisor.cancel({ activityId, message });
|
|
121
|
-
if (result.ok) {
|
|
122
|
-
return Response.json(result, { headers });
|
|
123
|
-
}
|
|
124
|
-
if (result.reason === "missing_target") {
|
|
125
|
-
return Response.json(result, { status: 400, headers });
|
|
126
|
-
}
|
|
127
|
-
if (result.reason === "not_running") {
|
|
128
|
-
return Response.json(result, { status: 409, headers });
|
|
129
|
-
}
|
|
130
|
-
return Response.json(result, { status: 404, headers });
|
|
131
|
-
}
|
|
132
|
-
if (path.startsWith("/api/activities/")) {
|
|
133
|
-
if (request.method !== "GET") {
|
|
134
|
-
return Response.json({ error: "Method Not Allowed" }, { status: 405, headers });
|
|
135
|
-
}
|
|
136
|
-
const rest = path.slice("/api/activities/".length);
|
|
137
|
-
const [rawId, maybeSub] = rest.split("/");
|
|
138
|
-
const activityId = decodeURIComponent(rawId ?? "").trim();
|
|
139
|
-
if (activityId.length === 0) {
|
|
140
|
-
return Response.json({ error: "missing activity id" }, { status: 400, headers });
|
|
141
|
-
}
|
|
142
|
-
if (maybeSub === "events") {
|
|
143
|
-
const limitRaw = url.searchParams.get("limit");
|
|
144
|
-
const limit = limitRaw && /^\d+$/.test(limitRaw)
|
|
145
|
-
? Math.max(1, Math.min(2_000, Number.parseInt(limitRaw, 10)))
|
|
146
|
-
: undefined;
|
|
147
|
-
const events = deps.activitySupervisor.events(activityId, { limit });
|
|
148
|
-
if (!events) {
|
|
149
|
-
return Response.json({ error: "activity not found" }, { status: 404, headers });
|
|
150
|
-
}
|
|
151
|
-
return Response.json({ count: events.length, events }, { headers });
|
|
152
|
-
}
|
|
153
|
-
const activity = deps.activitySupervisor.get(activityId);
|
|
154
|
-
if (!activity) {
|
|
155
|
-
return Response.json({ error: "activity not found" }, { status: 404, headers });
|
|
156
|
-
}
|
|
157
|
-
return Response.json(activity, { headers });
|
|
158
|
-
}
|
|
159
|
-
return Response.json({ error: "Not Found" }, { status: 404, headers });
|
|
160
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { ServerRoutingDependencies } from "../server_routing.js";
|
|
2
|
-
export type SessionFlashStatus = "pending" | "delivered";
|
|
3
|
-
export type SessionFlashRecord = {
|
|
4
|
-
flash_id: string;
|
|
5
|
-
created_at_ms: number;
|
|
6
|
-
session_id: string;
|
|
7
|
-
session_kind: string | null;
|
|
8
|
-
body: string;
|
|
9
|
-
context_ids: string[];
|
|
10
|
-
source: string | null;
|
|
11
|
-
metadata: Record<string, unknown>;
|
|
12
|
-
from: {
|
|
13
|
-
channel: string | null;
|
|
14
|
-
channel_tenant_id: string | null;
|
|
15
|
-
channel_conversation_id: string | null;
|
|
16
|
-
actor_binding_id: string | null;
|
|
17
|
-
};
|
|
18
|
-
status: SessionFlashStatus;
|
|
19
|
-
delivered_at_ms: number | null;
|
|
20
|
-
delivered_by: string | null;
|
|
21
|
-
delivery_note: string | null;
|
|
22
|
-
};
|
|
23
|
-
export declare function getSessionFlashPath(repoRoot: string): string;
|
|
24
|
-
export declare function listSessionFlashRecords(opts: {
|
|
25
|
-
repoRoot: string;
|
|
26
|
-
sessionId?: string | null;
|
|
27
|
-
sessionKind?: string | null;
|
|
28
|
-
status?: SessionFlashStatus | "all";
|
|
29
|
-
contains?: string | null;
|
|
30
|
-
limit?: number;
|
|
31
|
-
}): Promise<SessionFlashRecord[]>;
|
|
32
|
-
export declare function getSessionFlashRecord(opts: {
|
|
33
|
-
repoRoot: string;
|
|
34
|
-
flashId: string;
|
|
35
|
-
}): Promise<SessionFlashRecord | null>;
|
|
36
|
-
export declare function createSessionFlashRecord(opts: {
|
|
37
|
-
repoRoot: string;
|
|
38
|
-
sessionId: string;
|
|
39
|
-
body: string;
|
|
40
|
-
sessionKind?: string | null;
|
|
41
|
-
contextIds?: string[];
|
|
42
|
-
source?: string | null;
|
|
43
|
-
metadata?: Record<string, unknown>;
|
|
44
|
-
from?: {
|
|
45
|
-
channel?: string | null;
|
|
46
|
-
channel_tenant_id?: string | null;
|
|
47
|
-
channel_conversation_id?: string | null;
|
|
48
|
-
actor_binding_id?: string | null;
|
|
49
|
-
};
|
|
50
|
-
nowMs?: number;
|
|
51
|
-
}): Promise<SessionFlashRecord>;
|
|
52
|
-
export declare function ackSessionFlashRecord(opts: {
|
|
53
|
-
repoRoot: string;
|
|
54
|
-
flashId: string;
|
|
55
|
-
sessionId?: string | null;
|
|
56
|
-
deliveredBy?: string | null;
|
|
57
|
-
note?: string | null;
|
|
58
|
-
nowMs?: number;
|
|
59
|
-
}): Promise<SessionFlashRecord | null>;
|
|
60
|
-
export declare function sessionFlashRoutes(request: Request, url: URL, deps: ServerRoutingDependencies, headers: Headers): Promise<Response>;
|