@wwjd/pi-graphify 0.1.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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared result formatting helpers for graphify tools.
3
+ */
4
+
5
+ import type { GraphifyResult } from "../backends/types.js";
6
+ import { GraphifyError } from "../errors.js";
7
+ import { errorResult, successResult, type ToolResult } from "./types.js";
8
+
9
+ /** Format a successful Graphify result into tool content. */
10
+ export function formatGraphifyResult(result: GraphifyResult): ToolResult {
11
+ return successResult(result);
12
+ }
13
+
14
+ /** Format a Graphify or unexpected error into friendly tool content. */
15
+ export function formatGraphifyError(error: unknown): ToolResult {
16
+ if (error instanceof GraphifyError) {
17
+ return errorResult(error.userMessage, { code: error.code, details: error.details });
18
+ }
19
+
20
+ if (error instanceof Error) {
21
+ return errorResult(error.message);
22
+ }
23
+
24
+ return errorResult(String(error));
25
+ }
@@ -1,11 +1,89 @@
1
- /**
2
- * Tool registration barrel
3
- */
4
-
5
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
- import type { GraphifyConfig } from "../config.js";
2
+ import type { GraphifyCapabilities } from "../backends/types.js";
3
+ import type { GraphifyCoordinator } from "../coordinator.js";
4
+ import { registerAffectedTool } from "./affected.js";
5
+ import { registerBuildTool } from "./build.js";
6
+ import { registerExplainTool } from "./explain.js";
7
+ import { registerPathTool } from "./path.js";
8
+ import { registerQueryTool } from "./query.js";
7
9
  import { registerStatusTool } from "./status.js";
10
+ import { registerVersionTool } from "./version.js";
11
+
12
+ export type CoordinatorProvider = () => GraphifyCoordinator | null;
13
+
14
+ export interface RegisterToolsOptions {
15
+ /** Tracks tool names that have already been registered to prevent duplicates. */
16
+ registeredToolNames: Set<string>;
17
+ }
18
+
19
+ export function registerGraphifyTools(
20
+ pi: ExtensionAPI,
21
+ getCoordinator: CoordinatorProvider,
22
+ options: RegisterToolsOptions,
23
+ ): void {
24
+ const { registeredToolNames } = options;
25
+ const coordinator = getCoordinator();
26
+ if (!coordinator) {
27
+ return;
28
+ }
29
+
30
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_status", () =>
31
+ registerStatusTool(pi, getCoordinator),
32
+ );
33
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_build", () =>
34
+ registerBuildTool(pi, getCoordinator),
35
+ );
36
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_query", () =>
37
+ registerQueryTool(pi, getCoordinator),
38
+ );
39
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_path", () =>
40
+ registerPathTool(pi, getCoordinator),
41
+ );
42
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_explain", () =>
43
+ registerExplainTool(pi, getCoordinator),
44
+ );
45
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_affected", () =>
46
+ registerAffectedTool(pi, getCoordinator),
47
+ );
48
+ registerToolIfSupported(coordinator, registeredToolNames, "graphify_version", () =>
49
+ registerVersionTool(pi, getCoordinator),
50
+ );
51
+ }
52
+
53
+ function registerToolIfSupported(
54
+ coordinator: GraphifyCoordinator,
55
+ registeredToolNames: Set<string>,
56
+ name: string,
57
+ register: () => void,
58
+ ): void {
59
+ if (registeredToolNames.has(name)) {
60
+ return;
61
+ }
62
+
63
+ const feature = nameToFeature(name);
64
+ if (feature && !coordinator.supports(feature)) {
65
+ return;
66
+ }
67
+
68
+ registeredToolNames.add(name);
69
+ register();
70
+ }
8
71
 
