@nocobase/plugin-ai 2.1.0-alpha.29 → 2.1.0-alpha.30

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 (72) hide show
  1. package/dist/ai/docs/nocobase/ai-builder/security.md +4 -4
  2. package/dist/client/{559.c119db3f985a695f.js → 559.39872901b9053629.js} +1 -1
  3. package/dist/client/{646.85a5f27994307223.js → 646.217a40387efbd163.js} +1 -1
  4. package/dist/client/components/skill-settings.d.ts +2 -0
  5. package/dist/client/index.js +3 -3
  6. package/dist/client/llm-providers/xai/ModelSettings.d.ts +10 -0
  7. package/dist/client/llm-providers/xai/index.d.ts +10 -0
  8. package/dist/externalVersion.js +15 -15
  9. package/dist/node_modules/@langchain/xai/LICENSE +21 -0
  10. package/dist/node_modules/@langchain/xai/dist/_virtual/rolldown_runtime.cjs +25 -0
  11. package/dist/node_modules/@langchain/xai/dist/chat_models/completions.cjs +568 -0
  12. package/dist/node_modules/@langchain/xai/dist/chat_models/completions.d.cts +619 -0
  13. package/dist/node_modules/@langchain/xai/dist/chat_models/completions.d.ts +619 -0
  14. package/dist/node_modules/@langchain/xai/dist/chat_models/completions.js +566 -0
  15. package/dist/node_modules/@langchain/xai/dist/chat_models/index.cjs +2 -0
  16. package/dist/node_modules/@langchain/xai/dist/chat_models/index.d.ts +3 -0
  17. package/dist/node_modules/@langchain/xai/dist/chat_models/index.js +2 -0
  18. package/dist/node_modules/@langchain/xai/dist/chat_models/responses-types.d.cts +1178 -0
  19. package/dist/node_modules/@langchain/xai/dist/chat_models/responses-types.d.ts +1178 -0
  20. package/dist/node_modules/@langchain/xai/dist/chat_models/responses.cjs +233 -0
  21. package/dist/node_modules/@langchain/xai/dist/chat_models/responses.d.cts +70 -0
  22. package/dist/node_modules/@langchain/xai/dist/chat_models/responses.d.ts +70 -0
  23. package/dist/node_modules/@langchain/xai/dist/chat_models/responses.js +232 -0
  24. package/dist/node_modules/@langchain/xai/dist/converters/responses.cjs +168 -0
  25. package/dist/node_modules/@langchain/xai/dist/converters/responses.js +164 -0
  26. package/dist/node_modules/@langchain/xai/dist/index.cjs +7 -0
  27. package/dist/node_modules/@langchain/xai/dist/index.d.cts +5 -0
  28. package/dist/node_modules/@langchain/xai/dist/index.d.ts +6 -0
  29. package/dist/node_modules/@langchain/xai/dist/index.js +6 -0
  30. package/dist/node_modules/@langchain/xai/dist/live_search.cjs +54 -0
  31. package/dist/node_modules/@langchain/xai/dist/live_search.d.cts +145 -0
  32. package/dist/node_modules/@langchain/xai/dist/live_search.d.ts +145 -0
  33. package/dist/node_modules/@langchain/xai/dist/live_search.js +51 -0
  34. package/dist/node_modules/@langchain/xai/dist/profiles.cjs +289 -0
  35. package/dist/node_modules/@langchain/xai/dist/profiles.js +288 -0
  36. package/dist/node_modules/@langchain/xai/dist/tools/code_execution.cjs +52 -0
  37. package/dist/node_modules/@langchain/xai/dist/tools/code_execution.d.cts +64 -0
  38. package/dist/node_modules/@langchain/xai/dist/tools/code_execution.d.ts +64 -0
  39. package/dist/node_modules/@langchain/xai/dist/tools/code_execution.js +50 -0
  40. package/dist/node_modules/@langchain/xai/dist/tools/collections_search.cjs +60 -0
  41. package/dist/node_modules/@langchain/xai/dist/tools/collections_search.d.cts +90 -0
  42. package/dist/node_modules/@langchain/xai/dist/tools/collections_search.d.ts +90 -0
  43. package/dist/node_modules/@langchain/xai/dist/tools/collections_search.js +58 -0
  44. package/dist/node_modules/@langchain/xai/dist/tools/index.cjs +18 -0
  45. package/dist/node_modules/@langchain/xai/dist/tools/index.d.cts +18 -0
  46. package/dist/node_modules/@langchain/xai/dist/tools/index.d.ts +18 -0
  47. package/dist/node_modules/@langchain/xai/dist/tools/index.js +18 -0
  48. package/dist/node_modules/@langchain/xai/dist/tools/live_search.cjs +94 -0
  49. package/dist/node_modules/@langchain/xai/dist/tools/live_search.d.cts +149 -0
  50. package/dist/node_modules/@langchain/xai/dist/tools/live_search.d.ts +149 -0
  51. package/dist/node_modules/@langchain/xai/dist/tools/live_search.js +91 -0
  52. package/dist/node_modules/@langchain/xai/dist/tools/web_search.cjs +57 -0
  53. package/dist/node_modules/@langchain/xai/dist/tools/web_search.d.cts +104 -0
  54. package/dist/node_modules/@langchain/xai/dist/tools/web_search.d.ts +104 -0
  55. package/dist/node_modules/@langchain/xai/dist/tools/web_search.js +55 -0
  56. package/dist/node_modules/@langchain/xai/dist/tools/x_search.cjs +63 -0
  57. package/dist/node_modules/@langchain/xai/dist/tools/x_search.d.cts +145 -0
  58. package/dist/node_modules/@langchain/xai/dist/tools/x_search.d.ts +145 -0
  59. package/dist/node_modules/@langchain/xai/dist/tools/x_search.js +61 -0
  60. package/dist/node_modules/@langchain/xai/package.json +1 -0
  61. package/dist/node_modules/fs-extra/package.json +1 -1
  62. package/dist/node_modules/jsonrepair/package.json +1 -1
  63. package/dist/node_modules/just-bash/package.json +1 -1
  64. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  65. package/dist/node_modules/openai/package.json +1 -1
  66. package/dist/node_modules/zod/package.json +1 -1
  67. package/dist/server/llm-providers/common/reasoning.js +2 -4
  68. package/dist/server/llm-providers/xai.d.ts +17 -0
  69. package/dist/server/llm-providers/xai.js +88 -0
  70. package/dist/server/plugin.js +3 -0
  71. package/dist/server/workflow/nodes/employee/index.js +133 -130
  72. package/package.json +3 -2
