@reconcrap/boss-recommend-mcp 2.0.53 → 2.0.55

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.
@@ -1,310 +1,310 @@
1
- export const RUN_STATUS_QUEUED = "queued";
2
- export const RUN_STATUS_RUNNING = "running";
3
- export const RUN_STATUS_PAUSED = "paused";
4
- export const RUN_STATUS_COMPLETED = "completed";
5
- export const RUN_STATUS_CANCELING = "canceling";
6
- export const RUN_STATUS_CANCELED = "canceled";
7
- export const RUN_STATUS_FAILED = "failed";
8
-
9
- const TERMINAL_STATUSES = new Set([
10
- RUN_STATUS_COMPLETED,
11
- RUN_STATUS_CANCELED,
12
- RUN_STATUS_FAILED
13
- ]);
14
-
15
- export class RunCanceledError extends Error {
16
- constructor(message = "Run canceled") {
17
- super(message);
18
- this.name = "RunCanceledError";
19
- }
20
- }
21
-
22
- function nowIso() {
23
- return new Date().toISOString();
24
- }
25
-
26
- function createRunId(prefix = "run") {
27
- const random = Math.random().toString(36).slice(2, 10);
28
- return `${prefix}_${Date.now().toString(36)}_${random}`;
29
- }
30
-
31
- function clone(value) {
32
- return JSON.parse(JSON.stringify(value));
33
- }
34
-
35
- function createDeferred() {
36
- let resolve;
37
- let reject;
38
- const promise = new Promise((promiseResolve, promiseReject) => {
39
- resolve = promiseResolve;
40
- reject = promiseReject;
41
- });
42
- return { promise, resolve, reject };
43
- }
44
-
45
- function snapshotFromEntry(entry) {
46
- const run = entry.run;
47
- return clone({
48
- runId: run.runId,
49
- name: run.name,
50
- pid: run.pid,
51
- status: run.status,
52
- phase: run.phase,
53
- progress: run.progress,
54
- context: run.context,
55
- checkpoint: run.checkpoint,
56
- startedAt: run.startedAt,
57
- updatedAt: run.updatedAt,
58
- completedAt: run.completedAt,
59
- canPause: run.status === RUN_STATUS_RUNNING,
60
- canResume: run.status === RUN_STATUS_PAUSED,
61
- canCancel: !TERMINAL_STATUSES.has(run.status),
62
- error: run.error,
63
- summary: run.summary
64
- });
65
- }
66
-
67
- export function createRunLifecycleManager({
68
- idPrefix = "run",
69
- now = nowIso,
70
- onSnapshot = null
71
- } = {}) {
72
- const runs = new Map();
73
-
74
- function emitSnapshot(entry, event = {}) {
75
- if (typeof onSnapshot !== "function") return;
76
- try {
77
- onSnapshot(snapshotFromEntry(entry), {
78
- type: event.type || "update",
79
- at: now(),
80
- ...event
81
- });
82
- } catch {
83
- // Snapshot hooks must never interrupt an active browser run.
84
- }
85
- }
86
-
87
- function getEntry(runId) {
88
- const entry = runs.get(runId);
89
- if (!entry) throw new Error(`Unknown runId: ${runId}`);
90
- return entry;
91
- }
92
-
93
- function touch(entry) {
94
- entry.run.updatedAt = now();
95
- }
96
-
97
- function setStatus(entry, status, patch = {}) {
98
- entry.run.status = status;
99
- Object.assign(entry.run, patch);
100
- touch(entry);
101
- emitSnapshot(entry, { type: "status", status });
102
- }
103
-
104
- function createControls(entry) {
105
- return {
106
- signal: entry.controller.signal,
107
- get runId() {
108
- return entry.run.runId;
109
- },
110
- get status() {
111
- return entry.run.status;
112
- },
113
- setPhase(phase) {
114
- entry.run.phase = phase;
115
- touch(entry);
116
- },
117
- updateProgress(progressPatch = {}) {
118
- entry.run.progress = {
119
- ...entry.run.progress,
120
- ...progressPatch
121
- };
122
- touch(entry);
123
- emitSnapshot(entry, { type: "progress", progressPatch });
124
- return snapshotFromEntry(entry);
125
- },
126
- checkpoint(checkpointPatch = {}) {
127
- entry.run.checkpoint = {
128
- ...entry.run.checkpoint,
129
- ...checkpointPatch,
130
- updatedAt: now()
131
- };
132
- touch(entry);
133
- emitSnapshot(entry, { type: "checkpoint", checkpointPatch });
134
- return snapshotFromEntry(entry);
135
- },
136
- async waitIfPaused() {
137
- if (entry.controller.signal.aborted) {
138
- throw new RunCanceledError();
139
- }
140
- if (!entry.pauseRequested) return;
141
- setStatus(entry, RUN_STATUS_PAUSED);
142
- while (entry.pauseRequested) {
143
- const deferred = createDeferred();
144
- entry.pauseWaiters.add(deferred);
145
- try {
146
- await deferred.promise;
147
- } finally {
148
- entry.pauseWaiters.delete(deferred);
149
- }
150
- if (entry.controller.signal.aborted) {
151
- throw new RunCanceledError();
152
- }
153
- }
154
- setStatus(entry, RUN_STATUS_RUNNING);
155
- },
156
- async sleep(ms) {
157
- if (entry.controller.signal.aborted) {
158
- throw new RunCanceledError();
159
- }
160
- await new Promise((resolve, reject) => {
161
- const timer = setTimeout(resolve, ms);
162
- const onAbort = () => {
163
- clearTimeout(timer);
164
- reject(new RunCanceledError());
165
- };
166
- entry.controller.signal.addEventListener("abort", onAbort, { once: true });
167
- });
168
- },
169
- throwIfCanceled() {
170
- if (entry.controller.signal.aborted) {
171
- throw new RunCanceledError();
172
- }
173
- }
174
- };
175
- }
176
-
177
- async function settle(entry, task) {
178
- try {
179
- const summary = await task(entry.controls);
180
- if (entry.controller.signal.aborted || entry.cancelRequested) {
181
- setStatus(entry, RUN_STATUS_CANCELED, {
182
- completedAt: now(),
183
- summary: summary || entry.run.summary
184
- });
185
- } else {
186
- setStatus(entry, RUN_STATUS_COMPLETED, {
187
- completedAt: now(),
188
- summary: summary || entry.run.summary
189
- });
190
- }
191
- } catch (error) {
192
- if (error instanceof RunCanceledError || entry.controller.signal.aborted || entry.cancelRequested) {
193
- setStatus(entry, RUN_STATUS_CANCELED, {
194
- completedAt: now(),
195
- error: null
196
- });
197
- return;
198
- }
199
- setStatus(entry, RUN_STATUS_FAILED, {
200
- completedAt: now(),
201
- error: {
202
- name: error?.name || "Error",
203
- message: error?.message || String(error)
204
- }
205
- });
206
- }
207
- }
208
-
209
- function startRun({ runId: requestedRunId = "", name, pid = process.pid, context = {}, progress = {}, checkpoint = {}, task }) {
210
- if (typeof task !== "function") {
211
- throw new Error("startRun requires a task function");
212
- }
213
- const runId = String(requestedRunId || "").trim() || createRunId(idPrefix);
214
- if (runs.has(runId)) {
215
- throw new Error(`Run already exists: ${runId}`);
216
- }
217
- const startedAt = now();
218
- const entry = {
219
- controller: new AbortController(),
220
- pauseRequested: false,
221
- cancelRequested: false,
222
- pauseWaiters: new Set(),
223
- run: {
224
- runId,
225
- name: name || runId,
226
- pid: Number.isInteger(pid) && pid > 0 ? pid : process.pid,
227
- status: RUN_STATUS_QUEUED,
228
- phase: "queued",
229
- progress,
230
- context,
231
- checkpoint,
232
- startedAt,
233
- updatedAt: startedAt,
234
- completedAt: null,
235
- error: null,
236
- summary: null
237
- }
238
- };
239
- entry.controls = createControls(entry);
240
- runs.set(runId, entry);
241
- setStatus(entry, RUN_STATUS_RUNNING, { phase: "running" });
242
- entry.promise = settle(entry, task);
243
- return snapshotFromEntry(entry);
244
- }
245
-
246
- function getRun(runId) {
247
- return snapshotFromEntry(getEntry(runId));
248
- }
249
-
250
- function pauseRun(runId) {
251
- const entry = getEntry(runId);
252
- if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
253
- entry.pauseRequested = true;
254
- if (entry.run.status === RUN_STATUS_RUNNING) {
255
- touch(entry);
256
- emitSnapshot(entry, { type: "pause_requested" });
257
- }
258
- return snapshotFromEntry(entry);
259
- }
260
-
261
- function resumeRun(runId) {
262
- const entry = getEntry(runId);
263
- if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
264
- entry.pauseRequested = false;
265
- for (const waiter of entry.pauseWaiters) {
266
- waiter.resolve();
267
- }
268
- if (entry.run.status === RUN_STATUS_PAUSED) {
269
- setStatus(entry, RUN_STATUS_RUNNING);
270
- } else {
271
- touch(entry);
272
- emitSnapshot(entry, { type: "resume_requested" });
273
- }
274
- return snapshotFromEntry(entry);
275
- }
276
-
277
- function cancelRun(runId) {
278
- const entry = getEntry(runId);
279
- if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
280
- entry.cancelRequested = true;
281
- setStatus(entry, RUN_STATUS_CANCELING);
282
- entry.controller.abort();
283
- entry.pauseRequested = false;
284
- for (const waiter of entry.pauseWaiters) {
285
- waiter.resolve();
286
- }
287
- return snapshotFromEntry(entry);
288
- }
289
-
290
- async function waitForRun(runId, { timeoutMs = 10000 } = {}) {
291
- const entry = getEntry(runId);
292
- const timeout = new Promise((_, reject) => {
293
- setTimeout(() => reject(new Error(`Timed out waiting for run ${runId}`)), timeoutMs);
294
- });
295
- await Promise.race([entry.promise, timeout]);
296
- return snapshotFromEntry(entry);
297
- }
298
-
299
- return {
300
- startRun,
301
- getRun,
302
- pauseRun,
303
- resumeRun,
304
- cancelRun,
305
- waitForRun,
306
- listRuns() {
307
- return Array.from(runs.values()).map(snapshotFromEntry);
308
- }
309
- };
310
- }
1
+ export const RUN_STATUS_QUEUED = "queued";
2
+ export const RUN_STATUS_RUNNING = "running";
3
+ export const RUN_STATUS_PAUSED = "paused";
4
+ export const RUN_STATUS_COMPLETED = "completed";
5
+ export const RUN_STATUS_CANCELING = "canceling";
6
+ export const RUN_STATUS_CANCELED = "canceled";
7
+ export const RUN_STATUS_FAILED = "failed";
8
+
9
+ const TERMINAL_STATUSES = new Set([
10
+ RUN_STATUS_COMPLETED,
11
+ RUN_STATUS_CANCELED,
12
+ RUN_STATUS_FAILED
13
+ ]);
14
+
15
+ export class RunCanceledError extends Error {
16
+ constructor(message = "Run canceled") {
17
+ super(message);
18
+ this.name = "RunCanceledError";
19
+ }
20
+ }
21
+
22
+ function nowIso() {
23
+ return new Date().toISOString();
24
+ }
25
+
26
+ function createRunId(prefix = "run") {
27
+ const random = Math.random().toString(36).slice(2, 10);
28
+ return `${prefix}_${Date.now().toString(36)}_${random}`;
29
+ }
30
+
31
+ function clone(value) {
32
+ return JSON.parse(JSON.stringify(value));
33
+ }
34
+
35
+ function createDeferred() {
36
+ let resolve;
37
+ let reject;
38
+ const promise = new Promise((promiseResolve, promiseReject) => {
39
+ resolve = promiseResolve;
40
+ reject = promiseReject;
41
+ });
42
+ return { promise, resolve, reject };
43
+ }
44
+
45
+ function snapshotFromEntry(entry) {
46
+ const run = entry.run;
47
+ return clone({
48
+ runId: run.runId,
49
+ name: run.name,
50
+ pid: run.pid,
51
+ status: run.status,
52
+ phase: run.phase,
53
+ progress: run.progress,
54
+ context: run.context,
55
+ checkpoint: run.checkpoint,
56
+ startedAt: run.startedAt,
57
+ updatedAt: run.updatedAt,
58
+ completedAt: run.completedAt,
59
+ canPause: run.status === RUN_STATUS_RUNNING,
60
+ canResume: run.status === RUN_STATUS_PAUSED,
61
+ canCancel: !TERMINAL_STATUSES.has(run.status),
62
+ error: run.error,
63
+ summary: run.summary
64
+ });
65
+ }
66
+
67
+ export function createRunLifecycleManager({
68
+ idPrefix = "run",
69
+ now = nowIso,
70
+ onSnapshot = null
71
+ } = {}) {
72
+ const runs = new Map();
73
+
74
+ function emitSnapshot(entry, event = {}) {
75
+ if (typeof onSnapshot !== "function") return;
76
+ try {
77
+ onSnapshot(snapshotFromEntry(entry), {
78
+ type: event.type || "update",
79
+ at: now(),
80
+ ...event
81
+ });
82
+ } catch {
83
+ // Snapshot hooks must never interrupt an active browser run.
84
+ }
85
+ }
86
+
87
+ function getEntry(runId) {
88
+ const entry = runs.get(runId);
89
+ if (!entry) throw new Error(`Unknown runId: ${runId}`);
90
+ return entry;
91
+ }
92
+
93
+ function touch(entry) {
94
+ entry.run.updatedAt = now();
95
+ }
96
+
97
+ function setStatus(entry, status, patch = {}) {
98
+ entry.run.status = status;
99
+ Object.assign(entry.run, patch);
100
+ touch(entry);
101
+ emitSnapshot(entry, { type: "status", status });
102
+ }
103
+
104
+ function createControls(entry) {
105
+ return {
106
+ signal: entry.controller.signal,
107
+ get runId() {
108
+ return entry.run.runId;
109
+ },
110
+ get status() {
111
+ return entry.run.status;
112
+ },
113
+ setPhase(phase) {
114
+ entry.run.phase = phase;
115
+ touch(entry);
116
+ },
117
+ updateProgress(progressPatch = {}) {
118
+ entry.run.progress = {
119
+ ...entry.run.progress,
120
+ ...progressPatch
121
+ };
122
+ touch(entry);
123
+ emitSnapshot(entry, { type: "progress", progressPatch });
124
+ return snapshotFromEntry(entry);
125
+ },
126
+ checkpoint(checkpointPatch = {}) {
127
+ entry.run.checkpoint = {
128
+ ...entry.run.checkpoint,
129
+ ...checkpointPatch,
130
+ updatedAt: now()
131
+ };
132
+ touch(entry);
133
+ emitSnapshot(entry, { type: "checkpoint", checkpointPatch });
134
+ return snapshotFromEntry(entry);
135
+ },
136
+ async waitIfPaused() {
137
+ if (entry.controller.signal.aborted) {
138
+ throw new RunCanceledError();
139
+ }
140
+ if (!entry.pauseRequested) return;
141
+ setStatus(entry, RUN_STATUS_PAUSED);
142
+ while (entry.pauseRequested) {
143
+ const deferred = createDeferred();
144
+ entry.pauseWaiters.add(deferred);
145
+ try {
146
+ await deferred.promise;
147
+ } finally {
148
+ entry.pauseWaiters.delete(deferred);
149
+ }
150
+ if (entry.controller.signal.aborted) {
151
+ throw new RunCanceledError();
152
+ }
153
+ }
154
+ setStatus(entry, RUN_STATUS_RUNNING);
155
+ },
156
+ async sleep(ms) {
157
+ if (entry.controller.signal.aborted) {
158
+ throw new RunCanceledError();
159
+ }
160
+ await new Promise((resolve, reject) => {
161
+ const timer = setTimeout(resolve, ms);
162
+ const onAbort = () => {
163
+ clearTimeout(timer);
164
+ reject(new RunCanceledError());
165
+ };
166
+ entry.controller.signal.addEventListener("abort", onAbort, { once: true });
167
+ });
168
+ },
169
+ throwIfCanceled() {
170
+ if (entry.controller.signal.aborted) {
171
+ throw new RunCanceledError();
172
+ }
173
+ }
174
+ };
175
+ }
176
+
177
+ async function settle(entry, task) {
178
+ try {
179
+ const summary = await task(entry.controls);
180
+ if (entry.controller.signal.aborted || entry.cancelRequested) {
181
+ setStatus(entry, RUN_STATUS_CANCELED, {
182
+ completedAt: now(),
183
+ summary: summary || entry.run.summary
184
+ });
185
+ } else {
186
+ setStatus(entry, RUN_STATUS_COMPLETED, {
187
+ completedAt: now(),
188
+ summary: summary || entry.run.summary
189
+ });
190
+ }
191
+ } catch (error) {
192
+ if (error instanceof RunCanceledError || entry.controller.signal.aborted || entry.cancelRequested) {
193
+ setStatus(entry, RUN_STATUS_CANCELED, {
194
+ completedAt: now(),
195
+ error: null
196
+ });
197
+ return;
198
+ }
199
+ setStatus(entry, RUN_STATUS_FAILED, {
200
+ completedAt: now(),
201
+ error: {
202
+ name: error?.name || "Error",
203
+ message: error?.message || String(error)
204
+ }
205
+ });
206
+ }
207
+ }
208
+
209
+ function startRun({ runId: requestedRunId = "", name, pid = process.pid, context = {}, progress = {}, checkpoint = {}, task }) {
210
+ if (typeof task !== "function") {
211
+ throw new Error("startRun requires a task function");
212
+ }
213
+ const runId = String(requestedRunId || "").trim() || createRunId(idPrefix);
214
+ if (runs.has(runId)) {
215
+ throw new Error(`Run already exists: ${runId}`);
216
+ }
217
+ const startedAt = now();
218
+ const entry = {
219
+ controller: new AbortController(),
220
+ pauseRequested: false,
221
+ cancelRequested: false,
222
+ pauseWaiters: new Set(),
223
+ run: {
224
+ runId,
225
+ name: name || runId,
226
+ pid: Number.isInteger(pid) && pid > 0 ? pid : process.pid,
227
+ status: RUN_STATUS_QUEUED,
228
+ phase: "queued",
229
+ progress,
230
+ context,
231
+ checkpoint,
232
+ startedAt,
233
+ updatedAt: startedAt,
234
+ completedAt: null,
235
+ error: null,
236
+ summary: null
237
+ }
238
+ };
239
+ entry.controls = createControls(entry);
240
+ runs.set(runId, entry);
241
+ setStatus(entry, RUN_STATUS_RUNNING, { phase: "running" });
242
+ entry.promise = settle(entry, task);
243
+ return snapshotFromEntry(entry);
244
+ }
245
+
246
+ function getRun(runId) {
247
+ return snapshotFromEntry(getEntry(runId));
248
+ }
249
+
250
+ function pauseRun(runId) {
251
+ const entry = getEntry(runId);
252
+ if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
253
+ entry.pauseRequested = true;
254
+ if (entry.run.status === RUN_STATUS_RUNNING) {
255
+ touch(entry);
256
+ emitSnapshot(entry, { type: "pause_requested" });
257
+ }
258
+ return snapshotFromEntry(entry);
259
+ }
260
+
261
+ function resumeRun(runId) {
262
+ const entry = getEntry(runId);
263
+ if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
264
+ entry.pauseRequested = false;
265
+ for (const waiter of entry.pauseWaiters) {
266
+ waiter.resolve();
267
+ }
268
+ if (entry.run.status === RUN_STATUS_PAUSED) {
269
+ setStatus(entry, RUN_STATUS_RUNNING);
270
+ } else {
271
+ touch(entry);
272
+ emitSnapshot(entry, { type: "resume_requested" });
273
+ }
274
+ return snapshotFromEntry(entry);
275
+ }
276
+
277
+ function cancelRun(runId) {
278
+ const entry = getEntry(runId);
279
+ if (TERMINAL_STATUSES.has(entry.run.status)) return snapshotFromEntry(entry);
280
+ entry.cancelRequested = true;
281
+ setStatus(entry, RUN_STATUS_CANCELING);
282
+ entry.controller.abort();
283
+ entry.pauseRequested = false;
284
+ for (const waiter of entry.pauseWaiters) {
285
+ waiter.resolve();
286
+ }
287
+ return snapshotFromEntry(entry);
288
+ }
289
+
290
+ async function waitForRun(runId, { timeoutMs = 10000 } = {}) {
291
+ const entry = getEntry(runId);
292
+ const timeout = new Promise((_, reject) => {
293
+ setTimeout(() => reject(new Error(`Timed out waiting for run ${runId}`)), timeoutMs);
294
+ });
295
+ await Promise.race([entry.promise, timeout]);
296
+ return snapshotFromEntry(entry);
297
+ }
298
+
299
+ return {
300
+ startRun,
301
+ getRun,
302
+ pauseRun,
303
+ resumeRun,
304
+ cancelRun,
305
+ waitForRun,
306
+ listRuns() {
307
+ return Array.from(runs.values()).map(snapshotFromEntry);
308
+ }
309
+ };
310
+ }