9
- export function registerGraphifyTools(pi: ExtensionAPI, config: GraphifyConfig): void {
10
- registerStatusTool(pi, config);
72
+ function nameToFeature(name: string): keyof GraphifyCapabilities | null {
73
+ switch (name) {
74
+ case "graphify_build":
75
+ return "build";
76
+ case "graphify_query":
77
+ return "query";
78
+ case "graphify_path":
79
+ return "path";
80
+ case "graphify_explain":
81
+ return "explain";
82
+ case "graphify_affected":
83
+ return "affected";
84
+ case "graphify_version":
85
+ return "version";
86
+ default:
87
+ return null;
88
+ }
11
89
  }
@@ -0,0 +1,43 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerPathTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_path",
10
+ label: "Graphify Path",
11
+ description:
12
+ "Trace the shortest path between two nodes (functions, files, tools, or concepts) in the Graphify knowledge graph.",
13
+ promptSnippet:
14
+ "Trace how two functions, files, tools, or concepts are connected in the knowledge graph",
15
+ promptGuidelines: [
16
+ "Use graphify_path when the user asks how two concepts, functions, files, tools, or symbols are related.",
17
+ "Use graphify_path instead of manually tracing code when both source and target are known nodes in the graph.",
18
+ ],
19
+ parameters: Type.Object({
20
+ cwd: Type.String({ description: "Working directory of the project to search." }),
21
+ source: Type.String({
22
+ minLength: 1,
23
+ description: "The source node or concept to start from.",
24
+ }),
25
+ target: Type.String({
26
+ minLength: 1,
27
+ description: "The target node or concept to find a path to.",
28
+ }),
29
+ }),
30
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
31
+ const coordinator = getCoordinator();
32
+ if (!coordinator) {
33
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
34
+ }
35
+ try {
36
+ const result = await coordinator.path({ ...params, cwd: ctx.cwd });
37
+ return formatGraphifyResult(result);
38
+ } catch (error) {
39
+ return formatGraphifyError(error);
40
+ }
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,37 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerQueryTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_query",
10
+ label: "Graphify Query",
11
+ description: "Ask a natural-language question against the Graphify knowledge graph.",
12
+ promptSnippet: "Ask a natural-language question about the project's knowledge graph",
13
+ promptGuidelines: [
14
+ "Use graphify_query when the user asks a broad question about the codebase.",
15
+ "Use graphify_query instead of grepping or reading files when a graph is available.",
16
+ ],
17
+ parameters: Type.Object({
18
+ cwd: Type.String({ description: "Working directory of the project to query." }),
19
+ question: Type.String({
20
+ minLength: 1,
21
+ description: "The natural-language question to run against the graph.",
22
+ }),
23
+ }),
24
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
25
+ const coordinator = getCoordinator();
26
+ if (!coordinator) {
27
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
28
+ }
29
+ try {
30
+ const result = await coordinator.query({ ...params, cwd: ctx.cwd });
31
+ return formatGraphifyResult(result);
32
+ } catch (error) {
33
+ return formatGraphifyError(error);
34
+ }
35
+ },
36
+ });
37
+ }
@@ -4,39 +4,67 @@
4
4
 
5
5
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { Type } from "typebox";
7
- import type { GraphifyConfig } from "../config.js";
8
- import { graphifyStatus } from "../graphify.js";
7
+ import type { CoordinatorProvider } from "./index.js";
9
8
 
10
- export function registerStatusTool(pi: ExtensionAPI, _config: GraphifyConfig): void {
9
+ export function registerStatusTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
11
10
  pi.registerTool({
12
11
  name: "graphify_status",
13
12
  label: "Graphify Status",
14
- description:
15
- "Check whether a Graphify knowledge graph exists for the current project and whether the graphify CLI is available.",
13
+ description: "Check whether a Graphify knowledge graph exists for the current project.",
14
+ promptSnippet: "Check whether a Graphify knowledge graph exists for the current project",
15
+ promptGuidelines: [
16
+ "Use graphify_status when the user asks if a Graphify graph is available.",
17
+ "Use graphify_status before calling other graphify tools to confirm the graph exists.",
18
+ ],
16
19
  parameters: Type.Object({}),
17
20
  async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
18
- const status = await graphifyStatus(ctx.cwd);
21
+ const coordinator = getCoordinator();
22
+ if (!coordinator) {
23
+ return {
24
+ content: [
25
+ {
26
+ type: "text",
27
+ text: "Graphify is not initialized yet.",
28
+ },
29
+ ],
30
+ details: {},
31
+ };
32
+ }
33
+
34
+ const status = await coordinator.status({ cwd: ctx.cwd });
35
+ const details: Record<string, unknown> = {
36
+ hasGraph: status.hasGraph,
37
+ backendVersion: status.backendVersion,
38
+ };
19
39
 
20
40
  if (!status.hasGraph) {
41
+ const cliNote = status.backendVersion ? "" : " The Graphify CLI was not detected on PATH.";
21
42
  return {
22
43
  content: [
23
44
  {
24
45
  type: "text",
25
- text: "No Graphify graph found. Run /graphify-build to build one.",
46
+ text: `No Graphify graph found. Build one with \`graphify .\`.${cliNote}`,
26
47
  },
27
48
  ],
28
- details: {},
49
+ details,
29
50
  };
30
51
  }
31
52
 
53
+ const graphText = status.backendVersion
54
+ ? `Graphify graph is available at ${status.graphPath}.`
55
+ : `Graphify graph is available at ${status.graphPath}, but the Graphify CLI was not detected on PATH.`;
56
+
32
57
  return {
33
58
  content: [
34
59
  {
35
60
  type: "text",
36
- text: `Graphify graph is available at ${status.graphPath}.`,
61
+ text: graphText,
37
62
  },
38
63
  ],
39
- details: {},
64
+ details: {
65
+ ...details,
66
+ graphPath: status.graphPath,
67
+ },
40
68
  };
41
69
  },
42
70
  });