@@ -0,0 +1,17 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { ChatXAI } from '@langchain/xai';
10
+ import { LLMProvider } from './provider';
11
+ import { LLMProviderMeta } from '../manager/ai-manager';
12
+ export declare class XAIProvider extends LLMProvider {
13
+ chatModel: ChatXAI;
14
+ get baseURL(): string;
15
+ createModel(): ChatXAI;
16
+ }
17
+ export declare const xaiProviderOptions: LLMProviderMeta;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var xai_exports = {};
28
+ __export(xai_exports, {
29
+ XAIProvider: () => XAIProvider,
30
+ xaiProviderOptions: () => xaiProviderOptions
31
+ });
32
+ module.exports = __toCommonJS(xai_exports);
33
+ var import_xai = require("@langchain/xai");
34
+ var import_provider = require("./provider");
35
+ var import_ai_manager = require("../manager/ai-manager");
36
+ class XAIProvider extends import_provider.LLMProvider {
37
+ get baseURL() {
38
+ return "https://api.x.ai/v1";
39
+ }
40
+ createModel() {
41
+ const { baseURL, apiKey } = this.serviceOptions || {};
42
+ const { responseFormat, structuredOutput, ...restModelOptions } = this.modelOptions || {};
43
+ const { schema } = structuredOutput || {};
44
+ const responseFormatOptions = {
45
+ type: responseFormat ?? "text"
46
+ };
47
+ if (responseFormat === "json_schema" && schema) {
48
+ responseFormatOptions["json_schema"] = schema;
49
+ }
50
+ return new import_xai.ChatXAI({
51
+ apiKey,
52
+ ...restModelOptions,
53
+ baseURL: baseURL || this.baseURL,
54
+ modelKwargs: {
55
+ response_format: responseFormatOptions
56
+ }
57
+ });
58
+ }
59
+ }
60
+ const xaiProviderOptions = {
61
+ title: "xAI",
62
+ supportedModel: [import_ai_manager.SupportedModel.LLM],
63
+ models: {
64
+ [import_ai_manager.SupportedModel.LLM]: [
65
+ "grok-4",
66
+ "grok-4-1",
67
+ "grok-4-1-fast",
68
+ "grok-4-1-fast-non-reasoning",
69
+ "grok-3",
70
+ "grok-3-fast",
71
+ "grok-3-fast-beta",
72
+ "grok-3-beta",
73
+ "grok-3-mini",
74
+ "grok-3-mini-fast",
75
+ "grok-3-mini-beta",
76
+ "grok-3-mini-fast-beta",
77
+ "grok-2",
78
+ "grok-2-vision",
79
+ "grok-vision-beta"
80
+ ]
81
+ },
82
+ provider: XAIProvider
83
+ };
84
+ // Annotate the CommonJS export names for ESM import in node:
85
+ 0 && (module.exports = {
86
+ XAIProvider,
87
+ xaiProviderOptions
88
+ });
@@ -70,6 +70,7 @@ var import_aiMcpClients = __toESM(require("./resource/aiMcpClients"));
70
70
  var import_work_context_handler = require("./manager/work-context-handler");
