@datafog/fogclaw 0.2.0 → 0.3.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/backlog-tools.d.ts +57 -0
  3. package/dist/backlog-tools.d.ts.map +1 -0
  4. package/dist/backlog-tools.js +173 -0
  5. package/dist/backlog-tools.js.map +1 -0
  6. package/dist/backlog.d.ts +82 -0
  7. package/dist/backlog.d.ts.map +1 -0
  8. package/dist/backlog.js +169 -0
  9. package/dist/backlog.js.map +1 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +6 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +87 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/message-sending-handler.d.ts +2 -1
  18. package/dist/message-sending-handler.d.ts.map +1 -1
  19. package/dist/message-sending-handler.js +5 -1
  20. package/dist/message-sending-handler.js.map +1 -1
  21. package/dist/tool-result-handler.d.ts +2 -1
  22. package/dist/tool-result-handler.d.ts.map +1 -1
  23. package/dist/tool-result-handler.js +5 -1
  24. package/dist/tool-result-handler.js.map +1 -1
  25. package/dist/types.d.ts +15 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js.map +1 -1
  28. package/openclaw.plugin.json +11 -1
  29. package/package.json +7 -1
  30. package/.github/workflows/harness-docs.yml +0 -30
  31. package/AGENTS.md +0 -28
  32. package/docs/DATA.md +0 -28
  33. package/docs/DESIGN.md +0 -17
  34. package/docs/DOMAIN_DOCS.md +0 -30
  35. package/docs/FRONTEND.md +0 -24
  36. package/docs/OBSERVABILITY.md +0 -32
  37. package/docs/PLANS.md +0 -171
  38. package/docs/PRODUCT_SENSE.md +0 -20
  39. package/docs/RELIABILITY.md +0 -60
  40. package/docs/SECURITY.md +0 -52
  41. package/docs/design-docs/core-beliefs.md +0 -17
  42. package/docs/design-docs/index.md +0 -8
  43. package/docs/generated/README.md +0 -36
  44. package/docs/generated/memory.md +0 -1
  45. package/docs/plans/2026-02-16-fogclaw-design.md +0 -172
  46. package/docs/plans/2026-02-16-fogclaw-implementation.md +0 -1606
  47. package/docs/plans/README.md +0 -15
  48. package/docs/plans/active/2026-02-16-feat-openclaw-official-submission-plan.md +0 -386
  49. package/docs/plans/active/2026-02-17-feat-release-fogclaw-via-datafog-package-plan.md +0 -328
  50. package/docs/plans/active/2026-02-17-feat-submit-fogclaw-to-openclaw-plan.md +0 -244
  51. package/docs/plans/active/2026-02-17-feat-tool-result-pii-scanning-plan.md +0 -293
  52. package/docs/plans/tech-debt-tracker.md +0 -42
  53. package/docs/plugins/fogclaw.md +0 -101
  54. package/docs/runbooks/address-review-findings.md +0 -30
  55. package/docs/runbooks/ci-failures.md +0 -46
  56. package/docs/runbooks/code-review.md +0 -34
  57. package/docs/runbooks/merge-change.md +0 -28
  58. package/docs/runbooks/pull-request.md +0 -45
  59. package/docs/runbooks/record-evidence.md +0 -43
  60. package/docs/runbooks/reproduce-bug.md +0 -42
  61. package/docs/runbooks/respond-to-feedback.md +0 -42
  62. package/docs/runbooks/review-findings.md +0 -31
  63. package/docs/runbooks/submit-openclaw-plugin.md +0 -68
  64. package/docs/runbooks/update-agents-md.md +0 -59
  65. package/docs/runbooks/update-domain-docs.md +0 -42
  66. package/docs/runbooks/validate-current-state.md +0 -41
  67. package/docs/runbooks/verify-release.md +0 -69
  68. package/docs/specs/2026-02-16-feat-openclaw-official-submission-spec.md +0 -115
  69. package/docs/specs/2026-02-17-feat-outbound-message-pii-scanning-spec.md +0 -93
  70. package/docs/specs/2026-02-17-feat-submit-fogclaw-to-openclaw.md +0 -125
  71. package/docs/specs/2026-02-17-feat-tool-result-pii-scanning-spec.md +0 -122
  72. package/docs/specs/README.md +0 -5
  73. package/docs/specs/index.md +0 -8
  74. package/docs/spikes/README.md +0 -8
  75. package/fogclaw.config.example.json +0 -33
  76. package/scripts/ci/he-docs-config.json +0 -123
  77. package/scripts/ci/he-docs-drift.sh +0 -112
  78. package/scripts/ci/he-docs-lint.sh +0 -234
  79. package/scripts/ci/he-plans-lint.sh +0 -354
  80. package/scripts/ci/he-runbooks-lint.sh +0 -445
  81. package/scripts/ci/he-specs-lint.sh +0 -258
  82. package/scripts/ci/he-spikes-lint.sh +0 -249
  83. package/scripts/runbooks/select-runbooks.sh +0 -154
  84. package/src/config.ts +0 -183
  85. package/src/engines/gliner.ts +0 -240
  86. package/src/engines/regex.ts +0 -71
  87. package/src/extract.ts +0 -98
  88. package/src/index.ts +0 -381
  89. package/src/message-sending-handler.ts +0 -87
  90. package/src/redactor.ts +0 -51
  91. package/src/scanner.ts +0 -196
  92. package/src/tool-result-handler.ts +0 -133
  93. package/src/types.ts +0 -75
  94. package/tests/config.test.ts +0 -78
  95. package/tests/extract.test.ts +0 -185
  96. package/tests/gliner.test.ts +0 -289
  97. package/tests/message-sending-handler.test.ts +0 -244
  98. package/tests/plugin-smoke.test.ts +0 -250
  99. package/tests/redactor.test.ts +0 -320
  100. package/tests/regex.test.ts +0 -345
  101. package/tests/scanner.test.ts +0 -348
  102. package/tests/tool-result-handler.test.ts +0 -329
  103. package/tsconfig.json +0 -20
