@danceiny/gotry 0.0.1-rc.14 → 0.0.1-rc.15
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 +21 -19
- package/README.zh-CN.md +21 -19
- package/bin/gotry-bootstrap.js +39 -0
- package/dist/capabilities/artifacts.js +217 -0
- package/dist/scripts/booking-saga-tests.js +187 -0
- package/dist/scripts/bootstrap-tests.js +12 -2
- package/dist/scripts/nightly-evidence-tests.js +123 -0
- package/dist/scripts/nightly-evidence.js +233 -0
- package/dist/scripts/smoke.js +83 -0
- package/dist/src/booking-saga.js +153 -0
- package/dist/src/dsh-llm.js +30 -6
- package/dist/src/index.js +133 -0
- package/package.json +4 -2
- package/ts/capabilities/artifacts.ts +235 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/dsh-llm.ts +40 -3
- package/ts/src/index.ts +128 -20
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ GoTry turns "I want to go somewhere" into "can I, how, and at what true cost":
|
|
|
46
46
|
| **Fact extraction** | LLM | Working hours semantics, leave semantics |
|
|
47
47
|
| **Feasibility verdict** | **Z3 solver** | Which destinations are feasible / infeasible, why, and the **smallest change that makes them feasible** |
|
|
48
48
|
| **Door-to-door true cost** | Solver | Real flight duration (incl. time zones) + early-wake penalty + transfer cost + arrival energy % |
|
|
49
|
-
| **Evidence chain** | Render layer | Every number
|
|
49
|
+
| **Evidence chain** | Render layer | Every number carries a **source tag**: `[骨架:openflights]` = route existence verified against the public route database; `[实时API:flyai]` = pulled live from an API seconds ago; `[静态包:估算]` = a researched estimate (**not realtime — verify before booking**). On degradation the tag switches honestly — an estimate never poses as realtime |
|
|
50
50
|
|
|
51
51
|
**Unlike a regular AI chat**, the LLM only translates and explains. **Decisions and arithmetic are computed by a Z3 solver**, not guessed.
|
|
52
52
|
|
|
@@ -78,7 +78,7 @@ cp .env.example .env # ② set LLM_API_KEY
|
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
81
|
-
## 🧰
|
|
81
|
+
## 🧰 20 tools
|
|
82
82
|
|
|
83
83
|
| Group | Tool | What it does |
|
|
84
84
|
|---|---|---|
|
|
@@ -94,6 +94,7 @@ cp .env.example .env # ② set LLM_API_KEY
|
|
|
94
94
|
| **Memory & reachability** | `gotry_motivation_save` | Persist motivation profile (evidence mandatory, anti-fabrication) |
|
|
95
95
|
| | `gotry_wish_pool_add` / `gotry_wish_pool_list` | "next departure" wish pool + 0..1 conditional recall |
|
|
96
96
|
| | `gotry_companion_save` · `gotry_trip_log` | companion profile / travel timeline |
|
|
97
|
+
| **Artifacts** | `gotry_artifacts_list` / `gotry_artifacts_read` | Discover & view generated artifacts (async deliverables + working-dir markdown) as a line-numbered file view (read-only) |
|
|
97
98
|
| **General external** | `gotry_web_search` · `gotry_video_subtitle` · `gotry_github_search` · `gotry_agent_reach` | web / subtitles / GitHub / all-channel external info (via Agent-Reach) |
|
|
98
99
|
|
|
99
100
|
---
|
|
@@ -131,6 +132,8 @@ Engine verdict:
|
|
|
131
132
|
[static-pack:estimate] G7315/G7316 priced on Jul–Aug off-season rates
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
> Tag guide: `[骨架:openflights]` means "this route can be flown" was verified against the public route database; `[实时API:*]` marks data pulled live seconds ago; `[静态包:估算]` flags an off-season estimate — **verify before booking**. Tags are attached by the render layer, never by the model.
|
|
136
|
+
|
|
134
137
|
---
|
|
135
138
|
|
|
136
139
|
## 🏛️ Architecture
|
|
@@ -138,7 +141,7 @@ Engine verdict:
|
|
|
138
141
|
```
|
|
139
142
|
┌──────────────────────────────────────────────────────────────┐
|
|
140
143
|
│ L1 chat-as-interface; gates are in-message choice cards │
|
|
141
|
-
│ L2 orchestration dsh runtime + GoTry plugin (ReAct);
|
|
144
|
+
│ L2 orchestration dsh runtime + GoTry plugin (ReAct); 20 tools│
|
|
142
145
|
│ L3 domain unified itinerary model + Z3 feasibility engine │
|
|
143
146
|
│ L4 data static packs + hotelbyte-cli bridge + OpenFlights │
|
|
144
147
|
│ L5 governance LoopX (objective / gates / evidence / quota) │
|
|
@@ -147,9 +150,9 @@ Engine verdict:
|
|
|
147
150
|
|
|
148
151
|
| Layer | Module | Role |
|
|
149
152
|
|---|---|---|
|
|
150
|
-
| L2 | `ts/src/index.ts` (dsh plugin) |
|
|
153
|
+
| L2 | `ts/src/index.ts` (dsh plugin) | 20 tools, time-anchor & memory-brief variables; execute isolation + consent gate + process guards |
|
|
151
154
|
| L3 | `ts/src/unified.ts` · `py/gotry_feasibility/` | single solving entry (candidate enumeration + flight-chain Z3) |
|
|
152
|
-
| L4 | `ts/capabilities/hbcli.ts` · `skeleton-check.ts` | realtime inventory bridge + OpenFlights skeleton (three-valued semantics) |
|
|
155
|
+
| L4 | `ts/capabilities/effect.ts` · `hbcli.ts` · `skeleton-check.ts` | effect interpreter (backoff retry / circuit breaker / mock interpreter, issue #16) + realtime inventory bridge + OpenFlights skeleton (three-valued semantics) |
|
|
153
156
|
| L5 | loopx governance | objective / gates / evidence / quota |
|
|
154
157
|
|
|
155
158
|
> 📖 Full ADRs / evolution / debt ledger: [`docs/architecture.md`](docs/architecture.md) (Chinese — English versions planned for v0.1.0)
|
|
@@ -158,24 +161,23 @@ Engine verdict:
|
|
|
158
161
|
|
|
159
162
|
## ⚠️ Status & limitations
|
|
160
163
|
|
|
161
|
-
**
|
|
164
|
+
**Working today** (full-stack regression §1–§34 green; every item has deterministic tests):
|
|
162
165
|
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
- License MIT; legacy shell frontend removed (dsh web is the only product surface)
|
|
166
|
+
- **Z3 solving engine** — feasibility verdicts + door-to-door whole-cost; the historical concurrency race is fixed (§30 regression gate)
|
|
167
|
+
- **Realtime retrieval** — flight/train/hotel (Fliggy official channel), destination/hotel catalogs, weather, live flight observation, route connectivity; realtime prices can overwrite solver prices (`GOTRY_REALTIME_PRICING=1`)
|
|
168
|
+
- **Account session search** — Ctrip flights on your own logged-in Chrome; consent & privacy rules above (see 🔐 **Account session: consent & privacy**)
|
|
169
|
+
- **Memory & reachability** — motivation profile / wish pool / companions / travel timeline; English output via `GOTRY_LOCALE=en`
|
|
168
170
|
|
|
169
|
-
**Open (honest list)
|
|
171
|
+
**Open limitations** (as of 2026-08-29, honest list):
|
|
170
172
|
|
|
171
|
-
- ⏳ **M3 Exit not closed** — engineering & distribution ready, but real seed-user evidence (50–200 person cohort) not yet accumulated;
|
|
172
|
-
- ⏳ **
|
|
173
|
-
- ⏳ **
|
|
173
|
+
- ⏳ **M3 Exit not closed** — engineering & distribution ready, but real seed-user evidence (50–200 person cohort) not yet accumulated; automated tests prove contracts and formulas, not business pass
|
|
174
|
+
- ⏳ **Ctrip-hotel / Meituan logged-in adapters** — flights done; hotel session surfaces await real login-state backfill (next tick)
|
|
175
|
+
- ⏳ **Interface language** — English currently covers the deterministic solve-output layer only; the dsh host UI and dialogue surface belong to the host / calibration samples
|
|
174
176
|
|
|
175
177
|
<details>
|
|
176
|
-
<summary>📖
|
|
178
|
+
<summary>📖 Deeper engineering state (ledger contracts / evidence contracts / milestone stance)</summary>
|
|
177
179
|
|
|
178
|
-
|
|
180
|
+
The authoritative state lives in the docs, not this README: transactional state ledger (ADR-15) + dual-form freeze (ADR-16: one ledger semantics for local+web); the M3 real-cohort evidence contract stands (fixtures don't count toward Exit; 50–200 real samples open the gate); the M4 paired-cohort value evidence contract (run-all §34 — synthetic data is never Exit evidence); async work-order terminal contract (`gotry_async_terminal.v1`: 4/4 → succeeded / ledger settled / exit 0). Details: [`docs/roadmap.md`](docs/roadmap.md) / [`docs/architecture.md`](docs/architecture.md) §1 and issues #19–#22.
|
|
179
181
|
|
|
180
182
|
</details>
|
|
181
183
|
|
|
@@ -187,7 +189,7 @@ Transactional state ledger (ADR-15, `gotry_async_terminal.v1`: 4/4→`succeeded`
|
|
|
187
189
|
./scripts/run-all-tests.sh
|
|
188
190
|
```
|
|
189
191
|
|
|
190
|
-
One-shot full-stack green (pure TS, no Python needed): golden engines · dialogue replay · cross-process async work-orders · plugin smoke · hbcli · process guards · weather · flights · Anything · probePoi · agent-reach · dual-path stability · time-awareness eval · memory domain · **Z3 race (§30) · realtime pricing (§31) · i18n catalog (§32) · M3 cohort evidence contract (§33) · M4 value evidence contract (§34)**.
|
|
192
|
+
One-shot full-stack green (pure TS, no Python needed): golden engines · dialogue replay · cross-process async work-orders · plugin smoke · hbcli · process guards · weather · flights · Anything · probePoi · agent-reach · dual-path stability · time-awareness eval · memory domain · **Z3 race (§30) · realtime pricing (§31) · i18n catalog (§32) · M3 cohort evidence contract (§33) · M4 value evidence contract (§34) · M3 nightly evidence producer contract (§35)**.
|
|
191
193
|
|
|
192
194
|
---
|
|
193
195
|
|
|
@@ -207,4 +209,4 @@ Standard open-source flow: branch off latest `main` (`feat/ · fix/ · docs/ ·
|
|
|
207
209
|
|
|
208
210
|
**Built with**: DeepSeek Harness 0.1.2-alpha.1 (vendored) · Cordis · Z3 (WASM) · loopx (pipx) · hotelbyte-cli · Agent-Reach v1.5.0 (`.venv/`) · OpenFlights · TypeScript
|
|
209
211
|
|
|
210
|
-
**Last verified against `v0.0.1-rc.
|
|
212
|
+
**Last verified against `v0.0.1-rc.15` (2026-08-29)** — full-stack regression green §1-§36 (release flow: `scripts/publish-npm.sh`).
|
package/README.zh-CN.md
CHANGED
|
@@ -62,7 +62,7 @@ GoTry 把「想去哪」变成「能不能、怎么去」:
|
|
|
62
62
|
| **事实抽取** | LLM | 工作窗口生效 + 休假语义识别 |
|
|
63
63
|
| **可行性判定** | **Z3 求解器** | 哪些候选可行、哪些不可行、为什么、**最小改动让它可行** |
|
|
64
64
|
| **门到门全成本** | 求解器 | 真实飞行时长(含时差)+ 醒来起夜惩罚 + 接驳代价 + 到达精力 % |
|
|
65
|
-
| **证据链** | 渲染层 |
|
|
65
|
+
| **证据链** | 渲染层 | 每个数字都带**来源标签**:`[骨架:openflights]`=公开航线数据校验过"这条线能飞";`[实时API:flyai]`=刚从实时接口拉的现价现况;`[静态包:估算]`=非实时的调研估算(**下单前请核实**)。降级时标签如实更换,估算绝不冒充实时 |
|
|
66
66
|
|
|
67
67
|
**不像普通 AI 聊天**——LLM 只做理解和解释,**判定与算术是数学求解器**算的。
|
|
68
68
|
|
|
@@ -92,11 +92,13 @@ GoTry: 收到。先把约束记下来——
|
|
|
92
92
|
[静态包:估算] G7315/G7316 价格按 7-8 月淡季估算
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
>
|
|
95
|
+
> 标签导读:`[骨架:openflights]` 说的是"这条航线能飞"已被公开航线数据校验;`[实时API:*]` 说的是刚从实时接口拉回的当下数据;`[静态包:估算]` 提醒价格是淡季档估算——**订前核实**。
|
|
96
|
+
|
|
97
|
+
> **Brief English summary**: input → engine verdicts (feasibility + whole-cost) → recommendation + wish-pool entry for infeasible candidates. Every numeric carries an evidence tag. See the 证据链 row above for what each tag means.
|
|
96
98
|
|
|
97
99
|
---
|
|
98
100
|
|
|
99
|
-
## 🧰
|
|
101
|
+
## 🧰 20 个工具 — Tools
|
|
100
102
|
|
|
101
103
|
| 组 | 工具 | 干什么 |
|
|
102
104
|
|---|---|---|
|
|
@@ -112,6 +114,7 @@ GoTry: 收到。先把约束记下来——
|
|
|
112
114
|
| **记忆与触达** | `gotry_motivation_save` | 动机画像落盘(evidence 强制,反幻觉) |
|
|
113
115
|
| | `gotry_wish_pool_add` / `gotry_wish_pool_list` | 「下一次出发」愿望池 + 0..1 条件召回 |
|
|
114
116
|
| | `gotry_companion_save` · `gotry_trip_log` | 同行人档案 / 旅行时间线 |
|
|
117
|
+
| **产物** | `gotry_artifacts_list` / `gotry_artifacts_read` | 发现与查看已生成的产物(异步交付 + 工作目录 markdown),行号文件视图,只读 |
|
|
115
118
|
| **通用外部** | `gotry_web_search` · `gotry_video_subtitle` · `gotry_github_search` · `gotry_agent_reach` | 网页/字幕/GitHub/全渠道外部信息(经 Agent-Reach) |
|
|
116
119
|
|
|
117
120
|
---
|
|
@@ -134,7 +137,7 @@ GoTry: 收到。先把约束记下来——
|
|
|
134
137
|
```
|
|
135
138
|
┌──────────────────────────────────────────────────────────────┐
|
|
136
139
|
│ L1 对话即界面 chat-as-UI; gates 是消息内选择题 │
|
|
137
|
-
│ L2 编排 dsh 运行时 + GoTry 插件(ReAct);
|
|
140
|
+
│ L2 编排 dsh 运行时 + GoTry 插件(ReAct);20 个工具 │
|
|
138
141
|
│ L3 领域 统一行程模型 + Z3 可行性引擎(枚举/Z3 双形态) │
|
|
139
142
|
│ L4 数据 静态数据包 + hotelbyte-cli 实时桥 + OpenFlights 骨架 │
|
|
140
143
|
│ L5 治理 LoopX(objective / gates / evidence / quota) │
|
|
@@ -143,9 +146,9 @@ GoTry: 收到。先把约束记下来——
|
|
|
143
146
|
|
|
144
147
|
| 层 | 模块 | 角色 |
|
|
145
148
|
|---|---|---|
|
|
146
|
-
| L2 | `ts/src/index.ts`(dsh 插件) | 注册
|
|
149
|
+
| L2 | `ts/src/index.ts`(dsh 插件) | 注册 20 工具,挂时间锚点/记忆 brief 变量;execute 异常隔离 + 授权闸 + 进程护栏 |
|
|
147
150
|
| L3 | `ts/src/unified.ts` · `py/gotry_feasibility/` | 唯一求解入口(候选枚举 + 航班链 Z3) |
|
|
148
|
-
| L4 | `ts/capabilities/hbcli.ts` · `skeleton-check.ts` | 实时库存桥 + OpenFlights 骨架(三值语义) |
|
|
151
|
+
| L4 | `ts/capabilities/effect.ts` · `hbcli.ts` · `skeleton-check.ts` | 效应解译层(退避重试/断路器/mock 解译器,issue #16)+ 实时库存桥 + OpenFlights 骨架(三值语义) |
|
|
149
152
|
| L5 | loopx 治理面 | objective / gates / evidence / quota |
|
|
150
153
|
|
|
151
154
|
> 📖 完整 ADR / 演进 / 债务清单: [`docs/architecture.md`](docs/architecture.md)
|
|
@@ -154,24 +157,23 @@ GoTry: 收到。先把约束记下来——
|
|
|
154
157
|
|
|
155
158
|
## ⚠️ 状态与限制 — Status & limitations
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
**今天可用的能力**(全栈回归 §1–§34 全绿,每项都有确定性测试):
|
|
158
161
|
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
- ✅ License MIT;薄壳遗留已删除(dsh web 唯一产品面)
|
|
162
|
+
- **Z3 求解引擎** —— 可行性判定 + 门到门全成本;历史并发竞态已根治(§30 并发回归闸)
|
|
163
|
+
- **实时检索**:机票/火车/酒店(飞猪官方通道)、目的地/酒店目录、天气、航班观测、通航性校验;可选让实时票价覆写进求解(`GOTRY_REALTIME_PRICING=1`)
|
|
164
|
+
- **账号会话检索**:你本人登录态查携程机票,授权与隐私规则见上方 **🔐 账号会话:授权与隐私** 小节
|
|
165
|
+
- **记忆与触达**:动机画像 / 愿望池 / 同行人 / 旅行时间线;英文输出一键切换(`GOTRY_LOCALE=en`)
|
|
164
166
|
|
|
165
|
-
|
|
167
|
+
**已知限制**(截至 2026-08-29,诚实清单):
|
|
166
168
|
|
|
167
|
-
- ⏳ **M3 Exit 未关闭** ——
|
|
168
|
-
- ⏳
|
|
169
|
-
- ⏳
|
|
169
|
+
- ⏳ **M3 Exit 未关闭** —— 工程与分发面就绪,但"真实种子用户"证据(50–200 人 cohort)尚未积累;自动化测试证明的是合同与公式,不是 business pass
|
|
170
|
+
- ⏳ **携程酒店 / 美团的登录态会话适配** —— 机票已通,酒店面等登录态实测回填(下一个 tick)
|
|
171
|
+
- ⏳ **界面语言** —— 英文界面仅覆盖求解确定性输出层;dsh 宿主界面与对话面属宿主/校准件
|
|
170
172
|
|
|
171
173
|
<details>
|
|
172
|
-
<summary>📖
|
|
174
|
+
<summary>📖 更深的工程状态(账本合同 / 证据合同 / 里程碑口径)</summary>
|
|
173
175
|
|
|
174
|
-
|
|
176
|
+
状态权威面在这里,README 不展开:事务化状态账本(ADR-15)+ 双形态冻结(ADR-16:本地+Web 一套账本语义);M3 真实 cohort 证据合同已立(fixture 不充当 Exit,真实 50–200 人样本即开 Exit);M4 paired-cohort 价值证据合同(run-all §34)合成数据不充当 Exit 证据;异步工单终态合同(`gotry_async_terminal.v1`:4/4→succeeded/ledger settled/exit 0)。细则见 [`docs/roadmap.md`](docs/roadmap.md) / [`docs/architecture.md`](docs/architecture.md) §1 与 #19–#22。
|
|
175
177
|
|
|
176
178
|
</details>
|
|
177
179
|
|
|
@@ -230,4 +232,4 @@ GoTry: 收到。先把约束记下来——
|
|
|
230
232
|
|
|
231
233
|
**Built with**: DeepSeek Harness 0.1.2-alpha.1 (vendored) · Cordis · Z3 (WASM) · loopx (pipx) · hotelbyte-cli · Agent-Reach v1.5.0 (`.venv/`) · OpenFlights · TypeScript
|
|
232
234
|
|
|
233
|
-
**Last verified against `v0.0.1-rc.
|
|
235
|
+
**Last verified against `v0.0.1-rc.15`(2026-08-29)** — 全栈回归全绿 §1-§36(发布流程见 `scripts/publish-npm.sh`)。
|
package/bin/gotry-bootstrap.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* hbcli(hotelbyte-cli) → 官方 install.sh(原生二进制,~/.local/bin/hbcli)
|
|
5
5
|
* agent-reach → 官方 pip 安装 git+ upstream(包内 .venv,与 z3-solver 同址原则)
|
|
6
6
|
* flyai → 无需安装(npx 每次自拉 @fly-ai/flyai-cli)
|
|
7
|
+
* dsh-better-sidebar → dsh 宿主层插件市场组件(dshmarket.com #1 UI,18.9 万周装):
|
|
8
|
+
* dsh web 侧栏工作台(文件浏览/Markdown/Mermaid/PDF 预览)——
|
|
9
|
+
* gotry 产物(工单交付 md/行程 md)的成熟查看面(issue #25)。
|
|
10
|
+
* 宿主层安装(dsh plugin → ~/.dsh/profiles/web),不进 gotry 依赖。
|
|
7
11
|
*
|
|
8
12
|
* 用法:
|
|
9
13
|
* node bin/gotry-bootstrap.js # 显式安装(缺啥装啥;失败 exit 1)
|
|
@@ -14,6 +18,7 @@
|
|
|
14
18
|
* GOTRY_SETUP_SKIP=1 全部跳过
|
|
15
19
|
* GOTRY_SETUP_HBCLI=0 跳过 hbcli
|
|
16
20
|
* GOTRY_SETUP_REACH=0 跳过 agent-reach
|
|
21
|
+
* GOTRY_SETUP_SIDEBAR=0 跳过 dsh-better-sidebar
|
|
17
22
|
*
|
|
18
23
|
* 契约:安装外部依赖永远不挡 gotry 本体——能力层各有降级路径(静态包/not-installed
|
|
19
24
|
* verdict),自举失败只降级体验,不产生故障。凭证(hbcli auth / agent-reach 渠道
|
|
@@ -93,10 +98,42 @@ async function setupReach() {
|
|
|
93
98
|
return { ok: true }
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
async function setupSidebar() {
|
|
102
|
+
say('[gotry-setup] dsh-better-sidebar(dsh web 侧栏工作台,产物查看面 issue #25)')
|
|
103
|
+
const installed = existsSync(join(homedir(), '.dsh/profiles/web/node_modules/dsh-better-sidebar/package.json'))
|
|
104
|
+
if (installed) { say(' ✓ 已安装(~/.dsh/profiles/web)'); return { ok: true } }
|
|
105
|
+
if (CHECK_ONLY) { say(' ✗ 未安装(--check-only 只报告)'); return { ok: true } }
|
|
106
|
+
const SIDEBAR_PKG = 'dsh-better-sidebar@latest'
|
|
107
|
+
// dsh CLI:本包依赖里的 @deepseek-ai/dsh 优先;解析不到走 npx 自拉(官方安装途径同款)
|
|
108
|
+
const { createRequire } = await import('node:module')
|
|
109
|
+
const require_ = createRequire(join(repoRoot, 'package.json'))
|
|
110
|
+
let launched = false
|
|
111
|
+
try {
|
|
112
|
+
const dshBin = require_.resolve('@deepseek-ai/dsh/lib/bin.js')
|
|
113
|
+
say(` 安装中(dsh plugin → web profile): ${SIDEBAR_PKG}`)
|
|
114
|
+
const r = await run(process.execPath, [dshBin, 'plugin', '--profile', 'web', 'add', SIDEBAR_PKG], { timeoutMs: 300_000 })
|
|
115
|
+
launched = true
|
|
116
|
+
if (r.ok) {
|
|
117
|
+
say(' ✓ 安装完成(gotry web 刷新浏览器即见右侧工作台;工作区里的产物 md 可直接预览)')
|
|
118
|
+
return { ok: true }
|
|
119
|
+
}
|
|
120
|
+
say(` ✗ dsh plugin 安装失败(${r.error})——尝试 npx 途径`)
|
|
121
|
+
} catch { /* 本包未携带 @deepseek-ai/dsh */ }
|
|
122
|
+
if (!launched) say(' 本包未携带 @deepseek-ai/dsh,走 npx 途径安装')
|
|
123
|
+
const r2 = await run('npx', ['-y', '--package', '@deepseek-ai/dsh', 'dsh', 'plugin', '--profile', 'web', 'add', SIDEBAR_PKG], { timeoutMs: 300_000 })
|
|
124
|
+
if (!r2.ok) {
|
|
125
|
+
say(' ✗ 安装失败——不影响 gotry:产物仍可在对话里说「看看我生成的行程」经 gotry_artifacts_list/read 查看;可稍后重试: npx gotry setup')
|
|
126
|
+
return { ok: false }
|
|
127
|
+
}
|
|
128
|
+
say(' ✓ 安装完成(gotry web 刷新浏览器即见右侧工作台)')
|
|
129
|
+
return { ok: true }
|
|
130
|
+
}
|
|
131
|
+
|
|
96
132
|
async function main() {
|
|
97
133
|
if (process.platform === 'win32') {
|
|
98
134
|
say('[gotry-setup] Windows 暂不支持自动安装(hbcli 上游仅 darwin/linux)。手动指引:')
|
|
99
135
|
say(` hbcli: ${HBCLI_INSTALL_CMD}(WSL);agent-reach: python -m venv .venv && .venv/Scripts/pip install ${REACH_INSTALL_URL}`)
|
|
136
|
+
say(' dsh-better-sidebar: npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar@latest')
|
|
100
137
|
process.exit(AUTO ? 0 : 1)
|
|
101
138
|
}
|
|
102
139
|
if (AUTO && (process.env.CI || process.env.GOTRY_SETUP_SKIP === '1')) {
|
|
@@ -110,6 +147,8 @@ async function main() {
|
|
|
110
147
|
else say('[gotry-setup] hbcli:GOTRY_SETUP_HBCLI=0 跳过')
|
|
111
148
|
if (process.env.GOTRY_SETUP_REACH !== '0') results.push(await setupReach())
|
|
112
149
|
else say('[gotry-setup] agent-reach:GOTRY_SETUP_REACH=0 跳过')
|
|
150
|
+
if (process.env.GOTRY_SETUP_SIDEBAR !== '0') results.push(await setupSidebar())
|
|
151
|
+
else say('[gotry-setup] dsh-better-sidebar:GOTRY_SETUP_SIDEBAR=0 跳过')
|
|
113
152
|
say('[gotry-setup] flyai:无需安装(npx 每次自拉 @fly-ai/flyai-cli,免 key)')
|
|
114
153
|
const failed = results.filter((r) => !r.ok).length
|
|
115
154
|
if (failed > 0) {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
4
|
+
import { openLedgerIfExists } from '../src/state-ledger.js';
|
|
5
|
+
const MAX_LIST = 50;
|
|
6
|
+
const MAX_WINDOW = 400;
|
|
7
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
8
|
+
const TEXT_EXT_LANG = {
|
|
9
|
+
md: 'markdown',
|
|
10
|
+
txt: 'text',
|
|
11
|
+
json: 'json',
|
|
12
|
+
jsonl: 'json',
|
|
13
|
+
csv: 'csv',
|
|
14
|
+
log: 'text',
|
|
15
|
+
yaml: 'yaml',
|
|
16
|
+
yml: 'yaml'
|
|
17
|
+
};
|
|
18
|
+
const DIR_DENY = [
|
|
19
|
+
'node_modules',
|
|
20
|
+
'.git'
|
|
21
|
+
];
|
|
22
|
+
function rootOf(stateRoot) {
|
|
23
|
+
return stateRoot === '.' ? process.cwd() : resolve(stateRoot);
|
|
24
|
+
}
|
|
25
|
+
function underRoot(p, root) {
|
|
26
|
+
const r = resolve(root);
|
|
27
|
+
return p === r || p.startsWith(r + sep);
|
|
28
|
+
}
|
|
29
|
+
function hasDeniedSegment(p) {
|
|
30
|
+
return p.split(sep).some((seg)=>DIR_DENY.includes(seg));
|
|
31
|
+
}
|
|
32
|
+
function asyncDeliverablePath(root, id) {
|
|
33
|
+
return join(root, 'gotry-state', 'async', `${id}.deliverable.md`);
|
|
34
|
+
}
|
|
35
|
+
function listRunsFromLedger(root, tenant, limit) {
|
|
36
|
+
const ledger = openLedgerIfExists(root, tenant);
|
|
37
|
+
if (!ledger) return [];
|
|
38
|
+
const rows = ledger.db.prepare('SELECT id, goal, status, deliverable, updated FROM workflow_runs WHERE tenant_id = ? ORDER BY updated DESC LIMIT ?').all(ledger.tenant, limit);
|
|
39
|
+
return rows.map((r)=>{
|
|
40
|
+
const file = asyncDeliverablePath(root, r.id);
|
|
41
|
+
return {
|
|
42
|
+
source: 'async-run',
|
|
43
|
+
id: r.id,
|
|
44
|
+
title: r.goal,
|
|
45
|
+
path: file,
|
|
46
|
+
status: r.status,
|
|
47
|
+
updated: r.updated,
|
|
48
|
+
bytes: r.deliverable?.length
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function listDeliverableFiles(root, limit) {
|
|
53
|
+
const dir = join(root, 'gotry-state', 'async');
|
|
54
|
+
let names = [];
|
|
55
|
+
try {
|
|
56
|
+
names = (await readdir(dir)).filter((n)=>n.endsWith('.deliverable.md'));
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const entries = [];
|
|
61
|
+
for (const n of names.slice(0, limit * 2)){
|
|
62
|
+
const p = join(dir, n);
|
|
63
|
+
const st = await stat(p).catch(()=>null);
|
|
64
|
+
if (!st?.isFile()) continue;
|
|
65
|
+
entries.push({
|
|
66
|
+
source: 'async-run',
|
|
67
|
+
id: n.replace(/\.deliverable\.md$/, ''),
|
|
68
|
+
title: n.replace(/\.deliverable\.md$/, ''),
|
|
69
|
+
path: p,
|
|
70
|
+
status: existsSync(p.replace(/\.deliverable\.md$/, '.json')) ? 'pending-view' : 'legacy',
|
|
71
|
+
updated: new Date(st.mtimeMs).toISOString(),
|
|
72
|
+
bytes: st.size
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return entries.sort((a, b)=>String(b.updated).localeCompare(String(a.updated)));
|
|
76
|
+
}
|
|
77
|
+
async function listCwdMarkdown(cwd, limit) {
|
|
78
|
+
let dirents;
|
|
79
|
+
try {
|
|
80
|
+
dirents = await readdir(cwd, {
|
|
81
|
+
withFileTypes: true
|
|
82
|
+
});
|
|
83
|
+
} catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const entries = [];
|
|
87
|
+
for (const d of dirents){
|
|
88
|
+
if (!d.isFile() || !/\.md$/i.test(d.name) || d.name.startsWith('.')) continue;
|
|
89
|
+
const p = join(cwd, d.name);
|
|
90
|
+
const st = await stat(p).catch(()=>null);
|
|
91
|
+
if (!st) continue;
|
|
92
|
+
entries.push({
|
|
93
|
+
source: 'cwd-file',
|
|
94
|
+
id: d.name,
|
|
95
|
+
title: d.name.replace(/\.md$/i, ''),
|
|
96
|
+
path: p,
|
|
97
|
+
updated: new Date(st.mtimeMs).toISOString(),
|
|
98
|
+
bytes: st.size
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return entries.sort((a, b)=>String(b.updated).localeCompare(String(a.updated))).slice(0, limit);
|
|
102
|
+
}
|
|
103
|
+
export async function listArtifacts(opts) {
|
|
104
|
+
const limit = Math.max(1, Math.min(opts.limit ?? 20, MAX_LIST));
|
|
105
|
+
const root = rootOf(opts.stateRoot);
|
|
106
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
107
|
+
const seenPath = new Set();
|
|
108
|
+
const merged = [];
|
|
109
|
+
for (const e of [
|
|
110
|
+
...listRunsFromLedger(root, 'local', limit),
|
|
111
|
+
...await listDeliverableFiles(root, limit),
|
|
112
|
+
...await listCwdMarkdown(cwd, limit)
|
|
113
|
+
]){
|
|
114
|
+
if (seenPath.has(e.path)) continue;
|
|
115
|
+
seenPath.add(e.path);
|
|
116
|
+
merged.push(e);
|
|
117
|
+
}
|
|
118
|
+
merged.sort((a, b)=>String(b.updated ?? '').localeCompare(String(a.updated ?? '')));
|
|
119
|
+
const total = merged.length;
|
|
120
|
+
return {
|
|
121
|
+
artifacts: merged.slice(0, limit),
|
|
122
|
+
total,
|
|
123
|
+
truncated: total > limit,
|
|
124
|
+
roots: [
|
|
125
|
+
root,
|
|
126
|
+
cwd
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function readArtifact(opts) {
|
|
131
|
+
const root = rootOf(opts.stateRoot);
|
|
132
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
133
|
+
const raw = String(opts.path ?? '').trim();
|
|
134
|
+
if (!raw) return {
|
|
135
|
+
ok: false,
|
|
136
|
+
error: 'path 必填(来自 gotry_artifacts_list 的 path,或异步工单 id)'
|
|
137
|
+
};
|
|
138
|
+
let text = null;
|
|
139
|
+
let filePath = '';
|
|
140
|
+
if (!raw.includes('/') && !raw.includes('\\') && !raw.includes('.')) {
|
|
141
|
+
const ledger = openLedgerIfExists(root, 'local');
|
|
142
|
+
const run = ledger?.db.prepare('SELECT id, goal, status, deliverable FROM workflow_runs WHERE id = ? AND tenant_id = ?').get(raw, ledger.tenant);
|
|
143
|
+
if (run?.deliverable) {
|
|
144
|
+
text = run.deliverable;
|
|
145
|
+
filePath = asyncDeliverablePath(root, raw);
|
|
146
|
+
} else {
|
|
147
|
+
const p = asyncDeliverablePath(root, raw);
|
|
148
|
+
if (existsSync(p)) {
|
|
149
|
+
filePath = p;
|
|
150
|
+
text = await readFile(p, 'utf-8');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (text === null) return {
|
|
154
|
+
ok: false,
|
|
155
|
+
error: `工单 ${raw} 无 deliverable(未交付或不存在)`,
|
|
156
|
+
hint: '先 gotry_artifacts_list 看在册产物'
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (text === null) {
|
|
160
|
+
const candidates = isAbsolute(raw) ? [
|
|
161
|
+
resolve(raw)
|
|
162
|
+
] : [
|
|
163
|
+
resolve(cwd, raw),
|
|
164
|
+
resolve(root, raw),
|
|
165
|
+
resolve(root, 'gotry-state', 'async', raw)
|
|
166
|
+
];
|
|
167
|
+
const allowed = candidates.find((p)=>(underRoot(p, cwd) || underRoot(p, root)) && !hasDeniedSegment(p));
|
|
168
|
+
if (!allowed) {
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
error: `路径越界:${raw}`,
|
|
172
|
+
hint: `只读 ${root} 与 dsh 工作目录内的文本产物`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const ext = allowed.slice(allowed.lastIndexOf('.') + 1).toLowerCase();
|
|
176
|
+
if (!TEXT_EXT_LANG[ext]) {
|
|
177
|
+
return {
|
|
178
|
+
ok: false,
|
|
179
|
+
error: `不支持的文件类型 .${ext}`,
|
|
180
|
+
hint: `白名单:${Object.keys(TEXT_EXT_LANG).join('/')}`
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const st = await stat(allowed).catch(()=>null);
|
|
184
|
+
if (!st?.isFile()) return {
|
|
185
|
+
ok: false,
|
|
186
|
+
error: `文件不存在:${raw}`,
|
|
187
|
+
hint: '先 gotry_artifacts_list 看在册产物'
|
|
188
|
+
};
|
|
189
|
+
if (st.size > MAX_BYTES) return {
|
|
190
|
+
ok: false,
|
|
191
|
+
error: `文件过大(${st.size} bytes > ${MAX_BYTES})`
|
|
192
|
+
};
|
|
193
|
+
filePath = allowed;
|
|
194
|
+
text = await readFile(allowed, 'utf-8');
|
|
195
|
+
}
|
|
196
|
+
const allLines = text.split('\n');
|
|
197
|
+
const offset = Math.max(1, Math.min(opts.offset ?? 1, allLines.length));
|
|
198
|
+
const limit = Math.max(1, Math.min(opts.limit ?? MAX_WINDOW, MAX_WINDOW));
|
|
199
|
+
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
200
|
+
const ext = filePath.slice(filePath.lastIndexOf('.') + 1).toLowerCase();
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
path: filePath,
|
|
204
|
+
offset,
|
|
205
|
+
lines: slice.map((t, i)=>({
|
|
206
|
+
number: offset + i,
|
|
207
|
+
text: t
|
|
208
|
+
})),
|
|
209
|
+
totalLines: allLines.length,
|
|
210
|
+
lang: TEXT_EXT_LANG[ext] ?? 'text',
|
|
211
|
+
content: slice.join('\n'),
|
|
212
|
+
windowed: allLines.length > offset - 1 + slice.length
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
//# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/artifacts.ts
|