71
71
  var import_ai_coding_manager = require("./manager/ai-coding-manager");
72
72
  var import_kimi = require("./llm-providers/kimi");
73
+ var import_xai = require("./llm-providers/xai");
73
74
  var import_document_loader = require("./document-loader");
74
75
  var import_checkpoints = require("./ai-employees/checkpoints");
75
76
  var import_mimo = require("./llm-providers/mimo");
@@ -146,6 +147,8 @@ class PluginAIServer extends import_server.Plugin {
146
147
  this.aiManager.registerLLMProvider("mimo", import_mimo.mimoProviderOptions);
147
148
  this.aiManager.registerLLMProvider("ollama", import_ollama.ollamaProviderOptions);
148
149
  this.aiManager.registerLLMProvider("openai-completions", import_openai2.openaiCompletionsProviderOptions);
150
+ this.aiManager.registerLLMProvider("kimi", import_kimi.kimiProviderOptions);
151
+ this.aiManager.registerLLMProvider("xai", import_xai.xaiProviderOptions);
149
152
  }
150
153
  registerTools() {
151
154
  const toolsManager = this.ai.toolsManager;
@@ -49,7 +49,6 @@ __reExport(employee_exports, require("./handler"), module.exports);
49
49
  __reExport(employee_exports, require("./tools"), module.exports);
50
50
  class AIEmployeeInstruction extends import_plugin_workflow.Instruction {
51
51
  async run(node, input, processor) {
52
- var _a, _b, _c, _d, _e;
53
52
  const {
54
53
  username,
55
54
  message,
@@ -81,151 +80,155 @@ ${typeof message.system === "object" ? JSON.stringify(message.system) : message.
81
80
  upstreamId: (input == null ? void 0 : input.id) ?? null
82
81
  });
83
82
  await processor.exit();
84
- try {
85
- if (skillSettings && skillSettings.skillsVersion == null) {
86
- skillSettings.skillsVersion = 2;
87
- }
88
- if (skillSettings && skillSettings.toolsVersion == null) {
89
- skillSettings.toolsVersion = 2;
90
- }
91
- const { conversation, aiWorkflowTasks } = await this.createWorkflowTask({
92
- username,
93
- userMessage,
94
- systemMessage,
95
- skillSettings,
96
- requiresApproval,
97
- toolName,
98
- node,
99
- processor,
100
- jobId: id
101
- });
102
- let currentRoles = (_a = input == null ? void 0 : input.result) == null ? void 0 : _a.roleName;
103
- if (!currentRoles) {
104
- const defaultRole = await this.workflow.db.getRepository("rolesUsers").findOne({
83
+ const runner = async () => {
84
+ var _a, _b, _c, _d, _e;
85
+ try {
86
+ if (skillSettings && skillSettings.skillsVersion == null) {
87
+ skillSettings.skillsVersion = 2;
88
+ }
89
+ if (skillSettings && skillSettings.toolsVersion == null) {
90
+ skillSettings.toolsVersion = 2;
91
+ }
92
+ const { conversation, aiWorkflowTasks } = await this.createWorkflowTask({
93
+ username,
94
+ userMessage,
95
+ systemMessage,
96
+ skillSettings,
97
+ requiresApproval,
98
+ toolName,
99
+ node,
100
+ processor,
101
+ jobId: id
102
+ });
103
+ let currentRoles = (_a = input == null ? void 0 : input.result) == null ? void 0 : _a.roleName;
104
+ if (!currentRoles) {
105
+ const defaultRole = await this.workflow.db.getRepository("rolesUsers").findOne({
106
+ filter: {
107
+ userId: ((_c = (_b = input == null ? void 0 : input.result) == null ? void 0 : _b.user) == null ? void 0 : _c.id) ?? userId,
108
+ default: true
109
+ }
110
+ });
111
+ currentRoles = defaultRole == null ? void 0 : defaultRole.roleName;
112
+ }
113
+ const employee = await this.workflow.db.getRepository("aiEmployees").findOne({
105
114
  filter: {
106
- userId: ((_c = (_b = input == null ? void 0 : input.result) == null ? void 0 : _b.user) == null ? void 0 : _c.id) ?? userId,
107
- default: true
115
+ username
108
116
  }
109
117
  });
110
- currentRoles = defaultRole == null ? void 0 : defaultRole.roleName;
111
- }
112
- const employee = await this.workflow.db.getRepository("aiEmployees").findOne({
113
- filter: {
114
- username
115
- }
116
- });
117
- const aiEmployee = new import_ai_employee.AIEmployee({
118
- ctx: {
119
- app: this.workflow.app,
120
- db: this.workflow.app.db,
121
- log: this.workflow.app.log,
122
- logger: this.workflow.app.log,
123
- state: { currentRoles },
124
- auth: {
125
- user: {
126
- id: ((_e = (_d = input == null ? void 0 : input.result) == null ? void 0 : _d.user) == null ? void 0 : _e.id) ?? userId
127
- }
128
- },
129
- action: {
130
- params: {
131
- values: {
132
- sessionId: conversation.sessionId,
133
- model
118
+ const aiEmployee = new import_ai_employee.AIEmployee({
119
+ ctx: {
120
+ app: this.workflow.app,
121
+ db: this.workflow.app.db,
122
+ log: this.workflow.app.log,
123
+ logger: this.workflow.app.log,
124
+ state: { currentRoles },
125
+ auth: {
126
+ user: {
127
+ id: ((_e = (_d = input == null ? void 0 : input.result) == null ? void 0 : _d.user) == null ? void 0 : _e.id) ?? userId
134
128
  }
135
- }
136
- }
137
- },
138
- employee,
139
- sessionId: conversation.sessionId,
140
- systemMessage,
141
- skillSettings,
142
- webSearch,
143
- model,
144
- tools: [{ name: toolName }]
145
- });
146
- const attachmentPart = {};
147
- if (files == null ? void 0 : files.length) {
148
- const { resolveAttachments, resolveUrls } = import_files.Files.resolvers(this.workflow, attachmentPart);
149
- await resolveAttachments(files);
150
- await resolveUrls(files);
151
- }
152
- let result;
153
- let isToolInvoke = false;
154
- let retry = 0;
155
- do {
156
- const userMessages = [
157
- {
158
- role: "user",
159
- content: {
160
- type: "text",
161
- content: userMessage
162
129
  },
163
- ...attachmentPart
164
- }
165
- ];
166
- if (retry > 0) {
167
- if (retry < 2) {
168
- const firstUserMessage = await this.workflow.db.getRepository("aiConversations.messages", conversation.sessionId).findOne({
169
- filter: {
170
- role: "user"
130
+ action: {
131
+ params: {
132
+ values: {
133
+ sessionId: conversation.sessionId,
134
+ model
135
+ }
136
+ }
137
+ }
138
+ },
139
+ employee,
140
+ sessionId: conversation.sessionId,
141
+ systemMessage,
142
+ skillSettings,
143
+ webSearch,
144
+ model,
145
+ tools: [{ name: toolName }]
146
+ });
147
+ const attachmentPart = {};
148
+ if (files == null ? void 0 : files.length) {
149
+ const { resolveAttachments, resolveUrls } = import_files.Files.resolvers(this.workflow, attachmentPart);
150
+ await resolveAttachments(files);
151
+ await resolveUrls(files);
152
+ }
153
+ let result;
154
+ let isToolInvoke = false;
155
+ let retry = 0;
156
+ do {
157
+ const userMessages = [
158
+ {
159
+ role: "user",
160
+ content: {
161
+ type: "text",
162
+ content: userMessage
171
163
  },
172
- sort: ["messageId"]
173
- });
174
- const messageId = firstUserMessage == null ? void 0 : firstUserMessage.messageId;
175
- result = await aiEmployee.invoke({ messageId, userMessages });
176
- } else {
177
- result = await aiEmployee.invoke({
178
- userMessages: [
179
- {
180
- role: "user",
181
- content: {
182
- type: "text",
183
- content: `You failed to call the required tool "aiEmployeeWorkflowTaskOutput" in your previous response.
164
+ ...attachmentPart
165
+ }
166
+ ];
167
+ if (retry > 0) {
168
+ if (retry < 2) {
169
+ const firstUserMessage = await this.workflow.db.getRepository("aiConversations.messages", conversation.sessionId).findOne({
170
+ filter: {
171
+ role: "user"
172
+ },
173
+ sort: ["messageId"]
174
+ });
175
+ const messageId = firstUserMessage == null ? void 0 : firstUserMessage.messageId;
176
+ result = await aiEmployee.invoke({ messageId, userMessages });
177
+ } else {
178
+ result = await aiEmployee.invoke({
179
+ userMessages: [
180
+ {
181
+ role: "user",
182
+ content: {
183
+ type: "text",
184
+ content: `You failed to call the required tool "aiEmployeeWorkflowTaskOutput" in your previous response.
184
185
  Call "aiEmployeeWorkflowTaskOutput" now to submit the workflow outcome.
185
186
  Do not send another normal assistant response without invoking it.
186
187
  `
188
+ }
187
189
  }
188
- }
189
- ]
190
- });
190
+ ]
191
+ });
192
+ }
193
+ } else {
194
+ result = await aiEmployee.invoke({ userMessages });
191
195
  }
192
- } else {
193
- result = await aiEmployee.invoke({ userMessages });
196
+ isToolInvoke = result.messages.filter((it) => it.type === "ai").flatMap((it) => it.tool_calls).some((it) => it.name === toolName);
197
+ } while (!isToolInvoke && retry++ < 2);
198
+ if (!isToolInvoke) {
199
+ throw new Error("AI employee not do job correctly");
194
200
  }
195
- isToolInvoke = result.messages.filter((it) => it.type === "ai").flatMap((it) => it.tool_calls).some((it) => it.name === toolName);
196
- } while (!isToolInvoke && retry++ < 2);
197
- if (!isToolInvoke) {
198
- throw new Error("AI employee not do job correctly");
199
- }
200
- await this.checkApproval({ requiresApproval, conversation, aiWorkflowTasks, result, aiEmployee, toolName });
201
- } catch (e) {
202
- processor.logger.error(`ai employee invoke failed, ${e.message}`, {
203
- node: node.id,
204
- stack: e.stack,
205
- chatOptions: node.config
206
- });
207
- const job = await this.workflow.app.db.getRepository("jobs").findOne({
208
- filterByTk: id
209
- });
210
- job.set({
211
- status: import_plugin_workflow.JOB_STATUS.ERROR,
212
- result: e.message
213
- });
214
- const aiWorkflowTask = await this.workflow.db.getRepository("aiWorkflowTasks").findOne({
215
- filter: {
216
- jobId: id
217
- }
218
- });
219
- if (aiWorkflowTask) {
220
- await this.workflow.db.getRepository("aiWorkflowTasks").update({
221
- values: { status: "aborted" },
201
+ await this.checkApproval({ requiresApproval, conversation, aiWorkflowTasks, result, aiEmployee, toolName });
202
+ } catch (e) {
203
+ processor.logger.error(`ai employee invoke failed, ${e.message}`, {
204
+ node: node.id,
205
+ stack: e.stack,
206
+ chatOptions: node.config
207
+ });
208
+ const job = await this.workflow.app.db.getRepository("jobs").findOne({
209
+ filterByTk: id
210
+ });
211
+ job.set({
212
+ status: import_plugin_workflow.JOB_STATUS.ERROR,
213
+ result: e.message
214
+ });
215
+ const aiWorkflowTask = await this.workflow.db.getRepository("aiWorkflowTasks").findOne({
222
216
  filter: {
223
- id: aiWorkflowTask.id
217
+ jobId: id
224
218
  }
225
219
  });
220
+ if (aiWorkflowTask) {
221
+ await this.workflow.db.getRepository("aiWorkflowTasks").update({
222
+ values: { status: "aborted" },
223
+ filter: {
224
+ id: aiWorkflowTask.id
225
+ }
226
+ });
227
+ }
228
+ await this.workflow.resume(job);
226
229
  }
227
- await this.workflow.resume(job);
228
- }
230
+ };
231
+ runner();
229
232
  }
230
233
  resume(node, job, processor) {
231
234
  return job;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Create AI employees with diverse skills to collaborate with humans, build systems, and handle business operations.",
7
7
  "description.ru-RU": "Поддержка интеграции с AI-сервисами: предоставляются AI-узлы для рабочих процессов, расширяя возможности бизнес-обработки.",
8
8
  "description.zh-CN": "创建各种技能的 AI 员工,与人类协同,搭建系统,处理业务。",
9
- "version": "2.1.0-alpha.29",
9
+ "version": "2.1.0-alpha.30",
10
10
  "main": "dist/server/index.js",
11
11
  "homepage": "https://docs.nocobase.com/handbook/action-ai",
12
12
  "homepage.ru-RU": "https://docs-ru.nocobase.com/handbook/action-ai",
@@ -42,6 +42,7 @@
42
42
  "@langchain/langgraph-checkpoint": "^1.0.0",
43
43
  "@langchain/ollama": "^1.2.2",
44
44
  "@langchain/openai": "^1.2.7",
45
+ "@langchain/xai": "1.3.3",
45
46
  "@nocobase/ai-employee-avatars": "^1.0.2",
46
47
  "@xyflow/react": "^12.7.0",
47
48
  "diff": "9.0.0",
@@ -63,5 +64,5 @@
63
64
  "keywords": [
64
65
  "AI"
65
66
  ],
66
- "gitHead": "526e36e399a570755286f9a0d34fa202b4a58a03"
67
+ "gitHead": "292ae0ad87f195ed201b274902d21ecd96f5ddd0"
67
68
  }