@manycore/kjl-cli 0.0.0-stage → 0.2.8

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.
Files changed (133) hide show
  1. package/README.md +482 -2
  2. package/dist/api/client.d.ts +58 -0
  3. package/dist/api/client.js +418 -0
  4. package/dist/api/client.js.map +1 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.js +543 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/auth.d.ts +25 -0
  9. package/dist/commands/auth.js +165 -0
  10. package/dist/commands/auth.js.map +1 -0
  11. package/dist/commands/config.d.ts +21 -0
  12. package/dist/commands/config.js +306 -0
  13. package/dist/commands/config.js.map +1 -0
  14. package/dist/commands/init.d.ts +27 -0
  15. package/dist/commands/init.js +155 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/login.d.ts +21 -0
  18. package/dist/commands/login.js +174 -0
  19. package/dist/commands/login.js.map +1 -0
  20. package/dist/commands/page.d.ts +37 -0
  21. package/dist/commands/page.js +697 -0
  22. package/dist/commands/page.js.map +1 -0
  23. package/dist/commands/shared.d.ts +26 -0
  24. package/dist/commands/shared.js +209 -0
  25. package/dist/commands/shared.js.map +1 -0
  26. package/dist/commands/skill.d.ts +15 -0
  27. package/dist/commands/skill.js +332 -0
  28. package/dist/commands/skill.js.map +1 -0
  29. package/dist/commands/skillPrompts.d.ts +29 -0
  30. package/dist/commands/skillPrompts.js +211 -0
  31. package/dist/commands/skillPrompts.js.map +1 -0
  32. package/dist/commands/tools.d.ts +6 -0
  33. package/dist/commands/tools.js +209 -0
  34. package/dist/commands/tools.js.map +1 -0
  35. package/dist/commands/update.d.ts +5 -0
  36. package/dist/commands/update.js +87 -0
  37. package/dist/commands/update.js.map +1 -0
  38. package/dist/core/agentBrowser.d.ts +97 -0
  39. package/dist/core/agentBrowser.js +468 -0
  40. package/dist/core/agentBrowser.js.map +1 -0
  41. package/dist/core/browser.d.ts +139 -0
  42. package/dist/core/browser.js +658 -0
  43. package/dist/core/browser.js.map +1 -0
  44. package/dist/core/chromeInstaller.d.ts +14 -0
  45. package/dist/core/chromeInstaller.js +155 -0
  46. package/dist/core/chromeInstaller.js.map +1 -0
  47. package/dist/core/config.d.ts +112 -0
  48. package/dist/core/config.js +244 -0
  49. package/dist/core/config.js.map +1 -0
  50. package/dist/core/configSchema.d.ts +84 -0
  51. package/dist/core/configSchema.js +306 -0
  52. package/dist/core/configSchema.js.map +1 -0
  53. package/dist/core/configWriter.d.ts +17 -0
  54. package/dist/core/configWriter.js +220 -0
  55. package/dist/core/configWriter.js.map +1 -0
  56. package/dist/core/errors.d.ts +48 -0
  57. package/dist/core/errors.js +115 -0
  58. package/dist/core/errors.js.map +1 -0
  59. package/dist/core/freeModeling.d.ts +47 -0
  60. package/dist/core/freeModeling.js +790 -0
  61. package/dist/core/freeModeling.js.map +1 -0
  62. package/dist/core/freeformModelingBridge.d.ts +56 -0
  63. package/dist/core/freeformModelingBridge.js +716 -0
  64. package/dist/core/freeformModelingBridge.js.map +1 -0
  65. package/dist/core/log.d.ts +15 -0
  66. package/dist/core/log.js +184 -0
  67. package/dist/core/log.js.map +1 -0
  68. package/dist/core/packageInfo.d.ts +8 -0
  69. package/dist/core/packageInfo.js +31 -0
  70. package/dist/core/packageInfo.js.map +1 -0
  71. package/dist/core/selfUpdate.d.ts +34 -0
  72. package/dist/core/selfUpdate.js +514 -0
  73. package/dist/core/selfUpdate.js.map +1 -0
  74. package/dist/core/skillInstaller.d.ts +37 -0
  75. package/dist/core/skillInstaller.js +491 -0
  76. package/dist/core/skillInstaller.js.map +1 -0
  77. package/dist/core/toolLoad.d.ts +22 -0
  78. package/dist/core/toolLoad.js +53 -0
  79. package/dist/core/toolLoad.js.map +1 -0
  80. package/dist/core/tree.d.ts +60 -0
  81. package/dist/core/tree.js +398 -0
  82. package/dist/core/tree.js.map +1 -0
  83. package/dist/core/types.d.ts +135 -0
  84. package/dist/core/types.js +13 -0
  85. package/dist/core/types.js.map +1 -0
  86. package/dist/core/updateCheckWorker.d.ts +1 -0
  87. package/dist/core/updateCheckWorker.js +59 -0
  88. package/dist/core/updateCheckWorker.js.map +1 -0
  89. package/dist/core/updateNotifier.d.ts +36 -0
  90. package/dist/core/updateNotifier.js +366 -0
  91. package/dist/core/updateNotifier.js.map +1 -0
  92. package/dist/core/updateProcess.d.ts +15 -0
  93. package/dist/core/updateProcess.js +153 -0
  94. package/dist/core/updateProcess.js.map +1 -0
  95. package/dist/sources/freeformModelingSource.d.ts +51 -0
  96. package/dist/sources/freeformModelingSource.js +568 -0
  97. package/dist/sources/freeformModelingSource.js.map +1 -0
  98. package/dist/sources/localToolSource.d.ts +36 -0
  99. package/dist/sources/localToolSource.js +426 -0
  100. package/dist/sources/localToolSource.js.map +1 -0
  101. package/dist/sources/pageSource.d.ts +28 -0
  102. package/dist/sources/pageSource.js +212 -0
  103. package/dist/sources/pageSource.js.map +1 -0
  104. package/dist/tools/personal-upload/create-texture.tool.json +78 -0
  105. package/dist/tools/personal-upload/index.d.ts +9 -0
  106. package/dist/tools/personal-upload/index.js +79 -0
  107. package/dist/tools/personal-upload/index.js.map +1 -0
  108. package/dist/tools/personal-upload/parse-model.tool.json +50 -0
  109. package/dist/tools/personal-upload/prepare.tool.json +39 -0
  110. package/dist/tools/personal-upload/product-upload-contracts.d.ts +56 -0
  111. package/dist/tools/personal-upload/product-upload-contracts.js +12 -0
  112. package/dist/tools/personal-upload/product-upload-contracts.js.map +1 -0
  113. package/dist/tools/personal-upload/product-upload-errors.d.ts +10 -0
  114. package/dist/tools/personal-upload/product-upload-errors.js +115 -0
  115. package/dist/tools/personal-upload/product-upload-errors.js.map +1 -0
  116. package/dist/tools/personal-upload/product-upload-service.d.ts +42 -0
  117. package/dist/tools/personal-upload/product-upload-service.js +414 -0
  118. package/dist/tools/personal-upload/product-upload-service.js.map +1 -0
  119. package/dist/tools/personal-upload/product-upload-transport.d.ts +22 -0
  120. package/dist/tools/personal-upload/product-upload-transport.js +122 -0
  121. package/dist/tools/personal-upload/product-upload-transport.js.map +1 -0
  122. package/dist/tools/personal-upload/product-upload-validation.d.ts +3 -0
  123. package/dist/tools/personal-upload/product-upload-validation.js +32 -0
  124. package/dist/tools/personal-upload/product-upload-validation.js.map +1 -0
  125. package/dist/tools/personal-upload/submit-model.tool.json +53 -0
  126. package/dist/tools/personal-upload/upload-file/index.d.ts +47 -0
  127. package/dist/tools/personal-upload/upload-file/index.js +180 -0
  128. package/dist/tools/personal-upload/upload-file/index.js.map +1 -0
  129. package/dist/tools/personal-upload/upload-file/tool.json +59 -0
  130. package/package.json +57 -4
  131. package/skills/kjl-cli/SKILL.md +149 -0
  132. package/skills/kjl-cli-global-guide/SKILL.md +60 -0
  133. package/skills/kujiale-product-retrieval-selection/SKILL.md +40 -0