@@ -0,0 +1,282 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4
+ import { allCapabilitiesEnabled, createMockBackend } from "../backends/mock.js";
5
+ import { GraphifyCoordinator } from "../coordinator.js";
6
+ import { GraphifyError } from "../errors.js";
7
+ import { type RegisterToolsOptions, registerGraphifyTools } from "./index.js";
8
+
9
+ interface ToolRecord {
10
+ name: string;
11
+ label: string;
12
+ description: string;
13
+ parameters: unknown;
14
+ execute: (
15
+ toolCallId: string,
16
+ params: unknown,
17
+ signal: unknown,
18
+ onUpdate: (content: unknown) => void,
19
+ ctx: { cwd: string },
20
+ ) => Promise<{ content: { type: string; text: string }[]; details: Record<string, unknown> }>;
21
+ }
22
+
23
+ interface MockPi {
24
+ tools: Record<string, ToolRecord>;
25
+ registerTool: (tool: ToolRecord) => void;
26
+ }
27
+
28
+ function createMockPi(): MockPi {
29
+ const tools: Record<string, ToolRecord> = {};
30
+ return {
31
+ tools,
32
+ registerTool: (tool: ToolRecord) => {
33
+ tools[tool.name] = tool;
34
+ },
35
+ };
36
+ }
37
+
38
+ function createTestCoordinator(backend = createMockBackend()): GraphifyCoordinator {
39
+ return new GraphifyCoordinator({ cwd: process.cwd(), backend });
40
+ }
41
+
42
+ function createRegisterOptions(): RegisterToolsOptions {
43
+ return { registeredToolNames: new Set<string>() };
44
+ }
45
+
46
+ const noopOnUpdate = (): void => {};
47
+
48
+ const expectedTools = [
49
+ "graphify_status",
50
+ "graphify_build",
51
+ "graphify_query",
52
+ "graphify_path",
53
+ "graphify_explain",
54
+ "graphify_affected",
55
+ "graphify_version",
56
+ ];
57
+
58
+ describe("registerGraphifyTools", () => {
59
+ it("registers all expected tools when all capabilities are enabled", () => {
60
+ const pi = createMockPi();
61
+ const coordinator = createTestCoordinator();
62
+ const options = createRegisterOptions();
63
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => coordinator, options);
64
+
65
+ for (const name of expectedTools) {
66
+ assert.ok(name in pi.tools, `expected tool ${name} to be registered`);
67
+ }
68
+ });
69
+
70
+ it("does not register tools when coordinator is null", () => {
71
+ const pi = createMockPi();
72
+ const options = createRegisterOptions();
73
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => null, options);
74
+
75
+ assert.equal(Object.keys(pi.tools).length, 0);
76
+ });
77
+
78
+ it("skips tools whose capabilities are disabled", () => {
79
+ const pi = createMockPi();
80
+ const capabilities = { ...allCapabilitiesEnabled(), build: false, query: false };
81
+ const backend = createMockBackend({ capabilities });
82
+ const coordinator = createTestCoordinator(backend);
83
+ const options = createRegisterOptions();
84
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => coordinator, options);
85
+
86
+ assert.ok(
87
+ !("graphify_build" in pi.tools),
88
+ "graphify_build should not be registered when build is unsupported",
89
+ );
90
+ assert.ok(
91
+ !("graphify_query" in pi.tools),
92
+ "graphify_query should not be registered when query is unsupported",
93
+ );
94
+ assert.ok(
95
+ "graphify_path" in pi.tools,
96
+ "graphify_path should be registered when path is supported",
97
+ );
98
+ });
99
+
100
+ it("prevents duplicate registration when called again with the same set", () => {
101
+ const pi = createMockPi();
102
+ const coordinator = createTestCoordinator();
103
+ const options = createRegisterOptions();
104
+
105
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => coordinator, options);
106
+ const firstCount = Object.keys(pi.tools).length;
107
+
108
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => coordinator, options);
109
+ const secondCount = Object.keys(pi.tools).length;
110
+
111
+ assert.equal(firstCount, expectedTools.length);
112
+ assert.equal(secondCount, expectedTools.length);
113
+ });
114
+ });
115
+
116
+ async function runTool(
117
+ pi: MockPi,
118
+ name: string,
119
+ params: Record<string, unknown>,
120
+ cwd: string,
121
+ ): Promise<{ content: { type: string; text: string }[]; details: Record<string, unknown> }> {
122
+ return pi.tools[name].execute("call-1", params, null, noopOnUpdate, { cwd });
123
+ }
124
+
125
+ describe("graphify_build", () => {
126
+ it("returns formatted result on success", async () => {
127
+ const pi = createMockPi();
128
+ const coordinator = createTestCoordinator();
129
+ registerGraphifyTools(
130
+ pi as unknown as ExtensionAPI,
131
+ () => coordinator,
132
+ createRegisterOptions(),
133
+ );
134
+ const result = await runTool(pi, "graphify_build", {}, process.cwd());
135
+
136
+ assert.equal(result.content[0].text, "ok: build");
137
+ assert.equal(result.details.exitCode, 0);
138
+ assert.equal(result.details.feature, "build");
139
+ });
140
+ });
141
+
142
+ describe("graphify_query", () => {
143
+ it("passes question and cwd to the coordinator", async () => {
144
+ const pi = createMockPi();
145
+ let captured: unknown = null;
146
+ const backend = createMockBackend({
147
+ run: async (_operation, options) => {
148
+ captured = options;
149
+ return {
150
+ stdout: "answer",
151
+ stderr: "",
152
+ exitCode: 0,
153
+ durationMs: 1,
154
+ backend: "cli",
155
+ feature: "query",
156
+ };
157
+ },
158
+ });
159
+ const coordinator = createTestCoordinator(backend);
160
+ registerGraphifyTools(
161
+ pi as unknown as ExtensionAPI,
162
+ () => coordinator,
163
+ createRegisterOptions(),
164
+ );
165
+ const result = await runTool(
166
+ pi,
167
+ "graphify_query",
168
+ { question: "how does auth work?" },
169
+ process.cwd(),
170
+ );
171
+
172
+ assert.equal(result.content[0].text, "answer");
173
+ assert.deepEqual(captured, { cwd: process.cwd(), question: "how does auth work?" });
174
+ });
175
+
176
+ it("is not registered when the coordinator is missing", () => {
177
+ const pi = createMockPi();
178
+ registerGraphifyTools(pi as unknown as ExtensionAPI, () => null, createRegisterOptions());
179
+ assert.equal("graphify_query" in pi.tools, false);
180
+ });
181
+
182
+ it("returns an error when the coordinator becomes null after registration", async () => {
183
+ const pi = createMockPi();
184
+ let coordinator: GraphifyCoordinator | null = createTestCoordinator();
185
+ registerGraphifyTools(
186
+ pi as unknown as ExtensionAPI,
187
+ () => coordinator,
188
+ createRegisterOptions(),
189
+ );
190
+
191
+ coordinator = null;
192
+ const result = await runTool(pi, "graphify_query", { question: "x" }, process.cwd());
193
+
194
+ assert.ok(result.content[0].text.includes("Graphify is not initialized"));
195
+ });
196
+ });
197
+
198
+ describe("graphify_path", () => {
199
+ it("returns formatted result on success", async () => {
200
+ const pi = createMockPi();
201
+ const coordinator = createTestCoordinator();
202
+ registerGraphifyTools(
203
+ pi as unknown as ExtensionAPI,
204
+ () => coordinator,
205
+ createRegisterOptions(),
206
+ );
207
+ const result = await runTool(pi, "graphify_path", { source: "a", target: "b" }, process.cwd());
208
+
209
+ assert.equal(result.content[0].text, "ok: path");
210
+ });
211
+ });
212
+
213
+ describe("graphify_explain", () => {
214
+ it("returns formatted result on success", async () => {
215
+ const pi = createMockPi();
216
+ const coordinator = createTestCoordinator();
217
+ registerGraphifyTools(
218
+ pi as unknown as ExtensionAPI,
219
+ () => coordinator,
220
+ createRegisterOptions(),
221
+ );
222
+ const result = await runTool(pi, "graphify_explain", { node: "auth" }, process.cwd());
223
+
224
+ assert.equal(result.content[0].text, "ok: explain");
225
+ });
226
+ });
227
+
228
+ describe("graphify_affected", () => {
229
+ it("returns formatted result on success", async () => {
230
+ const pi = createMockPi();
231
+ const coordinator = createTestCoordinator();
232
+ registerGraphifyTools(
233
+ pi as unknown as ExtensionAPI,
234
+ () => coordinator,
235
+ createRegisterOptions(),
236
+ );
237
+ const result = await runTool(
238
+ pi,
239
+ "graphify_affected",
240
+ { files: ["src/auth.ts"] },
241
+ process.cwd(),
242
+ );
243
+
244
+ assert.equal(result.content[0].text, "ok: affected");
245
+ });
246
+ });
247
+
248
+ describe("graphify_version", () => {
249
+ it("returns formatted version on success", async () => {
250
+ const pi = createMockPi();
251
+ const backend = createMockBackend({ version: "2.100.0" });
252
+ const coordinator = createTestCoordinator(backend);
253
+ registerGraphifyTools(
254
+ pi as unknown as ExtensionAPI,
255
+ () => coordinator,
256
+ createRegisterOptions(),
257
+ );
258
+ const result = await runTool(pi, "graphify_version", {}, process.cwd());
259
+
260
+ assert.equal(result.content[0].text, "2.100.0");
261
+ });
262
+ });
263
+
264
+ describe("tool error handling", () => {
265
+ it("returns friendly error when the backend throws GraphifyError", async () => {
266
+ const pi = createMockPi();
267
+ const backend = createMockBackend({
268
+ run: async () => {
269
+ throw new GraphifyError("QUERY", "graphify query failed");
270
+ },
271
+ });
272
+ const coordinator = createTestCoordinator(backend);
273
+ registerGraphifyTools(
274
+ pi as unknown as ExtensionAPI,
275
+ () => coordinator,
276
+ createRegisterOptions(),
277
+ );
278
+ const result = await runTool(pi, "graphify_query", { question: "x" }, process.cwd());
279
+
280
+ assert.ok(result.content[0].text.includes("graphify query failed"));
281
+ });
282
+ });
@@ -0,0 +1,31 @@
1
+ import type { GraphifyResult } from "../backends/types.js";
2
+
3
+ export interface ToolContext {
4
+ cwd: string;
5
+ }
6
+
7
+ export interface TextContentItem {
8
+ type: "text";
9
+ text: string;
10
+ }
11
+
12
+ export interface ToolResult {
13
+ content: TextContentItem[];
14
+ details: Record<string, unknown>;
15
+ }
16
+
17
+ export function successResult(result: GraphifyResult): ToolResult {
18
+ return {
19
+ content: [{ type: "text", text: result.stdout || "Operation completed successfully." }],
20
+ details: {
21
+ exitCode: result.exitCode,
22
+ durationMs: result.durationMs,
23
+ backend: result.backend,
24
+ feature: result.feature,
25
+ },
26
+ };
27
+ }
28
+
29
+ export function errorResult(text: string, details: Record<string, unknown> = {}): ToolResult {
30
+ return { content: [{ type: "text", text: `Error: ${text}` }], details };
31
+ }
@@ -0,0 +1,35 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerVersionTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_version",
10
+ label: "Graphify Version",
11
+ description: "Report the installed Graphify version and compatibility status.",
12
+ promptSnippet: "Report the installed Graphify CLI version and compatibility status",
13
+ promptGuidelines: [
14
+ "Use graphify_version when the user asks what version of Graphify is installed.",
15
+ "Use graphify_version to check whether the installed Graphify version is supported.",
16
+ ],
17
+ parameters: Type.Object({
18
+ cwd: Type.Optional(
19
+ Type.String({ description: "Optional working directory to check Graphify in." }),
20
+ ),
21
+ }),
22
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
23
+ const coordinator = getCoordinator();
24
+ if (!coordinator) {
25
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
26
+ }
27
+ try {
28
+ const result = await coordinator.getVersion({ cwd: ctx.cwd });
29
+ return formatGraphifyResult(result);
30
+ } catch (error) {
31
+ return formatGraphifyError(error);
32
+ }
33
+ },
34
+ });
35
+ }