@shulex/cxclawctl 0.1.5 → 0.1.7
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/README.md +3 -4
- package/capabilities.lock.json +2 -2
- package/dist/cli.js +543 -72
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
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";
|
|
8
8
|
|
|
9
|
+
// src/config.ts
|
|
10
|
+
var AUTH_SERVER_URL = "https://ai.solvea.cx/api_v2/ticket";
|
|
11
|
+
|
|
9
12
|
// package.json
|
|
10
13
|
var package_default = {
|
|
11
14
|
name: "@shulex/cxclawctl",
|
|
12
|
-
version: "0.1.
|
|
15
|
+
version: "0.1.7",
|
|
13
16
|
description: "Solvea CXClaw CLI and Codex plugin runtime",
|
|
14
17
|
type: "module",
|
|
15
18
|
bin: {
|
|
@@ -38,6 +41,7 @@ var package_default = {
|
|
|
38
41
|
},
|
|
39
42
|
dependencies: {
|
|
40
43
|
"@alicloud/log": "^1.2.6",
|
|
44
|
+
"@aws-sdk/client-s3": "^3.1156.0",
|
|
41
45
|
"@napi-rs/keyring": "^2.1.2",
|
|
42
46
|
"compare-versions": "^6.1.1",
|
|
43
47
|
exceljs: "^4.4.0",
|
|
@@ -79,7 +83,7 @@ async function request(session, path, init = {}, tenant = false) {
|
|
|
79
83
|
let last;
|
|
80
84
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
81
85
|
try {
|
|
82
|
-
const response = await fetch(`${
|
|
86
|
+
const response = await fetch(`${AUTH_SERVER_URL}${path}`, { ...init, headers, signal: AbortSignal.timeout(3e4) });
|
|
83
87
|
const text = await response.text();
|
|
84
88
|
const value = text ? JSON.parse(text) : void 0;
|
|
85
89
|
if (!response.ok) throw new Error(`server returned ${response.status}: ${text}`);
|
|
@@ -112,8 +116,8 @@ async function deleteSession() {
|
|
|
112
116
|
|
|
113
117
|
// src/auth.ts
|
|
114
118
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
115
|
-
async function login(
|
|
116
|
-
const session = {
|
|
119
|
+
async function login(channel) {
|
|
120
|
+
const session = { channel, accessToken: "", refreshToken: "", accessExpiry: 0, employeeId: "", email: "" };
|
|
117
121
|
const start = await post(session, "/auth/device/start", { clientVersion: VERSION });
|
|
118
122
|
console.error(`\u8BF7\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\u5B8C\u6210\u9489\u9489\u6388\u6743\uFF1A
|
|
119
123
|
${start.verificationUriComplete}
|
|
@@ -189,13 +193,32 @@ function positionals(args) {
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
// src/business.ts
|
|
192
|
-
import { dirname, resolve } from "path";
|
|
196
|
+
import { dirname as dirname2, resolve } from "path";
|
|
193
197
|
import { fileURLToPath } from "url";
|
|
194
198
|
|
|
195
199
|
// src/solvea/commands.ts
|
|
196
|
-
import { readFile as
|
|
200
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
197
201
|
|
|
198
202
|
// src/solvea/client.ts
|
|
203
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
204
|
+
import { basename, dirname, extname } from "path";
|
|
205
|
+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
206
|
+
var KNOWLEDGE_TYPES = /* @__PURE__ */ new Set(["QA", "LEARN_SELF", "FILE", "WEBSITE", "PRODUCT"]);
|
|
207
|
+
var asObject = (value, message) => {
|
|
208
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(message);
|
|
209
|
+
return value;
|
|
210
|
+
};
|
|
211
|
+
var asPositiveInt = (value, message) => {
|
|
212
|
+
const number = Number(value);
|
|
213
|
+
if (!Number.isSafeInteger(number) || number <= 0) throw new Error(message);
|
|
214
|
+
return number;
|
|
215
|
+
};
|
|
216
|
+
var knowledgeType = (value) => {
|
|
217
|
+
const normalized = String(value ?? "").toUpperCase();
|
|
218
|
+
if (!KNOWLEDGE_TYPES.has(normalized)) throw new Error("knowledge type must be QA, LEARN_SELF, FILE, WEBSITE, or PRODUCT");
|
|
219
|
+
return normalized;
|
|
220
|
+
};
|
|
221
|
+
var isolationKnowledgeType = (value) => ({ QA: "QA", LEARN_SELF: "QA", FILE: "DOCUMENT", WEBSITE: "DOCUMENT", PRODUCT: "PRODUCT" })[knowledgeType(value)];
|
|
199
222
|
var SolveaClient = class {
|
|
200
223
|
constructor(session) {
|
|
201
224
|
this.session = session;
|
|
@@ -304,14 +327,25 @@ var SolveaClient = class {
|
|
|
304
327
|
return this.post("/api_v2/gpt/ap/createAgent", { projectId: Number(await this.projectId()), ...data3 });
|
|
305
328
|
}
|
|
306
329
|
async updateAgent(data3) {
|
|
307
|
-
|
|
330
|
+
const payload = { ...data3, projectId: Number(await this.projectId()) };
|
|
331
|
+
if (payload.agentId === void 0 && payload.id !== void 0) payload.agentId = payload.id;
|
|
332
|
+
return this.post("/api_v2/gpt/ap/updateAgent", payload);
|
|
308
333
|
}
|
|
309
334
|
async updateChatflow(agentId, graph, publish = false) {
|
|
310
335
|
const current = await this.getAgent(agentId);
|
|
311
336
|
const agent = Array.isArray(current) ? current[0] : current;
|
|
312
337
|
if (!agent || agent.type !== "CHAT_FLOW") throw new Error(`Agent ${agentId} is not CHAT_FLOW`);
|
|
338
|
+
if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) throw new Error("chatFlowGraph must be an object with nodes and edges");
|
|
313
339
|
const payload = { ...agent, agentId: agent.id ?? Number(agentId), meta: { ...agent.meta ?? {}, chatFlowGraph: graph } };
|
|
314
|
-
|
|
340
|
+
if (publish) {
|
|
341
|
+
delete payload.backupType;
|
|
342
|
+
delete payload.meta.backupType;
|
|
343
|
+
delete payload.meta.draft_flow_updatetime;
|
|
344
|
+
} else {
|
|
345
|
+
payload.backupType = "DRAFT";
|
|
346
|
+
payload.meta.draft_flow_updatetime = Date.now();
|
|
347
|
+
}
|
|
348
|
+
return this.updateAgent(payload);
|
|
315
349
|
}
|
|
316
350
|
async listPlugins() {
|
|
317
351
|
return this.post("/api_v2/gpt/ap/queryPlugin", { projectId: Number(await this.projectId()), needRelation: false });
|
|
@@ -323,7 +357,9 @@ var SolveaClient = class {
|
|
|
323
357
|
return this.post("/api_v2/gpt/ap/createPlugin", { projectId: Number(await this.projectId()), ...data3 });
|
|
324
358
|
}
|
|
325
359
|
async updatePlugin(data3) {
|
|
326
|
-
|
|
360
|
+
const payload = { projectId: Number(await this.projectId()), ...data3 };
|
|
361
|
+
if (payload.pluginId === void 0 && payload.id !== void 0) payload.pluginId = payload.id;
|
|
362
|
+
return this.post("/api_v2/gpt/ap/updatePlugin", payload);
|
|
327
363
|
}
|
|
328
364
|
async deletePlugin(id) {
|
|
329
365
|
return this.post("/api_v2/gpt/ap/deletePlugin", { projectId: Number(await this.projectId()), pluginId: Number(id) });
|
|
@@ -375,11 +411,48 @@ var SolveaClient = class {
|
|
|
375
411
|
}
|
|
376
412
|
async chat(data3) {
|
|
377
413
|
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 }
|
|
414
|
+
return this.post(`/api_v2/gpt/bot/${bot}/chat`, { ...data3, botId: Number(bot), projectId: Number(await this.projectId()), option: data3.option ?? { debug: true } });
|
|
415
|
+
}
|
|
416
|
+
async chatStream(data3) {
|
|
417
|
+
const bot = String(data3.botId ?? await this.botId());
|
|
418
|
+
const body = { ...data3, botId: Number(bot), projectId: Number(await this.projectId()), option: data3.option ?? { debug: true } };
|
|
419
|
+
const cookie = this.env === "prod" ? "_token" : "_token_staging";
|
|
420
|
+
const response = await fetch(`${this.baseUrl}/api_v2/gpt/bot/${encodeURIComponent(bot)}/chat/stream`, {
|
|
421
|
+
method: "POST",
|
|
422
|
+
headers: {
|
|
423
|
+
accept: "text/event-stream",
|
|
424
|
+
"content-type": "application/json",
|
|
425
|
+
origin: this.baseUrl,
|
|
426
|
+
referer: `${this.baseUrl}/chatbot`,
|
|
427
|
+
cookie: `${cookie}=${this.session.tenant.businessToken}`,
|
|
428
|
+
"x-cxclaw-client-version": process.env.CXCLAW_PLUGIN_VERSION ?? "unknown"
|
|
429
|
+
},
|
|
430
|
+
body: JSON.stringify(body),
|
|
431
|
+
signal: AbortSignal.timeout(3e4)
|
|
432
|
+
});
|
|
433
|
+
const text = await response.text();
|
|
434
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} POST /api_v2/gpt/bot/${bot}/chat/stream: ${text}`);
|
|
435
|
+
let finalAction;
|
|
436
|
+
const content = [];
|
|
437
|
+
for (const line of text.split(/\r?\n/)) {
|
|
438
|
+
if (!line.startsWith("data:")) continue;
|
|
439
|
+
const raw = line.slice(5).trim();
|
|
440
|
+
if (!raw) continue;
|
|
441
|
+
let event;
|
|
442
|
+
try {
|
|
443
|
+
event = asObject(JSON.parse(raw), "stream event must be an object");
|
|
444
|
+
} catch {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
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)}`);
|
|
448
|
+
if (event.content) content.push(String(event.content));
|
|
449
|
+
if (event.isEnd) finalAction = event.data && typeof event.data === "object" && !Array.isArray(event.data) ? event.data : event;
|
|
450
|
+
}
|
|
451
|
+
return finalAction ?? { content: content.join(""), isEnd: true };
|
|
379
452
|
}
|
|
380
453
|
async chatAsync(data3) {
|
|
381
454
|
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 }
|
|
455
|
+
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
456
|
}
|
|
384
457
|
async asyncResult(receipt, botId) {
|
|
385
458
|
const bot = botId ?? await this.botId();
|
|
@@ -410,10 +483,10 @@ var SolveaClient = class {
|
|
|
410
483
|
return this.post(`/api_v2/gpt/project/${await this.projectId()}/strategy?v4=true`, data3);
|
|
411
484
|
}
|
|
412
485
|
async updateStrategy(id, data3) {
|
|
413
|
-
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, data3);
|
|
486
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, data3, { v4: "true" });
|
|
414
487
|
}
|
|
415
488
|
async toggleStrategy(id, enabled2) {
|
|
416
|
-
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}
|
|
489
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, { status: enabled2 ? "ENABLED" : "DISABLED" }, { v4: "true" });
|
|
417
490
|
}
|
|
418
491
|
async qualityStatus() {
|
|
419
492
|
return this.get("/api_v2/intelli/inspection/ticket/status");
|
|
@@ -494,11 +567,212 @@ var SolveaClient = class {
|
|
|
494
567
|
async getKnowledge(id) {
|
|
495
568
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/ku/${encodeURIComponent(id)}`);
|
|
496
569
|
}
|
|
570
|
+
async getDocument(id) {
|
|
571
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/document/${encodeURIComponent(id)}`);
|
|
572
|
+
}
|
|
573
|
+
async getProductKnowledge(id) {
|
|
574
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/ku_product/${encodeURIComponent(id)}`);
|
|
575
|
+
}
|
|
576
|
+
async getKnowledgeByType(id, type) {
|
|
577
|
+
const normalized = knowledgeType(type);
|
|
578
|
+
if (normalized === "FILE" || normalized === "WEBSITE") return this.getDocument(id);
|
|
579
|
+
if (normalized === "PRODUCT") return this.getProductKnowledge(id);
|
|
580
|
+
return this.getKnowledge(id);
|
|
581
|
+
}
|
|
497
582
|
async updateKnowledge(id, data3) {
|
|
498
|
-
|
|
583
|
+
const payload = { ...data3 };
|
|
584
|
+
if (payload.directoryId === void 0) {
|
|
585
|
+
const current = asObject(await this.getKnowledge(id), `could not read knowledge ${id} before update`);
|
|
586
|
+
if (current.directoryId !== void 0) payload.directoryId = current.directoryId;
|
|
587
|
+
}
|
|
588
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku`, { ...payload, kuId: asPositiveInt(id, "knowledge id must be a positive integer") });
|
|
499
589
|
}
|
|
500
590
|
async uploadQa(items) {
|
|
501
|
-
|
|
591
|
+
if (!Array.isArray(items) || !items.length) throw new Error("QA items must be a non-empty array");
|
|
592
|
+
const payload = items.map((value, index) => {
|
|
593
|
+
const item = asObject(value, `QA item #${index + 1} must be an object`);
|
|
594
|
+
const title = String(item.title ?? "").trim();
|
|
595
|
+
const content = String(item.content ?? "").trim();
|
|
596
|
+
if (!title) throw new Error(`QA item #${index + 1} requires title`);
|
|
597
|
+
if (!content) throw new Error(`QA item #${index + 1} requires content`);
|
|
598
|
+
return {
|
|
599
|
+
title,
|
|
600
|
+
content,
|
|
601
|
+
forceIndex: item.forceIndex ?? true,
|
|
602
|
+
type: "QA",
|
|
603
|
+
published: item.published ?? true,
|
|
604
|
+
taskType: item.taskType ?? "MANUAL",
|
|
605
|
+
metadata: item.metadata ?? { tags: [] },
|
|
606
|
+
directoryId: item.directoryId ?? "",
|
|
607
|
+
isVisible: item.isVisible ?? true
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/kus`, payload);
|
|
611
|
+
}
|
|
612
|
+
async createDocuments(documents, directoryId, taskType) {
|
|
613
|
+
if (!documents.length) throw new Error("documents must be a non-empty array");
|
|
614
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/documents`, {
|
|
615
|
+
documents,
|
|
616
|
+
published: true,
|
|
617
|
+
taskType,
|
|
618
|
+
taskUrl: "MANUAL",
|
|
619
|
+
isFullSiteCrawl: false,
|
|
620
|
+
metadata: { tags: [] },
|
|
621
|
+
directoryId,
|
|
622
|
+
isVisible: true
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
async uploadTempFile(filePath, forKnowledge = false) {
|
|
626
|
+
const credentialPath = forKnowledge ? `${this.qamPrefix}/project/${await this.projectId()}/sts_credentials` : `/api_v2/gpt/project/${await this.projectId()}/sts_credentials`;
|
|
627
|
+
const credentials = asObject(
|
|
628
|
+
await this.get(credentialPath),
|
|
629
|
+
"STS credentials must be an object"
|
|
630
|
+
);
|
|
631
|
+
const required = ["accessKeyId", "secretAccessKey", "sessionToken", "region", "bucketName", "path"];
|
|
632
|
+
const missing = required.filter((key2) => !credentials[key2]);
|
|
633
|
+
if (missing.length) throw new Error(`knowledge STS credentials missing fields: ${missing.join(", ")}`);
|
|
634
|
+
const key = `${String(credentials.path)}${basename(filePath)}`;
|
|
635
|
+
const client = new S3Client({
|
|
636
|
+
region: String(credentials.region),
|
|
637
|
+
credentials: {
|
|
638
|
+
accessKeyId: String(credentials.accessKeyId),
|
|
639
|
+
secretAccessKey: String(credentials.secretAccessKey),
|
|
640
|
+
sessionToken: String(credentials.sessionToken)
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
const contentTypes = {
|
|
644
|
+
".doc": "application/msword",
|
|
645
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
646
|
+
".pdf": "application/pdf"
|
|
647
|
+
};
|
|
648
|
+
await client.send(new PutObjectCommand({
|
|
649
|
+
Bucket: String(credentials.bucketName),
|
|
650
|
+
Key: key,
|
|
651
|
+
Body: await readFile(filePath),
|
|
652
|
+
ContentType: contentTypes[extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
653
|
+
}));
|
|
654
|
+
return key;
|
|
655
|
+
}
|
|
656
|
+
async uploadKnowledgeFile(filePath) {
|
|
657
|
+
return this.uploadTempFile(filePath, true);
|
|
658
|
+
}
|
|
659
|
+
async downloadFile(path, target, query2) {
|
|
660
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
661
|
+
for (const [key, value] of Object.entries(query2 ?? {})) url.searchParams.set(key, String(value));
|
|
662
|
+
const cookie = this.env === "prod" ? "_token" : "_token_staging";
|
|
663
|
+
const response = await fetch(url, {
|
|
664
|
+
headers: { accept: "*/*", origin: this.baseUrl, referer: `${this.baseUrl}/chatbot`, cookie: `${cookie}=${this.session.tenant.businessToken}` },
|
|
665
|
+
signal: AbortSignal.timeout(3e4)
|
|
666
|
+
});
|
|
667
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} GET ${path}: ${await response.text()}`);
|
|
668
|
+
await mkdir(dirname(target), { recursive: true });
|
|
669
|
+
await writeFile(target, new Uint8Array(await response.arrayBuffer()));
|
|
670
|
+
return target;
|
|
671
|
+
}
|
|
672
|
+
async downloadLabelTemplate(templateType, target) {
|
|
673
|
+
if (!["VOC_LABEL", "VOC_LABEL_INIT"].includes(templateType)) throw new Error("template type must be VOC_LABEL or VOC_LABEL_INIT");
|
|
674
|
+
return this.downloadFile(`/api_v2/gpt/label/project/${await this.projectId()}/downloadLabelTemplate`, target, { type: templateType });
|
|
675
|
+
}
|
|
676
|
+
async downloadLabels(treeId, target) {
|
|
677
|
+
return this.downloadFile(`/api_v2/gpt/label/project/${await this.projectId()}/downloadLabel`, target, { treeId: asPositiveInt(treeId, "tree id must be a positive integer") });
|
|
678
|
+
}
|
|
679
|
+
async uploadLabelFile(treeId, filePath, init = false) {
|
|
680
|
+
const fileKey = await this.uploadTempFile(filePath);
|
|
681
|
+
const result = await this.post(`/api_v2/gpt/label/project/${await this.projectId()}/uploadLabel`, {
|
|
682
|
+
treeId: asPositiveInt(treeId, "tree id must be a positive integer"),
|
|
683
|
+
file: fileKey,
|
|
684
|
+
...init ? { uploadWay: "INIT" } : {}
|
|
685
|
+
});
|
|
686
|
+
return { fileKey, result };
|
|
687
|
+
}
|
|
688
|
+
async updateKus(value) {
|
|
689
|
+
const source = Array.isArray(value) ? value : asObject(value, "batch knowledge update must be an array or object with list").list;
|
|
690
|
+
if (!Array.isArray(source) || !source.length) throw new Error("batch knowledge update requires a non-empty array");
|
|
691
|
+
const list = [];
|
|
692
|
+
for (const [index, value2] of source.entries()) {
|
|
693
|
+
const row = { ...asObject(value2, `batch knowledge item #${index + 1} must be an object`) };
|
|
694
|
+
row.kuId = asPositiveInt(row.kuId ?? row.id, `batch knowledge item #${index + 1} requires kuId or id`);
|
|
695
|
+
delete row.id;
|
|
696
|
+
if (row.directoryId === void 0) {
|
|
697
|
+
const current = asObject(await this.getKnowledge(String(row.kuId)), `could not read knowledge ${row.kuId} before update`);
|
|
698
|
+
if (current.directoryId !== void 0) row.directoryId = current.directoryId;
|
|
699
|
+
}
|
|
700
|
+
list.push(row);
|
|
701
|
+
}
|
|
702
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/kus`, { list });
|
|
703
|
+
}
|
|
704
|
+
async updateDocumentContent(id, type, changes) {
|
|
705
|
+
const normalized = knowledgeType(type);
|
|
706
|
+
if (normalized !== "FILE" && normalized !== "WEBSITE") throw new Error("document type must be FILE or WEBSITE");
|
|
707
|
+
const unknown = Object.keys(changes).filter((key) => !["fileText", "filePath", "title"].includes(key));
|
|
708
|
+
if (unknown.length) throw new Error(`unsupported document content fields: ${unknown.sort().join(", ")}`);
|
|
709
|
+
if (changes.fileText === void 0 && changes.filePath === void 0) throw new Error("document update requires fileText or filePath");
|
|
710
|
+
const documentId = asPositiveInt(id, "document id must be a positive integer");
|
|
711
|
+
const current = asObject(await this.getDocument(id), `could not read document ${id} before update`);
|
|
712
|
+
const payload = {
|
|
713
|
+
type: normalized,
|
|
714
|
+
directoryId: current.directoryId,
|
|
715
|
+
fileName: current.fileName ?? current.name ?? current.title,
|
|
716
|
+
title: current.title ?? current.fileName ?? current.name,
|
|
717
|
+
metadata: current.metadata && typeof current.metadata === "object" && !Array.isArray(current.metadata) ? current.metadata : {},
|
|
718
|
+
...changes,
|
|
719
|
+
knowledgeIds: [documentId]
|
|
720
|
+
};
|
|
721
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/document/${documentId}/contentV2`, payload);
|
|
722
|
+
}
|
|
723
|
+
async createProductKnowledge(data3) {
|
|
724
|
+
if (!data3.title || !data3.spuUrl) throw new Error("product knowledge requires title and spuUrl");
|
|
725
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/ku_product`, {
|
|
726
|
+
source: "MANUAL",
|
|
727
|
+
published: true,
|
|
728
|
+
isVisible: true,
|
|
729
|
+
metadata: { tags: [] },
|
|
730
|
+
...data3
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
async updateProductKnowledge(id, changes) {
|
|
734
|
+
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"]);
|
|
735
|
+
const unknown = Object.keys(changes).filter((key) => !allowed.has(key) && key !== "id" && key !== "kuProductId");
|
|
736
|
+
if (unknown.length) throw new Error(`unsupported product fields: ${unknown.sort().join(", ")}`);
|
|
737
|
+
const current = asObject(await this.getProductKnowledge(id), `could not read product knowledge ${id} before update`);
|
|
738
|
+
const payload = {};
|
|
739
|
+
for (const key of allowed) if (current[key] !== void 0) payload[key] = current[key];
|
|
740
|
+
for (const key of allowed) if (changes[key] !== void 0) payload[key] = changes[key];
|
|
741
|
+
const missing = ["title", "describe", "spuUrl", "image", "source", "published", "isVisible"].filter((key) => payload[key] === void 0 || payload[key] === "");
|
|
742
|
+
if (missing.length) throw new Error(`product detail is incomplete: ${missing.join(", ")}`);
|
|
743
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku_product`, { ...payload, kuProductId: asPositiveInt(id, "product id must be a positive integer") });
|
|
744
|
+
}
|
|
745
|
+
async updateKuProducts(value) {
|
|
746
|
+
const source = Array.isArray(value) ? value : asObject(value, "batch product update must be an array or object with list").list;
|
|
747
|
+
if (!Array.isArray(source) || !source.length) throw new Error("batch product update requires a non-empty array");
|
|
748
|
+
const list = source.map((value2, index) => {
|
|
749
|
+
const row = asObject(value2, `batch product item #${index + 1} must be an object`);
|
|
750
|
+
const entry2 = { id: asPositiveInt(row.id ?? row.kuProductId, `batch product item #${index + 1} requires id or kuProductId`) };
|
|
751
|
+
if (row.mainPush !== void 0) entry2.mainPush = Boolean(row.mainPush);
|
|
752
|
+
if (row.enable !== void 0) entry2.enable = Boolean(row.enable);
|
|
753
|
+
else if (row.published !== void 0) entry2.enable = Boolean(row.published);
|
|
754
|
+
return entry2;
|
|
755
|
+
});
|
|
756
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku_products`, { list });
|
|
757
|
+
}
|
|
758
|
+
async operateKnowledge(value, confirmDelete = false) {
|
|
759
|
+
const source = Array.isArray(value) ? value : asObject(value, "knowledge operation must be an array or object with requestList").requestList;
|
|
760
|
+
if (!Array.isArray(source) || !source.length) throw new Error("knowledge operation requires a non-empty array");
|
|
761
|
+
const supportedOperations = /* @__PURE__ */ new Set(["DELETE", "MOVE", "PUBLISH", "UNPUBLISH", "VISIBLE", "INVISIBLE"]);
|
|
762
|
+
const requestList = [];
|
|
763
|
+
for (const [index, value2] of source.entries()) {
|
|
764
|
+
const row = asObject(value2, `knowledge operation #${index + 1} must be an object`);
|
|
765
|
+
const type = knowledgeType(row.knowledgeType);
|
|
766
|
+
const operateType = String(row.operateType ?? "").toUpperCase();
|
|
767
|
+
if (!supportedOperations.has(operateType)) throw new Error(`unsupported knowledge operation: ${operateType}`);
|
|
768
|
+
const knowledgeId = asPositiveInt(row.knowledgeId, `knowledge operation #${index + 1} requires knowledgeId`);
|
|
769
|
+
await this.getKnowledgeByType(String(knowledgeId), type);
|
|
770
|
+
const normalized = { knowledgeType: type, knowledgeId, operateType };
|
|
771
|
+
if (operateType === "MOVE") normalized.directoryId = asPositiveInt(row.directoryId, "MOVE requires directoryId");
|
|
772
|
+
requestList.push(normalized);
|
|
773
|
+
}
|
|
774
|
+
if (requestList.some((item) => item.operateType === "DELETE") && !confirmDelete) throw new Error("DELETE requires --confirm-delete after explicit user acknowledgement");
|
|
775
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/updateKnowledge`, { requestList });
|
|
502
776
|
}
|
|
503
777
|
async listDirectories() {
|
|
504
778
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/directories`, { keyword: "", kuPublished: "" });
|
|
@@ -515,12 +789,111 @@ var SolveaClient = class {
|
|
|
515
789
|
async knowledgeTags(keyword = "") {
|
|
516
790
|
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/searchKeywordAndIntents`, { keyword });
|
|
517
791
|
}
|
|
792
|
+
async moveDirectory(id, parentId) {
|
|
793
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/directory/move`, { currentId: asPositiveInt(id, "directory id must be a positive integer"), parentId: Number(parentId) });
|
|
794
|
+
}
|
|
795
|
+
async getKnowledgeIsolationKeywords(id, type) {
|
|
796
|
+
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") });
|
|
797
|
+
}
|
|
798
|
+
async batchUpdateIsolationKeywordBindings(data3) {
|
|
799
|
+
const items = data3.items ?? [], directoryIds = data3.directoryIds ?? [], keywordIds = data3.keywordIds ?? [];
|
|
800
|
+
if (!Array.isArray(items) || !Array.isArray(directoryIds) || !Array.isArray(keywordIds)) throw new Error("items, directoryIds and keywordIds must be arrays");
|
|
801
|
+
if (!items.length && !directoryIds.length && !keywordIds.length) throw new Error("isolation keyword binding update has no targets or keyword IDs");
|
|
802
|
+
if (items.length && directoryIds.length) throw new Error("submit knowledge items and directory IDs in separate binding updates");
|
|
803
|
+
const normalizedItems = items.map((value, index) => {
|
|
804
|
+
const row = asObject(value, `isolation binding item #${index + 1} must be an object`);
|
|
805
|
+
if (!Array.isArray(row.keywordIds)) throw new Error("items[].keywordIds must be an array");
|
|
806
|
+
return {
|
|
807
|
+
knowledgeType: isolationKnowledgeType(row.knowledgeType ?? "QA"),
|
|
808
|
+
knowledgeId: asPositiveInt(row.knowledgeId, "items[].knowledgeId must be a positive integer"),
|
|
809
|
+
keywordIds: [...new Set(row.keywordIds.map((value2) => asPositiveInt(value2, "items[].keywordIds must contain positive integer IDs")))]
|
|
810
|
+
};
|
|
811
|
+
});
|
|
812
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/knowledge/isolation_keywords/batch`, {
|
|
813
|
+
items: normalizedItems,
|
|
814
|
+
directoryIds: [...new Set(directoryIds.map((value) => asPositiveInt(value, "directoryIds must contain positive integer IDs")))],
|
|
815
|
+
keywordIds: [...new Set(keywordIds.map((value) => asPositiveInt(value, "keywordIds must contain positive integer IDs")))]
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
async applyIsolationKeywordBindings(data3) {
|
|
819
|
+
const operation = String(data3.operation ?? "append").toLowerCase();
|
|
820
|
+
if (!["append", "remove", "replace"].includes(operation)) throw new Error("operation must be append, remove, or replace");
|
|
821
|
+
const items = data3.items ?? [], directoryIds = data3.directoryIds ?? [];
|
|
822
|
+
if (!Array.isArray(items) || !Array.isArray(directoryIds) || !items.length && !directoryIds.length) throw new Error("binding update requires knowledge items or directory IDs");
|
|
823
|
+
if (items.length && directoryIds.length) throw new Error("submit knowledge items and directory IDs in separate binding updates");
|
|
824
|
+
if (directoryIds.length && operation !== "replace") throw new Error("directory bindings require operation=replace");
|
|
825
|
+
if ((directoryIds.length || operation === "replace") && data3.confirmReplace !== true) throw new Error("binding replacement requires confirmReplace=true after explicit user confirmation");
|
|
826
|
+
const resolvedItems = [];
|
|
827
|
+
for (const [index, value] of items.entries()) {
|
|
828
|
+
const row = asObject(value, `isolation binding item #${index + 1} must be an object`);
|
|
829
|
+
const id = asPositiveInt(row.knowledgeId, "items[].knowledgeId must be a positive integer");
|
|
830
|
+
const type = knowledgeType(row.knowledgeType);
|
|
831
|
+
if (!Array.isArray(row.keywordIds)) throw new Error("items[].keywordIds must be an array");
|
|
832
|
+
const requestedIds = row.keywordIds.map((value2) => asPositiveInt(value2, "items[].keywordIds must contain positive integer IDs"));
|
|
833
|
+
await this.getKnowledgeByType(String(id), type);
|
|
834
|
+
const existingResponse = await this.getKnowledgeIsolationKeywords(String(id), type);
|
|
835
|
+
const existingRows = Array.isArray(existingResponse) ? existingResponse : existingResponse && typeof existingResponse === "object" && Array.isArray(existingResponse.list) ? existingResponse.list : [];
|
|
836
|
+
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"));
|
|
837
|
+
const finalIds = operation === "append" ? [.../* @__PURE__ */ new Set([...existingIds, ...requestedIds])] : operation === "remove" ? existingIds.filter((id2) => !requestedIds.includes(id2)) : [...new Set(requestedIds)];
|
|
838
|
+
resolvedItems.push({ knowledgeType: type, knowledgeId: id, keywordIds: finalIds });
|
|
839
|
+
}
|
|
840
|
+
for (const value of directoryIds) await this.listKnowledge({ directoryId: asPositiveInt(value, "directoryIds must contain positive integer IDs"), pageIndex: 1, pageSize: 1 });
|
|
841
|
+
return this.batchUpdateIsolationKeywordBindings({ items: resolvedItems, directoryIds, keywordIds: data3.keywordIds ?? [] });
|
|
842
|
+
}
|
|
843
|
+
async editKnowledgeTags(data3) {
|
|
844
|
+
const knowledgeIds = data3.knowledgeIds, requestedTags = data3.tagList;
|
|
845
|
+
if (!Array.isArray(knowledgeIds) || !knowledgeIds.length) throw new Error("edit knowledge tags requires non-empty knowledgeIds");
|
|
846
|
+
if (!Array.isArray(requestedTags) || requestedTags.some((value) => typeof value !== "string" || !value.trim())) throw new Error("tagList must contain non-empty strings");
|
|
847
|
+
const operation = String(data3.operation ?? "append");
|
|
848
|
+
if (!["append", "replace", "remove"].includes(operation)) throw new Error("tag operation must be append, replace, or remove");
|
|
849
|
+
const type = knowledgeType(data3.type ?? "QA");
|
|
850
|
+
const results = [];
|
|
851
|
+
for (const value of knowledgeIds) {
|
|
852
|
+
const id = asPositiveInt(value, "knowledgeIds must contain positive integer IDs");
|
|
853
|
+
const current = asObject(await this.getKnowledgeByType(String(id), type), `could not read knowledge ${id} before tag update`);
|
|
854
|
+
const metadata = current.metadata && typeof current.metadata === "object" && !Array.isArray(current.metadata) ? current.metadata : {};
|
|
855
|
+
const existing = Array.isArray(current.tags) ? current.tags : Array.isArray(metadata.tags) ? metadata.tags : [];
|
|
856
|
+
const requested = requestedTags.map(String);
|
|
857
|
+
const tagList = operation === "append" ? [.../* @__PURE__ */ new Set([...existing.map(String), ...requested])] : operation === "remove" ? existing.map(String).filter((tag) => !requested.includes(tag)) : [...new Set(requested)];
|
|
858
|
+
results.push(await this.post(`${this.qamPrefix}/project/${await this.projectId()}/editTags`, { type, knowledgeIds: [id], tagList }));
|
|
859
|
+
}
|
|
860
|
+
return { success: true, results };
|
|
861
|
+
}
|
|
518
862
|
async transcript(chatId) {
|
|
519
863
|
return this.get(`/api_v2/gpt/project/${await this.projectId()}/transcript/${encodeURIComponent(chatId)}`, { isCompare: true });
|
|
520
864
|
}
|
|
521
865
|
async listTranscripts(data3) {
|
|
522
866
|
return this.post(`/api_v2/gpt/project/${await this.projectId()}/transcripts/filter`, data3);
|
|
523
867
|
}
|
|
868
|
+
async getConversationsByIntent(intent, limit = 50) {
|
|
869
|
+
const intentResponse = await this.listIntents("");
|
|
870
|
+
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 : [];
|
|
871
|
+
const matched = intents.find((value) => value && typeof value === "object" && String(value.name ?? "") === intent);
|
|
872
|
+
if (!matched?.id) return [];
|
|
873
|
+
const response = await this.listTranscripts({ pageSize: limit, pageIndex: 1, intentId: Number(matched.id) });
|
|
874
|
+
const rows = Array.isArray(response) ? response : response && typeof response === "object" && Array.isArray(response.list) ? response.list : [];
|
|
875
|
+
const result = [];
|
|
876
|
+
for (const value of rows) {
|
|
877
|
+
if (!value || typeof value !== "object") continue;
|
|
878
|
+
const row = value, chatId = row.chatId ?? row.id;
|
|
879
|
+
if (!chatId) continue;
|
|
880
|
+
try {
|
|
881
|
+
const detail = asObject(await this.transcript(String(chatId)), `transcript ${chatId} must be an object`);
|
|
882
|
+
const turns = Array.isArray(detail.turns) ? detail.turns : Array.isArray(detail.messages) ? detail.messages : [];
|
|
883
|
+
const messages = [];
|
|
884
|
+
let metadata = {};
|
|
885
|
+
for (const turnValue of turns) {
|
|
886
|
+
if (!turnValue || typeof turnValue !== "object") continue;
|
|
887
|
+
const turn = turnValue, role = turn.role ?? turn.type, content = turn.content ?? turn.message;
|
|
888
|
+
if (role === "USER" && content) messages.push(content);
|
|
889
|
+
if (!Object.keys(metadata).length && turn.metadata && typeof turn.metadata === "object" && !Array.isArray(turn.metadata)) metadata = turn.metadata;
|
|
890
|
+
}
|
|
891
|
+
if (messages.length) result.push({ source_chat_id: chatId, intent, messages, metadata });
|
|
892
|
+
} catch {
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
524
897
|
async resolveTranscriptChatId(externalId, pageSize2 = 10) {
|
|
525
898
|
const result = await this.listTranscripts({ pageSize: pageSize2, pageIndex: 1, debug: false, keyword: externalId });
|
|
526
899
|
const rows = Array.isArray(result) ? result : result?.list;
|
|
@@ -535,10 +908,10 @@ var SolveaClient = class {
|
|
|
535
908
|
};
|
|
536
909
|
|
|
537
910
|
// src/solvea/input.ts
|
|
538
|
-
import { readFile } from "fs/promises";
|
|
911
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
539
912
|
async function jsonData(args, required = true) {
|
|
540
913
|
const file = option(args, "--data-file");
|
|
541
|
-
const raw = file ? await
|
|
914
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data");
|
|
542
915
|
if (!raw) {
|
|
543
916
|
if (required) throw new Error("--data or --data-file is required");
|
|
544
917
|
return {};
|
|
@@ -549,7 +922,7 @@ async function jsonData(args, required = true) {
|
|
|
549
922
|
}
|
|
550
923
|
async function jsonValue(args) {
|
|
551
924
|
const file = option(args, "--data-file");
|
|
552
|
-
const raw = file ? await
|
|
925
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data", true);
|
|
553
926
|
return JSON.parse(raw);
|
|
554
927
|
}
|
|
555
928
|
function requireFlag(args, name, command) {
|
|
@@ -649,9 +1022,9 @@ var reportBody = async (args) => ({
|
|
|
649
1022
|
...await data(args)
|
|
650
1023
|
});
|
|
651
1024
|
var handlers2 = {
|
|
652
|
-
"chat-stream": async (c, a) => c.
|
|
1025
|
+
"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
1026
|
"update-chatflow": async (c, a) => c.updateChatflow(option(a, "--agent-id", true), await jsonData(a), flag(a, "--publish")),
|
|
654
|
-
"get-conversations-by-intent":
|
|
1027
|
+
"get-conversations-by-intent": (c, a) => c.getConversationsByIntent(option(a, "--intent", true), Number(option(a, "--limit") ?? 50)),
|
|
655
1028
|
"resolve-transcript-chat-id": async (c, a) => c.resolveTranscriptChatId(option(a, "--external-id", true), Number(option(a, "--page-size") ?? 10)),
|
|
656
1029
|
"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
1030
|
"create-isolation-keyword": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/isolation_keyword`, { keyword: option(a, "--keyword", true) }),
|
|
@@ -660,16 +1033,16 @@ var handlers2 = {
|
|
|
660
1033
|
requireFlag(a, "--confirm-delete", "delete-isolation-keyword");
|
|
661
1034
|
return c.rawCall("DELETE", `${c.qamPath}/project/${await project(c)}/isolation_keyword/${requireId(a)}`);
|
|
662
1035
|
},
|
|
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.
|
|
1036
|
+
"get-knowledge-isolation-keywords": (c, a) => c.getKnowledgeIsolationKeywords(requireId(a), option(a, "--type", true)),
|
|
1037
|
+
"batch-update-isolation-keyword-bindings": async (c, a) => c.applyIsolationKeywordBindings(await data(a)),
|
|
1038
|
+
"edit-knowledge-tags": async (c, a) => c.editKnowledgeTags(await data(a)),
|
|
1039
|
+
"move-directory": (c, a) => c.moveDirectory(requireId(a), option(a, "--parent-id", true)),
|
|
1040
|
+
"operate-knowledge": async (c, a) => c.operateKnowledge(await jsonValue(a), flag(a, "--confirm-delete")),
|
|
1041
|
+
"batch-update-knowledge": async (c, a) => c.updateKus(await jsonValue(a)),
|
|
1042
|
+
"update-document-content": async (c, a) => c.updateDocumentContent(requireId(a), option(a, "--type", true), await data(a)),
|
|
1043
|
+
"create-product-knowledge": async (c, a) => c.createProductKnowledge(await data(a)),
|
|
1044
|
+
"update-product-knowledge": async (c, a) => c.updateProductKnowledge(requireId(a), await data(a)),
|
|
1045
|
+
"batch-update-products": async (c, a) => c.updateKuProducts(await jsonValue(a)),
|
|
673
1046
|
"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
1047
|
"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
1048
|
"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 +1059,12 @@ var handlers2 = {
|
|
|
686
1059
|
requireFlag(a, "--confirm-delete", "delete-label-node");
|
|
687
1060
|
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
1061
|
},
|
|
689
|
-
"download-label-template": async (c, a) => c.
|
|
690
|
-
"download-labels": async (c, a) => c.
|
|
691
|
-
"upload-labels":
|
|
1062
|
+
"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`) }),
|
|
1063
|
+
"download-labels": async (c, a) => ({ file: await c.downloadLabels(option(a, "--tree-id", true), option(a, "--output") ?? `labels_${option(a, "--tree-id", true)}.xlsx`) }),
|
|
1064
|
+
"upload-labels": (c, a) => c.uploadLabelFile(option(a, "--tree-id", true), option(a, "--file", true), flag(a, "--init")),
|
|
692
1065
|
"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
1066
|
"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.
|
|
1067
|
+
"upload-batch-test-file": async (c, a) => ({ file: await c.uploadTempFile(option(a, "--file", true)) }),
|
|
695
1068
|
"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
1069
|
"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
1070
|
"list-voice-agents": (c) => c.listAgents("voice"),
|
|
@@ -732,7 +1105,7 @@ function enabled(args, command) {
|
|
|
732
1105
|
}
|
|
733
1106
|
async function data2(args) {
|
|
734
1107
|
const file = option(args, "--data-file");
|
|
735
|
-
const raw = file ? await
|
|
1108
|
+
const raw = file ? await readFile3(file, "utf8") : option(args, "--data");
|
|
736
1109
|
if (!raw) throw new Error("--data or --data-file is required");
|
|
737
1110
|
const parsed = JSON.parse(raw);
|
|
738
1111
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
|
|
@@ -858,7 +1231,7 @@ async function runSolveaCommand(session, command, args) {
|
|
|
858
1231
|
case "list-knowledge":
|
|
859
1232
|
return client.listKnowledge(await knowledgeFilters(args));
|
|
860
1233
|
case "get-knowledge":
|
|
861
|
-
return client.
|
|
1234
|
+
return client.getKnowledgeByType(id(), option(args, "--type", true));
|
|
862
1235
|
case "update-knowledge":
|
|
863
1236
|
return client.updateKnowledge(id(), await data2(args));
|
|
864
1237
|
case "upload-qa":
|
|
@@ -939,7 +1312,7 @@ function isNativeSolveaCommand(command) {
|
|
|
939
1312
|
}
|
|
940
1313
|
|
|
941
1314
|
// src/business.ts
|
|
942
|
-
var packageRoot = () => resolve(
|
|
1315
|
+
var packageRoot = () => resolve(dirname2(fileURLToPath(import.meta.url)), "..");
|
|
943
1316
|
async function runApi(session, command, args) {
|
|
944
1317
|
if (isNativeSolveaCommand(command)) {
|
|
945
1318
|
console.log(JSON.stringify(await runSolveaCommand(session, command, args), null, 2));
|
|
@@ -1000,7 +1373,7 @@ var resources = {
|
|
|
1000
1373
|
},
|
|
1001
1374
|
knowledge: {
|
|
1002
1375
|
list: action("list-knowledge", "List knowledge", "[--type <type>] [--page <number>] [--page-size <number>] [--keyword <text>] [--data <json> | --data-file <path>]"),
|
|
1003
|
-
get: action("get-knowledge", "Get one knowledge item", "--id <knowledge-id>"),
|
|
1376
|
+
get: action("get-knowledge", "Get one knowledge item", "--id <knowledge-id> --type <QA|LEARN_SELF|FILE|WEBSITE|PRODUCT>"),
|
|
1004
1377
|
update: action("update-knowledge", "Update a knowledge item", "--id <knowledge-id> (--data <json> | --data-file <path>)"),
|
|
1005
1378
|
"upload-qa": action("upload-qa", "Upload QA items", "--data <json> | --data-file <path>"),
|
|
1006
1379
|
tags: action("list-knowledge-tags", "List knowledge tags", "[--keyword <keyword>]")
|
|
@@ -1135,7 +1508,7 @@ function publicTenant(t) {
|
|
|
1135
1508
|
|
|
1136
1509
|
// src/update.ts
|
|
1137
1510
|
import { spawn } from "child_process";
|
|
1138
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
1511
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
1139
1512
|
import { homedir, platform } from "os";
|
|
1140
1513
|
import { isAbsolute, join as join2 } from "path";
|
|
1141
1514
|
var exec = (command, args, options3 = {}) => new Promise((resolve5, reject) => {
|
|
@@ -1213,8 +1586,8 @@ async function installScheduler() {
|
|
|
1213
1586
|
const dir = join2(homedir(), "Library/LaunchAgents");
|
|
1214
1587
|
const path = join2(dir, "com.solvea.cxclaw.update.plist");
|
|
1215
1588
|
const programArguments = [invocation.command, ...invocation.args].map((value) => `<string>${xml(value)}</string>`).join("");
|
|
1216
|
-
await
|
|
1217
|
-
await
|
|
1589
|
+
await mkdir2(dir, { recursive: true });
|
|
1590
|
+
await writeFile2(
|
|
1218
1591
|
path,
|
|
1219
1592
|
`<?xml version="1.0" encoding="UTF-8"?>
|
|
1220
1593
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -1228,15 +1601,15 @@ async function installScheduler() {
|
|
|
1228
1601
|
if (platform() === "linux") {
|
|
1229
1602
|
const dir = join2(homedir(), ".config/systemd/user");
|
|
1230
1603
|
const command = [invocation.command, ...invocation.args].map(systemdArg).join(" ");
|
|
1231
|
-
await
|
|
1232
|
-
await
|
|
1604
|
+
await mkdir2(dir, { recursive: true });
|
|
1605
|
+
await writeFile2(
|
|
1233
1606
|
join2(dir, "cxclaw-update.service"),
|
|
1234
1607
|
`[Service]
|
|
1235
1608
|
Type=oneshot
|
|
1236
1609
|
ExecStart=${command}
|
|
1237
1610
|
`
|
|
1238
1611
|
);
|
|
1239
|
-
await
|
|
1612
|
+
await writeFile2(
|
|
1240
1613
|
join2(dir, "cxclaw-update.timer"),
|
|
1241
1614
|
"[Timer]\nOnBootSec=5m\nOnUnitActiveSec=6h\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
|
|
1242
1615
|
);
|
|
@@ -1387,14 +1760,14 @@ async function runRuntime(action2, args) {
|
|
|
1387
1760
|
// src/workflows/solvea-kit.ts
|
|
1388
1761
|
import ExcelJS from "exceljs";
|
|
1389
1762
|
import { promises as fs2 } from "fs";
|
|
1390
|
-
import { dirname as
|
|
1763
|
+
import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
|
|
1391
1764
|
var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
|
|
1392
1765
|
var outputDir = (owner) => join4(workspace2(), "outputs", owner);
|
|
1393
1766
|
var stateDir = (owner) => join4(workspace2(), "state", owner);
|
|
1394
1767
|
var now = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1395
1768
|
var readJson2 = async (path) => JSON.parse(await fs2.readFile(path, "utf8"));
|
|
1396
1769
|
var writeJson2 = async (path, value) => {
|
|
1397
|
-
await fs2.mkdir(
|
|
1770
|
+
await fs2.mkdir(dirname3(path), { recursive: true });
|
|
1398
1771
|
await fs2.writeFile(path, JSON.stringify(value, null, 2));
|
|
1399
1772
|
};
|
|
1400
1773
|
var INIT_NOTICE = `The information of this row cannot be deleted! Tips:
|
|
@@ -1448,7 +1821,7 @@ async function generateLabelImport(args) {
|
|
|
1448
1821
|
sheet.columns.forEach((column) => {
|
|
1449
1822
|
column.width = 18;
|
|
1450
1823
|
});
|
|
1451
|
-
await fs2.mkdir(
|
|
1824
|
+
await fs2.mkdir(dirname3(output), { recursive: true });
|
|
1452
1825
|
await workbook.xlsx.writeFile(output);
|
|
1453
1826
|
return { file: output, format: init ? "init" : "update", rows: labels.length };
|
|
1454
1827
|
}
|
|
@@ -1472,8 +1845,8 @@ async function runSolveaKitWorkflow(action2, args) {
|
|
|
1472
1845
|
}
|
|
1473
1846
|
|
|
1474
1847
|
// src/workflows/support.ts
|
|
1475
|
-
import { readFile as
|
|
1476
|
-
import { basename as
|
|
1848
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1849
|
+
import { basename as basename3 } from "path";
|
|
1477
1850
|
var safe = (value) => encodeURIComponent(value);
|
|
1478
1851
|
var docsMcpUrl = process.env.CXCLAW_DOCS_MCP_URL ?? "https://solvea-39f11a36.mintlify.app/mcp";
|
|
1479
1852
|
var publicDocsPrefix = "https://www.voc.ai/cn/docs/";
|
|
@@ -1547,7 +1920,7 @@ async function runSupport(session, action2, args) {
|
|
|
1547
1920
|
const attachments = [];
|
|
1548
1921
|
for (const file of options2(args, "--attachment")) {
|
|
1549
1922
|
const form = new FormData();
|
|
1550
|
-
form.append("file", new Blob([await
|
|
1923
|
+
form.append("file", new Blob([await readFile4(file)]), basename3(file));
|
|
1551
1924
|
const tenant = session.tenant;
|
|
1552
1925
|
const base = tenant?.businessUrl?.replace(/\/$/, "");
|
|
1553
1926
|
if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
|
|
@@ -1578,24 +1951,24 @@ async function runSupport(session, action2, args) {
|
|
|
1578
1951
|
}
|
|
1579
1952
|
|
|
1580
1953
|
// src/workflows/common.ts
|
|
1581
|
-
import { mkdir as
|
|
1582
|
-
import { dirname as
|
|
1954
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
1955
|
+
import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
|
|
1583
1956
|
import { homedir as homedir2 } from "os";
|
|
1584
1957
|
var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join5(homedir2(), ".cxclaw", "workspace"));
|
|
1585
1958
|
var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join5(workspaceRoot(), "state"), domain);
|
|
1586
1959
|
var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join5(workspaceRoot(), "outputs"), domain);
|
|
1587
1960
|
async function ensureParent(file) {
|
|
1588
|
-
await
|
|
1961
|
+
await mkdir3(dirname4(file), { recursive: true });
|
|
1589
1962
|
}
|
|
1590
1963
|
async function ensureDir(directory) {
|
|
1591
|
-
await
|
|
1964
|
+
await mkdir3(directory, { recursive: true });
|
|
1592
1965
|
}
|
|
1593
1966
|
async function readJson3(file) {
|
|
1594
|
-
return JSON.parse(await
|
|
1967
|
+
return JSON.parse(await readFile5(file, "utf8"));
|
|
1595
1968
|
}
|
|
1596
1969
|
async function writeJson3(file, value) {
|
|
1597
1970
|
await ensureParent(file);
|
|
1598
|
-
await
|
|
1971
|
+
await writeFile3(file, `${JSON.stringify(value, null, 2)}
|
|
1599
1972
|
`, "utf8");
|
|
1600
1973
|
}
|
|
1601
1974
|
function intOption(args, name, fallback) {
|
|
@@ -1613,7 +1986,7 @@ function htmlEscape(value) {
|
|
|
1613
1986
|
}
|
|
1614
1987
|
async function writeHtml(file, title, body) {
|
|
1615
1988
|
await ensureParent(file);
|
|
1616
|
-
await
|
|
1989
|
+
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");
|
|
1617
1990
|
}
|
|
1618
1991
|
function asRows(value) {
|
|
1619
1992
|
if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
|
|
@@ -1820,8 +2193,8 @@ async function runLocalAgentWorkflow(action2, args) {
|
|
|
1820
2193
|
}
|
|
1821
2194
|
async function writeFileText(file, content) {
|
|
1822
2195
|
await ensureParent(file);
|
|
1823
|
-
const { writeFile:
|
|
1824
|
-
await
|
|
2196
|
+
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
2197
|
+
await writeFile4(file, `${content}
|
|
1825
2198
|
`, "utf8");
|
|
1826
2199
|
}
|
|
1827
2200
|
|
|
@@ -1859,9 +2232,9 @@ async function createSheet(args) {
|
|
|
1859
2232
|
return { output, count: cases.length, mode: option(args, "--mode") };
|
|
1860
2233
|
}
|
|
1861
2234
|
async function submit(client, args) {
|
|
1862
|
-
const file = option(args, "--file", true);
|
|
2235
|
+
const file = await client.uploadTempFile(option(args, "--file", true));
|
|
1863
2236
|
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")) } : {} });
|
|
1864
|
-
return result;
|
|
2237
|
+
return { fileKey: file, result };
|
|
1865
2238
|
}
|
|
1866
2239
|
async function runAgentTester(session, action2, args) {
|
|
1867
2240
|
if (!action2) throw new Error("agent-tester action is required");
|
|
@@ -2144,6 +2517,56 @@ async function extractWebQa(args) {
|
|
|
2144
2517
|
}
|
|
2145
2518
|
return { outputDir: outputDir2, files, count: files.length };
|
|
2146
2519
|
}
|
|
2520
|
+
var flattenDirectories = (nodes, result = []) => {
|
|
2521
|
+
for (const node of nodes) {
|
|
2522
|
+
result.push(node);
|
|
2523
|
+
if (Array.isArray(node.children)) flattenDirectories(node.children.filter((child) => child && typeof child === "object"), result);
|
|
2524
|
+
}
|
|
2525
|
+
return result;
|
|
2526
|
+
};
|
|
2527
|
+
function validateQaItems(values, maxDepth) {
|
|
2528
|
+
if (!values.length) throw new Error("QA plan must contain at least one item");
|
|
2529
|
+
return values.map((value, index) => {
|
|
2530
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`QA item #${index + 1} must be an object`);
|
|
2531
|
+
const item = value;
|
|
2532
|
+
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`);
|
|
2533
|
+
if (item.dir_path.length > maxDepth) throw new Error(`QA item #${index + 1} dir_path exceeds maximum depth ${maxDepth}`);
|
|
2534
|
+
const dirPath = item.dir_path.map((name, depth) => {
|
|
2535
|
+
if (typeof name !== "string" || !name.trim() || /[\\/]/.test(name)) throw new Error(`QA item #${index + 1} dir_path[${depth}] must be a folder name`);
|
|
2536
|
+
return name.trim();
|
|
2537
|
+
});
|
|
2538
|
+
const title = typeof item.question === "string" ? item.question.trim() : "";
|
|
2539
|
+
const content = typeof item.answer === "string" ? item.answer.trim() : "";
|
|
2540
|
+
if (!title) throw new Error(`QA item #${index + 1} question must be a non-empty string`);
|
|
2541
|
+
if (!content) throw new Error(`QA item #${index + 1} answer must be a non-empty markdown string`);
|
|
2542
|
+
return { dirPath, title, content };
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
async function resolveDirectory(client, path, directories, cache) {
|
|
2546
|
+
let parentId = 0;
|
|
2547
|
+
const prefix = [];
|
|
2548
|
+
for (const name of path) {
|
|
2549
|
+
prefix.push(name);
|
|
2550
|
+
const key = prefix.join("/");
|
|
2551
|
+
const cached = cache.get(key);
|
|
2552
|
+
if (cached !== void 0) {
|
|
2553
|
+
parentId = cached;
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2556
|
+
const match = directories.find((directory) => String(directory.name ?? "") === name && Number(directory.parentId ?? 0) === parentId);
|
|
2557
|
+
if (match) parentId = Number(match.id ?? match.directoryId);
|
|
2558
|
+
else {
|
|
2559
|
+
const createdParentId = parentId;
|
|
2560
|
+
const created = await client.createFolder(name, parentId);
|
|
2561
|
+
const id = Number(created?.id ?? created?.directoryId);
|
|
2562
|
+
if (!Number.isSafeInteger(id) || id <= 0) throw new Error(`could not create directory ${key}: response has no directory id`);
|
|
2563
|
+
parentId = id;
|
|
2564
|
+
directories.push({ id, name, parentId: Number(created?.parentId ?? createdParentId) });
|
|
2565
|
+
}
|
|
2566
|
+
cache.set(key, parentId);
|
|
2567
|
+
}
|
|
2568
|
+
return parentId;
|
|
2569
|
+
}
|
|
2147
2570
|
async function importQa(client, args) {
|
|
2148
2571
|
const qaFile = option(args, "--qa-file");
|
|
2149
2572
|
const fromDir = option(args, "--from-dir");
|
|
@@ -2155,23 +2578,67 @@ async function importQa(client, args) {
|
|
|
2155
2578
|
}
|
|
2156
2579
|
if (fromDir) {
|
|
2157
2580
|
const { readdir } = await import("fs/promises");
|
|
2158
|
-
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json"))) {
|
|
2581
|
+
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json")).sort()) {
|
|
2159
2582
|
const value = await readJson3(join8(fromDir, file));
|
|
2160
2583
|
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2161
2584
|
}
|
|
2162
2585
|
}
|
|
2586
|
+
const maxDepth = intOption(args, "--max-depth", 3);
|
|
2587
|
+
if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
|
|
2588
|
+
const normalized = validateQaItems(items, maxDepth);
|
|
2163
2589
|
const plan2 = { items, count: items.length, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2164
2590
|
const planFile = join8(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
|
|
2165
2591
|
await writeJson3(planFile, plan2);
|
|
2166
2592
|
if (flag(args, "--dry-run")) return { dryRun: true, planFile, count: items.length };
|
|
2167
|
-
|
|
2593
|
+
const directories = flattenDirectories(asRows(await client.listDirectories()));
|
|
2594
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2595
|
+
const payload = [];
|
|
2596
|
+
for (const item of normalized) payload.push({ title: item.title, content: item.content, directoryId: await resolveDirectory(client, item.dirPath, directories, cache) });
|
|
2597
|
+
return { planFile, count: payload.length, result: await client.uploadQa(payload) };
|
|
2168
2598
|
}
|
|
2169
2599
|
async function importSources(client, args) {
|
|
2170
|
-
const
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2600
|
+
const value = await readJson3(option(args, "--plan-file", true));
|
|
2601
|
+
const source = Array.isArray(value) ? value : value && typeof value === "object" && Array.isArray(value.items) ? value.items : [];
|
|
2602
|
+
if (!source.length) throw new Error("source document plan must be a non-empty array");
|
|
2603
|
+
const maxDepth = intOption(args, "--max-depth", 3);
|
|
2604
|
+
if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
|
|
2605
|
+
const { stat } = await import("fs/promises");
|
|
2606
|
+
const normalized = [];
|
|
2607
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
2608
|
+
for (const [index, raw] of source.entries()) {
|
|
2609
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`source document #${index + 1} must be an object`);
|
|
2610
|
+
const item = raw;
|
|
2611
|
+
const itemSource = typeof item.source === "string" ? item.source.trim() : "";
|
|
2612
|
+
if (!itemSource) throw new Error(`source document #${index + 1} needs a non-empty source`);
|
|
2613
|
+
const path = item.dir_path ?? [];
|
|
2614
|
+
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`);
|
|
2615
|
+
if (path.length > maxDepth) throw new Error(`source document #${index + 1} dir_path exceeds maximum depth ${maxDepth}`);
|
|
2616
|
+
const isUrl = /^https?:\/\//i.test(itemSource);
|
|
2617
|
+
if (!isUrl) {
|
|
2618
|
+
if (![".doc", ".docx", ".pdf"].some((extension) => itemSource.toLowerCase().endsWith(extension))) throw new Error(`source document #${index + 1} supports .doc, .docx, or .pdf`);
|
|
2619
|
+
const info = await stat(itemSource).catch(() => void 0);
|
|
2620
|
+
if (!info?.isFile()) throw new Error(`source document #${index + 1} file not found: ${itemSource}`);
|
|
2621
|
+
}
|
|
2622
|
+
const skippedDuplicate = isUrl && seenUrls.has(itemSource);
|
|
2623
|
+
if (isUrl) seenUrls.add(itemSource);
|
|
2624
|
+
normalized.push({ source: itemSource, dirPath: path.map(String).map((name) => name.trim()), isUrl, skippedDuplicate });
|
|
2625
|
+
}
|
|
2626
|
+
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" })) };
|
|
2627
|
+
const directories = flattenDirectories(asRows(await client.listDirectories()));
|
|
2628
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2629
|
+
const results = [];
|
|
2630
|
+
for (const item of normalized) {
|
|
2631
|
+
if (item.skippedDuplicate) {
|
|
2632
|
+
results.push({ source: item.source, status: "skipped_duplicate" });
|
|
2633
|
+
continue;
|
|
2634
|
+
}
|
|
2635
|
+
const directoryId = await resolveDirectory(client, item.dirPath, directories, cache);
|
|
2636
|
+
const filePath = item.isUrl ? item.source : await client.uploadKnowledgeFile(item.source);
|
|
2637
|
+
const documents = [{ filePath, docType: "TC", ...item.isUrl ? { fileType: "website" } : {} }];
|
|
2638
|
+
const result = await client.createDocuments(documents, directoryId, item.isUrl ? "WEB" : "FILE");
|
|
2639
|
+
results.push({ source: item.source, directoryId, status: "submitted", result });
|
|
2640
|
+
}
|
|
2641
|
+
return { count: normalized.length, results };
|
|
2175
2642
|
}
|
|
2176
2643
|
async function importTemplates(client, args) {
|
|
2177
2644
|
const source = option(args, "--plan-file") ?? option(args, "--xlsx");
|
|
@@ -2360,7 +2827,10 @@ var usage = () => [
|
|
|
2360
2827
|
"Example: cxclawctl intent create --data-file intent.json"
|
|
2361
2828
|
].join("\n\n");
|
|
2362
2829
|
async function runAuth(action2, args) {
|
|
2363
|
-
if (action2 === "login")
|
|
2830
|
+
if (action2 === "login") {
|
|
2831
|
+
if (flag(args, "--server") || flag(args, "--env")) throw new Error("auth login \u56FA\u5B9A\u4F7F\u7528\u751F\u4EA7\u6388\u6743\u670D\u52A1\uFF0C\u4E0D\u652F\u6301 --server \u6216 --env");
|
|
2832
|
+
return print(await login(option(args, "--channel") ?? "stable"));
|
|
2833
|
+
}
|
|
2364
2834
|
if (action2 === "logout") {
|
|
2365
2835
|
await logout();
|
|
2366
2836
|
return print({ ok: true, loggedOut: true });
|
|
@@ -2407,9 +2877,10 @@ async function main(args = process.argv.slice(2)) {
|
|
|
2407
2877
|
return;
|
|
2408
2878
|
}
|
|
2409
2879
|
if (command === "setup") {
|
|
2880
|
+
if (flag(args, "--server") || flag(args, "--env")) throw new Error("setup \u56FA\u5B9A\u4F7F\u7528\u751F\u4EA7\u6388\u6743\u670D\u52A1\uFF0C\u4E0D\u652F\u6301 --server \u6216 --env");
|
|
2410
2881
|
const channel = option(args, "--channel") ?? "stable";
|
|
2411
2882
|
await installMarketplace(channel);
|
|
2412
|
-
await login(
|
|
2883
|
+
await login(channel);
|
|
2413
2884
|
await installScheduler();
|
|
2414
2885
|
return;
|
|
2415
2886
|
}
|
|
@@ -2449,7 +2920,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
2449
2920
|
if (flag(args, "--scheduled")) await new Promise((resolve5) => setTimeout(resolve5, Math.random() * 18e5));
|
|
2450
2921
|
return print(await update());
|
|
2451
2922
|
}
|
|
2452
|
-
if (command === "commands" || command === "capabilities") console.log(await
|
|
2923
|
+
if (command === "commands" || command === "capabilities") console.log(await readFile6(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
|
|
2453
2924
|
else throw new Error(`unknown command: ${command}`);
|
|
2454
2925
|
}
|
|
2455
2926
|
main().catch((error) => {
|