@geometra/mcp 1.63.0 → 1.64.0
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/server.js +754 -353
- package/dist/session-state.js +72 -9
- package/dist/session.d.ts +96 -0
- package/dist/session.js +245 -8
- package/package.json +2 -2
package/dist/session-state.js
CHANGED
|
@@ -18,16 +18,59 @@ function resolveSessionStateFile() {
|
|
|
18
18
|
mkdirSync(dir, { recursive: true });
|
|
19
19
|
return path.join(dir, `parallel-mcp-${process.pid}-${threadId}.sqlite`);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
const
|
|
21
|
+
function isSessionLifecycleDisabled() {
|
|
22
|
+
const raw = process.env.GEOMETRA_MCP_DISABLE_SESSION_LIFECYCLE?.trim().toLowerCase();
|
|
23
|
+
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
24
|
+
}
|
|
25
|
+
function formatSessionLifecycleInitError(error) {
|
|
26
|
+
if (error instanceof Error) {
|
|
27
|
+
if (error.code === 'ERR_DLOPEN_FAILED' &&
|
|
28
|
+
error.message.includes('better_sqlite3.node')) {
|
|
29
|
+
return `${error.message}. Rebuild the native module with \`npm rebuild better-sqlite3\` in the MCP package directory, or reinstall dependencies for the current Node.js version.`;
|
|
30
|
+
}
|
|
31
|
+
return error.message;
|
|
32
|
+
}
|
|
33
|
+
return String(error);
|
|
34
|
+
}
|
|
35
|
+
function createSessionLifecycleRegistry() {
|
|
36
|
+
if (isSessionLifecycleDisabled()) {
|
|
37
|
+
process.stderr.write('[geometra-mcp] durable session lifecycle disabled via GEOMETRA_MCP_DISABLE_SESSION_LIFECYCLE\n');
|
|
38
|
+
return {
|
|
39
|
+
available: false,
|
|
40
|
+
orchestrator: null,
|
|
41
|
+
close: () => { },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
23
44
|
try {
|
|
24
|
-
orchestrator
|
|
45
|
+
const orchestrator = new ParallelMcpOrchestrator(new SqliteParallelMcpStore({ filename: resolveSessionStateFile() }), { defaultLeaseMs: SESSION_LEASE_MS });
|
|
46
|
+
const leaseSweep = setInterval(() => {
|
|
47
|
+
try {
|
|
48
|
+
orchestrator.expireLeases();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* ignore background lease sweep failures */
|
|
52
|
+
}
|
|
53
|
+
}, SESSION_SWEEP_MS);
|
|
54
|
+
leaseSweep.unref();
|
|
55
|
+
return {
|
|
56
|
+
available: true,
|
|
57
|
+
orchestrator,
|
|
58
|
+
close: () => {
|
|
59
|
+
clearInterval(leaseSweep);
|
|
60
|
+
orchestrator.close();
|
|
61
|
+
},
|
|
62
|
+
};
|
|
25
63
|
}
|
|
26
|
-
catch {
|
|
27
|
-
|
|
64
|
+
catch (error) {
|
|
65
|
+
process.stderr.write(`[geometra-mcp] durable session lifecycle disabled: ${formatSessionLifecycleInitError(error)}\n`);
|
|
66
|
+
return {
|
|
67
|
+
available: false,
|
|
68
|
+
orchestrator: null,
|
|
69
|
+
close: () => { },
|
|
70
|
+
};
|
|
28
71
|
}
|
|
29
|
-
}
|
|
30
|
-
|
|
72
|
+
}
|
|
73
|
+
const lifecycleRegistry = createSessionLifecycleRegistry();
|
|
31
74
|
function extractPageUrl(target) {
|
|
32
75
|
const cached = target.cachedA11y?.meta?.pageUrl;
|
|
33
76
|
if (typeof cached === 'string' && cached.length > 0)
|
|
@@ -86,6 +129,15 @@ function workerIdFor(sessionId) {
|
|
|
86
129
|
return `${SESSION_WORKER_PREFIX}:${sessionId}`;
|
|
87
130
|
}
|
|
88
131
|
export function initializeSessionLifecycle(target, options) {
|
|
132
|
+
if (!lifecycleRegistry.available || !lifecycleRegistry.orchestrator) {
|
|
133
|
+
target.lifecycleFinalized = true;
|
|
134
|
+
target.lifecycleTaskId = undefined;
|
|
135
|
+
target.lifecycleTaskKind = undefined;
|
|
136
|
+
target.lifecycleLeaseId = undefined;
|
|
137
|
+
target.lifecycleWorkerId = undefined;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const orchestrator = lifecycleRegistry.orchestrator;
|
|
89
141
|
const sessionId = target.id;
|
|
90
142
|
const taskId = liveTaskIdFor(sessionId);
|
|
91
143
|
const taskKind = liveTaskKindFor(sessionId);
|
|
@@ -131,6 +183,9 @@ export function initializeSessionLifecycle(target, options) {
|
|
|
131
183
|
target.lifecycleFinalized = false;
|
|
132
184
|
}
|
|
133
185
|
export function heartbeatSessionLifecycle(target) {
|
|
186
|
+
if (!lifecycleRegistry.orchestrator)
|
|
187
|
+
return;
|
|
188
|
+
const orchestrator = lifecycleRegistry.orchestrator;
|
|
134
189
|
if (target.lifecycleFinalized || !target.lifecycleTaskId || !target.lifecycleLeaseId || !target.lifecycleWorkerId)
|
|
135
190
|
return;
|
|
136
191
|
orchestrator.heartbeatLease({
|
|
@@ -141,6 +196,9 @@ export function heartbeatSessionLifecycle(target) {
|
|
|
141
196
|
});
|
|
142
197
|
}
|
|
143
198
|
export function recordSessionSnapshot(target, label, extra) {
|
|
199
|
+
if (!lifecycleRegistry.orchestrator)
|
|
200
|
+
return;
|
|
201
|
+
const orchestrator = lifecycleRegistry.orchestrator;
|
|
144
202
|
if (!target.lifecycleTaskId)
|
|
145
203
|
return;
|
|
146
204
|
orchestrator.appendContextSnapshot({
|
|
@@ -152,6 +210,9 @@ export function recordSessionSnapshot(target, label, extra) {
|
|
|
152
210
|
});
|
|
153
211
|
}
|
|
154
212
|
export function completeSessionLifecycle(target, reason, extra) {
|
|
213
|
+
if (!lifecycleRegistry.orchestrator)
|
|
214
|
+
return;
|
|
215
|
+
const orchestrator = lifecycleRegistry.orchestrator;
|
|
155
216
|
if (target.lifecycleFinalized || !target.lifecycleTaskId || !target.lifecycleLeaseId || !target.lifecycleWorkerId)
|
|
156
217
|
return;
|
|
157
218
|
orchestrator.completeTask({
|
|
@@ -171,6 +232,9 @@ export function completeSessionLifecycle(target, reason, extra) {
|
|
|
171
232
|
target.lifecycleFinalized = true;
|
|
172
233
|
}
|
|
173
234
|
export function failSessionLifecycle(target, error, extra) {
|
|
235
|
+
if (!lifecycleRegistry.orchestrator)
|
|
236
|
+
return;
|
|
237
|
+
const orchestrator = lifecycleRegistry.orchestrator;
|
|
174
238
|
if (target.lifecycleFinalized || !target.lifecycleTaskId || !target.lifecycleLeaseId || !target.lifecycleWorkerId)
|
|
175
239
|
return;
|
|
176
240
|
recordSessionSnapshot(target, 'session.failed', {
|
|
@@ -187,6 +251,5 @@ export function failSessionLifecycle(target, error, extra) {
|
|
|
187
251
|
target.lifecycleFinalized = true;
|
|
188
252
|
}
|
|
189
253
|
export function shutdownSessionLifecycleRegistry() {
|
|
190
|
-
|
|
191
|
-
orchestrator.close();
|
|
254
|
+
lifecycleRegistry.close();
|
|
192
255
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -29,6 +29,17 @@ export interface A11yNode {
|
|
|
29
29
|
scrollX?: number;
|
|
30
30
|
scrollY?: number;
|
|
31
31
|
controlTag?: string;
|
|
32
|
+
/** Stable authored DOM identity emitted by the proxy extractor (`id:` or `name:`). */
|
|
33
|
+
controlKey?: string;
|
|
34
|
+
controlId?: string;
|
|
35
|
+
controlName?: string;
|
|
36
|
+
options?: Array<{
|
|
37
|
+
value: string;
|
|
38
|
+
label: string;
|
|
39
|
+
disabled: boolean;
|
|
40
|
+
selected: boolean;
|
|
41
|
+
index: number;
|
|
42
|
+
}>;
|
|
32
43
|
placeholder?: string;
|
|
33
44
|
inputPattern?: string;
|
|
34
45
|
inputType?: string;
|
|
@@ -284,8 +295,19 @@ export interface PageSectionDetail {
|
|
|
284
295
|
export type FormSchemaFieldKind = 'text' | 'choice' | 'toggle' | 'multi_choice';
|
|
285
296
|
export type FormSchemaChoiceType = 'select' | 'group' | 'listbox';
|
|
286
297
|
export type FormSchemaContextMode = 'auto' | 'always' | 'none';
|
|
298
|
+
export interface FormSchemaOption {
|
|
299
|
+
/** Stable within the authored control identity; index disambiguates duplicate values/labels. */
|
|
300
|
+
id: string;
|
|
301
|
+
value: string;
|
|
302
|
+
label: string;
|
|
303
|
+
index: number;
|
|
304
|
+
disabled?: boolean;
|
|
305
|
+
selected?: boolean;
|
|
306
|
+
}
|
|
287
307
|
export interface FormSchemaField {
|
|
288
308
|
id: string;
|
|
309
|
+
/** Authored DOM identity used for exact proxy-side resolution before accessible-label fallback. */
|
|
310
|
+
fieldKey?: string;
|
|
289
311
|
kind: FormSchemaFieldKind;
|
|
290
312
|
label: string;
|
|
291
313
|
required?: boolean;
|
|
@@ -299,6 +321,7 @@ export interface FormSchemaField {
|
|
|
299
321
|
values?: string[];
|
|
300
322
|
optionCount?: number;
|
|
301
323
|
options?: string[];
|
|
324
|
+
optionDetails?: FormSchemaOption[];
|
|
302
325
|
aliases?: Record<string, string[]>;
|
|
303
326
|
format?: {
|
|
304
327
|
placeholder?: string;
|
|
@@ -346,6 +369,60 @@ export interface FormSchemaBuildOptions {
|
|
|
346
369
|
includeOptions?: boolean;
|
|
347
370
|
includeContext?: FormSchemaContextMode;
|
|
348
371
|
}
|
|
372
|
+
export interface FormGraphSource {
|
|
373
|
+
id: string;
|
|
374
|
+
kind: 'html';
|
|
375
|
+
title?: string;
|
|
376
|
+
url?: string;
|
|
377
|
+
}
|
|
378
|
+
export interface FormGraphSourceAnchor {
|
|
379
|
+
sourceId: string;
|
|
380
|
+
kind: 'html';
|
|
381
|
+
fieldName?: string;
|
|
382
|
+
pointer?: string;
|
|
383
|
+
}
|
|
384
|
+
export interface FormGraphField {
|
|
385
|
+
id: string;
|
|
386
|
+
path: string;
|
|
387
|
+
label: string;
|
|
388
|
+
kind: 'boolean' | 'enum' | 'text' | 'textarea' | 'email' | 'phone' | 'date' | 'number';
|
|
389
|
+
required?: boolean;
|
|
390
|
+
reviewRequired?: boolean;
|
|
391
|
+
aliases?: string[];
|
|
392
|
+
options?: Array<{
|
|
393
|
+
value: string;
|
|
394
|
+
label: string;
|
|
395
|
+
}>;
|
|
396
|
+
constraints?: {
|
|
397
|
+
pattern?: string;
|
|
398
|
+
};
|
|
399
|
+
sourceAnchors?: FormGraphSourceAnchor[];
|
|
400
|
+
metadata?: Record<string, unknown>;
|
|
401
|
+
}
|
|
402
|
+
export interface FormGraphModel {
|
|
403
|
+
formgraph: '0.1';
|
|
404
|
+
id: string;
|
|
405
|
+
title: string;
|
|
406
|
+
description?: string;
|
|
407
|
+
sources: FormGraphSource[];
|
|
408
|
+
fields: FormGraphField[];
|
|
409
|
+
evidence: [];
|
|
410
|
+
dependencies: [];
|
|
411
|
+
review: {
|
|
412
|
+
autoSubmitAllowed: false;
|
|
413
|
+
requiredBeforeSubmit: true;
|
|
414
|
+
};
|
|
415
|
+
metadata: {
|
|
416
|
+
producer: 'geometra';
|
|
417
|
+
geometra: {
|
|
418
|
+
formId: string;
|
|
419
|
+
fieldCount: number;
|
|
420
|
+
requiredCount: number;
|
|
421
|
+
invalidCount: number;
|
|
422
|
+
sections?: FormSchemaSection[];
|
|
423
|
+
};
|
|
424
|
+
};
|
|
425
|
+
}
|
|
349
426
|
export interface UiNodeUpdate {
|
|
350
427
|
before: CompactUiNode;
|
|
351
428
|
after: CompactUiNode;
|
|
@@ -460,12 +537,14 @@ export interface UpdateWaitResult {
|
|
|
460
537
|
export type ProxyFillField = {
|
|
461
538
|
kind: 'auto';
|
|
462
539
|
fieldId?: string;
|
|
540
|
+
fieldKey?: string;
|
|
463
541
|
fieldLabel: string;
|
|
464
542
|
value: string | boolean;
|
|
465
543
|
exact?: boolean;
|
|
466
544
|
} | {
|
|
467
545
|
kind: 'text';
|
|
468
546
|
fieldId?: string;
|
|
547
|
+
fieldKey?: string;
|
|
469
548
|
fieldLabel: string;
|
|
470
549
|
value: string;
|
|
471
550
|
exact?: boolean;
|
|
@@ -474,13 +553,17 @@ export type ProxyFillField = {
|
|
|
474
553
|
} | {
|
|
475
554
|
kind: 'choice';
|
|
476
555
|
fieldId?: string;
|
|
556
|
+
fieldKey?: string;
|
|
477
557
|
fieldLabel: string;
|
|
478
558
|
value: string;
|
|
559
|
+
optionIndex?: number;
|
|
479
560
|
query?: string;
|
|
480
561
|
exact?: boolean;
|
|
481
562
|
choiceType?: FormSchemaChoiceType;
|
|
482
563
|
} | {
|
|
483
564
|
kind: 'toggle';
|
|
565
|
+
fieldId?: string;
|
|
566
|
+
fieldKey?: string;
|
|
484
567
|
label: string;
|
|
485
568
|
checked?: boolean;
|
|
486
569
|
exact?: boolean;
|
|
@@ -488,6 +571,7 @@ export type ProxyFillField = {
|
|
|
488
571
|
} | {
|
|
489
572
|
kind: 'file';
|
|
490
573
|
fieldId?: string;
|
|
574
|
+
fieldKey?: string;
|
|
491
575
|
fieldLabel: string;
|
|
492
576
|
paths: string[];
|
|
493
577
|
exact?: boolean;
|
|
@@ -625,6 +709,8 @@ export declare function sendFileUpload(session: Session, paths: string[], opts?:
|
|
|
625
709
|
x: number;
|
|
626
710
|
y: number;
|
|
627
711
|
};
|
|
712
|
+
fieldId?: string;
|
|
713
|
+
fieldKey?: string;
|
|
628
714
|
fieldLabel?: string;
|
|
629
715
|
exact?: boolean;
|
|
630
716
|
strategy?: 'auto' | 'chooser' | 'hidden' | 'drop';
|
|
@@ -637,6 +723,7 @@ export declare function sendFileUpload(session: Session, paths: string[], opts?:
|
|
|
637
723
|
export declare function sendFieldText(session: Session, fieldLabel: string, value: string, opts?: {
|
|
638
724
|
exact?: boolean;
|
|
639
725
|
fieldId?: string;
|
|
726
|
+
fieldKey?: string;
|
|
640
727
|
typingDelayMs?: number;
|
|
641
728
|
imeFriendly?: boolean;
|
|
642
729
|
}, timeoutMs?: number): Promise<UpdateWaitResult>;
|
|
@@ -646,6 +733,8 @@ export declare function sendFieldChoice(session: Session, fieldLabel: string, va
|
|
|
646
733
|
query?: string;
|
|
647
734
|
choiceType?: FormSchemaChoiceType;
|
|
648
735
|
fieldId?: string;
|
|
736
|
+
fieldKey?: string;
|
|
737
|
+
optionIndex?: number;
|
|
649
738
|
}, timeoutMs?: number): Promise<UpdateWaitResult>;
|
|
650
739
|
/** Fill several semantic form fields in one proxy-side batch. */
|
|
651
740
|
export declare function sendFillFields(session: Session, fields: ProxyFillField[], timeoutMs?: number): Promise<UpdateWaitResult>;
|
|
@@ -668,6 +757,8 @@ export declare function sendListboxPick(session: Session, label: string, opts?:
|
|
|
668
757
|
};
|
|
669
758
|
fieldLabel?: string;
|
|
670
759
|
query?: string;
|
|
760
|
+
fieldId?: string;
|
|
761
|
+
fieldKey?: string;
|
|
671
762
|
}, timeoutMs?: number): Promise<UpdateWaitResult>;
|
|
672
763
|
/** Native `<select>` only: click the control center, then pick by value, label text, or zero-based index. */
|
|
673
764
|
export declare function sendSelectOption(session: Session, x: number, y: number, option: {
|
|
@@ -680,6 +771,9 @@ export declare function sendSetChecked(session: Session, label: string, opts?: {
|
|
|
680
771
|
checked?: boolean;
|
|
681
772
|
exact?: boolean;
|
|
682
773
|
controlType?: 'checkbox' | 'radio';
|
|
774
|
+
fieldKey?: string;
|
|
775
|
+
contextText?: string;
|
|
776
|
+
sectionText?: string;
|
|
683
777
|
}, timeoutMs?: number): Promise<UpdateWaitResult>;
|
|
684
778
|
/** Mouse wheel / scroll. Optional `x`,`y` move pointer before scrolling. */
|
|
685
779
|
export declare function sendWheel(session: Session, deltaY: number, opts?: {
|
|
@@ -732,6 +826,8 @@ export declare function buildPageModel(root: A11yNode, options?: {
|
|
|
732
826
|
blockDetection?: boolean;
|
|
733
827
|
}): PageModel;
|
|
734
828
|
export declare function buildFormSchemas(root: A11yNode, options?: FormSchemaBuildOptions): FormSchemaModel[];
|
|
829
|
+
export declare function buildFormGraphs(root: A11yNode, options?: FormSchemaBuildOptions): FormGraphModel[];
|
|
830
|
+
export declare function formSchemaToFormGraph(schema: FormSchemaModel, pageUrl?: string): FormGraphModel;
|
|
735
831
|
/**
|
|
736
832
|
* Required-field snapshot for automation: every required field in a form, including
|
|
737
833
|
* offscreen entries, annotated with visibility and scroll hints so agents do not
|