@shulex/cxclawctl 0.1.4 → 0.1.6
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/capabilities.lock.json +2 -2
- package/dist/cli.js +553 -69
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { readFile as
|
|
4
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
5
5
|
|
|
6
6
|
// src/auth.ts
|
|
7
7
|
import open from "open";
|
|
@@ -9,7 +9,7 @@ import open from "open";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "@shulex/cxclawctl",
|
|
12
|
-
version: "0.1.
|
|
12
|
+
version: "0.1.6",
|
|
13
13
|
description: "Solvea CXClaw CLI and Codex plugin runtime",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
@@ -38,6 +38,7 @@ var package_default = {
|
|
|
38
38
|
},
|
|
39
39
|
dependencies: {
|
|
40
40
|
"@alicloud/log": "^1.2.6",
|
|
41
|
+
"@aws-sdk/client-s3": "^3.1156.0",
|
|
41
42
|
"@napi-rs/keyring": "^2.1.2",
|
|
42
43
|
"compare-versions": "^6.1.1",
|
|
43
44
|
exceljs: "^4.4.0",
|
|
@@ -189,13 +190,32 @@ function positionals(args) {
|
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
// src/business.ts
|
|
192
|
-
import { dirname, resolve } from "path";
|
|
193
|
+
import { dirname as dirname2, resolve } from "path";
|
|
193
194
|
import { fileURLToPath } from "url";
|
|
194
195
|
|
|
195
196
|
// src/solvea/commands.ts
|
|
196
|
-
import { readFile as
|
|
197
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
197
198
|
|
|
198
199
|
// src/solvea/client.ts
|
|
200
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
201
|
+
import { basename, dirname, extname } from "path";
|
|
202
|
+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
203
|
+
var KNOWLEDGE_TYPES = /* @__PURE__ */ new Set(["QA", "LEARN_SELF", "FILE", "WEBSITE", "PRODUCT"]);
|
|
204
|
+
var asObject = (value, message) => {
|
|
205
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(message);
|
|
206
|
+
return value;
|
|
207
|
+
};
|
|
208
|
+
var asPositiveInt = (value, message) => {
|
|
209
|
+
const number = Number(value);
|
|
210
|
+
if (!Number.isSafeInteger(number) || number <= 0) throw new Error(message);
|
|
211
|
+
return number;
|
|
212
|
+
};
|
|
213
|
+
var knowledgeType = (value) => {
|
|
214
|
+
const normalized = String(value ?? "").toUpperCase();
|
|
215
|
+
if (!KNOWLEDGE_TYPES.has(normalized)) throw new Error("knowledge type must be QA, LEARN_SELF, FILE, WEBSITE, or PRODUCT");
|
|
216
|
+
return normalized;
|
|
217
|
+
};
|
|
218
|
+
var isolationKnowledgeType = (value) => ({ QA: "QA", LEARN_SELF: "QA", FILE: "DOCUMENT", WEBSITE: "DOCUMENT", PRODUCT: "PRODUCT" })[knowledgeType(value)];
|
|
199
219
|
var SolveaClient = class {
|
|
200
220
|
constructor(session) {
|
|
201
221
|
this.session = session;
|
|
@@ -304,14 +324,25 @@ var SolveaClient = class {
|
|
|
304
324
|
return this.post("/api_v2/gpt/ap/createAgent", { projectId: Number(await this.projectId()), ...data3 });
|
|
305
325
|
}
|
|
306
326
|
async updateAgent(data3) {
|
|
307
|
-
|
|
327
|
+
const payload = { ...data3, projectId: Number(await this.projectId()) };
|
|
328
|
+
if (payload.agentId === void 0 && payload.id !== void 0) payload.agentId = payload.id;
|
|
329
|
+
return this.post("/api_v2/gpt/ap/updateAgent", payload);
|
|
308
330
|
}
|
|
309
331
|
async updateChatflow(agentId, graph, publish = false) {
|
|
310
332
|
const current = await this.getAgent(agentId);
|
|
311
333
|
const agent = Array.isArray(current) ? current[0] : current;
|
|
312
334
|
if (!agent || agent.type !== "CHAT_FLOW") throw new Error(`Agent ${agentId} is not CHAT_FLOW`);
|
|
335
|
+
if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) throw new Error("chatFlowGraph must be an object with nodes and edges");
|
|
313
336
|
const payload = { ...agent, agentId: agent.id ?? Number(agentId), meta: { ...agent.meta ?? {}, chatFlowGraph: graph } };
|
|
314
|
-
|
|
337
|
+
if (publish) {
|
|
338
|
+
delete payload.backupType;
|
|
339
|
+
delete payload.meta.backupType;
|
|
340
|
+
delete payload.meta.draft_flow_updatetime;
|
|
341
|
+
} else {
|
|
342
|
+
payload.backupType = "DRAFT";
|
|
343
|
+
payload.meta.draft_flow_updatetime = Date.now();
|
|
344
|
+
}
|
|
345
|
+
return this.updateAgent(payload);
|
|
315
346
|
}
|
|
316
347
|
async listPlugins() {
|
|
317
348
|
return this.post("/api_v2/gpt/ap/queryPlugin", { projectId: Number(await this.projectId()), needRelation: false });
|
|
@@ -323,7 +354,9 @@ var SolveaClient = class {
|
|
|
323
354
|
return this.post("/api_v2/gpt/ap/createPlugin", { projectId: Number(await this.projectId()), ...data3 });
|
|
324
355
|
}
|
|
325
356
|
async updatePlugin(data3) {
|
|
326
|
-
|
|
357
|
+
const payload = { projectId: Number(await this.projectId()), ...data3 };
|
|
358
|
+
if (payload.pluginId === void 0 && payload.id !== void 0) payload.pluginId = payload.id;
|
|
359
|
+
return this.post("/api_v2/gpt/ap/updatePlugin", payload);
|
|
327
360
|
}
|
|
328
361
|
async deletePlugin(id) {
|
|
329
362
|
return this.post("/api_v2/gpt/ap/deletePlugin", { projectId: Number(await this.projectId()), pluginId: Number(id) });
|
|
@@ -375,11 +408,48 @@ var SolveaClient = class {
|
|
|
375
408
|
}
|
|
376
409
|
async chat(data3) {
|
|
377
410
|
const bot = String(data3.botId ?? await this.botId());
|
|
378
|
-
return this.post(`/api_v2/gpt/bot/${bot}/chat`, { botId: Number(bot), projectId: Number(await this.projectId()), option: { debug: true }
|
|
411
|
+
return this.post(`/api_v2/gpt/bot/${bot}/chat`, { ...data3, botId: Number(bot), projectId: Number(await this.projectId()), option: data3.option ?? { debug: true } });
|
|
412
|
+
}
|
|
413
|
+
async chatStream(data3) {
|
|
414
|
+
const bot = String(data3.botId ?? await this.botId());
|
|
415
|
+
const body = { ...data3, botId: Number(bot), projectId: Number(await this.projectId()), option: data3.option ?? { debug: true } };
|
|
416
|
+
const cookie = this.env === "prod" ? "_token" : "_token_staging";
|
|
417
|
+
const response = await fetch(`${this.baseUrl}/api_v2/gpt/bot/${encodeURIComponent(bot)}/chat/stream`, {
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: {
|
|
420
|
+
accept: "text/event-stream",
|
|
421
|
+
"content-type": "application/json",
|
|
422
|
+
origin: this.baseUrl,
|
|
423
|
+
referer: `${this.baseUrl}/chatbot`,
|
|
424
|
+
cookie: `${cookie}=${this.session.tenant.businessToken}`,
|
|
425
|
+
"x-cxclaw-client-version": process.env.CXCLAW_PLUGIN_VERSION ?? "unknown"
|
|
426
|
+
},
|
|
427
|
+
body: JSON.stringify(body),
|
|
428
|
+
signal: AbortSignal.timeout(3e4)
|
|
429
|
+
});
|
|
430
|
+
const text = await response.text();
|
|
431
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} POST /api_v2/gpt/bot/${bot}/chat/stream: ${text}`);
|
|
432
|
+
let finalAction;
|
|
433
|
+
const content = [];
|
|
434
|
+
for (const line of text.split(/\r?\n/)) {
|
|
435
|
+
if (!line.startsWith("data:")) continue;
|
|
436
|
+
const raw = line.slice(5).trim();
|
|
437
|
+
if (!raw) continue;
|
|
438
|
+
let event;
|
|
439
|
+
try {
|
|
440
|
+
event = asObject(JSON.parse(raw), "stream event must be an object");
|
|
441
|
+
} catch {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (event.errno !== void 0 && ![0, "0"].includes(event.errno)) throw new Error(`API error POST /api_v2/gpt/bot/${bot}/chat/stream: ${String(event.errmsg ?? event.message ?? event.errno)}`);
|
|
445
|
+
if (event.content) content.push(String(event.content));
|
|
446
|
+
if (event.isEnd) finalAction = event.data && typeof event.data === "object" && !Array.isArray(event.data) ? event.data : event;
|
|
447
|
+
}
|
|
448
|
+
return finalAction ?? { content: content.join(""), isEnd: true };
|
|
379
449
|
}
|
|
380
450
|
async chatAsync(data3) {
|
|
381
451
|
const bot = String(data3.botId ?? await this.botId());
|
|
382
|
-
return this.post(`/api_v2/gpt/bot/${bot}/chat/async`, { botId: Number(bot), projectId: Number(await this.projectId()), option: { debug: true }
|
|
452
|
+
return this.post(`/api_v2/gpt/bot/${bot}/chat/async`, { ...data3, botId: Number(bot), projectId: Number(await this.projectId()), option: data3.option ?? { debug: true } });
|
|
383
453
|
}
|
|
384
454
|
async asyncResult(receipt, botId) {
|
|
385
455
|
const bot = botId ?? await this.botId();
|
|
@@ -410,10 +480,10 @@ var SolveaClient = class {
|
|
|
410
480
|
return this.post(`/api_v2/gpt/project/${await this.projectId()}/strategy?v4=true`, data3);
|
|
411
481
|
}
|
|
412
482
|
async updateStrategy(id, data3) {
|
|
413
|
-
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, data3);
|
|
483
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, data3, { v4: "true" });
|
|
414
484
|
}
|
|
415
485
|
async toggleStrategy(id, enabled2) {
|
|
416
|
-
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}
|
|
486
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, { status: enabled2 ? "ENABLED" : "DISABLED" }, { v4: "true" });
|
|
417
487
|
}
|
|
418
488
|
async qualityStatus() {
|
|
419
489
|
return this.get("/api_v2/intelli/inspection/ticket/status");
|
|
@@ -494,11 +564,212 @@ var SolveaClient = class {
|
|
|
494
564
|
async getKnowledge(id) {
|
|
495
565
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/ku/${encodeURIComponent(id)}`);
|
|
496
566
|
}
|
|
567
|
+
async getDocument(id) {
|
|
568
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/document/${encodeURIComponent(id)}`);
|
|
569
|
+
}
|
|
570
|
+
async getProductKnowledge(id) {
|
|
571
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/ku_product/${encodeURIComponent(id)}`);
|
|
572
|
+
}
|
|
573
|
+
async getKnowledgeByType(id, type) {
|
|
574
|
+
const normalized = knowledgeType(type);
|
|
575
|
+
if (normalized === "FILE" || normalized === "WEBSITE") return this.getDocument(id);
|
|
576
|
+
if (normalized === "PRODUCT") return this.getProductKnowledge(id);
|
|
577
|
+
return this.getKnowledge(id);
|
|
578
|
+
}
|
|
497
579
|
async updateKnowledge(id, data3) {
|
|
498
|
-
|
|
580
|
+
const payload = { ...data3 };
|
|
581
|
+
if (payload.directoryId === void 0) {
|
|
582
|
+
const current = asObject(await this.getKnowledge(id), `could not read knowledge ${id} before update`);
|
|
583
|
+
if (current.directoryId !== void 0) payload.directoryId = current.directoryId;
|
|
584
|
+
}
|
|
585
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku`, { ...payload, kuId: asPositiveInt(id, "knowledge id must be a positive integer") });
|
|
499
586
|
}
|
|
500
587
|
async uploadQa(items) {
|
|
501
|
-
|
|
588
|
+
if (!Array.isArray(items) || !items.length) throw new Error("QA items must be a non-empty array");
|
|
589
|
+
const payload = items.map((value, index) => {
|
|
590
|
+
const item = asObject(value, `QA item #${index + 1} must be an object`);
|
|
591
|
+
const title = String(item.title ?? "").trim();
|
|
592
|
+
const content = String(item.content ?? "").trim();
|
|
593
|
+
if (!title) throw new Error(`QA item #${index + 1} requires title`);
|
|
594
|
+
if (!content) throw new Error(`QA item #${index + 1} requires content`);
|
|
595
|
+
return {
|
|
596
|
+
title,
|
|
597
|
+
content,
|
|
598
|
+
forceIndex: item.forceIndex ?? true,
|
|
599
|
+
type: "QA",
|
|
600
|
+
published: item.published ?? true,
|
|
601
|
+
taskType: item.taskType ?? "MANUAL",
|
|
602
|
+
metadata: item.metadata ?? { tags: [] },
|
|
603
|
+
directoryId: item.directoryId ?? "",
|
|
604
|
+
isVisible: item.isVisible ?? true
|
|
605
|
+
};
|
|
606
|
+
});
|
|
607
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/kus`, payload);
|
|
608
|
+
}
|
|
609
|
+
async createDocuments(documents, directoryId, taskType) {
|
|
610
|
+
if (!documents.length) throw new Error("documents must be a non-empty array");
|
|
611
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/documents`, {
|
|
612
|
+
documents,
|
|
613
|
+
published: true,
|
|
614
|
+
taskType,
|
|
615
|
+
taskUrl: "MANUAL",
|
|
616
|
+
isFullSiteCrawl: false,
|
|
617
|
+
metadata: { tags: [] },
|
|
618
|
+
directoryId,
|
|
619
|
+
isVisible: true
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
async uploadTempFile(filePath, forKnowledge = false) {
|
|
623
|
+
const credentialPath = forKnowledge ? `${this.qamPrefix}/project/${await this.projectId()}/sts_credentials` : `/api_v2/gpt/project/${await this.projectId()}/sts_credentials`;
|
|
624
|
+
const credentials = asObject(
|
|
625
|
+
await this.get(credentialPath),
|
|
626
|
+
"STS credentials must be an object"
|
|
627
|
+
);
|
|
628
|
+
const required = ["accessKeyId", "secretAccessKey", "sessionToken", "region", "bucketName", "path"];
|
|
629
|
+
const missing = required.filter((key2) => !credentials[key2]);
|
|
630
|
+
if (missing.length) throw new Error(`knowledge STS credentials missing fields: ${missing.join(", ")}`);
|
|
631
|
+
const key = `${String(credentials.path)}${basename(filePath)}`;
|
|
632
|
+
const client = new S3Client({
|
|
633
|
+
region: String(credentials.region),
|
|
634
|
+
credentials: {
|
|
635
|
+
accessKeyId: String(credentials.accessKeyId),
|
|
636
|
+
secretAccessKey: String(credentials.secretAccessKey),
|
|
637
|
+
sessionToken: String(credentials.sessionToken)
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
const contentTypes = {
|
|
641
|
+
".doc": "application/msword",
|
|
642
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
643
|
+
".pdf": "application/pdf"
|
|
644
|
+
};
|
|
645
|
+
await client.send(new PutObjectCommand({
|
|
646
|
+
Bucket: String(credentials.bucketName),
|
|
647
|
+
Key: key,
|
|
648
|
+
Body: await readFile(filePath),
|
|
649
|
+
ContentType: contentTypes[extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
650
|
+
}));
|
|
651
|
+
return key;
|
|
652
|
+
}
|
|
653
|
+
async uploadKnowledgeFile(filePath) {
|
|
654
|
+
return this.uploadTempFile(filePath, true);
|
|
655
|
+
}
|
|
656
|
+
async downloadFile(path, target, query2) {
|
|
657
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
658
|
+
for (const [key, value] of Object.entries(query2 ?? {})) url.searchParams.set(key, String(value));
|
|
659
|
+
const cookie = this.env === "prod" ? "_token" : "_token_staging";
|
|
660
|
+
const response = await fetch(url, {
|
|
661
|
+
headers: { accept: "*/*", origin: this.baseUrl, referer: `${this.baseUrl}/chatbot`, cookie: `${cookie}=${this.session.tenant.businessToken}` },
|
|
662
|
+
signal: AbortSignal.timeout(3e4)
|
|
663
|
+
});
|
|
664
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} GET ${path}: ${await response.text()}`);
|
|
665
|
+
await mkdir(dirname(target), { recursive: true });
|
|
666
|
+
await writeFile(target, new Uint8Array(await response.arrayBuffer()));
|
|
667
|
+
return target;
|
|
668
|
+
}
|
|
669
|
+
async downloadLabelTemplate(templateType, target) {
|
|
670
|
+
if (!["VOC_LABEL", "VOC_LABEL_INIT"].includes(templateType)) throw new Error("template type must be VOC_LABEL or VOC_LABEL_INIT");
|
|
671
|
+
return this.downloadFile(`/api_v2/gpt/label/project/${await this.projectId()}/downloadLabelTemplate`, target, { type: templateType });
|
|
672
|
+
}
|
|
673
|
+
async downloadLabels(treeId, target) {
|
|
674
|
+
return this.downloadFile(`/api_v2/gpt/label/project/${await this.projectId()}/downloadLabel`, target, { treeId: asPositiveInt(treeId, "tree id must be a positive integer") });
|
|
675
|
+
}
|
|
676
|
+
async uploadLabelFile(treeId, filePath, init = false) {
|
|
677
|
+
const fileKey = await this.uploadTempFile(filePath);
|
|
678
|
+
const result = await this.post(`/api_v2/gpt/label/project/${await this.projectId()}/uploadLabel`, {
|
|
679
|
+
treeId: asPositiveInt(treeId, "tree id must be a positive integer"),
|
|
680
|
+
file: fileKey,
|
|
681
|
+
...init ? { uploadWay: "INIT" } : {}
|
|
682
|
+
});
|
|
683
|
+
return { fileKey, result };
|
|
684
|
+
}
|
|
685
|
+
async updateKus(value) {
|
|
686
|
+
const source = Array.isArray(value) ? value : asObject(value, "batch knowledge update must be an array or object with list").list;
|
|
687
|
+
if (!Array.isArray(source) || !source.length) throw new Error("batch knowledge update requires a non-empty array");
|
|
688
|
+
const list = [];
|
|
689
|
+
for (const [index, value2] of source.entries()) {
|
|
690
|
+
const row = { ...asObject(value2, `batch knowledge item #${index + 1} must be an object`) };
|
|
691
|
+
row.kuId = asPositiveInt(row.kuId ?? row.id, `batch knowledge item #${index + 1} requires kuId or id`);
|
|
692
|
+
delete row.id;
|
|
693
|
+
if (row.directoryId === void 0) {
|
|
694
|
+
const current = asObject(await this.getKnowledge(String(row.kuId)), `could not read knowledge ${row.kuId} before update`);
|
|
695
|
+
if (current.directoryId !== void 0) row.directoryId = current.directoryId;
|
|
696
|
+
}
|
|
697
|
+
list.push(row);
|
|
698
|
+
}
|
|
699
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/kus`, { list });
|
|
700
|
+
}
|
|
701
|
+
async updateDocumentContent(id, type, changes) {
|
|
702
|
+
const normalized = knowledgeType(type);
|
|
703
|
+
if (normalized !== "FILE" && normalized !== "WEBSITE") throw new Error("document type must be FILE or WEBSITE");
|
|
704
|
+
const unknown = Object.keys(changes).filter((key) => !["fileText", "filePath", "title"].includes(key));
|
|
705
|
+
if (unknown.length) throw new Error(`unsupported document content fields: ${unknown.sort().join(", ")}`);
|
|
706
|
+
if (changes.fileText === void 0 && changes.filePath === void 0) throw new Error("document update requires fileText or filePath");
|
|
707
|
+
const documentId = asPositiveInt(id, "document id must be a positive integer");
|
|
708
|
+
const current = asObject(await this.getDocument(id), `could not read document ${id} before update`);
|
|
709
|
+
const payload = {
|
|
710
|
+
type: normalized,
|
|
711
|
+
directoryId: current.directoryId,
|
|
712
|
+
fileName: current.fileName ?? current.name ?? current.title,
|
|
713
|
+
title: current.title ?? current.fileName ?? current.name,
|
|
714
|
+
metadata: current.metadata && typeof current.metadata === "object" && !Array.isArray(current.metadata) ? current.metadata : {},
|
|
715
|
+
...changes,
|
|
716
|
+
knowledgeIds: [documentId]
|
|
717
|
+
};
|
|
718
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/document/${documentId}/contentV2`, payload);
|
|
719
|
+
}
|
|
720
|
+
async createProductKnowledge(data3) {
|
|
721
|
+
if (!data3.title || !data3.spuUrl) throw new Error("product knowledge requires title and spuUrl");
|
|
722
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/ku_product`, {
|
|
723
|
+
source: "MANUAL",
|
|
724
|
+
published: true,
|
|
725
|
+
isVisible: true,
|
|
726
|
+
metadata: { tags: [] },
|
|
727
|
+
...data3
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
async updateProductKnowledge(id, changes) {
|
|
731
|
+
const allowed = /* @__PURE__ */ new Set(["productId", "tags", "documentId", "title", "handle", "vendor", "category", "brand", "productStatus", "country", "spuUrl", "image", "platform", "currency", "price", "compareAtPrice", "describe", "source", "skuList", "directoryId", "published", "metadata", "specialTagMap", "isVisible", "optionName"]);
|
|
732
|
+
const unknown = Object.keys(changes).filter((key) => !allowed.has(key) && key !== "id" && key !== "kuProductId");
|
|
733
|
+
if (unknown.length) throw new Error(`unsupported product fields: ${unknown.sort().join(", ")}`);
|
|
734
|
+
const current = asObject(await this.getProductKnowledge(id), `could not read product knowledge ${id} before update`);
|
|
735
|
+
const payload = {};
|
|
736
|
+
for (const key of allowed) if (current[key] !== void 0) payload[key] = current[key];
|
|
737
|
+
for (const key of allowed) if (changes[key] !== void 0) payload[key] = changes[key];
|
|
738
|
+
const missing = ["title", "describe", "spuUrl", "image", "source", "published", "isVisible"].filter((key) => payload[key] === void 0 || payload[key] === "");
|
|
739
|
+
if (missing.length) throw new Error(`product detail is incomplete: ${missing.join(", ")}`);
|
|
740
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku_product`, { ...payload, kuProductId: asPositiveInt(id, "product id must be a positive integer") });
|
|
741
|
+
}
|
|
742
|
+
async updateKuProducts(value) {
|
|
743
|
+
const source = Array.isArray(value) ? value : asObject(value, "batch product update must be an array or object with list").list;
|
|
744
|
+
if (!Array.isArray(source) || !source.length) throw new Error("batch product update requires a non-empty array");
|
|
745
|
+
const list = source.map((value2, index) => {
|
|
746
|
+
const row = asObject(value2, `batch product item #${index + 1} must be an object`);
|
|
747
|
+
const entry2 = { id: asPositiveInt(row.id ?? row.kuProductId, `batch product item #${index + 1} requires id or kuProductId`) };
|
|
748
|
+
if (row.mainPush !== void 0) entry2.mainPush = Boolean(row.mainPush);
|
|
749
|
+
if (row.enable !== void 0) entry2.enable = Boolean(row.enable);
|
|
750
|
+
else if (row.published !== void 0) entry2.enable = Boolean(row.published);
|
|
751
|
+
return entry2;
|
|
752
|
+
});
|
|
753
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku_products`, { list });
|
|
754
|
+
}
|
|
755
|
+
async operateKnowledge(value, confirmDelete = false) {
|
|
756
|
+
const source = Array.isArray(value) ? value : asObject(value, "knowledge operation must be an array or object with requestList").requestList;
|
|
757
|
+
if (!Array.isArray(source) || !source.length) throw new Error("knowledge operation requires a non-empty array");
|
|
758
|
+
const supportedOperations = /* @__PURE__ */ new Set(["DELETE", "MOVE", "PUBLISH", "UNPUBLISH", "VISIBLE", "INVISIBLE"]);
|
|
759
|
+
const requestList = [];
|
|
760
|
+
for (const [index, value2] of source.entries()) {
|
|
761
|
+
const row = asObject(value2, `knowledge operation #${index + 1} must be an object`);
|
|
762
|
+
const type = knowledgeType(row.knowledgeType);
|
|
763
|
+
const operateType = String(row.operateType ?? "").toUpperCase();
|
|
764
|
+
if (!supportedOperations.has(operateType)) throw new Error(`unsupported knowledge operation: ${operateType}`);
|
|
765
|
+
const knowledgeId = asPositiveInt(row.knowledgeId, `knowledge operation #${index + 1} requires knowledgeId`);
|
|
766
|
+
await this.getKnowledgeByType(String(knowledgeId), type);
|
|
767
|
+
const normalized = { knowledgeType: type, knowledgeId, operateType };
|
|
768
|
+
if (operateType === "MOVE") normalized.directoryId = asPositiveInt(row.directoryId, "MOVE requires directoryId");
|
|
769
|
+
requestList.push(normalized);
|
|
770
|
+
}
|
|
771
|
+
if (requestList.some((item) => item.operateType === "DELETE") && !confirmDelete) throw new Error("DELETE requires --confirm-delete after explicit user acknowledgement");
|
|
772
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/updateKnowledge`, { requestList });
|
|
502
773
|
}
|
|
503
774
|
async listDirectories() {
|
|
504
775
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/directories`, { keyword: "", kuPublished: "" });
|
|
@@ -515,12 +786,111 @@ var SolveaClient = class {
|
|
|
515
786
|
async knowledgeTags(keyword = "") {
|
|
516
787
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/searchKeywordAndIntents`, { keyword });
|
|
517
788
|
}
|
|
789
|
+
async moveDirectory(id, parentId) {
|
|
790
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/directory/move`, { currentId: asPositiveInt(id, "directory id must be a positive integer"), parentId: Number(parentId) });
|
|
791
|
+
}
|
|
792
|
+
async getKnowledgeIsolationKeywords(id, type) {
|
|
793
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/knowledge/isolation_keywords`, { knowledgeType: isolationKnowledgeType(type), knowledgeId: asPositiveInt(id, "knowledge id must be a positive integer") });
|
|
794
|
+
}
|
|
795
|
+
async batchUpdateIsolationKeywordBindings(data3) {
|
|
796
|
+
const items = data3.items ?? [], directoryIds = data3.directoryIds ?? [], keywordIds = data3.keywordIds ?? [];
|
|
797
|
+
if (!Array.isArray(items) || !Array.isArray(directoryIds) || !Array.isArray(keywordIds)) throw new Error("items, directoryIds and keywordIds must be arrays");
|
|
798
|
+
if (!items.length && !directoryIds.length && !keywordIds.length) throw new Error("isolation keyword binding update has no targets or keyword IDs");
|
|
799
|
+
if (items.length && directoryIds.length) throw new Error("submit knowledge items and directory IDs in separate binding updates");
|
|
800
|
+
const normalizedItems = items.map((value, index) => {
|
|
801
|
+
const row = asObject(value, `isolation binding item #${index + 1} must be an object`);
|
|
802
|
+
if (!Array.isArray(row.keywordIds)) throw new Error("items[].keywordIds must be an array");
|
|
803
|
+
return {
|
|
804
|
+
knowledgeType: isolationKnowledgeType(row.knowledgeType ?? "QA"),
|
|
805
|
+
knowledgeId: asPositiveInt(row.knowledgeId, "items[].knowledgeId must be a positive integer"),
|
|
806
|
+
keywordIds: [...new Set(row.keywordIds.map((value2) => asPositiveInt(value2, "items[].keywordIds must contain positive integer IDs")))]
|
|
807
|
+
};
|
|
808
|
+
});
|
|
809
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/knowledge/isolation_keywords/batch`, {
|
|
810
|
+
items: normalizedItems,
|
|
811
|
+
directoryIds: [...new Set(directoryIds.map((value) => asPositiveInt(value, "directoryIds must contain positive integer IDs")))],
|
|
812
|
+
keywordIds: [...new Set(keywordIds.map((value) => asPositiveInt(value, "keywordIds must contain positive integer IDs")))]
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
async applyIsolationKeywordBindings(data3) {
|
|
816
|
+
const operation = String(data3.operation ?? "append").toLowerCase();
|
|
817
|
+
if (!["append", "remove", "replace"].includes(operation)) throw new Error("operation must be append, remove, or replace");
|
|
818
|
+
const items = data3.items ?? [], directoryIds = data3.directoryIds ?? [];
|
|
819
|
+
if (!Array.isArray(items) || !Array.isArray(directoryIds) || !items.length && !directoryIds.length) throw new Error("binding update requires knowledge items or directory IDs");
|
|
820
|
+
if (items.length && directoryIds.length) throw new Error("submit knowledge items and directory IDs in separate binding updates");
|
|
821
|
+
if (directoryIds.length && operation !== "replace") throw new Error("directory bindings require operation=replace");
|
|
822
|
+
if ((directoryIds.length || operation === "replace") && data3.confirmReplace !== true) throw new Error("binding replacement requires confirmReplace=true after explicit user confirmation");
|
|
823
|
+
const resolvedItems = [];
|
|
824
|
+
for (const [index, value] of items.entries()) {
|
|
825
|
+
const row = asObject(value, `isolation binding item #${index + 1} must be an object`);
|
|
826
|
+
const id = asPositiveInt(row.knowledgeId, "items[].knowledgeId must be a positive integer");
|
|
827
|
+
const type = knowledgeType(row.knowledgeType);
|
|
828
|
+
if (!Array.isArray(row.keywordIds)) throw new Error("items[].keywordIds must be an array");
|
|
829
|
+
const requestedIds = row.keywordIds.map((value2) => asPositiveInt(value2, "items[].keywordIds must contain positive integer IDs"));
|
|
830
|
+
await this.getKnowledgeByType(String(id), type);
|
|
831
|
+
const existingResponse = await this.getKnowledgeIsolationKeywords(String(id), type);
|
|
832
|
+
const existingRows = Array.isArray(existingResponse) ? existingResponse : existingResponse && typeof existingResponse === "object" && Array.isArray(existingResponse.list) ? existingResponse.list : [];
|
|
833
|
+
const existingIds = existingRows.map((value2) => asPositiveInt(asObject(value2, "existing isolation keyword binding must be an object").id, "existing isolation keyword binding has an invalid ID"));
|
|
834
|
+
const finalIds = operation === "append" ? [.../* @__PURE__ */ new Set([...existingIds, ...requestedIds])] : operation === "remove" ? existingIds.filter((id2) => !requestedIds.includes(id2)) : [...new Set(requestedIds)];
|
|
835
|
+
resolvedItems.push({ knowledgeType: type, knowledgeId: id, keywordIds: finalIds });
|
|
836
|
+
}
|
|
837
|
+
for (const value of directoryIds) await this.listKnowledge({ directoryId: asPositiveInt(value, "directoryIds must contain positive integer IDs"), pageIndex: 1, pageSize: 1 });
|
|
838
|
+
return this.batchUpdateIsolationKeywordBindings({ items: resolvedItems, directoryIds, keywordIds: data3.keywordIds ?? [] });
|
|
839
|
+
}
|
|
840
|
+
async editKnowledgeTags(data3) {
|
|
841
|
+
const knowledgeIds = data3.knowledgeIds, requestedTags = data3.tagList;
|
|
842
|
+
if (!Array.isArray(knowledgeIds) || !knowledgeIds.length) throw new Error("edit knowledge tags requires non-empty knowledgeIds");
|
|
843
|
+
if (!Array.isArray(requestedTags) || requestedTags.some((value) => typeof value !== "string" || !value.trim())) throw new Error("tagList must contain non-empty strings");
|
|
844
|
+
const operation = String(data3.operation ?? "append");
|
|
845
|
+
if (!["append", "replace", "remove"].includes(operation)) throw new Error("tag operation must be append, replace, or remove");
|
|
846
|
+
const type = knowledgeType(data3.type ?? "QA");
|
|
847
|
+
const results = [];
|
|
848
|
+
for (const value of knowledgeIds) {
|
|
849
|
+
const id = asPositiveInt(value, "knowledgeIds must contain positive integer IDs");
|
|
850
|
+
const current = asObject(await this.getKnowledgeByType(String(id), type), `could not read knowledge ${id} before tag update`);
|
|
851
|
+
const metadata = current.metadata && typeof current.metadata === "object" && !Array.isArray(current.metadata) ? current.metadata : {};
|
|
852
|
+
const existing = Array.isArray(current.tags) ? current.tags : Array.isArray(metadata.tags) ? metadata.tags : [];
|
|
853
|
+
const requested = requestedTags.map(String);
|
|
854
|
+
const tagList = operation === "append" ? [.../* @__PURE__ */ new Set([...existing.map(String), ...requested])] : operation === "remove" ? existing.map(String).filter((tag) => !requested.includes(tag)) : [...new Set(requested)];
|
|
855
|
+
results.push(await this.post(`${this.qamPrefix}/project/${await this.projectId()}/editTags`, { type, knowledgeIds: [id], tagList }));
|
|
856
|
+
}
|
|
857
|
+
return { success: true, results };
|
|
858
|
+
}
|
|
518
859
|
async transcript(chatId) {
|
|
519
860
|
return this.get(`/api_v2/gpt/project/${await this.projectId()}/transcript/${encodeURIComponent(chatId)}`, { isCompare: true });
|
|
520
861
|
}
|
|
521
862
|
async listTranscripts(data3) {
|
|
522
863
|
return this.post(`/api_v2/gpt/project/${await this.projectId()}/transcripts/filter`, data3);
|
|
523
864
|
}
|
|
865
|
+
async getConversationsByIntent(intent, limit = 50) {
|
|
866
|
+
const intentResponse = await this.listIntents("");
|
|
867
|
+
const intents = Array.isArray(intentResponse) ? intentResponse : intentResponse && typeof intentResponse === "object" && Array.isArray(intentResponse.data) ? intentResponse.data : intentResponse && typeof intentResponse === "object" && Array.isArray(intentResponse.list) ? intentResponse.list : [];
|
|
868
|
+
const matched = intents.find((value) => value && typeof value === "object" && String(value.name ?? "") === intent);
|
|
869
|
+
if (!matched?.id) return [];
|
|
870
|
+
const response = await this.listTranscripts({ pageSize: limit, pageIndex: 1, intentId: Number(matched.id) });
|
|
871
|
+
const rows = Array.isArray(response) ? response : response && typeof response === "object" && Array.isArray(response.list) ? response.list : [];
|
|
872
|
+
const result = [];
|
|
873
|
+
for (const value of rows) {
|
|
874
|
+
if (!value || typeof value !== "object") continue;
|
|
875
|
+
const row = value, chatId = row.chatId ?? row.id;
|
|
876
|
+
if (!chatId) continue;
|
|
877
|
+
try {
|
|
878
|
+
const detail = asObject(await this.transcript(String(chatId)), `transcript ${chatId} must be an object`);
|
|
879
|
+
const turns = Array.isArray(detail.turns) ? detail.turns : Array.isArray(detail.messages) ? detail.messages : [];
|
|
880
|
+
const messages = [];
|
|
881
|
+
let metadata = {};
|
|
882
|
+
for (const turnValue of turns) {
|
|
883
|
+
if (!turnValue || typeof turnValue !== "object") continue;
|
|
884
|
+
const turn = turnValue, role = turn.role ?? turn.type, content = turn.content ?? turn.message;
|
|
885
|
+
if (role === "USER" && content) messages.push(content);
|
|
886
|
+
if (!Object.keys(metadata).length && turn.metadata && typeof turn.metadata === "object" && !Array.isArray(turn.metadata)) metadata = turn.metadata;
|
|
887
|
+
}
|
|
888
|
+
if (messages.length) result.push({ source_chat_id: chatId, intent, messages, metadata });
|
|
889
|
+
} catch {
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return result;
|
|
893
|
+
}
|
|
524
894
|
async resolveTranscriptChatId(externalId, pageSize2 = 10) {
|
|
525
895
|
const result = await this.listTranscripts({ pageSize: pageSize2, pageIndex: 1, debug: false, keyword: externalId });
|
|
526
896
|
const rows = Array.isArray(result) ? result : result?.list;
|
|
@@ -535,10 +905,10 @@ var SolveaClient = class {
|
|
|
535
905
|
};
|
|
536
906
|
|
|
537
907
|
// src/solvea/input.ts
|
|
538
|
-
import { readFile } from "fs/promises";
|
|
908
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
539
909
|
async function jsonData(args, required = true) {
|
|
540
910
|
const file = option(args, "--data-file");
|
|
541
|
-
const raw = file ? await
|
|
911
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data");
|
|
542
912
|
if (!raw) {
|
|
543
913
|
if (required) throw new Error("--data or --data-file is required");
|
|
544
914
|
return {};
|
|
@@ -549,7 +919,7 @@ async function jsonData(args, required = true) {
|
|
|
549
919
|
}
|
|
550
920
|
async function jsonValue(args) {
|
|
551
921
|
const file = option(args, "--data-file");
|
|
552
|
-
const raw = file ? await
|
|
922
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data", true);
|
|
553
923
|
return JSON.parse(raw);
|
|
554
924
|
}
|
|
555
925
|
function requireFlag(args, name, command) {
|
|
@@ -649,9 +1019,9 @@ var reportBody = async (args) => ({
|
|
|
649
1019
|
...await data(args)
|
|
650
1020
|
});
|
|
651
1021
|
var handlers2 = {
|
|
652
|
-
"chat-stream": async (c, a) => c.
|
|
1022
|
+
"chat-stream": async (c, a) => c.chatStream({ message: option(a, "--message", true), botId: Number(option(a, "--bot-id") ?? await c.botId()), touchPoint: option(a, "--touch-point") ?? "LIVE_CHAT", ...await data(a) }),
|
|
653
1023
|
"update-chatflow": async (c, a) => c.updateChatflow(option(a, "--agent-id", true), await jsonData(a), flag(a, "--publish")),
|
|
654
|
-
"get-conversations-by-intent":
|
|
1024
|
+
"get-conversations-by-intent": (c, a) => c.getConversationsByIntent(option(a, "--intent", true), Number(option(a, "--limit") ?? 50)),
|
|
655
1025
|
"resolve-transcript-chat-id": async (c, a) => c.resolveTranscriptChatId(option(a, "--external-id", true), Number(option(a, "--page-size") ?? 10)),
|
|
656
1026
|
"list-isolation-keywords": async (c, a) => c.rawCall("GET", `${c.qamPath}/project/${await project(c)}/isolation_keywords`, void 0, { keyword: option(a, "--keyword") ?? "", pageIndex: page(a), pageSize: pageSize(a) }),
|
|
657
1027
|
"create-isolation-keyword": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/isolation_keyword`, { keyword: option(a, "--keyword", true) }),
|
|
@@ -660,16 +1030,16 @@ var handlers2 = {
|
|
|
660
1030
|
requireFlag(a, "--confirm-delete", "delete-isolation-keyword");
|
|
661
1031
|
return c.rawCall("DELETE", `${c.qamPath}/project/${await project(c)}/isolation_keyword/${requireId(a)}`);
|
|
662
1032
|
},
|
|
663
|
-
"get-knowledge-isolation-keywords":
|
|
664
|
-
"batch-update-isolation-keyword-bindings": async (c, a) => c.
|
|
665
|
-
"edit-knowledge-tags": async (c, a) => c.
|
|
666
|
-
"move-directory":
|
|
667
|
-
"operate-knowledge": async (c, a) => c.
|
|
668
|
-
"batch-update-knowledge": async (c, a) => c.
|
|
669
|
-
"update-document-content": async (c, a) => c.
|
|
670
|
-
"create-product-knowledge": async (c, a) => c.
|
|
671
|
-
"update-product-knowledge": async (c, a) => c.
|
|
672
|
-
"batch-update-products": async (c, a) => c.
|
|
1033
|
+
"get-knowledge-isolation-keywords": (c, a) => c.getKnowledgeIsolationKeywords(requireId(a), option(a, "--type", true)),
|
|
1034
|
+
"batch-update-isolation-keyword-bindings": async (c, a) => c.applyIsolationKeywordBindings(await data(a)),
|
|
1035
|
+
"edit-knowledge-tags": async (c, a) => c.editKnowledgeTags(await data(a)),
|
|
1036
|
+
"move-directory": (c, a) => c.moveDirectory(requireId(a), option(a, "--parent-id", true)),
|
|
1037
|
+
"operate-knowledge": async (c, a) => c.operateKnowledge(await jsonValue(a), flag(a, "--confirm-delete")),
|
|
1038
|
+
"batch-update-knowledge": async (c, a) => c.updateKus(await jsonValue(a)),
|
|
1039
|
+
"update-document-content": async (c, a) => c.updateDocumentContent(requireId(a), option(a, "--type", true), await data(a)),
|
|
1040
|
+
"create-product-knowledge": async (c, a) => c.createProductKnowledge(await data(a)),
|
|
1041
|
+
"update-product-knowledge": async (c, a) => c.updateProductKnowledge(requireId(a), await data(a)),
|
|
1042
|
+
"batch-update-products": async (c, a) => c.updateKuProducts(await jsonValue(a)),
|
|
673
1043
|
"upload-web": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/documents`, { documents: optionList(a, "--url").map((filePath) => ({ filePath, docType: "TC", fileType: "website" })), taskType: "WEB", published: true, isVisible: true, metadata: { tags: [] }, directoryId: option(a, "--directory-id") ?? "" }),
|
|
674
1044
|
"list-label-groups": async (c, a) => c.rawCall("GET", `/api_v2/gpt/label/project/${await project(c)}/getTreeInfo`, void 0, option(a, "--name") ? { treeName: option(a, "--name") } : void 0),
|
|
675
1045
|
"create-label-group": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateTreeInfo`, { ...await data(a), treeName: option(a, "--name", true), operateType: "CREATE" }),
|
|
@@ -686,12 +1056,12 @@ var handlers2 = {
|
|
|
686
1056
|
requireFlag(a, "--confirm-delete", "delete-label-node");
|
|
687
1057
|
return c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateNode`, { treeId: Number(option(a, "--tree-id", true)), operateType: "DELETE", nodes: [{ id: Number(requireId(a)) }] });
|
|
688
1058
|
},
|
|
689
|
-
"download-label-template": async (c, a) => c.
|
|
690
|
-
"download-labels": async (c, a) => c.
|
|
691
|
-
"upload-labels":
|
|
1059
|
+
"download-label-template": async (c, a) => ({ file: await c.downloadLabelTemplate(option(a, "--template-type") === "init" ? "VOC_LABEL_INIT" : "VOC_LABEL", option(a, "--output") ?? `${option(a, "--template-type") ?? "label"}_template.xlsx`) }),
|
|
1060
|
+
"download-labels": async (c, a) => ({ file: await c.downloadLabels(option(a, "--tree-id", true), option(a, "--output") ?? `labels_${option(a, "--tree-id", true)}.xlsx`) }),
|
|
1061
|
+
"upload-labels": (c, a) => c.uploadLabelFile(option(a, "--tree-id", true), option(a, "--file", true), flag(a, "--init")),
|
|
692
1062
|
"export-transcripts": async (c, a) => c.rawCall("POST", `/api_v2/gpt/project/${await project(c)}/transcripts/export/async`, { pageSize: pageSize(a), pageIndex: page(a), timeOffset: Number(option(a, "--time-offset") ?? 8), ...await data(a) }),
|
|
693
1063
|
"get-export-tasks": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/getLabelTask`, { page: page(a), pageSize: pageSize(a) }),
|
|
694
|
-
"upload-batch-test-file": async (c, a) => c.
|
|
1064
|
+
"upload-batch-test-file": async (c, a) => ({ file: await c.uploadTempFile(option(a, "--file", true)) }),
|
|
695
1065
|
"submit-batch-test": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/uploadMessage`, { file: option(a, "--file", true), type: option(a, "--type", true), ...option(a, "--bot-id") ? { botId: Number(option(a, "--bot-id")) } : {}, ...await data(a) }),
|
|
696
1066
|
"get-batch-test-tasks": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/getLabelTask`, { page: page(a), pageSize: pageSize(a), ...option(a, "--type") ? { taskType: option(a, "--type") } : {} }),
|
|
697
1067
|
"list-voice-agents": (c) => c.listAgents("voice"),
|
|
@@ -732,7 +1102,7 @@ function enabled(args, command) {
|
|
|
732
1102
|
}
|
|
733
1103
|
async function data2(args) {
|
|
734
1104
|
const file = option(args, "--data-file");
|
|
735
|
-
const raw = file ? await
|
|
1105
|
+
const raw = file ? await readFile3(file, "utf8") : option(args, "--data");
|
|
736
1106
|
if (!raw) throw new Error("--data or --data-file is required");
|
|
737
1107
|
const parsed = JSON.parse(raw);
|
|
738
1108
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
|
|
@@ -761,6 +1131,26 @@ async function transcriptFilters(args) {
|
|
|
761
1131
|
}
|
|
762
1132
|
return filters;
|
|
763
1133
|
}
|
|
1134
|
+
async function knowledgeFilters(args) {
|
|
1135
|
+
const filters = {
|
|
1136
|
+
type: "ALL",
|
|
1137
|
+
pageIndex: 1,
|
|
1138
|
+
pageSize: 50,
|
|
1139
|
+
keyword: "",
|
|
1140
|
+
...await optionalData(args)
|
|
1141
|
+
};
|
|
1142
|
+
const optional = [
|
|
1143
|
+
["type", "--type", String],
|
|
1144
|
+
["pageIndex", "--page", Number],
|
|
1145
|
+
["pageSize", "--page-size", Number],
|
|
1146
|
+
["keyword", "--keyword", String]
|
|
1147
|
+
];
|
|
1148
|
+
for (const [field, name, convert] of optional) {
|
|
1149
|
+
const value = option(args, name);
|
|
1150
|
+
if (value !== void 0) filters[field] = convert(value);
|
|
1151
|
+
}
|
|
1152
|
+
return filters;
|
|
1153
|
+
}
|
|
764
1154
|
async function runSolveaCommand(session, command, args) {
|
|
765
1155
|
const client = new SolveaClient(session);
|
|
766
1156
|
if (EXTENDED_COMMANDS.has(command)) return runExtendedCommand(client, command, args);
|
|
@@ -836,9 +1226,9 @@ async function runSolveaCommand(session, command, args) {
|
|
|
836
1226
|
case "delete-custom-quality-rule":
|
|
837
1227
|
return client.deleteCustomQualityRule(option(args, "--inspection-key", true));
|
|
838
1228
|
case "list-knowledge":
|
|
839
|
-
return client.listKnowledge(await
|
|
1229
|
+
return client.listKnowledge(await knowledgeFilters(args));
|
|
840
1230
|
case "get-knowledge":
|
|
841
|
-
return client.
|
|
1231
|
+
return client.getKnowledgeByType(id(), option(args, "--type", true));
|
|
842
1232
|
case "update-knowledge":
|
|
843
1233
|
return client.updateKnowledge(id(), await data2(args));
|
|
844
1234
|
case "upload-qa":
|
|
@@ -919,7 +1309,7 @@ function isNativeSolveaCommand(command) {
|
|
|
919
1309
|
}
|
|
920
1310
|
|
|
921
1311
|
// src/business.ts
|
|
922
|
-
var packageRoot = () => resolve(
|
|
1312
|
+
var packageRoot = () => resolve(dirname2(fileURLToPath(import.meta.url)), "..");
|
|
923
1313
|
async function runApi(session, command, args) {
|
|
924
1314
|
if (isNativeSolveaCommand(command)) {
|
|
925
1315
|
console.log(JSON.stringify(await runSolveaCommand(session, command, args), null, 2));
|
|
@@ -979,8 +1369,8 @@ var resources = {
|
|
|
979
1369
|
delete: action("delete-templates", "Delete reply templates", "--ids <id> [id...] --confirm-delete")
|
|
980
1370
|
},
|
|
981
1371
|
knowledge: {
|
|
982
|
-
list: action("list-knowledge", "List knowledge", "[--data <json> | --data-file <path>]"),
|
|
983
|
-
get: action("get-knowledge", "Get one knowledge item", "--id <knowledge-id>"),
|
|
1372
|
+
list: action("list-knowledge", "List knowledge", "[--type <type>] [--page <number>] [--page-size <number>] [--keyword <text>] [--data <json> | --data-file <path>]"),
|
|
1373
|
+
get: action("get-knowledge", "Get one knowledge item", "--id <knowledge-id> --type <QA|LEARN_SELF|FILE|WEBSITE|PRODUCT>"),
|
|
984
1374
|
update: action("update-knowledge", "Update a knowledge item", "--id <knowledge-id> (--data <json> | --data-file <path>)"),
|
|
985
1375
|
"upload-qa": action("upload-qa", "Upload QA items", "--data <json> | --data-file <path>"),
|
|
986
1376
|
tags: action("list-knowledge-tags", "List knowledge tags", "[--keyword <keyword>]")
|
|
@@ -1115,7 +1505,7 @@ function publicTenant(t) {
|
|
|
1115
1505
|
|
|
1116
1506
|
// src/update.ts
|
|
1117
1507
|
import { spawn } from "child_process";
|
|
1118
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
1508
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
1119
1509
|
import { homedir, platform } from "os";
|
|
1120
1510
|
import { isAbsolute, join as join2 } from "path";
|
|
1121
1511
|
var exec = (command, args, options3 = {}) => new Promise((resolve5, reject) => {
|
|
@@ -1193,8 +1583,8 @@ async function installScheduler() {
|
|
|
1193
1583
|
const dir = join2(homedir(), "Library/LaunchAgents");
|
|
1194
1584
|
const path = join2(dir, "com.solvea.cxclaw.update.plist");
|
|
1195
1585
|
const programArguments = [invocation.command, ...invocation.args].map((value) => `<string>${xml(value)}</string>`).join("");
|
|
1196
|
-
await
|
|
1197
|
-
await
|
|
1586
|
+
await mkdir2(dir, { recursive: true });
|
|
1587
|
+
await writeFile2(
|
|
1198
1588
|
path,
|
|
1199
1589
|
`<?xml version="1.0" encoding="UTF-8"?>
|
|
1200
1590
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -1208,15 +1598,15 @@ async function installScheduler() {
|
|
|
1208
1598
|
if (platform() === "linux") {
|
|
1209
1599
|
const dir = join2(homedir(), ".config/systemd/user");
|
|
1210
1600
|
const command = [invocation.command, ...invocation.args].map(systemdArg).join(" ");
|
|
1211
|
-
await
|
|
1212
|
-
await
|
|
1601
|
+
await mkdir2(dir, { recursive: true });
|
|
1602
|
+
await writeFile2(
|
|
1213
1603
|
join2(dir, "cxclaw-update.service"),
|
|
1214
1604
|
`[Service]
|
|
1215
1605
|
Type=oneshot
|
|
1216
1606
|
ExecStart=${command}
|
|
1217
1607
|
`
|
|
1218
1608
|
);
|
|
1219
|
-
await
|
|
1609
|
+
await writeFile2(
|
|
1220
1610
|
join2(dir, "cxclaw-update.timer"),
|
|
1221
1611
|
"[Timer]\nOnBootSec=5m\nOnUnitActiveSec=6h\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
|
|
1222
1612
|
);
|
|
@@ -1367,14 +1757,14 @@ async function runRuntime(action2, args) {
|
|
|
1367
1757
|
// src/workflows/solvea-kit.ts
|
|
1368
1758
|
import ExcelJS from "exceljs";
|
|
1369
1759
|
import { promises as fs2 } from "fs";
|
|
1370
|
-
import { dirname as
|
|
1760
|
+
import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
|
|
1371
1761
|
var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
|
|
1372
1762
|
var outputDir = (owner) => join4(workspace2(), "outputs", owner);
|
|
1373
1763
|
var stateDir = (owner) => join4(workspace2(), "state", owner);
|
|
1374
1764
|
var now = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1375
1765
|
var readJson2 = async (path) => JSON.parse(await fs2.readFile(path, "utf8"));
|
|
1376
1766
|
var writeJson2 = async (path, value) => {
|
|
1377
|
-
await fs2.mkdir(
|
|
1767
|
+
await fs2.mkdir(dirname3(path), { recursive: true });
|
|
1378
1768
|
await fs2.writeFile(path, JSON.stringify(value, null, 2));
|
|
1379
1769
|
};
|
|
1380
1770
|
var INIT_NOTICE = `The information of this row cannot be deleted! Tips:
|
|
@@ -1428,7 +1818,7 @@ async function generateLabelImport(args) {
|
|
|
1428
1818
|
sheet.columns.forEach((column) => {
|
|
1429
1819
|
column.width = 18;
|
|
1430
1820
|
});
|
|
1431
|
-
await fs2.mkdir(
|
|
1821
|
+
await fs2.mkdir(dirname3(output), { recursive: true });
|
|
1432
1822
|
await workbook.xlsx.writeFile(output);
|
|
1433
1823
|
return { file: output, format: init ? "init" : "update", rows: labels.length };
|
|
1434
1824
|
}
|
|
@@ -1452,8 +1842,8 @@ async function runSolveaKitWorkflow(action2, args) {
|
|
|
1452
1842
|
}
|
|
1453
1843
|
|
|
1454
1844
|
// src/workflows/support.ts
|
|
1455
|
-
import { readFile as
|
|
1456
|
-
import { basename as
|
|
1845
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1846
|
+
import { basename as basename3 } from "path";
|
|
1457
1847
|
var safe = (value) => encodeURIComponent(value);
|
|
1458
1848
|
var docsMcpUrl = process.env.CXCLAW_DOCS_MCP_URL ?? "https://solvea-39f11a36.mintlify.app/mcp";
|
|
1459
1849
|
var publicDocsPrefix = "https://www.voc.ai/cn/docs/";
|
|
@@ -1527,7 +1917,7 @@ async function runSupport(session, action2, args) {
|
|
|
1527
1917
|
const attachments = [];
|
|
1528
1918
|
for (const file of options2(args, "--attachment")) {
|
|
1529
1919
|
const form = new FormData();
|
|
1530
|
-
form.append("file", new Blob([await
|
|
1920
|
+
form.append("file", new Blob([await readFile4(file)]), basename3(file));
|
|
1531
1921
|
const tenant = session.tenant;
|
|
1532
1922
|
const base = tenant?.businessUrl?.replace(/\/$/, "");
|
|
1533
1923
|
if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
|
|
@@ -1558,24 +1948,24 @@ async function runSupport(session, action2, args) {
|
|
|
1558
1948
|
}
|
|
1559
1949
|
|
|
1560
1950
|
// src/workflows/common.ts
|
|
1561
|
-
import { mkdir as
|
|
1562
|
-
import { dirname as
|
|
1951
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
1952
|
+
import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
|
|
1563
1953
|
import { homedir as homedir2 } from "os";
|
|
1564
1954
|
var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join5(homedir2(), ".cxclaw", "workspace"));
|
|
1565
1955
|
var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join5(workspaceRoot(), "state"), domain);
|
|
1566
1956
|
var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join5(workspaceRoot(), "outputs"), domain);
|
|
1567
1957
|
async function ensureParent(file) {
|
|
1568
|
-
await
|
|
1958
|
+
await mkdir3(dirname4(file), { recursive: true });
|
|
1569
1959
|
}
|
|
1570
1960
|
async function ensureDir(directory) {
|
|
1571
|
-
await
|
|
1961
|
+
await mkdir3(directory, { recursive: true });
|
|
1572
1962
|
}
|
|
1573
1963
|
async function readJson3(file) {
|
|
1574
|
-
return JSON.parse(await
|
|
1964
|
+
return JSON.parse(await readFile5(file, "utf8"));
|
|
1575
1965
|
}
|
|
1576
1966
|
async function writeJson3(file, value) {
|
|
1577
1967
|
await ensureParent(file);
|
|
1578
|
-
await
|
|
1968
|
+
await writeFile3(file, `${JSON.stringify(value, null, 2)}
|
|
1579
1969
|
`, "utf8");
|
|
1580
1970
|
}
|
|
1581
1971
|
function intOption(args, name, fallback) {
|
|
@@ -1593,7 +1983,7 @@ function htmlEscape(value) {
|
|
|
1593
1983
|
}
|
|
1594
1984
|
async function writeHtml(file, title, body) {
|
|
1595
1985
|
await ensureParent(file);
|
|
1596
|
-
await
|
|
1986
|
+
await writeFile3(file, `<!doctype html><html><head><meta charset="utf-8"><title>${htmlEscape(title)}</title><style>body{font-family:system-ui,sans-serif;margin:2rem;line-height:1.5}pre{white-space:pre-wrap;background:#f6f8fa;padding:1rem;border-radius:8px}.card{border:1px solid #ddd;border-radius:8px;padding:1rem;margin:1rem 0}</style></head><body><h1>${htmlEscape(title)}</h1>${body}</body></html>`, "utf8");
|
|
1597
1987
|
}
|
|
1598
1988
|
function asRows(value) {
|
|
1599
1989
|
if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
|
|
@@ -1800,8 +2190,8 @@ async function runLocalAgentWorkflow(action2, args) {
|
|
|
1800
2190
|
}
|
|
1801
2191
|
async function writeFileText(file, content) {
|
|
1802
2192
|
await ensureParent(file);
|
|
1803
|
-
const { writeFile:
|
|
1804
|
-
await
|
|
2193
|
+
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
2194
|
+
await writeFile4(file, `${content}
|
|
1805
2195
|
`, "utf8");
|
|
1806
2196
|
}
|
|
1807
2197
|
|
|
@@ -1839,9 +2229,9 @@ async function createSheet(args) {
|
|
|
1839
2229
|
return { output, count: cases.length, mode: option(args, "--mode") };
|
|
1840
2230
|
}
|
|
1841
2231
|
async function submit(client, args) {
|
|
1842
|
-
const file = option(args, "--file", true);
|
|
2232
|
+
const file = await client.uploadTempFile(option(args, "--file", true));
|
|
1843
2233
|
const result = await client.rawCall("POST", `/api_v2/gpt/label/project/${await client.projectId()}/uploadMessage`, { file, type: option(args, "--mode", true), ...option(args, "--agent-id") ? { agentId: Number(option(args, "--agent-id")) } : {}, ...option(args, "--flow-version") ? { flowVersion: Number(option(args, "--flow-version")) } : {} });
|
|
1844
|
-
return result;
|
|
2234
|
+
return { fileKey: file, result };
|
|
1845
2235
|
}
|
|
1846
2236
|
async function runAgentTester(session, action2, args) {
|
|
1847
2237
|
if (!action2) throw new Error("agent-tester action is required");
|
|
@@ -2124,6 +2514,56 @@ async function extractWebQa(args) {
|
|
|
2124
2514
|
}
|
|
2125
2515
|
return { outputDir: outputDir2, files, count: files.length };
|
|
2126
2516
|
}
|
|
2517
|
+
var flattenDirectories = (nodes, result = []) => {
|
|
2518
|
+
for (const node of nodes) {
|
|
2519
|
+
result.push(node);
|
|
2520
|
+
if (Array.isArray(node.children)) flattenDirectories(node.children.filter((child) => child && typeof child === "object"), result);
|
|
2521
|
+
}
|
|
2522
|
+
return result;
|
|
2523
|
+
};
|
|
2524
|
+
function validateQaItems(values, maxDepth) {
|
|
2525
|
+
if (!values.length) throw new Error("QA plan must contain at least one item");
|
|
2526
|
+
return values.map((value, index) => {
|
|
2527
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`QA item #${index + 1} must be an object`);
|
|
2528
|
+
const item = value;
|
|
2529
|
+
if (!Array.isArray(item.dir_path) || !item.dir_path.length) throw new Error(`QA item #${index + 1} dir_path must be a non-empty array`);
|
|
2530
|
+
if (item.dir_path.length > maxDepth) throw new Error(`QA item #${index + 1} dir_path exceeds maximum depth ${maxDepth}`);
|
|
2531
|
+
const dirPath = item.dir_path.map((name, depth) => {
|
|
2532
|
+
if (typeof name !== "string" || !name.trim() || /[\\/]/.test(name)) throw new Error(`QA item #${index + 1} dir_path[${depth}] must be a folder name`);
|
|
2533
|
+
return name.trim();
|
|
2534
|
+
});
|
|
2535
|
+
const title = typeof item.question === "string" ? item.question.trim() : "";
|
|
2536
|
+
const content = typeof item.answer === "string" ? item.answer.trim() : "";
|
|
2537
|
+
if (!title) throw new Error(`QA item #${index + 1} question must be a non-empty string`);
|
|
2538
|
+
if (!content) throw new Error(`QA item #${index + 1} answer must be a non-empty markdown string`);
|
|
2539
|
+
return { dirPath, title, content };
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
async function resolveDirectory(client, path, directories, cache) {
|
|
2543
|
+
let parentId = 0;
|
|
2544
|
+
const prefix = [];
|
|
2545
|
+
for (const name of path) {
|
|
2546
|
+
prefix.push(name);
|
|
2547
|
+
const key = prefix.join("/");
|
|
2548
|
+
const cached = cache.get(key);
|
|
2549
|
+
if (cached !== void 0) {
|
|
2550
|
+
parentId = cached;
|
|
2551
|
+
continue;
|
|
2552
|
+
}
|
|
2553
|
+
const match = directories.find((directory) => String(directory.name ?? "") === name && Number(directory.parentId ?? 0) === parentId);
|
|
2554
|
+
if (match) parentId = Number(match.id ?? match.directoryId);
|
|
2555
|
+
else {
|
|
2556
|
+
const createdParentId = parentId;
|
|
2557
|
+
const created = await client.createFolder(name, parentId);
|
|
2558
|
+
const id = Number(created?.id ?? created?.directoryId);
|
|
2559
|
+
if (!Number.isSafeInteger(id) || id <= 0) throw new Error(`could not create directory ${key}: response has no directory id`);
|
|
2560
|
+
parentId = id;
|
|
2561
|
+
directories.push({ id, name, parentId: Number(created?.parentId ?? createdParentId) });
|
|
2562
|
+
}
|
|
2563
|
+
cache.set(key, parentId);
|
|
2564
|
+
}
|
|
2565
|
+
return parentId;
|
|
2566
|
+
}
|
|
2127
2567
|
async function importQa(client, args) {
|
|
2128
2568
|
const qaFile = option(args, "--qa-file");
|
|
2129
2569
|
const fromDir = option(args, "--from-dir");
|
|
@@ -2135,23 +2575,67 @@ async function importQa(client, args) {
|
|
|
2135
2575
|
}
|
|
2136
2576
|
if (fromDir) {
|
|
2137
2577
|
const { readdir } = await import("fs/promises");
|
|
2138
|
-
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json"))) {
|
|
2578
|
+
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json")).sort()) {
|
|
2139
2579
|
const value = await readJson3(join8(fromDir, file));
|
|
2140
2580
|
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2141
2581
|
}
|
|
2142
2582
|
}
|
|
2583
|
+
const maxDepth = intOption(args, "--max-depth", 3);
|
|
2584
|
+
if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
|
|
2585
|
+
const normalized = validateQaItems(items, maxDepth);
|
|
2143
2586
|
const plan2 = { items, count: items.length, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2144
2587
|
const planFile = join8(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
|
|
2145
2588
|
await writeJson3(planFile, plan2);
|
|
2146
2589
|
if (flag(args, "--dry-run")) return { dryRun: true, planFile, count: items.length };
|
|
2147
|
-
|
|
2590
|
+
const directories = flattenDirectories(asRows(await client.listDirectories()));
|
|
2591
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2592
|
+
const payload = [];
|
|
2593
|
+
for (const item of normalized) payload.push({ title: item.title, content: item.content, directoryId: await resolveDirectory(client, item.dirPath, directories, cache) });
|
|
2594
|
+
return { planFile, count: payload.length, result: await client.uploadQa(payload) };
|
|
2148
2595
|
}
|
|
2149
2596
|
async function importSources(client, args) {
|
|
2150
|
-
const
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2597
|
+
const value = await readJson3(option(args, "--plan-file", true));
|
|
2598
|
+
const source = Array.isArray(value) ? value : value && typeof value === "object" && Array.isArray(value.items) ? value.items : [];
|
|
2599
|
+
if (!source.length) throw new Error("source document plan must be a non-empty array");
|
|
2600
|
+
const maxDepth = intOption(args, "--max-depth", 3);
|
|
2601
|
+
if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
|
|
2602
|
+
const { stat } = await import("fs/promises");
|
|
2603
|
+
const normalized = [];
|
|
2604
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
2605
|
+
for (const [index, raw] of source.entries()) {
|
|
2606
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`source document #${index + 1} must be an object`);
|
|
2607
|
+
const item = raw;
|
|
2608
|
+
const itemSource = typeof item.source === "string" ? item.source.trim() : "";
|
|
2609
|
+
if (!itemSource) throw new Error(`source document #${index + 1} needs a non-empty source`);
|
|
2610
|
+
const path = item.dir_path ?? [];
|
|
2611
|
+
if (!Array.isArray(path) || path.some((name) => typeof name !== "string" || !name.trim() || /[\\/]/.test(name))) throw new Error(`source document #${index + 1} dir_path must contain folder names`);
|
|
2612
|
+
if (path.length > maxDepth) throw new Error(`source document #${index + 1} dir_path exceeds maximum depth ${maxDepth}`);
|
|
2613
|
+
const isUrl = /^https?:\/\//i.test(itemSource);
|
|
2614
|
+
if (!isUrl) {
|
|
2615
|
+
if (![".doc", ".docx", ".pdf"].some((extension) => itemSource.toLowerCase().endsWith(extension))) throw new Error(`source document #${index + 1} supports .doc, .docx, or .pdf`);
|
|
2616
|
+
const info = await stat(itemSource).catch(() => void 0);
|
|
2617
|
+
if (!info?.isFile()) throw new Error(`source document #${index + 1} file not found: ${itemSource}`);
|
|
2618
|
+
}
|
|
2619
|
+
const skippedDuplicate = isUrl && seenUrls.has(itemSource);
|
|
2620
|
+
if (isUrl) seenUrls.add(itemSource);
|
|
2621
|
+
normalized.push({ source: itemSource, dirPath: path.map(String).map((name) => name.trim()), isUrl, skippedDuplicate });
|
|
2622
|
+
}
|
|
2623
|
+
if (!flag(args, "--execute")) return { dryRun: true, count: normalized.length, items: normalized.map((item) => ({ source: item.source, dir_path: item.dirPath, status: item.skippedDuplicate ? "skipped_duplicate" : "pending" })) };
|
|
2624
|
+
const directories = flattenDirectories(asRows(await client.listDirectories()));
|
|
2625
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2626
|
+
const results = [];
|
|
2627
|
+
for (const item of normalized) {
|
|
2628
|
+
if (item.skippedDuplicate) {
|
|
2629
|
+
results.push({ source: item.source, status: "skipped_duplicate" });
|
|
2630
|
+
continue;
|
|
2631
|
+
}
|
|
2632
|
+
const directoryId = await resolveDirectory(client, item.dirPath, directories, cache);
|
|
2633
|
+
const filePath = item.isUrl ? item.source : await client.uploadKnowledgeFile(item.source);
|
|
2634
|
+
const documents = [{ filePath, docType: "TC", ...item.isUrl ? { fileType: "website" } : {} }];
|
|
2635
|
+
const result = await client.createDocuments(documents, directoryId, item.isUrl ? "WEB" : "FILE");
|
|
2636
|
+
results.push({ source: item.source, directoryId, status: "submitted", result });
|
|
2637
|
+
}
|
|
2638
|
+
return { count: normalized.length, results };
|
|
2155
2639
|
}
|
|
2156
2640
|
async function importTemplates(client, args) {
|
|
2157
2641
|
const source = option(args, "--plan-file") ?? option(args, "--xlsx");
|
|
@@ -2429,7 +2913,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
2429
2913
|
if (flag(args, "--scheduled")) await new Promise((resolve5) => setTimeout(resolve5, Math.random() * 18e5));
|
|
2430
2914
|
return print(await update());
|
|
2431
2915
|
}
|
|
2432
|
-
if (command === "commands" || command === "capabilities") console.log(await
|
|
2916
|
+
if (command === "commands" || command === "capabilities") console.log(await readFile6(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
|
|
2433
2917
|
else throw new Error(`unknown command: ${command}`);
|
|
2434
2918
|
}
|
|
2435
2919
|
main().catch((error) => {
|