@mortiseai/stem 0.0.22 → 0.0.24
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 +175 -23
- package/bin/stem-dev.mjs +4 -0
- package/bin/stem-launcher-lib.mjs +57 -0
- package/bin/stem-supervisor-lib.mjs +167 -43
- package/bin/stem.mjs +27 -20
- package/dist/cli.mjs +1847 -930
- package/dist/daemon.mjs +1493 -613
- package/dist/mcp.mjs +1489 -609
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -135,13 +135,28 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
135
135
|
- `vision`:该模型是否支持图片理解(布尔);未声明视为能力未知
|
|
136
136
|
- `default`:全局默认模型;`roles.fast`:`/fast` 快速模式用的模型
|
|
137
137
|
|
|
138
|
-
**per-model
|
|
138
|
+
**per-model 深度思考开关与强度**—— 控制这个模型默认是否思考,以及 OpenAI 推理模型的原生强度:
|
|
139
139
|
|
|
140
140
|
| 字段 | 必填 | 说明 |
|
|
141
141
|
|---|---|---|
|
|
142
142
|
| `thinking` | 否 | 布尔。`true` = 默认开启深度思考,`false` = 默认关闭。**不写 = 一个字段都不发**,由网关自己的默认决定 |
|
|
143
143
|
| `thinking_flag` | 否 | 开关字段名,缺省 `enable_thinking`。**仅 `protocol: "openai"` 有效**(见下)。写坏了整条开关不发 |
|
|
144
144
|
| `thinking_budget` | 否 | 思考预算 tokens,仅在 `thinking: true` 时同发;不写则不发 |
|
|
145
|
+
| `reasoning_effort_levels` | 否 | 该模型支持的推理强度数组,可用值:`none` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max` / `ultra`。`/effort` 只接受当前模型列出的档位;不写则保持原方案 `low` / `medium` / `high` / `max` |
|
|
146
|
+
| `reasoning_effort` | 否 | 推理策略:`auto` 按任务和执行反馈自动分配;具体档位表示固定默认值;`provider` 关闭应用层分配、完全跟随供应商。**不写等同 `auto`** |
|
|
147
|
+
|
|
148
|
+
> `auto` 是 Stem 策略状态,不会原样发给 API。主会话会把它解析成具体档位:严格的上一轮增量调整使用低档,普通任务使用中档,规划/跨模块复杂任务使用高档;工具失败或连续空搜索会升档,修改校验通过后的收尾会降档。用户显式 `/effort <档位>` 始终优先,`/effort auto` 清除会话覆写并恢复模型策略。
|
|
149
|
+
>
|
|
150
|
+
> 协议投递按能力降级:OpenAI 模型显式配置 `reasoning_effort` 或 `reasoning_effort_levels` 时发送具体原生档位;Anthropic/兼容网关已配置 `thinking` 时按档位调整开关和预算;能力未知时仅注入 system prompt,不盲发未知字段。即使完全未配置而默认进入 auto,也不会因此让兼容网关收到不支持的参数。
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
"gpt-5.6-sol": {
|
|
154
|
+
"reasoning_effort_levels": ["low", "medium", "high", "xhigh", "max", "ultra"],
|
|
155
|
+
"reasoning_effort": "auto"
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
如需保留某个特殊网关的原始默认行为,可显式写 `"reasoning_effort": "provider"`。
|
|
145
160
|
|
|
146
161
|
> **两条协议发的形状不同**(2026-08-08 实测,别改回统一形状):
|
|
147
162
|
>
|
|
@@ -253,9 +268,9 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
253
268
|
}
|
|
254
269
|
```
|
|
255
270
|
|
|
256
|
-
### MCP 配置 · `.mstem
|
|
271
|
+
### MCP 配置 · `.mstem/<scope>/mcp.json`
|
|
257
272
|
|
|
258
|
-
MCP server
|
|
273
|
+
MCP server 清单只从当前工程读取:`<cwd>/.mstem/platform/mcp.json`;租户模式读取 `<cwd>/.mstem/tenant/<code>/mcp.json`。`$STEM_MCP_CONFIG_PATH` 是测试/显式覆写入口,设置后改为读取该文件。MCP 不读取 `~/.mstem` 下的配置。顶层键 `servers`(也兼容 CC 布局的 `mcpServers`)。
|
|
259
274
|
|
|
260
275
|
每项是**本地 stdio** 或**远程 HTTP** 二选一:
|
|
261
276
|
|
|
@@ -322,11 +337,17 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
322
337
|
#### 快速开始
|
|
323
338
|
|
|
324
339
|
```
|
|
325
|
-
/chrome setup #
|
|
340
|
+
/chrome setup # 写入当前工程 .mstem/<scope>/mcp.json
|
|
326
341
|
/chrome connect # 连接(首次会用 npx 拉包,约 30–60 秒)
|
|
327
342
|
```
|
|
328
343
|
|
|
329
|
-
然后直接说「打开 example.com
|
|
344
|
+
然后直接说「打开 example.com,告诉我页面标题」即可。`/chrome connect` 会立即刷新
|
|
345
|
+
动态工具目录,下一条普通请求生效,无需退出或重启 CLI。
|
|
346
|
+
|
|
347
|
+
当请求包含打开页面、点击、截图、控制台或 DOM 复测等浏览器意图时,Stem 会先检查
|
|
348
|
+
本轮刷新后的工具池:Chrome DevTools MCP 可用就要求走 snapshot → action → 再检查
|
|
349
|
+
页面/console 的闭环;不可用则只允许静态诊断并提示 `/chrome connect`,不会悄悄
|
|
350
|
+
退回 AppleScript、手写 CDP、remote-debugging 端口或临时 Puppeteer/Playwright 驱动。
|
|
330
351
|
|
|
331
352
|
前置条件:Node ≥ 20.19(`^20.19 || ^22.12 || >=23`)、已安装 Chrome、首次运行需联网。
|
|
332
353
|
|
|
@@ -345,11 +366,11 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
345
366
|
| 参数 | 作用 |
|
|
346
367
|
|---|---|
|
|
347
368
|
| `--headless` | 无头模式。默认**不开** —— 参考体验是用户看着 Chrome 自己动 |
|
|
348
|
-
| `--isolated` | 每次用全新临时 profile
|
|
369
|
+
| `--isolated` | 每次用全新临时 profile;这是默认值,可显式写出 |
|
|
370
|
+
| `--persistent` | 复用上游持久 profile 以保留登录态;同一时间只能由一个 Chrome MCP 会话使用 |
|
|
349
371
|
| `--channel <c>` | `stable` / `beta` / `canary` / `dev` |
|
|
350
372
|
| `--executable-path <p>` | 指定 Chrome 可执行文件 |
|
|
351
373
|
| `--browser-url <url>` | attach 到已在运行的 Chrome(需其带 `--remote-debugging-port` 启动) |
|
|
352
|
-
| `--project` | 写到 `<cwd>/.mstem/mcp.json` 而非用户级 |
|
|
353
374
|
| `--force` | 条目已存在且与默认不同时才需要 |
|
|
354
375
|
|
|
355
376
|
#### 写入的配置
|
|
@@ -360,9 +381,9 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
360
381
|
"chrome-devtools": {
|
|
361
382
|
"command": "npx",
|
|
362
383
|
"args": ["-y", "chrome-devtools-mcp@latest",
|
|
363
|
-
"--viewport", "1280x720",
|
|
364
384
|
"--screenshotFormat", "webp",
|
|
365
|
-
"--screenshotMaxWidth", "1568"
|
|
385
|
+
"--screenshotMaxWidth", "1568",
|
|
386
|
+
"--isolated"]
|
|
366
387
|
}
|
|
367
388
|
}
|
|
368
389
|
}
|
|
@@ -373,6 +394,8 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
373
394
|
- **`-y`** —— MCP 的 stdio transport 以 `shell: false` spawn 子进程。没有 `-y` 时 npx 会把 "Ok to proceed?" 提示写进握手用的同一条 stdio,连接挂死直到超时,然后落成 failed
|
|
374
395
|
- **Windows 上是 `npx.cmd`** —— 同样因为 `shell: false`,`"npx"` 在 Windows 会 ENOENT。`/chrome setup` 会按平台自动写对
|
|
375
396
|
- **`--screenshotFormat webp` + `--screenshotMaxWidth 1568`** —— 截图直接决定 token 成本,这是主要的尺寸控制手段。stem 侧还有一道 5MB 兜底,超限会丢图并提示改用 webp
|
|
397
|
+
- **`--isolated`** —— 每个 MCP server 使用自己的临时 profile,多个 Stem CLI 并行时不会争用 `SingletonLock`。需要登录态时再显式选择 `--persistent`,并确保只运行一个持久 profile 会话
|
|
398
|
+
- **不设置 `--viewport`** —— 上游在最大化窗口上执行 `Browser.setContentsSize` 会失败,并可能留下占用 profile 的 Chrome;需要调整尺寸时在页面连接成功后调用 `resize_page`
|
|
376
399
|
|
|
377
400
|
配置改动**无需重启** —— MCP 配置按 mtime/size 指纹热更新。
|
|
378
401
|
|
|
@@ -406,6 +429,7 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
406
429
|
| 首次 connect 超时 | 正在下载包。重试,或先跑 `npx -y chrome-devtools-mcp@latest --help` 预热 |
|
|
407
430
|
| `Could not find Chrome` | 装 Chrome,或 `/chrome setup --executable-path <路径>` |
|
|
408
431
|
| `SingletonLock` / profile 被占用 | `/chrome setup --isolated` |
|
|
432
|
+
| `Browser.setContentsSize` / `Restore window to normal state` | 去掉配置中的 `--viewport` 后 `/chrome connect`;新版 `/chrome setup` 会自动迁移旧默认配置 |
|
|
409
433
|
| `ECONNREFUSED`(用了 `--browser-url`) | 目标端口没有 Chrome 在监听,先带 `--remote-debugging-port` 启动 Chrome |
|
|
410
434
|
| 会话中途杀掉 Chrome 后一直报错 | 等 60 秒(连接失败缓存 TTL)或直接 `/chrome connect` |
|
|
411
435
|
| 模型说「我截图了」但描述不出画面 | 图没送到。查日志 `mcpTool:result` 的 `images` 字段是否为 0 |
|
|
@@ -458,7 +482,7 @@ MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(
|
|
|
458
482
|
| `/bailian tools [过滤词]` | 列出工具(人读版) |
|
|
459
483
|
| `/bailian remove` | 从所属配置层删除条目 |
|
|
460
484
|
|
|
461
|
-
`setup` 的可选参数:`--platform <id>`(引用哪个平台,缺省 `bailian`)、`--model <id>`(改用该模型的 `search_mcp_key`)、`--
|
|
485
|
+
`setup` 的可选参数:`--platform <id>`(引用哪个平台,缺省 `bailian`)、`--model <id>`(改用该模型的 `search_mcp_key`)、`--force`(覆盖已有条目)。配置始终写入当前工程的 `.mstem/<scope>/mcp.json`。
|
|
462
486
|
|
|
463
487
|
> 端点用 `/WebSearch/mcp`(Streamable HTTP,与官方文档一致)。百炼在 2026-08-08 ~ 08-10 间完成了 SSE → Streamable HTTP 的服务端迁移(此前 `/mcp` 回 405、只有 `/sse` 通;迁移后倒过来,`/sse` 只回空流)。若连接报 `Invalid content type` 一类错,先用 curl 对照两个路径确认服务端协议,再改 `type`。套餐专属 workspace 域名(`llm-<id>.<region>.maas.aliyuncs.com`)下同路径也可用。
|
|
464
488
|
>
|
|
@@ -502,15 +526,17 @@ Stem 的三种核心业务模式:
|
|
|
502
526
|
|---|---|---|
|
|
503
527
|
| Group 数据(session/causal/distill/evolve) | `<cwd>/.mstem/platform/<kind>/` | `<cwd>/.mstem/H/<kind>/` |
|
|
504
528
|
| 用户空间(--user 时的全部数据,见下节) | `<cwd>/mstem-storage/platform/<user>/` | `<cwd>/mstem-storage/H/<user>/` |
|
|
505
|
-
| 家目录状态(projects/
|
|
529
|
+
| 家目录状态(projects/keybindings/memory/plugins/skills/hooks/llms/workflows/agents;输入历史为进程内内存,不落盘) | `~/.mstem/...` | `~/.mstem/H/...` |
|
|
530
|
+
| 项目级共享产物(plan 与 wiki 同级、不插作用域段) | `<存储根>/plans/` | `<存储根>/plans/` |
|
|
506
531
|
| 用户 settings | `~/.mstem.json` | `~/.mstem/H/settings.json` |
|
|
507
532
|
| 项目配置(settings/skills/plugins/agents/workflows/hooks/mcp/llms/memory) | `<cwd>/.mstem/platform/...` | `<cwd>/.mstem/H/...` |
|
|
508
|
-
| debug 日志 | `<cwd>/logs
|
|
533
|
+
| debug 日志 | `<cwd>/logs/<YYYY-MM-DD>/<HH-MM-SS-sessionId>/` | 同左(统一按日期/会话归档;sessionId 唯一) |
|
|
509
534
|
|
|
510
535
|
- **env 覆盖 × 租户**:目录形根只有两个 env(`STEM_STORAGE_DIR` 重定向存储根、
|
|
511
536
|
`STEM_DIR` 重定向项目配置根;不再有逐 kind 的独立重定向)。即使显式
|
|
512
537
|
设置,租户段照插(如 `STEM_STORAGE_DIR=/data` + `--tenant acme` →
|
|
513
|
-
`/data/tenant/acme/...`)—
|
|
538
|
+
`/data/tenant/acme/...`)— 隔离是边界,不随根重定向失效;独立作用域的
|
|
539
|
+
`wiki/` 与 `plans/` 例外,始终直接位于存储根。文件形显式指针
|
|
514
540
|
env(`STEM_SETTINGS_PATH` / `STEM_CMD_LOG` / `STEM_LLMS_PATH` /
|
|
515
541
|
`STEM_HOOKS_PATH` / `STEM_MCP_CONFIG_PATH` / `STEM_PLUGINS_DIR`)按字面生效。
|
|
516
542
|
- **首启为空 + 自动建空间**:租户首次以正常路径启动(TUI / daemon run /
|
|
@@ -530,11 +556,13 @@ Stem 的三种核心业务模式:
|
|
|
530
556
|
本身不含任何用户段:
|
|
531
557
|
|
|
532
558
|
- 用户数据落用户空间;不传 `--user` 时数据属 Group 维度,落 STEM_DIR
|
|
533
|
-
(`.mstem/<scope>/<kind>/`)
|
|
534
|
-
|
|
535
|
-
workflow 日志(`projects/`)、plan 文件(`plans/`)、洞察报告与洞察记忆
|
|
559
|
+
(`.mstem/<scope>/<kind>/`)。会话数据的口径是完整的:会话/洞察四目录、
|
|
560
|
+
task 快照与 workflow 日志(`projects/`)、洞察报告与洞察记忆
|
|
536
561
|
—— 用户会话的全部落盘都在自己空间内,Group 与其他用户不可见。
|
|
537
|
-
(debug 日志例外:统一落 `<cwd>/logs
|
|
562
|
+
(debug 日志例外:统一落 `<cwd>/logs/<YYYY-MM-DD>/<HH-MM-SS-sessionId>/`,
|
|
563
|
+
按日期与唯一 sessionId 区分会话。)
|
|
564
|
+
- plan 与 wiki 是项目级独立作用域,不属于会话用户数据:分别落
|
|
565
|
+
`<存储根>/plans/`、`<存储根>/wiki/`,不插 platform/tenant/user 段。
|
|
538
566
|
- 五类资源(workflows / skills / plugins / memory / agents)同样绝对隔离:
|
|
539
567
|
`--user` 会话**只读**自己用户空间下的对应子目录(如
|
|
540
568
|
`mstem-storage/platform/<id>/skills/`),不读 Group 的 `.mstem`/家目录层;
|
|
@@ -566,6 +594,26 @@ Stem 的三种核心业务模式:
|
|
|
566
594
|
- 超限图片(>5MB 或长边 >8000px)自动用系统 `sips` 缩放到长边 1568px。
|
|
567
595
|
- 已知限制:`--resume` 恢复的历史会话中图片不回灌,模型只见 `[Image #N]` 字面。
|
|
568
596
|
|
|
597
|
+
### 运行中继续输入:排队与直接插入
|
|
598
|
+
|
|
599
|
+
当前任务执行时仍可继续输入,但默认不会改变正在执行的轮次:
|
|
600
|
+
|
|
601
|
+
- **Enter**:加入等待队列,当前任务完成后作为独立新轮次按顺序执行。
|
|
602
|
+
- **Tab**:显式直接插入当前运行轮,在下一个合法消息边界交给模型;仅在确实要修正当前任务方向时使用。
|
|
603
|
+
- **⌥/Alt+↑**:取回最后一条排队消息继续编辑,不中断当前任务。
|
|
604
|
+
- **Esc**:中断当前任务,并把尚未执行的排队消息取回输入框。
|
|
605
|
+
|
|
606
|
+
直接插入遇到无活动轮、slash 命令、服务端拒绝或超时,会自动回退到等待队列,确保输入不丢失。补全浮层打开时,Tab 仍优先接受补全。
|
|
607
|
+
|
|
608
|
+
### Ctrl+I 引用剪贴板来源 · `@path#Lx-y`
|
|
609
|
+
|
|
610
|
+
在编辑器里复制一段代码/文档后按 **Ctrl+I**(macOS 也可 **⌘I**,需终端把 Cmd 组合键透传给程序,如 iTerm2/kitty/WezTerm/ghostty 开启 kitty 键盘协议),stem 用剪贴板文本在当前目录反查来源文件与行号区间,输入框插入 `@mind/Wiki_Manual_Test_Cases.md#L228-236` 这样的引用而不是原文;发送时把对应行以 `<file path lines>` 块附给模型,模型无需再 Read。
|
|
611
|
+
|
|
612
|
+
- 反查规则:首条非空行为锚,逐行比对(容忍缩进差异;首/尾行允许从行中间开始/结束选中;两侧剥掉 `#`/反引号/`**`/表格竖线等 Markdown 语法并跳过空行,因此从 Markdown **预览/渲染视图**复制的文本也能对回原始 `.md`)。多处匹配取匹配行数最多、路径最短者并提示「存在多处匹配」;找不到则退化为普通粘贴文本。
|
|
613
|
+
- 手写 `@path#L10-20` / `@path#L10` 同样在发送时展开;裸 `@path`(无 `#L`)保持原状,模型自行 Read。
|
|
614
|
+
- 终端差异:只有 iTerm2 / kitty / WezTerm / ghostty / tmux / Windows Terminal 能区分 Ctrl+I 与 Tab;其它终端(macOS Terminal.app、VS Code 内置终端等)在**无补全浮层、不忙**时按裸 **Tab** 触发同一功能(补全浮层打开时 Tab 仍是接受补全,运行中 Tab 为显式直接插入当前轮)。
|
|
615
|
+
- 跳过 node_modules/.git 等目录、所有隐藏目录(含 `.mstem` 会话日志)、`.jsonl/.log`、二进制与 >2MB 文件;反查上限 2s。
|
|
616
|
+
|
|
569
617
|
### 其他参数
|
|
570
618
|
|
|
571
619
|
| 参数 | 说明 |
|
|
@@ -581,7 +629,7 @@ Stem 的三种核心业务模式:
|
|
|
581
629
|
|
|
582
630
|
### 运行时开关 · `.mstem/settings.local.json` 的 `env` 段
|
|
583
631
|
|
|
584
|
-
CLI
|
|
632
|
+
CLI 子进程的运行时开关统一放 settings 级联的 **`env` 段**(不再使用 `.env`):
|
|
585
633
|
入口启动时把 `env` 对象逐 key 注入 `process.env`,**仅补缺不覆盖** — shell
|
|
586
634
|
显式 `export` 的同名变量仍最优先(CI/测试钩子用,如 `STEM_LLMS_PATH` /
|
|
587
635
|
`STEM_SETTINGS_PATH`)。
|
|
@@ -613,8 +661,11 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
613
661
|
| `STEM_DEF_MODE` | 业务模式(`--agent` 等的兜底) |
|
|
614
662
|
| `STEM_DEF_LLM_MODEL` | 模型引用;优先级低于 `--model` / `/model` **与 `llms.json` 的 `default`** —— 只在没配 `default` 时兜底 |
|
|
615
663
|
| `STEM_DEBUG` | `1` = 渲染 tool-use/tool-result 调试卡片 + Debug banner |
|
|
664
|
+
| `STEM_ACTIVITY_MODE` | 任务过程密度:`normal`(默认,紧凑语义步骤、错误/小 diff/验证结果智能展开)\|`quiet`(只显示状态 spinner)\|`detailed`(同时预览全部工具结果);也可运行时使用 `/statusline activity quiet\|normal\|detailed`,`STEM_DEBUG=1` 仍使用原始调试卡 |
|
|
616
665
|
| `STEM_API_RETRY_INTERVAL_MS` | LLM 网络错误重试间隔(默认 60000,最多 10 次) |
|
|
617
666
|
| `STEM_API_TIMEOUT_MS` | LLM 请求全局超时(平台未配 `timeoutMs` 时生效,默认 600000) |
|
|
667
|
+
| `STEM_AUTO_COMPACT_TIMEOUT_MS` | 自动压缩摘要的独立硬截止(默认 60000);到时中止辅助摘要并继续主任务,不继承 LLM 的全局 10 分钟超时 |
|
|
668
|
+
| `STEM_ADAPTIVE_TASK_CONTRACT_MODE` | 复杂/批量改动任务的自适应执行合同:`enforce`(默认,深度理解后硬门控并逐条绑定用户明确约束)、`observe`(同样规划但只审计不阻断)、`off`(关闭);未完成时回复独立的“继续/continue/resume”会恢复 scope、约束、步骤和原任务预算,版本化快照可随同一 workspace/session 的 `--resume` 恢复;磁盘恢复始终要求重新验收,若目标文件指纹漂移则退回深度理解并重提合同。合同验收命令失败时会在原回合提供有上限的自动修复机会并把推理升到 `xhigh`/`max`,成功重跑后自动清账,连续失败达到上限才允许收口为阻塞。运行中的 Bash 会对合同目标做前后指纹检查:发生修改即清除旧验收并要求干净重跑,后台启动的命令不会直接形成验收证据;验收还绑定 workspace 根目录、拒绝未写进 verifier 的隐藏 `cwd`/`env` 覆写,并只接受与 verifier 完整一致的 Bash 命令,需要子目录、环境变量或复合步骤时须原样写为 `cd path && ...` / `VAR=value ...`。Bash、PowerShell 与未声明只读的 MCP 工具还会经过有界 Git mutation manifest:既有脏文件在调用期间再次变化也可识别;发现合同范围外路径时不自动回滚或归因,而是冻结旧合同、升到 `max` 重新理解并要求带 `revisionReason` 重提合同。普通新请求会丢弃旧快照;旧开关 `STEM_ADAPTIVE_TASK_CONTRACT=0` 仍等价于 `off`。可运行 `bun run smoke:task-contract` 做无网络状态机检查 |
|
|
618
669
|
| `STEM_PERMISSION_MODE` | 工具权限模式 `default\|allow\|plan\|bypassPermissions\|...` |
|
|
619
670
|
| `STEM_MCP_CONNECT_TIMEOUT_MS` | MCP server 连接超时(默认 120000;首次 npx 拉包 + 浏览器冷启动可能很久) |
|
|
620
671
|
| `STEM_MCP_CALL_TIMEOUT_MS` | MCP 单次工具调用超时(默认 120000;性能 trace 一类会很慢) |
|
|
@@ -623,11 +674,16 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
623
674
|
| `STEM_HUD_SHOW_USAGE` / `STEM_HUD_SHOW_WEEK` | HUD 用量行 / 本周行显示开关 |
|
|
624
675
|
| `STEM_TELEMETRY` | `1` = 无头模式开启 telemetry 观测帧(`--sdk-telemetry` 的 env 形式) |
|
|
625
676
|
| `STEM_TELEMETRY_WS` | telemetry WS tap 地址 |
|
|
626
|
-
| `STEM_STORAGE_DIR` |
|
|
677
|
+
| `STEM_STORAGE_DIR` | 存储根(缺省 `<cwd>/mstem-storage`;优先于 settings `storage.dir`)。用户空间自动追加 `platform/<user>` 或 `tenant/<code>/<user>`;项目级独立作用域 `wiki/`、`plans/` 直接位于根下。Group 数据(session/causal/distill/evolve)落 STEM_DIR 项目配置根 |
|
|
627
678
|
| `STEM_DIR` | 项目配置根(缺省 `<cwd>/.mstem`;取值形态与作用域追加规则同 `STEM_STORAGE_DIR`) |
|
|
628
679
|
| `STEM_TENANT` | **内部通道,勿手工设置** — 只由 `--tenant` flag 写入,env 段里的值会被入口忽略 |
|
|
629
680
|
| `STEM_USER` | **内部通道,勿手工设置** — 只由 `--user` flag 写入,env 段里的值会被入口忽略 |
|
|
630
681
|
|
|
682
|
+
> 启动器例外:`STEM_SUPERVISOR_OFF` 必须在 CLI 子进程读取
|
|
683
|
+
> `settings.local.json` 之前生效,因此只从 shell 环境或 Stem 安装/源码根的
|
|
684
|
+
> `.env` 读取。缺省或 `0` 开启 supervisor,`1` 关闭。`bun run dev`
|
|
685
|
+
> 也经过 supervisor;直接执行 Bun 源码入口才会绕过。
|
|
686
|
+
|
|
631
687
|
`STEM_STORAGE_DIR` / `STEM_DIR` 的取值支持两种形态:绝对路径、相对路径
|
|
632
688
|
(相对当前工程路径 cwd 解析)——**不做 `~` home 展开**,`~/...` 不会被特殊
|
|
633
689
|
处理,直接按相对路径拼接。会话聊天文件默认落
|
|
@@ -638,12 +694,107 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
638
694
|
|
|
639
695
|
---
|
|
640
696
|
|
|
697
|
+
## LLM Wiki · `/stem-wiki`
|
|
698
|
+
|
|
699
|
+
> 三层知识体系的完整指南与使用用例:`MSTEM-WIKI.md`(沉淀)· `MSTEM-ONTO.md`(治理)· `MSTEM-GUC.md`(度量)。本节及后两节为速查。
|
|
700
|
+
|
|
701
|
+
参考 [nashsu/llm_wiki](https://github.com/nashsu/llm_wiki)(Karpathy「LLM Wiki」模式):与 RAG 每次从零检索不同,agent **增量构建并维护一份持久化、互链的 Markdown 知识库** —— 你负责策展(`purpose.md` 写目标、`schema.md` 定结构、丢源文件),agent 负责维护(ingest / query / lint)。
|
|
702
|
+
|
|
703
|
+
- **独立作用域**:wiki 不挂 platform/tenant/user。一份 wiki = 一个业务场景,可有多份;统一注册在存储根 `mstem-storage/wiki/wikis.json`(与 `platform/`、`tenant/` 同级;随 `STEM_STORAGE_DIR`/`storage.dir` 走),缺省每份落 `mstem-storage/wiki/<name>/`,`--dir` 可指到任意目录(如项目内)。
|
|
704
|
+
- **会话挂载**:`/stem-wiki <name>` 只按已注册名称把 wiki 挂进当前会话(不接受路径);之后 import/query/lint 都作用于它。wiki / onto / guc 各有独立挂载槽,底栏和 session meta 同时保存三者,`--resume` / `/resume` 分别恢复。
|
|
705
|
+
- **目录布局**与 llm_wiki 桌面端一致,可用 Obsidian 直接打开:`purpose.md` · `schema.md` · `raw/sources/` · `wiki/{index,log,overview}.md + entities/ concepts/ sources/ queries/ comparisons/ synthesis/(+模板目录)` · `.llm-wiki/{reviews,converted,analysis,ingest-cache.json,search-index.json}`。
|
|
706
|
+
|
|
707
|
+
```bash
|
|
708
|
+
/stem-wiki create research-a research # 建 wiki(模板 general|research|reading|personal|business)并挂载
|
|
709
|
+
/stem-wiki create biz-b business --dir ./kb # 放到项目内
|
|
710
|
+
/stem-wiki list · /stem-wiki <name> · /stem-wiki detach · /stem-wiki remove <name> [--purge] · /stem-wiki status · /stem-wiki templates
|
|
711
|
+
/stem-wiki import ./papers/ [--force] [--max N] # 归档 → 转 md → SHA256 增量 → agent 分析 → 生成合规页面(ingest 为兼容别名)
|
|
712
|
+
/stem-wiki query 这个领域的核心争议是什么? [--save] # 关键词 BM25 + [[wikilink]] 图扩展预检索 → agent 引用作答;--save 存为 query 页
|
|
713
|
+
/stem-wiki lint [--fix | --semantic] # 结构检查(零 LLM)/ agent 修复 / 内容语义检查(产出审阅项)
|
|
714
|
+
/stem-wiki review [<id> [skip|create]] # 人工审阅队列:矛盾 / 重复 / 缺页 / 建议 / 过期
|
|
715
|
+
/stem-wiki overview # 重生成 overview.md(唯一允许 agent 整体重写的聚合页)
|
|
716
|
+
/stem-wiki search <关键词> # 纯检索,调试召回
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
V2:`/stem-wiki search|query --mode hybrid [--explain]`(BM25 + 语义 + 图 + 本体的 RRF 融合;需配置 `wiki.embedding.url/model` 或 `STEM_EMBEDDING_URL`,否则显式 DEGRADED)· 页面 `## Fact Assertions` 表(事实级证据 / 有效期)· `entityId / externalIds / canonical` 实体归一 + `review <id> canonicalize <target>` · `/stem-wiki snapshot create|list|show` 发布快照。详见 `MSTEM-WIKI.md` §8。
|
|
720
|
+
|
|
721
|
+
agent 侧配套 5 件工具(MCP server 同样暴露为 `tool_wiki_*`,外部调用须显式传 `name` 或 `dir`):`WikiSearch`(检索)· `WikiIndex`(commit:归一 frontmatter、重建 `index.md`、追加 `log.md`、审阅项入库)· `WikiLint`(orphan / broken-link / no-outlinks / schema-routing / frontmatter / index-drift)· `WikiIngestPrep`(源转换与分块读取)· `WikiReview`。`index.md` 是派生物、`log.md` 只追加,LLM 误写会在下次 commit 时被还原。
|
|
722
|
+
|
|
723
|
+
提示:ingest 一次会写多个文件,默认权限模式会反复弹确认卡 —— 可 Shift+Tab 切到 acceptEdits,或在 settings 权限段放行 `Write(mstem-storage/wiki/**)`。prompt 模板可用 `.mstem/skills/wiki-<ingest|query|lint-fix|lint-semantic|review|overview>/SKILL.md` 覆盖指令块。PDF/DOCX 解析依赖 `pdfjs-dist` / `mammoth`(随包安装;缺失时仅对应格式不可用)。
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
## Stem-Ontology · `/stem-onto`
|
|
727
|
+
|
|
728
|
+
独立的正式语义空间(可选读取会话当前 Wiki):Wiki 是 bottom-up 的知识沉淀,本体是 top-down 的语义治理,**Wiki ≠ Ontology**。本体本身也是 Markdown(一类 / 一属性 / 一规则各一页,Obsidian 可开),`ontology/**` 只由人或经审核的 proposal 合并写入;Agent 只能通过工具**提议**变更。
|
|
729
|
+
|
|
730
|
+
- **独立空间**:`<存储根>/onto/<name>/ontology/{ontology.md, classes/, properties/, rules/}` + `.llm-wiki/ontology/{ontology.json, graph.json, proposals/, export/}`;注册表 `onto/ontos.json`,用 `/stem-onto <name>` 挂载。未挂 Wiki 时 build/query 仍可用,页面实例按空集处理。
|
|
731
|
+
- **实例标注**(不改 frontmatter 解析器,Obsidian Dataview 原生兼容):页面 frontmatter 加 `class: Customer`;正文用行内字段写类型化关系与属性值 —— `industry:: 制造业`、`- hasContact:: [[entities/zhang-san]]`。`[[链接]]` 仍进 outlinks,现有图扩展 / broken-link lint 直接生效;类与属性可用别名(含中文)书写,编译时归一。
|
|
732
|
+
- **实例图**:`WikiIndex commit` 末尾自动把标注编译成类型化三元组(`type` / `object` / `datatype`,含 `inverseOf` 反向补边),未知类 / 未知属性 / 悬空链接分别记账而非报错。
|
|
733
|
+
|
|
734
|
+
```bash
|
|
735
|
+
/stem-onto list · /stem-onto create <name> [--dir p] · /stem-onto <name> · /stem-onto detach · /stem-onto remove <name> [--purge]
|
|
736
|
+
/stem-onto init [--name <name>] [--base <iri>] # 未挂自身空间时自动创建;缺 Wiki 时建同名协作 Wiki
|
|
737
|
+
/stem-onto status # 类 / 属性 / 规则数、实例覆盖率、三元组数、类树、是否 stale
|
|
738
|
+
/stem-onto show <Class|property> # 类:祖先 / 子类 / 可用属性(含继承与槽覆盖)/ 实例;属性:domain / range / 使用样本
|
|
739
|
+
/stem-onto tree · /stem-onto build # 类树 / 由 ontology/** 与页面重编译,报告解析问题、继承环、unknown、unresolved
|
|
740
|
+
/stem-onto query "class:Customer industry=制造业" # 实例图 DSL:class:X · key=value · A -pred-> B(* 任意)
|
|
741
|
+
/stem-onto query "entities/acme -hasOpportunity-> *"
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
```bash
|
|
745
|
+
/stem-onto proposals [--all] # 本体变更提案队列(.llm-wiki/ontology/proposals/*.json,p-YYYYMMDD-xxxx)
|
|
746
|
+
/stem-onto propose [提示] # 让 agent 调研 wiki 后经 OntologyPropose 提交提案(addClass / addProperty / addRule / mergeAlias / reparent / deprecate)
|
|
747
|
+
/stem-onto review <id> [--explain] # 按当前磁盘本体校验提案;--explain 让 agent 评估影响面并给出 merge / amend / reject 建议
|
|
748
|
+
/stem-onto merge <id> · /stem-onto reject <id> [原因] # merge 是唯一写 ontology/** 的路径:校验 → 落盘 → 重编译 → ontology.md Changelog + log.md
|
|
749
|
+
/stem-onto lint [--fix] # 约束校验:unknownClass / deprecatedUse / unknownProperty / domainMismatch / rangeMismatch / cardinality / requiredMissing / unclassified;--fix 只改页面
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
治理不变量:**本体只增不破**(没有 delete,deprecate 保留页面且有实例时必须给 replacedBy)、**LLM 无本体写权**(工具只写 proposals;合并按磁盘现状重新校验,人手改过导致冲突会拒绝)。`/stem-wiki lint` 默认也带本体 finding(`WikiLint` 的 `checks` 加了 `ontology`),但 `--fix` 分流:结构问题走 `/stem-wiki lint --fix`,标注问题走 `/stem-onto lint --fix`。
|
|
753
|
+
|
|
754
|
+
agent 侧配套工具(MCP 同样暴露为 `tool_ontology_*`):`OntologySchema`(tree / classes / class / property / lookup —— 写标注前先查 id,别发明新词)· `OntologyGraph`(facts / neighbors / instances / path / query —— 结构性、关系性问题走图而不是关键词)· `OntologyPropose`(唯一的 Agent 侧写入口,只进队列)· `OntologyValidate`(标注校验;给 `proposalId` 则校验提案可否合并)。prompt 模板可用 `.mstem/skills/onto-<propose|lint-fix|explain|mine|classify>/SKILL.md` 覆盖指令块。
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
/stem-onto mine [--min N] [--scope 目录] [--dry] # 阶段②③:确定性统计候选(未知 class 值 / 高频 tag / 高频 key:: 及其主宾类分布 / 标题词 / 未类型化链接的类对)→ agent 聚类消歧后经 OntologyPropose 提案;--dry 只看统计
|
|
758
|
+
/stem-onto classify [--scope 目录] [--max N] # 阶段⑥:让 agent 给未分类页面补 class: 与 prop::(只用已有 id;新词留给 propose)
|
|
759
|
+
/stem-onto export [--format jsonld|ttl|shacl|instances|all] # 阶段⑦导出:ontology.jsonld / ontology.ttl(OWL-Lite 子集)/ shapes.ttl(SHACL)/ instances.jsonld
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
本体存在时,`WikiSearch` 多两个参数:`class`(只保留该类及子类的实例)与 `predicate`(沿类型化边扩展种子);system prompt 的 `# Wiki` 区块追加 `## Ontology` 段(类树 ≤ 40 行 + 标注写法硬规则 + 最近黄金集分数),让 ingest 天然产出实例标注。
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## 黄金用例集 · `/stem-guc`
|
|
767
|
+
|
|
768
|
+
三条命令的职责边界:**`/stem-wiki` 沉淀 · `/stem-onto` 治理 · `/stem-guc` 度量**。三者拥有独立目录、注册表、会话挂载槽与写门;跨空间读取只使用会话当前挂载的协作者。黄金用例集(Golden Use Cases)默认 10 条,配比正例 50% / 错例 30% / 边界 20%,只依赖人的 KnowHow,**可以先于任何页面与本体存在**;缺 Wiki / Ontology 时解析与结构分明确降级为 n/a。
|
|
769
|
+
|
|
770
|
+
- **独立空间**:`<存储根>/guc/<name>/guc/{README.md, cases/<g-NNNN>.md}` + `.llm-wiki/guc/{runs/, derived/, latest.json}`;注册表 `guc/gucs.json`,用 `/stem-guc <name>` 挂载。
|
|
771
|
+
- **用例**:`kind`(positive: lookup / relation / aggregate;negative: false-premise / stale / misattribution;edge: multi-hop / alias / cross-class / ambiguous)、`question`、`mode`(answer / refute / abstain / list)、`mustContain` / `mustNotContain`、`entities` / `relations` / `classes`、`weight`,正文 `## Rationale`。
|
|
772
|
+
- **打分(确定性优先)**:字面分(mustContain 命中 ×6 + mustNotContain 无违反 ×4)+ 模式一致性(从回答检测 refute / abstain / list;不符封顶 3 分)+ 结构分(已解析的实体 / 关系在 KB 中命中率;无本体时报告标 `n/a`,不给假低分);open 题可用 `--judge` 让 agent 自评。**负例通过率单独列**,防止"全答对但都顺着说"。每条失败带归因码:`missingPage / missingClass / missingRelation / wrongAnswer / followedFalsePremise / overAbstain` —— 前三种直接就是建设清单。
|
|
773
|
+
|
|
774
|
+
```bash
|
|
775
|
+
/stem-guc list · /stem-guc create <name> [--dir p] · /stem-guc <name> · /stem-guc detach · /stem-guc remove <name> [--purge]
|
|
776
|
+
/stem-guc init [--name 名称] [--n 10] [--ratio 5/3/2] [--wiki 名称] [--no-interview] # 未挂自身空间时自动创建;缺 Wiki 时建同名协作 Wiki
|
|
777
|
+
/stem-guc import <文件|目录> [--analyze] [--max N] # 合规 md/json/csv 直接导入;普通外部资料自动分析后生成 case
|
|
778
|
+
/stem-guc cases [--kind k] · /stem-guc show <id> · /stem-guc add [提示] · /stem-guc status
|
|
779
|
+
/stem-guc check # 配比 + 期望解析:resolved / unresolved 两栏,unresolved 即待建清单
|
|
780
|
+
/stem-guc derive [--apply] # 待建清单 → 本体提案草案 + 页面待办;--apply 让 agent 经 OntologyPropose / WikiReview 落成正式项
|
|
781
|
+
/stem-guc run [--judge] [--kind k] # agent 逐题用 WikiSearch / OntologyGraph 作答 → GucScore score(确定性打分 + 归因)→ 全部打完自动收尾
|
|
782
|
+
/stem-guc report [runId] [--finish] # 套件分 / 按 kind 通过率 / 归因分布 / 逐题明细 / 与上次 run 对比 / 是否已过期(页面或本体变了)
|
|
783
|
+
/stem-guc evolve <目标分> <最大轮数> # run → 修根因(补页 / 提案 / 改标注)→ 再 run,直到达标(prompt 驱动循环;不改用例凑分)
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
三种起步顺序都支持:用例先行(init/import → derive → `/stem-onto init` + merge → `/stem-wiki import` → run)、知识先行(基于类树 + 访谈生成 → run)、交替演化(evolve)。`/stem-onto status` 与 system prompt 会显示最近分数与最弱 kind,引导 import / classify 优先补短板。agent 工具:`GucScore`(list / get / add / score / status;MCP `tool_guc_score`)。prompt 指令块可用 `.mstem/skills/guc-<init|import|add|run|derive|evolve>/SKILL.md` 覆盖。
|
|
787
|
+
|
|
788
|
+
**独立面板**:`run` / `evolve` 发起后,输入框上方出现黄金用例面板(`src/stem-ui/GucPanel.tsx`,数据源 `stem-utils/gucPanelStore`):逐题 ✓/✗/spinner、每题分数条与归因、套件分、按 kind 汇总、evolve 的轮次轨迹(R1 → R2 …);Agent 每次调 `GucScore score` 即时刷新;Ctrl+C / Esc 标记中断并冻结为快照;`/stem-guc hide` 关闭。它与洞察模式(`/stem-causal` / `/stem-distill` / `/stem-evolve` 的 InsightPanel)**零耦合**——自有 store、自有组件、自有生命周期,不新增 InsightMode。
|
|
789
|
+
|
|
790
|
+
---
|
|
791
|
+
|
|
641
792
|
## 配置结构说明
|
|
642
793
|
|
|
643
794
|
两个配置根均落在目标项目目录下,可用 env `STEM_DIR` / `STEM_STORAGE_DIR` 重定向(见「运行时开关」):
|
|
644
795
|
|
|
645
796
|
- **`.mstem/`** — 项目配置根:配置文件 + Group 数据(session/causal/distill/evolve 等)
|
|
646
|
-
- **`mstem-storage/`** —
|
|
797
|
+
- **`mstem-storage/`** — 存储根:承载 `--user` 用户隔离数据,以及独立作用域的 wiki/plan 项目产物;不放配置文件
|
|
647
798
|
|
|
648
799
|
```
|
|
649
800
|
.mstem/ # 项目配置根(STEM_DIR)
|
|
@@ -656,7 +807,9 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
656
807
|
└── tenant/<租户代码>/ # tenant 作用域:结构同 platform,首启自动建目录
|
|
657
808
|
└── … # 但配置文件不播种,需手工放置
|
|
658
809
|
|
|
659
|
-
mstem-storage/ #
|
|
810
|
+
mstem-storage/ # 存储根(STEM_STORAGE_DIR)
|
|
811
|
+
├── wiki/ # 独立作用域的 Wiki(不插 platform/tenant/user)
|
|
812
|
+
├── plans/ # plan file,与 wiki 同级
|
|
660
813
|
├── platform/
|
|
661
814
|
│ ├── common/ # 未指定 --user 时的公共用户空间
|
|
662
815
|
│ └── <用户ID>/ # stem --user <用户ID> 的用户空间
|
|
@@ -726,7 +879,7 @@ PATH: "uvx"`。常见修法:装上对应工具,或把 `command` 写成 `npx.cmd`
|
|
|
726
879
|
|
|
727
880
|
### 报障时请带上
|
|
728
881
|
|
|
729
|
-
- `logs/<sessionId>/*.txt`(含 `[termProfile]`,一眼能看出终端画像)
|
|
882
|
+
- `logs/<YYYY-MM-DD>/<HH-MM-SS-sessionId>/*.txt`(含 `[termProfile]`,一眼能看出终端画像)
|
|
730
883
|
- 复现时加 `STEM_DEBUG_REPAINTS=1`,日志里会多出全屏重画的归因
|
|
731
884
|
- 画面问题可加 `STEM_TTY_RECORD=<路径>`,把原始终端字节流录下来一并发回 —— 我们能在
|
|
732
885
|
本地虚拟终端里按不同口径回放,把"猜"变成"量"
|
|
@@ -743,4 +896,3 @@ PATH: "uvx"`。常见修法:装上对应工具,或把 `command` 写成 `npx.cmd`
|
|
|
743
896
|
| 许可证 | Apache-2.0 |
|
|
744
897
|
|
|
745
898
|
---
|
|
746
|
-
|
package/bin/stem-dev.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// 启动器纯判定逻辑。独立出来供 smoke 覆盖,避免 import bin/stem.mjs 时直接启动 CLI。
|
|
2
|
+
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { win32 } from 'node:path'
|
|
5
|
+
|
|
6
|
+
export const SUPERVISOR_OFF_ENV = 'STEM_SUPERVISOR_OFF'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve Bun to a native executable that node:child_process can spawn directly.
|
|
10
|
+
*
|
|
11
|
+
* On Windows, a global `npm install -g bun` exposes bun.ps1/bun.cmd on PATH while
|
|
12
|
+
* the native executable lives under node_modules/bun/bin. PowerShell can invoke
|
|
13
|
+
* those shims, but spawn('bun') cannot, which otherwise surfaces as ENOENT.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveBunCommand(options = {}) {
|
|
16
|
+
const {
|
|
17
|
+
env = process.env,
|
|
18
|
+
platform = process.platform,
|
|
19
|
+
fileExists = existsSync,
|
|
20
|
+
} = options
|
|
21
|
+
if (platform !== 'win32') return 'bun'
|
|
22
|
+
|
|
23
|
+
const candidates = []
|
|
24
|
+
const npmExecPath = String(env.npm_execpath ?? '').trim()
|
|
25
|
+
if (/^bun\.exe$/i.test(win32.basename(npmExecPath))) candidates.push(npmExecPath)
|
|
26
|
+
|
|
27
|
+
const pathValue = String(env.PATH ?? env.Path ?? '')
|
|
28
|
+
for (const rawDir of pathValue.split(win32.delimiter)) {
|
|
29
|
+
const dir = rawDir.trim().replace(/^"|"$/g, '')
|
|
30
|
+
if (!dir) continue
|
|
31
|
+
candidates.push(win32.join(dir, 'bun.exe'))
|
|
32
|
+
candidates.push(win32.join(dir, 'node_modules', 'bun', 'bin', 'bun.exe'))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const bunInstall = String(env.BUN_INSTALL ?? '').trim()
|
|
36
|
+
if (bunInstall) candidates.push(win32.join(bunInstall, 'bin', 'bun.exe'))
|
|
37
|
+
const userProfile = String(env.USERPROFILE ?? '').trim()
|
|
38
|
+
if (userProfile) candidates.push(win32.join(userProfile, '.bun', 'bin', 'bun.exe'))
|
|
39
|
+
|
|
40
|
+
return candidates.find(candidate => {
|
|
41
|
+
try { return fileExists(candidate) } catch { return false }
|
|
42
|
+
}) ?? 'bun'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 只有交互式 TUI 需要看护;显式关闭、子进程、无头模式均单次直跑。 */
|
|
46
|
+
export function shouldSupervise(argv, options = {}) {
|
|
47
|
+
const {
|
|
48
|
+
env = process.env,
|
|
49
|
+
stdinIsTTY = process.stdin.isTTY,
|
|
50
|
+
stdoutIsTTY = process.stdout.isTTY,
|
|
51
|
+
} = options
|
|
52
|
+
if (env[SUPERVISOR_OFF_ENV] === '1') return false
|
|
53
|
+
if (env.STEM_SUPERVISED === '1') return false
|
|
54
|
+
if (!stdoutIsTTY || !stdinIsTTY) return false
|
|
55
|
+
const nonInteractive = new Set(['--help', '-h', '--version', '-v', '--print'])
|
|
56
|
+
return !argv.some(a => nonInteractive.has(a) || a === '--sdk-url' || a.startsWith('--sdk-url='))
|
|
57
|
+
}
|