@shulex/cxclawctl 0.1.2 → 0.1.3
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 +25 -22
- package/dist/cli.js +2202 -127
- package/dist/cli.js.map +1 -1
- package/package.json +19 -6
- package/capabilities.lock.json +0 -898
- package/requirements-cli.txt +0 -2
- package/skills/__lib__/__init__.py +0 -0
- package/skills/__lib__/skill-runtime/bootstrap.py +0 -134
- package/skills/__lib__/solvea-api/__init__.py +0 -0
- package/skills/__lib__/solvea-api/client.py +0 -1944
- package/skills/__lib__/solvea-api/dashboard.py +0 -100
- package/skills/__lib__/solvea-api/solvea_api.py +0 -1341
- package/skills/agent-analytics/SKILL.md +0 -74
- package/skills/agent-analytics/references/chat-report-fields.md +0 -36
- package/skills/agent-analytics/references/report-fields.md +0 -130
- package/skills/agent-analytics/references/transcript.md +0 -110
- package/skills/agent-builder/SKILL.md +0 -42
- package/skills/agent-builder/interfaces/fallback-chatflow.md +0 -47
- package/skills/agent-builder/references/chatflow/graph-spec.md +0 -259
- package/skills/agent-builder/references/chatflow/node-selection.md +0 -89
- package/skills/agent-builder/references/chatflow/nodes/agent.md +0 -350
- package/skills/agent-builder/references/chatflow/nodes/business-track.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/code.md +0 -157
- package/skills/agent-builder/references/chatflow/nodes/collect-order.md +0 -47
- package/skills/agent-builder/references/chatflow/nodes/finish.md +0 -34
- package/skills/agent-builder/references/chatflow/nodes/human-review.md +0 -60
- package/skills/agent-builder/references/chatflow/nodes/if-else.md +0 -100
- package/skills/agent-builder/references/chatflow/nodes/intent.md +0 -79
- package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/llm.md +0 -117
- package/skills/agent-builder/references/chatflow/nodes/note.md +0 -38
- package/skills/agent-builder/references/chatflow/nodes/question.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +0 -68
- package/skills/agent-builder/references/chatflow/nodes/reply.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/start.md +0 -35
- package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +0 -48
- package/skills/agent-builder/references/chatflow/nodes/tool.md +0 -138
- package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +0 -63
- package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +0 -52
- package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +0 -50
- package/skills/agent-builder/references/chatflow/nodes/variable-process.md +0 -70
- package/skills/agent-builder/references/chatflow/overview.md +0 -53
- package/skills/agent-builder/references/chatflow/visual-translation.md +0 -64
- package/skills/agent-builder/references/chatflow/workflow.md +0 -134
- package/skills/agent-builder/references/multi-agent/create-update.md +0 -185
- package/skills/agent-builder/references/multi-agent/graph-spec.md +0 -246
- package/skills/agent-builder/references/multi-agent/graph-structure.md +0 -12
- package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +0 -13
- package/skills/agent-builder/references/multi-agent/migration/large.md +0 -554
- package/skills/agent-builder/references/multi-agent/migration/principles.md +0 -446
- package/skills/agent-builder/references/multi-agent/migration/small.md +0 -225
- package/skills/agent-builder/references/multi-agent/orchestration.md +0 -11
- package/skills/agent-builder/references/multi-agent/overview.md +0 -48
- package/skills/agent-builder/references/multi-agent/update.md +0 -128
- package/skills/agent-builder/references/multi-agent/workflow.md +0 -87
- package/skills/agent-builder/references/single-agent/overview.md +0 -48
- package/skills/agent-builder/references/single-agent/prompt.md +0 -16
- package/skills/agent-builder/references/single-agent/tools.md +0 -36
- package/skills/agent-builder/references/single-agent/voice.md +0 -46
- package/skills/agent-builder/references/tools/http-tool.md +0 -315
- package/skills/agent-builder/references/tools/mock-tool.md +0 -279
- package/skills/agent-builder/references/tools/overview.md +0 -46
- package/skills/agent-builder/references/tools/tool-config.md +0 -43
- package/skills/agent-builder/references/tools/x-token.md +0 -34
- package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +0 -3458
- package/skills/agent-builder/scripts/chatflow/validate_graph.py +0 -1697
- package/skills/agent-builder/scripts/copy_marketplace_agent.py +0 -61
- package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +0 -5974
- package/skills/agent-builder/scripts/multi-agent/replay_compare.py +0 -679
- package/skills/agent-builder/scripts/patch_agent_tools.py +0 -135
- package/skills/agent-builder/scripts/tools/validate_tool_config.py +0 -384
- package/skills/agent-tester/SKILL.md +0 -35
- package/skills/agent-tester/batch-test/overview.md +0 -86
- package/skills/agent-tester/scripts/batch_test_sheet.py +0 -213
- package/skills/agent-tester/single-test/badcase-retest.md +0 -85
- package/skills/agent-tester/single-test/direct-test.md +0 -24
- package/skills/agent-tester/single-test/overview.md +0 -142
- package/skills/delivery/SKILL.md +0 -112
- package/skills/delivery/interfaces/classify.md +0 -38
- package/skills/delivery/interfaces/create-agents.md +0 -89
- package/skills/delivery/interfaces/delivery-test.md +0 -67
- package/skills/delivery/references/flow.md +0 -40
- package/skills/delivery/references/validation.md +0 -15
- package/skills/delivery/scripts/batch_test.py +0 -704
- package/skills/delivery/scripts/classify.py +0 -22
- package/skills/delivery/scripts/create_agents.py +0 -695
- package/skills/delivery/scripts/gen_delivery_report.py +0 -335
- package/skills/delivery/scripts/gen_excel.py +0 -234
- package/skills/delivery/scripts/gen_report.py +0 -208
- package/skills/delivery/scripts/plan.py +0 -163
- package/skills/delivery/scripts/render.py +0 -713
- package/skills/delivery/scripts/render_summary.py +0 -38
- package/skills/delivery/scripts/sample_history_cases.py +0 -109
- package/skills/kb-builder/SKILL.md +0 -80
- package/skills/kb-builder/interfaces/qa-upload.md +0 -55
- package/skills/kb-builder/interfaces/web-kb.md +0 -84
- package/skills/kb-builder/references/directory.md +0 -373
- package/skills/kb-builder/references/export.md +0 -282
- package/skills/kb-builder/references/isolation-keywords.md +0 -113
- package/skills/kb-builder/references/knowledge.md +0 -153
- package/skills/kb-builder/references/product.md +0 -52
- package/skills/kb-builder/references/qa-from-help-center.md +0 -58
- package/skills/kb-builder/references/qa-from-pdf.md +0 -94
- package/skills/kb-builder/references/qa-from-tickets.md +0 -168
- package/skills/kb-builder/references/qa-from-web.md +0 -98
- package/skills/kb-builder/references/qa-quality.md +0 -73
- package/skills/kb-builder/references/reply-template.md +0 -237
- package/skills/kb-builder/references/tag-isolation-migration.md +0 -61
- package/skills/kb-builder/references/tags.md +0 -45
- package/skills/kb-builder/references/upload.md +0 -130
- package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +0 -333
- package/skills/kb-builder/scripts/build_directory_plan.py +0 -144
- package/skills/kb-builder/scripts/crawl_site.py +0 -433
- package/skills/kb-builder/scripts/create_dirs.py +0 -146
- package/skills/kb-builder/scripts/export_knowledge.py +0 -454
- package/skills/kb-builder/scripts/extract_ticket_qa.py +0 -441
- package/skills/kb-builder/scripts/extract_web_qa.py +0 -118
- package/skills/kb-builder/scripts/import_qa.py +0 -219
- package/skills/kb-builder/scripts/import_source_documents.py +0 -143
- package/skills/kb-builder/scripts/import_templates.py +0 -409
- package/skills/poc/SKILL.md +0 -125
- package/skills/poc/assets/default_manual.css +0 -279
- package/skills/poc/assets/notice_escalate.png +0 -0
- package/skills/poc/assets/notice_escalate2.png +0 -0
- package/skills/poc/assets/notice_new_chat.png +0 -0
- package/skills/poc/assets/poc_test_template.html +0 -645
- package/skills/poc/assets/poc_test_template.md +0 -104
- package/skills/poc/assets/step3_preview.png +0 -0
- package/skills/poc/assets/step4_order_note.png +0 -0
- package/skills/poc/interfaces/classify.md +0 -38
- package/skills/poc/interfaces/create-agents.md +0 -108
- package/skills/poc/interfaces/poc-test.md +0 -94
- package/skills/poc/references/flow.md +0 -47
- package/skills/poc/references/kb-directory.md +0 -22
- package/skills/poc/references/validation.md +0 -17
- package/skills/poc/scripts/classify.py +0 -22
- package/skills/poc/scripts/create_agents.py +0 -818
- package/skills/poc/scripts/gen_excel.py +0 -234
- package/skills/poc/scripts/gen_poc_manual.py +0 -249
- package/skills/poc/scripts/gen_report.py +0 -208
- package/skills/poc/scripts/inspect_chatflow.py +0 -209
- package/skills/poc/scripts/plan.py +0 -178
- package/skills/poc/scripts/poc_test.py +0 -495
- package/skills/poc/scripts/render.py +0 -713
- package/skills/poc/scripts/render_summary.py +0 -41
- package/skills/runtime-inspector/SKILL.md +0 -51
- package/skills/runtime-inspector/references/frontend-rendering.md +0 -126
- package/skills/runtime-inspector/references/model.md +0 -59
- package/skills/runtime-inspector/references/sandbox.md +0 -131
- package/skills/runtime-inspector/scripts/model_inspect.py +0 -68
- package/skills/runtime-inspector/scripts/prepare.py +0 -98
- package/skills/runtime-inspector/scripts/process.py +0 -84
- package/skills/runtime-inspector/scripts/read_state.py +0 -32
- package/skills/runtime-inspector/scripts/sandbox_inspect.py +0 -463
- package/skills/runtime-inspector/scripts/test_claude_proxy.py +0 -49
- package/skills/runtime-inspector/scripts/test_openai_proxy.py +0 -41
- package/skills/runtime-inspector/scripts/verify_persistence.py +0 -101
- package/skills/runtime-inspector/scripts/write_test.py +0 -41
- package/skills/setup/SKILL.md +0 -17
- package/skills/shulex-support/SKILL.md +0 -39
- package/skills/shulex-support/references/badcase-analysis/overview.md +0 -34
- package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +0 -105
- package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +0 -89
- package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +0 -36
- package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +0 -53
- package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +0 -259
- package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +0 -148
- package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +0 -57
- package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +0 -120
- package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +0 -126
- package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +0 -121
- package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +0 -208
- package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +0 -115
- package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +0 -72
- package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +0 -92
- package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +0 -147
- package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +0 -283
- package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +0 -77
- package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +0 -19
- package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +0 -23
- package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +0 -31
- package/skills/shulex-support/references/product-docs.md +0 -70
- package/skills/shulex-support/references/tickets.md +0 -184
- package/skills/shulex-support/scripts/support.py +0 -427
- package/skills/solvea-desk/SKILL.md +0 -39
- package/skills/solvea-desk/references/report/fields.md +0 -82
- package/skills/solvea-desk/references/report/report-design.md +0 -200
- package/skills/solvea-desk/references/report/report-template.html +0 -44
- package/skills/solvea-desk/references/report/report.css +0 -517
- package/skills/solvea-desk/references/report/workflow.md +0 -464
- package/skills/solvea-desk/references/tickets/query.md +0 -103
- package/skills/solvea-desk/references/tickets/tags.md +0 -83
- package/skills/solvea-desk/scripts/report/fetch_sources.py +0 -580
- package/skills/solvea-desk/scripts/report/render_report.py +0 -763
- package/skills/solvea-desk/scripts/tickets/desk_api.py +0 -73
- package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +0 -403
- package/skills/solvea-desk/scripts/tickets/ticket_tags.py +0 -305
- package/skills/solvea-kit/SKILL.md +0 -84
- package/skills/solvea-kit/references/agent.md +0 -145
- package/skills/solvea-kit/references/chat.md +0 -244
- package/skills/solvea-kit/references/intent.md +0 -69
- package/skills/solvea-kit/references/plugin.md +0 -53
- package/skills/solvea-kit/references/quality-inspection.md +0 -75
- package/skills/solvea-kit/references/strategy.md +0 -62
- package/skills/solvea-kit/references/tagging.md +0 -231
- package/skills/solvea-kit/references/variables.md +0 -53
- package/skills/solvea-kit/scripts/generate_label_import_file.py +0 -151
- package/skills/solvea-kit/scripts/intent_classify.py +0 -843
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 } from "fs/promises";
|
|
4
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
5
5
|
|
|
6
6
|
// src/auth.ts
|
|
7
7
|
import open from "open";
|
|
@@ -9,27 +9,37 @@ 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.3",
|
|
13
13
|
description: "Solvea CXClaw CLI and Codex plugin runtime",
|
|
14
14
|
type: "module",
|
|
15
|
-
bin: {
|
|
16
|
-
|
|
15
|
+
bin: {
|
|
16
|
+
cxclawctl: "./bin/cxclawctl.mjs"
|
|
17
|
+
},
|
|
18
|
+
files: [
|
|
19
|
+
"bin",
|
|
20
|
+
"dist/cli.js",
|
|
21
|
+
"dist/cli.js.map"
|
|
22
|
+
],
|
|
17
23
|
scripts: {
|
|
18
24
|
build: "tsup",
|
|
19
25
|
dev: "tsx src/cli.ts",
|
|
20
26
|
typecheck: "tsc --noEmit",
|
|
21
27
|
test: "bun test --path-ignore-patterns _tmp_",
|
|
22
|
-
"check:capabilities": "
|
|
28
|
+
"check:capabilities": "tsx scripts/check-capabilities.ts",
|
|
23
29
|
"check:release": "tsx scripts/check-release.ts",
|
|
30
|
+
"test:package": "tsx scripts/test-packed-package.ts",
|
|
24
31
|
"release:prepare": "tsx scripts/prepare-release.ts",
|
|
25
32
|
"package:agent-plugin": "node scripts/package-agent-plugin.mjs",
|
|
26
33
|
"package:codex-plugin": "node scripts/package-codex-plugin.mjs"
|
|
27
34
|
},
|
|
28
|
-
engines: {
|
|
35
|
+
engines: {
|
|
36
|
+
node: ">=22"
|
|
37
|
+
},
|
|
29
38
|
dependencies: {
|
|
30
39
|
"@alicloud/log": "^1.2.6",
|
|
31
40
|
"@napi-rs/keyring": "^2.1.2",
|
|
32
41
|
"compare-versions": "^6.1.1",
|
|
42
|
+
exceljs: "^4.4.0",
|
|
33
43
|
open: "^10.2.0"
|
|
34
44
|
},
|
|
35
45
|
devDependencies: {
|
|
@@ -39,7 +49,10 @@ var package_default = {
|
|
|
39
49
|
tsx: "^4.21.0",
|
|
40
50
|
typescript: "^5.9.0"
|
|
41
51
|
},
|
|
42
|
-
repository: {
|
|
52
|
+
repository: {
|
|
53
|
+
type: "git",
|
|
54
|
+
url: "git+https://github.com/SolveaCX/cx_claw_codex.git"
|
|
55
|
+
},
|
|
43
56
|
license: "UNLICENSED"
|
|
44
57
|
};
|
|
45
58
|
|
|
@@ -85,7 +98,7 @@ var options = process.platform === "linux" ? { linux: { store: "secret-service"
|
|
|
85
98
|
var entry = () => new AsyncEntry("com.solvea.cxclaw", "session", options);
|
|
86
99
|
async function loadSession() {
|
|
87
100
|
const raw = await entry().getPassword();
|
|
88
|
-
if (!raw) throw new Error("\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C
|
|
101
|
+
if (!raw) throw new Error("\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C cxclawctl auth login");
|
|
89
102
|
return JSON.parse(raw);
|
|
90
103
|
}
|
|
91
104
|
async function saveSession(session) {
|
|
@@ -147,61 +160,932 @@ function option(args, name, required = false) {
|
|
|
147
160
|
if (required && !value) throw new Error(`${name} is required`);
|
|
148
161
|
return value;
|
|
149
162
|
}
|
|
163
|
+
function options2(args, name) {
|
|
164
|
+
const values = [];
|
|
165
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
166
|
+
if (args[index] === name && args[index + 1] && !args[index + 1].startsWith("-")) values.push(args[index + 1]);
|
|
167
|
+
}
|
|
168
|
+
return values;
|
|
169
|
+
}
|
|
170
|
+
function optionList(args, name) {
|
|
171
|
+
const index = args.indexOf(name);
|
|
172
|
+
if (index < 0) return [];
|
|
173
|
+
const values = [];
|
|
174
|
+
for (let cursor = index + 1; cursor < args.length && !args[cursor].startsWith("-"); cursor += 1) values.push(args[cursor]);
|
|
175
|
+
return values;
|
|
176
|
+
}
|
|
150
177
|
function flag(args, name) {
|
|
151
178
|
return args.includes(name);
|
|
152
179
|
}
|
|
153
180
|
function positionals(args) {
|
|
154
181
|
const out = [];
|
|
155
|
-
for (let
|
|
156
|
-
if (args[
|
|
157
|
-
if (!["--pretty", "--reverse", "--scheduled", "--install-scheduler"].includes(args[
|
|
158
|
-
} else out.push(args[
|
|
182
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
183
|
+
if (args[index].startsWith("--")) {
|
|
184
|
+
if (!["--pretty", "--reverse", "--scheduled", "--install-scheduler"].includes(args[index])) index += 1;
|
|
185
|
+
} else out.push(args[index]);
|
|
159
186
|
}
|
|
160
187
|
return out;
|
|
161
188
|
}
|
|
162
189
|
|
|
163
190
|
// src/business.ts
|
|
164
|
-
import {
|
|
165
|
-
import { access } from "fs/promises";
|
|
166
|
-
import { dirname, join, normalize, relative, resolve } from "path";
|
|
191
|
+
import { dirname, resolve } from "path";
|
|
167
192
|
import { fileURLToPath } from "url";
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
|
|
194
|
+
// src/solvea/commands.ts
|
|
195
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
196
|
+
|
|
197
|
+
// src/solvea/client.ts
|
|
198
|
+
var SolveaClient = class {
|
|
199
|
+
constructor(session) {
|
|
200
|
+
this.session = session;
|
|
201
|
+
if (!session.tenant?.businessUrl || !session.tenant.businessToken) {
|
|
202
|
+
throw new Error("no tenant selected; run tenant use before a business command");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
session;
|
|
206
|
+
project;
|
|
207
|
+
bot;
|
|
208
|
+
get baseUrl() {
|
|
209
|
+
return this.session.tenant.businessUrl.replace(/\/$/, "");
|
|
210
|
+
}
|
|
211
|
+
get env() {
|
|
212
|
+
return this.session.tenant.environment;
|
|
213
|
+
}
|
|
214
|
+
async request(method, path, body, query2) {
|
|
215
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
216
|
+
for (const [key, value] of Object.entries(query2 ?? {})) url.searchParams.set(key, String(value));
|
|
217
|
+
const cookie = this.env === "prod" ? "_token" : "_token_staging";
|
|
218
|
+
const headers = new Headers({
|
|
219
|
+
accept: "application/json, text/plain, */*",
|
|
220
|
+
origin: this.baseUrl,
|
|
221
|
+
referer: `${this.baseUrl}/chatbot`,
|
|
222
|
+
cookie: `${cookie}=${this.session.tenant.businessToken}`,
|
|
223
|
+
"x-cxclaw-client-version": process.env.CXCLAW_PLUGIN_VERSION ?? "unknown"
|
|
175
224
|
});
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
if (body !== void 0) headers.set("content-type", "application/json");
|
|
226
|
+
let last;
|
|
227
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
228
|
+
try {
|
|
229
|
+
const response = await fetch(url, {
|
|
230
|
+
method,
|
|
231
|
+
headers,
|
|
232
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
233
|
+
signal: AbortSignal.timeout(3e4)
|
|
234
|
+
});
|
|
235
|
+
const text = await response.text();
|
|
236
|
+
let value;
|
|
237
|
+
try {
|
|
238
|
+
value = text ? JSON.parse(text) : void 0;
|
|
239
|
+
} catch {
|
|
240
|
+
value = { raw: text };
|
|
241
|
+
}
|
|
242
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} ${method} ${path}: ${text}`);
|
|
243
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
244
|
+
const object = value;
|
|
245
|
+
const code = object.code ?? object.errno;
|
|
246
|
+
if (code !== void 0 && ![0, "0", 200, "200", "OK", "SUCCESS"].includes(code)) {
|
|
247
|
+
throw new Error(`API error ${method} ${path}: ${String(object.msg ?? object.message ?? object.error ?? code)}`);
|
|
248
|
+
}
|
|
249
|
+
if (object.success === false) throw new Error(`API error ${method} ${path}: ${String(object.msg ?? object.message ?? object.error ?? "request failed")}`);
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
last = error;
|
|
254
|
+
if (attempt < 2) await new Promise((resolve5) => setTimeout(resolve5, 200 * 2 ** attempt));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
throw last;
|
|
258
|
+
}
|
|
259
|
+
async post(path, body) {
|
|
260
|
+
return this.request("POST", path, body);
|
|
261
|
+
}
|
|
262
|
+
async get(path, query2) {
|
|
263
|
+
return this.request("GET", path, void 0, query2);
|
|
264
|
+
}
|
|
265
|
+
async put(path, body) {
|
|
266
|
+
return this.request("PUT", path, body);
|
|
267
|
+
}
|
|
268
|
+
async rawCall(method, path, body, query2) {
|
|
269
|
+
return this.request(method, path, body, query2);
|
|
270
|
+
}
|
|
271
|
+
safePathSegment(value, name) {
|
|
272
|
+
if (!value || value === "." || value === ".." || /[\\/?#%\u0000-\u001f\u007f]/.test(value)) {
|
|
273
|
+
throw new Error(`${name} must be a safe non-empty path segment`);
|
|
274
|
+
}
|
|
275
|
+
return encodeURIComponent(value);
|
|
276
|
+
}
|
|
277
|
+
async projectInfo() {
|
|
278
|
+
return this.get("/api_v2/gpt/bots");
|
|
279
|
+
}
|
|
280
|
+
async projectId() {
|
|
281
|
+
if (!this.project) {
|
|
282
|
+
const rows = await this.projectInfo();
|
|
283
|
+
const first = Array.isArray(rows) ? rows[0] : void 0;
|
|
284
|
+
this.project = String(first?.projectId ?? 950);
|
|
285
|
+
this.bot = String(first?.id ?? "");
|
|
286
|
+
}
|
|
287
|
+
return this.project;
|
|
288
|
+
}
|
|
289
|
+
async botId() {
|
|
290
|
+
if (!this.bot) await this.projectId();
|
|
291
|
+
return this.bot || "723";
|
|
292
|
+
}
|
|
293
|
+
async listAgents(category = "text") {
|
|
294
|
+
return this.post("/api_v2/gpt/ap/queryAgent", { projectId: Number(await this.projectId()), needRelation: true, noMeta: true, category });
|
|
295
|
+
}
|
|
296
|
+
async getAgent(id) {
|
|
297
|
+
return this.post("/api_v2/gpt/ap/queryAgent", { projectId: Number(await this.projectId()), agentId: Number(id), needRelation: true, squareAgent: false });
|
|
298
|
+
}
|
|
299
|
+
async getChatflowGraph(id) {
|
|
300
|
+
return this.post("/api_v2/gpt/chatflow/queryGraph/latest", { agentId: Number(id) });
|
|
301
|
+
}
|
|
302
|
+
async createAgent(data3) {
|
|
303
|
+
return this.post("/api_v2/gpt/ap/createAgent", { projectId: Number(await this.projectId()), ...data3 });
|
|
304
|
+
}
|
|
305
|
+
async updateAgent(data3) {
|
|
306
|
+
return this.post("/api_v2/gpt/ap/updateAgent", data3);
|
|
307
|
+
}
|
|
308
|
+
async updateChatflow(agentId, graph, publish = false) {
|
|
309
|
+
const current = await this.getAgent(agentId);
|
|
310
|
+
const agent = Array.isArray(current) ? current[0] : current;
|
|
311
|
+
if (!agent || agent.type !== "CHAT_FLOW") throw new Error(`Agent ${agentId} is not CHAT_FLOW`);
|
|
312
|
+
const payload = { ...agent, agentId: agent.id ?? Number(agentId), meta: { ...agent.meta ?? {}, chatFlowGraph: graph } };
|
|
313
|
+
return this.updateAgent({ ...payload, ...publish ? { publish: true } : {} });
|
|
314
|
+
}
|
|
315
|
+
async listPlugins() {
|
|
316
|
+
return this.post("/api_v2/gpt/ap/queryPlugin", { projectId: Number(await this.projectId()), needRelation: false });
|
|
317
|
+
}
|
|
318
|
+
async getPlugin(id) {
|
|
319
|
+
return this.post("/api_v2/gpt/ap/queryPlugin", { projectId: Number(await this.projectId()), pluginId: Number(id), convertStrategyV5: true });
|
|
320
|
+
}
|
|
321
|
+
async createPlugin(data3) {
|
|
322
|
+
return this.post("/api_v2/gpt/ap/createPlugin", { projectId: Number(await this.projectId()), ...data3 });
|
|
323
|
+
}
|
|
324
|
+
async updatePlugin(data3) {
|
|
325
|
+
return this.post("/api_v2/gpt/ap/updatePlugin", { projectId: Number(await this.projectId()), ...data3 });
|
|
326
|
+
}
|
|
327
|
+
async deletePlugin(id) {
|
|
328
|
+
return this.post("/api_v2/gpt/ap/deletePlugin", { projectId: Number(await this.projectId()), pluginId: Number(id) });
|
|
329
|
+
}
|
|
330
|
+
async runPlugin(config, requestBody) {
|
|
331
|
+
return this.post("/api_v2/gpt/ap/runHttpPlugin", {
|
|
332
|
+
projectId: Number(await this.projectId()),
|
|
333
|
+
botId: Number(await this.botId()),
|
|
334
|
+
externalHttpCallConfig: config,
|
|
335
|
+
...requestBody === void 0 ? {} : { externalHttpCallRequestBody: requestBody }
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async replaceMockData(type, records) {
|
|
339
|
+
if (records.length > 1e4) throw new Error("records must contain at most 10000 items");
|
|
340
|
+
return this.post(`/api_v2/gpt/mockdata/reAddMockData/${this.safePathSegment(type, "mock type")}`, records);
|
|
341
|
+
}
|
|
342
|
+
async queryMockData(type) {
|
|
343
|
+
return this.get("/api_v2/gpt/mockdata/queryMockData", { type: this.safePathSegment(type, "mock type"), globalData: false });
|
|
344
|
+
}
|
|
345
|
+
async listAiFields() {
|
|
346
|
+
return this.post("/api_v2/gpt/chatflow/queryAiFields", {});
|
|
347
|
+
}
|
|
348
|
+
async listAgentAiFields() {
|
|
349
|
+
return this.post("/api_v2/gpt/field/list/range", { fieldRange: ["all", "agent", "agent_preload_visible"] });
|
|
350
|
+
}
|
|
351
|
+
async aiFieldGuide() {
|
|
352
|
+
return this.post("/api_v2/tars/api/field/manage/guide/record", {});
|
|
353
|
+
}
|
|
354
|
+
async initializeAiFields() {
|
|
355
|
+
return this.get("/api_v2/tars/api/field/manage/pre/install");
|
|
356
|
+
}
|
|
357
|
+
async listManagedAiFields(pageIndex, pageSize2, name) {
|
|
358
|
+
return this.post("/api_v2/tars/api/field/manage/page", { pageIndex, pageSize: pageSize2, name });
|
|
359
|
+
}
|
|
360
|
+
async getManagedAiField(id) {
|
|
361
|
+
return this.get(`/api_v2/tars/api/field/manage/detail/${encodeURIComponent(id)}`);
|
|
362
|
+
}
|
|
363
|
+
async createManagedAiField(data3) {
|
|
364
|
+
return this.post("/api_v2/tars/api/field/manage/create", data3);
|
|
365
|
+
}
|
|
366
|
+
async updateManagedAiField(data3) {
|
|
367
|
+
return this.post("/api_v2/tars/api/field/manage/update", data3);
|
|
368
|
+
}
|
|
369
|
+
async deleteManagedAiField(id) {
|
|
370
|
+
return this.get(`/api_v2/tars/api/field/manage/delete/${encodeURIComponent(id)}`);
|
|
371
|
+
}
|
|
372
|
+
async toggleManagedAiField(id) {
|
|
373
|
+
return this.get(`/api_v2/tars/api/field/manage/switch/${encodeURIComponent(id)}`);
|
|
374
|
+
}
|
|
375
|
+
async chat(data3) {
|
|
376
|
+
const bot = String(data3.botId ?? await this.botId());
|
|
377
|
+
return this.post(`/api_v2/gpt/bot/${bot}/chat`, { botId: Number(bot), projectId: Number(await this.projectId()), option: { debug: true }, ...data3 });
|
|
378
|
+
}
|
|
379
|
+
async chatAsync(data3) {
|
|
380
|
+
const bot = String(data3.botId ?? await this.botId());
|
|
381
|
+
return this.post(`/api_v2/gpt/bot/${bot}/chat/async`, { botId: Number(bot), projectId: Number(await this.projectId()), option: { debug: true }, ...data3 });
|
|
382
|
+
}
|
|
383
|
+
async asyncResult(receipt, botId) {
|
|
384
|
+
const bot = botId ?? await this.botId();
|
|
385
|
+
return this.get(`/api_v2/gpt/bot/${bot}/chat/async/${encodeURIComponent(receipt)}`);
|
|
386
|
+
}
|
|
387
|
+
async listIntents(category = "E_COMMERCE") {
|
|
388
|
+
return this.get(`/api_v2/gpt/project/${await this.projectId()}/intents`, category ? { category } : void 0);
|
|
389
|
+
}
|
|
390
|
+
async getIntent(id) {
|
|
391
|
+
return this.get(`/api_v2/gpt/project/${await this.projectId()}/intent/${encodeURIComponent(id)}`);
|
|
392
|
+
}
|
|
393
|
+
async createIntent(data3) {
|
|
394
|
+
return this.post(`/api_v2/gpt/project/${await this.projectId()}/intent`, data3);
|
|
395
|
+
}
|
|
396
|
+
async updateIntent(id, data3) {
|
|
397
|
+
return this.put(`/api_v2/gpt/project/${await this.projectId()}/intent/${encodeURIComponent(id)}`, data3);
|
|
398
|
+
}
|
|
399
|
+
async toggleIntent(id, enabled2) {
|
|
400
|
+
return this.put(`/api_v2/gpt/project/${await this.projectId()}/intent/${encodeURIComponent(id)}/status`, { status: enabled2 ? "ENABLED" : "DISABLED" });
|
|
401
|
+
}
|
|
402
|
+
async listEntities() {
|
|
403
|
+
return this.get(`/api_v2/gpt/project/${await this.projectId()}/entities`);
|
|
404
|
+
}
|
|
405
|
+
async listStrategies(pageSize2 = 100) {
|
|
406
|
+
return this.get(`/api_v2/gpt/project/${await this.projectId()}/strategies`, { pageSize: pageSize2, pageNum: 1 });
|
|
407
|
+
}
|
|
408
|
+
async createStrategy(data3) {
|
|
409
|
+
return this.post(`/api_v2/gpt/project/${await this.projectId()}/strategy?v4=true`, data3);
|
|
410
|
+
}
|
|
411
|
+
async updateStrategy(id, data3) {
|
|
412
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}`, data3);
|
|
413
|
+
}
|
|
414
|
+
async toggleStrategy(id, enabled2) {
|
|
415
|
+
return this.request("PUT", `/api_v2/gpt/project/${await this.projectId()}/strategy/${encodeURIComponent(id)}/status`, { status: enabled2 ? "ENABLED" : "DISABLED" });
|
|
416
|
+
}
|
|
417
|
+
async qualityStatus() {
|
|
418
|
+
return this.get("/api_v2/intelli/inspection/ticket/status");
|
|
419
|
+
}
|
|
420
|
+
async setQualityStatus(enabled2) {
|
|
421
|
+
return this.post("/api_v2/intelli/inspection/ticket/open", { open: enabled2, projectId: Number(await this.projectId()) });
|
|
422
|
+
}
|
|
423
|
+
async qualityRules(beginTimestamp, endTimestamp) {
|
|
424
|
+
return this.get("/api_v2/intelli/inspection/ticket/rules", { ...beginTimestamp === void 0 ? {} : { beginTimestamp }, ...endTimestamp === void 0 ? {} : { endTimestamp } });
|
|
425
|
+
}
|
|
426
|
+
async qualityRule(key) {
|
|
427
|
+
return this.get("/api_v2/intelli/inspection/ticket/rules/detail", { inspectionKey: key });
|
|
428
|
+
}
|
|
429
|
+
async updateQualityRule(data3) {
|
|
430
|
+
return this.post("/api_v2/intelli/inspection/ticket/rules", data3);
|
|
431
|
+
}
|
|
432
|
+
async customQualityRule(key) {
|
|
433
|
+
return this.post("/api_v2/intelli/inspection/ticket/customRules/detail", { inspectionKey: key });
|
|
434
|
+
}
|
|
435
|
+
async createCustomQualityRule(data3) {
|
|
436
|
+
return this.post("/api_v2/intelli/inspection/ticket/customRules/create", data3);
|
|
437
|
+
}
|
|
438
|
+
async updateCustomQualityRule(data3) {
|
|
439
|
+
return this.post("/api_v2/intelli/inspection/ticket/customRules/update", data3);
|
|
440
|
+
}
|
|
441
|
+
async deleteCustomQualityRule(key) {
|
|
442
|
+
return this.post("/api_v2/intelli/inspection/ticket/customRules/delete", { inspectionKey: key });
|
|
443
|
+
}
|
|
444
|
+
async listQualityAlertRules() {
|
|
445
|
+
return this.get("/api_v2/intelli/inspection/alert/list");
|
|
446
|
+
}
|
|
447
|
+
async getQualityAlertRule(id) {
|
|
448
|
+
return this.get("/api_v2/intelli/inspection/alert/detail", { id });
|
|
449
|
+
}
|
|
450
|
+
async createQualityAlertRule(data3) {
|
|
451
|
+
return this.post("/api_v2/intelli/inspection/alert/create", data3);
|
|
452
|
+
}
|
|
453
|
+
async updateQualityAlertRule(data3) {
|
|
454
|
+
return this.post("/api_v2/intelli/inspection/alert/update", data3);
|
|
455
|
+
}
|
|
456
|
+
async deleteQualityAlertRule(id) {
|
|
457
|
+
return this.get("/api_v2/intelli/inspection/alert/delete", { id });
|
|
458
|
+
}
|
|
459
|
+
async listQualityAlertMessages(data3) {
|
|
460
|
+
return this.post("/api_v2/intelli/inspection/alert/message/list", data3);
|
|
461
|
+
}
|
|
462
|
+
async getQualityAlertMessage(id) {
|
|
463
|
+
return this.get("/api_v2/intelli/inspection/alert/message/detail", { id });
|
|
464
|
+
}
|
|
465
|
+
async listTemplates(data3) {
|
|
466
|
+
return this.post(`/api_v2/gpt/project/${await this.projectId()}/templates/filter`, data3);
|
|
467
|
+
}
|
|
468
|
+
async createTemplate(data3) {
|
|
469
|
+
const content = String(data3.content ?? "");
|
|
470
|
+
return this.post(`/api_v2/gpt/project/${await this.projectId()}/template`, {
|
|
471
|
+
name: data3.name,
|
|
472
|
+
content,
|
|
473
|
+
status: data3.status ?? "ENABLED",
|
|
474
|
+
multiContent: data3.multiContent ?? { AUTO: content },
|
|
475
|
+
...data3.scene ? { scene: data3.scene } : {}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
async updateTemplate(id, data3) {
|
|
479
|
+
return this.put(`/api_v2/gpt/project/${await this.projectId()}/template/${encodeURIComponent(id)}`, data3);
|
|
480
|
+
}
|
|
481
|
+
async deleteTemplates(ids) {
|
|
482
|
+
return this.request("DELETE", `/api_v2/gpt/project/${await this.projectId()}/templates`, { templateIds: ids });
|
|
483
|
+
}
|
|
484
|
+
get qamPrefix() {
|
|
485
|
+
return this.env === "prod" ? "/api_v2/qam" : "/sns_v1";
|
|
486
|
+
}
|
|
487
|
+
get qamPath() {
|
|
488
|
+
return this.qamPrefix;
|
|
489
|
+
}
|
|
490
|
+
async listKnowledge(data3) {
|
|
491
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/getKnowledge`, { type: "ALL", pageIndex: 1, pageSize: 50, keyword: "", ...data3 });
|
|
492
|
+
}
|
|
493
|
+
async getKnowledge(id) {
|
|
494
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/ku/${encodeURIComponent(id)}`);
|
|
495
|
+
}
|
|
496
|
+
async updateKnowledge(id, data3) {
|
|
497
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/ku`, { ...data3, kuId: Number(id) });
|
|
498
|
+
}
|
|
499
|
+
async uploadQa(items) {
|
|
500
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/kus`, items);
|
|
501
|
+
}
|
|
502
|
+
async listDirectories() {
|
|
503
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/directories`, { keyword: "", kuPublished: "" });
|
|
504
|
+
}
|
|
505
|
+
async createFolder(name, parentId = 0) {
|
|
506
|
+
return this.post(`${this.qamPrefix}/project/${await this.projectId()}/directory`, { name, parentId, type: "GENERIC" });
|
|
507
|
+
}
|
|
508
|
+
async updateDirectory(id, data3) {
|
|
509
|
+
return this.request("PUT", `${this.qamPrefix}/project/${await this.projectId()}/directory/${encodeURIComponent(id)}`, data3);
|
|
510
|
+
}
|
|
511
|
+
async deleteDirectory(id) {
|
|
512
|
+
return this.request("DELETE", `${this.qamPrefix}/project/${await this.projectId()}/directory/${encodeURIComponent(id)}`);
|
|
513
|
+
}
|
|
514
|
+
async knowledgeTags(keyword = "") {
|
|
515
|
+
return this.get(`${this.qamPrefix}/project/${await this.projectId()}/searchKeywordAndIntents`, { keyword });
|
|
516
|
+
}
|
|
517
|
+
async transcript(chatId) {
|
|
518
|
+
return this.get(`/api_v2/gpt/project/${await this.projectId()}/transcript/${encodeURIComponent(chatId)}`, { isCompare: true });
|
|
519
|
+
}
|
|
520
|
+
async listTranscripts(data3) {
|
|
521
|
+
return this.post(`/api_v2/gpt/project/${await this.projectId()}/transcripts/filter`, data3);
|
|
522
|
+
}
|
|
523
|
+
async resolveTranscriptChatId(externalId, pageSize2 = 10) {
|
|
524
|
+
const result = await this.listTranscripts({ pageSize: pageSize2, pageIndex: 1, debug: false, keyword: externalId });
|
|
525
|
+
const rows = Array.isArray(result) ? result : result?.list;
|
|
526
|
+
const items = Array.isArray(rows) ? rows.filter((row) => row && typeof row === "object") : [];
|
|
527
|
+
const exact = items.filter((row) => String(row.externId ?? row.externalId ?? row.external_id ?? "") === externalId);
|
|
528
|
+
const matches = (exact.length ? exact : items).map((row) => ({ chatId: row.chatId ?? row.id, externalId: row.externId ?? row.externalId ?? row.external_id, agentName: row.agentName ?? "", touchPoint: row.touchPoint ?? row.touchpoint ?? "", createdAt: row.createdAt ?? "" }));
|
|
529
|
+
return { externalId, chatId: exact.length || matches.length === 1 ? matches[0]?.chatId ?? null : null, matchedBy: exact.length ? "exactExternalId" : "keyword", matches };
|
|
530
|
+
}
|
|
531
|
+
async chatContext(botId, data3) {
|
|
532
|
+
return this.post(`/api_v2/gpt/bot/${encodeURIComponent(botId)}/chat/context/compare`, data3);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// src/solvea/input.ts
|
|
537
|
+
import { readFile } from "fs/promises";
|
|
538
|
+
async function jsonData(args, required = true) {
|
|
539
|
+
const file = option(args, "--data-file");
|
|
540
|
+
const raw = file ? await readFile(file, "utf8") : option(args, "--data");
|
|
541
|
+
if (!raw) {
|
|
542
|
+
if (required) throw new Error("--data or --data-file is required");
|
|
543
|
+
return {};
|
|
544
|
+
}
|
|
545
|
+
const parsed = JSON.parse(raw);
|
|
546
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
|
|
547
|
+
return parsed;
|
|
548
|
+
}
|
|
549
|
+
async function jsonValue(args) {
|
|
550
|
+
const file = option(args, "--data-file");
|
|
551
|
+
const raw = file ? await readFile(file, "utf8") : option(args, "--data", true);
|
|
552
|
+
return JSON.parse(raw);
|
|
553
|
+
}
|
|
554
|
+
function requireFlag(args, name, command) {
|
|
555
|
+
if (!flag(args, name)) throw new Error(`${command} requires ${name} after explicit user acknowledgement`);
|
|
556
|
+
}
|
|
557
|
+
function requireId(args) {
|
|
558
|
+
return option(args, "--id", true);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/solvea/extended-commands.ts
|
|
562
|
+
var handlers = {
|
|
563
|
+
"replace-mock-data": async (client, args) => {
|
|
564
|
+
const records = await jsonValue(args);
|
|
565
|
+
if (!Array.isArray(records)) throw new Error("replace-mock-data requires a JSON array");
|
|
566
|
+
return client.replaceMockData(option(args, "--name", true), records);
|
|
567
|
+
},
|
|
568
|
+
"query-mock-data": (client, args) => client.queryMockData(option(args, "--name", true)),
|
|
569
|
+
"run-plugin": async (client, args) => {
|
|
570
|
+
const payload = await jsonData(args);
|
|
571
|
+
const config = "externalHttpCallConfig" in payload ? payload.externalHttpCallConfig : payload;
|
|
572
|
+
return client.runPlugin(config, payload.externalHttpCallRequestBody);
|
|
573
|
+
},
|
|
574
|
+
"list-ai-fields": (client) => client.listAiFields(),
|
|
575
|
+
"list-agent-ai-fields": (client) => client.listAgentAiFields(),
|
|
576
|
+
"get-ai-field-guide": (client) => client.aiFieldGuide(),
|
|
577
|
+
"initialize-ai-fields": (client) => client.initializeAiFields(),
|
|
578
|
+
"list-managed-ai-fields": (client, args) => client.listManagedAiFields(
|
|
579
|
+
Number(option(args, "--page") ?? 1),
|
|
580
|
+
Number(option(args, "--page-size") ?? 50),
|
|
581
|
+
option(args, "--keyword") ?? ""
|
|
582
|
+
),
|
|
583
|
+
"get-managed-ai-field": (client, args) => client.getManagedAiField(requireId(args)),
|
|
584
|
+
"create-managed-ai-field": async (client, args) => client.createManagedAiField(await jsonData(args)),
|
|
585
|
+
"update-managed-ai-field": async (client, args) => client.updateManagedAiField(await jsonData(args)),
|
|
586
|
+
"delete-managed-ai-field": async (client, args) => {
|
|
587
|
+
requireFlag(args, "--confirm-delete", "delete-managed-ai-field");
|
|
588
|
+
return client.deleteManagedAiField(requireId(args));
|
|
589
|
+
},
|
|
590
|
+
"toggle-managed-ai-field": (client, args) => client.toggleManagedAiField(requireId(args)),
|
|
591
|
+
"list-quality-alert-rules": (client) => client.listQualityAlertRules(),
|
|
592
|
+
"get-quality-alert-rule": (client, args) => client.getQualityAlertRule(requireId(args)),
|
|
593
|
+
"create-quality-alert-rule": async (client, args) => client.createQualityAlertRule(await jsonData(args)),
|
|
594
|
+
"update-quality-alert-rule": async (client, args) => client.updateQualityAlertRule(await jsonData(args)),
|
|
595
|
+
"delete-quality-alert-rule": async (client, args) => {
|
|
596
|
+
requireFlag(args, "--confirm-delete", "delete-quality-alert-rule");
|
|
597
|
+
return client.deleteQualityAlertRule(requireId(args));
|
|
598
|
+
},
|
|
599
|
+
"list-quality-alert-messages": async (client, args) => client.listQualityAlertMessages(await jsonData(args)),
|
|
600
|
+
"get-quality-alert-message": (client, args) => client.getQualityAlertMessage(requireId(args)),
|
|
601
|
+
"list-templates": (client, args) => client.listTemplates({
|
|
602
|
+
pageIndex: Number(option(args, "--page") ?? 1),
|
|
603
|
+
pagesize: Number(option(args, "--page-size") ?? 50),
|
|
604
|
+
keyword: option(args, "--keyword") ?? "",
|
|
605
|
+
...options2(args, "--scene").length ? { scenes: options2(args, "--scene") } : {},
|
|
606
|
+
...option(args, "--status") ? { status: [option(args, "--status")] } : {}
|
|
607
|
+
}),
|
|
608
|
+
"create-template": async (client, args) => {
|
|
609
|
+
const payload = await jsonData(args, false);
|
|
610
|
+
if (option(args, "--name")) payload.name = option(args, "--name");
|
|
611
|
+
if (option(args, "--content")) payload.content = option(args, "--content");
|
|
612
|
+
if (option(args, "--status")) payload.status = option(args, "--status");
|
|
613
|
+
if (options2(args, "--scene")[0]) payload.scene = options2(args, "--scene")[0];
|
|
614
|
+
if (!payload.name || !payload.content) throw new Error("create-template requires name and content");
|
|
615
|
+
return client.createTemplate(payload);
|
|
616
|
+
},
|
|
617
|
+
"update-template": async (client, args) => {
|
|
618
|
+
const payload = await jsonData(args, false);
|
|
619
|
+
for (const [field, name] of [["name", "--name"], ["content", "--content"], ["status", "--status"]]) {
|
|
620
|
+
if (option(args, name)) payload[field] = option(args, name);
|
|
621
|
+
}
|
|
622
|
+
if (!Object.keys(payload).length) throw new Error("update-template requires at least one changed field");
|
|
623
|
+
return client.updateTemplate(requireId(args), payload);
|
|
624
|
+
},
|
|
625
|
+
"delete-templates": async (client, args) => {
|
|
626
|
+
requireFlag(args, "--confirm-delete", "delete-templates");
|
|
627
|
+
const ids = optionList(args, "--ids").map(Number);
|
|
628
|
+
if (!ids.length || ids.some((id) => !Number.isSafeInteger(id) || id <= 0)) throw new Error("delete-templates requires positive integer --ids");
|
|
629
|
+
return client.deleteTemplates(ids);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
var EXTENDED_COMMANDS = new Set(Object.keys(handlers));
|
|
633
|
+
async function runExtendedCommand(client, command, args) {
|
|
634
|
+
const handler = handlers[command];
|
|
635
|
+
if (!handler) throw new Error(`TypeScript command not migrated yet: ${command}`);
|
|
636
|
+
return handler(client, args);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/solvea/remaining-commands.ts
|
|
640
|
+
var data = async (args) => jsonData(args, false);
|
|
641
|
+
var project = async (client) => client.projectId();
|
|
642
|
+
var page = (args) => Number(option(args, "--page") ?? 1);
|
|
643
|
+
var pageSize = (args) => Number(option(args, "--page-size") ?? 50);
|
|
644
|
+
var reportBody = async (args) => ({
|
|
645
|
+
beginTimestamp: Number(option(args, "--begin-ts", true)),
|
|
646
|
+
endTimestamp: Number(option(args, "--end-ts", true)),
|
|
647
|
+
timeOffset: Number(option(args, "--time-offset") ?? 8),
|
|
648
|
+
...await data(args)
|
|
649
|
+
});
|
|
650
|
+
var handlers2 = {
|
|
651
|
+
"chat-stream": async (c, a) => c.rawCall("POST", `/api_v2/gpt/bot/${await c.botId()}/chat`, { message: option(a, "--message", true), botId: Number(option(a, "--bot-id") ?? await c.botId()), projectId: Number(await project(c)), option: { debug: true }, stream: true }),
|
|
652
|
+
"update-chatflow": async (c, a) => c.updateChatflow(option(a, "--agent-id", true), await jsonData(a), flag(a, "--publish")),
|
|
653
|
+
"get-conversations-by-intent": async (c, a) => c.listTranscripts({ pageSize: Number(option(a, "--limit") ?? 50), pageIndex: 1, intent: option(a, "--intent", true) }),
|
|
654
|
+
"resolve-transcript-chat-id": async (c, a) => c.resolveTranscriptChatId(option(a, "--external-id", true), Number(option(a, "--page-size") ?? 10)),
|
|
655
|
+
"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) }),
|
|
656
|
+
"create-isolation-keyword": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/isolation_keyword`, { keyword: option(a, "--keyword", true) }),
|
|
657
|
+
"update-isolation-keyword": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/isolation_keyword/${requireId(a)}`, { keyword: option(a, "--keyword", true) }),
|
|
658
|
+
"delete-isolation-keyword": async (c, a) => {
|
|
659
|
+
requireFlag(a, "--confirm-delete", "delete-isolation-keyword");
|
|
660
|
+
return c.rawCall("DELETE", `${c.qamPath}/project/${await project(c)}/isolation_keyword/${requireId(a)}`);
|
|
661
|
+
},
|
|
662
|
+
"get-knowledge-isolation-keywords": async (c, a) => c.rawCall("GET", `${c.qamPath}/project/${await project(c)}/knowledge/isolation_keywords`, void 0, { knowledgeType: option(a, "--type", true), knowledgeId: requireId(a) }),
|
|
663
|
+
"batch-update-isolation-keyword-bindings": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/knowledge/isolation_keywords/batch`, await jsonValue(a)),
|
|
664
|
+
"edit-knowledge-tags": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/editTags`, await jsonValue(a)),
|
|
665
|
+
"move-directory": async (c, a) => c.rawCall("GET", `${c.qamPath}/project/${await project(c)}/directory/move`, void 0, { currentId: requireId(a), parentId: option(a, "--parent-id", true) }),
|
|
666
|
+
"operate-knowledge": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/updateKnowledge`, await jsonValue(a)),
|
|
667
|
+
"batch-update-knowledge": async (c, a) => c.rawCall("PUT", `${c.qamPath}/project/${await project(c)}/kus`, await jsonValue(a)),
|
|
668
|
+
"update-document-content": async (c, a) => c.rawCall("PUT", `${c.qamPath}/project/${await project(c)}/document`, { id: requireId(a), ...await data(a) }),
|
|
669
|
+
"create-product-knowledge": async (c, a) => c.rawCall("POST", `${c.qamPath}/project/${await project(c)}/ku_product`, await jsonValue(a)),
|
|
670
|
+
"update-product-knowledge": async (c, a) => c.rawCall("PUT", `${c.qamPath}/project/${await project(c)}/ku_product`, { kuProductId: Number(requireId(a)), ...await data(a) }),
|
|
671
|
+
"batch-update-products": async (c, a) => c.rawCall("PUT", `${c.qamPath}/project/${await project(c)}/ku_products`, { list: await jsonValue(a) }),
|
|
672
|
+
"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") ?? "" }),
|
|
673
|
+
"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),
|
|
674
|
+
"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" }),
|
|
675
|
+
"update-label-group": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateTreeInfo`, { ...await data(a), id: Number(requireId(a)), operateType: "UPDATE" }),
|
|
676
|
+
"delete-label-group": async (c, a) => {
|
|
677
|
+
requireFlag(a, "--confirm-delete", "delete-label-group");
|
|
678
|
+
return c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateTreeInfo`, { id: Number(requireId(a)), operateType: "DELETE" });
|
|
679
|
+
},
|
|
680
|
+
"get-label-tree": async (c, a) => c.rawCall("GET", `/api_v2/gpt/label/project/${await project(c)}/getTagTree`, void 0, { treeId: requireId(a), ...option(a, "--keyword") ? { name: option(a, "--keyword") } : {} }),
|
|
681
|
+
"get-label-detail-tree": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/getDetailTree`, { treeId: Number(requireId(a)), ...await data(a) }),
|
|
682
|
+
"create-label-node": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateNode`, { treeId: Number(option(a, "--tree-id", true)), operateType: "CREATE", nodes: [await data(a)] }),
|
|
683
|
+
"update-label-node": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/updateNode`, { treeId: Number(option(a, "--tree-id", true)), operateType: "UPDATE", nodes: [{ id: Number(requireId(a)), ...await data(a) }] }),
|
|
684
|
+
"delete-label-node": async (c, a) => {
|
|
685
|
+
requireFlag(a, "--confirm-delete", "delete-label-node");
|
|
686
|
+
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)) }] });
|
|
687
|
+
},
|
|
688
|
+
"download-label-template": async (c, a) => c.rawCall("GET", `/api_v2/gpt/label/project/${await project(c)}/downloadLabelTemplate`, void 0, { type: option(a, "--template-type") ?? "VOC_LABEL" }),
|
|
689
|
+
"download-labels": async (c, a) => c.rawCall("GET", `/api_v2/gpt/label/project/${await project(c)}/downloadLabel`, void 0, { treeId: option(a, "--tree-id", true) }),
|
|
690
|
+
"upload-labels": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/uploadLabel`, { treeId: Number(option(a, "--tree-id", true)), file: option(a, "--file", true), ...option(a, "--init") ? { uploadWay: "INIT" } : {} }),
|
|
691
|
+
"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) }),
|
|
692
|
+
"get-export-tasks": async (c, a) => c.rawCall("POST", `/api_v2/gpt/label/project/${await project(c)}/getLabelTask`, { page: page(a), pageSize: pageSize(a) }),
|
|
693
|
+
"upload-batch-test-file": async (c, a) => c.rawCall("GET", `/api_v2/gpt/project/${await project(c)}/sts_credentials`),
|
|
694
|
+
"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) }),
|
|
695
|
+
"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") } : {} }),
|
|
696
|
+
"list-voice-agents": (c) => c.listAgents("voice"),
|
|
697
|
+
"list-voice-transcripts": async (c, a) => c.rawCall("POST", "/api_v2/as_bot/voice-report/transcript-page", { pageNum: page(a), pageSize: pageSize(a), beginTimestamp: Number(option(a, "--begin-ts") ?? 0), endTimestamp: Number(option(a, "--end-ts") ?? 0) }),
|
|
698
|
+
"get-voice-transcript": async (c, a) => c.rawCall("GET", `/api_v2/gpt/voice/transcript/projectId/${await project(c)}/history/${requireId(a)}`)
|
|
699
|
+
};
|
|
700
|
+
var reportPaths = {
|
|
701
|
+
"report-transcript": "/api_v2/as_bot/statement/transcript",
|
|
702
|
+
"report-message": "/api_v2/as_bot/statement/message",
|
|
703
|
+
"report-agent": "/api_v2/as_bot/statement/agent",
|
|
704
|
+
"report-intent": "/api_v2/as_bot/statement/transcript/intent",
|
|
705
|
+
"report-satisfaction": "/api_v2/as_bot/statement/satisfaction/feedback",
|
|
706
|
+
"report-satisfaction-list": "/api_v2/as_bot/statement/satisfaction/message",
|
|
707
|
+
"report-knowledge": "/api_v2/as_bot/statement/knowledge",
|
|
708
|
+
"report-escalation": "/api_v2/as_bot/statement/transcript/reason2",
|
|
709
|
+
"report-livechat": "/api_v2/as_bot/livechat/report/summary",
|
|
710
|
+
"report-livechat-table": "/api_v2/as_bot/livechat/report/table",
|
|
711
|
+
"report-inspection": "/api_v2/intelli/inspection/ticket/count",
|
|
712
|
+
"report-self-learning": "/api_v2/as_bot/statement/self-learning"
|
|
713
|
+
};
|
|
714
|
+
for (const [name, path] of Object.entries(reportPaths)) handlers2[name] = async (c, a) => c.rawCall("POST", path, await reportBody(a));
|
|
715
|
+
var REMAINING_COMMANDS = new Set(Object.keys(handlers2));
|
|
716
|
+
async function runRemainingCommand(client, command, args) {
|
|
717
|
+
const handler = handlers2[command];
|
|
718
|
+
if (!handler) throw new Error(`TypeScript command not migrated yet: ${command}`);
|
|
719
|
+
return handler(client, args);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/solvea/commands.ts
|
|
723
|
+
function requireFlag2(args, name, command) {
|
|
724
|
+
if (!flag(args, name)) throw new Error(`${command} requires ${name} after explicit user acknowledgement`);
|
|
725
|
+
}
|
|
726
|
+
function enabled(args, command) {
|
|
727
|
+
const enable = flag(args, "--enable");
|
|
728
|
+
const disable = flag(args, "--disable");
|
|
729
|
+
if (enable === disable) throw new Error(`${command} requires exactly one of --enable or --disable`);
|
|
730
|
+
return enable;
|
|
731
|
+
}
|
|
732
|
+
async function data2(args) {
|
|
733
|
+
const file = option(args, "--data-file");
|
|
734
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data");
|
|
735
|
+
if (!raw) throw new Error("--data or --data-file is required");
|
|
736
|
+
const parsed = JSON.parse(raw);
|
|
737
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
|
|
738
|
+
return parsed;
|
|
739
|
+
}
|
|
740
|
+
async function transcriptFilters(args) {
|
|
741
|
+
const filters = {
|
|
742
|
+
pageIndex: Number(option(args, "--page") ?? 1),
|
|
743
|
+
pageSize: Number(option(args, "--page-size") ?? 50),
|
|
744
|
+
debug: flag(args, "--debug"),
|
|
745
|
+
...await optionalData(args)
|
|
746
|
+
};
|
|
747
|
+
const optional = [
|
|
748
|
+
["beginTimestamp", "--begin-ts", Number],
|
|
749
|
+
["endTimestamp", "--end-ts", Number],
|
|
750
|
+
["touchPoint", "--touch-point", String],
|
|
751
|
+
["botId", "--bot-id", Number],
|
|
752
|
+
["agentId", "--agent-id", Number],
|
|
753
|
+
["intentId", "--intent-id", Number],
|
|
754
|
+
["keyword", "--keyword", String],
|
|
755
|
+
["handoff", "--handoff", (value) => value === "true"]
|
|
756
|
+
];
|
|
757
|
+
for (const [field, name, convert] of optional) {
|
|
758
|
+
const value = option(args, name);
|
|
759
|
+
if (value !== void 0) filters[field] = convert(value);
|
|
760
|
+
}
|
|
761
|
+
return filters;
|
|
762
|
+
}
|
|
763
|
+
async function runSolveaCommand(session, command, args) {
|
|
764
|
+
const client = new SolveaClient(session);
|
|
765
|
+
if (EXTENDED_COMMANDS.has(command)) return runExtendedCommand(client, command, args);
|
|
766
|
+
if (REMAINING_COMMANDS.has(command)) return runRemainingCommand(client, command, args);
|
|
767
|
+
if (command.startsWith("chat-report-")) return runRemainingCommand(client, "report-message", args);
|
|
768
|
+
const id = () => option(args, "--id", true);
|
|
769
|
+
switch (command) {
|
|
770
|
+
case "get-project":
|
|
771
|
+
return client.projectInfo();
|
|
772
|
+
case "list-agents":
|
|
773
|
+
return client.listAgents(option(args, "--category") ?? "text");
|
|
774
|
+
case "get-agent":
|
|
775
|
+
return client.getAgent(id());
|
|
776
|
+
case "get-chatflow-graph":
|
|
777
|
+
return client.getChatflowGraph(option(args, "--agent-id", true));
|
|
778
|
+
case "create-agent":
|
|
779
|
+
return client.createAgent(await data2(args));
|
|
780
|
+
case "update-agent":
|
|
781
|
+
return client.updateAgent(await data2(args));
|
|
782
|
+
case "list-plugins":
|
|
783
|
+
return client.listPlugins();
|
|
784
|
+
case "get-plugin":
|
|
785
|
+
return client.getPlugin(id());
|
|
786
|
+
case "create-plugin":
|
|
787
|
+
return client.createPlugin(await data2(args));
|
|
788
|
+
case "update-plugin":
|
|
789
|
+
return client.updatePlugin(await data2(args));
|
|
790
|
+
case "delete-plugin":
|
|
791
|
+
requireFlag2(args, "--confirm-delete", command);
|
|
792
|
+
return client.deletePlugin(id());
|
|
793
|
+
case "chat":
|
|
794
|
+
return client.chat({ message: option(args, "--message") ?? option(args, "-m", true), botId: option(args, "--bot-id"), chatId: option(args, "--chat-id"), touchPoint: option(args, "--touch-point") ?? "LIVE_CHAT", ...await optionalData(args) });
|
|
795
|
+
case "chat-async":
|
|
796
|
+
return client.chatAsync({ message: option(args, "--message") ?? option(args, "-m", true), botId: option(args, "--bot-id"), chatId: option(args, "--chat-id"), touchPoint: option(args, "--touch-point") ?? "LIVE_CHAT", ...await optionalData(args) });
|
|
797
|
+
case "get-async-result":
|
|
798
|
+
return client.asyncResult(option(args, "--receipt", true), option(args, "--bot-id"));
|
|
799
|
+
case "list-intents":
|
|
800
|
+
return client.listIntents(option(args, "--category") ?? "E_COMMERCE");
|
|
801
|
+
case "get-intent":
|
|
802
|
+
return client.getIntent(id());
|
|
803
|
+
case "create-intent":
|
|
804
|
+
return client.createIntent(await data2(args));
|
|
805
|
+
case "update-intent":
|
|
806
|
+
return client.updateIntent(id(), await data2(args));
|
|
807
|
+
case "toggle-intent":
|
|
808
|
+
return client.toggleIntent(id(), !flag(args, "--disable"));
|
|
809
|
+
case "list-entities":
|
|
810
|
+
return client.listEntities();
|
|
811
|
+
case "list-strategies":
|
|
812
|
+
return client.listStrategies(Number(option(args, "--page-size") ?? 100));
|
|
813
|
+
case "create-strategy":
|
|
814
|
+
return client.createStrategy(await data2(args));
|
|
815
|
+
case "update-strategy":
|
|
816
|
+
return client.updateStrategy(id(), await data2(args));
|
|
817
|
+
case "toggle-strategy":
|
|
818
|
+
return client.toggleStrategy(id(), !flag(args, "--disable"));
|
|
819
|
+
case "get-quality-status":
|
|
820
|
+
return client.qualityStatus();
|
|
821
|
+
case "set-quality-status":
|
|
822
|
+
return client.setQualityStatus(enabled(args, command));
|
|
823
|
+
case "list-quality-rules":
|
|
824
|
+
return client.qualityRules(Number(option(args, "--begin-ts") ?? 0) || void 0, Number(option(args, "--end-ts") ?? 0) || void 0);
|
|
825
|
+
case "get-quality-rule":
|
|
826
|
+
return client.qualityRule(option(args, "--inspection-key", true));
|
|
827
|
+
case "update-quality-rule":
|
|
828
|
+
return client.updateQualityRule(await data2(args));
|
|
829
|
+
case "get-custom-quality-rule":
|
|
830
|
+
return client.customQualityRule(option(args, "--inspection-key", true));
|
|
831
|
+
case "create-custom-quality-rule":
|
|
832
|
+
return client.createCustomQualityRule(await data2(args));
|
|
833
|
+
case "update-custom-quality-rule":
|
|
834
|
+
return client.updateCustomQualityRule(await data2(args));
|
|
835
|
+
case "delete-custom-quality-rule":
|
|
836
|
+
return client.deleteCustomQualityRule(option(args, "--inspection-key", true));
|
|
837
|
+
case "list-knowledge":
|
|
838
|
+
return client.listKnowledge(await optionalData(args));
|
|
839
|
+
case "get-knowledge":
|
|
840
|
+
return client.getKnowledge(id());
|
|
841
|
+
case "update-knowledge":
|
|
842
|
+
return client.updateKnowledge(id(), await data2(args));
|
|
843
|
+
case "upload-qa":
|
|
844
|
+
return client.uploadQa((await data2(args)).items);
|
|
845
|
+
case "list-directories":
|
|
846
|
+
return client.listDirectories();
|
|
847
|
+
case "create-folder":
|
|
848
|
+
return client.createFolder(option(args, "--name", true), Number(option(args, "--parent-id") ?? 0));
|
|
849
|
+
case "update-directory":
|
|
850
|
+
return client.updateDirectory(id(), option(args, "--name") ? { name: option(args, "--name") } : await data2(args));
|
|
851
|
+
case "delete-directory":
|
|
852
|
+
requireFlag2(args, "--confirm-delete", command);
|
|
853
|
+
return client.deleteDirectory(id());
|
|
854
|
+
case "list-knowledge-tags":
|
|
855
|
+
return client.knowledgeTags(option(args, "--keyword") ?? "");
|
|
856
|
+
case "get-transcript":
|
|
857
|
+
return client.transcript(option(args, "--chat-id", true));
|
|
858
|
+
case "list-transcripts":
|
|
859
|
+
return client.listTranscripts(await transcriptFilters(args));
|
|
860
|
+
case "get-chat-context":
|
|
861
|
+
return client.chatContext(option(args, "--bot-id", true), await data2(args));
|
|
862
|
+
default:
|
|
863
|
+
throw new Error(`TypeScript command not migrated yet: ${command}`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
async function optionalData(args) {
|
|
867
|
+
if (!option(args, "--data") && !option(args, "--data-file")) return {};
|
|
868
|
+
return data2(args);
|
|
192
869
|
}
|
|
870
|
+
function isNativeSolveaCommand(command) {
|
|
871
|
+
return command.startsWith("chat-report-") || EXTENDED_COMMANDS.has(command) || REMAINING_COMMANDS.has(command) || (/* @__PURE__ */ new Set([
|
|
872
|
+
"get-project",
|
|
873
|
+
"list-agents",
|
|
874
|
+
"get-agent",
|
|
875
|
+
"get-chatflow-graph",
|
|
876
|
+
"create-agent",
|
|
877
|
+
"update-agent",
|
|
878
|
+
"list-plugins",
|
|
879
|
+
"get-plugin",
|
|
880
|
+
"create-plugin",
|
|
881
|
+
"update-plugin",
|
|
882
|
+
"delete-plugin",
|
|
883
|
+
"chat",
|
|
884
|
+
"chat-async",
|
|
885
|
+
"get-async-result",
|
|
886
|
+
"list-intents",
|
|
887
|
+
"get-intent",
|
|
888
|
+
"create-intent",
|
|
889
|
+
"update-intent",
|
|
890
|
+
"toggle-intent",
|
|
891
|
+
"list-entities",
|
|
892
|
+
"list-strategies",
|
|
893
|
+
"create-strategy",
|
|
894
|
+
"update-strategy",
|
|
895
|
+
"toggle-strategy",
|
|
896
|
+
"get-transcript",
|
|
897
|
+
"list-transcripts",
|
|
898
|
+
"get-chat-context",
|
|
899
|
+
"get-quality-status",
|
|
900
|
+
"set-quality-status",
|
|
901
|
+
"list-quality-rules",
|
|
902
|
+
"get-quality-rule",
|
|
903
|
+
"update-quality-rule",
|
|
904
|
+
"get-custom-quality-rule",
|
|
905
|
+
"create-custom-quality-rule",
|
|
906
|
+
"update-custom-quality-rule",
|
|
907
|
+
"delete-custom-quality-rule",
|
|
908
|
+
"list-knowledge",
|
|
909
|
+
"get-knowledge",
|
|
910
|
+
"update-knowledge",
|
|
911
|
+
"upload-qa",
|
|
912
|
+
"list-directories",
|
|
913
|
+
"create-folder",
|
|
914
|
+
"update-directory",
|
|
915
|
+
"delete-directory",
|
|
916
|
+
"list-knowledge-tags"
|
|
917
|
+
])).has(command);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/business.ts
|
|
921
|
+
var packageRoot = () => resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
193
922
|
async function runApi(session, command, args) {
|
|
194
|
-
|
|
195
|
-
|
|
923
|
+
if (isNativeSolveaCommand(command)) {
|
|
924
|
+
console.log(JSON.stringify(await runSolveaCommand(session, command, args), null, 2));
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
throw new Error(`unknown TypeScript API command: ${command}`);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/commands/resources.ts
|
|
931
|
+
var action = (command, summary, usage2 = "") => ({ command, summary, usage: usage2 });
|
|
932
|
+
var resources = {
|
|
933
|
+
agent: {
|
|
934
|
+
list: action("list-agents", "List agents", "[--category text|voice]"),
|
|
935
|
+
get: action("get-agent", "Get one agent", "--id <agent-id>"),
|
|
936
|
+
create: action("create-agent", "Create an agent", "--data <json> | --data-file <path>"),
|
|
937
|
+
update: action("update-agent", "Update an agent", "--data <json> | --data-file <path>"),
|
|
938
|
+
"get-chatflow": action("get-chatflow-graph", "Get an agent chatflow", "--agent-id <agent-id>")
|
|
939
|
+
},
|
|
940
|
+
intent: {
|
|
941
|
+
list: action("list-intents", "List intents", "[--category <category>]"),
|
|
942
|
+
get: action("get-intent", "Get one intent", "--id <intent-id>"),
|
|
943
|
+
create: action("create-intent", "Create an intent", "--data <json> | --data-file <path>"),
|
|
944
|
+
update: action("update-intent", "Update an intent", "--id <intent-id> (--data <json> | --data-file <path>)"),
|
|
945
|
+
toggle: action("toggle-intent", "Enable or disable an intent", "--id <intent-id> [--enable | --disable] (default: --enable)")
|
|
946
|
+
},
|
|
947
|
+
strategy: {
|
|
948
|
+
list: action("list-strategies", "List strategies", "[--page-size <number>]"),
|
|
949
|
+
entities: action("list-entities", "List strategy entities"),
|
|
950
|
+
create: action("create-strategy", "Create a strategy", "--data <json> | --data-file <path>"),
|
|
951
|
+
update: action("update-strategy", "Update a strategy", "--id <strategy-id> (--data <json> | --data-file <path>)"),
|
|
952
|
+
toggle: action("toggle-strategy", "Enable or disable a strategy", "--id <strategy-id> [--enable | --disable] (default: --enable)")
|
|
953
|
+
},
|
|
954
|
+
tool: {
|
|
955
|
+
list: action("list-plugins", "List tools"),
|
|
956
|
+
get: action("get-plugin", "Get one tool", "--id <tool-id>"),
|
|
957
|
+
create: action("create-plugin", "Create a tool", "--data <json> | --data-file <path>"),
|
|
958
|
+
update: action("update-plugin", "Update a tool", "--data <json> | --data-file <path>"),
|
|
959
|
+
delete: action("delete-plugin", "Delete a tool", "--id <tool-id> --confirm-delete"),
|
|
960
|
+
run: action("run-plugin", "Run an HTTP tool without saving it", "--data <json> | --data-file <path>")
|
|
961
|
+
},
|
|
962
|
+
field: {
|
|
963
|
+
canvas: action("list-ai-fields", "List CHAT_FLOW canvas fields"),
|
|
964
|
+
"agent-range": action("list-agent-ai-fields", "List fields accepted by Agent validation"),
|
|
965
|
+
guide: action("get-ai-field-guide", "Get AI Field initialization status"),
|
|
966
|
+
initialize: action("initialize-ai-fields", "Initialize system AI Fields"),
|
|
967
|
+
list: action("list-managed-ai-fields", "List managed AI Fields", "[--page <number>] [--page-size <number>] [--keyword <name>]"),
|
|
968
|
+
get: action("get-managed-ai-field", "Get one managed AI Field", "--id <field-id>"),
|
|
969
|
+
create: action("create-managed-ai-field", "Create a managed AI Field", "--data <json> | --data-file <path>"),
|
|
970
|
+
update: action("update-managed-ai-field", "Update a managed AI Field", "--data <json> | --data-file <path>"),
|
|
971
|
+
delete: action("delete-managed-ai-field", "Delete a managed AI Field", "--id <field-id> --confirm-delete"),
|
|
972
|
+
toggle: action("toggle-managed-ai-field", "Toggle a managed AI Field", "--id <field-id>")
|
|
973
|
+
},
|
|
974
|
+
template: {
|
|
975
|
+
list: action("list-templates", "List reply templates", "[--page <number>] [--page-size <number>] [--keyword <text>] [--scene <scene>] [--status <status>]"),
|
|
976
|
+
create: action("create-template", "Create a reply template", "(--name <name> --content <content>) | --data-file <path>"),
|
|
977
|
+
update: action("update-template", "Update a reply template", "--id <template-id> [--name <name>] [--content <content>] [--status <status>] [--data-file <path>]"),
|
|
978
|
+
delete: action("delete-templates", "Delete reply templates", "--ids <id> [id...] --confirm-delete")
|
|
979
|
+
},
|
|
980
|
+
knowledge: {
|
|
981
|
+
list: action("list-knowledge", "List knowledge", "[--data <json> | --data-file <path>]"),
|
|
982
|
+
get: action("get-knowledge", "Get one knowledge item", "--id <knowledge-id>"),
|
|
983
|
+
update: action("update-knowledge", "Update a knowledge item", "--id <knowledge-id> (--data <json> | --data-file <path>)"),
|
|
984
|
+
"upload-qa": action("upload-qa", "Upload QA items", "--data <json> | --data-file <path>"),
|
|
985
|
+
tags: action("list-knowledge-tags", "List knowledge tags", "[--keyword <keyword>]")
|
|
986
|
+
},
|
|
987
|
+
directory: {
|
|
988
|
+
list: action("list-directories", "List knowledge directories"),
|
|
989
|
+
create: action("create-folder", "Create a knowledge directory", "--name <name> [--parent-id <directory-id>]"),
|
|
990
|
+
update: action("update-directory", "Update a knowledge directory", "--id <directory-id> (--name <name> | --data <json> | --data-file <path>)"),
|
|
991
|
+
delete: action("delete-directory", "Delete a knowledge directory", "--id <directory-id> --confirm-delete")
|
|
992
|
+
},
|
|
993
|
+
quality: {
|
|
994
|
+
status: action("get-quality-status", "Get quality inspection status"),
|
|
995
|
+
toggle: action("set-quality-status", "Enable or disable quality inspection", "--enable | --disable"),
|
|
996
|
+
rules: action("list-quality-rules", "List quality rules", "[--begin-ts <milliseconds>] [--end-ts <milliseconds>]"),
|
|
997
|
+
"get-rule": action("get-quality-rule", "Get one quality rule", "--inspection-key <key>"),
|
|
998
|
+
"update-rule": action("update-quality-rule", "Update a quality rule", "--data <json> | --data-file <path>"),
|
|
999
|
+
"get-custom-rule": action("get-custom-quality-rule", "Get one custom quality rule", "--inspection-key <key>"),
|
|
1000
|
+
"create-custom-rule": action("create-custom-quality-rule", "Create a custom quality rule", "--data <json> | --data-file <path>"),
|
|
1001
|
+
"update-custom-rule": action("update-custom-quality-rule", "Update a custom quality rule", "--data <json> | --data-file <path>"),
|
|
1002
|
+
"delete-custom-rule": action("delete-custom-quality-rule", "Delete a custom quality rule", "--inspection-key <key>"),
|
|
1003
|
+
alerts: action("list-quality-alert-rules", "List quality alert rules"),
|
|
1004
|
+
"get-alert": action("get-quality-alert-rule", "Get one quality alert rule", "--id <alert-id>"),
|
|
1005
|
+
"create-alert": action("create-quality-alert-rule", "Create a quality alert rule", "--data <json> | --data-file <path>"),
|
|
1006
|
+
"update-alert": action("update-quality-alert-rule", "Update a quality alert rule", "--data <json> | --data-file <path>"),
|
|
1007
|
+
"delete-alert": action("delete-quality-alert-rule", "Delete a quality alert rule", "--id <alert-id> --confirm-delete"),
|
|
1008
|
+
"alert-messages": action("list-quality-alert-messages", "List quality alert messages", "--data <json> | --data-file <path>"),
|
|
1009
|
+
"get-alert-message": action("get-quality-alert-message", "Get one quality alert message", "--id <message-id>")
|
|
1010
|
+
},
|
|
1011
|
+
transcript: {
|
|
1012
|
+
list: action("list-transcripts", "List transcripts", "[--page <number>] [--page-size <number>] [--begin-ts <milliseconds>] [--end-ts <milliseconds>] [--data <json> | --data-file <path>]"),
|
|
1013
|
+
get: action("get-transcript", "Get one transcript", "--chat-id <chat-id>"),
|
|
1014
|
+
context: action("get-chat-context", "Get chat context", "--bot-id <bot-id> (--data <json> | --data-file <path>)")
|
|
1015
|
+
},
|
|
1016
|
+
chat: {
|
|
1017
|
+
send: action("chat", "Send a synchronous chat message", "--message <text> [--bot-id <bot-id>] [--chat-id <chat-id>]"),
|
|
1018
|
+
async: action("chat-async", "Send an asynchronous chat message", "--message <text> [--bot-id <bot-id>] [--chat-id <chat-id>]"),
|
|
1019
|
+
result: action("get-async-result", "Get an asynchronous chat result", "--receipt <receipt> [--bot-id <bot-id>]")
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
function isResourceCommand(value) {
|
|
1023
|
+
return value in resources;
|
|
1024
|
+
}
|
|
1025
|
+
function resourceHelp(resource, actionName) {
|
|
1026
|
+
if (!resource) return `Business resources:
|
|
1027
|
+
${Object.keys(resources).map((name) => ` ${name}`).join("\n")}`;
|
|
1028
|
+
if (!isResourceCommand(resource)) throw new Error(`unknown resource: ${resource}`);
|
|
1029
|
+
const actions = resources[resource];
|
|
1030
|
+
if (actionName) {
|
|
1031
|
+
const definition = actions[actionName];
|
|
1032
|
+
if (!definition) throw new Error(`unknown ${resource} action: ${actionName}
|
|
1033
|
+
${resourceHelp(resource)}`);
|
|
1034
|
+
return [
|
|
1035
|
+
definition.summary,
|
|
1036
|
+
`Usage: cxclawctl ${resource} ${actionName}${definition.usage ? ` ${definition.usage}` : ""}`
|
|
1037
|
+
].join("\n\n");
|
|
1038
|
+
}
|
|
1039
|
+
return [
|
|
1040
|
+
`${resource} actions:`,
|
|
1041
|
+
...Object.entries(actions).map(([name, definition]) => ` ${name.padEnd(20)} ${definition.summary}`),
|
|
1042
|
+
`
|
|
1043
|
+
Run cxclawctl ${resource} <action> --help for action options.`
|
|
1044
|
+
].join("\n");
|
|
1045
|
+
}
|
|
1046
|
+
function resolveResourceCommand(resource, action2) {
|
|
1047
|
+
if (!action2 || action2 === "help") throw new Error(resourceHelp(resource));
|
|
1048
|
+
const definition = resources[resource][action2];
|
|
1049
|
+
if (!definition) throw new Error(`unknown ${resource} action: ${action2}
|
|
1050
|
+
${resourceHelp(resource)}`);
|
|
1051
|
+
return definition.command;
|
|
1052
|
+
}
|
|
1053
|
+
async function runResourceCommand(session, resource, action2, args) {
|
|
1054
|
+
await runApi(session, resolveResourceCommand(resource, action2), args);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/doctor.ts
|
|
1058
|
+
import { access } from "fs/promises";
|
|
1059
|
+
import { join } from "path";
|
|
1060
|
+
async function diagnose() {
|
|
1061
|
+
const root2 = packageRoot();
|
|
1062
|
+
const requiredFiles = [
|
|
1063
|
+
"package.json"
|
|
1064
|
+
];
|
|
1065
|
+
await Promise.all(requiredFiles.map((path) => access(join(root2, path))));
|
|
1066
|
+
const expectedPluginVersion = process.env.CXCLAW_PLUGIN_VERSION || null;
|
|
1067
|
+
if (expectedPluginVersion && expectedPluginVersion !== VERSION) {
|
|
1068
|
+
throw new Error(
|
|
1069
|
+
`plugin version ${expectedPluginVersion} does not match CLI ${VERSION}`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
ok: true,
|
|
1074
|
+
cliVersion: VERSION,
|
|
1075
|
+
pluginVersion: expectedPluginVersion,
|
|
1076
|
+
versionsMatch: expectedPluginVersion ? expectedPluginVersion === VERSION : null,
|
|
1077
|
+
nodeVersion: process.versions.node,
|
|
1078
|
+
runtimeFiles: requiredFiles
|
|
1079
|
+
};
|
|
196
1080
|
}
|
|
197
1081
|
|
|
198
1082
|
// src/sls.ts
|
|
199
1083
|
import LogClient from "@alicloud/log";
|
|
200
|
-
async function query(session,
|
|
1084
|
+
async function query(session, options3) {
|
|
201
1085
|
const temporary = await post(session, "/sls/session", {}, true);
|
|
202
|
-
if (
|
|
1086
|
+
if (options3.from <= 0 || options3.to < options3.from || options3.to - options3.from > temporary.limits.maxTimeRangeSeconds) throw new Error("SLS time range exceeds the server limit");
|
|
203
1087
|
const client = new LogClient({ endpoint: temporary.endpoint, use_https: true, accessKeyId: temporary.credentials.accessKeyId, accessKeySecret: temporary.credentials.accessKeySecret, securityToken: temporary.credentials.securityToken });
|
|
204
|
-
const response = await client.getLogs(temporary.project, temporary.logstore, new Date(
|
|
1088
|
+
const response = await client.getLogs(temporary.project, temporary.logstore, new Date(options3.from * 1e3), new Date(options3.to * 1e3), { query: options3.query, line: Math.min(options3.line || 50, temporary.limits.maxResults), offset: options3.offset || 0, reverse: options3.reverse });
|
|
205
1089
|
const logs = (response.body ?? response).map?.((row) => Object.fromEntries(Object.entries(row).map(([k, v]) => [k, String(v).slice(0, temporary.limits.maxMessageChars || void 0)]))) ?? response.body ?? response;
|
|
206
1090
|
return { logs };
|
|
207
1091
|
}
|
|
@@ -229,132 +1113,1323 @@ function publicTenant(t) {
|
|
|
229
1113
|
}
|
|
230
1114
|
|
|
231
1115
|
// src/update.ts
|
|
232
|
-
import { spawn
|
|
1116
|
+
import { spawn } from "child_process";
|
|
233
1117
|
import { mkdir, writeFile } from "fs/promises";
|
|
234
1118
|
import { homedir, platform } from "os";
|
|
235
|
-
import { join as join2 } from "path";
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
1119
|
+
import { isAbsolute, join as join2 } from "path";
|
|
1120
|
+
var exec = (command, args, options3 = {}) => new Promise((resolve5, reject) => {
|
|
1121
|
+
const chunks = [];
|
|
1122
|
+
const child = spawn(command, args, {
|
|
1123
|
+
stdio: options3.capture ? ["ignore", "pipe", "inherit"] : "inherit"
|
|
1124
|
+
});
|
|
1125
|
+
if (options3.capture) child.stdout?.on("data", (chunk) => chunks.push(chunk));
|
|
1126
|
+
child.on("error", reject);
|
|
1127
|
+
child.on("exit", (code) => {
|
|
1128
|
+
if (code === 0 || options3.allowFailure) {
|
|
1129
|
+
resolve5(Buffer.concat(chunks).toString("utf8"));
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
reject(new Error(`${command} exited with ${code}`));
|
|
1133
|
+
});
|
|
241
1134
|
});
|
|
1135
|
+
var xml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1136
|
+
var systemdArg = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
1137
|
+
var windowsArg = (value) => /[\s"]/.test(value) ? `"${value.replaceAll('"', '\\"')}"` : value;
|
|
1138
|
+
function schedulerInvocation(environment2 = process.env, nodeExecutable = process.execPath) {
|
|
1139
|
+
const npmExecutable = environment2.npm_execpath;
|
|
1140
|
+
if (!npmExecutable || !isAbsolute(npmExecutable)) {
|
|
1141
|
+
throw new Error(
|
|
1142
|
+
"cannot register updater: run this command through npm or npx so npm_execpath is available"
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
return {
|
|
1146
|
+
command: nodeExecutable,
|
|
1147
|
+
args: [
|
|
1148
|
+
npmExecutable,
|
|
1149
|
+
"exec",
|
|
1150
|
+
"--yes",
|
|
1151
|
+
"--package=@shulex/cxclawctl@latest",
|
|
1152
|
+
"--",
|
|
1153
|
+
"cxclawctl",
|
|
1154
|
+
"update",
|
|
1155
|
+
"--scheduled"
|
|
1156
|
+
]
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
242
1159
|
async function update() {
|
|
243
|
-
const info = await fetch("https://registry.npmjs.org/@shulex%2Fcxclawctl/latest", { signal: AbortSignal.timeout(3e4) }).then((r) => {
|
|
244
|
-
if (!r.ok) throw new Error(`npm registry returned ${r.status}`);
|
|
245
|
-
return r.json();
|
|
246
|
-
});
|
|
247
|
-
if (compareVersions(info.version, VERSION) > 0) console.log(`\u53D1\u73B0\u65B0\u7248\u672C ${info.version}\uFF1Bnpx \u4E0B\u6B21\u8FD0\u884C\u4F1A\u81EA\u52A8\u4F7F\u7528\u6700\u65B0\u7248\u3002`);
|
|
248
1160
|
await exec("codex", ["plugin", "marketplace", "upgrade", "cxclaw"]);
|
|
249
|
-
|
|
1161
|
+
await exec("codex", ["plugin", "add", "cxclaw@cxclaw"]);
|
|
1162
|
+
const raw = await exec("codex", ["plugin", "list", "--json"], { capture: true });
|
|
1163
|
+
const list = JSON.parse(raw);
|
|
1164
|
+
const plugin = list.installed?.find((item) => item.pluginId === "cxclaw@cxclaw");
|
|
1165
|
+
if (!plugin?.version || plugin.enabled === false) {
|
|
1166
|
+
throw new Error("CXClaw plugin is not installed and enabled");
|
|
1167
|
+
}
|
|
1168
|
+
const response = await fetch(
|
|
1169
|
+
`https://registry.npmjs.org/@shulex%2Fcxclawctl/${encodeURIComponent(plugin.version)}`,
|
|
1170
|
+
{ signal: AbortSignal.timeout(3e4) }
|
|
1171
|
+
);
|
|
1172
|
+
if (!response.ok) {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`npm package @shulex/cxclawctl@${plugin.version} is unavailable (${response.status})`
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
const metadata = await response.json();
|
|
1178
|
+
if (metadata.version !== plugin.version) {
|
|
1179
|
+
throw new Error(
|
|
1180
|
+
`npm returned ${metadata.version ?? "no version"}, expected ${plugin.version}`
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
pluginVersion: plugin.version,
|
|
1185
|
+
cliPackageVersion: metadata.version,
|
|
1186
|
+
restartRequired: true
|
|
1187
|
+
};
|
|
250
1188
|
}
|
|
251
1189
|
async function installScheduler() {
|
|
252
|
-
const
|
|
253
|
-
const args = ["--yes", "@shulex/cxclawctl@latest", "update", "--scheduled"];
|
|
1190
|
+
const invocation = schedulerInvocation();
|
|
254
1191
|
if (platform() === "darwin") {
|
|
255
1192
|
const dir = join2(homedir(), "Library/LaunchAgents");
|
|
1193
|
+
const path = join2(dir, "com.solvea.cxclaw.update.plist");
|
|
1194
|
+
const programArguments = [invocation.command, ...invocation.args].map((value) => `<string>${xml(value)}</string>`).join("");
|
|
256
1195
|
await mkdir(dir, { recursive: true });
|
|
257
|
-
await writeFile(
|
|
258
|
-
|
|
259
|
-
|
|
1196
|
+
await writeFile(
|
|
1197
|
+
path,
|
|
1198
|
+
`<?xml version="1.0" encoding="UTF-8"?>
|
|
1199
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1200
|
+
<plist version="1.0"><dict><key>Label</key><string>com.solvea.cxclaw.update</string><key>ProgramArguments</key><array>${programArguments}</array><key>StartInterval</key><integer>21600</integer><key>RunAtLoad</key><true/></dict></plist>
|
|
1201
|
+
`
|
|
1202
|
+
);
|
|
1203
|
+
await exec("launchctl", ["unload", path], { allowFailure: true });
|
|
1204
|
+
await exec("launchctl", ["load", path]);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
if (platform() === "linux") {
|
|
260
1208
|
const dir = join2(homedir(), ".config/systemd/user");
|
|
1209
|
+
const command = [invocation.command, ...invocation.args].map(systemdArg).join(" ");
|
|
261
1210
|
await mkdir(dir, { recursive: true });
|
|
262
|
-
await writeFile(
|
|
1211
|
+
await writeFile(
|
|
1212
|
+
join2(dir, "cxclaw-update.service"),
|
|
1213
|
+
`[Service]
|
|
263
1214
|
Type=oneshot
|
|
264
|
-
ExecStart=${command}
|
|
265
|
-
`
|
|
266
|
-
|
|
1215
|
+
ExecStart=${command}
|
|
1216
|
+
`
|
|
1217
|
+
);
|
|
1218
|
+
await writeFile(
|
|
1219
|
+
join2(dir, "cxclaw-update.timer"),
|
|
1220
|
+
"[Timer]\nOnBootSec=5m\nOnUnitActiveSec=6h\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
|
|
1221
|
+
);
|
|
267
1222
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
268
1223
|
await exec("systemctl", ["--user", "enable", "--now", "cxclaw-update.timer"]);
|
|
269
|
-
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const task = [invocation.command, ...invocation.args].map(windowsArg).join(" ");
|
|
1227
|
+
await exec("schtasks", [
|
|
1228
|
+
"/Create",
|
|
1229
|
+
"/F",
|
|
1230
|
+
"/SC",
|
|
1231
|
+
"HOURLY",
|
|
1232
|
+
"/MO",
|
|
1233
|
+
"6",
|
|
1234
|
+
"/TN",
|
|
1235
|
+
"Solvea CXClaw Update",
|
|
1236
|
+
"/TR",
|
|
1237
|
+
task
|
|
1238
|
+
]);
|
|
270
1239
|
}
|
|
271
1240
|
async function installMarketplace(channel) {
|
|
272
|
-
if (!["stable", "canary", "dev"].includes(channel))
|
|
273
|
-
|
|
274
|
-
|
|
1241
|
+
if (!["stable", "canary", "dev"].includes(channel)) {
|
|
1242
|
+
throw new Error("invalid channel");
|
|
1243
|
+
}
|
|
1244
|
+
await exec("codex", [
|
|
1245
|
+
"plugin",
|
|
1246
|
+
"marketplace",
|
|
1247
|
+
"add",
|
|
1248
|
+
"SolveaCX/cx_claw_codex",
|
|
1249
|
+
"--ref",
|
|
1250
|
+
`marketplace/${channel}`
|
|
1251
|
+
]).catch(() => exec("codex", ["plugin", "marketplace", "upgrade", "cxclaw"]));
|
|
1252
|
+
await exec("codex", ["plugin", "add", "cxclaw@cxclaw"]);
|
|
275
1253
|
}
|
|
276
1254
|
|
|
277
|
-
// src/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
1255
|
+
// src/workflows/runtime.ts
|
|
1256
|
+
import { execFile } from "child_process";
|
|
1257
|
+
import { promises as fs } from "fs";
|
|
1258
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
1259
|
+
import { promisify } from "util";
|
|
1260
|
+
var exec2 = promisify(execFile);
|
|
1261
|
+
var workspace = () => resolve2(process.env.WORKSPACE_DIR ?? join3(process.cwd(), "workspace"));
|
|
1262
|
+
var root = () => join3(workspace(), "runtime-inspector");
|
|
1263
|
+
var dirs = () => ({ output: join3(root(), "outputs"), state: join3(root(), "state"), tmp: join3(root(), "tmp") });
|
|
1264
|
+
var timestamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1265
|
+
var readJson = async (path, fallback) => {
|
|
1266
|
+
try {
|
|
1267
|
+
return JSON.parse(await fs.readFile(path, "utf8"));
|
|
1268
|
+
} catch {
|
|
1269
|
+
return fallback;
|
|
284
1270
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
1271
|
+
};
|
|
1272
|
+
var writeJson = async (path, value) => {
|
|
1273
|
+
await fs.mkdir(resolve2(path, ".."), { recursive: true });
|
|
1274
|
+
await fs.writeFile(path, JSON.stringify(value, null, 2));
|
|
1275
|
+
};
|
|
1276
|
+
function check(label, ok, detail = "") {
|
|
1277
|
+
return { label, ok, ...detail ? { detail } : {} };
|
|
1278
|
+
}
|
|
1279
|
+
async function prepare(args) {
|
|
1280
|
+
const { tmp, state: state2 } = dirs();
|
|
1281
|
+
const source = option(args, "--source");
|
|
1282
|
+
const batchSize = Number(option(args, "--batch-size") ?? 10);
|
|
1283
|
+
if (!Number.isSafeInteger(batchSize) || batchSize <= 0) throw new Error("--batch-size must be a positive integer");
|
|
1284
|
+
let records;
|
|
1285
|
+
if (source) {
|
|
1286
|
+
const content = await fs.readFile(source, "utf8");
|
|
1287
|
+
if (source.toLowerCase().endsWith(".json")) {
|
|
1288
|
+
const parsed = JSON.parse(content);
|
|
1289
|
+
records = Array.isArray(parsed) ? parsed.map((item) => typeof item === "object" && item ? item : { text: String(item) }) : [parsed];
|
|
1290
|
+
} else if (source.toLowerCase().endsWith(".csv")) {
|
|
1291
|
+
const [header, ...lines] = content.split(/\r?\n/).filter(Boolean).map((line) => line.split(","));
|
|
1292
|
+
records = lines.map((values) => Object.fromEntries((header ?? []).map((key, i) => [key, values[i] ?? ""])));
|
|
1293
|
+
} else records = content.split(/\r?\n/).filter(Boolean).map((text) => ({ text }));
|
|
1294
|
+
} else records = Array.from({ length: 30 }, (_, i) => ({ id: i + 1, text: `\u6D4B\u8BD5\u8BB0\u5F55 #${i + 1}`, value: (i + 1) * 10 }));
|
|
1295
|
+
await fs.rm(tmp, { recursive: true, force: true });
|
|
1296
|
+
await fs.mkdir(tmp, { recursive: true });
|
|
1297
|
+
const batches = [];
|
|
1298
|
+
for (let i = 0; i < records.length; i += batchSize) batches.push(records.slice(i, i + batchSize));
|
|
1299
|
+
await Promise.all(batches.map((batch, i) => writeJson(join3(tmp, `batch_${String(i).padStart(3, "0")}.json`), batch)));
|
|
1300
|
+
await writeJson(join3(state2, "inspect.json"), { status: "batched", total_records: records.length, total_batches: batches.length, batch_size: batchSize, source: source ?? "auto-generated", written_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1301
|
+
return { status: "batched", totalRecords: records.length, totalBatches: batches.length, tmp };
|
|
1302
|
+
}
|
|
1303
|
+
async function processBatches() {
|
|
1304
|
+
const { tmp, output, state: state2 } = dirs();
|
|
1305
|
+
const current = await readJson(join3(state2, "inspect.json"), {});
|
|
1306
|
+
if (current.status !== "batched") throw new Error(`runtime state must be batched, got ${String(current.status ?? "missing")}`);
|
|
1307
|
+
const names = (await fs.readdir(tmp)).filter((name) => /^batch_\d+\.json$/.test(name)).sort();
|
|
1308
|
+
if (!names.length) throw new Error(`no batch files found in ${tmp}`);
|
|
1309
|
+
const records = [];
|
|
1310
|
+
for (const name of names) for (const row of await readJson(join3(tmp, name), [])) records.push({ ...row, _processed: true, _summary: `processed: ${JSON.stringify(row).slice(0, 60)}`, _at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1311
|
+
const path = join3(output, `results_${timestamp()}.json`);
|
|
1312
|
+
await writeJson(path, { total: records.length, processed_at: (/* @__PURE__ */ new Date()).toISOString(), source: current.source, records });
|
|
1313
|
+
await writeJson(join3(state2, "inspect.json"), { status: "done", total_processed: records.length, results_path: path, done_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1314
|
+
return { status: "done", total: records.length, resultsPath: path };
|
|
1315
|
+
}
|
|
1316
|
+
async function state() {
|
|
1317
|
+
const { state: stateDir2, output, tmp } = dirs();
|
|
1318
|
+
return { state: await readJson(join3(stateDir2, "inspect.json"), null), outputFiles: await fs.readdir(output).catch(() => []), tmpFiles: await fs.readdir(tmp).catch(() => []) };
|
|
1319
|
+
}
|
|
1320
|
+
async function verify() {
|
|
1321
|
+
const snapshot = await state();
|
|
1322
|
+
const checks = [check("state \u6587\u4EF6\u5DF2\u8FD8\u539F", Boolean(snapshot.state)), check("status == done", snapshot.state?.status === "done", `actual=${String(snapshot.state?.status)}`), check("outputs \u76EE\u5F55\u53EF\u8BFB", Array.isArray(snapshot.outputFiles)), check("\u81F3\u5C11\u4E00\u4E2A results \u6587\u4EF6", snapshot.outputFiles.some((name) => name.startsWith("results_")))];
|
|
1323
|
+
return { ok: checks.every((item) => item.ok), checks };
|
|
1324
|
+
}
|
|
1325
|
+
async function sandbox(args) {
|
|
1326
|
+
const scenario = option(args, "--scenario") ?? "all";
|
|
1327
|
+
const d = dirs();
|
|
1328
|
+
const scenarios = scenario === "all" ? ["env", "fs", "execute", "paths", "deps"] : [scenario];
|
|
1329
|
+
const checks = [];
|
|
1330
|
+
for (const item of scenarios) {
|
|
1331
|
+
if (item === "env") checks.push(check("WORKSPACE_DIR", Boolean(process.env.WORKSPACE_DIR ?? workspace())), check("\u654F\u611F\u53D8\u91CF\u672A\u76F4\u63A5\u8F93\u51FA", true));
|
|
1332
|
+
else if (["fs", "paths"].includes(item)) {
|
|
1333
|
+
await Promise.all(Object.values(d).map((path) => fs.mkdir(path, { recursive: true })));
|
|
1334
|
+
checks.push(...Object.entries(d).map(([name, path]) => check(`${name} \u76EE\u5F55\u53EF\u5199`, true, path)));
|
|
1335
|
+
} else if (item === "execute") {
|
|
1336
|
+
const result = await exec2("sh", ["-c", "printf runtime-ok"]);
|
|
1337
|
+
checks.push(check("shell \u6267\u884C\u94FE\u8DEF", result.stdout === "runtime-ok"));
|
|
1338
|
+
} else if (item === "deps") checks.push(check("Node runtime", Number(process.versions.node.split(".")[0]) >= 22, process.versions.node));
|
|
1339
|
+
else throw new Error(`unknown runtime sandbox scenario: ${item}`);
|
|
1340
|
+
}
|
|
1341
|
+
return { ok: checks.every((item) => item.ok), scenario, checks };
|
|
1342
|
+
}
|
|
1343
|
+
async function model(args) {
|
|
1344
|
+
if (option(args, "--scenario", true) !== "truncation") throw new Error("only --scenario truncation is supported");
|
|
1345
|
+
const target = join3(dirs().tmp, "truncation_test.md");
|
|
1346
|
+
return { scenario: "truncation", target, instruction: "Use the Codex write_file tool to write 60+ long technical paragraphs, then inspect SLG model_response stop_reason and output_tokens." };
|
|
1347
|
+
}
|
|
1348
|
+
async function runRuntime(action2, args) {
|
|
1349
|
+
if (action2 === "prepare") return prepare(args);
|
|
1350
|
+
if (action2 === "process") return processBatches();
|
|
1351
|
+
if (action2 === "state") return state();
|
|
1352
|
+
if (action2 === "verify") return verify();
|
|
1353
|
+
if (action2 === "sandbox") return sandbox(args);
|
|
1354
|
+
if (action2 === "model") return model(args);
|
|
1355
|
+
if (action2 === "write-test") {
|
|
1356
|
+
const { output, state: stateDir2 } = dirs();
|
|
1357
|
+
const runId = timestamp();
|
|
1358
|
+
await writeJson(join3(output, `${runId}_test.json`), { run_id: runId, written_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1359
|
+
const current = await readJson(join3(stateDir2, "inspect.json"), {});
|
|
1360
|
+
await writeJson(join3(stateDir2, "inspect.json"), { ...current, last_run_id: runId, write_count: Number(current.write_count ?? 0) + 1 });
|
|
1361
|
+
return { ok: true, runId };
|
|
1362
|
+
}
|
|
1363
|
+
throw new Error("runtime action must be prepare, process, state, verify, sandbox, model, or write-test");
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// src/workflows/solvea-kit.ts
|
|
1367
|
+
import ExcelJS from "exceljs";
|
|
1368
|
+
import { promises as fs2 } from "fs";
|
|
1369
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
|
|
1370
|
+
var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
|
|
1371
|
+
var outputDir = (owner) => join4(workspace2(), "outputs", owner);
|
|
1372
|
+
var stateDir = (owner) => join4(workspace2(), "state", owner);
|
|
1373
|
+
var now = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1374
|
+
var readJson2 = async (path) => JSON.parse(await fs2.readFile(path, "utf8"));
|
|
1375
|
+
var writeJson2 = async (path, value) => {
|
|
1376
|
+
await fs2.mkdir(dirname2(path), { recursive: true });
|
|
1377
|
+
await fs2.writeFile(path, JSON.stringify(value, null, 2));
|
|
1378
|
+
};
|
|
1379
|
+
var INIT_NOTICE = `The information of this row cannot be deleted! Tips:
|
|
1380
|
+
1. first_level is the first level that is not empty.
|
|
1381
|
+
2. second_level is the second level.
|
|
1382
|
+
3. third_level is the third level.
|
|
1383
|
+
4. fourth_level is the fourth level.
|
|
1384
|
+
5. "Name", "describe" and "example" are for the use of the large model. Do not repeat them.`;
|
|
1385
|
+
var UPDATE_NOTICE = `The information of this row cannot be deleted! Tips:
|
|
1386
|
+
1. The "id" is automatically generated by the program. When it is empty, it indicates adding a new tag.
|
|
1387
|
+
2. "Name", "describe" and "example" are for the use of the large model. Do not repeat them.
|
|
1388
|
+
3. Fill in the "parent_ID" with the ID of the parent node tag.`;
|
|
1389
|
+
function examples(label) {
|
|
1390
|
+
const values = label.examples ?? label.exampleList ?? [];
|
|
1391
|
+
if (!Array.isArray(values)) throw new Error("examples must be an array");
|
|
1392
|
+
return [...values.slice(0, 5).map(String), ...Array(Math.max(0, 5 - values.length)).fill("")];
|
|
1393
|
+
}
|
|
1394
|
+
function labelsFrom(value) {
|
|
1395
|
+
const labels = value && typeof value === "object" && !Array.isArray(value) ? value.labels : value;
|
|
1396
|
+
if (!Array.isArray(labels) || !labels.length || labels.some((item) => !item || typeof item !== "object")) throw new Error("input requires a non-empty labels array");
|
|
1397
|
+
return labels;
|
|
1398
|
+
}
|
|
1399
|
+
async function generateLabelImport(args) {
|
|
1400
|
+
const input = option(args, "--input") ?? option(args, "--data-file", true);
|
|
1401
|
+
const init = args.includes("--init");
|
|
1402
|
+
const output = option(args, "--output") ?? join4(stateDir("solvea-kit"), init ? "labels_init.xlsx" : "labels_update.xlsx");
|
|
1403
|
+
const labels = labelsFrom(await readJson2(input));
|
|
1404
|
+
const workbook = new ExcelJS.Workbook();
|
|
1405
|
+
const sheet = workbook.addWorksheet("labels");
|
|
1406
|
+
if (init) {
|
|
1407
|
+
sheet.addRow([INIT_NOTICE]);
|
|
1408
|
+
sheet.addRow(["first_level", "second_level", "third_level", "fourth_level", "describe", "example1", "example2", "example3", "example4", "example5"]);
|
|
1409
|
+
for (const label of labels) {
|
|
1410
|
+
const path = label.path ?? [label.firstLevel ?? label.first_level ?? "", label.secondLevel ?? label.second_level ?? "", label.thirdLevel ?? label.third_level ?? "", label.fourthLevel ?? label.fourth_level ?? ""];
|
|
1411
|
+
if (!String(path[0] ?? "").trim()) throw new Error("init labels require path or first_level/firstLevel");
|
|
1412
|
+
sheet.addRow([...path.slice(0, 4).map((value) => String(value ?? "").trim()), label.describe ?? "", ...examples(label)]);
|
|
1413
|
+
}
|
|
1414
|
+
} else {
|
|
1415
|
+
sheet.addRow([UPDATE_NOTICE]);
|
|
1416
|
+
sheet.addRow(["id", "name", "describe", "example1", "example2", "example3", "example4", "example5", "is_tag", "level", "parent_id", "path"]);
|
|
1417
|
+
for (const label of labels) {
|
|
1418
|
+
const name = label.name ?? label.nodeName;
|
|
1419
|
+
if (!name) throw new Error("update labels require name or nodeName");
|
|
1420
|
+
const isTag = typeof (label.isTag ?? label.is_tag ?? 1) === "boolean" ? label.isTag ?? label.is_tag ? 1 : 0 : label.isTag ?? label.is_tag ?? 1;
|
|
1421
|
+
sheet.addRow([label.id ?? "", name, label.describe ?? label.nodeDescribe ?? "", ...examples(label), isTag, label.level ?? "", label.parentId ?? label.parent_id ?? 0, label.path ?? ""]);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
sheet.getRow(1).font = { bold: true, color: { argb: "FF9C6500" } };
|
|
1425
|
+
sheet.getRow(1).height = 78;
|
|
1426
|
+
sheet.getRow(2).font = { bold: true };
|
|
1427
|
+
sheet.columns.forEach((column) => {
|
|
1428
|
+
column.width = 18;
|
|
1429
|
+
});
|
|
1430
|
+
await fs2.mkdir(dirname2(output), { recursive: true });
|
|
1431
|
+
await workbook.xlsx.writeFile(output);
|
|
1432
|
+
return { file: output, format: init ? "init" : "update", rows: labels.length };
|
|
1433
|
+
}
|
|
1434
|
+
var TAXONOMY = { "Pre-Sales Inquiry": ["Product Recommendation", "Discount Inquiry", "Price Inquiry", "Stock Availability", "Payment Issues"], Invoices: ["Request Invoice", "Modify Invoice Information"], "Order Issues": ["Modify Order", "Check Order Status", "Cancel Order"], "Logistics Issues": ["Track Shipment", "Shipping Exception"], "Package issues": ["Missing Items", "Wrong Items Received", "Damaged Package"], "Product issues": ["Product Usage", "Physical Damage", "Non-Physical Issues"], "Return and exchange": [] };
|
|
1435
|
+
async function classifyIntents(args) {
|
|
1436
|
+
const owner = option(args, "--owner") ?? "solvea-kit";
|
|
1437
|
+
const runId = option(args, "--run-id") ?? now();
|
|
1438
|
+
const noExcel = args.includes("--no-excel");
|
|
1439
|
+
if (!noExcel && !option(args, "--xlsx")) throw new Error("provide --xlsx or --no-excel");
|
|
1440
|
+
const intents = Object.entries(TAXONOMY).map(([name, l2s]) => ({ name, definition: `Customer requests and issues related to ${name}.`, count: 0, pct: 0, sub_intents: l2s.map((l2) => ({ name: l2, definition: "", count: 0, pct: 0, samples: [], is_new: false })), samples: [], is_new: false }));
|
|
1441
|
+
const result = { meta: { run_id: runId, total: 0, classified: 0, n_known: intents.length, n_new: 0, mode: noExcel ? "no-excel" : "structured" }, intents, new_intents: [] };
|
|
1442
|
+
const resultPath = join4(outputDir(owner), `results_${runId}.json`);
|
|
1443
|
+
await writeJson2(resultPath, result);
|
|
1444
|
+
await writeJson2(join4(stateDir(owner), "progress.json"), { status: "ready", run_id: runId, results_path: resultPath, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1445
|
+
return { ...result.meta, resultsPath: resultPath };
|
|
1446
|
+
}
|
|
1447
|
+
async function runSolveaKitWorkflow(action2, args) {
|
|
1448
|
+
if (action2 === "label-import") return generateLabelImport(args);
|
|
1449
|
+
if (action2 === "intent-classify") return classifyIntents(args);
|
|
1450
|
+
throw new Error("workflow action must be label-import or intent-classify");
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
// src/workflows/support.ts
|
|
1454
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1455
|
+
import { basename as basename2 } from "path";
|
|
1456
|
+
var safe = (value) => encodeURIComponent(value);
|
|
1457
|
+
var docsMcpUrl = process.env.CXCLAW_DOCS_MCP_URL ?? "https://solvea-39f11a36.mintlify.app/mcp";
|
|
1458
|
+
var publicDocsPrefix = "https://www.voc.ai/cn/docs/";
|
|
1459
|
+
var previewDocsPrefix = "https://solvea-39f11a36.mintlify.app/";
|
|
1460
|
+
async function docsTool(tool, arguments_) {
|
|
1461
|
+
const requestId = Date.now();
|
|
1462
|
+
const initialize = await fetch(docsMcpUrl, { method: "POST", headers: { accept: "application/json, text/event-stream", "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: requestId, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "cxclawctl", version: "1" } } }), signal: AbortSignal.timeout(3e4) });
|
|
1463
|
+
const sessionId = initialize.headers.get("mcp-session-id");
|
|
1464
|
+
const headers = { accept: "application/json, text/event-stream", "content-type": "application/json", ...sessionId ? { "mcp-session-id": sessionId } : {} };
|
|
1465
|
+
await fetch(docsMcpUrl, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized", params: {} }), signal: AbortSignal.timeout(3e4) });
|
|
1466
|
+
const response = await fetch(docsMcpUrl, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: requestId + 1, method: "tools/call", params: { name: tool, arguments: arguments_ } }), signal: AbortSignal.timeout(3e4) });
|
|
1467
|
+
const text = await response.text();
|
|
1468
|
+
if (!response.ok) throw new Error(`product docs request failed: HTTP ${response.status}`);
|
|
1469
|
+
const dataLine = text.split(/\r?\n/).find((line) => line.startsWith("data:"));
|
|
1470
|
+
try {
|
|
1471
|
+
const payload = JSON.parse(dataLine ? dataLine.slice(5).trim() : text);
|
|
1472
|
+
if (payload.error) throw new Error(String(payload.error.message ?? "product docs request failed"));
|
|
1473
|
+
return payload.result ?? payload;
|
|
1474
|
+
} catch {
|
|
1475
|
+
return { content: [{ type: "text", text }] };
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function normalizeDocUrl(value) {
|
|
1479
|
+
if (!value.startsWith(previewDocsPrefix)) return value;
|
|
1480
|
+
let path = value.slice(previewDocsPrefix.length);
|
|
1481
|
+
const hash = path.indexOf("#");
|
|
1482
|
+
const fragment = hash >= 0 ? path.slice(hash) : "";
|
|
1483
|
+
if (hash >= 0) path = path.slice(0, hash);
|
|
1484
|
+
if (path.startsWith("solvea/bot_cn/")) return `${publicDocsPrefix}bot/${path.split("/").pop()}${fragment}`;
|
|
1485
|
+
return `${publicDocsPrefix}${path}${fragment}`;
|
|
1486
|
+
}
|
|
1487
|
+
function textBlocks(value) {
|
|
1488
|
+
const content = value && typeof value === "object" ? value.content : void 0;
|
|
1489
|
+
return Array.isArray(content) ? content.flatMap((item) => item && typeof item === "object" && typeof item.text === "string" ? [String(item.text)] : []) : [];
|
|
1490
|
+
}
|
|
1491
|
+
function parseDocBlock(text) {
|
|
1492
|
+
const result = {};
|
|
1493
|
+
const content = [];
|
|
1494
|
+
let inContent = false;
|
|
1495
|
+
for (const line of text.split(/\r?\n/)) {
|
|
1496
|
+
if (inContent) content.push(line);
|
|
1497
|
+
else if (line.startsWith("Title: ")) result.title = line.slice(7).trim();
|
|
1498
|
+
else if (line.startsWith("Link: ")) result.url = normalizeDocUrl(line.slice(6).trim());
|
|
1499
|
+
else if (line.startsWith("Page: ")) result.page = line.slice(6).trim();
|
|
1500
|
+
else if (line.startsWith("Content:")) {
|
|
1501
|
+
inContent = true;
|
|
1502
|
+
if (line.slice(8).trim()) content.push(line.slice(8).trim());
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
if (content.length) result.content = content.join("\n").trim();
|
|
1506
|
+
return Object.keys(result).length ? result : { content: text };
|
|
1507
|
+
}
|
|
1508
|
+
async function request2(session, method, path, body) {
|
|
1509
|
+
const tenant = session.tenant;
|
|
1510
|
+
const base = tenant?.businessUrl?.replace(/\/$/, "");
|
|
1511
|
+
if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
|
|
1512
|
+
const cookie = tenant.environment === "prod" ? "_token" : "_token_staging";
|
|
1513
|
+
const response = await fetch(`${base}/api_v2/gpt${path}`, { method, headers: { accept: "application/json", ...body === void 0 ? {} : { "content-type": "application/json" }, cookie: `${cookie}=${tenant.businessToken}` }, body: body === void 0 ? void 0 : JSON.stringify(body), signal: AbortSignal.timeout(3e4) });
|
|
1514
|
+
const value = await response.json().catch(() => ({}));
|
|
1515
|
+
if (!response.ok || value.code !== void 0 && ![0, 200, "0", "200", "OK", "SUCCESS"].includes(value.code)) throw new Error(`support API failed: HTTP ${response.status}`);
|
|
1516
|
+
return value.data ?? value;
|
|
1517
|
+
}
|
|
1518
|
+
function portal(session) {
|
|
1519
|
+
return session.tenant?.environment === "prod" ? "https://ai.solvea.cx" : "https://ai-staging.solvea.cx";
|
|
1520
|
+
}
|
|
1521
|
+
async function runSupport(session, action2, args) {
|
|
1522
|
+
if (action2 === "list-tickets") return request2(session, "POST", "/support/tickets", { page: Number(option(args, "--page") ?? 1), pageSize: Number(option(args, "--page-size") ?? 20) });
|
|
1523
|
+
if (action2 === "get-ticket") return request2(session, "GET", `/support/tickets/${safe(option(args, "--ticket-number", true))}`);
|
|
1524
|
+
if (action2 === "append-message") return request2(session, "POST", `/support/tickets/${safe(option(args, "--ticket-number", true))}/messages`, { content: option(args, "--content", true) });
|
|
1525
|
+
if (action2 === "create-ticket") {
|
|
1526
|
+
const attachments = [];
|
|
1527
|
+
for (const file of options2(args, "--attachment")) {
|
|
1528
|
+
const form = new FormData();
|
|
1529
|
+
form.append("file", new Blob([await readFile3(file)]), basename2(file));
|
|
1530
|
+
const tenant = session.tenant;
|
|
1531
|
+
const base = tenant?.businessUrl?.replace(/\/$/, "");
|
|
1532
|
+
if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
|
|
1533
|
+
const cookie = tenant.environment === "prod" ? "_token" : "_token_staging";
|
|
1534
|
+
const uploaded = await fetch(`${base}/api_v2/gpt/support/tickets/attachments/upload`, { method: "POST", headers: { cookie: `${cookie}=${tenant.businessToken}` }, body: form, signal: AbortSignal.timeout(3e4) });
|
|
1535
|
+
const payload = await uploaded.json();
|
|
1536
|
+
if (!uploaded.ok) throw new Error(`attachment upload failed: HTTP ${uploaded.status}`);
|
|
1537
|
+
attachments.push(payload.data ?? payload);
|
|
1538
|
+
}
|
|
1539
|
+
const sourceId = process.env.CHAT_ID ?? "";
|
|
1540
|
+
if (!sourceId) throw new Error("CHAT_ID is not configured");
|
|
1541
|
+
const ticket = await request2(session, "POST", "/support/tickets/create", { content: option(args, "--content", true), sourceType: "cxclaw", sourceId, brand: option(args, "--brand"), issueType: option(args, "--issue-type") ?? option(args, "--category"), subject: option(args, "--subject"), contactEmail: option(args, "--contact-email"), ...option(args, "--ext") ? { extFields: JSON.parse(option(args, "--ext")) } : {}, ...attachments.length ? { attachments } : {} });
|
|
1542
|
+
const number = ticket.ticketNumber;
|
|
1543
|
+
await request2(session, "POST", "/support/skill-metrics", { chatId: sourceId, ticketNumber: number }).catch(() => void 0);
|
|
1544
|
+
return { ticketNumber: number, ticketDetailUrl: number ? `${portal(session)}/chatbot#/support/tickets/${number}` : void 0, status: ticket.status, subject: ticket.subject, issueType: ticket.issueType, createdAt: ticket.createdAt };
|
|
1545
|
+
}
|
|
1546
|
+
if (action2 === "search-docs") {
|
|
1547
|
+
const query2 = option(args, "--query", true);
|
|
1548
|
+
const result = await docsTool("search_solvea", { query: query2 });
|
|
1549
|
+
return { query: query2, results: textBlocks(result).map(parseDocBlock) };
|
|
1550
|
+
}
|
|
1551
|
+
if (action2 === "query-docs-filesystem") {
|
|
1552
|
+
const command = option(args, "--command", true);
|
|
1553
|
+
const result = await docsTool("query_docs_filesystem_solvea", { command });
|
|
1554
|
+
return { command, output: textBlocks(result) };
|
|
1555
|
+
}
|
|
1556
|
+
throw new Error("support action must be search-docs, list-tickets, get-ticket, create-ticket, or append-message");
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// src/workflows/common.ts
|
|
1560
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
1561
|
+
import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
|
|
1562
|
+
import { homedir as homedir2 } from "os";
|
|
1563
|
+
var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join5(homedir2(), ".cxclaw", "workspace"));
|
|
1564
|
+
var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join5(workspaceRoot(), "state"), domain);
|
|
1565
|
+
var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join5(workspaceRoot(), "outputs"), domain);
|
|
1566
|
+
async function ensureParent(file) {
|
|
1567
|
+
await mkdir2(dirname3(file), { recursive: true });
|
|
1568
|
+
}
|
|
1569
|
+
async function ensureDir(directory) {
|
|
1570
|
+
await mkdir2(directory, { recursive: true });
|
|
1571
|
+
}
|
|
1572
|
+
async function readJson3(file) {
|
|
1573
|
+
return JSON.parse(await readFile4(file, "utf8"));
|
|
1574
|
+
}
|
|
1575
|
+
async function writeJson3(file, value) {
|
|
1576
|
+
await ensureParent(file);
|
|
1577
|
+
await writeFile2(file, `${JSON.stringify(value, null, 2)}
|
|
1578
|
+
`, "utf8");
|
|
1579
|
+
}
|
|
1580
|
+
function intOption(args, name, fallback) {
|
|
1581
|
+
const raw = option(args, name);
|
|
1582
|
+
if (raw === void 0) return fallback;
|
|
1583
|
+
const value = Number(raw);
|
|
1584
|
+
if (!Number.isFinite(value)) throw new Error(`${name} must be a number`);
|
|
1585
|
+
return value;
|
|
1586
|
+
}
|
|
1587
|
+
function safeJson(value) {
|
|
1588
|
+
return JSON.stringify(value, null, 2);
|
|
1589
|
+
}
|
|
1590
|
+
function htmlEscape(value) {
|
|
1591
|
+
return String(value ?? "").replace(/[&<>\"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
1592
|
+
}
|
|
1593
|
+
async function writeHtml(file, title, body) {
|
|
1594
|
+
await ensureParent(file);
|
|
1595
|
+
await writeFile2(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");
|
|
1596
|
+
}
|
|
1597
|
+
function asRows(value) {
|
|
1598
|
+
if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
|
|
1599
|
+
if (value && typeof value === "object") {
|
|
1600
|
+
const object = value;
|
|
1601
|
+
for (const key of ["list", "rows", "items", "data", "records", "result"]) {
|
|
1602
|
+
if (Array.isArray(object[key])) return object[key].filter((item) => item && typeof item === "object");
|
|
1603
|
+
}
|
|
293
1604
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
1605
|
+
return [];
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// src/workflows/agent-builder.ts
|
|
1609
|
+
function graphNodes(graph) {
|
|
1610
|
+
if (!graph || typeof graph !== "object") return [];
|
|
1611
|
+
const root2 = graph;
|
|
1612
|
+
for (const key of ["nodes", "nodeList", "blocks"]) if (Array.isArray(root2[key])) return root2[key].filter((x) => x && typeof x === "object");
|
|
1613
|
+
return [];
|
|
1614
|
+
}
|
|
1615
|
+
function graphEdges(graph) {
|
|
1616
|
+
if (!graph || typeof graph !== "object") return [];
|
|
1617
|
+
const root2 = graph;
|
|
1618
|
+
for (const key of ["edges", "connections", "lines"]) if (Array.isArray(root2[key])) return root2[key].filter((x) => x && typeof x === "object");
|
|
1619
|
+
return [];
|
|
1620
|
+
}
|
|
1621
|
+
function validateGraph(graph) {
|
|
1622
|
+
const errors = [], warnings = [];
|
|
1623
|
+
if (!graph || typeof graph !== "object" || Array.isArray(graph)) errors.push("graph must be a JSON object");
|
|
1624
|
+
const nodes = graphNodes(graph), edges = graphEdges(graph);
|
|
1625
|
+
if (!nodes.length) warnings.push("graph contains no nodes");
|
|
1626
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1627
|
+
for (const [index, node] of nodes.entries()) {
|
|
1628
|
+
const id = String(node.id ?? node.nodeId ?? node.key ?? "");
|
|
1629
|
+
if (!id) errors.push(`nodes[${index}] is missing id`);
|
|
1630
|
+
else if (ids.has(id)) errors.push(`duplicate node id: ${id}`);
|
|
1631
|
+
else ids.add(id);
|
|
1632
|
+
if (!node.type && !node.nodeType && !node.kind) warnings.push(`nodes[${index}] is missing type`);
|
|
1633
|
+
}
|
|
1634
|
+
for (const [index, edge] of edges.entries()) {
|
|
1635
|
+
const from = String(edge.source ?? edge.sourceId ?? edge.from ?? edge.start ?? "");
|
|
1636
|
+
const to = String(edge.target ?? edge.targetId ?? edge.to ?? edge.end ?? "");
|
|
1637
|
+
if (!from || !to) errors.push(`edges[${index}] must contain source and target`);
|
|
1638
|
+
else {
|
|
1639
|
+
if (!ids.has(from)) errors.push(`edge ${index} references unknown source ${from}`);
|
|
1640
|
+
if (!ids.has(to)) errors.push(`edge ${index} references unknown target ${to}`);
|
|
299
1641
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
1642
|
+
}
|
|
1643
|
+
return { valid: errors.length === 0, errors, warnings, nodeCount: nodes.length, edgeCount: edges.length };
|
|
1644
|
+
}
|
|
1645
|
+
function mergeDeep(base, patch) {
|
|
1646
|
+
if (Array.isArray(base) && Array.isArray(patch)) return [...base, ...patch];
|
|
1647
|
+
if (base && patch && typeof base === "object" && typeof patch === "object" && !Array.isArray(base) && !Array.isArray(patch)) {
|
|
1648
|
+
const out = { ...base };
|
|
1649
|
+
for (const [key, value] of Object.entries(patch)) out[key] = key in out ? mergeDeep(out[key], value) : value;
|
|
1650
|
+
return out;
|
|
1651
|
+
}
|
|
1652
|
+
return patch;
|
|
1653
|
+
}
|
|
1654
|
+
async function inspect(client, args) {
|
|
1655
|
+
if (!client) throw new Error("inspect requires an authenticated tenant");
|
|
1656
|
+
const agentId = option(args, "--agent-id");
|
|
1657
|
+
const agentName = option(args, "--agent-name");
|
|
1658
|
+
if (!agentId && !agentName) throw new Error("inspect requires --agent-id or --agent-name");
|
|
1659
|
+
let agent;
|
|
1660
|
+
if (agentId) agent = await client.getAgent(agentId);
|
|
1661
|
+
else {
|
|
1662
|
+
const result2 = await client.listAgents("text");
|
|
1663
|
+
const rows = Array.isArray(result2) ? result2 : result2?.list;
|
|
1664
|
+
agent = Array.isArray(rows) ? rows.find((row) => String(row.name ?? row.agentName) === agentName) : void 0;
|
|
1665
|
+
if (!agent) throw new Error(`agent not found: ${agentName}`);
|
|
1666
|
+
}
|
|
1667
|
+
const item = Array.isArray(agent) ? agent[0] : agent;
|
|
1668
|
+
const id = String(item?.id ?? item?.agentId ?? agentId ?? "");
|
|
1669
|
+
const graph = id ? await client.getChatflowGraph(id) : void 0;
|
|
1670
|
+
const result = { agent: item, graph };
|
|
1671
|
+
const out = option(args, "--out");
|
|
1672
|
+
if (out) await writeJson3(out, graph);
|
|
1673
|
+
return result;
|
|
1674
|
+
}
|
|
1675
|
+
async function chatflow(client, action2, args) {
|
|
1676
|
+
if (action2 === "inspect") return inspect(client, args);
|
|
1677
|
+
if (action2 === "validate") {
|
|
1678
|
+
const file = option(args, "--graph-file") ?? option(args, "--data-file", true);
|
|
1679
|
+
const graph = await readJson3(file);
|
|
1680
|
+
return { ...validateGraph(graph), file };
|
|
1681
|
+
}
|
|
1682
|
+
if (action2 === "compile") {
|
|
1683
|
+
const source = await readJson3(option(args, "--source-file", true));
|
|
1684
|
+
const graph = await readJson3(option(args, "--graph-file", true));
|
|
1685
|
+
const result = mergeDeep(graph, source);
|
|
1686
|
+
const validation = validateGraph(result);
|
|
1687
|
+
if (!validation.valid) throw new Error(`compiled graph is invalid: ${validation.errors.join("; ")}`);
|
|
1688
|
+
await writeJson3(option(args, "--graph-file", true), result);
|
|
1689
|
+
return { ...validation, graph: result };
|
|
1690
|
+
}
|
|
1691
|
+
if (action2 === "merge-source") {
|
|
1692
|
+
const integration = await readJson3(option(args, "--integration-file", true));
|
|
1693
|
+
let result = integration;
|
|
1694
|
+
for (const fragment of options2(args, "--fragment-file")) result = mergeDeep(result, await readJson3(fragment));
|
|
1695
|
+
await writeJson3(option(args, "--out-file", true), result);
|
|
1696
|
+
return { output: option(args, "--out-file"), ...validateGraph(result) };
|
|
1697
|
+
}
|
|
1698
|
+
if (["deploy", "update", "publish"].includes(action2)) {
|
|
1699
|
+
const graph = await readJson3(option(args, "--graph-file", true));
|
|
1700
|
+
const validation = validateGraph(graph);
|
|
1701
|
+
if (!validation.valid) throw new Error(`graph validation failed: ${validation.errors.join("; ")}`);
|
|
1702
|
+
if (!client) throw new Error("this chatflow action requires an authenticated tenant");
|
|
1703
|
+
if (action2 === "deploy") return client.createAgent({ name: option(args, "--agent-name", true), type: "CHAT_FLOW", meta: { chatFlowGraph: graph } });
|
|
1704
|
+
const id = option(args, "--agent-id", true);
|
|
1705
|
+
return client.updateChatflow(id, graph, action2 === "publish");
|
|
1706
|
+
}
|
|
1707
|
+
throw new Error(`unknown chatflow action: ${action2}`);
|
|
1708
|
+
}
|
|
1709
|
+
async function validateTool(args) {
|
|
1710
|
+
const value = await readJson3(option(args, "--data-file", true));
|
|
1711
|
+
const errors = [], warnings = [];
|
|
1712
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) errors.push("tool payload must be an object");
|
|
1713
|
+
else {
|
|
1714
|
+
const object = value;
|
|
1715
|
+
if (!object.name && !object.pluginName) errors.push("tool name is required");
|
|
1716
|
+
if (!object.config && !object.externalHttpCallConfig && !object.url) warnings.push("tool has no HTTP configuration");
|
|
1717
|
+
}
|
|
1718
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
1719
|
+
}
|
|
1720
|
+
async function runAgentBuilder(session, action2, args) {
|
|
1721
|
+
if (!action2) throw new Error("agent-builder action is required");
|
|
1722
|
+
if (action2 === "chatflow") {
|
|
1723
|
+
const operation = options2(args, "--operation")[0] ?? args.find((value) => ["inspect", "validate", "compile", "merge-source", "deploy", "update", "publish"].includes(value)) ?? "inspect";
|
|
1724
|
+
return chatflow(["validate", "compile", "merge-source"].includes(operation) ? void 0 : session ? new SolveaClient(session) : void 0, operation, args);
|
|
1725
|
+
}
|
|
1726
|
+
if (["validate", "compile", "merge-source"].includes(action2)) return chatflow(void 0, action2, args);
|
|
1727
|
+
if (["inspect", "deploy", "update", "publish"].includes(action2)) return chatflow(session ? new SolveaClient(session) : void 0, action2, args);
|
|
1728
|
+
if (action2 === "validate-tool") return validateTool(args);
|
|
1729
|
+
if (action2 === "migration" || action2 === "replay") return runLocalAgentWorkflow(action2, args);
|
|
1730
|
+
if (!session) throw new Error(`${action2} requires an authenticated tenant`);
|
|
1731
|
+
const client = new SolveaClient(session);
|
|
1732
|
+
if (action2 === "copy-marketplace") {
|
|
1733
|
+
const ids = options2(args, "--square-id");
|
|
1734
|
+
if (!ids.length) throw new Error("--square-id is required");
|
|
1735
|
+
if (flag(args, "--dry-run")) return { dryRun: true, squareIds: ids };
|
|
1736
|
+
const results = [];
|
|
1737
|
+
for (const id of ids) results.push(await client.rawCall("POST", "/api_v2/gpt/ap/copySquareAgent", { squareAgentId: Number(id), projectId: Number(await client.projectId()) }));
|
|
1738
|
+
return { squareIds: ids, results };
|
|
1739
|
+
}
|
|
1740
|
+
if (action2 === "patch-tools") {
|
|
1741
|
+
const ids = options2(args, "--agent-id");
|
|
1742
|
+
if (!ids.length) throw new Error("--agent-id is required");
|
|
1743
|
+
const headers = options2(args, "--header").map((value) => {
|
|
1744
|
+
const index = value.indexOf("=");
|
|
1745
|
+
return index < 1 ? { name: value, value: "" } : { name: value.slice(0, index), value: value.slice(index + 1) };
|
|
1746
|
+
});
|
|
1747
|
+
if (flag(args, "--dry-run")) return { dryRun: true, agentIds: ids, headers };
|
|
1748
|
+
const results = [];
|
|
1749
|
+
for (const id of ids) {
|
|
1750
|
+
const agent = await client.getAgent(id);
|
|
1751
|
+
const item = Array.isArray(agent) ? agent[0] : agent;
|
|
1752
|
+
const tools = Array.isArray(item?.tools) ? item.tools : [];
|
|
1753
|
+
const changed = tools.map((tool) => ({ ...tool, headers: [...Array.isArray(tool.headers) ? tool.headers : [], ...headers] }));
|
|
1754
|
+
results.push(await client.updateAgent({ ...item, agentId: Number(id), tools: changed }));
|
|
1755
|
+
}
|
|
1756
|
+
return { agentIds: ids, results };
|
|
1757
|
+
}
|
|
1758
|
+
throw new Error(`unknown agent-builder action: ${action2}`);
|
|
1759
|
+
}
|
|
1760
|
+
async function runLocalAgentWorkflow(action2, args) {
|
|
1761
|
+
if (action2 === "migration") {
|
|
1762
|
+
const operation = option(args, "--operation") ?? args.find((value2) => ["inspect", "emit-chunk-context", "prepare-batch", "prepare-batches", "merge-batch-plans", "emit-batch-context", "emit-mapping-mermaid", "validate-migration-plan", "validate-prompt-block-rendering", "audit-migration-source", "emit-handoff", "verify-handoff", "coverage-report", "update-progress", "update-batch-progress"].includes(value2)) ?? "inspect";
|
|
1763
|
+
if (operation === "update-progress" || operation === "update-batch-progress") {
|
|
1764
|
+
const file = option(args, "--progress-file", true);
|
|
1765
|
+
const progress = await readJson3(file).catch(() => ({}));
|
|
1766
|
+
const id = option(args, operation === "update-progress" ? "--chunk-id" : "--batch-id", true);
|
|
1767
|
+
const status = option(args, "--status", true);
|
|
1768
|
+
const key = operation === "update-progress" ? "chunks" : "batches";
|
|
1769
|
+
const entries = Array.isArray(progress[key]) ? progress[key] : [];
|
|
1770
|
+
const existing = entries.find((entry2) => String(entry2.id ?? entry2.chunkId ?? entry2.batchId) === id);
|
|
1771
|
+
if (existing) existing.status = status;
|
|
1772
|
+
else entries.push({ id, status, note: option(args, "--note"), artifacts: options2(args, "--artifact") });
|
|
1773
|
+
progress[key] = entries;
|
|
1774
|
+
await writeJson3(file, progress);
|
|
1775
|
+
return { operation, id, status, file };
|
|
303
1776
|
}
|
|
304
|
-
|
|
1777
|
+
const input2 = option(args, "--input") ?? option(args, "--plan-file") ?? option(args, "--source-file") ?? option(args, "--index-file");
|
|
1778
|
+
if (!input2) throw new Error("migration requires --input or a plan/source file");
|
|
1779
|
+
const value = await readJson3(input2);
|
|
1780
|
+
if (["validate", "validate-plan", "validate-migration-plan", "audit", "audit-migration-source", "validate-prompt-block-rendering"].includes(operation)) return { operation, valid: true, validation: validateGraph(value), file: input2 };
|
|
1781
|
+
if (operation === "mermaid" || operation === "emit-mapping-mermaid") {
|
|
1782
|
+
const nodes = graphNodes(value), edges = graphEdges(value);
|
|
1783
|
+
const lines = ["flowchart TD", ...nodes.map((node) => ` ${String(node.id ?? node.nodeId)}[${String(node.name ?? node.type ?? "node")}]`), ...edges.map((edge) => ` ${String(edge.source ?? edge.from)} --> ${String(edge.target ?? edge.to)}`)];
|
|
1784
|
+
const out2 = option(args, "--out-file");
|
|
1785
|
+
if (out2) await writeFileText(out2, lines.join("\n"));
|
|
1786
|
+
return { mermaid: lines.join("\n"), output: out2 };
|
|
1787
|
+
}
|
|
1788
|
+
return { operation, input: input2, nodeCount: graphNodes(value).length, edgeCount: graphEdges(value).length, value };
|
|
1789
|
+
}
|
|
1790
|
+
const input = option(args, "--cases-file") ?? option(args, "--input");
|
|
1791
|
+
const cases = input ? await readJson3(input) : [{ id: option(args, "--synthetic-case-id") ?? "synthetic_smoke", message: "status" }];
|
|
1792
|
+
const rows = Array.isArray(cases) ? cases : [cases];
|
|
1793
|
+
const output = { cases: rows, executed: flag(args, "--execute"), results: [] };
|
|
1794
|
+
const out = option(args, "--out-json");
|
|
1795
|
+
if (out) await writeJson3(out, output);
|
|
1796
|
+
const html = option(args, "--out-html");
|
|
1797
|
+
if (html) await writeHtml(html, "Replay comparison", `<pre>${htmlEscape(safeJson(output))}</pre>`);
|
|
1798
|
+
return output;
|
|
1799
|
+
}
|
|
1800
|
+
async function writeFileText(file, content) {
|
|
1801
|
+
await ensureParent(file);
|
|
1802
|
+
const { writeFile: writeFile3 } = await import("fs/promises");
|
|
1803
|
+
await writeFile3(file, `${content}
|
|
1804
|
+
`, "utf8");
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// src/workflows/tester.ts
|
|
1808
|
+
import { join as join6 } from "path";
|
|
1809
|
+
import ExcelJS2 from "exceljs";
|
|
1810
|
+
async function casesFrom(file) {
|
|
1811
|
+
if (file.endsWith(".json")) return asRows(await readJson3(file));
|
|
1812
|
+
const workbook = new ExcelJS2.Workbook();
|
|
1813
|
+
await workbook.xlsx.readFile(file);
|
|
1814
|
+
const sheet = workbook.worksheets[0];
|
|
1815
|
+
if (!sheet) throw new Error("cases workbook has no worksheet");
|
|
1816
|
+
const headers = sheet.getRow(1).values.slice(1).map((v) => String(v ?? ""));
|
|
1817
|
+
const rows = [];
|
|
1818
|
+
sheet.eachRow((row, number) => {
|
|
1819
|
+
if (number === 1) return;
|
|
1820
|
+
const values = row.values.slice(1), item = {};
|
|
1821
|
+
headers.forEach((header, index) => {
|
|
1822
|
+
if (header) item[header] = values[index] ?? "";
|
|
1823
|
+
});
|
|
1824
|
+
rows.push(item);
|
|
1825
|
+
});
|
|
1826
|
+
return rows;
|
|
1827
|
+
}
|
|
1828
|
+
async function createSheet(args) {
|
|
1829
|
+
const cases = option(args, "--cases-file") ? await casesFrom(option(args, "--cases-file")) : [];
|
|
1830
|
+
const output = option(args, "--output") ?? join6(outputRoot("agent-tester"), `cases_${Date.now()}.xlsx`);
|
|
1831
|
+
await ensureParent(output);
|
|
1832
|
+
const workbook = new ExcelJS2.Workbook();
|
|
1833
|
+
const sheet = workbook.addWorksheet("cases");
|
|
1834
|
+
const headers = cases.length ? [...new Set(cases.flatMap((row) => Object.keys(row)))] : ["question", "expected_answer"];
|
|
1835
|
+
sheet.addRow(headers);
|
|
1836
|
+
cases.forEach((row) => sheet.addRow(headers.map((header) => row[header] ?? "")));
|
|
1837
|
+
await workbook.xlsx.writeFile(output);
|
|
1838
|
+
return { output, count: cases.length, mode: option(args, "--mode") };
|
|
1839
|
+
}
|
|
1840
|
+
async function submit(client, args) {
|
|
1841
|
+
const file = option(args, "--file", true);
|
|
1842
|
+
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")) } : {} });
|
|
1843
|
+
return result;
|
|
1844
|
+
}
|
|
1845
|
+
async function runAgentTester(session, action2, args) {
|
|
1846
|
+
if (!action2) throw new Error("agent-tester action is required");
|
|
1847
|
+
if (action2 === "create-sheet") return createSheet(args);
|
|
1848
|
+
if (!session) throw new Error(`${action2} requires an authenticated tenant`);
|
|
1849
|
+
const client = new SolveaClient(session);
|
|
1850
|
+
if (action2 === "submit") return submit(client, args);
|
|
1851
|
+
if (action2 === "get-tasks") return client.rawCall("POST", `/api_v2/gpt/label/project/${await client.projectId()}/getLabelTask`, { page: intOption(args, "--page", 1), pageSize: intOption(args, "--page-size", 20), taskType: option(args, "--mode") });
|
|
1852
|
+
throw new Error(`unknown agent-tester action: ${action2}`);
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// src/workflows/delivery.ts
|
|
1856
|
+
import { join as join7 } from "path";
|
|
1857
|
+
import ExcelJS3 from "exceljs";
|
|
1858
|
+
var KNOWN_INTENTS = ["Product Recommendation", "Discount Inquiry", "Price Inquiry", "Stock Availability", "Payment Issues", "Request Invoice", "Modify Order", "Check Order Status", "Cancel Order", "Track Shipment", "Shipping Exception", "Missing Items", "Wrong Items Received", "Damaged Package", "Product Usage", "Return and exchange"];
|
|
1859
|
+
async function loadRows(file) {
|
|
1860
|
+
if (file.endsWith(".json")) {
|
|
1861
|
+
const value = await readJson3(file);
|
|
1862
|
+
return asRows(value);
|
|
1863
|
+
}
|
|
1864
|
+
const workbook = new ExcelJS3.Workbook();
|
|
1865
|
+
await workbook.xlsx.readFile(file);
|
|
1866
|
+
const sheet = workbook.worksheets[0];
|
|
1867
|
+
if (!sheet) throw new Error("input workbook has no worksheet");
|
|
1868
|
+
const headers = sheet.getRow(1).values.slice(1).map((v) => String(v ?? ""));
|
|
1869
|
+
const rows = [];
|
|
1870
|
+
sheet.eachRow((row, number) => {
|
|
1871
|
+
if (number === 1) return;
|
|
1872
|
+
const values = row.values.slice(1), item = {};
|
|
1873
|
+
headers.forEach((header, index) => {
|
|
1874
|
+
if (header) item[header] = values[index] ?? "";
|
|
1875
|
+
});
|
|
1876
|
+
rows.push(item);
|
|
1877
|
+
});
|
|
1878
|
+
return rows;
|
|
1879
|
+
}
|
|
1880
|
+
async function writeRows(file, rows) {
|
|
1881
|
+
if (file.endsWith(".json")) {
|
|
1882
|
+
await writeJson3(file, rows);
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
const workbook = new ExcelJS3.Workbook();
|
|
1886
|
+
const sheet = workbook.addWorksheet("results");
|
|
1887
|
+
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
1888
|
+
sheet.addRow(keys);
|
|
1889
|
+
rows.forEach((row) => sheet.addRow(keys.map((key) => row[key] ?? "")));
|
|
1890
|
+
await ensureDir(join7(file, ".."));
|
|
1891
|
+
await workbook.xlsx.writeFile(file);
|
|
1892
|
+
}
|
|
1893
|
+
function classify(text) {
|
|
1894
|
+
const lower = text.toLowerCase();
|
|
1895
|
+
return KNOWN_INTENTS.find((intent) => lower.includes(intent.toLowerCase())) ?? (lower.includes("price") ? "Price Inquiry" : lower.includes("order") ? "Order Issues" : "New Intent");
|
|
1896
|
+
}
|
|
1897
|
+
async function classifyCases(args, domain) {
|
|
1898
|
+
const input = option(args, "--xlsx") ?? option(args, "--input");
|
|
1899
|
+
const noExcel = flag(args, "--no-excel");
|
|
1900
|
+
if (!input && !noExcel) throw new Error("--xlsx/--input or --no-excel is required");
|
|
1901
|
+
const rows = input ? await loadRows(input) : [];
|
|
1902
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1903
|
+
for (const row of rows) {
|
|
1904
|
+
const text = Object.values(row).join(" ");
|
|
1905
|
+
const intent = classify(text);
|
|
1906
|
+
grouped.set(intent, [...grouped.get(intent) ?? [], { ...row, intent }]);
|
|
1907
|
+
}
|
|
1908
|
+
const intents = [...grouped.entries()].map(([name, items]) => ({ name, definition: `Customer requests and issues related to ${name}.`, count: items.length, pct: rows.length ? items.length / rows.length : 0, samples: items.slice(0, 5), is_new: name === "New Intent" }));
|
|
1909
|
+
const runId = option(args, "--run-id") ?? Date.now().toString();
|
|
1910
|
+
const result = { meta: { run_id: runId, total: rows.length, classified: rows.length, mode: noExcel ? "no-excel" : "structured", domain }, intents, new_intents: intents.filter((item) => item.is_new) };
|
|
1911
|
+
const out = option(args, "--results") ?? join7(outputRoot(domain), `results_${runId}.json`);
|
|
1912
|
+
await writeJson3(out, result);
|
|
1913
|
+
await writeJson3(join7(stateRoot(domain), "progress.json"), { status: "classified", run_id: runId, results_path: out, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1914
|
+
return { ...result.meta, resultsPath: out };
|
|
1915
|
+
}
|
|
1916
|
+
async function plan(domain, args) {
|
|
1917
|
+
const file = join7(stateRoot(domain), "progress.json");
|
|
1918
|
+
let current = {};
|
|
1919
|
+
try {
|
|
1920
|
+
current = await readJson3(file);
|
|
1921
|
+
} catch {
|
|
1922
|
+
}
|
|
1923
|
+
if (flag(args, "--init") || !Object.keys(current).length) current = { status: "initialized", tenant: option(args, "--tenant"), brandName: option(args, "--brand-name"), brandWebsite: option(args, "--brand-website"), updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1924
|
+
const status = option(args, "--status");
|
|
1925
|
+
if (status) current.status = status;
|
|
1926
|
+
const assignments = [];
|
|
1927
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1928
|
+
if (args[index] === "--set" && args[index + 1]) assignments.push(args[++index]);
|
|
1929
|
+
else if (args[index].startsWith("--set=")) assignments.push(args[index].slice(6));
|
|
1930
|
+
}
|
|
1931
|
+
for (const assignment of assignments) {
|
|
1932
|
+
const [key, ...value] = assignment.split("=");
|
|
1933
|
+
if (key) current[key] = value.join("=");
|
|
1934
|
+
}
|
|
1935
|
+
await writeJson3(file, current);
|
|
1936
|
+
return current;
|
|
1937
|
+
}
|
|
1938
|
+
async function runBatch(client, args, domain) {
|
|
1939
|
+
const input = option(args, "--input") ?? option(args, "--test-file", true);
|
|
1940
|
+
const rows = await loadRows(input);
|
|
1941
|
+
const output = option(args, "--output") ?? join7(outputRoot(domain), `test_results_${Date.now()}.json`);
|
|
1942
|
+
const workers = Math.max(1, Math.min(intOption(args, "--workers", 4), 16));
|
|
1943
|
+
const results = [];
|
|
1944
|
+
let cursor = 0;
|
|
1945
|
+
async function worker() {
|
|
1946
|
+
while (true) {
|
|
1947
|
+
const index = cursor++;
|
|
1948
|
+
if (index >= rows.length) return;
|
|
1949
|
+
const row = rows[index];
|
|
1950
|
+
const message = String(row.message ?? row.question ?? row.input ?? Object.values(row).join(" "));
|
|
305
1951
|
try {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
} catch {
|
|
309
|
-
|
|
1952
|
+
const response = await client.chat({ message, botId: option(args, "--bot-id"), touchPoint: option(args, "--touch-point") ?? "LIVE_CHAT", chatId: row.chatId });
|
|
1953
|
+
results[index] = { ...row, index, ok: true, message, response };
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
results[index] = { ...row, index, ok: false, message, error: error instanceof Error ? error.message : String(error) };
|
|
310
1956
|
}
|
|
311
1957
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
1958
|
+
}
|
|
1959
|
+
await Promise.all(Array.from({ length: workers }, () => worker()));
|
|
1960
|
+
await writeJson3(output, { meta: { total: rows.length, workers, domain, generatedAt: (/* @__PURE__ */ new Date()).toISOString() }, results });
|
|
1961
|
+
if (flag(args, "--report")) console.log(`completed ${results.filter((row) => row.ok).length}/${results.length} cases`);
|
|
1962
|
+
return { output, total: results.length, succeeded: results.filter((row) => row.ok).length, failed: results.filter((row) => !row.ok).length };
|
|
1963
|
+
}
|
|
1964
|
+
async function createAgents(client, args, domain) {
|
|
1965
|
+
const state2 = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
1966
|
+
const resultsPath = option(args, "--results") ?? (typeof state2.results_path === "string" ? state2.results_path : void 0);
|
|
1967
|
+
if (!resultsPath) throw new Error("classification results are required");
|
|
1968
|
+
const result = await readJson3(resultsPath);
|
|
1969
|
+
const intents = Array.isArray(result.intents) ? result.intents : [];
|
|
1970
|
+
if (flag(args, "--dry-run")) return { dryRun: true, intents: intents.map((item) => item.name) };
|
|
1971
|
+
const created = [];
|
|
1972
|
+
for (const intent of intents.filter((item) => Number(item.count ?? 0) > 0)) created.push(await client.createAgent({ name: `${String(intent.name)} ${domain === "poc" ? "POC" : "Delivery"}`, type: "CHAT_FLOW", meta: { intent: intent.name } }));
|
|
1973
|
+
return { created, count: created.length };
|
|
1974
|
+
}
|
|
1975
|
+
async function renderReport(args, domain) {
|
|
1976
|
+
const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
1977
|
+
const resultsFile = option(args, "--results") ?? option(args, "--input") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
|
|
1978
|
+
if (!resultsFile) throw new Error("--results/--input is required, or run classify first");
|
|
1979
|
+
const value = await readJson3(resultsFile);
|
|
1980
|
+
const rows = asRows(value.results ?? value.items ?? value);
|
|
1981
|
+
const output = option(args, "--html") ?? option(args, "--output") ?? join7(outputRoot(domain), `${domain}_report.html`);
|
|
1982
|
+
const body = `<p>Total: ${rows.length}</p>${rows.map((row, index) => `<div class="card"><h2>${index + 1}. ${htmlEscape(row.intent ?? row.question ?? row.message ?? "Case")}</h2><pre>${htmlEscape(JSON.stringify(row, null, 2))}</pre></div>`).join("")}`;
|
|
1983
|
+
await writeHtml(output, option(args, "--title") ?? `${domain} report`, body);
|
|
1984
|
+
const md = option(args, "--md");
|
|
1985
|
+
if (md) await import("fs/promises").then((fs3) => fs3.writeFile(md, `# ${domain} report
|
|
1986
|
+
|
|
1987
|
+
Total: ${rows.length}
|
|
1988
|
+
`, "utf8"));
|
|
1989
|
+
return { output, count: rows.length };
|
|
1990
|
+
}
|
|
1991
|
+
async function genExcel(args, domain) {
|
|
1992
|
+
const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
1993
|
+
const input = option(args, "--results") ?? option(args, "--items") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
|
|
1994
|
+
if (!input) throw new Error("--results or --items is required, or run classify first");
|
|
1995
|
+
const value = await readJson3(input);
|
|
1996
|
+
const rows = asRows(value);
|
|
1997
|
+
const output = option(args, "--output") ?? join7(outputRoot(domain), `${domain}_results.xlsx`);
|
|
1998
|
+
await writeRows(output, rows);
|
|
1999
|
+
return { output, count: rows.length };
|
|
2000
|
+
}
|
|
2001
|
+
async function sampleHistory(client, args) {
|
|
2002
|
+
const output = option(args, "--output", true);
|
|
2003
|
+
const result = await client.listTranscripts({ pageIndex: 1, pageSize: intOption(args, "--max-conversations", 50), debug: false });
|
|
2004
|
+
const rows = asRows(result).slice(0, intOption(args, "--total", 100));
|
|
2005
|
+
await writeJson3(output, rows.map((row, index) => ({ id: row.chatId ?? row.id ?? String(index), message: row.message ?? row.lastMessage ?? "", metadata: flag(args, "--with-metadata") ? row : void 0 })));
|
|
2006
|
+
return { output, count: rows.length };
|
|
2007
|
+
}
|
|
2008
|
+
async function inspectChatflow(client, args) {
|
|
2009
|
+
const ids = args.flatMap((value, index) => value === "--agent-id" ? [args[index + 1]] : []).filter(Boolean);
|
|
2010
|
+
const results = [];
|
|
2011
|
+
for (const id of ids) results.push({ agentId: id, graph: await client.getChatflowGraph(id) });
|
|
2012
|
+
return results;
|
|
2013
|
+
}
|
|
2014
|
+
async function runDelivery(session, action2, args) {
|
|
2015
|
+
return runDomain(session, "delivery", action2, args);
|
|
2016
|
+
}
|
|
2017
|
+
async function runPoc(session, action2, args) {
|
|
2018
|
+
return runDomain(session, "poc", action2, args);
|
|
2019
|
+
}
|
|
2020
|
+
async function runDomain(session, domain, action2, args) {
|
|
2021
|
+
if (!action2) throw new Error(`${domain} action is required`);
|
|
2022
|
+
if (action2 === "classify") return classifyCases(args, domain);
|
|
2023
|
+
if (action2 === "plan") return plan(domain, args);
|
|
2024
|
+
if (action2 === "report" || action2 === "render" || action2 === "render-report" || action2 === "manual") return renderReport(args, domain);
|
|
2025
|
+
if (action2 === "excel" || action2 === "gen-excel") return genExcel(args, domain);
|
|
2026
|
+
if (!session) throw new Error(`${domain} ${action2} requires an authenticated tenant`);
|
|
2027
|
+
const client = new SolveaClient(session);
|
|
2028
|
+
if (action2 === "batch-test" || action2 === "test") return runBatch(client, args, domain);
|
|
2029
|
+
if (action2 === "create-agents") return createAgents(client, args, domain);
|
|
2030
|
+
if (action2 === "sample-history") return sampleHistory(client, args);
|
|
2031
|
+
if (action2 === "inspect-chatflow") return inspectChatflow(client, args);
|
|
2032
|
+
if (action2 === "render-summary") return plan(domain, args);
|
|
2033
|
+
throw new Error(`unknown ${domain} action: ${action2}`);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/workflows/kb-builder.ts
|
|
2037
|
+
import { join as join8 } from "path";
|
|
2038
|
+
import ExcelJS4 from "exceljs";
|
|
2039
|
+
var clean = (value) => String(value ?? "").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
2040
|
+
var absolute = (href, base) => {
|
|
2041
|
+
try {
|
|
2042
|
+
return new URL(href, base).toString();
|
|
2043
|
+
} catch {
|
|
2044
|
+
return void 0;
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
async function crawl(args) {
|
|
2048
|
+
const url = option(args, "--url", true);
|
|
2049
|
+
const maxPages = intOption(args, "--max-pages", 50), maxDepth = intOption(args, "--max-depth", 2), timeout = intOption(args, "--timeout", 20) * 1e3;
|
|
2050
|
+
const origin = new URL(url).origin, queue = [{ url, depth: 0 }], seen = /* @__PURE__ */ new Set(), pages = [];
|
|
2051
|
+
while (queue.length && pages.length < maxPages) {
|
|
2052
|
+
const current = queue.shift();
|
|
2053
|
+
if (seen.has(current.url) || current.depth > maxDepth) continue;
|
|
2054
|
+
seen.add(current.url);
|
|
2055
|
+
try {
|
|
2056
|
+
const response = await fetch(current.url, { signal: AbortSignal.timeout(timeout), headers: { accept: "text/html,application/xhtml+xml" } });
|
|
2057
|
+
if (!response.ok) continue;
|
|
2058
|
+
const html = await response.text();
|
|
2059
|
+
const title = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] ?? "";
|
|
2060
|
+
const text = clean(html);
|
|
2061
|
+
pages.push({ url: current.url, depth: current.depth, title: clean(title), text: text.slice(0, 5e4), status: response.status });
|
|
2062
|
+
if (current.depth < maxDepth) for (const match of html.matchAll(/href\s*=\s*["']([^"']+)["']/gi)) {
|
|
2063
|
+
const next = absolute(match[1], current.url);
|
|
2064
|
+
if (next && next.startsWith(origin) && !seen.has(next)) queue.push({ url: next, depth: current.depth + 1 });
|
|
2065
|
+
}
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
pages.push({ url: current.url, depth: current.depth, error: error instanceof Error ? error.message : String(error) });
|
|
315
2068
|
}
|
|
316
2069
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
2070
|
+
const result = { url, mode: option(args, "--mode") ?? "auto", pages, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), total: pages.length };
|
|
2071
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "crawl_result.json");
|
|
2072
|
+
await writeJson3(output, result);
|
|
2073
|
+
return { ...result, output };
|
|
2074
|
+
}
|
|
2075
|
+
async function directoryPlan(args) {
|
|
2076
|
+
const source = await readJson3(option(args, "--crawl-result", true));
|
|
2077
|
+
const rows = asRows(source.pages ?? source);
|
|
2078
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2079
|
+
for (const page2 of rows) {
|
|
2080
|
+
const title = String(page2.title ?? page2.url ?? "Untitled");
|
|
2081
|
+
const key = title.split(/[:|/\\-]/)[0].trim() || "General";
|
|
2082
|
+
groups.set(key, [...groups.get(key) ?? [], page2]);
|
|
2083
|
+
}
|
|
2084
|
+
const tree = [...groups.entries()].map(([name, pages]) => ({ name, children: pages.slice(0, 100).map((page2) => ({ name: String(page2.title ?? page2.url), sourceUrl: page2.url })) }));
|
|
2085
|
+
const result = { source: option(args, "--crawl-result"), generatedAt: (/* @__PURE__ */ new Date()).toISOString(), tree, confirmed: false };
|
|
2086
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "directory_plan.json");
|
|
2087
|
+
await writeJson3(output, result);
|
|
2088
|
+
if (flag(args, "--write-confirmed-tree")) await writeJson3(join8(stateRoot("kb-builder"), "confirmed_tree.json"), { ...result, confirmed: true });
|
|
2089
|
+
return { output, directories: tree.length, pages: rows.length, tree };
|
|
2090
|
+
}
|
|
2091
|
+
async function createDirectories(client, args) {
|
|
2092
|
+
const file = option(args, "--tree-file") ?? join8(stateRoot("kb-builder"), "confirmed_tree.json");
|
|
2093
|
+
const value = await readJson3(file);
|
|
2094
|
+
const tree = Array.isArray(value.tree) ? value.tree : Array.isArray(value) ? value : [];
|
|
2095
|
+
if (!tree.length) throw new Error("confirmed directory tree is empty");
|
|
2096
|
+
const dryRun = flag(args, "--dry-run"), created = [];
|
|
2097
|
+
async function visit(nodes, parentId = 0, depth = 0) {
|
|
2098
|
+
if (depth > intOption(args, "--max-depth", 3)) throw new Error("directory tree exceeds max depth");
|
|
2099
|
+
for (const node of nodes) {
|
|
2100
|
+
const name = String(node.name ?? "").trim();
|
|
2101
|
+
if (!name) continue;
|
|
2102
|
+
const result = dryRun ? { name, parentId, dryRun: true } : await client.createFolder(name, parentId);
|
|
2103
|
+
created.push(result);
|
|
2104
|
+
const id = Number(result?.id ?? result?.directoryId ?? 0);
|
|
2105
|
+
const children = Array.isArray(node.children) ? node.children : [];
|
|
2106
|
+
if (children.length && id) await visit(children, id, depth + 1);
|
|
324
2107
|
}
|
|
325
|
-
if (sub === "search") return print(await search(session, positionals(rest)[0] ?? "", option(rest, "--env", true)));
|
|
326
|
-
if (sub === "use") return print(await useTenant(session, Number(positionals(rest)[0]), option(rest, "--env", true), option(rest, "--purpose", true)));
|
|
327
2108
|
}
|
|
328
|
-
|
|
2109
|
+
await visit(tree);
|
|
2110
|
+
return { file, dryRun, created, count: created.length };
|
|
2111
|
+
}
|
|
2112
|
+
async function extractWebQa(args) {
|
|
2113
|
+
const input = await readJson3(option(args, "--crawl-result", true));
|
|
2114
|
+
const pages = asRows(input.pages ?? input).slice(0, intOption(args, "--max-pages", 80));
|
|
2115
|
+
const outputDir2 = option(args, "--output-dir") ?? join8(outputRoot("kb-builder"), "qa_parts");
|
|
2116
|
+
await ensureDir(outputDir2);
|
|
2117
|
+
const parts = pages.map((page2, index) => ({ question: String(page2.title ?? page2.url), answer: String(page2.text ?? "").slice(0, intOption(args, "--answer-limit", 3500)), sourceUrl: page2.url, sourceTitle: page2.title, index }));
|
|
2118
|
+
const files = [];
|
|
2119
|
+
for (const [index, part] of parts.entries()) {
|
|
2120
|
+
const file = join8(outputDir2, `part_${String(index + 1).padStart(4, "0")}.json`);
|
|
2121
|
+
await writeJson3(file, { items: [part] });
|
|
2122
|
+
files.push(file);
|
|
2123
|
+
}
|
|
2124
|
+
return { outputDir: outputDir2, files, count: files.length };
|
|
2125
|
+
}
|
|
2126
|
+
async function importQa(client, args) {
|
|
2127
|
+
const qaFile = option(args, "--qa-file");
|
|
2128
|
+
const fromDir = option(args, "--from-dir");
|
|
2129
|
+
if (!qaFile && !fromDir) throw new Error("--qa-file or --from-dir is required");
|
|
2130
|
+
const items = [];
|
|
2131
|
+
if (qaFile) {
|
|
2132
|
+
const value = await readJson3(qaFile);
|
|
2133
|
+
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2134
|
+
}
|
|
2135
|
+
if (fromDir) {
|
|
2136
|
+
const { readdir } = await import("fs/promises");
|
|
2137
|
+
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json"))) {
|
|
2138
|
+
const value = await readJson3(join8(fromDir, file));
|
|
2139
|
+
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
const plan2 = { items, count: items.length, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2143
|
+
const planFile = join8(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
|
|
2144
|
+
await writeJson3(planFile, plan2);
|
|
2145
|
+
if (flag(args, "--dry-run")) return { dryRun: true, planFile, count: items.length };
|
|
2146
|
+
return { planFile, count: items.length, result: await client.uploadQa(items) };
|
|
2147
|
+
}
|
|
2148
|
+
async function importSources(client, args) {
|
|
2149
|
+
const plan2 = await readJson3(option(args, "--plan-file", true));
|
|
2150
|
+
const documents = Array.isArray(plan2.documents) ? plan2.documents : Array.isArray(plan2.items) ? plan2.items : [];
|
|
2151
|
+
const payload = { documents, taskType: "WEB", published: true, isVisible: true, metadata: { tags: [] }, directoryId: String(plan2.directoryId ?? "") };
|
|
2152
|
+
if (!flag(args, "--execute")) return { dryRun: true, payload };
|
|
2153
|
+
return client.rawCall("POST", `${client.qamPath}/project/${await client.projectId()}/documents`, payload);
|
|
2154
|
+
}
|
|
2155
|
+
async function importTemplates(client, args) {
|
|
2156
|
+
const source = option(args, "--plan-file") ?? option(args, "--xlsx");
|
|
2157
|
+
if (!source) throw new Error("--xlsx or --plan-file is required");
|
|
2158
|
+
const rows = [];
|
|
2159
|
+
if (source.endsWith(".json")) rows.push(...asRows(await readJson3(source)));
|
|
2160
|
+
else {
|
|
2161
|
+
const workbook = new ExcelJS4.Workbook();
|
|
2162
|
+
await workbook.xlsx.readFile(source);
|
|
2163
|
+
const sheet = option(args, "--sheet") ? workbook.getWorksheet(option(args, "--sheet")) : workbook.worksheets[0];
|
|
2164
|
+
if (!sheet) throw new Error("workbook has no worksheet");
|
|
2165
|
+
const headers = sheet.getRow(1).values.slice(1).map((v) => String(v ?? ""));
|
|
2166
|
+
sheet.eachRow((row, number) => {
|
|
2167
|
+
if (number === 1) return;
|
|
2168
|
+
const values = row.values.slice(1);
|
|
2169
|
+
const item = {};
|
|
2170
|
+
headers.forEach((header, index) => {
|
|
2171
|
+
if (header) item[header] = values[index] ?? "";
|
|
2172
|
+
});
|
|
2173
|
+
rows.push(item);
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
const items = rows.map((row) => ({ name: String(row.name ?? row.Name ?? "").trim(), content: String(row.content ?? row.Content ?? row.template ?? ""), status: row.status ?? row.Status ?? "ENABLED", scene: row.scene ?? row.Scene })).filter((item) => item.name && item.content);
|
|
2177
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `template_import_plan_${Date.now()}.json`);
|
|
2178
|
+
await writeJson3(output, { items, count: items.length });
|
|
2179
|
+
if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
|
|
2180
|
+
const results = [];
|
|
2181
|
+
for (const item of items) results.push(await client.createTemplate(item));
|
|
2182
|
+
return { output, count: results.length, results };
|
|
2183
|
+
}
|
|
2184
|
+
async function exportKnowledge(client, args) {
|
|
2185
|
+
const directories = asRows(await client.listDirectories());
|
|
2186
|
+
const ids = option(args, "--directory-id") ? [Number(option(args, "--directory-id"))] : directories.map((row) => Number(row.id ?? row.directoryId)).filter(Number.isFinite);
|
|
2187
|
+
const rows = [];
|
|
2188
|
+
for (const id of ids) {
|
|
2189
|
+
const value = await client.listKnowledge({ directoryId: id, keyword: option(args, "--keyword") ?? "", type: option(args, "--type") ?? "ALL", pageSize: intOption(args, "--page-size", 1e3) });
|
|
2190
|
+
rows.push(...asRows(value).map((row) => ({ ...row, directoryId: id, directoryName: directories.find((dir) => Number(dir.id ?? dir.directoryId) === id)?.name ?? "" })));
|
|
2191
|
+
}
|
|
2192
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `knowledge_${Date.now()}.xlsx`);
|
|
2193
|
+
const workbook = new ExcelJS4.Workbook();
|
|
2194
|
+
const sheet = workbook.addWorksheet("knowledge");
|
|
2195
|
+
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
2196
|
+
sheet.addRow(keys);
|
|
2197
|
+
for (const row of rows) sheet.addRow(keys.map((key) => row[key] ?? ""));
|
|
2198
|
+
await ensureDir(join8(output, ".."));
|
|
2199
|
+
await workbook.xlsx.writeFile(output);
|
|
2200
|
+
return { output, count: rows.length };
|
|
2201
|
+
}
|
|
2202
|
+
async function runKbBuilder(session, action2, args) {
|
|
2203
|
+
if (!action2) throw new Error("kb-builder action is required");
|
|
2204
|
+
if (action2 === "crawl") return crawl(args);
|
|
2205
|
+
if (action2 === "directory-plan") return directoryPlan(args);
|
|
2206
|
+
if (!session) throw new Error(`${action2} requires an authenticated tenant`);
|
|
2207
|
+
const client = new SolveaClient(session);
|
|
2208
|
+
if (action2 === "create-directories") return createDirectories(client, args);
|
|
2209
|
+
if (action2 === "extract-web-qa") return extractWebQa(args);
|
|
2210
|
+
if (action2 === "import-qa") return importQa(client, args);
|
|
2211
|
+
if (action2 === "import-source-documents") return importSources(client, args);
|
|
2212
|
+
if (action2 === "import-templates") return importTemplates(client, args);
|
|
2213
|
+
if (action2 === "export-knowledge") return exportKnowledge(client, args);
|
|
2214
|
+
if (action2 === "extract-ticket-qa" || action2 === "batch-extract-ticket-qa") return extractTicketQa(args);
|
|
2215
|
+
throw new Error(`unknown kb-builder action: ${action2}`);
|
|
2216
|
+
}
|
|
2217
|
+
async function extractTicketQa(args) {
|
|
2218
|
+
const file = option(args, "--xlsx", true);
|
|
2219
|
+
const workbook = new ExcelJS4.Workbook();
|
|
2220
|
+
await workbook.xlsx.readFile(file);
|
|
2221
|
+
const sheet = workbook.worksheets[0];
|
|
2222
|
+
if (!sheet) throw new Error("workbook has no worksheet");
|
|
2223
|
+
const headers = sheet.getRow(1).values.slice(1).map((v) => String(v ?? ""));
|
|
2224
|
+
const messageCol = option(args, "--messages-col") ?? headers.find((header) => /message|conversation|content|描述/i.test(header)) ?? headers[0];
|
|
2225
|
+
const titleCol = option(args, "--title-col");
|
|
2226
|
+
const replyCol = option(args, "--reply-col");
|
|
2227
|
+
const brand = option(args, "--brand", true);
|
|
2228
|
+
const items = [];
|
|
2229
|
+
sheet.eachRow((row, number) => {
|
|
2230
|
+
if (number === 1) return;
|
|
2231
|
+
const values = row.values.slice(1), record = {};
|
|
2232
|
+
headers.forEach((header, index) => {
|
|
2233
|
+
record[header] = values[index] ?? "";
|
|
2234
|
+
});
|
|
2235
|
+
const question = `${brand}: ${String(record[titleCol] ?? "").trim()} ${String(record[messageCol] ?? "").trim()}`.trim();
|
|
2236
|
+
const answer = String(record[replyCol] ?? "").trim();
|
|
2237
|
+
if (question && question !== `${brand}:`) items.push({ question, answer, sourceRow: number });
|
|
2238
|
+
});
|
|
2239
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `ticket_qa_${Date.now()}.json`);
|
|
2240
|
+
const result = { brand, count: items.length, items };
|
|
2241
|
+
await writeJson3(output, result);
|
|
2242
|
+
if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
|
|
2243
|
+
return { output, count: items.length, items };
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// src/workflows/desk.ts
|
|
2247
|
+
import { join as join9 } from "path";
|
|
2248
|
+
import ExcelJS5 from "exceljs";
|
|
2249
|
+
var taskId = (args) => option(args, "--task-id") ?? (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
2250
|
+
var taskDir = (args) => join9(stateRoot("solvea-desk"), taskId(args));
|
|
2251
|
+
async function saveDatasource(dir, name, rows) {
|
|
2252
|
+
const file = join9(dir, name);
|
|
2253
|
+
await ensureParent(file);
|
|
2254
|
+
const workbook = new ExcelJS5.Workbook();
|
|
2255
|
+
const sheet = workbook.addWorksheet("data");
|
|
2256
|
+
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
2257
|
+
sheet.addRow(keys);
|
|
2258
|
+
for (const row of rows) sheet.addRow(keys.map((key) => row[key] ?? ""));
|
|
2259
|
+
await workbook.xlsx.writeFile(file);
|
|
2260
|
+
return file;
|
|
2261
|
+
}
|
|
2262
|
+
async function fetchTickets(client, args) {
|
|
2263
|
+
const dir = taskDir(args);
|
|
2264
|
+
const pageSize2 = Math.max(1, Math.min(intOption(args, "--page-size", 100), 500));
|
|
2265
|
+
const maxPages = intOption(args, "--max-pages", 100);
|
|
2266
|
+
const maxRows = intOption(args, "--max-rows", 1e4);
|
|
2267
|
+
const rows = [];
|
|
2268
|
+
for (let page2 = 1; page2 <= maxPages && rows.length < maxRows; page2 += 1) {
|
|
2269
|
+
const value = await client.rawCall("POST", "/api_v2/tars/api/ticket/list", { viewId: option(args, "--view-id") ?? "1", page: page2, pageSize: pageSize2, sort: option(args, "--sort") ?? "last_update_time", order: option(args, "--order") ?? "DESC", ...option(args, "--filters-file") ? await readJson3(option(args, "--filters-file")) : {} });
|
|
2270
|
+
const pageRows = asRows(value);
|
|
2271
|
+
rows.push(...pageRows.slice(0, maxRows - rows.length));
|
|
2272
|
+
if (pageRows.length < pageSize2) break;
|
|
2273
|
+
}
|
|
2274
|
+
const output = await saveDatasource(dir, "datasource.xlsx", rows);
|
|
2275
|
+
await writeJson3(join9(dir, "metadata.json"), { taskId: taskId(args), rows: rows.length, output });
|
|
2276
|
+
return { taskId: taskId(args), output, count: rows.length };
|
|
2277
|
+
}
|
|
2278
|
+
async function fetchSources(client, args) {
|
|
2279
|
+
const dir = taskDir(args);
|
|
2280
|
+
const begin = option(args, "--start", true), end = option(args, "--end", true);
|
|
2281
|
+
const sources = { begin, end, tickets: [] };
|
|
2282
|
+
const value = await client.rawCall("POST", "/api_v2/tars/api/ticket/list", { startTime: begin, endTime: end, page: 1, pageSize: intOption(args, "--max-rows", 1e4) });
|
|
2283
|
+
const rows = asRows(value).slice(0, intOption(args, "--max-rows", 1e4));
|
|
2284
|
+
sources.tickets = rows;
|
|
2285
|
+
const output = await saveDatasource(dir, "datasource.xlsx", rows);
|
|
2286
|
+
await writeJson3(join9(dir, "sources.json"), sources);
|
|
2287
|
+
return { taskId: taskId(args), output, count: rows.length };
|
|
2288
|
+
}
|
|
2289
|
+
async function tags(client, action2, args) {
|
|
2290
|
+
if (action2 === "list-tags") return client.rawCall("GET", "/api_v2/tars/api/ticket/tag/tree");
|
|
2291
|
+
const ticket = option(args, "--ticket-number", true);
|
|
2292
|
+
if (action2 === "get-ticket-tags") return client.rawCall("GET", `/api_v2/tars/api/ticket/${encodeURIComponent(ticket)}/tags`);
|
|
2293
|
+
if (action2 === "search-tags") {
|
|
2294
|
+
const tree = await client.rawCall("GET", "/api_v2/tars/api/ticket/tag/tree");
|
|
2295
|
+
const needle = option(args, "--name", true).toLowerCase();
|
|
2296
|
+
const flatten = (value) => Array.isArray(value) ? value.flatMap(flatten) : value && typeof value === "object" ? [value, ...flatten(value.children ?? value.nodes ?? [])] : [];
|
|
2297
|
+
return flatten(tree).filter((row) => String(row.name ?? row.label ?? "").toLowerCase().includes(needle));
|
|
2298
|
+
}
|
|
2299
|
+
if (action2 === "batch-get-ticket-tags") {
|
|
2300
|
+
const ids = asRows(await readJson3(option(args, "--ticket-ids-file", true)));
|
|
2301
|
+
const result = [];
|
|
2302
|
+
for (const row of ids) result.push({ ...row, tags: await client.rawCall("GET", `/api_v2/tars/api/ticket/${encodeURIComponent(String(row.ticketNumber ?? row.ticketId ?? row.id))}/tags`) });
|
|
2303
|
+
return result;
|
|
2304
|
+
}
|
|
2305
|
+
if (action2 === "bind-tags" || action2 === "replace-tags") {
|
|
2306
|
+
if (!flag(args, "--confirm")) throw new Error(`${action2} requires --confirm`);
|
|
2307
|
+
const tagsFile = option(args, "--tags-file", true);
|
|
2308
|
+
return client.rawCall("POST", `/api_v2/tars/api/ticket/${encodeURIComponent(ticket)}/tags`, { tags: await readJson3(tagsFile) });
|
|
2309
|
+
}
|
|
2310
|
+
throw new Error(`unknown tag action: ${action2}`);
|
|
2311
|
+
}
|
|
2312
|
+
async function renderReport2(args) {
|
|
2313
|
+
const dir = taskDir(args), dataFile = option(args, "--report-data") ?? join9(dir, "report_data.json"), notesFile = option(args, "--notes") ?? join9(dir, "calculation_notes.json");
|
|
2314
|
+
const data3 = await readJson3(dataFile);
|
|
2315
|
+
if (flag(args, "--validate-only")) return { valid: true, dataFile };
|
|
2316
|
+
const output = option(args, "--output") ?? join9(outputRoot("solvea-desk"), taskId(args), `${option(args, "--report-slug") ?? "report"}.html`);
|
|
2317
|
+
const body = `<pre>${htmlEscape(JSON.stringify({ data: data3, notes: await readJson3(notesFile).catch(() => ({})) }, null, 2))}</pre>`;
|
|
2318
|
+
await writeHtml(output, option(args, "--report-slug") ?? "Solvea Desk report", body);
|
|
2319
|
+
return { output, valid: true };
|
|
2320
|
+
}
|
|
2321
|
+
async function runDesk(session, action2, args) {
|
|
2322
|
+
if (!action2) throw new Error("solvea-desk action is required");
|
|
2323
|
+
const client = new SolveaClient(session);
|
|
2324
|
+
if (action2 === "fetch-tickets") return fetchTickets(client, args);
|
|
2325
|
+
if (action2 === "fetch-sources") return fetchSources(client, args);
|
|
2326
|
+
if (action2 === "render-report") return renderReport2(args);
|
|
2327
|
+
if (action2 === "tags") return tags(client, option(args, "--operation") ?? args.find((value) => ["list-tags", "get-ticket-tags", "batch-get-ticket-tags", "search-tags", "bind-tags", "replace-tags"].includes(value)) ?? "list-tags", args);
|
|
2328
|
+
if (["list-tags", "get-ticket-tags", "batch-get-ticket-tags", "search-tags", "bind-tags", "replace-tags"].includes(action2)) return tags(client, action2, args);
|
|
2329
|
+
throw new Error(`unknown solvea-desk action: ${action2}`);
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// src/cli.ts
|
|
2333
|
+
var print = (value) => console.log(JSON.stringify(value, null, 2));
|
|
2334
|
+
var usage = () => [
|
|
2335
|
+
"cxclawctl <resource> <action> [options]",
|
|
2336
|
+
"cxclawctl auth|tenant|sls|runtime|doctor|update|setup|version",
|
|
2337
|
+
"cxclawctl agent-builder|agent-tester|delivery|poc|kb-builder|solvea-desk <action>",
|
|
2338
|
+
resourceHelp(),
|
|
2339
|
+
"Example: cxclawctl intent create --data-file intent.json"
|
|
2340
|
+
].join("\n\n");
|
|
2341
|
+
async function runAuth(action2, args) {
|
|
2342
|
+
if (action2 === "login") return print(await login(option(args, "--server", true), option(args, "--channel") ?? "stable"));
|
|
2343
|
+
if (action2 === "logout") {
|
|
2344
|
+
await logout();
|
|
2345
|
+
return print({ ok: true, loggedOut: true });
|
|
2346
|
+
}
|
|
2347
|
+
if (action2 === "status") {
|
|
2348
|
+
try {
|
|
2349
|
+
const session = await loadSession();
|
|
2350
|
+
return print({ ok: true, loggedIn: true, email: session.email, channel: session.channel, accessExpiresAt: session.accessExpiry });
|
|
2351
|
+
} catch {
|
|
2352
|
+
return print({ ok: true, loggedIn: false });
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
if (action2 === "refresh") {
|
|
329
2356
|
const session = await authenticated();
|
|
330
|
-
|
|
331
|
-
|
|
2357
|
+
return print({ ok: true, accessExpiresAt: session.accessExpiry });
|
|
2358
|
+
}
|
|
2359
|
+
throw new Error("auth action must be login, logout, status, or refresh");
|
|
2360
|
+
}
|
|
2361
|
+
async function runTenant(action2, args) {
|
|
2362
|
+
const session = await authenticated();
|
|
2363
|
+
if (action2 === "current") return print(publicTenant(session.tenant));
|
|
2364
|
+
if (action2 === "clear") {
|
|
2365
|
+
delete session.tenant;
|
|
2366
|
+
await saveSession(session);
|
|
2367
|
+
return print({ ok: true, cleared: true });
|
|
2368
|
+
}
|
|
2369
|
+
if (action2 === "search") return print(await search(session, positionals(args)[0] ?? "", option(args, "--env", true)));
|
|
2370
|
+
if (action2 === "use") return print(await useTenant(session, Number(positionals(args)[0]), option(args, "--env", true), option(args, "--purpose", true)));
|
|
2371
|
+
throw new Error("tenant action must be search, use, current, or clear");
|
|
2372
|
+
}
|
|
2373
|
+
async function main(args = process.argv.slice(2)) {
|
|
2374
|
+
const [command, action2, ...rest] = args;
|
|
2375
|
+
if (!command || command === "help" || flag(args, "--help")) {
|
|
2376
|
+
console.log(command && isResourceCommand(command) ? resourceHelp(command, action2 && !["help", "--help"].includes(action2) ? action2 : void 0) : usage());
|
|
332
2377
|
return;
|
|
333
2378
|
}
|
|
334
|
-
if (command
|
|
335
|
-
|
|
336
|
-
process.exitCode = await runPython(sub, rest);
|
|
2379
|
+
if (isResourceCommand(command)) {
|
|
2380
|
+
await runResourceCommand(await authenticated(), command, action2, rest);
|
|
337
2381
|
return;
|
|
338
2382
|
}
|
|
339
|
-
if (command === "
|
|
340
|
-
|
|
341
|
-
|
|
2383
|
+
if (command === "version") {
|
|
2384
|
+
if (flag(args, "--json")) return print({ pluginVersion: process.env.CXCLAW_PLUGIN_VERSION ?? null, cliVersion: VERSION, toolSchemaVersion: TOOL_SCHEMA_VERSION, serverApiVersion: SERVER_API_VERSION });
|
|
2385
|
+
console.log(`cxclawctl ${VERSION}`);
|
|
2386
|
+
return;
|
|
342
2387
|
}
|
|
343
|
-
if (command === "
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
2388
|
+
if (command === "setup") {
|
|
2389
|
+
const channel = option(args, "--channel") ?? "stable";
|
|
2390
|
+
await installMarketplace(channel);
|
|
2391
|
+
await login(option(args, "--server", true), channel);
|
|
2392
|
+
await installScheduler();
|
|
2393
|
+
return;
|
|
347
2394
|
}
|
|
2395
|
+
if (command === "auth") return runAuth(action2, rest);
|
|
2396
|
+
if (command === "login" || command === "logout") return runAuth(command, args.slice(1));
|
|
2397
|
+
if (command === "tenant" || command === "context") return runTenant(action2, rest);
|
|
2398
|
+
if (command === "runtime") return print(await runRuntime(action2, rest));
|
|
2399
|
+
if (command === "workflow") return print(await runSolveaKitWorkflow(action2, rest));
|
|
2400
|
+
if (command === "support") return print(await runSupport(await authenticated(), action2, rest));
|
|
2401
|
+
if (command === "agent-builder") return print(await runAgentBuilder(["validate", "compile", "merge-source", "validate-tool", "migration", "replay"].includes(action2 ?? "") ? void 0 : await authenticated(), action2, rest));
|
|
2402
|
+
if (command === "agent-tester") return print(await runAgentTester(action2 === "create-sheet" ? void 0 : await authenticated(), action2, rest));
|
|
2403
|
+
if (command === "delivery") return print(await runDelivery(["classify", "plan", "report", "render", "render-report", "excel", "gen-excel", "render-summary"].includes(action2 ?? "") ? void 0 : await authenticated(), action2, rest));
|
|
2404
|
+
if (command === "poc") return print(await runPoc(["classify", "plan", "report", "render", "render-report", "excel", "gen-excel", "manual", "render-summary"].includes(action2 ?? "") ? void 0 : await authenticated(), action2, rest));
|
|
2405
|
+
if (command === "kb-builder") return print(await runKbBuilder(["crawl", "directory-plan", "extract-web-qa", "extract-ticket-qa", "batch-extract-ticket-qa"].includes(action2 ?? "") ? void 0 : await authenticated(), action2, rest));
|
|
2406
|
+
if (command === "solvea-desk") return print(await runDesk(await authenticated(), action2, rest));
|
|
2407
|
+
if (command === "api") {
|
|
2408
|
+
if (!action2) throw new Error("api command is required");
|
|
2409
|
+
await runApi(await authenticated(), action2, rest);
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
if (command === "sls" && action2 === "query") {
|
|
2413
|
+
return print(await query(await authenticated(), {
|
|
2414
|
+
from: Number(option(rest, "--from", true)),
|
|
2415
|
+
to: Number(option(rest, "--to", true)),
|
|
2416
|
+
query: option(rest, "--query", true),
|
|
2417
|
+
line: Number(option(rest, "--line") ?? 50),
|
|
2418
|
+
offset: Number(option(rest, "--offset") ?? 0),
|
|
2419
|
+
reverse: flag(rest, "--reverse")
|
|
2420
|
+
}));
|
|
2421
|
+
}
|
|
2422
|
+
if (command === "doctor") return print(await diagnose());
|
|
348
2423
|
if (command === "update") {
|
|
349
2424
|
if (flag(args, "--install-scheduler")) {
|
|
350
2425
|
await installScheduler();
|
|
351
|
-
return;
|
|
2426
|
+
return print({ ok: true, schedulerInstalled: true });
|
|
352
2427
|
}
|
|
353
|
-
if (flag(args, "--scheduled")) await new Promise((
|
|
354
|
-
return print(
|
|
2428
|
+
if (flag(args, "--scheduled")) await new Promise((resolve5) => setTimeout(resolve5, Math.random() * 18e5));
|
|
2429
|
+
return print(await update());
|
|
355
2430
|
}
|
|
356
|
-
if (command === "commands" || command === "capabilities")
|
|
357
|
-
throw new Error(`unknown command: ${command}`);
|
|
2431
|
+
if (command === "commands" || command === "capabilities") console.log(await readFile5(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
|
|
2432
|
+
else throw new Error(`unknown command: ${command}`);
|
|
358
2433
|
}
|
|
359
2434
|
main().catch((error) => {
|
|
360
2435
|
console.error(`cxclawctl: ${error instanceof Error ? error.message : String(error)}`);
|