package/README.md CHANGED
@@ -1,3 +1,483 @@
1
- # Temporary Holding Version
1
+ # kjl-cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Kujiale Agent CLI — 一个命令面,覆盖三条数据面。
4
+
5
+ ```
6
+ kjl-cli init 复用 Chrome 149+,不存在时自动下载兼容版本
7
+ kjl-cli skill install 安装随 CLI 发布的全部 skill
8
+ kjl-cli auth status|login|logout 查询、建立或清除浏览器 profile 中的登录态
9
+ kjl-cli page open|status|close 管理 CLI 自己持有的浏览器
10
+ kjl-cli tools <path...> 调用任意数据面的能力
11
+ kjl-cli config list|get|set|unset 查看与修改配置
12
+ kjl-cli update [--check | --dry-run] 检查版本或更新当前全局安装
13
+ ```
14
+
15
+ `kjl-cli tools` **始终存在**。某个来源没连上时,它下面的能力为空,因此 AI 能区分
16
+ 「没有这个能力」和「这个能力当前不可用」。
17
+
18
+ ## 检查版本与更新
19
+
20
+ ```bash
21
+ kjl-cli --version # 当前包版本,不联网
22
+ kjl-cli update --check # 查询最新版本与 Node.js 兼容性,不安装
23
+ kjl-cli update --dry-run # 查看目标版本、安装位置和执行计划,不安装
24
+ kjl-cli update # 更新当前全局安装,并验证更新结果
25
+ ```
26
+
27
+ 支持 npm、pnpm 和 Yarn Classic(1.x)的全局安装。CLI 会核对当前包的真实安装路径,
28
+ 使用管理该安装的包管理器查询和安装精确版本。不会根据当前项目的 `packageManager`
29
+ 或机器上是否存在某个包管理器来选择安装方式。源码、本地依赖、开发链接及无法确认归属的
30
+ 安装不会自动更新;此时请通过原安装方式手动更新。pnpm 支持传统全局布局和新版单包隔离
31
+ 安装;多个包共享同一隔离环境的分组安装需手动更新。
32
+
33
+ registry、认证、代理和证书由原包管理器处理。更新命令不指定 registry,不切换源,
34
+ 也不绕过私有镜像。查询和安装使用相同的全局配置上下文,不读取调用目录的项目配置。
35
+ 网络、认证、权限或包管理器错误会返回失败,不会被解释成“已是最新”。当前版本高于
36
+ 发布渠道版本时保留当前版本;目标版本不兼容当前 Node.js 时停止安装。
37
+
38
+ 更新使用当前包 package.json 中的实际包名:内网发行包为 `@qunhe/kjl-cli`,公开发行包为
39
+ `@manycore/kjl-cli`,两者都使用 `kjl-cli` 命令。更新范围是当前发行包及其声明的 npm 依赖,
40
+ 不会在两个 scope 之间切换。包内 `skills/` 随包更新,已复制到
41
+ Agent 或项目目录的 skill 不会同步;需要时再次执行 `skill install`。更新不会调用 `init`、
42
+ 下载 Chrome、关闭浏览器或重启造型 bridge。包管理器仍按自身策略处理依赖的安装脚本。
43
+
44
+ `update` 系列命令的 stdout 始终是一个 JSON envelope,`--check` 发现新版本仍返回成功,
45
+ 由 `data.updateAvailable` 表示是否存在更高版本,`data.nodeCompatible` 表示 Node.js 是否兼容。
46
+ 没有更高兼容版本时,`data.plan` 为 `null`。`--check` 与 `--dry-run` 互斥。安装失败不会
47
+ 承诺回滚;可用原包管理器重新安装所需版本。
48
+
49
+ 普通交互终端启用低频版本检查:后台检查成功后缓存 24 小时,失败后退避重试,
50
+ 发现更新只在 stderr 提醒,不自动安装,也不等待网络后再执行业务命令。
51
+ CI、非交互调用、帮助、版本查询和显式更新命令不会触发自动检查。关闭自动检查:
52
+
53
+ ```bash
54
+ export KJL_NO_UPDATE_CHECK=1
55
+ ```
56
+
57
+ 显式 `update --check` 和 `update` 不受该环境变量影响。
58
+
59
+ ## 安装 Agent Skill
60
+
61
+ `kjl-cli skill install` 将当前 CLI 包内 `skills/` 下的全部 skill 复制到指定位置。
62
+ 每个包含 `SKILL.md` 的一级子目录是一份 skill,按原目录名独立安装,并完整保留辅助资源。
63
+ 安装过程离线执行,所有 skill 随 CLI 的 npm 包一同发布;以后向 `skills/` 添加新的 skill
64
+ 并发布 CLI,即会自动纳入安装。
65
+
66
+ 在终端执行下面的命令,按菜单完成安装:
67
+
68
+ ```bash
69
+ kjl-cli skill install
70
+ ```
71
+
72
+ 第一步选择“当前用户”“当前项目”或“自定义路径”。选择当前用户或当前项目后,
73
+ 用方向键移动、空格勾选一个或多个 Agent,回车确认。支持 Codex、Claude Code、Cursor、
74
+ Gemini CLI、GitHub Copilot、OpenCode。选择自定义路径后,直接输入目录。
75
+ 单份 skill 时 Agent 列表显示完整最终路径,多份时显示 skills 父目录及数量;
76
+ 安装确认页会展开全部 skill 的名称和最终路径。
77
+ 自定义路径填写 skills 父目录,支持 `~`,安装器会按每份 skill 的目录名创建子目录。
78
+ 同一客户端无需逐个选择 skill;多个客户端共享同一目录时,每份 skill 只复制一次。
79
+ 内容冲突时会询问是否整目录替换,默认保留原内容。按 Ctrl+C 可取消。
80
+
81
+ 脚本和 Agent 可以通过参数指定目标,直接安装:
82
+
83
+ ```bash
84
+ kjl-cli skill install --project # 全部安装到当前目录的 .agents/skills/
85
+ kjl-cli skill install --agent claude-code # 全部安装到当前目录的 .claude/skills/
86
+ kjl-cli skill install --agent codex --global # 全部安装到用户目录的 .agents/skills/
87
+ kjl-cli skill install --dir /path/to/skills # 全部安装到自定义 skills 父目录
88
+ kjl-cli skill install --dir /path/to/skills --force # 按各 skill 子目录替换已有内容
89
+ ```
90
+
91
+ 项目目录以命令执行目录为准。参数模式默认安装到项目目录,`--agent` 选择路径预设,
92
+ `--global` 切换到用户全局目录:
93
+
94
+ | `--agent` | 项目 skills 目录 | 默认全局 skills 目录 |
95
+ |---|---|---|
96
+ | `codex` | `.agents/skills` | `~/.agents/skills` |
97
+ | `claude-code` | `.claude/skills` | `~/.claude/skills` |
98
+ | `cursor` | `.agents/skills` | `~/.cursor/skills` |
99
+ | `gemini-cli` | `.agents/skills` | `~/.gemini/skills` |
100
+ | `github-copilot` | `.agents/skills` | `~/.copilot/skills` |
101
+ | `opencode` | `.agents/skills` | `~/.config/opencode/skills` |
102
+
103
+ 不指定 Agent 时使用 `.agents/skills`,单独传 `--global` 使用 `~/.agents/skills`。
104
+ Claude Code 的全局目录遵循 `CLAUDE_CONFIG_DIR`,OpenCode 的全局目录遵循 `XDG_CONFIG_HOME`。
105
+ 参数模式指定一个 skills 父目录;交互模式支持多选位置,两种方式都会安装全部包内 skill。
106
+
107
+ 无交互终端时,需传入 `--project`、`--agent`、`--global` 或 `--dir` 指定目标,
108
+ 否则命令返回 `INVALID_ARGUMENT`,不会自动选择位置。交互安装只显示菜单、安装完成或失败提示,
109
+ 不追加 JSON,stdout 为空;按 Ctrl+C 取消,退出码为 130。
110
+ 通过参数指定目标时,stdout 保持一个 JSON envelope,供脚本或 Agent 解析。
111
+
112
+ `--project` 与 `--global` 互斥;`--dir` 与 `--agent`、`--project`、`--global` 互斥。
113
+ 每个目标的完整内容一致时返回 `status: "reused"`;首次安装返回 `"installed"`。
114
+ 参数模式下内容不同会报告冲突;确认替换后加 `--force`,成功返回 `"updated"`。
115
+ **`--force` 会逐个替换包内 skill 对应的子目录,包括删除各子目录中的用户修改和添加文件。**
116
+ 目标父目录下的其他 skill 会保留。
117
+ 最终目标若是普通文件或符号链接,需要先由用户处理,`--force` 也不会覆盖它。
118
+
119
+ 升级 CLI 后,再次执行相同命令并按需加 `--force`,即可更新已安装的 skill。
120
+ 参数模式只有一个安装结果时,JSON envelope 中的 `data.path` 是实际安装目录的绝对路径;
121
+ 多个结果时,`data.results` 列出每份 skill 的实际名称、状态和完整路径。
122
+ 交互批量安装中途失败或取消时,会显示已完成与未完成的路径;已完成的目录会保留,
123
+ 再次安装可以复用。若替换失败且无法自动恢复,会显示备份目录,供用户恢复原内容。
124
+
125
+ skill 固化了完整前置流程:检测并安装 CLI、执行幂等 `init` 准备兼容 Chrome、通过
126
+ `auth status` / `auth login` 确认登录态,再打开空白方案或按方案 ID、URL 执行 `page open`。
127
+ 同时记录 `freeform_modeling` 和 `personal_upload` domain 的能力前置条件。
128
+
129
+ ## 三条数据面
130
+
131
+ | 数据面 | 能力提供方 | 连接方式 |
132
+ |---|---|---|
133
+ | `page` | 酷家乐设计工具页面,经 `web-mcp-plugin` 注册到浏览器 WebMCP | CLI 经 agent-browser 控制浏览器调用 |
134
+ | `freeform_modeling` | 自由造型 MCP Server(`freeform-modeling-mcp`) | CLI 按需启动,作为 MCP client 走 stdio |
135
+ | `local` | 随 npm 包发布的本地工具声明和模块 | 发现阶段只读 `tool.json`,调用时才加载对应模块 |
136
+
137
+ 三条面**并存**:各自独立探测、独立读取契约、独立失效。任意一条不可达都不阻塞其余两条。
138
+ 旧的远端 `api` 契约发现来源已经删除;本地工具需要后端数据时直接复用浏览器登录态发起
139
+ 目标明确的 HTTP 请求,不再依赖 `/_agent/contract`。
140
+
141
+ ## 契约与命令树
142
+
143
+ 页面与酷大师能力由运行时提供;本地能力由包内 `tool.json` 声明。三者统一组装成一棵树:
144
+
145
+ ```
146
+ kjl-cli tools kam_floorplan create_room --params '{"name":"主卧"}'
147
+ ```
148
+
149
+ 第一版各来源按 `[moduleId, toolName]` 两段填写。路径变长不是破坏性变更,命令形状不变。
150
+ 能力发现采用两级输出,避免一次返回所有工具 schema:
151
+
152
+ ```bash
153
+ kjl-cli tools --list # 只列 domain、工具数和来源
154
+ kjl-cli tools kam_floorplan --list # 只列该 domain 的工具
155
+ kjl-cli tools kam_floorplan --list --json # 该 domain 的完整 schema
156
+ ```
157
+
158
+ 本地工具声明必须包含 `path`、`description`、`inputSchema`、`outputSchema` 和
159
+ `module.entry/export`。扫描阶段不加载模块;重复命令路径、非法 schema、越界模块路径或
160
+ 缺失模块会让 `local` 来源显式显示为 unavailable,而不是静默覆盖。
161
+
162
+ ## 浏览器生命周期
163
+
164
+ 首次使用先准备已验证的 Chrome 版本:
165
+
166
+ ```bash
167
+ kjl-cli init
168
+ ```
169
+
170
+ 命令通过项目捆绑的 `agent-browser` 检查其实际会选择的浏览器。用户机器上已有 Chrome 149
171
+ 或更高版本时直接复用;未发现浏览器或版本低于 149 时,由锁定的 `playwright-core 1.61.1`
172
+ 下载 Chrome for Testing `149.0.7827.55` 作为兼容回退,并注册到 agent-browser 的优先缓存后
173
+ 再次校验。stdout 仍只输出一个 JSON envelope,其中 `status` 为 `reused` 或 `installed`,并
174
+ 包含支持范围 `{ "min": 149, "max": null }`、实际版本与可执行文件路径。
175
+
176
+ 最低版本 149 以 WebMCP 的[浏览器实现状态](https://github.com/webmachinelearning/webmcp/blob/main/implementation-status.md#chrome)为准:Chrome 149 是首个公开 Origin Trial 版本。更早的实现依赖实验 Feature Flag,不作为 KJL 的正式兼容基线。`agent-browser 0.39.1` 在自己启动的 Chrome 上会默认加入 `WebMCPTesting` 和 `DevToolsWebMCPSupport`,但外接浏览器仍需在启动时启用,因此不能据此降低最低版本。
177
+
178
+ 窗口常驻,直到 `kjl-cli page close` 或用户手动关闭。因此「浏览器还在、daemon 没了」是**常态**而非异常,启动时探测三态:
179
+
180
+ | 状态 | 行为 |
181
+ |---|---|
182
+ | `absent` | 启动新浏览器 |
183
+ | `detached` | **附着**(`--cdp` 重新接管),不重启 —— 孤儿仍持有 profile 锁,重启必然失败 |
184
+ | `running` | 直接复用 |
185
+
186
+ 两个必须知道的 agent-browser 行为(已实测):
187
+
188
+ - **普通命令必须带全启动 flags**(`globalFlags()` 现在等同于 `launchFlags()`)。
189
+ agent-browser 会比较当前选项集与启动时的记录,**不一致就静默重启 Chrome 成
190
+ headless + 一次性 `/tmp` profile**:没有窗口、登录态写进会被丢弃的目录,而命令仍
191
+ 报成功。这正是此前 `kjl-cli auth login` 不弹窗的原因。
192
+ (曾记录的「`--profile` 只能在启动时传」是错的,那是在孤儿进程仍持 profile 锁时
193
+ 观察到的现象,与 flags 无关。)
194
+ - **`session info` 必须带 `--namespace`**,否则会把运行中的会话报成 inactive。
195
+ - **`close` 只关 daemon 自己启动的浏览器**。附着来的浏览器会存活,所以 `page close`
196
+ 在优雅关闭后仍会校验端口,必要时终止该进程,避免 profile 被锁死。
197
+ - **没有 `DISPLAY` 时不再静默降级**:headed 配置下如果既无 `DISPLAY` 也无
198
+ `WAYLAND_DISPLAY`,CLI 直接报 `BROWSER_UNAVAILABLE` 并提示,而不是悄悄起一个
199
+ headless 浏览器。
200
+
201
+ ## 凭据
202
+
203
+ 登录态由**浏览器 profile** 持有,CLI 不保存凭据。后端接口调用时按 RFC 6265 过滤
204
+ cookie 后仅在该次请求内使用:
205
+
206
+ - 不落盘、不跨请求缓存、不进 argv、不进日志
207
+ - 实测:后端收到 `sessionid=...`,而 CLI 的 config/state 目录中不存在该值
208
+ - `kjl-cli auth status` 只读取当前浏览器状态和 URL,不启动或导航浏览器。它返回
209
+ `logged-in`、`login-required` 或 `unknown`;浏览器未运行、已脱管或停在空白页时无法安全验证
210
+ 持久 profile,因此返回 `unknown`,调用方应继续执行 `auth login`。
211
+ - `kjl-cli auth logout` 会先关闭该 profile 对应的浏览器,再删除整个专用 profile;
212
+ 下一次 `auth login` 会创建全新 profile。传入自定义 `--profile-dir` 时,只清理该目录,
213
+ 且会拒绝 home、工作目录、符号链接及无法识别的普通目录
214
+
215
+ ```bash
216
+ kjl-cli auth status
217
+ kjl-cli auth login
218
+ ```
219
+
220
+ `auth status` 是无副作用的 URL 启发式检查:运行中的浏览器落在 `signin`、`login`、
221
+ `passport` 或 `sso` 页面时视为需要登录,正常页面视为已登录。服务端会话在页面尚未跳转前失效时,
222
+ 最终仍以 `page open` 后是否跳到登录页为准。
223
+
224
+ ## 已知限制
225
+
226
+ - **Chrome 最低兼容版本为 149,不设置上限**:`init` 会复用 agent-browser 在用户机器上发现的
227
+ Chrome 149 或更高版本;未发现或低于 149 时安装 Chrome for Testing 149 作为回退。高于当前
228
+ 实机验证版本的 Chrome 仍会直接复用,其兼容性依赖 agent-browser 和 Chrome CDP 的向后兼容。
229
+ 持久 profile 场景要求每条普通命令重复完整启动 flags,避免 agent-browser 因选项集变化静默
230
+ 切换为临时 profile。详见 `.trellis/spec/cli/agent-browser.md`。
231
+ - **动态注册感知**:页面中途注销/新增工具时命令树不会自动更新。`tools` 在找不到
232
+ 命令时会重拉一次契约再失败,这是当前唯一的兜底。
233
+ - **统一错误码**:页面侧参数校验与机器可读错误原因尚未约定;失败格式散在各 tool 内。
234
+ - **契约路径细化**:多层层级的完整方案待定。
235
+
236
+ ## `freeform_modeling` 页面能力
237
+
238
+ 每次 `kjl-cli page open` 都会在导航前自动启动或复用独立的持久 WebSocket bridge,并在
239
+ 结果中返回实际 `loadedUrl`、`bridgeUrl`、`serverInitialized` 和 `pageConnected`,无需额外
240
+ 启动参数。后续短命令只关闭自己的 stdio MCP client,不会关闭 bridge,
241
+ `kjl-cli page close` 则会关闭 KJL 自己启动的 bridge。
242
+
243
+ `freeform_modeling` 下有两个由 CLI 自己实现的页面切换工具;freeform-modeling MCP source 可用时,它们始终可发现:
244
+
245
+ ```bash
246
+ kjl-cli tools freeform_modeling enter_freeform_modeling
247
+ kjl-cli tools freeform_modeling exit_freeform_modeling
248
+ ```
249
+
250
+ 必须先通过 `kjl-cli page open` 打开设计工具,否则这两个调用会直接失败。`enter_freeform_modeling`
251
+ 在 CLI 内完成“行业库 > 自由造型”的页面操作,并以 bridge 报告 `pageConnected: true` 作为成功条件;
252
+ `exit_freeform_modeling` 点击“完成”,并以页面客户端从 bridge 断开作为成功条件。两者均为幂等调用,AI
253
+ 不需要也不应再通过外部浏览器 skill 执行这两个页面动作。
254
+
255
+ `kjl-cli tools freeform_modeling --list --json` 中出现其他自由造型工具只表示 MCP 服务已注册能力,**不表示当前
256
+ 页面上下文可以调用**。普通建模工具仍要求 freeform_modeling source 同时报告 `state: "ready"`、
257
+ `connection.serverInitialized: true` 和 `connection.pageConnected: true`;调用等待连接超时时返回
258
+ `FREEFORM_MODELING_PAGE_NOT_CONNECTED`,并携带 bridge 和当前页面 URL。单个自由造型制作完成后,先将
259
+ 结果组合/成组,再调用 `exit_freeform_modeling` 返回方案。
260
+
261
+ ## 个人模型与材质上传
262
+
263
+ 包内提供五个 `personal_upload` 本地工具(命令名不是 `personnal_upload`):
264
+
265
+ ```text
266
+ personal_upload prepare
267
+ personal_upload upload_file
268
+ personal_upload parse_model
269
+ personal_upload submit_model
270
+ personal_upload create_texture
271
+ ```
272
+
273
+ `personal_upload` **必须在登录后使用**。先执行 `kjl-cli auth status`;若不是
274
+ `logged-in`,执行 `kjl-cli auth login` 建立专用浏览器 profile 登录态。登录确认有效后,先用
275
+ `kjl-cli tools --list` 确认 `personal_upload` domain 存在,再用
276
+ `kjl-cli tools personal_upload --list` 查看上述命令和 schema。上传接口默认使用
277
+ `baseUrl`;如部署环境要求独立网关,可继续用既有 `KJL_API_BASE_URL` 覆盖请求
278
+ base URL,但它不再承担远端工具发现。
279
+
280
+ ### 模型流程
281
+
282
+ 1. `prepare`:传 `{"type":"model","fileName":"文件名.zip"}`。`fileName` 只能是文件名,
283
+ 不能是完整路径;返回实时真分类、摆放位置、混淆任务号和一次性 STS 数据。
284
+ 2. `upload_file`:传完整本地路径与第 1 步的 `upload` 数据。**只能用 `--params -`
285
+ 从标准输入传递敏感运行期数据**,禁止把 STS 数据放进命令行参数、临时参数文件、
286
+ 日志、报告或长期存储:
287
+
288
+ ```bash
289
+ kjl-cli tools personal_upload upload_file --params -
290
+ ```
291
+
292
+ 调用方应在内存中把 JSON 写入该进程标准输入。工具使用锁定版本的 `ali-oss`
293
+ 官方 Node SDK 上传,并且只返回任务号、远端对象键、文件大小和 ETag 等非敏感数据。
294
+ 3. `parse_model`:只传同一 `uploadTaskId`。状态 0 触发一次解析,状态 1 每秒轮询;
295
+ 单次命令总预算 30 秒,超时返回 `processing`,之后继续用同一任务号恢复,不得重新
296
+ prepare 或重复触发。
297
+ 4. 解析成功后必须实际读取返回的 `previewImg` 图像,结合文件名和文件信息,从第 1 步
298
+ 实时 `formData` 中精确选择 `name`、`prodCatId`、`location`。
299
+ 5. `submit_model`:提交上述字段,成功返回 `productId`(混淆商品编号)。该写接口没有创建结果查询能力;网络结果不确定时不得
300
+ 自动重试,先按名称查询个人模型库,确认没有创建后才能再次提交。
301
+
302
+ ### 图片材质流程
303
+
304
+ 1. `prepare` 传 `{"type":"texture","fileName":"文件名.png"}`。
305
+ 2. 按模型流程第 2 步调用 `upload_file`。
306
+ 3. 首次调用 `create_texture` 传完整字段:
307
+ `uploadTaskId/name/length/width/physicalCatId/prodCatId`。两个分类编号必须来自本次
308
+ `prepare` 的实时 `formData`;尺寸是 10~5000 的整数毫米,不能把图片像素直接当毫米。
309
+ 4. 状态 1 或 `POLL_TIMEOUT` 后只传同一 `uploadTaskId` 恢复。状态 0 只排队一次,
310
+ 状态 2 返回 `productId`(混淆商品编号),状态 3 明确失败且不会自动重排。
311
+
312
+ 上传任务对外只使用混淆字符串 `productId` 表达商品编号,并在工具的输出声明中列出该字段;拒绝数字、纯数字字符串、空串和空白值。
313
+ 为兼容接口逐步升级,内部仍读取旧响应的 `brandGoodId`,但不把旧字段输出给调用方;
314
+ 同时出现新旧字段时,以 `productId` 为准,不改变编号内容或自行加解密。
315
+
316
+ 所有编排请求都会在实际发送前重新读取浏览器 Cookie,按目标域、路径和 secure 属性过滤;
317
+ Cookie 不落盘、不跨请求缓存、不进入命令行或错误信息。业务错误会作为 CLI 失败返回,
318
+ 不会被包装成成功结果。
319
+
320
+ ## 页面侧依赖(不由本仓库实现)
321
+
322
+ - **`web-mcp-plugin` 必须把 WebMCP 入口改成双探测**:
323
+ `document.modelContext || navigator.modelContext`。
324
+ 实测 Chrome **149 上只有 `navigator`**、**153 上只有 `document`**,两版本正好相反,
325
+ 只查任意一个都必然在另一版本上失败。当前实现写死了 `document`,在兼容范围下限 149 上
326
+ 会抛 `WebMCP is unavailable`。参考实现见 `test/fixtures/design-page.html`。
327
+
328
+ ## 开发
329
+
330
+ 包管理用 **pnpm**(`packageManager` 已锁定 `pnpm@11.22.0`)。仓库里只有 `pnpm-lock.yaml`,
331
+ 没有 `package-lock.json`——**不要用 `npm install`**,它不会读 `pnpm-workspace.yaml`,
332
+ 会另建一棵不符合本项目依赖布局的 `node_modules`。
333
+
334
+ ```bash
335
+ pnpm install # 首次;CI 用 pnpm install --frozen-lockfile
336
+ pnpm run build
337
+ pnpm test # 单元测试(含 55 条个人上传迁移用例)
338
+ pnpm run test:e2e # 端到端(需要 agent-browser + Chromium)
339
+ pnpm run kjl-cli --help
340
+ ```
341
+
342
+ > `pnpm-workspace.yaml` 里的 `allowBuilds: { tldjs: false }` 是**必需**的,不是可选配置:
343
+ > `strictDepBuilds` 默认为 true,未审查的依赖构建脚本会让干净的 `pnpm install`
344
+ > 以 `ERR_PNPM_IGNORED_BUILDS` 退出 1。详见该文件内注释。
345
+
346
+ ## 配置
347
+
348
+ 配置文件:`~/.kjl/config.json`(可用 `KJL_CONFIG_DIR` 覆盖目录)。
349
+ **不要手写这个文件**,用 `config` 命令——它会校验取值、保留未知字段、并原子写入。
350
+
351
+ ```bash
352
+ kjl-cli config list
353
+ kjl-cli config get baseUrl
354
+ kjl-cli config set baseUrl https://sit.kujiale.com
355
+ kjl-cli config set designPath /cloud/tool/h5/bim
356
+ kjl-cli config set signinPath /signin
357
+ kjl-cli config set api.baseUrl https://gateway.example.com
358
+ kjl-cli config set api.headers '{"X-Client":"kjl-cli"}'
359
+ ```
360
+
361
+ ### 站点与路径配置
362
+
363
+ 页面地址由一个站点根地址和两个独立路径组成:
364
+
365
+ | 配置项 | 默认值 | 用途 |
366
+ |---|---|---|
367
+ | `baseUrl` | `https://yun.kujiale.com` | 页面和默认后端请求共用的站点根地址 |
368
+ | `designPath` | `/cloud/tool/h5/bim` | `page open`、浏览器恢复和空白页兜底 |
369
+ | `signinPath` | `/signin` | `auth login` |
370
+
371
+ 也可分别用 `KJL_BASE_URL`、`KJL_DESIGN_PATH`、`KJL_SIGNIN_PATH` 临时覆盖。路径必须以单个
372
+ `/` 开头,CLI 使用标准 URL 解析拼接,不依赖手工处理斜杠。
373
+
374
+ > **破坏性变更**:旧 `designUrl` 和 `KJL_DESIGN_URL` 不再读取,也不会自动迁移。升级后请用
375
+ > 上述三个配置项重新配置环境。
376
+
377
+ ### 打开空白或已有设计方案
378
+
379
+ `--design-id` 不是必填项。未指定方案时直接打开空白方案:
380
+
381
+ ```bash
382
+ kjl-cli page open
383
+ ```
384
+
385
+ 打开已有方案时再传 ID:
386
+
387
+ ```bash
388
+ kjl-cli page open --design-id 3FO3D5WA3JRQ
389
+ ```
390
+
391
+ designId 由 CLI 拼成 query 参数(`?designid=`),不要自己拼接——**参数名必须全小写**:
392
+ camelCase 的 `?designId=` 不会被站点识别(详见 `.trellis/spec/cli/command-surface.md`)。
393
+ `--design-id` 不持久化,也不出现在 `config list`。`page open` 不接受任意 URL;切换站点、页面入口或前端版本必须分别配置 `baseUrl`、`designPath` 和 `appfv`。
394
+
395
+ > **注意**:站点把「无法识别的 designid」当作「开一个空白方案」,不会报错。
396
+ > 因此命令成功只代表 URL 被打开了,**不代表打开的是你要的那个方案**——
397
+ > 传入一个不存在的 id 同样会得到空白方案。需要确认时回读页面里的 `window.g_designId`。
398
+
399
+ ### 测试环境前端版本(`appfv`)
400
+
401
+ 测试环境可以用 `appfv` 指定前端版本;设置后会作为 `__appfv` 追加到**`page open` 打开的
402
+ 酷家乐页面 URL** 上:
403
+
404
+ ```bash
405
+ kjl-cli config set appfv kjl_agent
406
+ kjl-cli page open --design-id 3FO3D5WA3JRQ
407
+ # → https://yun.kujiale.com/cloud/tool/h5/bim?__appfv=kjl_agent&designid=3FO3D5WA3JRQ
408
+ kjl-cli config unset appfv # 关掉,URL 恢复原样
409
+ ```
410
+
411
+ - 也可用环境变量 `KJL_APPFV` 临时指定(优先级高于 config 文件)。
412
+ - **为空时不发送该参数**,URL 与未引入此功能时逐字节相同。
413
+ - **只作用于 `page open`**。`auth login` 使用 `baseUrl + signinPath`;浏览器重启、附着恢复
414
+ 使用不带 `__appfv` 的 `baseUrl + designPath`,即「只有主动开页面时才断言这个版本」。
415
+ - `__appfv` 的双下划线是站点自己的写法(从真实方案 URL 上观察到的),
416
+ **只确认了「参数被原样透传」,未验证它真正切换了前端版本**。
417
+
418
+ ### Debug 日志(`logLevel`)
419
+
420
+ 默认不写运行日志。遇到页面加载检测超时等问题时,可持久开启 debug 日志:
421
+
422
+ ```bash
423
+ kjl-cli config set logLevel debug
424
+ kjl-cli page open
425
+ # 日志:~/.kjl/logs/kjl-cli-YYYY-MM-DD.jsonl
426
+ kjl-cli config unset logLevel
427
+ ```
428
+
429
+ 也可用 `KJL_LOG_LEVEL=debug` 只覆盖当前命令。每行日志都是 JSON,使用同一个 `runId` 关联一次
430
+ CLI 调用,包含浏览器状态、agent-browser 调用耗时、导航结果、每轮加载等待、页面事件快照和最终超时状态。
431
+ 开启后 CLI 会在 stderr 打印本次日志文件路径;加载超时的 JSON envelope 也会在
432
+ `error.details.logFile` 中返回该路径。
433
+
434
+ 日志不会原样记录 agent-browser stdout/stderr、Cookie、Authorization、请求头或 URL 参数值;URL
435
+ 仅保留 origin、path 和参数名。日志按日期追加,目前不会自动清理,排障结束后应将
436
+ `logLevel` 恢复为 `off`。
437
+
438
+ ### API 配置
439
+
440
+ `api.baseUrl` 和 `api.headers` 可通过 `config set` 持久化到同一个配置文件:
441
+
442
+ ```bash
443
+ kjl-cli config set api.baseUrl https://gateway.example.com
444
+ kjl-cli config set api.headers '{"X-Client":"kjl-cli","X-Trace":"enabled"}'
445
+ kjl-cli config unset api.headers
446
+ ```
447
+
448
+ - `api.baseUrl` 必须是绝对 HTTP(S) URL;`KJL_API_BASE_URL` 仍可临时覆盖配置文件。
449
+ - `api.headers` 必须是 JSON 对象,且每个值都必须是字符串。不要在这里保存 Cookie、Authorization 或 token;身份凭据仍从浏览器 profile 按请求读取。
450
+ - 修改 `api.baseUrl` 会保留现有 `api.headers`,反之亦然。
451
+
452
+ ### 配置项一览
453
+
454
+ `baseUrl`、`designPath`、`signinPath`、`appfv`、`logLevel`、`api.baseUrl` 和 `api.headers` 可写;其余项在 `config list`
455
+ 中只读展示并给出替代方式(如 `headed` 用 `--headless` flag、`session` 用
456
+ `--session` flag),避免「配置」与「全局 flag」两套机制互相打架。
457
+
458
+ **新增配置项 = 在 `src/core/configSchema.ts` 的 `CONFIG_SPECS` 里加一行**,
459
+ 不需要新增子命令;把该行 `writable` 改成 `true` 即开放写入。
460
+
461
+ ### 依赖说明
462
+
463
+ - `agent-browser` 版本锁定 `0.39.1`。内网 registry 的 `latest` 映射不可靠,**必须写精确版本**。
464
+ - `playwright-core` 版本锁定 `1.61.1`,仅供 `init` 在没有 Chrome 149+ 时下载 Chromium 1228 / Chrome 149 回退;浏览器控制仍全部交给 agent-browser。
465
+ - `freeform-modeling-mcp` 作为**本地依赖**随包安装,版本锁定 **`1.0.38`**(`--save-exact`)。
466
+ 解析顺序:`KJL_FREEFORM_MODELING_BIN` 覆盖 → 本地依赖 → 全局安装(开发期兜底)。
467
+ 注意 1.0.38 声明了 `"exports": { ".": "./dist/cli.js" }`,**`dist/cli.js` 子路径不可
468
+ `require.resolve`**,必须解析包名本身。
469
+ - `ali-oss` 作为个人上传文件阶段的官方 Node SDK,版本锁定 **`6.23.0`**;SDK 原始异常
470
+ 可能含签名上下文,工具只返回脱敏后的稳定错误,不回显原始异常。
471
+ - 上游已知缺陷:agent-browser 的 postinstall 会把全局命令改写为指向项目本地目录。
472
+ 因此 CLI 一律按解析路径调用二进制,不依赖 PATH。
473
+
474
+ ## 目录
475
+
476
+ ```
477
+ src/core/ 配置、错误契约、agent-browser 封装、浏览器生命周期、契约树组装
478
+ src/sources/ page / freeform_modeling / local 三条数据面的统一 adapter
479
+ src/api/ 请求级浏览器 Cookie 过滤与后端 HTTP 客户端
480
+ src/tools/ 随包发布的本地工具声明和运行模块
481
+ src/commands/ auth / page / tools
482
+ test/ 单元测试 + 端到端测试 + fixture 页面
483
+ ```
@@ -0,0 +1,58 @@
1
+ import type { KjConfig } from '../core/config.js';
2
+ import { runAgentBrowser } from '../core/agentBrowser.js';
3
+ export interface BrowserCookie {
4
+ name: string;
5
+ value: string;
6
+ domain?: string;
7
+ path?: string;
8
+ secure?: boolean;
9
+ }
10
+ export interface ApiRequest {
11
+ method?: string;
12
+ query?: Record<string, unknown>;
13
+ body?: unknown;
14
+ headers?: Record<string, string>;
15
+ timeoutMs?: number;
16
+ }
17
+ export interface ApiClientDependencies {
18
+ fetchImpl?: typeof fetch;
19
+ cookieSnapshot?: (url: string, timeoutMs: number) => Promise<string>;
20
+ now?: () => number;
21
+ }
22
+ /** Safe public transport failure with no credential-bearing request details. */
23
+ export declare class ApiClientError extends Error {
24
+ readonly code: string;
25
+ readonly status?: number | undefined;
26
+ readonly responseBody?: unknown | undefined;
27
+ constructor(code: string, message: string, status?: number | undefined, responseBody?: unknown | undefined);
28
+ }
29
+ /** RFC 6265 domain/path/secure filtering for one request URL. */
30
+ export declare function selectCookies(cookies: BrowserCookie[], url: string): string;
31
+ /** Build a request URL from the configured API base or the configured site base. */
32
+ export declare function resolveApiUrl(cfg: KjConfig, target: string, query?: Record<string, unknown>): string;
33
+ /** Compatibility helper retained for pure request-shape tests. */
34
+ export declare function buildRequest(base: string, endpoint: string, method: string, input: Record<string, unknown>): {
35
+ url: string;
36
+ body: Record<string, unknown> | undefined;
37
+ };
38
+ /**
39
+ * Stateful only for a non-reversible identity fingerprint.
40
+ *
41
+ * Every cookie snapshot remains request-local; the hash detects an account/session
42
+ * switch during one command without retaining the credential itself.
43
+ */
44
+ export declare class ApiClient {
45
+ private readonly cfg;
46
+ private readonly fetchImpl;
47
+ private readonly cookieSnapshot;
48
+ private readonly now;
49
+ private identityFingerprint?;
50
+ constructor(cfg: KjConfig, dependencies?: ApiClientDependencies);
51
+ /** Pin or verify the current browser identity without returning credential material. */
52
+ ensureIdentity(target: string, timeoutMs: number): Promise<string>;
53
+ /** Send one authenticated JSON request with a hard end-to-end timeout. */
54
+ requestJson<T = unknown>(target: string, request?: ApiRequest): Promise<T>;
55
+ private verifyIdentity;
56
+ }
57
+ /** Read one cookie snapshot from agent-browser and immediately filter it for the target. */
58
+ export declare function readCookieHeader(cfg: KjConfig, url: string, timeoutMs: number, runner?: typeof runAgentBrowser): Promise<string>;