@puyinkai/wow-xiaobao-cli 0.0.0-stage → 1.0.0
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 +42 -2
- package/dist/add-Da4a88Dn.mjs +21 -0
- package/dist/admin-nr9IIF2K.mjs +14 -0
- package/dist/alerts-DjMCQESW.mjs +71 -0
- package/dist/analyze-BJATzFtI.mjs +67 -0
- package/dist/api-C1s2co2-.mjs +113 -0
- package/dist/audio-BYm3m77S.mjs +15 -0
- package/dist/auth-JZ34ksvF.mjs +14 -0
- package/dist/beautx-D5y2iytj.mjs +16 -0
- package/dist/call-BfT4uEa7.mjs +67 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +47 -0
- package/dist/config-DipFfMio.mjs +11 -0
- package/dist/config-DqUxKQ3m.mjs +131 -0
- package/dist/consultant-CJ0tH11c.mjs +11 -0
- package/dist/current-Di1OImPn.mjs +33 -0
- package/dist/customer-Bpfdfh0U.mjs +15 -0
- package/dist/date-C9LQUbfA.mjs +21 -0
- package/dist/deal-analysis-Q4GMijIy.mjs +10 -0
- package/dist/detail-Df2eLTSN.mjs +69 -0
- package/dist/detail-k-zGzX4n.mjs +71 -0
- package/dist/diff-DkyjJG49.mjs +21 -0
- package/dist/doc-content-DlRfFk_V.mjs +54 -0
- package/dist/doc-e7Hu-v3F.mjs +42 -0
- package/dist/docs-C2ukQfbY.mjs +41 -0
- package/dist/fields-CNQUQ3BI.mjs +49 -0
- package/dist/focus-BNA14pxJ.mjs +11 -0
- package/dist/follow-BYYbUVCh.mjs +15 -0
- package/dist/format-bDFUVuDQ.mjs +622 -0
- package/dist/get-ZFU7CjqO.mjs +61 -0
- package/dist/headers-Dy5gROBB.mjs +11 -0
- package/dist/intent-DF8cH6nw.mjs +63 -0
- package/dist/json-CXENUkN_.mjs +13 -0
- package/dist/kb-n84Hl0uL.mjs +16 -0
- package/dist/last-7-days-WPinYIJi.mjs +5 -0
- package/dist/list-BVk58WIy.mjs +30 -0
- package/dist/list-BwhH-ZlP.mjs +87 -0
- package/dist/list-CJx_zclT.mjs +68 -0
- package/dist/list-CL5W1jMc.mjs +75 -0
- package/dist/list-Cb2q7k36.mjs +36 -0
- package/dist/list-Cl0VBo_Z.mjs +71 -0
- package/dist/list-D27lkRNJ.mjs +86 -0
- package/dist/list-D7iMacYA.mjs +71 -0
- package/dist/list-DTiXpPZF.mjs +38 -0
- package/dist/list-Dv2BGPQa.mjs +71 -0
- package/dist/list-KTc5uu5P.mjs +86 -0
- package/dist/now-Rn6gT4Bn.mjs +9 -0
- package/dist/panke-_HDbr3xq.mjs +14 -0
- package/dist/plan-c3BW0aCB.mjs +10 -0
- package/dist/portrait-BopNLEQX.mjs +14 -0
- package/dist/portrait-tags-Dhwy6-6U.mjs +10 -0
- package/dist/project-CIOk-2wk.mjs +15 -0
- package/dist/project-store-B-2rqrgM.mjs +63 -0
- package/dist/qa-CxQZgKRF.mjs +59 -0
- package/dist/quantum-BSlMUFk6.mjs +178 -0
- package/dist/range-BsL08fkP.mjs +18 -0
- package/dist/resistance-CzbXP4Mj.mjs +11 -0
- package/dist/sales-report-BbNoueUk.mjs +65 -0
- package/dist/score-CVu51n3k.mjs +5 -0
- package/dist/score-yakz2AnG.mjs +5 -0
- package/dist/search-kLJrFOVI.mjs +70 -0
- package/dist/service-score-CiIxFGJ8.mjs +91 -0
- package/dist/shared-BCf9YDEa.mjs +11 -0
- package/dist/shared-COOwe14d.mjs +87 -0
- package/dist/shared-CdUFV4rc.mjs +277 -0
- package/dist/staff-DasX8vTs.mjs +14 -0
- package/dist/staff-client-B3hnEyNi.mjs +59 -0
- package/dist/status-BFJYeL2I.mjs +24 -0
- package/dist/subtract-wUG7Jzdu.mjs +21 -0
- package/dist/summary-Dgt4Wm5k.mjs +79 -0
- package/dist/summary-DtLL_XW3.mjs +60 -0
- package/dist/text-Dj9OFG5s.mjs +44 -0
- package/dist/this-month-BsrOeTBH.mjs +5 -0
- package/dist/this-week-tCzGJfrf.mjs +5 -0
- package/dist/today-Br-4_qMr.mjs +9 -0
- package/dist/today-CqrcQCGQ.mjs +5 -0
- package/dist/today-so-far-CSGFhwkN.mjs +5 -0
- package/dist/token-store-BGLVc825.mjs +33 -0
- package/dist/token-usage-BK5JIJKx.mjs +97 -0
- package/dist/tomorrow-DYhV_kGk.mjs +9 -0
- package/dist/use-89I-jHty.mjs +61 -0
- package/dist/util-BuoFIeLS.mjs +46 -0
- package/dist/visit-ChOnvK8M.mjs +16 -0
- package/dist/visit-evaluate-BnxsFnQc.mjs +9 -0
- package/dist/weekday-7aGLxWaZ.mjs +14 -0
- package/dist/whoami-Ci6-uJJd.mjs +51 -0
- package/dist/yesterday-BzT2U1_f.mjs +9 -0
- package/dist/yesterday-DgYoUKYd.mjs +5 -0
- package/package.json +35 -4
package/README.md
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @puyinkai/wow-xiaobao-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
旺喔(WoW)云电脑专用的旺小宝 CLI,命令名 `wow-xiaobao`。由 `@puyinkai/xiaobao-cli` 复制而来,**只供旺喔平台使用**;其他场景请用 `@puyinkai/xiaobao-cli`。
|
|
4
|
+
|
|
5
|
+
和原 CLI 的区别:
|
|
6
|
+
|
|
7
|
+
- 没有登录、设备码、刷新和写令牌:授权由用户在旺喔 App 的连接器里完成,平台每次运行前把访问令牌写入 `$WOW_XIAOBAO_TOKEN_DIR/token.json`(格式 `xiaobao-token-v1`),运行结束清空。
|
|
8
|
+
- 未设置 `WOW_XIAOBAO_TOKEN_DIR`、文件不存在、为空或令牌过期时,命令报 `NOT_AUTHENTICATED`,提示到旺喔 App 授权。
|
|
9
|
+
- 不内置应用凭据;业务接口地址可用 `--api-base` 或 `XIAOBAO_API_BASE` 覆盖。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
电脑镜像预装固定版本(和连接器包 `connector/wow.json` 的 `binary.version` 一致):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @puyinkai/wow-xiaobao-cli@1.0.0
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 用法
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
wow-xiaobao auth whoami # 当前授权的旺小宝用户
|
|
23
|
+
wow-xiaobao project list # 有权限的租户和项目
|
|
24
|
+
wow-xiaobao project use --tenant-id ... --tenant-name ... --project-id ... --project-name ...
|
|
25
|
+
wow-xiaobao --help # 全部命令
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 连接器包
|
|
29
|
+
|
|
30
|
+
`connector/` 是旺小宝连接器包(兼容 WorkBuddy,另加 `wow.json`),规范见根仓库 `docs/references/connector-package.md`。打包:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python3 scripts/connector-pack.py # 输出 dist-connector/wangxiaobao-{version}.zip
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 开发
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm install # 或 npm install
|
|
40
|
+
npm test # 构建并跑单测
|
|
41
|
+
npm run typecheck
|
|
42
|
+
npm pack # 本地验证发布产物
|
|
43
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { n as parsePositiveDays, r as addDays, t as defineDateCommand } from "./shared-CdUFV4rc.mjs";
|
|
2
|
+
//#region src/commands/date/add.ts
|
|
3
|
+
var add_default = defineDateCommand({
|
|
4
|
+
name: "add",
|
|
5
|
+
description: "日期往后推 N 天",
|
|
6
|
+
args: {
|
|
7
|
+
date: {
|
|
8
|
+
type: "positional",
|
|
9
|
+
required: true,
|
|
10
|
+
description: "基准日期 YYYY-MM-DD"
|
|
11
|
+
},
|
|
12
|
+
days: {
|
|
13
|
+
type: "positional",
|
|
14
|
+
required: true,
|
|
15
|
+
description: "往后推的天数(非负整数)"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
run: ({ args }) => addDays(args.date, parsePositiveDays(args.days, "days"), args.timezone)
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { add_default as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/admin/index.ts
|
|
3
|
+
var admin_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "admin",
|
|
6
|
+
description: "super-admin 经营报表:公司经营数据(出库/回款)/ LLM token 用量(独立白名单)"
|
|
7
|
+
},
|
|
8
|
+
subCommands: {
|
|
9
|
+
"sales-report": () => import("./sales-report-BbNoueUk.mjs").then((m) => m.default),
|
|
10
|
+
"token-usage": () => import("./token-usage-BK5JIJKx.mjs").then((m) => m.default)
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { admin_default as default };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
|
|
2
|
+
import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
|
|
3
|
+
import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
|
|
4
|
+
import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
//#region src/commands/visit/alerts.ts
|
|
7
|
+
/** 查询现有来访分析;片段正文是系统总结,不是录音逐字原文。 */
|
|
8
|
+
var alerts_default = defineCommand({
|
|
9
|
+
meta: {
|
|
10
|
+
name: "alerts",
|
|
11
|
+
description: "来访逼单/风险大总结,可选查询带时间的片段"
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
"visit-id": {
|
|
15
|
+
type: "string",
|
|
16
|
+
required: true,
|
|
17
|
+
description: "来访 ID"
|
|
18
|
+
},
|
|
19
|
+
type: {
|
|
20
|
+
type: "string",
|
|
21
|
+
required: true,
|
|
22
|
+
description: "forcing 逼单 / risk 风险"
|
|
23
|
+
},
|
|
24
|
+
"include-fragments": {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
default: false,
|
|
27
|
+
description: "同时查询标记片段(默认只查询大总结)"
|
|
28
|
+
},
|
|
29
|
+
"api-base": { type: "string" },
|
|
30
|
+
format: {
|
|
31
|
+
type: "string",
|
|
32
|
+
default: "json"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async run({ args }) {
|
|
36
|
+
try {
|
|
37
|
+
if (!/^[1-9]\d*$/.test(String(args["visit-id"])) || !["forcing", "risk"].includes(String(args.type))) {
|
|
38
|
+
writeError({
|
|
39
|
+
code: "BAD_ARGUMENT",
|
|
40
|
+
message: "visit-id 必须为正整数;type 必须为 forcing 或 risk"
|
|
41
|
+
}, args.format);
|
|
42
|
+
process.exit(2);
|
|
43
|
+
}
|
|
44
|
+
const result = await xbApiFetch(resolveConfig({ apiBase: args["api-base"] }), "POST", "/ai-open/visits/alerts", {
|
|
45
|
+
headers: tenantProjectHeaders(await requireActiveProject()),
|
|
46
|
+
body: {
|
|
47
|
+
visitId: args["visit-id"],
|
|
48
|
+
type: args.type,
|
|
49
|
+
includeFragments: args["include-fragments"]
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const envelope = result.data;
|
|
53
|
+
if (!result.ok || !envelope || typeof envelope !== "object" || String(envelope.code) !== "0") throw {
|
|
54
|
+
code: "API_ERROR",
|
|
55
|
+
status: result.status,
|
|
56
|
+
message: envelope?.msg ?? "来访提醒查询失败",
|
|
57
|
+
body: result.data
|
|
58
|
+
};
|
|
59
|
+
if (typeof envelope.data === "string") throw {
|
|
60
|
+
code: "ACCESS_DENIED",
|
|
61
|
+
message: envelope.data
|
|
62
|
+
};
|
|
63
|
+
writeResult(result, args.format);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
writeError(err, args.format);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
//#endregion
|
|
71
|
+
export { alerts_default as default };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
|
|
2
|
+
import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
|
|
3
|
+
import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
|
|
4
|
+
import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
|
|
5
|
+
import { t as csvToArray } from "./util-BuoFIeLS.mjs";
|
|
6
|
+
import { defineCommand } from "citty";
|
|
7
|
+
//#region src/commands/audio/analyze.ts
|
|
8
|
+
/**
|
|
9
|
+
* `wow-xiaobao audio analyze` — 录音批量分析(LLM)。
|
|
10
|
+
*
|
|
11
|
+
* 对指定客户/顾问范围的录音做 LLM 分析(共性抗性、话术问题等开放问题)。
|
|
12
|
+
* 与 `audio list/text`(元数据/逐字稿查询)不同,这是耗时的生成式分析。
|
|
13
|
+
* 调 POST /ai-open/audio/analyze。
|
|
14
|
+
*/
|
|
15
|
+
var analyze_default = defineCommand({
|
|
16
|
+
meta: {
|
|
17
|
+
name: "analyze",
|
|
18
|
+
description: "录音批量分析(LLM,耗时较长;元数据查询用 audio list)"
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
query: {
|
|
22
|
+
type: "positional",
|
|
23
|
+
required: true,
|
|
24
|
+
description: "分析问题/指令"
|
|
25
|
+
},
|
|
26
|
+
type: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "分析类型:general_analysis(默认)| timeline_analysis"
|
|
29
|
+
},
|
|
30
|
+
"customer-id": {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "单个客户 wang_id"
|
|
33
|
+
},
|
|
34
|
+
"customer-ids": {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "CSV: 客户 wang_id 列表"
|
|
37
|
+
},
|
|
38
|
+
"advisor-ids": {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "CSV: 顾问 userId 列表"
|
|
41
|
+
},
|
|
42
|
+
"api-base": { type: "string" },
|
|
43
|
+
format: {
|
|
44
|
+
type: "string",
|
|
45
|
+
default: "toon"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
async run({ args }) {
|
|
49
|
+
try {
|
|
50
|
+
writeResult(await xbApiFetch(resolveConfig({ apiBase: args["api-base"] }), "POST", "/ai-open/audio/analyze", {
|
|
51
|
+
headers: tenantProjectHeaders(await requireActiveProject()),
|
|
52
|
+
body: {
|
|
53
|
+
query: String(args.query),
|
|
54
|
+
analysisType: args.type,
|
|
55
|
+
customerId: args["customer-id"],
|
|
56
|
+
customerIds: csvToArray(args["customer-ids"]),
|
|
57
|
+
advisorIds: csvToArray(args["advisor-ids"])
|
|
58
|
+
}
|
|
59
|
+
}), args.format);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
writeError(err, args.format);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
//#endregion
|
|
67
|
+
export { analyze_default as default };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { t as parseJsonPreservingIntegers } from "./json-CXENUkN_.mjs";
|
|
2
|
+
import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
|
|
3
|
+
import { t as loadActiveProject } from "./project-store-B-2rqrgM.mjs";
|
|
4
|
+
import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
|
|
5
|
+
import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
|
|
6
|
+
import { defineCommand } from "citty";
|
|
7
|
+
//#region src/commands/api.ts
|
|
8
|
+
/**
|
|
9
|
+
* `wow-xiaobao api <METHOD> <path>` — generic authenticated wangxiaobao API call.
|
|
10
|
+
*
|
|
11
|
+
* Bearer token auto-injected (platform-managed, never refreshed). Use for endpoints
|
|
12
|
+
* not yet wrapped as a dedicated command, or for one-off API exploration.
|
|
13
|
+
*/
|
|
14
|
+
const METHODS = new Set([
|
|
15
|
+
"GET",
|
|
16
|
+
"POST",
|
|
17
|
+
"PUT",
|
|
18
|
+
"PATCH",
|
|
19
|
+
"DELETE"
|
|
20
|
+
]);
|
|
21
|
+
/** Parse `k1=v1&k2=v2` or `k=v,k=v` into a Record. */
|
|
22
|
+
function parseKV(input) {
|
|
23
|
+
if (!input) return void 0;
|
|
24
|
+
const out = {};
|
|
25
|
+
const pairs = input.includes("&") ? input.split("&") : input.split(",");
|
|
26
|
+
for (const pair of pairs) {
|
|
27
|
+
const trimmed = pair.trim();
|
|
28
|
+
if (!trimmed) continue;
|
|
29
|
+
const idx = trimmed.indexOf("=");
|
|
30
|
+
if (idx < 0) continue;
|
|
31
|
+
out[trimmed.slice(0, idx).trim()] = trimmed.slice(idx + 1).trim();
|
|
32
|
+
}
|
|
33
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
34
|
+
}
|
|
35
|
+
var api_default = defineCommand({
|
|
36
|
+
meta: {
|
|
37
|
+
name: "api",
|
|
38
|
+
description: "generic authenticated API call — 自动注入 Bearer(探索/调试用)"
|
|
39
|
+
},
|
|
40
|
+
args: {
|
|
41
|
+
method: {
|
|
42
|
+
type: "positional",
|
|
43
|
+
required: true,
|
|
44
|
+
description: "GET / POST / PUT / PATCH / DELETE"
|
|
45
|
+
},
|
|
46
|
+
path: {
|
|
47
|
+
type: "positional",
|
|
48
|
+
required: true,
|
|
49
|
+
description: "API 相对路径,如 `/saas/v2/estate/tenant-and-estate/by-user-id`"
|
|
50
|
+
},
|
|
51
|
+
query: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "查询串,`k1=v1&k2=v2` 或 `k=v,k=v`"
|
|
54
|
+
},
|
|
55
|
+
body: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "JSON body(POST/PUT/PATCH 用,传 JSON 字符串)"
|
|
58
|
+
},
|
|
59
|
+
headers: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "自定义 headers,`K1=V1&K2=V2` 或 `K=V,K=V`。与默认注入的 tenant/project headers 合并(同名 key 用户传入优先)"
|
|
62
|
+
},
|
|
63
|
+
"default-headers": {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
default: true,
|
|
66
|
+
description: "默认按当前激活项目自动注入 X-Tenant-Id / X-Project-Id headers(调 /ai-open/* 开箱即用);`--no-default-headers` 关闭注入"
|
|
67
|
+
},
|
|
68
|
+
"api-base": { type: "string" },
|
|
69
|
+
format: {
|
|
70
|
+
type: "string",
|
|
71
|
+
default: "toon"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async run({ args }) {
|
|
75
|
+
try {
|
|
76
|
+
const method = String(args.method).toUpperCase();
|
|
77
|
+
if (!METHODS.has(method)) {
|
|
78
|
+
writeError({
|
|
79
|
+
code: "BAD_METHOD",
|
|
80
|
+
message: `unknown method: ${args.method}`
|
|
81
|
+
}, args.format);
|
|
82
|
+
process.exit(2);
|
|
83
|
+
}
|
|
84
|
+
const config = resolveConfig({ apiBase: args["api-base"] });
|
|
85
|
+
let body;
|
|
86
|
+
if (args.body) try {
|
|
87
|
+
body = parseJsonPreservingIntegers(args.body);
|
|
88
|
+
} catch {
|
|
89
|
+
body = args.body;
|
|
90
|
+
}
|
|
91
|
+
let mergedHeaders;
|
|
92
|
+
const userHeaders = parseKV(args.headers);
|
|
93
|
+
if (args["default-headers"]) {
|
|
94
|
+
const active = await loadActiveProject();
|
|
95
|
+
if (active) mergedHeaders = {
|
|
96
|
+
...tenantProjectHeaders(active),
|
|
97
|
+
...userHeaders ?? {}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
mergedHeaders = mergedHeaders ?? userHeaders;
|
|
101
|
+
writeResult(await xbApiFetch(config, method, String(args.path), {
|
|
102
|
+
query: parseKV(args.query),
|
|
103
|
+
body,
|
|
104
|
+
headers: mergedHeaders
|
|
105
|
+
}), args.format);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
writeError(err, args.format);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
//#endregion
|
|
113
|
+
export { api_default as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/audio/index.ts
|
|
3
|
+
var audio_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "audio",
|
|
6
|
+
description: "录音元数据分页 / 单条文本查询"
|
|
7
|
+
},
|
|
8
|
+
subCommands: {
|
|
9
|
+
list: () => import("./list-CL5W1jMc.mjs").then((m) => m.default),
|
|
10
|
+
text: () => import("./text-Dj9OFG5s.mjs").then((m) => m.default),
|
|
11
|
+
analyze: () => import("./analyze-BJATzFtI.mjs").then((m) => m.default)
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { audio_default as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/auth/index.ts
|
|
3
|
+
var auth_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "auth",
|
|
6
|
+
description: "授权状态查询(令牌由旺喔平台托管)"
|
|
7
|
+
},
|
|
8
|
+
subCommands: {
|
|
9
|
+
whoami: () => import("./whoami-Ci6-uJJd.mjs").then((m) => m.default),
|
|
10
|
+
status: () => import("./status-BFJYeL2I.mjs").then((m) => m.default)
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { auth_default as default };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/beautx/index.ts
|
|
3
|
+
var beautx_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "beautx",
|
|
6
|
+
description: "医美:变美计划(plan) / 成交分析(deal-analysis) / 画像标签(portrait-tags) / 接诊评价(visit-evaluate)"
|
|
7
|
+
},
|
|
8
|
+
subCommands: {
|
|
9
|
+
plan: () => import("./plan-c3BW0aCB.mjs").then((m) => m.default),
|
|
10
|
+
"deal-analysis": () => import("./deal-analysis-Q4GMijIy.mjs").then((m) => m.default),
|
|
11
|
+
"portrait-tags": () => import("./portrait-tags-Dhwy6-6U.mjs").then((m) => m.default),
|
|
12
|
+
"visit-evaluate": () => import("./visit-evaluate-BnxsFnQc.mjs").then((m) => m.default)
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { beautx_default as default };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { t as parseJsonPreservingIntegers } from "./json-CXENUkN_.mjs";
|
|
2
|
+
import { t as resolveConfig } from "./config-DqUxKQ3m.mjs";
|
|
3
|
+
import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
|
|
4
|
+
import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
|
|
5
|
+
import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
|
|
6
|
+
import { n as fetchStaffTools, t as callStaffTool } from "./staff-client-B3hnEyNi.mjs";
|
|
7
|
+
import { defineCommand } from "citty";
|
|
8
|
+
//#region src/commands/staff/call.ts
|
|
9
|
+
function parseVariables(raw) {
|
|
10
|
+
if (!raw) return {};
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = parseJsonPreservingIntegers(raw);
|
|
14
|
+
} catch {
|
|
15
|
+
throw Object.assign(/* @__PURE__ */ new Error("--variables 必须是合法 JSON object"), { code: "INVALID_VARIABLES" });
|
|
16
|
+
}
|
|
17
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw Object.assign(/* @__PURE__ */ new Error("--variables 必须是 JSON object"), { code: "INVALID_VARIABLES" });
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
var call_default = defineCommand({
|
|
21
|
+
meta: {
|
|
22
|
+
name: "call",
|
|
23
|
+
description: "调用一个员工工具"
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
tool: {
|
|
27
|
+
type: "positional",
|
|
28
|
+
required: true,
|
|
29
|
+
description: "工具名,如 staff-receptions"
|
|
30
|
+
},
|
|
31
|
+
variables: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "工具业务变量 JSON object"
|
|
34
|
+
},
|
|
35
|
+
"time-start": {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "统一统计起始时间(北京时间)"
|
|
38
|
+
},
|
|
39
|
+
"time-end": {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "统一统计结束时间(北京时间)"
|
|
42
|
+
},
|
|
43
|
+
"api-base": { type: "string" },
|
|
44
|
+
format: {
|
|
45
|
+
type: "string",
|
|
46
|
+
default: "json"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
async run({ args }) {
|
|
50
|
+
try {
|
|
51
|
+
const variables = parseVariables(args.variables);
|
|
52
|
+
const config = resolveConfig({ apiBase: args["api-base"] });
|
|
53
|
+
const headers = tenantProjectHeaders(await requireActiveProject());
|
|
54
|
+
const tools = await fetchStaffTools(config, headers);
|
|
55
|
+
if (!args.tool.startsWith("staff-") || !tools.some((tool) => tool.name === args.tool)) throw Object.assign(/* @__PURE__ */ new Error(`员工工具未在当前服务开放:${args.tool}`), { code: "STAFF_TOOL_NOT_FOUND" });
|
|
56
|
+
writeResult(await callStaffTool(config, headers, args.tool, variables, {
|
|
57
|
+
start: args["time-start"],
|
|
58
|
+
end: args["time-end"]
|
|
59
|
+
}), args.format);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
writeError(err, args.format);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
//#endregion
|
|
67
|
+
export { call_default as default };
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { setDefaultResultOrder } from "node:dns";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { defineCommand, runMain } from "citty";
|
|
7
|
+
//#region src/cli.ts
|
|
8
|
+
/**
|
|
9
|
+
* wow-xiaobao entrypoint.
|
|
10
|
+
*
|
|
11
|
+
* Capabilities mapped from the openclaw-xiaobao plugin to CLI subcommands,
|
|
12
|
+
* plus visit-summary, knowledge-base and quantum KPI stats — all proxied
|
|
13
|
+
* through ai-open → wang-ai-mcp. See README for the full command map.
|
|
14
|
+
*/
|
|
15
|
+
setDefaultResultOrder("ipv4first");
|
|
16
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
17
|
+
runMain(defineCommand({
|
|
18
|
+
meta: {
|
|
19
|
+
name: "wow-xiaobao",
|
|
20
|
+
version: JSON.parse(readFileSync(pkgPath, "utf8")).version,
|
|
21
|
+
description: "旺小宝 CLI — host-agnostic 业务数据 / 问数 / 来访总结 / 知识库 能力 (openclaw-xiaobao plugin 的 CLI 等价物)"
|
|
22
|
+
},
|
|
23
|
+
subCommands: {
|
|
24
|
+
auth: () => import("./auth-JZ34ksvF.mjs").then((m) => m.default),
|
|
25
|
+
project: () => import("./project-CIOk-2wk.mjs").then((m) => m.default),
|
|
26
|
+
consultant: () => import("./consultant-CJ0tH11c.mjs").then((m) => m.default),
|
|
27
|
+
audio: () => import("./audio-BYm3m77S.mjs").then((m) => m.default),
|
|
28
|
+
customer: () => import("./customer-Bpfdfh0U.mjs").then((m) => m.default),
|
|
29
|
+
visit: () => import("./visit-ChOnvK8M.mjs").then((m) => m.default),
|
|
30
|
+
panke: () => import("./panke-_HDbr3xq.mjs").then((m) => m.default),
|
|
31
|
+
focus: () => import("./focus-BNA14pxJ.mjs").then((m) => m.default),
|
|
32
|
+
resistance: () => import("./resistance-CzbXP4Mj.mjs").then((m) => m.default),
|
|
33
|
+
qa: () => import("./qa-CxQZgKRF.mjs").then((m) => m.default),
|
|
34
|
+
kb: () => import("./kb-n84Hl0uL.mjs").then((m) => m.default),
|
|
35
|
+
quantum: () => import("./quantum-BSlMUFk6.mjs").then((m) => m.default),
|
|
36
|
+
follow: () => import("./follow-BYYbUVCh.mjs").then((m) => m.default),
|
|
37
|
+
portrait: () => import("./portrait-BopNLEQX.mjs").then((m) => m.default),
|
|
38
|
+
config: () => import("./config-DipFfMio.mjs").then((m) => m.default),
|
|
39
|
+
beautx: () => import("./beautx-D5y2iytj.mjs").then((m) => m.default),
|
|
40
|
+
date: () => import("./date-C9LQUbfA.mjs").then((m) => m.default),
|
|
41
|
+
admin: () => import("./admin-nr9IIF2K.mjs").then((m) => m.default),
|
|
42
|
+
api: () => import("./api-C1s2co2-.mjs").then((m) => m.default),
|
|
43
|
+
staff: () => import("./staff-DasX8vTs.mjs").then((m) => m.default)
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
46
|
+
//#endregion
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/config/index.ts
|
|
3
|
+
var config_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "config",
|
|
6
|
+
description: "项目配置:客户字段配置(fields,解读 dynamic_tags 标签含义)"
|
|
7
|
+
},
|
|
8
|
+
subCommands: { fields: () => import("./fields-CNQUQ3BI.mjs").then((m) => m.default) }
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { config_default as default };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { n as isAccessTokenValid, r as readToken } from "./token-store-BGLVc825.mjs";
|
|
2
|
+
import { t as parseJsonPreservingIntegers } from "./json-CXENUkN_.mjs";
|
|
3
|
+
import { request } from "node:http";
|
|
4
|
+
import { request as request$1 } from "node:https";
|
|
5
|
+
//#region src/core/api-client.ts
|
|
6
|
+
/**
|
|
7
|
+
* HTTP client for wangxiaobao open API endpoints.
|
|
8
|
+
*
|
|
9
|
+
* Port of openclaw-xiaobao/src/core/api-client.ts. Difference: no
|
|
10
|
+
* workspaceDir param (CLI has a single global token, not per-agent
|
|
11
|
+
* isolation).
|
|
12
|
+
*
|
|
13
|
+
* Adds `Authorization: Bearer <accessToken>` from the platform-managed token
|
|
14
|
+
* file. Never refreshes: no valid token means NOT_AUTHENTICATED.
|
|
15
|
+
*/
|
|
16
|
+
var NotAuthenticatedError = class extends Error {
|
|
17
|
+
code = "NOT_AUTHENTICATED";
|
|
18
|
+
hint = "请在旺喔 App 的连接器里授权或重新连接旺小宝,然后再试。";
|
|
19
|
+
constructor() {
|
|
20
|
+
super("当前未授权旺小宝或令牌已过期");
|
|
21
|
+
this.name = "NotAuthenticatedError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
async function ensureAccessToken() {
|
|
25
|
+
const cached = await readToken();
|
|
26
|
+
if (isAccessTokenValid(cached)) return cached.access_token;
|
|
27
|
+
throw new NotAuthenticatedError();
|
|
28
|
+
}
|
|
29
|
+
function buildUrl(apiBase, path, query) {
|
|
30
|
+
const url = new URL(path, apiBase);
|
|
31
|
+
if (query) for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v);
|
|
32
|
+
return url.toString();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Single HTTP(S) request on Node's native http/https modules with
|
|
36
|
+
* `agent: false` — no connection pool, a fresh TCP connection per request,
|
|
37
|
+
* closed when the response ends (same behavior as curl). This avoids the
|
|
38
|
+
* intermittent "fetch failed" caused by undici's keep-alive pool reusing a
|
|
39
|
+
* connection the server has already half-closed.
|
|
40
|
+
*/
|
|
41
|
+
function requestOnce(url, method, headers, body, timeoutMs) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const u = new URL(url);
|
|
44
|
+
const req = (u.protocol === "http:" ? request : request$1)(u, {
|
|
45
|
+
method,
|
|
46
|
+
headers,
|
|
47
|
+
agent: false
|
|
48
|
+
}, (res) => {
|
|
49
|
+
const chunks = [];
|
|
50
|
+
res.on("data", (c) => chunks.push(c));
|
|
51
|
+
res.on("end", () => resolve({
|
|
52
|
+
status: res.statusCode ?? 0,
|
|
53
|
+
contentType: String(res.headers["content-type"] ?? ""),
|
|
54
|
+
text: Buffer.concat(chunks).toString("utf8")
|
|
55
|
+
}));
|
|
56
|
+
res.on("error", reject);
|
|
57
|
+
});
|
|
58
|
+
req.setTimeout(timeoutMs, () => {
|
|
59
|
+
const e = /* @__PURE__ */ new Error(`request timeout after ${timeoutMs}ms: ${method} ${url}`);
|
|
60
|
+
e.code = "XB_TIMEOUT";
|
|
61
|
+
req.destroy(e);
|
|
62
|
+
});
|
|
63
|
+
req.on("error", reject);
|
|
64
|
+
if (body != null) req.write(body);
|
|
65
|
+
req.end();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Retry transient network errors (ECONNRESET / EPIPE / connection refused…)
|
|
70
|
+
* with backoff 200ms → 400ms → 800ms, max 3 retries. Timeouts and HTTP error
|
|
71
|
+
* responses are NOT retried.
|
|
72
|
+
*/
|
|
73
|
+
async function requestWithRetry(url, method, headers, body, timeoutMs) {
|
|
74
|
+
let lastErr;
|
|
75
|
+
for (let attempt = 0; attempt <= 3; attempt++) {
|
|
76
|
+
if (attempt > 0) await new Promise((r) => setTimeout(r, 200 * 2 ** (attempt - 1)));
|
|
77
|
+
try {
|
|
78
|
+
return await requestOnce(url, method, headers, body, timeoutMs);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
lastErr = err;
|
|
81
|
+
if (err.code === "XB_TIMEOUT") throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw lastErr;
|
|
85
|
+
}
|
|
86
|
+
async function doFetch(url, method, token, opts) {
|
|
87
|
+
const headers = {
|
|
88
|
+
Accept: "application/json, text/event-stream",
|
|
89
|
+
Authorization: `Bearer ${token}`,
|
|
90
|
+
...opts.headers ?? {}
|
|
91
|
+
};
|
|
92
|
+
let body;
|
|
93
|
+
if (opts.body != null && method !== "GET" && method !== "DELETE") if (typeof opts.body === "string") body = opts.body;
|
|
94
|
+
else {
|
|
95
|
+
body = JSON.stringify(opts.body);
|
|
96
|
+
if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
|
97
|
+
}
|
|
98
|
+
const raw = await requestWithRetry(url, method, headers, body, opts.timeoutMs ?? 3e4);
|
|
99
|
+
const parsed = raw.contentType.includes("application/json") && raw.text ? parseJsonPreservingIntegers(raw.text) : raw.text;
|
|
100
|
+
return {
|
|
101
|
+
status: raw.status,
|
|
102
|
+
ok: raw.status >= 200 && raw.status < 300,
|
|
103
|
+
parsed
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Make an authenticated request to the wangxiaobao API.
|
|
108
|
+
*/
|
|
109
|
+
async function xbApiFetch(config, method, path, opts = {}) {
|
|
110
|
+
const result = await doFetch(buildUrl(config.apiBase, path, opts.query), method, await ensureAccessToken(), opts);
|
|
111
|
+
return {
|
|
112
|
+
status: result.status,
|
|
113
|
+
ok: result.ok,
|
|
114
|
+
data: result.parsed
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/core/config.ts
|
|
119
|
+
/**
|
|
120
|
+
* CLI 配置:只有旺小宝业务接口地址。
|
|
121
|
+
*
|
|
122
|
+
* 优先级:--api-base 参数 > 环境变量 XIAOBAO_API_BASE > 默认值。
|
|
123
|
+
* 授权由旺喔平台完成,CLI 不需要授权服务地址和应用凭据。
|
|
124
|
+
*/
|
|
125
|
+
const DEFAULT_API_BASE = "https://open-ai.wangxiaobao.com";
|
|
126
|
+
/** 按优先级解析配置。 */
|
|
127
|
+
function resolveConfig(overrides = {}) {
|
|
128
|
+
return { apiBase: overrides.apiBase || process.env.XIAOBAO_API_BASE || DEFAULT_API_BASE };
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { xbApiFetch as n, resolveConfig as t };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
//#region src/commands/consultant/index.ts
|
|
3
|
+
var consultant_default = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "consultant",
|
|
6
|
+
description: "查询有权限的置业顾问列表"
|
|
7
|
+
},
|
|
8
|
+
subCommands: { list: () => import("./list-Cb2q7k36.mjs").then((m) => m.default) }
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { consultant_default as default };
|