@@ -1,289 +0,0 @@
1
- import { beforeAll, beforeEach, afterAll, describe, it, expect, vi } from "vitest";
2
- import fs from "node:fs/promises";
3
- import os from "node:os";
4
- import path from "node:path";
5
-
6
- // Mock the gliner npm package so we don't need the actual 1.4GB model
7
- vi.mock("gliner", () => {
8
- class MockGliner {
9
- private config: any;
10
-
11
- constructor(config: any) {
12
- this.config = config;
13
- }
14
-
15
- async initialize(): Promise<void> {
16
- // No-op in mock
17
- }
18
-
19
- async inference(
20
- request: { texts: string[]; entities: string[] } | string | string[],
21
- maybeEntities?: string[],
22
- _flatNer = false,
23
- _threshold = 0.5,
24
- ): Promise<Array<{ text: string; label: string; score: number; start: number; end: number }>> {
25
- const text =
26
- typeof request === "string"
27
- ? request
28
- : Array.isArray(request)
29
- ? request[0] ?? ""
30
- : request.texts[0] ?? "";
31
- const requestEntities =
32
- typeof request === "object" && request !== null && "entities" in request
33
- ? request.entities
34
- : undefined;
35
- const labels =
36
- Array.isArray(maybeEntities)
37
- ? maybeEntities
38
- : requestEntities ?? [];
39
- const results: Array<{ text: string; label: string; score: number; start: number; end: number }> = [];
40
-
41
- // Simulate entity detection for "John Smith"
42
- const johnIndex = text.indexOf("John Smith");
43
- if (johnIndex !== -1 && labels.includes("person")) {
44
- results.push({
45
- text: "John Smith",
46
- label: "person",
47
- score: 0.95,
48
- start: johnIndex,
49
- end: johnIndex + "John Smith".length,
50
- });
51
- }
52
-
53
- // Simulate entity detection for "Acme Corp"
54
- const acmeIndex = text.indexOf("Acme Corp");
55
- if (acmeIndex !== -1 && labels.includes("organization")) {
56
- results.push({
57
- text: "Acme Corp",
58
- label: "organization",
59
- score: 0.88,
60
- start: acmeIndex,
61
- end: acmeIndex + "Acme Corp".length,
62
- });
63
- }
64
-
65
- // Simulate entity detection for "New York"
66
- const nyIndex = text.indexOf("New York");
67
- if (nyIndex !== -1 && labels.includes("location")) {
68
- results.push({
69
- text: "New York",
70
- label: "location",
71
- score: 0.91,
72
- start: nyIndex,
73
- end: nyIndex + "New York".length,
74
- });
75
- }
76
-
77
- return results;
78
- }
79
- }
80
-
81
- return { Gliner: MockGliner };
82
- });
83
-
84
- vi.mock("gliner/node", () => {
85
- class MockGliner {
86
- private config: any;
87
-
88
- constructor(config: any) {
89
- this.config = config;
90
- }
91
-
92
- async initialize(): Promise<void> {
93
- // No-op in mock
94
- }
95
-
96
- async inference(
97
- request: { texts: string[]; entities: string[] } | string | string[],
98
- maybeEntities?: string[],
99
- _flatNer = false,
100
- _threshold = 0.5,
101
- ): Promise<Array<{ text: string; label: string; score: number; start: number; end: number }>> {
102
- const text =
103
- typeof request === "string"
104
- ? request
105
- : Array.isArray(request)
106
- ? request[0] ?? ""
107
- : request.texts[0] ?? "";
108
- const requestEntities =
109
- typeof request === "object" && request !== null && "entities" in request
110
- ? request.entities
111
- : undefined;
112
- const labels =
113
- Array.isArray(maybeEntities)
114
- ? maybeEntities
115
- : requestEntities ?? [];
116
- const results: Array<{ text: string; label: string; score: number; start: number; end: number }> = [];
117
-
118
- // Simulate entity detection for "John Smith"
119
- const johnIndex = text.indexOf("John Smith");
120
- if (johnIndex !== -1 && labels.includes("person")) {
121
- results.push({
122
- text: "John Smith",
123
- label: "person",
124
- score: 0.95,
125
- start: johnIndex,
126
- end: johnIndex + "John Smith".length,
127
- });
128
- }
129
-
130
- // Simulate entity detection for "Acme Corp"
131
- const acmeIndex = text.indexOf("Acme Corp");
132
- if (acmeIndex !== -1 && labels.includes("organization")) {
133
- results.push({
134
- text: "Acme Corp",
135
- label: "organization",
136
- score: 0.88,
137
- start: acmeIndex,
138
- end: acmeIndex + "Acme Corp".length,
139
- });
140
- }
141
-
142
- // Simulate entity detection for "New York"
143
- const nyIndex = text.indexOf("New York");
144
- if (nyIndex !== -1 && labels.includes("location")) {
145
- results.push({
146
- text: "New York",
147
- label: "location",
148
- score: 0.91,
149
- start: nyIndex,
150
- end: nyIndex + "New York".length,
151
- });
152
- }
153
-
154
- return results;
155
- }
156
- }
157
-
158
- return { Gliner: MockGliner };
159
- });
160
-
161
- import { GlinerEngine } from "../src/engines/gliner.js";
162
-
163
- const TEST_ONNX_MODEL_PATH = path.join(os.tmpdir(), "fogclaw-gliner-model-test.onnx");
164
-
165
- beforeAll(async () => {
166
- await fs.writeFile(TEST_ONNX_MODEL_PATH, "mock-onnx-model", "utf8");
167
- });
168
-
169
- afterAll(async () => {
170
- await fs.unlink(TEST_ONNX_MODEL_PATH).catch(() => undefined);
171
- });
172
-
173
- describe("GlinerEngine", () => {
174
- let engine: GlinerEngine;
175
-
176
- beforeEach(async () => {
177
- engine = new GlinerEngine(TEST_ONNX_MODEL_PATH, 0.5);
178
- await engine.initialize();
179
- });
180
-
181
- it("detects person entities with canonical PERSON label", async () => {
182
- const entities = await engine.scan("My name is John Smith and I live here.");
183
-
184
- expect(entities).toHaveLength(1);
185
- expect(entities[0].text).toBe("John Smith");
186
- expect(entities[0].label).toBe("PERSON");
187
- });
188
-
189
- it("detects organization entities with canonical ORGANIZATION label", async () => {
190
- const entities = await engine.scan("I work at Acme Corp downtown.");
191
-
192
- expect(entities).toHaveLength(1);
193
- expect(entities[0].text).toBe("Acme Corp");
194
- expect(entities[0].label).toBe("ORGANIZATION");
195
- });
196
-
197
- it("detects multiple entity types in the same text", async () => {
198
- const entities = await engine.scan(
199
- "John Smith works at Acme Corp in New York.",
200
- );
201
-
202
- expect(entities).toHaveLength(3);
203
-
204
- const labels = entities.map((e) => e.label);
205
- expect(labels).toContain("PERSON");
206
- expect(labels).toContain("ORGANIZATION");
207
- expect(labels).toContain("LOCATION");
208
- });
209
-
210
- it("returns empty array for text with no entities", async () => {
211
- const entities = await engine.scan("Hello world, this is a test.");
212
-
213
- expect(entities).toEqual([]);
214
- });
215
-
216
- it("returns empty array for empty string input", async () => {
217
- const entities = await engine.scan("");
218
-
219
- expect(entities).toEqual([]);
220
- });
221
-
222
- it("allows setting custom labels without crashing", async () => {
223
- expect(() => engine.setCustomLabels(["product", "event"])).not.toThrow();
224
-
225
- // Scan still works after setting custom labels
226
- const entities = await engine.scan("John Smith attended the event.");
227
- expect(entities).toHaveLength(1);
228
- expect(entities[0].label).toBe("PERSON");
229
- });
230
-
231
- it("applies canonical type mapping (lowercase person -> PERSON)", async () => {
232
- // The mock returns lowercase "person" as label; canonicalType should map it to "PERSON"
233
- const entities = await engine.scan("John Smith is here.");
234
-
235
- expect(entities[0].label).toBe("PERSON");
236
- // Verify it's not lowercase
237
- expect(entities[0].label).not.toBe("person");
238
- });
239
-
240
- it("sets source to gliner for all detected entities", async () => {
241
- const entities = await engine.scan(
242
- "John Smith works at Acme Corp in New York.",
243
- );
244
-
245
- for (const entity of entities) {
246
- expect(entity.source).toBe("gliner");
247
- }
248
- });
249
-
250
- it("confidence comes from model score", async () => {
251
- const entities = await engine.scan(
252
- "John Smith works at Acme Corp in New York.",
253
- );
254
-
255
- const person = entities.find((e) => e.label === "PERSON");
256
- const org = entities.find((e) => e.label === "ORGANIZATION");
257
- const loc = entities.find((e) => e.label === "LOCATION");
258
-
259
- // These match the scores set in our mock
260
- expect(person?.confidence).toBe(0.95);
261
- expect(org?.confidence).toBe(0.88);
262
- expect(loc?.confidence).toBe(0.91);
263
- });
264
-
265
- it("throws if scan is called before initialize", async () => {
266
- const uninitializedEngine = new GlinerEngine("some-model", 0.5);
267
-
268
- await expect(uninitializedEngine.scan("test")).rejects.toThrow(
269
- "GLiNER engine not initialized. Call initialize() first.",
270
- );
271
- });
272
-
273
- it("reports isInitialized correctly", async () => {
274
- const freshEngine = new GlinerEngine(TEST_ONNX_MODEL_PATH, 0.5);
275
- expect(freshEngine.isInitialized).toBe(false);
276
-
277
- await freshEngine.initialize();
278
- expect(freshEngine.isInitialized).toBe(true);
279
- });
280
-
281
- it("includes correct start and end offsets", async () => {
282
- const text = "Contact John Smith for details.";
283
- const entities = await engine.scan(text);
284
-
285
- expect(entities).toHaveLength(1);
286
- expect(entities[0].start).toBe(8); // "Contact " is 8 chars
287
- expect(entities[0].end).toBe(18); // 8 + "John Smith".length = 18
288
- });
289
- });
@@ -1,244 +0,0 @@
1
- import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";
2
- import { createMessageSendingHandler } from "../src/message-sending-handler.js";
3
- import { Scanner } from "../src/scanner.js";
4
- import type { FogClawConfig } from "../src/types.js";
5
-
6
- function makeConfig(overrides: Partial<FogClawConfig> = {}): FogClawConfig {
7
- return {
8
- enabled: true,
9
- guardrail_mode: "redact",
10
- redactStrategy: "token",
11
- model: "invalid:/not/real/model",
12
- confidence_threshold: 0.5,
13
- custom_entities: [],
14
- entityActions: {},
15
- entityConfidenceThresholds: {},
16
- allowlist: { values: [], patterns: [], entities: {} },
17
- auditEnabled: false,
18
- ...overrides,
19
- };
20
- }
21
-
22
- function makeLogger() {
23
- return {
24
- info: vi.fn(),
25
- warn: vi.fn(),
26
- };
27
- }
28
-
29
- function makeCtx(channelId = "telegram") {
30
- return { channelId };
31
- }
32
-
33
- describe("createMessageSendingHandler", () => {
34
- // Suppress GLiNER init warnings
35
- beforeAll(() => {
36
- vi.spyOn(console, "warn").mockImplementation(() => undefined);
37
- });
38
- afterAll(() => {
39
- vi.restoreAllMocks();
40
- });
41
-
42
- it("returns an async function", () => {
43
- const config = makeConfig();
44
- const scanner = new Scanner(config);
45
- const handler = createMessageSendingHandler(config, scanner);
46
- expect(typeof handler).toBe("function");
47
- });
48
-
49
- it("redacts SSN in outbound message", async () => {
50
- const config = makeConfig();
51
- const scanner = new Scanner(config);
52
- const handler = createMessageSendingHandler(config, scanner);
53
-
54
- const result = await handler(
55
- { to: "user123", content: "Your SSN is 123-45-6789." },
56
- makeCtx(),
57
- );
58
-
59
- expect(result).toBeDefined();
60
- expect(result!.content).toContain("[SSN_1]");
61
- expect(result!.content).not.toContain("123-45-6789");
62
- expect(result!.cancel).toBeUndefined();
63
- });
64
-
65
- it("redacts email and phone in outbound message", async () => {
66
- const config = makeConfig();
67
- const scanner = new Scanner(config);
68
- const handler = createMessageSendingHandler(config, scanner);
69
-
70
- const result = await handler(
71
- { to: "user", content: "Call 555-123-4567 or email john@example.com" },
72
- makeCtx(),
73
- );
74
-
75
- expect(result).toBeDefined();
76
- expect(result!.content).toContain("[PHONE_1]");
77
- expect(result!.content).toContain("[EMAIL_1]");
78
- expect(result!.content).not.toContain("555-123-4567");
79
- expect(result!.content).not.toContain("john@example.com");
80
- });
81
-
82
- it("returns void when no PII found", async () => {
83
- const config = makeConfig();
84
- const scanner = new Scanner(config);
85
- const handler = createMessageSendingHandler(config, scanner);
86
-
87
- const result = await handler(
88
- { to: "user", content: "Hello, how can I help you today?" },
89
- makeCtx(),
90
- );
91
-
92
- expect(result).toBeUndefined();
93
- });
94
-
95
- it("returns void for empty content", async () => {
96
- const config = makeConfig();
97
- const scanner = new Scanner(config);
98
- const handler = createMessageSendingHandler(config, scanner);
99
-
100
- const result = await handler(
101
- { to: "user", content: "" },
102
- makeCtx(),
103
- );
104
-
105
- expect(result).toBeUndefined();
106
- });
107
-
108
- it("never returns cancel: true", async () => {
109
- const config = makeConfig({
110
- entityActions: { SSN: "block" },
111
- });
112
- const scanner = new Scanner(config);
113
- const handler = createMessageSendingHandler(config, scanner);
114
-
115
- const result = await handler(
116
- { to: "user", content: "SSN 123-45-6789" },
117
- makeCtx(),
118
- );
119
-
120
- expect(result).toBeDefined();
121
- expect(result!.cancel).toBeUndefined();
122
- expect(result!.content).toContain("[SSN_1]");
123
- });
124
-
125
- it("all guardrail modes produce span-level redaction", async () => {
126
- const config = makeConfig({
127
- entityActions: { SSN: "block", EMAIL: "warn", PHONE: "redact" },
128
- });
129
- const scanner = new Scanner(config);
130
- const handler = createMessageSendingHandler(config, scanner);
131
-
132
- const result = await handler(
133
- {
134
- to: "user",
135
- content: "SSN 123-45-6789, email john@example.com, call 555-123-4567",
136
- },
137
- makeCtx(),
138
- );
139
-
140
- expect(result).toBeDefined();
141
- expect(result!.content).toContain("[SSN_1]");
142
- expect(result!.content).toContain("[EMAIL_1]");
143
- expect(result!.content).toContain("[PHONE_1]");
144
- });
145
-
146
- it("respects allowlist — global values", async () => {
147
- const config = makeConfig({
148
- allowlist: {
149
- values: ["noreply@example.com"],
150
- patterns: [],
151
- entities: {},
152
- },
153
- });
154
- const scanner = new Scanner(config);
155
- const handler = createMessageSendingHandler(config, scanner);
156
-
157
- const result = await handler(
158
- { to: "user", content: "Contact noreply@example.com for help" },
159
- makeCtx(),
160
- );
161
-
162
- expect(result).toBeUndefined();
163
- });
164
-
165
- it("uses mask redaction strategy", async () => {
166
- const config = makeConfig({ redactStrategy: "mask" });
167
- const scanner = new Scanner(config);
168
- const handler = createMessageSendingHandler(config, scanner);
169
-
170
- const result = await handler(
171
- { to: "user", content: "SSN is 123-45-6789" },
172
- makeCtx(),
173
- );
174
-
175
- expect(result).toBeDefined();
176
- expect(result!.content).toContain("***********");
177
- expect(result!.content).not.toContain("123-45-6789");
178
- });
179
-
180
- it("uses hash redaction strategy", async () => {
181
- const config = makeConfig({ redactStrategy: "hash" });
182
- const scanner = new Scanner(config);
183
- const handler = createMessageSendingHandler(config, scanner);
184
-
185
- const result = await handler(
186
- { to: "user", content: "SSN is 123-45-6789" },
187
- makeCtx(),
188
- );
189
-
190
- expect(result).toBeDefined();
191
- expect(result!.content).toMatch(/\[SSN_[a-f0-9]{12}\]/);
192
- });
193
-
194
- describe("audit logging", () => {
195
- it("emits audit log with source outbound when PII found", async () => {
196
- const config = makeConfig({ auditEnabled: true });
197
- const scanner = new Scanner(config);
198
- const logger = makeLogger();
199
- const handler = createMessageSendingHandler(config, scanner, logger);
200
-
201
- await handler(
202
- { to: "user", content: "SSN 123-45-6789" },
203
- makeCtx("discord"),
204
- );
205
-
206
- expect(logger.info).toHaveBeenCalledOnce();
207
- const logCall = logger.info.mock.calls[0][0] as string;
208
- expect(logCall).toContain("[FOGCLAW AUDIT]");
209
- expect(logCall).toContain("outbound_scan");
210
- expect(logCall).toContain('"source":"outbound"');
211
- expect(logCall).toContain('"channelId":"discord"');
212
- expect(logCall).toContain('"SSN"');
213
- expect(logCall).not.toContain("123-45-6789");
214
- });
215
-
216
- it("does not emit audit log when auditEnabled is false", async () => {
217
- const config = makeConfig({ auditEnabled: false });
218
- const scanner = new Scanner(config);
219
- const logger = makeLogger();
220
- const handler = createMessageSendingHandler(config, scanner, logger);
221
-
222
- await handler(
223
- { to: "user", content: "SSN 123-45-6789" },
224
- makeCtx(),
225
- );
226
-
227
- expect(logger.info).not.toHaveBeenCalled();
228
- });
229
-
230
- it("does not emit audit log when no PII found", async () => {
231
- const config = makeConfig({ auditEnabled: true });
232
- const scanner = new Scanner(config);
233
- const logger = makeLogger();
234
- const handler = createMessageSendingHandler(config, scanner, logger);
235
-
236
- await handler(
237
- { to: "user", content: "Clean message" },
238
- makeCtx(),
239
- );
240
-
241
- expect(logger.info).not.toHaveBeenCalled();
242
- });
243
- });
244
- });