@aetherwing/fcp-core 0.1.0 → 0.1.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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parsed-op.d.ts +2 -0
- package/dist/parsed-op.d.ts.map +1 -1
- package/dist/parsed-op.js +6 -3
- package/dist/parsed-op.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/tokenizer.d.ts +10 -0
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +31 -4
- package/dist/tokenizer.js.map +1 -1
- package/package.json +17 -3
- package/src/event-log.ts +0 -209
- package/src/formatter.ts +0 -70
- package/src/index.ts +0 -40
- package/src/parsed-op.ts +0 -64
- package/src/server.ts +0 -241
- package/src/session.ts +0 -163
- package/src/tokenizer.ts +0 -108
- package/src/verb-registry.ts +0 -84
- package/tests/event-log.test.ts +0 -177
- package/tests/formatter.test.ts +0 -61
- package/tests/parsed-op.test.ts +0 -95
- package/tests/server.test.ts +0 -94
- package/tests/session.test.ts +0 -210
- package/tests/tokenizer.test.ts +0 -144
- package/tests/verb-registry.test.ts +0 -76
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { tokenize, isKeyValue, parseKeyValue, isArrow, isSelector, } from "./tokenizer.js";
|
|
1
|
+
export { tokenize, isKeyValue, parseKeyValue, parseKeyValueWithMeta, isArrow, isSelector, } from "./tokenizer.js";
|
|
2
2
|
export { parseOp, isParseError, type ParsedOp, type ParseError, } from "./parsed-op.js";
|
|
3
3
|
export { EventLog } from "./event-log.js";
|
|
4
4
|
export { VerbRegistry, type VerbSpec } from "./verb-registry.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EACL,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EACL,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Tokenizer
|
|
2
|
-
export { tokenize, isKeyValue, parseKeyValue, isArrow, isSelector, } from "./tokenizer.js";
|
|
2
|
+
export { tokenize, isKeyValue, parseKeyValue, parseKeyValueWithMeta, isArrow, isSelector, } from "./tokenizer.js";
|
|
3
3
|
// Parsed operation
|
|
4
4
|
export { parseOp, isParseError, } from "./parsed-op.js";
|
|
5
5
|
// Event log
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,mBAAmB;AACnB,OAAO,EACL,OAAO,EACP,YAAY,GAGb,MAAM,gBAAgB,CAAC;AAExB,YAAY;AACZ,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAiB,MAAM,oBAAoB,CAAC;AAEjE,UAAU;AACV,OAAO,EACL,iBAAiB,GAElB,MAAM,cAAc,CAAC;AAEtB,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvD,SAAS;AACT,OAAO,EACL,eAAe,GAKhB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,OAAO,EACP,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,mBAAmB;AACnB,OAAO,EACL,OAAO,EACP,YAAY,GAGb,MAAM,gBAAgB,CAAC;AAExB,YAAY;AACZ,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAiB,MAAM,oBAAoB,CAAC;AAEjE,UAAU;AACV,OAAO,EACL,iBAAiB,GAElB,MAAM,cAAc,CAAC;AAEtB,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEvD,SAAS;AACT,OAAO,EACL,eAAe,GAKhB,MAAM,aAAa,CAAC"}
|
package/dist/parsed-op.d.ts
CHANGED
package/dist/parsed-op.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsed-op.d.ts","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"parsed-op.d.ts","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CA4B5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,IAAI,UAAU,CAEhF"}
|
package/dist/parsed-op.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { tokenize, isKeyValue,
|
|
1
|
+
import { tokenize, isKeyValue, parseKeyValueWithMeta, isSelector } from "./tokenizer.js";
|
|
2
2
|
/**
|
|
3
3
|
* Parse an operation string into a structured ParsedOp.
|
|
4
4
|
*
|
|
@@ -17,20 +17,23 @@ export function parseOp(input) {
|
|
|
17
17
|
const positionals = [];
|
|
18
18
|
const params = {};
|
|
19
19
|
const selectors = [];
|
|
20
|
+
const quotedParams = new Set();
|
|
20
21
|
for (let i = 1; i < tokens.length; i++) {
|
|
21
22
|
const token = tokens[i];
|
|
22
23
|
if (isSelector(token)) {
|
|
23
24
|
selectors.push(token);
|
|
24
25
|
}
|
|
25
26
|
else if (isKeyValue(token)) {
|
|
26
|
-
const { key, value } =
|
|
27
|
+
const { key, value, wasQuoted } = parseKeyValueWithMeta(token);
|
|
27
28
|
params[key] = value;
|
|
29
|
+
if (wasQuoted)
|
|
30
|
+
quotedParams.add(key);
|
|
28
31
|
}
|
|
29
32
|
else {
|
|
30
33
|
positionals.push(token);
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
|
-
return { verb, positionals, params, selectors, raw };
|
|
36
|
+
return { verb, positionals, params, selectors, raw, quotedParams };
|
|
34
37
|
}
|
|
35
38
|
/**
|
|
36
39
|
* Type guard: check if a parse result is an error.
|
package/dist/parsed-op.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsed-op.js","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"parsed-op.js","sourceRoot":"","sources":["../src/parsed-op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,qBAAqB,EAAE,UAAU,EAAW,MAAM,gBAAgB,CAAC;AAwBlG;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC/D,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACpB,IAAI,SAAS;gBAAE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAA6B;IACxD,OAAO,SAAS,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC;AACzD,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface FcpDomainAdapter<Model, Event> {
|
|
|
47
47
|
* Configuration for creating an FCP MCP server.
|
|
48
48
|
*/
|
|
49
49
|
export interface FcpServerConfig<Model, Event> {
|
|
50
|
-
/** Domain name (e.g., "midi", "
|
|
50
|
+
/** Domain name (e.g., "midi", "drawio"). Used as tool name prefix. */
|
|
51
51
|
domain: string;
|
|
52
52
|
/** Domain adapter implementing all domain-specific logic. */
|
|
53
53
|
adapter: FcpDomainAdapter<Model, Event>;
|
package/dist/tokenizer.d.ts
CHANGED
|
@@ -10,11 +10,21 @@ export declare function tokenize(input: string): string[];
|
|
|
10
10
|
export declare function isKeyValue(token: string): boolean;
|
|
11
11
|
/**
|
|
12
12
|
* Parse a key:value token. The value may include colons (e.g., "style:orthogonal").
|
|
13
|
+
* Strips surrounding quotes from the value for backwards compatibility.
|
|
13
14
|
*/
|
|
14
15
|
export declare function parseKeyValue(token: string): {
|
|
15
16
|
key: string;
|
|
16
17
|
value: string;
|
|
17
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse a key:value token with metadata about quoting.
|
|
21
|
+
* Returns the unquoted value plus a `wasQuoted` flag.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseKeyValueWithMeta(token: string): {
|
|
24
|
+
key: string;
|
|
25
|
+
value: string;
|
|
26
|
+
wasQuoted: boolean;
|
|
27
|
+
};
|
|
18
28
|
/**
|
|
19
29
|
* Check if a token is an arrow operator.
|
|
20
30
|
*/
|
package/dist/tokenizer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuEhD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKjD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAW3E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAavG;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD"}
|
package/dist/tokenizer.js
CHANGED
|
@@ -39,11 +39,12 @@ export function tokenize(input) {
|
|
|
39
39
|
tokens.push(token);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
// Unquoted token —
|
|
42
|
+
// Unquoted token — preserve embedded quotes (e.g., key:"value" → key:"value")
|
|
43
43
|
let token = "";
|
|
44
44
|
while (i < len && input[i] !== " ") {
|
|
45
45
|
if (input[i] === '"') {
|
|
46
|
-
// Embedded quoted value
|
|
46
|
+
// Embedded quoted value — preserve quotes in token for downstream detection
|
|
47
|
+
token += '"';
|
|
47
48
|
i++; // skip opening quote
|
|
48
49
|
while (i < len && input[i] !== '"') {
|
|
49
50
|
if (input[i] === "\\" && i + 1 < len) {
|
|
@@ -63,8 +64,10 @@ export function tokenize(input) {
|
|
|
63
64
|
i++;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
|
-
if (i < len)
|
|
67
|
+
if (i < len) {
|
|
68
|
+
token += '"';
|
|
67
69
|
i++; // skip closing quote
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
else {
|
|
70
73
|
token += input[i];
|
|
@@ -91,12 +94,36 @@ export function isKeyValue(token) {
|
|
|
91
94
|
}
|
|
92
95
|
/**
|
|
93
96
|
* Parse a key:value token. The value may include colons (e.g., "style:orthogonal").
|
|
97
|
+
* Strips surrounding quotes from the value for backwards compatibility.
|
|
94
98
|
*/
|
|
95
99
|
export function parseKeyValue(token) {
|
|
96
100
|
const colonIdx = token.indexOf(":");
|
|
101
|
+
let value = token.slice(colonIdx + 1);
|
|
102
|
+
// Strip surrounding quotes preserved by tokenizer
|
|
103
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
104
|
+
value = value.slice(1, -1);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
key: token.slice(0, colonIdx),
|
|
108
|
+
value,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Parse a key:value token with metadata about quoting.
|
|
113
|
+
* Returns the unquoted value plus a `wasQuoted` flag.
|
|
114
|
+
*/
|
|
115
|
+
export function parseKeyValueWithMeta(token) {
|
|
116
|
+
const colonIdx = token.indexOf(":");
|
|
117
|
+
let value = token.slice(colonIdx + 1);
|
|
118
|
+
let wasQuoted = false;
|
|
119
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
120
|
+
value = value.slice(1, -1);
|
|
121
|
+
wasQuoted = true;
|
|
122
|
+
}
|
|
97
123
|
return {
|
|
98
124
|
key: token.slice(0, colonIdx),
|
|
99
|
-
value
|
|
125
|
+
value,
|
|
126
|
+
wasQuoted,
|
|
100
127
|
};
|
|
101
128
|
}
|
|
102
129
|
/**
|
package/dist/tokenizer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAEzB,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC;QACf,kBAAkB;QAClB,OAAO,CAAC,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG;YAAE,MAAM;QAEpB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,gBAAgB;YAChB,CAAC,EAAE,CAAC,CAAC,qBAAqB;YAC1B,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACnC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC;oBACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC1B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;wBACjB,KAAK,IAAI,IAAI,CAAC;wBACd,CAAC,IAAI,CAAC,CAAC;oBACT,CAAC;yBAAM,CAAC;wBACN,CAAC,EAAE,CAAC;wBACJ,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;wBAClB,CAAC,EAAE,CAAC;oBACN,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;oBAClB,CAAC,EAAE,CAAC;gBACN,CAAC;YACH,CAAC;YACD,IAAI,CAAC,GAAG,GAAG;gBAAE,CAAC,EAAE,CAAC,CAAC,qBAAqB;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../src/tokenizer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aetherwing/fcp-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "File Context Protocol — reusable framework for building MCP servers",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/aetherwing-io/fcp-core"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"fcp",
|
|
12
|
+
"mcp",
|
|
13
|
+
"file-context-protocol",
|
|
14
|
+
"framework"
|
|
15
|
+
],
|
|
5
16
|
"type": "module",
|
|
6
17
|
"main": "dist/index.js",
|
|
7
18
|
"types": "dist/index.d.ts",
|
|
@@ -11,13 +22,16 @@
|
|
|
11
22
|
"types": "./dist/index.d.ts"
|
|
12
23
|
}
|
|
13
24
|
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
14
28
|
"scripts": {
|
|
15
29
|
"build": "tsc",
|
|
16
30
|
"test": "vitest run",
|
|
17
31
|
"test:watch": "vitest"
|
|
18
32
|
},
|
|
19
33
|
"dependencies": {
|
|
20
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.27.1"
|
|
21
35
|
},
|
|
22
36
|
"devDependencies": {
|
|
23
37
|
"@types/node": "^22.15.2",
|
|
@@ -25,6 +39,6 @@
|
|
|
25
39
|
"vitest": "^3.1.1"
|
|
26
40
|
},
|
|
27
41
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
42
|
+
"node": ">=22"
|
|
29
43
|
}
|
|
30
44
|
}
|
package/src/event-log.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sentinel object stored in the event log to mark a checkpoint.
|
|
3
|
-
*/
|
|
4
|
-
const CHECKPOINT_SENTINEL = Symbol("checkpoint");
|
|
5
|
-
|
|
6
|
-
interface CheckpointEntry {
|
|
7
|
-
__type: typeof CHECKPOINT_SENTINEL;
|
|
8
|
-
name: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function isCheckpoint<T>(entry: T | CheckpointEntry): entry is CheckpointEntry {
|
|
12
|
-
return (
|
|
13
|
-
typeof entry === "object" &&
|
|
14
|
-
entry !== null &&
|
|
15
|
-
"__type" in entry &&
|
|
16
|
-
(entry as CheckpointEntry).__type === CHECKPOINT_SENTINEL
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Generic cursor-based event log with undo/redo and named checkpoints.
|
|
22
|
-
*
|
|
23
|
-
* Events are appended at the cursor position. The cursor always points
|
|
24
|
-
* one past the last applied event. Undo moves the cursor back; redo
|
|
25
|
-
* moves it forward. Appending a new event truncates the redo tail.
|
|
26
|
-
*
|
|
27
|
-
* Checkpoint sentinels are stored in the log but skipped during
|
|
28
|
-
* undo/redo traversal.
|
|
29
|
-
*/
|
|
30
|
-
export class EventLog<T> {
|
|
31
|
-
private events: Array<T | CheckpointEntry> = [];
|
|
32
|
-
private _cursor = 0;
|
|
33
|
-
private checkpoints = new Map<string, number>();
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Append an event, truncating any redo history beyond the cursor.
|
|
37
|
-
*/
|
|
38
|
-
append(event: T): void {
|
|
39
|
-
if (this._cursor < this.events.length) {
|
|
40
|
-
this.events.length = this._cursor;
|
|
41
|
-
// Remove checkpoints pointing beyond new length
|
|
42
|
-
for (const [name, idx] of this.checkpoints) {
|
|
43
|
-
if (idx > this._cursor) {
|
|
44
|
-
this.checkpoints.delete(name);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
this.events.push(event);
|
|
49
|
-
this._cursor = this.events.length;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Create a named checkpoint at the current cursor position.
|
|
54
|
-
*/
|
|
55
|
-
checkpoint(name: string): void {
|
|
56
|
-
this.checkpoints.set(name, this._cursor);
|
|
57
|
-
const sentinel: CheckpointEntry = { __type: CHECKPOINT_SENTINEL, name };
|
|
58
|
-
this.events.push(sentinel);
|
|
59
|
-
this._cursor = this.events.length;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Undo up to `count` non-checkpoint events. Returns events in reverse
|
|
64
|
-
* order (most recent first) for the caller to reverse-apply.
|
|
65
|
-
*/
|
|
66
|
-
undo(count: number = 1): T[] {
|
|
67
|
-
const result: T[] = [];
|
|
68
|
-
let pos = this._cursor - 1;
|
|
69
|
-
let undone = 0;
|
|
70
|
-
|
|
71
|
-
while (pos >= 0 && undone < count) {
|
|
72
|
-
const entry = this.events[pos];
|
|
73
|
-
if (!isCheckpoint(entry)) {
|
|
74
|
-
result.push(entry);
|
|
75
|
-
undone++;
|
|
76
|
-
}
|
|
77
|
-
pos--;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
this._cursor = pos + 1;
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Undo to a named checkpoint. Returns events in reverse order.
|
|
86
|
-
* Returns null if the checkpoint doesn't exist or is at/beyond cursor.
|
|
87
|
-
*/
|
|
88
|
-
undoTo(name: string): T[] | null {
|
|
89
|
-
const target = this.checkpoints.get(name);
|
|
90
|
-
if (target === undefined || target >= this._cursor) return null;
|
|
91
|
-
|
|
92
|
-
const result: T[] = [];
|
|
93
|
-
for (let i = this._cursor - 1; i >= target; i--) {
|
|
94
|
-
const entry = this.events[i];
|
|
95
|
-
if (!isCheckpoint(entry)) {
|
|
96
|
-
result.push(entry);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
this._cursor = target;
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Redo up to `count` non-checkpoint events. Returns events in forward
|
|
105
|
-
* order for the caller to re-apply.
|
|
106
|
-
*/
|
|
107
|
-
redo(count: number = 1): T[] {
|
|
108
|
-
const result: T[] = [];
|
|
109
|
-
let pos = this._cursor;
|
|
110
|
-
let redone = 0;
|
|
111
|
-
|
|
112
|
-
while (pos < this.events.length && redone < count) {
|
|
113
|
-
const entry = this.events[pos];
|
|
114
|
-
if (!isCheckpoint(entry)) {
|
|
115
|
-
result.push(entry);
|
|
116
|
-
redone++;
|
|
117
|
-
}
|
|
118
|
-
pos++;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this._cursor = pos;
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Get the last N non-checkpoint events (up to cursor). Returned in
|
|
127
|
-
* chronological order (oldest first).
|
|
128
|
-
*/
|
|
129
|
-
recent(count?: number): T[] {
|
|
130
|
-
const limit = count ?? this._cursor;
|
|
131
|
-
const result: T[] = [];
|
|
132
|
-
for (let i = this._cursor - 1; i >= 0 && result.length < limit; i--) {
|
|
133
|
-
const entry = this.events[i];
|
|
134
|
-
if (!isCheckpoint(entry)) {
|
|
135
|
-
result.push(entry);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return result.reverse();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Current cursor position (one past last applied event).
|
|
143
|
-
*/
|
|
144
|
-
get cursor(): number {
|
|
145
|
-
return this._cursor;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Total number of entries in the log (including checkpoints).
|
|
150
|
-
*/
|
|
151
|
-
get length(): number {
|
|
152
|
-
return this.events.length;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Whether there are events before the cursor that can be undone.
|
|
157
|
-
*/
|
|
158
|
-
canUndo(): boolean {
|
|
159
|
-
for (let i = this._cursor - 1; i >= 0; i--) {
|
|
160
|
-
if (!isCheckpoint(this.events[i])) return true;
|
|
161
|
-
}
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Whether there are events after the cursor that can be redone.
|
|
167
|
-
*/
|
|
168
|
-
canRedo(): boolean {
|
|
169
|
-
return this._cursor < this.events.length;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Get the event index for a named checkpoint.
|
|
174
|
-
* Returns undefined if the checkpoint doesn't exist.
|
|
175
|
-
*/
|
|
176
|
-
getCheckpointIndex(name: string): number | undefined {
|
|
177
|
-
return this.checkpoints.get(name);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Number of named checkpoints.
|
|
182
|
-
*/
|
|
183
|
-
get checkpointCount(): number {
|
|
184
|
-
return this.checkpoints.size;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Get all checkpoint names and their indices.
|
|
189
|
-
*/
|
|
190
|
-
getCheckpoints(): Map<string, number> {
|
|
191
|
-
return new Map(this.checkpoints);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Get non-checkpoint events from a given index to the cursor.
|
|
196
|
-
* Used for diff queries.
|
|
197
|
-
*/
|
|
198
|
-
eventsSince(fromIndex: number): T[] {
|
|
199
|
-
const result: T[] = [];
|
|
200
|
-
const end = Math.min(this._cursor, this.events.length);
|
|
201
|
-
for (let i = fromIndex; i < end; i++) {
|
|
202
|
-
const entry = this.events[i];
|
|
203
|
-
if (!isCheckpoint(entry)) {
|
|
204
|
-
result.push(entry);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
|
-
}
|
package/src/formatter.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format a result message with a prefix character.
|
|
3
|
-
*
|
|
4
|
-
* Prefix conventions:
|
|
5
|
-
* + created
|
|
6
|
-
* ~ modified (edge/connection)
|
|
7
|
-
* * changed (property)
|
|
8
|
-
* - removed
|
|
9
|
-
* ! meta/group operation
|
|
10
|
-
* @ bulk/layout operation
|
|
11
|
-
*/
|
|
12
|
-
export function formatResult(
|
|
13
|
-
success: boolean,
|
|
14
|
-
message: string,
|
|
15
|
-
prefix?: string,
|
|
16
|
-
): string {
|
|
17
|
-
if (!success) return `ERROR: ${message}`;
|
|
18
|
-
if (prefix) return `${prefix} ${message}`;
|
|
19
|
-
return message;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Suggest a correction for a misspelled input by finding the closest
|
|
24
|
-
* candidate using Levenshtein distance. Returns null if no candidate
|
|
25
|
-
* is close enough (distance > 3).
|
|
26
|
-
*/
|
|
27
|
-
export function suggest(input: string, candidates: string[]): string | null {
|
|
28
|
-
if (candidates.length === 0) return null;
|
|
29
|
-
|
|
30
|
-
let best: string | null = null;
|
|
31
|
-
let bestDist = Infinity;
|
|
32
|
-
|
|
33
|
-
for (const candidate of candidates) {
|
|
34
|
-
const dist = levenshtein(input.toLowerCase(), candidate.toLowerCase());
|
|
35
|
-
if (dist < bestDist) {
|
|
36
|
-
bestDist = dist;
|
|
37
|
-
best = candidate;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return bestDist <= 3 ? best : null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Compute Levenshtein distance between two strings.
|
|
46
|
-
*/
|
|
47
|
-
function levenshtein(a: string, b: string): number {
|
|
48
|
-
const m = a.length;
|
|
49
|
-
const n = b.length;
|
|
50
|
-
|
|
51
|
-
// Use a single-row DP array
|
|
52
|
-
const prev = new Array<number>(n + 1);
|
|
53
|
-
for (let j = 0; j <= n; j++) prev[j] = j;
|
|
54
|
-
|
|
55
|
-
for (let i = 1; i <= m; i++) {
|
|
56
|
-
let prevDiag = prev[0];
|
|
57
|
-
prev[0] = i;
|
|
58
|
-
for (let j = 1; j <= n; j++) {
|
|
59
|
-
const temp = prev[j];
|
|
60
|
-
if (a[i - 1] === b[j - 1]) {
|
|
61
|
-
prev[j] = prevDiag;
|
|
62
|
-
} else {
|
|
63
|
-
prev[j] = 1 + Math.min(prevDiag, prev[j - 1], prev[j]);
|
|
64
|
-
}
|
|
65
|
-
prevDiag = temp;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return prev[n];
|
|
70
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// Tokenizer
|
|
2
|
-
export {
|
|
3
|
-
tokenize,
|
|
4
|
-
isKeyValue,
|
|
5
|
-
parseKeyValue,
|
|
6
|
-
isArrow,
|
|
7
|
-
isSelector,
|
|
8
|
-
} from "./tokenizer.js";
|
|
9
|
-
|
|
10
|
-
// Parsed operation
|
|
11
|
-
export {
|
|
12
|
-
parseOp,
|
|
13
|
-
isParseError,
|
|
14
|
-
type ParsedOp,
|
|
15
|
-
type ParseError,
|
|
16
|
-
} from "./parsed-op.js";
|
|
17
|
-
|
|
18
|
-
// Event log
|
|
19
|
-
export { EventLog } from "./event-log.js";
|
|
20
|
-
|
|
21
|
-
// Verb registry
|
|
22
|
-
export { VerbRegistry, type VerbSpec } from "./verb-registry.js";
|
|
23
|
-
|
|
24
|
-
// Session
|
|
25
|
-
export {
|
|
26
|
-
SessionDispatcher,
|
|
27
|
-
type SessionHooks,
|
|
28
|
-
} from "./session.js";
|
|
29
|
-
|
|
30
|
-
// Formatter
|
|
31
|
-
export { formatResult, suggest } from "./formatter.js";
|
|
32
|
-
|
|
33
|
-
// Server
|
|
34
|
-
export {
|
|
35
|
-
createFcpServer,
|
|
36
|
-
type FcpServerConfig,
|
|
37
|
-
type FcpDomainAdapter,
|
|
38
|
-
type OpResult,
|
|
39
|
-
type QueryResult,
|
|
40
|
-
} from "./server.js";
|
package/src/parsed-op.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { tokenize, isKeyValue, parseKeyValue, isSelector, isArrow } from "./tokenizer.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A successfully parsed operation.
|
|
5
|
-
*/
|
|
6
|
-
export interface ParsedOp {
|
|
7
|
-
verb: string;
|
|
8
|
-
positionals: string[];
|
|
9
|
-
params: Record<string, string>;
|
|
10
|
-
selectors: string[];
|
|
11
|
-
raw: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A parse failure.
|
|
16
|
-
*/
|
|
17
|
-
export interface ParseError {
|
|
18
|
-
success: false;
|
|
19
|
-
error: string;
|
|
20
|
-
raw: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Parse an operation string into a structured ParsedOp.
|
|
25
|
-
*
|
|
26
|
-
* First token becomes the verb. Remaining tokens are classified:
|
|
27
|
-
* @-prefixed -> selectors
|
|
28
|
-
* key:value -> params
|
|
29
|
-
* everything else -> positionals (in order)
|
|
30
|
-
*/
|
|
31
|
-
export function parseOp(input: string): ParsedOp | ParseError {
|
|
32
|
-
const raw = input.trim();
|
|
33
|
-
const tokens = tokenize(raw);
|
|
34
|
-
|
|
35
|
-
if (tokens.length === 0) {
|
|
36
|
-
return { success: false, error: "empty operation", raw };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const verb = tokens[0].toLowerCase();
|
|
40
|
-
const positionals: string[] = [];
|
|
41
|
-
const params: Record<string, string> = {};
|
|
42
|
-
const selectors: string[] = [];
|
|
43
|
-
|
|
44
|
-
for (let i = 1; i < tokens.length; i++) {
|
|
45
|
-
const token = tokens[i];
|
|
46
|
-
if (isSelector(token)) {
|
|
47
|
-
selectors.push(token);
|
|
48
|
-
} else if (isKeyValue(token)) {
|
|
49
|
-
const { key, value } = parseKeyValue(token);
|
|
50
|
-
params[key] = value;
|
|
51
|
-
} else {
|
|
52
|
-
positionals.push(token);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return { verb, positionals, params, selectors, raw };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Type guard: check if a parse result is an error.
|
|
61
|
-
*/
|
|
62
|
-
export function isParseError(result: ParsedOp | ParseError): result is ParseError {
|
|
63
|
-
return "success" in result && result.success === false;
|
|
64
|
-
}
|