@xuda.io/ai_module 1.1.5612 → 1.1.5614
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/index.mjs +112 -23
- package/index_ms.mjs +4 -0
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -4266,14 +4266,17 @@ export const create_conversation = async function (req, job_id, headers) {
|
|
|
4266
4266
|
// write conversation to host after thumbnail update
|
|
4267
4267
|
// get sender conversation
|
|
4268
4268
|
|
|
4269
|
-
|
|
4269
|
+
// Re-fetch the freshest copy of the conversation doc after process_conversation
|
|
4270
|
+
// had a chance to write thumbnail / title updates. Renamed (was `conversation_doc`)
|
|
4271
|
+
// to avoid TDZ shadowing the outer-scope `conversation_doc` we read for the lookup id.
|
|
4272
|
+
const fresh_conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_doc._id);
|
|
4270
4273
|
|
|
4271
4274
|
const recipient_account_profile_info = await get_active_account_profile_info(recipient_uid);
|
|
4272
|
-
let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id,
|
|
4273
|
-
recipient_conversation_doc.title =
|
|
4275
|
+
let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id, fresh_conversation_doc._id);
|
|
4276
|
+
recipient_conversation_doc.title = fresh_conversation_doc.title;
|
|
4274
4277
|
|
|
4275
|
-
recipient_conversation_doc.thumbnail_request_ts =
|
|
4276
|
-
recipient_conversation_doc.chat_image =
|
|
4278
|
+
recipient_conversation_doc.thumbnail_request_ts = fresh_conversation_doc.thumbnail_request_ts;
|
|
4279
|
+
recipient_conversation_doc.chat_image = fresh_conversation_doc.chat_image;
|
|
4277
4280
|
recipient_conversation_doc.direction = 'in';
|
|
4278
4281
|
await db_module.save_app_couch_doc_native(recipient_account_profile_info.app_id, recipient_conversation_doc);
|
|
4279
4282
|
|
|
@@ -5927,13 +5930,7 @@ Return a concise answer. Include only non-sensitive findings and mention when th
|
|
|
5927
5930
|
|
|
5928
5931
|
await fs.promises.mkdir(dump_dir, { recursive: true });
|
|
5929
5932
|
const sitemap_xml = await fetch_text(`${website_base}/sitemap.xml`, 8000);
|
|
5930
|
-
const priority_urls = [
|
|
5931
|
-
`${website_base}/`,
|
|
5932
|
-
`${website_base}/about`,
|
|
5933
|
-
`${website_base}/legal/privacy-policy`,
|
|
5934
|
-
`${website_base}/legal/terms`,
|
|
5935
|
-
`${website_base}/contact`,
|
|
5936
|
-
];
|
|
5933
|
+
const priority_urls = [`${website_base}/`, `${website_base}/about`, `${website_base}/legal/privacy-policy`, `${website_base}/legal/terms`, `${website_base}/contact`];
|
|
5937
5934
|
const sitemap_urls = [...sitemap_xml.matchAll(/<loc>([\s\S]*?)<\/loc>/gi)]
|
|
5938
5935
|
.map((match) => decode_html(match[1]).trim())
|
|
5939
5936
|
.filter((url) => {
|
|
@@ -6029,10 +6026,7 @@ Return a concise answer. Include only non-sensitive findings and mention when th
|
|
|
6029
6026
|
};
|
|
6030
6027
|
|
|
6031
6028
|
try {
|
|
6032
|
-
const data = await Promise.race([
|
|
6033
|
-
run_lookup(),
|
|
6034
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('xuda.ai website lookup timed out')), 20000)),
|
|
6035
|
-
]);
|
|
6029
|
+
const data = await Promise.race([run_lookup(), new Promise((_, reject) => setTimeout(() => reject(new Error('xuda.ai website lookup timed out')), 20000))]);
|
|
6036
6030
|
return JSON.stringify(data, null, 2);
|
|
6037
6031
|
} catch (err) {
|
|
6038
6032
|
return `xuda.ai website lookup failed: ${err.message || String(err)}`;
|
|
@@ -8197,6 +8191,14 @@ export const get_profile_picture = async function (uid, account_type = 'business
|
|
|
8197
8191
|
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
8198
8192
|
const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`);
|
|
8199
8193
|
|
|
8194
|
+
// Normalize: visitor accounts created through the chat widget can arrive with
|
|
8195
|
+
// account_type missing, null, or some other value. Treat anything that isn't
|
|
8196
|
+
// explicitly 'business' as 'personal' so the chat flow doesn't crash before
|
|
8197
|
+
// ever reaching the agent reply.
|
|
8198
|
+
if (account_type !== 'personal' && account_type !== 'business') {
|
|
8199
|
+
account_type = 'personal';
|
|
8200
|
+
}
|
|
8201
|
+
|
|
8200
8202
|
const { email, create_avatar } = metadata;
|
|
8201
8203
|
|
|
8202
8204
|
let prompt,
|
|
@@ -9176,6 +9178,78 @@ export const is_spam_email = async function (uid, email, subject, account_profil
|
|
|
9176
9178
|
} catch (error) {}
|
|
9177
9179
|
};
|
|
9178
9180
|
|
|
9181
|
+
// AI screening for a marketplace publish. Receives plain-JSON context from
|
|
9182
|
+
// marketplace_module (zod schemas cannot cross the broker, so the prompt +
|
|
9183
|
+
// structured schema must live here). Returns a structured verdict across the
|
|
9184
|
+
// approval rule dimensions. The caller (marketplace_approval_review) merges
|
|
9185
|
+
// this with deterministic account-standing checks and decides the final stat.
|
|
9186
|
+
export const marketplace_review_item = async function (req) {
|
|
9187
|
+
const { uid, item_type, item = {}, account_context = {}, account_name = '', model = _conf.default_ai_model, account_profile_info } = req || {};
|
|
9188
|
+
|
|
9189
|
+
const check_schema = z.object({
|
|
9190
|
+
flag: z.boolean().describe('true if this concern is present'),
|
|
9191
|
+
severity: z.enum(['none', 'low', 'medium', 'high']).describe('how serious the concern is'),
|
|
9192
|
+
note: z.string().describe('one short sentence of evidence; empty string if no concern'),
|
|
9193
|
+
});
|
|
9194
|
+
|
|
9195
|
+
const response_format = z.object({
|
|
9196
|
+
decision: z.enum(['approve', 'reject', 'manual_review']).describe('approve = safe to publish; reject = clear violation; manual_review = uncertain, needs a human'),
|
|
9197
|
+
confidence: z.number().min(0).max(100).describe('confidence in the decision, 0-100'),
|
|
9198
|
+
checks: z.object({
|
|
9199
|
+
ai_bot_activity: check_schema.describe('automated/bot publishing: high velocity, near-duplicate items, low-effort generated bulk'),
|
|
9200
|
+
commercial_undisclosed: check_schema.describe('undisclosed commercial intent: external paid links, lead-gen, off-platform payment, advertising inconsistent with declared price/category'),
|
|
9201
|
+
harmful_code: check_schema.describe('destructive ops, data exfiltration, crypto-miners, obfuscation, malicious network calls in the submitted code'),
|
|
9202
|
+
suspicious_activity: check_schema.describe('mismatched metadata, hidden payloads, evasion patterns'),
|
|
9203
|
+
malformed_account_name: check_schema.describe('gibberish, homoglyphs, embedded URLs, or impersonation in the account name'),
|
|
9204
|
+
spam_low_quality: check_schema.describe('spam, filler, or low-quality content with no real utility'),
|
|
9205
|
+
impersonation_trademark: check_schema.describe('impersonates a brand/person or abuses a trademark'),
|
|
9206
|
+
prohibited_content: check_schema.describe('adult, illegal, hateful, or otherwise disallowed content'),
|
|
9207
|
+
plagiarism_duplicate: check_schema.describe('appears copied from or duplicates an existing item'),
|
|
9208
|
+
secrets_pii_leak: check_schema.describe('hardcoded API keys, tokens, credentials, or exposed PII in the code'),
|
|
9209
|
+
}),
|
|
9210
|
+
reasons: z.array(z.string()).describe('short user-facing lines explaining the decision (empty if approved cleanly)'),
|
|
9211
|
+
});
|
|
9212
|
+
|
|
9213
|
+
const prompt = `You are the xuda marketplace moderation reviewer. Decide whether the following ${item_type} should be published to the public marketplace.
|
|
9214
|
+
|
|
9215
|
+
Account name: ${account_name}
|
|
9216
|
+
Account context (signals): ${JSON.stringify(account_context)}
|
|
9217
|
+
|
|
9218
|
+
Item:
|
|
9219
|
+
- name: ${item.name || ''}
|
|
9220
|
+
- description: ${item.description || ''}
|
|
9221
|
+
- category: ${item.category || ''}
|
|
9222
|
+
- tags: ${JSON.stringify(item.tags || [])}
|
|
9223
|
+
- price: ${item.price ?? 0}
|
|
9224
|
+
- code/content summary: ${item.code_summary || '(none provided)'}
|
|
9225
|
+
|
|
9226
|
+
Evaluate every check honestly. Set decision to:
|
|
9227
|
+
- "approve" when no medium/high concerns and the item is genuine and useful.
|
|
9228
|
+
- "reject" when there is a clear high-severity violation (harmful code, leaked secrets, prohibited content, obvious impersonation/spam, or clear bot abuse).
|
|
9229
|
+
- "manual_review" when concerns are present but ambiguous, or you are not confident.
|
|
9230
|
+
Keep "reasons" concise, factual, and user-facing (no internal jargon).`;
|
|
9231
|
+
|
|
9232
|
+
const ret = await submit_chat_gpt_prompt({
|
|
9233
|
+
uid,
|
|
9234
|
+
prompt,
|
|
9235
|
+
model,
|
|
9236
|
+
response_format,
|
|
9237
|
+
metadata: { func: 'marketplace_review_item', item_type },
|
|
9238
|
+
account_profile_info,
|
|
9239
|
+
});
|
|
9240
|
+
|
|
9241
|
+
if (ret.code < 0) {
|
|
9242
|
+
return { code: ret.code, data: ret.data };
|
|
9243
|
+
}
|
|
9244
|
+
|
|
9245
|
+
try {
|
|
9246
|
+
const verdict = JSON.parse(ret.data);
|
|
9247
|
+
return { code: 1, data: { ...verdict, model } };
|
|
9248
|
+
} catch (err) {
|
|
9249
|
+
return { code: -1, data: `marketplace_review_item parse error: ${err.message}` };
|
|
9250
|
+
}
|
|
9251
|
+
};
|
|
9252
|
+
|
|
9179
9253
|
export const is_business_contact = async function (uid, email, name, subject, body, account_profile_info) {
|
|
9180
9254
|
let prompt = `detect if the email "${email}", subject "${subject}", name "${name}", or body "${body}" is business or personal. Return true if it is a business , false otherwise.
|
|
9181
9255
|
|
|
@@ -14254,7 +14328,7 @@ export const get_widget_embed_snippet = async function (req, job_id, headers) {
|
|
|
14254
14328
|
};
|
|
14255
14329
|
|
|
14256
14330
|
const _widget_signup_core = async function (req, job_id, headers, opts) {
|
|
14257
|
-
const { profile_id, email, name, widget_origin, google_sub, signup_method } = req;
|
|
14331
|
+
const { profile_id, email, name, widget_origin, google_sub, signup_method, visitor_lang } = req;
|
|
14258
14332
|
const [owner_uid_part, pid_part] = String(profile_id || '').split('.');
|
|
14259
14333
|
if (!owner_uid_part || !pid_part) return { code: -404, data: 'invalid_profile' };
|
|
14260
14334
|
|
|
@@ -14266,19 +14340,19 @@ const _widget_signup_core = async function (req, job_id, headers, opts) {
|
|
|
14266
14340
|
if (ap_doc.widget_enabled === false) return { code: -404, data: 'widget_disabled' };
|
|
14267
14341
|
|
|
14268
14342
|
const visitor_uid = await _widget_find_or_create_visitor(email, name, google_sub);
|
|
14269
|
-
return await _widget_signup_finalize(visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers);
|
|
14343
|
+
return await _widget_signup_finalize(visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers, visitor_lang);
|
|
14270
14344
|
};
|
|
14271
14345
|
|
|
14272
14346
|
export const widget_signup = async function (req, job_id, headers) {
|
|
14273
14347
|
try {
|
|
14274
|
-
let { profile_id, email, name, widget_origin } = req;
|
|
14348
|
+
let { profile_id, email, name, widget_origin, visitor_lang } = req;
|
|
14275
14349
|
if (typeof email !== 'string' || typeof name !== 'string') return { code: -1, data: 'invalid_input' };
|
|
14276
14350
|
email = email.toLowerCase().trim();
|
|
14277
14351
|
name = name.trim();
|
|
14278
14352
|
if (!email || !name) return { code: -1, data: 'invalid_input' };
|
|
14279
14353
|
if (!WIDGET_EMAIL_RE.test(email)) return { code: -1, data: 'invalid_email' };
|
|
14280
14354
|
|
|
14281
|
-
return await _widget_signup_core({ profile_id, email, name, widget_origin, signup_method: 'email' }, job_id, headers);
|
|
14355
|
+
return await _widget_signup_core({ profile_id, email, name, widget_origin, visitor_lang, signup_method: 'email' }, job_id, headers);
|
|
14282
14356
|
} catch (err) {
|
|
14283
14357
|
return { code: -1, data: err.message || String(err) };
|
|
14284
14358
|
}
|
|
@@ -14286,7 +14360,7 @@ export const widget_signup = async function (req, job_id, headers) {
|
|
|
14286
14360
|
|
|
14287
14361
|
export const widget_signup_google = async function (req, job_id, headers) {
|
|
14288
14362
|
try {
|
|
14289
|
-
const { profile_id, id_token, widget_origin } = req;
|
|
14363
|
+
const { profile_id, id_token, widget_origin, visitor_lang } = req;
|
|
14290
14364
|
if (!id_token || typeof id_token !== 'string') return { code: -1, data: 'missing_id_token' };
|
|
14291
14365
|
|
|
14292
14366
|
let payload;
|
|
@@ -14302,13 +14376,13 @@ export const widget_signup_google = async function (req, job_id, headers) {
|
|
|
14302
14376
|
const name = String(payload.name || payload.email?.split('@')[0] || 'Friend').trim();
|
|
14303
14377
|
if (!email) return { code: -1, data: 'no_email_in_token' };
|
|
14304
14378
|
|
|
14305
|
-
return await _widget_signup_core({ profile_id, email, name, widget_origin, google_sub: payload.sub, signup_method: 'google' }, job_id, headers);
|
|
14379
|
+
return await _widget_signup_core({ profile_id, email, name, widget_origin, visitor_lang, google_sub: payload.sub, signup_method: 'google' }, job_id, headers);
|
|
14306
14380
|
} catch (err) {
|
|
14307
14381
|
return { code: -1, data: err.message || String(err) };
|
|
14308
14382
|
}
|
|
14309
14383
|
};
|
|
14310
14384
|
|
|
14311
|
-
const _widget_signup_finalize = async function (visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers) {
|
|
14385
|
+
const _widget_signup_finalize = async function (visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers, visitor_lang) {
|
|
14312
14386
|
try {
|
|
14313
14387
|
let team_req_doc = await _widget_find_existing_team_req(visitor_uid, canonical_profile_id);
|
|
14314
14388
|
let assigned_uid;
|
|
@@ -14372,12 +14446,20 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
|
|
|
14372
14446
|
|
|
14373
14447
|
const widget_token = `wds_${crypto.randomUUID().replace(/-/g, '')}`;
|
|
14374
14448
|
const now = Date.now();
|
|
14449
|
+
// Normalize visitor_lang to a short tag (e.g. "ja-JP" → "ja"). The
|
|
14450
|
+
// auto-reply agent reads this from the session to respond in the
|
|
14451
|
+
// visitor's language. Empty / missing → English fallback at reply time.
|
|
14452
|
+
const visitor_lang_base = String(visitor_lang || '')
|
|
14453
|
+
.toLowerCase()
|
|
14454
|
+
.split('-')[0]
|
|
14455
|
+
.slice(0, 8);
|
|
14375
14456
|
const session_doc = {
|
|
14376
14457
|
_id: widget_token,
|
|
14377
14458
|
docType: 'widget_session',
|
|
14378
14459
|
stat: 2,
|
|
14379
14460
|
state,
|
|
14380
14461
|
visitor_uid,
|
|
14462
|
+
visitor_lang: visitor_lang_base || '',
|
|
14381
14463
|
team_req_id: team_req_doc._id,
|
|
14382
14464
|
assigned_uid,
|
|
14383
14465
|
owner_uid: canonical_owner_uid,
|
|
@@ -14396,6 +14478,12 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
|
|
|
14396
14478
|
const assigned_avatar = assigned_info_ret?.data?.profile_picture || assigned_info_ret?.data?.profile_avatar || '';
|
|
14397
14479
|
const greeting = ap_doc?.widget_config?.greeting || WIDGET_DEFAULT_GREETING;
|
|
14398
14480
|
|
|
14481
|
+
// Surface the visitor's first name so the chat widget can personalize the
|
|
14482
|
+
// greeting on the page ("Hi {visitor_first_name}, I'm Valentina..."). The
|
|
14483
|
+
// widget reads this from the persisted session and replaces the
|
|
14484
|
+
// {visitor_name} placeholder client-side.
|
|
14485
|
+
const visitor_first_name = _widget_split_name(name).first_name || '';
|
|
14486
|
+
|
|
14399
14487
|
return {
|
|
14400
14488
|
code: 1,
|
|
14401
14489
|
data: {
|
|
@@ -14405,6 +14493,7 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
|
|
|
14405
14493
|
assigned_avatar,
|
|
14406
14494
|
greeting,
|
|
14407
14495
|
conversation_id,
|
|
14496
|
+
visitor_first_name,
|
|
14408
14497
|
signup_method: signup_method || 'email',
|
|
14409
14498
|
account_active: signup_method === 'google',
|
|
14410
14499
|
signin_url: signup_method === 'google' ? `https://${_conf.domain || 'xuda.ai'}/dashboard/login` : undefined,
|
package/index_ms.mjs
CHANGED
|
@@ -260,3 +260,7 @@ export const widget_send_message = async function (...args) {
|
|
|
260
260
|
export const widget_get_messages = async function (...args) {
|
|
261
261
|
return await broker.send_to_queue("widget_get_messages", ...args);
|
|
262
262
|
};
|
|
263
|
+
|
|
264
|
+
export const marketplace_review_item = async function (...args) {
|
|
265
|
+
return await broker.send_to_queue("marketplace_review_item", ...args);
|
|
266
|
+
};
|