@versini/sassysaint-common 4.11.1 → 4.12.1
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 +26 -12
- package/dist/index.js +114 -98
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,12 @@ declare const MODEL_SONAR_PRO = "sonar-pro";
|
|
|
32
32
|
declare const MODEL_DEV = "mistralai/mistral-small-3.2-24b-instruct:free";
|
|
33
33
|
declare const MODEL_MEMORY_INFERENCE = "gpt-4o-mini";
|
|
34
34
|
declare const MODEL_EMBEDDING_TEXT = "text-embedding-3-small";
|
|
35
|
+
/**
|
|
36
|
+
* Maps model constants to user-friendly display names.
|
|
37
|
+
* Used by client UI to show readable model names in plan descriptions and
|
|
38
|
+
* settings.
|
|
39
|
+
*/
|
|
40
|
+
declare const MODEL_DISPLAY_NAMES: Record<string, string>;
|
|
35
41
|
/**
|
|
36
42
|
* List of all models available TODAY.
|
|
37
43
|
* This is used by the client to display the list of models.
|
|
@@ -72,6 +78,24 @@ declare const DIGGIDY_CHAT_ID_HEADER = "x-diggidy-chat-id";
|
|
|
72
78
|
*
|
|
73
79
|
*/
|
|
74
80
|
declare const findProvider: (modelName: string) => null | typeof PROVIDER_ANTHROPIC | typeof PROVIDER_OPENAI | typeof PROVIDER_GOOGLE;
|
|
81
|
+
/**
|
|
82
|
+
* Derives the list of available providers from a given list of models.
|
|
83
|
+
* This allows the client to automatically filter provider options based on
|
|
84
|
+
* which models are available in a user's plan.
|
|
85
|
+
*
|
|
86
|
+
* @param models - Array of model names (e.g., from allowedModels in policies).
|
|
87
|
+
* @returns Array of provider names that have at least one model available.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // If plan only has GPT and Claude models:
|
|
91
|
+
* getProvidersFromModels([MODEL_GPT4_MINI, MODEL_CLAUDE_HAIKU])
|
|
92
|
+
* // returns: [PROVIDER_OPENAI, PROVIDER_ANTHROPIC]
|
|
93
|
+
*
|
|
94
|
+
* // If Gemini models are removed from plan:
|
|
95
|
+
* getProvidersFromModels([MODEL_GPT4_MINI, MODEL_CLAUDE_HAIKU])
|
|
96
|
+
* // Google provider will not be in the result
|
|
97
|
+
*/
|
|
98
|
+
declare const getProvidersFromModels: (models: string[]) => Array<typeof PROVIDER_OPENAI | typeof PROVIDER_ANTHROPIC | typeof PROVIDER_GOOGLE>;
|
|
75
99
|
/**
|
|
76
100
|
* Sort capabilities are shared across client and server.
|
|
77
101
|
*/
|
|
@@ -323,19 +347,9 @@ declare namespace index {
|
|
|
323
347
|
/**
|
|
324
348
|
* List of all plans available.
|
|
325
349
|
*/
|
|
326
|
-
declare const
|
|
350
|
+
declare const PLAN_BASIC = "sassy:basic";
|
|
327
351
|
declare const PLAN_PLUS = "sassy:plus";
|
|
328
352
|
declare const PLAN_PREMIUM = "sassy:advanced";
|
|
329
|
-
/**
|
|
330
|
-
* List of grants available (coarse grain). This is to be used by client with
|
|
331
|
-
* the isGranted() helper to check for feature availability via JWT
|
|
332
|
-
* capabilities.
|
|
333
|
-
*/
|
|
334
|
-
declare const POLICY_GRANTS: {
|
|
335
|
-
PLAN_FREE: string;
|
|
336
|
-
PLAN_PLUS: string;
|
|
337
|
-
PLAN_PREMIUM: string;
|
|
338
|
-
};
|
|
339
353
|
/**
|
|
340
354
|
* Capability constants (single source of truth for string ids).
|
|
341
355
|
* NOTE: these map 1:1 to tool names in ALL_TOOLS from @sassysaint/tools
|
|
@@ -384,4 +398,4 @@ declare function isEntitlementLoaded(state: EntitlementStateLike | null | undefi
|
|
|
384
398
|
declare function toCapabilitySet(capabilities: string[] | undefined | null): Set<string> | null;
|
|
385
399
|
declare function isEntitled(capabilitiesOrSet: string[] | Set<string> | null | undefined, required: CapabilityInput, options?: IsEntitledOptions): boolean;
|
|
386
400
|
|
|
387
|
-
export { ALL_MODELS, ALL_PROVIDERS, ALL_REASONING_MODELS, APPLICATION_NAME, CAPABILITIES, type CapabilityInput, DEFAULT_PROVIDER, DIGGIDY_CHAT_ID_HEADER, type EntitlementStateLike, type IsEntitledOptions, MODELS_PER_PROVIDER, MODEL_CLAUDE_HAIKU, MODEL_CLAUDE_SONNET, MODEL_DEV, MODEL_EMBEDDING_TEXT, MODEL_GEMINI_FLASH, MODEL_GEMINI_PRO, MODEL_GPT4_MINI, MODEL_GPT5, MODEL_MEMORY_INFERENCE, MODEL_SONAR, MODEL_SONAR_PRO,
|
|
401
|
+
export { ALL_MODELS, ALL_PROVIDERS, ALL_REASONING_MODELS, APPLICATION_NAME, CAPABILITIES, type CapabilityInput, DEFAULT_PROVIDER, DIGGIDY_CHAT_ID_HEADER, type EntitlementStateLike, type IsEntitledOptions, MODELS_PER_PROVIDER, MODEL_CLAUDE_HAIKU, MODEL_CLAUDE_SONNET, MODEL_DEV, MODEL_DISPLAY_NAMES, MODEL_EMBEDDING_TEXT, MODEL_GEMINI_FLASH, MODEL_GEMINI_PRO, MODEL_GPT4_MINI, MODEL_GPT5, MODEL_MEMORY_INFERENCE, MODEL_SONAR, MODEL_SONAR_PRO, PLAN_BASIC, PLAN_PLUS, PLAN_PREMIUM, PROVIDER_ANTHROPIC, PROVIDER_GOOGLE, PROVIDER_MEMORY, PROVIDER_MISTRAL, PROVIDER_OPENAI, PROVIDER_PERPLEXITY, PROVIDER_ROLE_MAP, PROVIDER_SUMMARY, ROLE_ASSISTANT, ROLE_HIDDEN, ROLE_INTERNAL, ROLE_SYSTEM, ROLE_USER, SORT_BY_TIMESTAMP, SORT_BY_TOKEN_USAGE, index as crypto, findProvider, getProvidersFromModels, isEntitled, isEntitlementLoaded, toCapabilitySet };
|
package/dist/index.js
CHANGED
|
@@ -1,61 +1,80 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
], u = "gpt-5", p = "gpt-4.1-nano",
|
|
1
|
+
const L = "system", a = "user", i = "assistant", q = "hidden", ee = "data", c = "OpenAI", y = "Anthropic", d = "Google", M = "Summary", T = "Memory", O = "Perplexity", te = "Mistral", ne = c, m = [
|
|
2
|
+
c,
|
|
3
|
+
y,
|
|
4
|
+
d
|
|
5
|
+
], u = "gpt-5", p = "gpt-4.1-nano", f = "claude-haiku-4-5-20251001", A = "claude-sonnet-4-5-20250929", _ = "gemini-2.5-flash", l = "gemini-2.5-pro", R = "sonar", P = "sonar-pro", re = "mistralai/mistral-small-3.2-24b-instruct:free", oe = "gpt-4o-mini", se = "text-embedding-3-small", ae = {
|
|
6
|
+
[u]: "GPT-5",
|
|
7
|
+
[p]: "GPT-4.1 Nano",
|
|
8
|
+
[f]: "Claude Haiku 4.5",
|
|
9
|
+
[A]: "Claude Sonnet 4.5",
|
|
10
|
+
[_]: "Gemini 2.5 Flash",
|
|
11
|
+
[l]: "Gemini 2.5 Pro",
|
|
12
|
+
[R]: "Sonar",
|
|
13
|
+
[P]: "Sonar Pro"
|
|
14
|
+
}, ie = [
|
|
6
15
|
u,
|
|
7
16
|
p,
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
f,
|
|
18
|
+
A,
|
|
10
19
|
_,
|
|
11
20
|
l
|
|
12
|
-
],
|
|
21
|
+
], ce = [
|
|
13
22
|
u,
|
|
14
|
-
|
|
23
|
+
A,
|
|
15
24
|
l
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
], w = "claude-sonnet-4", h = "claude-3", b = "gpt-", N = "o3", C = "o4", v = "gemini", U = "sonar", K = {
|
|
26
|
+
[y]: [
|
|
27
|
+
w,
|
|
28
|
+
h
|
|
20
29
|
],
|
|
21
|
-
[
|
|
22
|
-
h,
|
|
30
|
+
[c]: [
|
|
23
31
|
b,
|
|
24
|
-
N
|
|
32
|
+
N,
|
|
33
|
+
C
|
|
25
34
|
],
|
|
26
|
-
[
|
|
27
|
-
[
|
|
28
|
-
},
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
33
|
-
},
|
|
34
|
-
[
|
|
35
|
-
[
|
|
36
|
-
[
|
|
37
|
-
[
|
|
38
|
-
[
|
|
39
|
-
[
|
|
40
|
-
},
|
|
35
|
+
[d]: [v],
|
|
36
|
+
[O]: [U]
|
|
37
|
+
}, k = {
|
|
38
|
+
[c]: [p, u],
|
|
39
|
+
[y]: [f, A],
|
|
40
|
+
[d]: [_, l],
|
|
41
|
+
[O]: [R, P]
|
|
42
|
+
}, Ee = {
|
|
43
|
+
[c]: [L, a, i],
|
|
44
|
+
[y]: [a, i],
|
|
45
|
+
[M]: [a, i],
|
|
46
|
+
[T]: [a, i],
|
|
47
|
+
[d]: [a, i],
|
|
48
|
+
[O]: [a, i]
|
|
49
|
+
}, ye = "x-diggidy-chat-id", de = (n) => {
|
|
41
50
|
for (const [e, r] of Object.entries(
|
|
42
|
-
|
|
51
|
+
K
|
|
43
52
|
))
|
|
44
53
|
if (r.some((t) => n.startsWith(t)))
|
|
45
54
|
return e;
|
|
46
55
|
return null;
|
|
47
|
-
},
|
|
56
|
+
}, ue = (n) => {
|
|
57
|
+
if (!n || n.length === 0)
|
|
58
|
+
return [];
|
|
59
|
+
const e = /* @__PURE__ */ new Set();
|
|
60
|
+
for (const r of n)
|
|
61
|
+
for (const [t, E] of Object.entries(
|
|
62
|
+
k
|
|
63
|
+
))
|
|
64
|
+
E.includes(r) && (t === c || t === y || t === d) && e.add(t);
|
|
65
|
+
return m.filter((r) => e.has(r));
|
|
66
|
+
}, Ae = "timestamp", le = "tokenUsage", Oe = "Diggidy";
|
|
48
67
|
class s extends Error {
|
|
49
68
|
constructor(e, r, t) {
|
|
50
69
|
super(e), this.code = r, this.originalError = t, this.name = "CryptoError";
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
|
-
const
|
|
72
|
+
const S = {
|
|
54
73
|
keySize: 4096,
|
|
55
74
|
debug: !1
|
|
56
75
|
};
|
|
57
|
-
async function
|
|
58
|
-
const e = { ...
|
|
76
|
+
async function G(n = {}) {
|
|
77
|
+
const e = { ...S, ...n };
|
|
59
78
|
try {
|
|
60
79
|
const r = {
|
|
61
80
|
modulusLength: e.keySize,
|
|
@@ -91,7 +110,7 @@ async function K(n = {}) {
|
|
|
91
110
|
);
|
|
92
111
|
}
|
|
93
112
|
}
|
|
94
|
-
async function
|
|
113
|
+
async function F(n) {
|
|
95
114
|
try {
|
|
96
115
|
return {
|
|
97
116
|
jwk: await crypto.subtle.exportKey("jwk", n),
|
|
@@ -106,7 +125,7 @@ async function k(n) {
|
|
|
106
125
|
);
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
|
-
async function
|
|
128
|
+
async function I(n) {
|
|
110
129
|
try {
|
|
111
130
|
const e = JSON.parse(n);
|
|
112
131
|
return await crypto.subtle.importKey(
|
|
@@ -128,14 +147,14 @@ async function P(n) {
|
|
|
128
147
|
);
|
|
129
148
|
}
|
|
130
149
|
}
|
|
131
|
-
async function
|
|
150
|
+
async function V(n, e, r) {
|
|
132
151
|
try {
|
|
133
|
-
const
|
|
152
|
+
const E = new TextEncoder().encode(n), o = await crypto.subtle.encrypt(
|
|
134
153
|
{
|
|
135
154
|
name: "RSA-OAEP"
|
|
136
155
|
},
|
|
137
156
|
e,
|
|
138
|
-
|
|
157
|
+
E
|
|
139
158
|
);
|
|
140
159
|
return {
|
|
141
160
|
data: btoa(
|
|
@@ -152,7 +171,7 @@ async function G(n, e, r) {
|
|
|
152
171
|
);
|
|
153
172
|
}
|
|
154
173
|
}
|
|
155
|
-
async function
|
|
174
|
+
async function Y(n, e) {
|
|
156
175
|
try {
|
|
157
176
|
const r = Uint8Array.from(
|
|
158
177
|
atob(n.data),
|
|
@@ -173,9 +192,9 @@ async function F(n, e) {
|
|
|
173
192
|
);
|
|
174
193
|
}
|
|
175
194
|
}
|
|
176
|
-
async function
|
|
195
|
+
async function H(n, e, r) {
|
|
177
196
|
try {
|
|
178
|
-
const t = await
|
|
197
|
+
const t = await I(
|
|
179
198
|
n.serverPublicKey
|
|
180
199
|
);
|
|
181
200
|
return {
|
|
@@ -193,10 +212,10 @@ async function V(n, e, r) {
|
|
|
193
212
|
);
|
|
194
213
|
}
|
|
195
214
|
}
|
|
196
|
-
function
|
|
215
|
+
function x(n) {
|
|
197
216
|
return !(!n || !n.clientKeyPair || !n.serverPublicKey);
|
|
198
217
|
}
|
|
199
|
-
function
|
|
218
|
+
function B() {
|
|
200
219
|
const n = Date.now().toString(36), e = new Uint8Array(8);
|
|
201
220
|
if (typeof window < "u" && window.crypto?.getRandomValues)
|
|
202
221
|
window.crypto.getRandomValues(e);
|
|
@@ -206,50 +225,46 @@ function H() {
|
|
|
206
225
|
const r = Array.from(e).map((t) => t.toString(36)).join("");
|
|
207
226
|
return `device_${n}_${r}`;
|
|
208
227
|
}
|
|
209
|
-
function
|
|
228
|
+
function D() {
|
|
210
229
|
return typeof window < "u" && typeof window.crypto < "u" && typeof window.crypto.subtle < "u";
|
|
211
230
|
}
|
|
212
|
-
function
|
|
231
|
+
function g() {
|
|
213
232
|
return typeof window < "u" && (window.isSecureContext || window.location.protocol === "https:");
|
|
214
233
|
}
|
|
215
|
-
function
|
|
216
|
-
return
|
|
234
|
+
function X() {
|
|
235
|
+
return D() && g();
|
|
217
236
|
}
|
|
218
|
-
function
|
|
237
|
+
function j(n) {
|
|
219
238
|
const e = new Uint8Array(n);
|
|
220
239
|
let r = "";
|
|
221
240
|
for (let t = 0; t < e.byteLength; t++)
|
|
222
241
|
r += String.fromCharCode(e[t]);
|
|
223
242
|
return btoa(r);
|
|
224
243
|
}
|
|
225
|
-
function
|
|
244
|
+
function $(n) {
|
|
226
245
|
const e = atob(n), r = new Uint8Array(e.length);
|
|
227
246
|
for (let t = 0; t < e.length; t++)
|
|
228
247
|
r[t] = e.charCodeAt(t);
|
|
229
248
|
return r.buffer;
|
|
230
249
|
}
|
|
231
|
-
function
|
|
250
|
+
function z(n) {
|
|
232
251
|
try {
|
|
233
252
|
return JSON.stringify(n);
|
|
234
253
|
} catch {
|
|
235
254
|
return String(n);
|
|
236
255
|
}
|
|
237
256
|
}
|
|
238
|
-
function
|
|
257
|
+
function W(n) {
|
|
239
258
|
try {
|
|
240
259
|
return JSON.parse(n);
|
|
241
260
|
} catch {
|
|
242
261
|
return null;
|
|
243
262
|
}
|
|
244
263
|
}
|
|
245
|
-
function
|
|
264
|
+
function J(n, ...e) {
|
|
246
265
|
typeof process < "u" && process.env && process.env.NODE_ENV === "development" && console.info(`🔐 [Crypto] ${n}`, ...e);
|
|
247
266
|
}
|
|
248
|
-
const
|
|
249
|
-
PLAN_FREE: W,
|
|
250
|
-
PLAN_PLUS: J,
|
|
251
|
-
PLAN_PREMIUM: Z
|
|
252
|
-
}, fe = {
|
|
267
|
+
const pe = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, CryptoError: s, DEFAULT_CRYPTO_CONFIG: S, arrayBufferToBase64: j, base64ToArrayBuffer: $, canUseEncryption: X, debugLog: J, decryptFromServer: Y, encryptForServer: V, establishCryptoSession: H, exportPublicKey: F, generateClientKeyPair: G, generateDeviceId: B, importServerPublicKey: I, isCryptoSessionValid: x, isSecureContext: g, isWebCryptoSupported: D, safeParse: W, safeStringify: z }, Symbol.toStringTag, { value: "Module" })), fe = "sassy:basic", _e = "sassy:plus", Re = "sassy:advanced", Pe = {
|
|
253
268
|
TOOL: {
|
|
254
269
|
DATETIME: "getDateTime",
|
|
255
270
|
IMAGES: "getImages",
|
|
@@ -268,60 +283,61 @@ const Re = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
|
268
283
|
CODEINTERPRETER: "addon:codeinterpreter"
|
|
269
284
|
}
|
|
270
285
|
};
|
|
271
|
-
function
|
|
286
|
+
function Se(n) {
|
|
272
287
|
return !!n && Array.isArray(n.capabilities) && n.capabilities.length >= 0;
|
|
273
288
|
}
|
|
274
|
-
function
|
|
289
|
+
function Z(n) {
|
|
275
290
|
return n ? new Set(n) : null;
|
|
276
291
|
}
|
|
277
292
|
function Ie(n, e, r) {
|
|
278
293
|
if (!e || Array.isArray(e) && e.length === 0)
|
|
279
294
|
return !0;
|
|
280
|
-
const t = n instanceof Set ? n :
|
|
295
|
+
const t = n instanceof Set ? n : Z(n);
|
|
281
296
|
return !t || t.size === 0 ? !1 : typeof e == "string" ? t.has(e) : r?.any === !0 ? e.some((o) => t.has(o)) : e.every((o) => t.has(o));
|
|
282
297
|
}
|
|
283
298
|
export {
|
|
284
299
|
ie as ALL_MODELS,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
300
|
+
m as ALL_PROVIDERS,
|
|
301
|
+
ce as ALL_REASONING_MODELS,
|
|
302
|
+
Oe as APPLICATION_NAME,
|
|
303
|
+
Pe as CAPABILITIES,
|
|
304
|
+
ne as DEFAULT_PROVIDER,
|
|
305
|
+
ye as DIGGIDY_CHAT_ID_HEADER,
|
|
306
|
+
k as MODELS_PER_PROVIDER,
|
|
307
|
+
f as MODEL_CLAUDE_HAIKU,
|
|
308
|
+
A as MODEL_CLAUDE_SONNET,
|
|
309
|
+
re as MODEL_DEV,
|
|
310
|
+
ae as MODEL_DISPLAY_NAMES,
|
|
311
|
+
se as MODEL_EMBEDDING_TEXT,
|
|
296
312
|
_ as MODEL_GEMINI_FLASH,
|
|
297
313
|
l as MODEL_GEMINI_PRO,
|
|
298
314
|
p as MODEL_GPT4_MINI,
|
|
299
315
|
u as MODEL_GPT5,
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
ee as
|
|
317
|
-
|
|
318
|
-
S as ROLE_SYSTEM,
|
|
316
|
+
oe as MODEL_MEMORY_INFERENCE,
|
|
317
|
+
R as MODEL_SONAR,
|
|
318
|
+
P as MODEL_SONAR_PRO,
|
|
319
|
+
fe as PLAN_BASIC,
|
|
320
|
+
_e as PLAN_PLUS,
|
|
321
|
+
Re as PLAN_PREMIUM,
|
|
322
|
+
y as PROVIDER_ANTHROPIC,
|
|
323
|
+
d as PROVIDER_GOOGLE,
|
|
324
|
+
T as PROVIDER_MEMORY,
|
|
325
|
+
te as PROVIDER_MISTRAL,
|
|
326
|
+
c as PROVIDER_OPENAI,
|
|
327
|
+
O as PROVIDER_PERPLEXITY,
|
|
328
|
+
Ee as PROVIDER_ROLE_MAP,
|
|
329
|
+
M as PROVIDER_SUMMARY,
|
|
330
|
+
i as ROLE_ASSISTANT,
|
|
331
|
+
q as ROLE_HIDDEN,
|
|
332
|
+
ee as ROLE_INTERNAL,
|
|
333
|
+
L as ROLE_SYSTEM,
|
|
319
334
|
a as ROLE_USER,
|
|
320
|
-
|
|
335
|
+
Ae as SORT_BY_TIMESTAMP,
|
|
321
336
|
le as SORT_BY_TOKEN_USAGE,
|
|
322
|
-
|
|
323
|
-
|
|
337
|
+
pe as crypto,
|
|
338
|
+
de as findProvider,
|
|
339
|
+
ue as getProvidersFromModels,
|
|
324
340
|
Ie as isEntitled,
|
|
325
|
-
|
|
326
|
-
|
|
341
|
+
Se as isEntitlementLoaded,
|
|
342
|
+
Z as toCapabilitySet
|
|
327
343
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/sassysaint-common",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
"test:watch": "vitest",
|
|
33
33
|
"watch": "npm-run-all dev"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "414c62d95d35eca0575db970411a3a5c517b013a"
|
|
36
36
|
}
|