@quq/keline-cli
v0.1.0
Published
客来茵后端查询 CLI(以只读为主,含定向发券写命令),供客户侧 AI Agent 经 Skill 调用
Readme
keline-cli
给客来茵后端接口做薄封装的命令行工具(以只读为主,含定向发券写命令 coupon send)。本文档面向调用方——编排本 CLI 的 Skill / AI Agent 作者——不是安装教程,也不是项目接手者文档(那些在 docs/)。
除 init 外,每条命令执行完毕后 stdout 恰好输出一个 JSON 对象(信封),不含任何日志或提示文本;诊断信息一律走 stderr。init 是全 CLI 唯一面向人的命令,被成功路由后输出人类可读文本而非信封(其命令行解析层的失败——如未知选项——仍走信封,边界见下文「命令速查」的 init 条目)。
契约
输出信封
成功:
{"ok": true, "data": <结果>, "error": null}失败:
{"ok": false, "data": null, "error": {"code": "<稳定错误码>", "message": "<说明>", "details": <诊断信息或 null>}}ok、data、error 三键恒在,缺省侧显式为 null(不是省略该键)。请求帮助(--help)与版本号(--version)同样走这个信封,data 是帮助/版本号文本,不会有裸文本直接出现在 stdout 上。
退出码
| 退出码 | 含义 | 调用方应做的事 |
|---|---|---|
| 0 | 成功 | 正常消费 data |
| 1 | 非用法问题(上游/网络/CLI 内部故障) | 不等于「可重试」,见下方按 error.code 分支 |
| 2 | 用法或配置错误(error.code 恒为 E_USAGE) | 修正调用参数,重试无意义 |
退出码 1 MUST NOT 被当作「可重试」的统一信号:E_AUTH 同样是退出码 1,而它重试无用,唯一补救是更换凭证。调用方 MUST 按 error.code 分支,而不是按退出码统一处置。
稳定错误码
| error.code | 含义 | 调用方应做的事 |
|---|---|---|
| E_USAGE | 调用方用法或配置错误 | 按 error.details 修正参数 |
| E_AUTH | 凭证无效、过期或权限不足 | 更换凭证,重试无用 |
| E_UPSTREAM | 后端返回业务失败 | 依 error.details 中的后端原始码与消息判断,不盲目重试 |
| E_NETWORK | 网络故障或超时 | 可按自身策略重试 |
| E_INTERNAL | CLI 自身未预期的错误 | 重试无用,应上报 |
后端内部错误码不会直接暴露为 error.code——调用方无需了解、也不应依赖后端内部枚举。
配置
两项后端配置(地址与凭证)从固定路径的配置文件读取,唯一来源——CLI 不再读取任何环境变量。
| 项 | 说明 |
|---|---|
| 路径 | $XDG_CONFIG_HOME/keline/config.json;XDG_CONFIG_HOME 未设置、为空串、纯空白,或不是绝对路径时,仅在 HOME 为有效绝对路径时回落为 ~/.config/keline/config.json;两者均无效则路径不可解析(普通命令报 E_USAGE 且 details.path 为 null,init 在任何交互前拒绝执行) |
| 格式 | 顶层为 JSON 对象,字段 baseUrl(后端地址,必填)与 apiKey(后端凭证,必填);字段缺失、为空串、纯空白或非字符串均视同未配置 |
| 前置约定 | 部署前须先运行 keline init——它交互式收集这两项配置、做形态校验后发一次真实请求验证链路可用,通过后才写入配置文件。跑通 init 即代表链路已验证可用 |
配置文件缺失、不可读、内容不合法,或 baseUrl/apiKey 未配置时,命令以 E_USAGE、退出码 2 失败,不发出任何请求,error.message 含指引运行 keline init 的提示。
命令速查
本 CLI 目前共提供 15 条命令,覆盖门店、报表、客户、产品/服务项目、操作单、订单、发券七个维度,以及初始化命令 init。参数、必填性与默认值均以下方各命令的 --help 实测为准;如与本文档不一致,以 --help 为准并请提交 issue。
⚠️ 「返回观察形状」列是后端通常返回的形状,不是 CLI 的保证:本 CLI 对绝大多数命令只承诺透传的忠实性,不承诺 data 的具体结构——后端以成功码返回 null 或对象时会被原样透传,不会被伪造成空数组。调用方应对形状不符做防御。唯一例外是 customer get 与 customer inactive:前者的七键聚合结构、后者的差集结果都是 CLI 自己组装的,可作契约性描述。
| 命令 | 位置参数 | 必填选项 | 可选选项(默认值) | 后端端点 | 返回观察形状 |
|---|---|---|---|---|---|
| keline store list | 无 | 无 | 无 | GET /store/listAll | 数组(后端通常形状,非 CLI 保证) |
| keline report scope | 无 | --month | --store-id(可选,省略即不收窄门店范围) | GET /businessReport/listStoreAndStaff | 数组(门店→员工树节点,后端通常形状,非 CLI 保证) |
| keline report monthly | 无 | --month | --store-id(可选,省略即企业整体) | GET /businessMonthlyReport/getReport | 单条报表对象(后端通常形状,非 CLI 保证) |
| keline report daily | 无 | --date | --store-id(可选,省略即企业整体) | GET /businessDailyReport/getReport | 单条报表对象(后端通常形状,非 CLI 保证) |
| keline report item-order | 无 | --month | --store-id(可选,省略即企业整体)、--page(默认 1)、--size(默认 20,上限 200) | GET /businessReport/pageItemOrder | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline report item-consume | 无 | --month | --store-id(可选,省略即企业整体)、--page(默认 1)、--size(默认 20,上限 200) | GET /businessReport/pageServiceConsume | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline customer list | 无 | 无 | --itinerary(可选,旅程名,取值集合运行时查询)、--keyword(可选,匹配昵称或姓名;手机号按尾号方向匹配——前缀不命中、过短不参与)、--page(默认 1)、--size(默认 20,上限 200) | GET /customer/page | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline customer get | id(必填) | 无 | 无 | GET /customer/${id}、GET /customer-portrait/${id}、GET /customer-portrait/preference/${id}、GET /customer-portrait/efficacy/${id}、GET /customer-portrait/project/${id}、GET /customerServiceRecord/page、GET /order/page | CLI 自有的固定七键聚合对象(profile/tags/preference/efficacy/project/latestService/lastVisitByConsumption),CLI 自身承诺的结构,不是透传形状 |
| keline customer stats | 无 | 无 | 无 | GET /customerStatistics/customerWorkbenches | 统计对象(后端通常形状,非 CLI 保证) |
| keline customer inactive | 无 | --days | --with-last-visit(可选,默认关闭;每位未到店客户至少 2 次额外请求) | GET /customer/page、GET /customerServiceRecord/page、GET /order/page | CLI 自有的差集结果对象(since/total/list),CLI 自身承诺的结构,不是透传形状 |
| keline product list | 无 | --type | --keyword(可选)、--page(默认 1)、--size(默认 20,上限 200) | GET /product/page | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline coupon send | 无 | --body | 无 | POST /activityConfig/addActivity(会在后端创建活动数据,不可撤销;请求超时后须先人工确认活动是否已创建,勿直接重发) | 后端固定返回 data:null(后端通常形状,非 CLI 保证) |
| keline sheet list | 无 | --date | --store-id(可选,范围取决于凭证登录类型,见口径约束 1)、--page(默认 1)、--size(默认 20,上限 200) | GET /operationSheet/page | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline order list | 无 | --date | --order-type(可选,逗号分隔,省略即不按类型筛选)、--store-id(可选,省略即全企业)、--page(默认 1)、--size(默认 20,上限 200) | GET /order/page | 分页外壳对象(后端通常形状,非 CLI 保证) |
| keline init | 无 | 无 | 无 | 无后端业务端点(落盘前会以一次只读请求做活体校验,见下方说明) | 人类可读文本,不受三键信封约束——全 CLI 唯一面向人的命令 |
示例:
keline store list{"ok": true, "data": [{"id": "1234567890123456789", "storeName": "示例门店"}], "error": null}customer get 是唯一带位置参数的命令,且必须来自本 CLI 自身 customer list 的返回结果(见口径约束 7):
keline customer get 1234567890123456789customer inactive:列出「今天减 N 天」至今天(含两端)内没有到店记录的客户(消费口径近似,见口径约束 14):
keline customer inactive --days 30product list:核实用户口述的服务项目/产品名称,取得可用于组装品项券的 id 与卡类型支持范围:
keline product list --type service --keyword 艾灸coupon send:读取一份 JSON 请求体,创建专享礼包活动定向发券(唯一的写命令,见口径约束 15);--body 取文件路径或 -(标准输入):
keline coupon send --body body.jsonbody.json 的最小合法请求体(品项券,服务项目):
{
"activityName": "回店礼",
"startTime": "now",
"customerPhoneList": ["13800000001"],
"giftList": [
{
"productType": 1,
"periodValidity": 30,
"serviceProductList": [{ "id": "2031909916478136322", "num": 1, "cardType": 1 }]
}
]
}一份专享礼包至多一项品项券,全部产品与服务项目合在这一项里;品项券每人固定 1 张,不接受 totalNum,要多次或多盒调整品项的 num;优惠券可用 totalNum 指定每人份数。
辅助请求
提供 --store-id 时,CLI 会先发一次门店存在性校验请求;提供 --itinerary 时,CLI 会先发一次旅程名翻译请求;coupon send 的请求体含品项券时,CLI 会先对其中每个去重后的产品/服务项目 id 各发一次 GET /product/{id} 确认其存在、启用、类型与卡类型受支持。这些请求自身失败时,错误码为 E_AUTH / E_NETWORK / E_UPSTREAM(退出码 1),不会被表述为「参数错误」(E_USAGE);品项确认查询唯有后端业务码 1001 会被判为「品项不存在」(E_USAGE、退出码 2),其余非零码一律 E_UPSTREAM。
调用方 MUST 按 error.code 分支处理,而不是把退出码 1 一律当作「可重试」:E_AUTH 要换凭证,重试无用;E_NETWORK 可重试;E_UPSTREAM 依 details 判断。否则一次后端不可达会被误当成「我传错了门店 id」,反复修改一个本来正确的值;或者一次凭证过期被无人值守的 Agent 无限重试。
口径约束
以下约束是既有决策已认定为「不这么写就会让调用方说出错误事实」的硬约束,不是使用建议。
| # | 约束 | 若缺失会发生什么 |
|---|---|---|
| 1 | sheet list 不提供 --store-id 时,查询范围取决于凭证登录类型:管理员/系统凭证下为全企业,员工凭证会被后端静默收窄为该员工所属门店。 | 调用方按无条件全企业汇报,实际只拿到了单店数据。 |
| 2 | 服务操作单没有任何金额字段,不是开单;「今天谁开单了、多少钱」只能由 order list 回答。 | 调用方拿操作单条数当开单数、拿操作单列表当营收依据上报。 |
| 3 | order list 的金额需由调用方自行累加各条的 paymentAmount,并按 totalElements 判断是否需要翻页;MUST NOT 用日报的营业收入字段(如 incomeAmount)冒充下单金额——该口径不含线上订单支付额。 | 只取第一页即汇报总额,金额静默少算;或拿营业收入冒充下单金额,得到一个看不出异常的错误数字。 |
| 4 | 「到店」存在两种不可混用的口径:customer stats 的 todayArrivalCustomerNumber 只统计「预约且到店」,不含未预约直接进店的客户。 | 把「预约且到店」的人数当作全口径到店人数汇报。 |
| 5 | 品项数据没有「已售卖次数」,只有金额;report item-order 每项仅含品项名与金额。 | 承诺一个后端不提供的能力,向使用者答出一个编造的次数。 |
| 6 | 客户侧的时间维度查询有限:2026-08-18 曾裁定完全不做,2026-09-12 推翻该裁决,接受消费口径近似(ADR-0001)——customer get 的 lastVisitByConsumption 与 customer inactive 只计券核销/门店订单/客户消耗三类服务记录,以及门店/门店手工/券核销三类已支付订单;到店只咨询、回访而未消费的不计入。「近 x 天刚流失」仍不可回答——后端无旅程变更时间的查询出口。 | 把「最近一次消费」说成「最近一次到店」,或把只咨询过的客户列为未到店;或声称能回答「刚流失」。 |
| 7 | customer get <id> 的 id MUST 来自本 CLI 自身 customer list 的返回结果,MUST NOT 使用外部来源的 id——后端存在跨企业越权读取风险。 | 外部来源的 id 会读到别家企业的客户数据。 |
| 8 | report scope 返回的不是企业员工花名册,而是该期间有报表数据的门店与员工。 | 把本月没出业绩的员工当成不存在的员工。 |
| 9 | 「今天谁开单了、多少钱」的订单类型口径是 --order-type 2,3(门店 + 门店手工)。 | 不传类型会把线上订单与券核销一并计入,得到一个口径不符却看不出异常的金额。 |
| 10 | 品项数据要做全局排序或取 top N,必须先翻完所有页——CLI 每页上限 200,totalElements 是判断是否还有下一页的依据。 | 只对第一页排序,会漏掉真正的全局 top。 |
| 11 | 订单返回中,付款金额字段名是 paymentAmount、商品列表字段名是 orderGoodsList,MUST 按原拼写读取。 | 键名写错,拿到 undefined 并当作零。 |
| 12 | sheet list 同样不自动翻页:调用方 MUST 按 totalElements 逐页取回。 | 只取默认第一页,把最多 20 条当成了当天全部操作单。 |
| 13 | order list 只返回已支付订单(状态口径由 CLI 固定,不可调整)。 | 把结果表述为「当天全部订单」,而未支付/已取消/已关闭的并不在内。 |
| 14 | customer inactive 是 CLI 在全量扫描上计算的差集:--days N 的窗口为今天减 N 天至今天、含两端(Asia/Shanghai,共 N + 1 个自然日),扫描期间刚发生的到店可能未被计入;--with-last-visit 的成本为未到店人数 × 2 次以上请求,默认关闭。 | 把「3 天未到店」当成精确实时集合;或对全体客户默认开启补查打出数以千计的请求。 |
| 15 | 发券(coupon send)的手机号与品项 id MUST 来自本 CLI 自身的查询结果(手机号取自 customer list / customer inactive / customer get,品项 id 取自 product list):后端对匹配不到的手机号静默忽略,产品详情端点不按企业过滤。coupon send 是本 CLI唯一的写命令,会在后端创建活动并按发放时间发券,不可撤销;请求超时后 MUST 先人工确认活动是否已创建,MUST NOT 直接重发。定时发放请至少提前一分钟以上,临近时刻请直接用 now。 | 手误的号码没人收到券且无报错;外来的品项 id 生成一张引用别家项目的券;把发券当查询反复重试造成重复发券。 |
| 16 | 服务项目的 serviceCardSupportList(product list 返回项)是该项目支持的卡类型编号数组(1 次卡 / 2 月卡 / 3 季卡 / 4 半年卡 / 5 年卡 / 6 两年卡,如 [1, 2]);品项券中服务项目的 cardType MUST 取自其中。 | 给一个只支持次卡的项目发月卡券,券无法核销。 |
| 17 | 一份专享礼包(coupon send)至多一张品项券,全部产品与服务项目合在这一张里,每人固定一张;要让客户得到多次或多盒,调的是券内品项的 num,不是发多张品项券(品项券不接受 totalNum)。优惠券不受此限,可用 totalNum 指定每人份数。 | 在对话里承诺「每人发 3 张艾灸券」,请求被 E_USAGE 拒绝后才发现兑现不了;或把多个品项拆成多张品项券,同样被拒。 |
组合示例
以下跨命令的编排、翻页与业务汇总均由调用方(Skill)完成,CLI 不做跨命令的循环或汇总(见口径约束 3、10、12)。
唯一的例外是 customer get 与 customer inactive:前者内部聚合的八个来源(profile/tags/preference/efficacy/project/latestService/lastVisitServiceRecord/lastVisitOrder,见命令速查)是 CLI 自己组装的固定结构,不是「Skill 该做的编排」——档案被后端拆在多个端点上这件事本身就是后端知识,而不是一项业务决策,因此收在 CLI 内一次性做掉;调用方不需要、也不应对这些请求自行重复编排。后者的差集计算同理:「哪些表证明客户到过店」也是后端知识,不留给调用方拼装(见口径约束 14)。
今日经营总结(report daily + sheet list + order list,注意操作单没有金额,金额来自订单):
keline report daily --date 2026-08-18
keline sheet list --date 2026-08-18
keline order list --date 2026-08-18 --order-type 2,3⚠️ 以上三行只演示单次请求的命令形式,不是可直接得出总结的完整流程。 sheet list 与 order list 都不自动翻页(口径约束 3、12):取回响应后必须读 totalElements,若大于本次已取回的条数,递增 --page 继续请求同一条命令,直到累计取回条数达到 totalElements 为止,再汇总操作单条数、累加订单的 paymentAmount。当天记录不超过一页(默认 20 条)时,以上三行恰好就是完整流程;一旦超过,只跑一次就当作总结会静默漏报操作单、少算订单金额——这正是本文档要防止的那类失效,示例本身不能带头犯。
各门店月度经营对比(先取门店列表,再逐店查询——CLI 没有「一次返回各门店多条」的接口):
keline store list
keline report monthly --month 2026-08 --store-id 1234567890123456789按旅程排查客户并取档案(id 必须来自 customer list 的返回结果):
keline customer list --itinerary 流失 --keyword 138
keline customer get 1234567890123456789customer list 同样不自动翻页(默认每页 20 条):如需该筛选条件下的完整匹配列表而不只是前几条,需按 totalElements 用 --page 翻页取全,做法与上面订单/操作单的翻页相同。
