@ainetwork/adk-provider-memory-mongodb 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-JMRBIQNX.js +96 -0
- package/dist/chunk-JMRBIQNX.js.map +1 -0
- package/dist/index.cjs +190 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -1
- package/dist/models/document.model.cjs +128 -0
- package/dist/models/document.model.cjs.map +1 -0
- package/dist/models/document.model.d.cts +87 -0
- package/dist/models/document.model.d.ts +87 -0
- package/dist/models/document.model.js +9 -0
- package/dist/models/document.model.js.map +1 -0
- package/dist/models/messages.model.d.cts +9 -9
- package/dist/models/messages.model.d.ts +9 -9
- package/dist/models/threads.model.d.cts +6 -6
- package/dist/models/threads.model.d.ts +6 -6
- package/dist/models/user-workflow.model.d.cts +3 -3
- package/dist/models/user-workflow.model.d.ts +3 -3
- package/implements/base.memory.ts +12 -1
- package/implements/document.memory.ts +88 -0
- package/models/document.model.ts +113 -0
- package/package.json +3 -3
|
@@ -23,42 +23,42 @@ declare const MessageObjectSchema: Schema<any, mongoose.Model<any, any, any, any
|
|
|
23
23
|
createdAt: NativeDate;
|
|
24
24
|
updatedAt: NativeDate;
|
|
25
25
|
} & {
|
|
26
|
-
threadId: string;
|
|
27
26
|
userId: string;
|
|
28
|
-
messageId: string;
|
|
29
|
-
role: MessageRole;
|
|
30
27
|
content: {
|
|
31
28
|
type: string;
|
|
32
29
|
parts: any[];
|
|
33
30
|
};
|
|
31
|
+
threadId: string;
|
|
32
|
+
messageId: string;
|
|
33
|
+
role: MessageRole;
|
|
34
34
|
timestamp: number;
|
|
35
35
|
metadata: any;
|
|
36
36
|
}, Document<unknown, {}, mongoose.FlatRecord<{
|
|
37
37
|
createdAt: NativeDate;
|
|
38
38
|
updatedAt: NativeDate;
|
|
39
39
|
} & {
|
|
40
|
-
threadId: string;
|
|
41
40
|
userId: string;
|
|
42
|
-
messageId: string;
|
|
43
|
-
role: MessageRole;
|
|
44
41
|
content: {
|
|
45
42
|
type: string;
|
|
46
43
|
parts: any[];
|
|
47
44
|
};
|
|
45
|
+
threadId: string;
|
|
46
|
+
messageId: string;
|
|
47
|
+
role: MessageRole;
|
|
48
48
|
timestamp: number;
|
|
49
49
|
metadata: any;
|
|
50
50
|
}>, {}> & mongoose.FlatRecord<{
|
|
51
51
|
createdAt: NativeDate;
|
|
52
52
|
updatedAt: NativeDate;
|
|
53
53
|
} & {
|
|
54
|
-
threadId: string;
|
|
55
54
|
userId: string;
|
|
56
|
-
messageId: string;
|
|
57
|
-
role: MessageRole;
|
|
58
55
|
content: {
|
|
59
56
|
type: string;
|
|
60
57
|
parts: any[];
|
|
61
58
|
};
|
|
59
|
+
threadId: string;
|
|
60
|
+
messageId: string;
|
|
61
|
+
role: MessageRole;
|
|
62
62
|
timestamp: number;
|
|
63
63
|
metadata: any;
|
|
64
64
|
}> & {
|
|
@@ -8,31 +8,31 @@ declare const ThreadObjectSchema: Schema<any, mongoose.Model<any, any, any, any,
|
|
|
8
8
|
updatedAt: NativeDate;
|
|
9
9
|
} & {
|
|
10
10
|
type: ThreadType;
|
|
11
|
-
threadId: string;
|
|
12
11
|
userId: string;
|
|
12
|
+
threadId: string;
|
|
13
13
|
title?: string | null | undefined;
|
|
14
|
-
isPinned?: boolean | null | undefined;
|
|
15
14
|
workflowId?: string | null | undefined;
|
|
15
|
+
isPinned?: boolean | null | undefined;
|
|
16
16
|
}, Document<unknown, {}, mongoose.FlatRecord<{
|
|
17
17
|
createdAt: NativeDate;
|
|
18
18
|
updatedAt: NativeDate;
|
|
19
19
|
} & {
|
|
20
20
|
type: ThreadType;
|
|
21
|
-
threadId: string;
|
|
22
21
|
userId: string;
|
|
22
|
+
threadId: string;
|
|
23
23
|
title?: string | null | undefined;
|
|
24
|
-
isPinned?: boolean | null | undefined;
|
|
25
24
|
workflowId?: string | null | undefined;
|
|
25
|
+
isPinned?: boolean | null | undefined;
|
|
26
26
|
}>, {}> & mongoose.FlatRecord<{
|
|
27
27
|
createdAt: NativeDate;
|
|
28
28
|
updatedAt: NativeDate;
|
|
29
29
|
} & {
|
|
30
30
|
type: ThreadType;
|
|
31
|
-
threadId: string;
|
|
32
31
|
userId: string;
|
|
32
|
+
threadId: string;
|
|
33
33
|
title?: string | null | undefined;
|
|
34
|
-
isPinned?: boolean | null | undefined;
|
|
35
34
|
workflowId?: string | null | undefined;
|
|
35
|
+
isPinned?: boolean | null | undefined;
|
|
36
36
|
}> & {
|
|
37
37
|
_id: mongoose.Types.ObjectId;
|
|
38
38
|
} & {
|
|
@@ -8,31 +8,31 @@ declare const ThreadObjectSchema: Schema<any, mongoose.Model<any, any, any, any,
|
|
|
8
8
|
updatedAt: NativeDate;
|
|
9
9
|
} & {
|
|
10
10
|
type: ThreadType;
|
|
11
|
-
threadId: string;
|
|
12
11
|
userId: string;
|
|
12
|
+
threadId: string;
|
|
13
13
|
title?: string | null | undefined;
|
|
14
|
-
isPinned?: boolean | null | undefined;
|
|
15
14
|
workflowId?: string | null | undefined;
|
|
15
|
+
isPinned?: boolean | null | undefined;
|
|
16
16
|
}, Document<unknown, {}, mongoose.FlatRecord<{
|
|
17
17
|
createdAt: NativeDate;
|
|
18
18
|
updatedAt: NativeDate;
|
|
19
19
|
} & {
|
|
20
20
|
type: ThreadType;
|
|
21
|
-
threadId: string;
|
|
22
21
|
userId: string;
|
|
22
|
+
threadId: string;
|
|
23
23
|
title?: string | null | undefined;
|
|
24
|
-
isPinned?: boolean | null | undefined;
|
|
25
24
|
workflowId?: string | null | undefined;
|
|
25
|
+
isPinned?: boolean | null | undefined;
|
|
26
26
|
}>, {}> & mongoose.FlatRecord<{
|
|
27
27
|
createdAt: NativeDate;
|
|
28
28
|
updatedAt: NativeDate;
|
|
29
29
|
} & {
|
|
30
30
|
type: ThreadType;
|
|
31
|
-
threadId: string;
|
|
32
31
|
userId: string;
|
|
32
|
+
threadId: string;
|
|
33
33
|
title?: string | null | undefined;
|
|
34
|
-
isPinned?: boolean | null | undefined;
|
|
35
34
|
workflowId?: string | null | undefined;
|
|
35
|
+
isPinned?: boolean | null | undefined;
|
|
36
36
|
}> & {
|
|
37
37
|
_id: mongoose.Types.ObjectId;
|
|
38
38
|
} & {
|
|
@@ -9,8 +9,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
9
9
|
} & {
|
|
10
10
|
userId: string;
|
|
11
11
|
title: string;
|
|
12
|
-
workflowId: string;
|
|
13
12
|
content: string;
|
|
13
|
+
workflowId: string;
|
|
14
14
|
active: boolean;
|
|
15
15
|
description?: string | null | undefined;
|
|
16
16
|
category?: string | null | undefined;
|
|
@@ -29,8 +29,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
29
29
|
} & {
|
|
30
30
|
userId: string;
|
|
31
31
|
title: string;
|
|
32
|
-
workflowId: string;
|
|
33
32
|
content: string;
|
|
33
|
+
workflowId: string;
|
|
34
34
|
active: boolean;
|
|
35
35
|
description?: string | null | undefined;
|
|
36
36
|
category?: string | null | undefined;
|
|
@@ -49,8 +49,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
49
49
|
} & {
|
|
50
50
|
userId: string;
|
|
51
51
|
title: string;
|
|
52
|
-
workflowId: string;
|
|
53
52
|
content: string;
|
|
53
|
+
workflowId: string;
|
|
54
54
|
active: boolean;
|
|
55
55
|
description?: string | null | undefined;
|
|
56
56
|
category?: string | null | undefined;
|
|
@@ -9,8 +9,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
9
9
|
} & {
|
|
10
10
|
userId: string;
|
|
11
11
|
title: string;
|
|
12
|
-
workflowId: string;
|
|
13
12
|
content: string;
|
|
13
|
+
workflowId: string;
|
|
14
14
|
active: boolean;
|
|
15
15
|
description?: string | null | undefined;
|
|
16
16
|
category?: string | null | undefined;
|
|
@@ -29,8 +29,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
29
29
|
} & {
|
|
30
30
|
userId: string;
|
|
31
31
|
title: string;
|
|
32
|
-
workflowId: string;
|
|
33
32
|
content: string;
|
|
33
|
+
workflowId: string;
|
|
34
34
|
active: boolean;
|
|
35
35
|
description?: string | null | undefined;
|
|
36
36
|
category?: string | null | undefined;
|
|
@@ -49,8 +49,8 @@ declare const UserWorkflowObjectSchema: Schema<any, mongoose.Model<any, any, any
|
|
|
49
49
|
} & {
|
|
50
50
|
userId: string;
|
|
51
51
|
title: string;
|
|
52
|
-
workflowId: string;
|
|
53
52
|
content: string;
|
|
53
|
+
workflowId: string;
|
|
54
54
|
active: boolean;
|
|
55
55
|
description?: string | null | undefined;
|
|
56
56
|
category?: string | null | undefined;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IAgentMemory, IIntentMemory, IMemory, IUserWorkflowMemory, IThreadMemory, IWorkflowTemplateMemory } from "@ainetwork/adk/modules";
|
|
1
|
+
import { IAgentMemory, IDocumentMemory, IIntentMemory, IMemory, IUserWorkflowMemory, IThreadMemory, IWorkflowTemplateMemory } from "@ainetwork/adk/modules";
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
3
|
import { loggers } from "@ainetwork/adk/utils/logger";
|
|
4
4
|
import { MongoDBAgent } from "./agent.memory";
|
|
5
|
+
import { MongoDBDocument } from "./document.memory";
|
|
5
6
|
import { MongoDBIntent } from "./intent.memory";
|
|
6
7
|
import { MongoDBThread } from "./thread.memory";
|
|
7
8
|
import { MongoDBUserWorkflow } from "./user-workflow.memory";
|
|
@@ -39,6 +40,7 @@ export class MongoDBMemory implements IMemory {
|
|
|
39
40
|
private threadMemory: MongoDBThread;
|
|
40
41
|
private workflowTemplateMemory: MongoDBWorkflowTemplate;
|
|
41
42
|
private userWorkflowMemory: MongoDBUserWorkflow;
|
|
43
|
+
private documentMemory: MongoDBDocument;
|
|
42
44
|
|
|
43
45
|
constructor(config: string | MongoDBMemoryConfig) {
|
|
44
46
|
const cfg = typeof config === 'string' ? { uri: config } : config;
|
|
@@ -93,6 +95,11 @@ export class MongoDBMemory implements IMemory {
|
|
|
93
95
|
this.executeWithRetry.bind(this),
|
|
94
96
|
this.getOperationTimeout.bind(this)
|
|
95
97
|
);
|
|
98
|
+
|
|
99
|
+
this.documentMemory = new MongoDBDocument(
|
|
100
|
+
this.executeWithRetry.bind(this),
|
|
101
|
+
this.getOperationTimeout.bind(this)
|
|
102
|
+
);
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
public getAgentMemory(): IAgentMemory {
|
|
@@ -115,6 +122,10 @@ export class MongoDBMemory implements IMemory {
|
|
|
115
122
|
return this.userWorkflowMemory;
|
|
116
123
|
}
|
|
117
124
|
|
|
125
|
+
public getDocumentMemory(): IDocumentMemory {
|
|
126
|
+
return this.documentMemory;
|
|
127
|
+
}
|
|
128
|
+
|
|
118
129
|
private setupMongooseEventListeners(): void {
|
|
119
130
|
if (this.eventListenersSetup) return;
|
|
120
131
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { IDocumentMemory } from "@ainetwork/adk/modules";
|
|
2
|
+
import type { Document, DocumentFilter } from "@ainetwork/adk/types/document";
|
|
3
|
+
import { DocumentModel } from "../models/document.model";
|
|
4
|
+
|
|
5
|
+
export type ExecuteWithRetryFn = <T>(
|
|
6
|
+
operation: () => Promise<T>,
|
|
7
|
+
operationName?: string
|
|
8
|
+
) => Promise<T>;
|
|
9
|
+
|
|
10
|
+
export type GetOperationTimeoutFn = () => number;
|
|
11
|
+
|
|
12
|
+
export class MongoDBDocument implements IDocumentMemory {
|
|
13
|
+
private executeWithRetry: ExecuteWithRetryFn;
|
|
14
|
+
private getOperationTimeout: GetOperationTimeoutFn;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
executeWithRetry: ExecuteWithRetryFn,
|
|
18
|
+
getOperationTimeout: GetOperationTimeoutFn
|
|
19
|
+
) {
|
|
20
|
+
this.executeWithRetry = executeWithRetry;
|
|
21
|
+
this.getOperationTimeout = getOperationTimeout;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async getDocument(documentId: string): Promise<Document | undefined> {
|
|
25
|
+
return this.executeWithRetry(async () => {
|
|
26
|
+
const timeout = this.getOperationTimeout();
|
|
27
|
+
const document = await DocumentModel.findOne({ documentId })
|
|
28
|
+
.maxTimeMS(timeout)
|
|
29
|
+
.lean<Document>();
|
|
30
|
+
return document || undefined;
|
|
31
|
+
}, "getDocument()");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async createDocument(document: Document): Promise<Document> {
|
|
35
|
+
return this.executeWithRetry(async () => {
|
|
36
|
+
const created = await DocumentModel.create(document);
|
|
37
|
+
return created.toObject() as Document;
|
|
38
|
+
}, "createDocument()");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async updateDocument(
|
|
42
|
+
documentId: string,
|
|
43
|
+
document: Partial<Document>
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
// `documentId` is immutable; never let a payload reassign it.
|
|
46
|
+
const { documentId: _documentId, ...mutableUpdates } = document;
|
|
47
|
+
|
|
48
|
+
return this.executeWithRetry(async () => {
|
|
49
|
+
const timeout = this.getOperationTimeout();
|
|
50
|
+
await DocumentModel.updateOne(
|
|
51
|
+
{ documentId },
|
|
52
|
+
{ $set: mutableUpdates }
|
|
53
|
+
).maxTimeMS(timeout);
|
|
54
|
+
}, "updateDocument()");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public async deleteDocument(documentId: string): Promise<void> {
|
|
58
|
+
return this.executeWithRetry(async () => {
|
|
59
|
+
const timeout = this.getOperationTimeout();
|
|
60
|
+
await DocumentModel.deleteOne({ documentId }).maxTimeMS(timeout);
|
|
61
|
+
}, "deleteDocument()");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public async listDocuments(
|
|
65
|
+
userId?: string,
|
|
66
|
+
filter?: DocumentFilter
|
|
67
|
+
): Promise<Document[]> {
|
|
68
|
+
return this.executeWithRetry(async () => {
|
|
69
|
+
const timeout = this.getOperationTimeout();
|
|
70
|
+
const query: Record<string, unknown> = {};
|
|
71
|
+
if (userId) query.userId = userId;
|
|
72
|
+
if (filter?.workflowId) query.workflowId = filter.workflowId;
|
|
73
|
+
if (filter?.threadId) query.threadId = filter.threadId;
|
|
74
|
+
if (filter?.source) query.source = filter.source;
|
|
75
|
+
if (filter?.labels) {
|
|
76
|
+
// Subset match: every provided label must equal the stored value.
|
|
77
|
+
for (const [key, value] of Object.entries(filter.labels)) {
|
|
78
|
+
query[`labels.${key}`] = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const documents = await DocumentModel.find(query)
|
|
83
|
+
.maxTimeMS(timeout)
|
|
84
|
+
.lean<Document[]>();
|
|
85
|
+
return documents;
|
|
86
|
+
}, "listDocuments()");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DocumentAdvice,
|
|
3
|
+
DocumentFormat,
|
|
4
|
+
type DocumentSlot,
|
|
5
|
+
DocumentSource,
|
|
6
|
+
} from "@ainetwork/adk/types/document";
|
|
7
|
+
import type { WorkflowRenderedBlock } from "@ainetwork/adk/types/memory";
|
|
8
|
+
import { type Document, Schema } from "mongoose";
|
|
9
|
+
import mongoose from "mongoose";
|
|
10
|
+
|
|
11
|
+
export const DocumentObjectSchema = new Schema(
|
|
12
|
+
{
|
|
13
|
+
documentId: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
unique: true,
|
|
17
|
+
},
|
|
18
|
+
userId: {
|
|
19
|
+
type: String,
|
|
20
|
+
required: true,
|
|
21
|
+
index: true,
|
|
22
|
+
},
|
|
23
|
+
title: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
format: {
|
|
28
|
+
type: String,
|
|
29
|
+
enum: Object.values(DocumentFormat),
|
|
30
|
+
required: true,
|
|
31
|
+
default: DocumentFormat.MARKDOWN,
|
|
32
|
+
},
|
|
33
|
+
content: {
|
|
34
|
+
type: String,
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
blocks: {
|
|
38
|
+
type: Schema.Types.Mixed,
|
|
39
|
+
},
|
|
40
|
+
slots: {
|
|
41
|
+
type: Schema.Types.Mixed,
|
|
42
|
+
},
|
|
43
|
+
// Cached AI advice ({ content, generatedAt }). Free-form so the schema
|
|
44
|
+
// doesn't strip it on update (mongoose strict mode).
|
|
45
|
+
advice: {
|
|
46
|
+
type: Schema.Types.Mixed,
|
|
47
|
+
},
|
|
48
|
+
// Faceted grouping (e.g. category/workplaceId/month). Stored as a free
|
|
49
|
+
// map; index specific keys (e.g. `labels.workplaceId`) if query volume
|
|
50
|
+
// warrants it.
|
|
51
|
+
labels: {
|
|
52
|
+
type: Schema.Types.Mixed,
|
|
53
|
+
},
|
|
54
|
+
source: {
|
|
55
|
+
type: String,
|
|
56
|
+
enum: Object.values(DocumentSource),
|
|
57
|
+
required: true,
|
|
58
|
+
},
|
|
59
|
+
workflowId: {
|
|
60
|
+
type: String,
|
|
61
|
+
index: true,
|
|
62
|
+
},
|
|
63
|
+
threadId: {
|
|
64
|
+
type: String,
|
|
65
|
+
index: true,
|
|
66
|
+
},
|
|
67
|
+
version: {
|
|
68
|
+
type: Number,
|
|
69
|
+
required: true,
|
|
70
|
+
default: 1,
|
|
71
|
+
},
|
|
72
|
+
editedManually: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
},
|
|
75
|
+
createdAt: {
|
|
76
|
+
type: String,
|
|
77
|
+
required: true,
|
|
78
|
+
},
|
|
79
|
+
updatedAt: {
|
|
80
|
+
type: String,
|
|
81
|
+
required: true,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// `createdAt`/`updatedAt` are caller-supplied ISO strings (see Document type),
|
|
85
|
+
// so mongoose's automatic Date timestamps are intentionally disabled.
|
|
86
|
+
{
|
|
87
|
+
timestamps: false,
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export interface DocumentDocument extends Document {
|
|
92
|
+
documentId: string;
|
|
93
|
+
userId: string;
|
|
94
|
+
title: string;
|
|
95
|
+
format: DocumentFormat;
|
|
96
|
+
content: string;
|
|
97
|
+
blocks?: WorkflowRenderedBlock[];
|
|
98
|
+
slots?: DocumentSlot[];
|
|
99
|
+
advice?: DocumentAdvice;
|
|
100
|
+
labels?: Record<string, string>;
|
|
101
|
+
source: DocumentSource;
|
|
102
|
+
workflowId?: string;
|
|
103
|
+
threadId?: string;
|
|
104
|
+
version: number;
|
|
105
|
+
editedManually?: boolean;
|
|
106
|
+
createdAt: string;
|
|
107
|
+
updatedAt: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const DocumentModel = mongoose.model<DocumentDocument>(
|
|
111
|
+
"Document",
|
|
112
|
+
DocumentObjectSchema
|
|
113
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainetwork/adk-provider-memory-mongodb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"author": "AI Network (https://ainetwork.ai)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"mongoose": "^8.16.5"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@ainetwork/adk": "^0.
|
|
34
|
+
"@ainetwork/adk": "^0.7.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"typescript": "^5.0.0"
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "39b90b146668f11b0b6a15a3ba0ab5bf80eb026f"
|
|
44
44
|
}
|