@yancyyu/openhermit 1.6.4 → 1.6.5
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 +52 -140
- package/bin/hermit.mjs +39 -258
- package/dist-renderer/assets/{ProjectEditorOverlay-BcjkdR8y.js → ProjectEditorOverlay-14yC9eQy.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-B9PP0b_t.js → TeamGraphOverlay-RAoJDOnS.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-CPquAmj5.js → _basePickBy-BhDOA0cG.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-A66EsJn2.js → _baseUniq-DjjY0tMN.js} +1 -1
- package/dist-renderer/assets/{arc-YLxbV3Qw.js → arc-CzoaaE90.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-wwpiLSwy.js → architectureDiagram-VXUJARFQ-D7ZTVCML.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-3CHE3NYR.js → blockDiagram-VD42YOAC-DDVOvV1H.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-K8hDNmEC.js → c4Diagram-YG6GDRKO-CMswQy_R.js} +1 -1
- package/dist-renderer/assets/channel-DjoT-21b.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-5OabZrhH.js → chunk-4BX2VUAB-aYfdMo75.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-v2kdM_aT.js → chunk-55IACEB6-DUhZJ0mV.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-C0Ju56SH.js → chunk-B4BG7PRW-BrGjG-E6.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-DPTWTKRm.js → chunk-DI55MBZ5-CfPUMKlq.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-DSkYppkv.js → chunk-FMBD7UC4-BMr0Vrdu.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-C_4cCLCl.js → chunk-QN33PNHL-C9gTfFZV.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-ojL7PmOD.js → chunk-QZHKN3VN-TTPdfwHP.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-D1g7Vl_v.js → chunk-TZMSLE5B-DPh3DBqf.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-C5mL3TLG.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-C5mL3TLG.js +1 -0
- package/dist-renderer/assets/clone-cS8bapaK.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-TJnGh924.js → cose-bilkent-S5V4N54A-BFbgRWOS.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-cPgfHhoX.js → dagre-6UL2VRFP-CfXdU7Il.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-BS5Y-RR6.js → diagram-PSM6KHXK-MdOyrxZl.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-D9AF7AGJ.js → diagram-QEK2KX5R-DpmnBR-A.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-DTFUadMS.js → diagram-S2PKOQOG-JXgp2H5I.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-DB_StEwC.js → erDiagram-Q2GNP2WA-CRfYO8W3.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-DGn40aPj.js → flowDiagram-NV44I4VS-BJvmgply.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-9NiFCSBT.js → ganttDiagram-JELNMOA3-BLXnpZat.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BdveeU3c.js → gitGraphDiagram-V2S2FVAM-BKtbxazQ.js} +1 -1
- package/dist-renderer/assets/{graph-aQYbgTDH.js → graph-D6n4zNVe.js} +1 -1
- package/dist-renderer/assets/{index-DmgKTZAa.js → index-3JdA9Dab.js} +529 -524
- package/dist-renderer/assets/{index-CaG9mf8s.css → index-C4x095x4.css} +1 -1
- package/dist-renderer/assets/{index-CWqPn0NY.js → index-CVdwMXdQ.js} +1 -1
- package/dist-renderer/assets/{index-DyEKO6GV.js → index-CkO1A9ft.js} +1 -1
- package/dist-renderer/assets/{index-oyepEosi.js → index-ar0tAtBS.js} +1 -1
- package/dist-renderer/assets/{index-CrCHolXN.js → index-c2GABSvo.js} +1 -1
- package/dist-renderer/assets/{index-DiAK42nd.js → index-trDFOqz-.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-Dmc_xn8U.js → infoDiagram-HS3SLOUP-Bqq_toop.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-D9LJr-B5.js → journeyDiagram-XKPGCS4Q-BRQs07r0.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CjOWoNys.js → kanban-definition-3W4ZIXB7-DHQnAijJ.js} +1 -1
- package/dist-renderer/assets/{layout-D6GzYK4K.js → layout-BljiazG5.js} +1 -1
- package/dist-renderer/assets/{linear-Dt3GyUQf.js → linear-fx8cDfux.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-XwY2hZr8.js → mindmap-definition-VGOIOE7T-DCfQbCFK.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BU4nfYd7.js → pieDiagram-ADFJNKIX-DyAFYy6H.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BYk6f63x.js → quadrantDiagram-AYHSOK5B-CCvqn9gd.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-kbadr_bU.js → requirementDiagram-UZGBJVZJ-JYde-Xl2.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-ZstP2Vth.js → sankeyDiagram-TZEHDZUN-C2Im6-aG.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-obK_-ssz.js → sequenceDiagram-WL72ISMW-X6JGIoEB.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-BgZDg0VT.js → stateDiagram-FKZM4ZOC-BJTDs8MY.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-CMa5sz7x.js → stateDiagram-v2-4FDKWEC3-DUrYslPS.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BOmCNnab.js → timeline-definition-IT6M3QCI-C7ECznev.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-BU0ha0Ww.js → treemap-GDKQZRPO-BRg3Zpk4.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-BzAHNASi.js → xychartDiagram-PRI3JC2R-CoZGyc2f.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +23 -17
- package/src/main/server.ts +179 -23
- package/src/main/services/teams-mvp/TaskDispatchService.ts +440 -0
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +36 -33
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +2 -0
- package/src/renderer/components/settings/SettingsTabs.tsx +8 -2
- package/src/renderer/components/settings/SettingsView.tsx +4 -0
- package/src/renderer/components/settings/sections/GeneralSection.tsx +168 -206
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +176 -0
- package/src/renderer/components/sidebar/SidebarSessions.tsx +31 -4
- package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +37 -0
- package/src/renderer/components/team/messages/MessageComposer.tsx +36 -228
- package/src/renderer/components/team/messages/MessagesPanel.tsx +0 -3
- package/src/renderer/store/slices/teamSlice.ts +30 -1
- package/src/shared/types/team.ts +73 -0
- package/dist-renderer/assets/channel-BSWYOYIc.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-mw4yABob.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-mw4yABob.js +0 -1
- package/dist-renderer/assets/clone-KtZfFt-o.js +0 -1
package/README.md
CHANGED
|
@@ -5,152 +5,84 @@
|
|
|
5
5
|
<h1 align="center">openHermit</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong
|
|
9
|
-
|
|
8
|
+
<strong>超级个体的 AI 基础设施:用代码重构公司形态</strong><br/>
|
|
9
|
+
告别“玩具级”的提示词角色扮演。用状态机接管工作流,让一个人成为一支军队。
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<a href="https://github.com/yancyuu/Hermit/releases/latest"><img src="https://img.shields.io/github/v/release/yancyuu/Hermit?style=flat-square&label=version&color=
|
|
14
|
-
<a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-
|
|
13
|
+
<a href="https://github.com/yancyuu/Hermit/releases/latest"><img src="https://img.shields.io/github/v/release/yancyuu/Hermit?style=flat-square&label=version&color=black" alt="最新版本" /></a>
|
|
14
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-black?style=flat-square" alt="许可证" /></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
## openHermit
|
|
19
|
+
## 💡 设计哲学:为什么我们需要 openHermit?
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
目前的 AI Agent 赛道充满了一个巨大的误区:**大家都在试图用人类的“HR 组织架构”来管理 AI。**
|
|
22
|
+
让大模型扮演“资深前端”或“产品经理”,本质上是在模仿人类的**职责驱动**(因岗设人)。这注定会走向死锁——因为人类有精力上限和认知边界,才需要划分部门和扯皮;而 AI 没有。
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
**openHermit 是一场对传统协作模式的降维打击。** 它的核心哲学只有两条:
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
### 1. 对创业者:组织形态的“资产化” (OPC 范式)
|
|
27
|
+
**OPC(One Person Company)** 不再是一个浪漫的口号,而是最具杠杆率的商业形态。
|
|
28
|
+
你不需要招聘、不需要对齐价值观、不需要处理情绪内耗。你是唯一的决策者,AI 是绝对服从的执行网格。openHermit 为你提供 **TPC(Team · Process · Channel)** 协作结构,将产品、开发、测试、运营转化为可并发现程。你的每一次业务跑通,都是在积累固化的“数字资产”,而不是随员工离职而流失的经验。
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
- Agent 的执行过程散在终端日志里,不方便复盘。
|
|
34
|
-
- 代码改动可以生成,但缺少明确的审查入口。
|
|
35
|
-
- 团队模板、Skills 和项目经验沉淀不下来。
|
|
36
|
-
|
|
37
|
-
openHermit 做的事很简单:把多个 Agent 的工作过程放进一个本地控制台里,让一个人也能按“团队”的方式分配任务、查看进度、接收结果和审查改动。
|
|
30
|
+
### 2. 对开发者与工程师:去中心化的“状态机”驱动 (State-Driven DAG)
|
|
31
|
+
**用管理机器的方式管理 Agent,而不是用管理人的方式。**
|
|
32
|
+
Agent 的协作本质不该是“角色扮演”,而是**分布式状态机**。任务流是一张 DAG,每个节点代表一种确定的状态(Pending → Running → Review → Done)。
|
|
33
|
+
* **拒绝死锁**:遇到网络波动或反爬环境,状态机自动 `Fail-Over` 切换运行时,流水线永远不会因为“超出角色职责”而卡死。
|
|
34
|
+
* **Zero-Trust 与 Local-First**:真正的生产力工具不能是黑盒。openHermit 坚持本地优先的零信任架构。配置、项目代码、长短期记忆同步都在你的本地机器完成,彻底掌控数据主权。
|
|
35
|
+
* **即用即走,上下文随状态流转**:Agent 不拥有固定岗位,只有当任务状态流转到 `Ready` 时,控制面才会将当前状态、上下文(Context)和能力(Skills)动态注入给对应的 Agent 运行时。
|
|
38
36
|
|
|
39
37
|
---
|
|
40
38
|
|
|
41
|
-
##
|
|
42
|
-
|
|
43
|
-
- **创建 AI 团队**:为前端、后端、测试、调研等工作创建不同团队或角色。
|
|
44
|
-
- **用看板管任务**:任务有状态、负责人、执行记录和结果,不再散在聊天里。
|
|
45
|
-
- **给 Agent 发消息**:向负责人或具体成员补充指令,保留上下文。
|
|
46
|
-
- **审查代码改动**:Agent 产生的变更进入审查流程,而不是直接混进代码库。
|
|
47
|
-
- **查看运行状态**:会话、日志、错误和启动状态集中展示,便于排障。
|
|
48
|
-
- **接入外部渠道**:通过 cc-connect 接入飞书、微信、Telegram 等消息来源。
|
|
49
|
-
- **复用团队配置**:把团队模板、Skills 和工作方式沉淀下来,下次直接复用。
|
|
50
|
-
|
|
51
|
-
---
|
|
39
|
+
## ⚙️ 核心架构:TPC 引擎
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
想要让 OPC 跑起来,你需要的不是一堆散落的终端窗口,而是 TPC 架构:
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|---|---|---|---|
|
|
59
|
-
| **Claude Code** | `claudecode` | ✅ 自动 | 默认推荐的编码运行时 |
|
|
60
|
-
| **Qoder** | `qoder` | ✅ 自动 | Claude Code 兼容运行时 |
|
|
61
|
-
| **Codex** | `codex` | 手动配置 | OpenAI/Codex 生态 |
|
|
62
|
-
| **Gemini** | `gemini` | 手动配置 | Google/Gemini 生态 |
|
|
63
|
-
| **OpenCode** | `opencode` | 手动配置 | 多 provider 开源运行时 |
|
|
64
|
-
| **Cursor** | `cursor` | 手动配置 | Cursor 相关运行时 |
|
|
65
|
-
| **Kimi** | `kimi` | 手动配置 | 长文本和文档任务 |
|
|
66
|
-
|
|
67
|
-
实际可用能力取决于本机安装的 CLI、账号状态和 cc-connect 的支持情况。
|
|
43
|
+
* **[ T ] Team(隔离与并发)**:给不同的工作建立独立团队。前端用 Claude Code 撸代码,后端用 Codex 构架,调研交由 Kimi。每个团队有独立的隔离运行时环境,但在全局协作看板上,任务可以跨团队无缝调度。
|
|
44
|
+
* **[ P ] Process(状态流转)**:彻底放弃“岗位 KPI”。一切以任务的原子化状态为核心,MCP Server 动态向 Agent 注入当前所需工具。
|
|
45
|
+
* **[ C ] Channel(全渠道触达)**:支持飞书、微信、Telegram、Discord、Slack 等 10+ 渠道接入。消息即指令,外部输入自动路由至对应 Agent 团队,支持独立上下文串或共享群会话,将你的 IM 变成公司级控制台。
|
|
68
46
|
|
|
69
47
|
---
|
|
70
48
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
openHermit 复用 cc-connect 的渠道能力。同一个 cc-connect project 可以绑定一个或多个渠道:
|
|
74
|
-
|
|
75
|
-
| 渠道 | 场景 |
|
|
76
|
-
|---|---|
|
|
77
|
-
| **飞书** | 企业级,支持消息卡片 |
|
|
78
|
-
| **微信** | 个人/小团队最顺手 |
|
|
79
|
-
| **Telegram** | 海外 / 技术向首选 |
|
|
80
|
-
| **Bridge** | openHermit 内部和自定义集成 |
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## 主要流程
|
|
85
|
-
|
|
86
|
-
### 1. 任务看板
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
用户:在看板里创建任务,例如 "重构支付模块"
|
|
90
|
-
↓
|
|
91
|
-
openHermit:把任务写入团队看板,并通知对应 Agent
|
|
92
|
-
↓
|
|
93
|
-
Agent:用 MCP 认领任务,开始执行
|
|
94
|
-
↓
|
|
95
|
-
完成后:结果写回看板,状态更新
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 2. 多团队
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
前端团队 (Claude Code) ——→ UI 任务
|
|
102
|
-
后端团队 (Codex) ——→ API 和业务逻辑
|
|
103
|
-
测试团队 (Gemini) ——→ 测试和验证
|
|
104
|
-
调研团队 (Kimi) ——→ 文档和资料整理
|
|
105
|
-
```
|
|
49
|
+
## 🛠️ 支持的 Agent 运行时与渠道
|
|
106
50
|
|
|
107
|
-
|
|
51
|
+
openHermit 不提供闭源模型,也不劫持你的代码。它是一个高度可扩展的本地环境壳层。
|
|
108
52
|
|
|
109
|
-
###
|
|
53
|
+
### 极客级的多端运行时支持
|
|
54
|
+
创建团队时,底层 MCP Server 会自动完成零配置注入。能否启动,取决于你的算力边界。
|
|
110
55
|
|
|
111
|
-
|
|
56
|
+
| 标识 | 运行时说明 | 标识 | 运行时说明 |
|
|
57
|
+
|:---|:---|:---|:---|
|
|
58
|
+
| `claudecode` | Anthropic 官方 CLI | `devin` | Cognition Devin |
|
|
59
|
+
| `codex` | OpenAI Codex CLI | `opencode` | OpenCode CLI |
|
|
60
|
+
| `cursor` | Cursor IDE Agent | `qoder` | Qoder CLI |
|
|
61
|
+
| `gemini` | Google Gemini CLI | `pi` | Inflection Pi |
|
|
62
|
+
| `iflow` | iFlow CLI | `acp` | Agent Communication Protocol |
|
|
63
|
+
| `kimi` | Moonshot Kimi | `tmux` | 经典 Tmux Session 桥接 |
|
|
112
64
|
|
|
113
|
-
###
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
claim_task — 认领任务,开始干活
|
|
120
|
-
complete_task — 完成任务,写入结果
|
|
121
|
-
create_task — 创建新任务分配给其他团队
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## 架构
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
你的指令 / 飞书消息 / 微信消息 / Telegram
|
|
130
|
-
↓
|
|
131
|
-
cc-connect
|
|
132
|
-
(渠道接入 + Agent 进程管理)
|
|
133
|
-
↓
|
|
134
|
-
openHermit
|
|
135
|
-
(团队管理 + 任务路由 + 看板 UI)
|
|
136
|
-
↓
|
|
137
|
-
MCP Server(hermit-tasks)
|
|
138
|
-
↓
|
|
139
|
-
Claude Code / Codex / Gemini / Qoder / ...
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
openHermit 和 cc-connect 都运行在本机。项目代码、任务数据和配置默认存放在本地。
|
|
65
|
+
### 模块化的信使网络 (Channels)
|
|
66
|
+
让外部世界无缝接入你的自动化流水线:
|
|
67
|
+
* **研发协同**:飞书(支持高级消息卡片)、钉钉、企业微信、Slack
|
|
68
|
+
* **极客与海外**:Telegram、Discord、LINE
|
|
69
|
+
* **私域与社群**:微信、QQ
|
|
70
|
+
* **自定义集成**:原生支持 openHermit 内部 Bridge 协议
|
|
143
71
|
|
|
144
72
|
---
|
|
145
73
|
|
|
146
|
-
##
|
|
74
|
+
## 🚀 极速部署
|
|
147
75
|
|
|
148
|
-
###
|
|
76
|
+
### 1. 安装启动
|
|
149
77
|
|
|
150
|
-
|
|
78
|
+
方式一:npx(免安装直接运行)
|
|
79
|
+
```bash
|
|
80
|
+
npx @yancyyu/openhermit@latest
|
|
81
|
+
```
|
|
151
82
|
|
|
83
|
+
方式二:全局安装
|
|
152
84
|
```bash
|
|
153
|
-
npm install -g @yancyyu/openhermit
|
|
85
|
+
npm install -g @yancyyu/openhermit@latest --prefer-online
|
|
154
86
|
openhermit
|
|
155
87
|
```
|
|
156
88
|
|
|
@@ -168,13 +100,11 @@ openhermit --daemon # 后台运行
|
|
|
168
100
|
openhermit status # 查看后台运行状态
|
|
169
101
|
openhermit stop # 停止后台服务
|
|
170
102
|
openhermit --port 8080 # 指定 Web 控制台端口
|
|
171
|
-
openhermit --no-cc-connect # 不自动启动 cc-connect
|
|
172
103
|
openhermit --version # 查看版本
|
|
173
104
|
openhermit update # 更新 openHermit
|
|
174
105
|
```
|
|
175
106
|
|
|
176
|
-
|
|
177
|
-
(9820)和 Bridge(9810),并生成本地 token。
|
|
107
|
+
首次启动会自动创建本地运行时配置,并生成本地 token。通常只需要打开 `http://127.0.0.1:5680` 使用 openHermit。
|
|
178
108
|
|
|
179
109
|
### 本地开发
|
|
180
110
|
|
|
@@ -187,41 +117,23 @@ pnpm dev
|
|
|
187
117
|
|
|
188
118
|
浏览器打开 `http://localhost:5174`
|
|
189
119
|
|
|
190
|
-
|
|
191
|
-
`~/.hermit/cc-connect/config.toml`。
|
|
120
|
+
开发模式默认连接本机运行时服务;生产 CLI 会优先使用 openHermit 管理的本地配置。
|
|
192
121
|
|
|
193
122
|
### 创建第一个 AI 团队
|
|
194
123
|
|
|
195
124
|
1. 点击「新建团队」
|
|
196
125
|
2. 填写团队名、选 harness(如 `claudecode`)
|
|
197
|
-
3.
|
|
126
|
+
3. 选择对应的本地项目和运行时
|
|
198
127
|
4. 保存 → 看板就绪,任务等你分配
|
|
199
128
|
|
|
200
129
|
---
|
|
201
130
|
|
|
202
|
-
## 文件结构
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
~/.hermit/
|
|
206
|
-
└── teams/
|
|
207
|
-
├── frontend/
|
|
208
|
-
│ ├── team.json # 团队配置(harness、bindProject、color)
|
|
209
|
-
│ └── tasks/board.json # 任务看板
|
|
210
|
-
└── backend/
|
|
211
|
-
├── team.json
|
|
212
|
-
└── tasks/board.json
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
所有数据存本地,可以用 Git 备份。
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
131
|
## 技术栈
|
|
220
132
|
|
|
221
133
|
- **前端**:React + TypeScript + Tailwind CSS + Zustand
|
|
222
134
|
- **后端**:Fastify(Node.js)
|
|
223
135
|
- **存储**:本地文件(`~/.hermit/`)
|
|
224
|
-
-
|
|
136
|
+
- **通信**:本地 Bridge WebSocket + Management HTTP API
|
|
225
137
|
- **协议**:MCP over HTTP(SSE + JSON-RPC)
|
|
226
138
|
|
|
227
139
|
---
|
package/bin/hermit.mjs
CHANGED
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
import { spawn, execSync } from 'node:child_process';
|
|
18
18
|
import crypto from 'node:crypto';
|
|
19
|
-
import { mkdirSync, writeFileSync, readFileSync, existsSync
|
|
19
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
20
20
|
import { createRequire } from 'node:module';
|
|
21
21
|
import os from 'node:os';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
-
import { fileURLToPath
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
24
|
|
|
25
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
26
26
|
const repoRoot = path.resolve(__dirname, '..');
|
|
@@ -56,23 +56,16 @@ Usage:
|
|
|
56
56
|
Options:
|
|
57
57
|
--port <number> HTTP server port (default: 5680)
|
|
58
58
|
--no-cc-connect Do not auto-start bundled cc-connect
|
|
59
|
-
--daemon Run in the background
|
|
60
59
|
--version Show current version
|
|
61
60
|
--help Show this help message
|
|
62
61
|
update Check and install updates
|
|
63
|
-
status Show background service status
|
|
64
|
-
stop Stop the background service
|
|
65
62
|
|
|
66
63
|
Examples:
|
|
67
|
-
openhermit
|
|
68
|
-
openhermit --
|
|
69
|
-
openhermit
|
|
70
|
-
openhermit
|
|
71
|
-
openhermit
|
|
72
|
-
openhermit --no-cc-connect # Start only openHermit
|
|
73
|
-
openhermit --version # Show version
|
|
74
|
-
openhermit update # Check for updates
|
|
75
|
-
npx @yancyyu/openhermit # Run without installing
|
|
64
|
+
npx @yancyyu/openhermit # Run without installing
|
|
65
|
+
npx @yancyyu/openhermit --port 8080
|
|
66
|
+
openhermit # After global install
|
|
67
|
+
openhermit --version
|
|
68
|
+
openhermit update
|
|
76
69
|
`);
|
|
77
70
|
process.exit(0);
|
|
78
71
|
}
|
|
@@ -87,180 +80,12 @@ const portIndex = args.indexOf('--port');
|
|
|
87
80
|
const port = portIndex !== -1 && args[portIndex + 1] ? args[portIndex + 1] : '5680';
|
|
88
81
|
const skipCcConnect = args.includes('--no-cc-connect') || process.env.HERMIT_NO_CC_CONNECT === '1';
|
|
89
82
|
const hermitHome = process.env.HERMIT_HOME || path.join(os.homedir(), '.hermit');
|
|
90
|
-
const daemonRequested = args.includes('--daemon');
|
|
91
|
-
const daemonChild = process.env.HERMIT_DAEMON_CHILD === '1';
|
|
92
|
-
const daemonPidPath = path.join(hermitHome, 'openhermit.pid');
|
|
93
|
-
const daemonLogPath = path.join(hermitHome, 'logs', 'openhermit.log');
|
|
94
83
|
const ccConnectConfigPath =
|
|
95
84
|
process.env.HERMIT_CC_CONNECT_CONFIG ||
|
|
96
85
|
process.env.CC_CONNECT_CONFIG ||
|
|
97
86
|
path.join(hermitHome, 'cc-connect', 'config.toml');
|
|
98
87
|
const bootstrapProjectName = '__openhermit_bootstrap__';
|
|
99
88
|
|
|
100
|
-
function readDaemonPid() {
|
|
101
|
-
try {
|
|
102
|
-
const raw = readFileSync(daemonPidPath, 'utf-8').trim();
|
|
103
|
-
const pid = Number.parseInt(raw, 10);
|
|
104
|
-
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
105
|
-
} catch {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function isPidRunning(pid) {
|
|
111
|
-
try {
|
|
112
|
-
process.kill(pid, 0);
|
|
113
|
-
return true;
|
|
114
|
-
} catch {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function removeDaemonPidFile() {
|
|
120
|
-
try {
|
|
121
|
-
unlinkSync(daemonPidPath);
|
|
122
|
-
} catch {
|
|
123
|
-
// Already gone.
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function signalDaemon(pid, signal) {
|
|
128
|
-
try {
|
|
129
|
-
process.kill(-pid, signal);
|
|
130
|
-
return true;
|
|
131
|
-
} catch {
|
|
132
|
-
// Fall back to direct process signal.
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
process.kill(pid, signal);
|
|
136
|
-
return true;
|
|
137
|
-
} catch {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function collectFallbackPids() {
|
|
143
|
-
const pids = new Set();
|
|
144
|
-
const commands = [
|
|
145
|
-
`lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null || true`,
|
|
146
|
-
'lsof -tiTCP:9810 -sTCP:LISTEN 2>/dev/null || true',
|
|
147
|
-
'lsof -tiTCP:9820 -sTCP:LISTEN 2>/dev/null || true',
|
|
148
|
-
"pgrep -f '@yancyyu/openhermit|openhermit/bin/hermit\\.mjs|src/main/server\\.ts|cc-connect' 2>/dev/null || true",
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
for (const command of commands) {
|
|
152
|
-
try {
|
|
153
|
-
const out = execSync(command, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
154
|
-
for (const line of out.split(/\s+/)) {
|
|
155
|
-
const pid = Number.parseInt(line, 10);
|
|
156
|
-
if (Number.isFinite(pid) && pid > 0 && pid !== process.pid) {
|
|
157
|
-
pids.add(pid);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} catch {
|
|
161
|
-
// Ignore missing lsof/pgrep or races with exiting processes.
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return [...pids];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async function stopFallbackProcesses() {
|
|
169
|
-
const pids = collectFallbackPids();
|
|
170
|
-
if (pids.length === 0) return false;
|
|
171
|
-
|
|
172
|
-
for (const pid of pids) {
|
|
173
|
-
signalDaemon(pid, 'SIGTERM');
|
|
174
|
-
}
|
|
175
|
-
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
176
|
-
for (const pid of pids) {
|
|
177
|
-
if (isPidRunning(pid)) {
|
|
178
|
-
signalDaemon(pid, 'SIGKILL');
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function printDaemonStatus() {
|
|
185
|
-
const pid = readDaemonPid();
|
|
186
|
-
if (pid && isPidRunning(pid)) {
|
|
187
|
-
console.log(`[openHermit] Running in background (pid ${pid})`);
|
|
188
|
-
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
189
|
-
process.exit(0);
|
|
190
|
-
}
|
|
191
|
-
if (pid) removeDaemonPidFile();
|
|
192
|
-
const fallbackPids = collectFallbackPids();
|
|
193
|
-
if (fallbackPids.length > 0) {
|
|
194
|
-
console.log(`[openHermit] Running without daemon pidfile (pids ${fallbackPids.join(', ')})`);
|
|
195
|
-
process.exit(0);
|
|
196
|
-
}
|
|
197
|
-
console.log('[openHermit] Not running');
|
|
198
|
-
process.exit(1);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function stopDaemon() {
|
|
202
|
-
const pid = readDaemonPid();
|
|
203
|
-
if (!pid || !isPidRunning(pid)) {
|
|
204
|
-
if (pid) removeDaemonPidFile();
|
|
205
|
-
const stoppedFallback = await stopFallbackProcesses();
|
|
206
|
-
console.log(stoppedFallback ? '[openHermit] Stopped orphaned service processes' : '[openHermit] Not running');
|
|
207
|
-
process.exit(0);
|
|
208
|
-
}
|
|
209
|
-
console.log(`[openHermit] Stopping background service (pid ${pid})...`);
|
|
210
|
-
signalDaemon(pid, 'SIGTERM');
|
|
211
|
-
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
212
|
-
if (isPidRunning(pid)) {
|
|
213
|
-
signalDaemon(pid, 'SIGKILL');
|
|
214
|
-
}
|
|
215
|
-
removeDaemonPidFile();
|
|
216
|
-
console.log('[openHermit] Stopped');
|
|
217
|
-
process.exit(0);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function startDaemon() {
|
|
221
|
-
const existingPid = readDaemonPid();
|
|
222
|
-
if (existingPid && isPidRunning(existingPid)) {
|
|
223
|
-
console.log(`[openHermit] Already running in background (pid ${existingPid})`);
|
|
224
|
-
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
225
|
-
process.exit(0);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
mkdirSync(path.dirname(daemonPidPath), { recursive: true });
|
|
229
|
-
mkdirSync(path.dirname(daemonLogPath), { recursive: true });
|
|
230
|
-
const out = openSync(daemonLogPath, 'a');
|
|
231
|
-
const err = openSync(daemonLogPath, 'a');
|
|
232
|
-
const childArgs = process.argv.slice(2).filter((arg) => arg !== '--daemon');
|
|
233
|
-
const child = spawn(process.execPath, [fileURLToPath(import.meta.url), ...childArgs], {
|
|
234
|
-
cwd: repoRoot,
|
|
235
|
-
detached: true,
|
|
236
|
-
env: {
|
|
237
|
-
...process.env,
|
|
238
|
-
HERMIT_DAEMON_CHILD: '1',
|
|
239
|
-
},
|
|
240
|
-
stdio: ['ignore', out, err],
|
|
241
|
-
});
|
|
242
|
-
child.unref();
|
|
243
|
-
closeSync(out);
|
|
244
|
-
closeSync(err);
|
|
245
|
-
writeFileSync(daemonPidPath, String(child.pid), 'utf-8');
|
|
246
|
-
console.log(`[openHermit] Started in background (pid ${child.pid})`);
|
|
247
|
-
console.log(`[openHermit] URL: http://127.0.0.1:${port}`);
|
|
248
|
-
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
249
|
-
process.exit(0);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (args.includes('status')) {
|
|
253
|
-
printDaemonStatus();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (args.includes('stop')) {
|
|
257
|
-
await stopDaemon();
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (daemonRequested && !daemonChild) {
|
|
261
|
-
startDaemon();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
89
|
// ---------------------------------------------------------------------------
|
|
265
90
|
// Update command
|
|
266
91
|
// ---------------------------------------------------------------------------
|
|
@@ -372,8 +197,8 @@ function hasProjectEntries(raw) {
|
|
|
372
197
|
|
|
373
198
|
function buildBootstrapProjectToml() {
|
|
374
199
|
return `
|
|
375
|
-
#
|
|
376
|
-
#
|
|
200
|
+
# Bootstrap project — cc-connect requires at least one project to start.
|
|
201
|
+
# This entry is automatically managed by openHermit and will be ignored at runtime.
|
|
377
202
|
[[projects]]
|
|
378
203
|
name = "${bootstrapProjectName}"
|
|
379
204
|
disabled_commands = ["*"]
|
|
@@ -386,13 +211,7 @@ work_dir = "${escapeTomlPath(hermitHome)}"
|
|
|
386
211
|
mode = "default"
|
|
387
212
|
|
|
388
213
|
[[projects.platforms]]
|
|
389
|
-
type = "
|
|
390
|
-
|
|
391
|
-
[projects.platforms.options]
|
|
392
|
-
channel_secret = "openhermit-bootstrap"
|
|
393
|
-
channel_token = "openhermit-bootstrap"
|
|
394
|
-
port = "0"
|
|
395
|
-
callback_path = "/openhermit-bootstrap"
|
|
214
|
+
type = "bridge"
|
|
396
215
|
`;
|
|
397
216
|
}
|
|
398
217
|
|
|
@@ -462,42 +281,8 @@ function resolveCcConnectRunner() {
|
|
|
462
281
|
return path.join(path.dirname(pkgPath), 'run.js');
|
|
463
282
|
}
|
|
464
283
|
|
|
465
|
-
function
|
|
466
|
-
return require.resolve('tsx');
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function resolveAliasLoaderRegister() {
|
|
470
|
-
const aliasLoaderUrl = pathToFileURL(path.join(__dirname, 'alias-loader.mjs')).href;
|
|
471
|
-
return `data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(${JSON.stringify(aliasLoaderUrl)}, pathToFileURL("./"));`;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function terminateProcessGroup(child, signal = 'SIGTERM') {
|
|
475
|
-
if (!child?.pid) return;
|
|
476
|
-
try {
|
|
477
|
-
process.kill(-child.pid, signal);
|
|
478
|
-
return;
|
|
479
|
-
} catch {
|
|
480
|
-
// Fall back to the direct child if it was not started as a process group.
|
|
481
|
-
}
|
|
482
|
-
try {
|
|
483
|
-
child.kill(signal);
|
|
484
|
-
} catch {
|
|
485
|
-
// Already gone.
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let shuttingDown = false;
|
|
490
|
-
function shutdown(exitCode = 0) {
|
|
491
|
-
if (shuttingDown) return;
|
|
492
|
-
shuttingDown = true;
|
|
493
|
-
console.log('\n[openHermit] Shutting down...');
|
|
494
|
-
terminateProcessGroup(serverProcess, 'SIGTERM');
|
|
495
|
-
terminateProcessGroup(ccConnectProcess, 'SIGTERM');
|
|
496
|
-
setTimeout(() => {
|
|
497
|
-
terminateProcessGroup(serverProcess, 'SIGKILL');
|
|
498
|
-
terminateProcessGroup(ccConnectProcess, 'SIGKILL');
|
|
499
|
-
process.exit(exitCode);
|
|
500
|
-
}, 2_000).unref();
|
|
284
|
+
function resolveTsxCli() {
|
|
285
|
+
return require.resolve('tsx/cli');
|
|
501
286
|
}
|
|
502
287
|
|
|
503
288
|
let ccConnectProcess = null;
|
|
@@ -517,7 +302,6 @@ if (!skipCcConnect) {
|
|
|
517
302
|
console.log(`[openHermit] cc-connect config: ${ccConnectConfigPath}`);
|
|
518
303
|
ccConnectProcess = spawn(process.execPath, [resolveCcConnectRunner(), '-config', ccConnectConfigPath], {
|
|
519
304
|
cwd: repoRoot,
|
|
520
|
-
detached: true,
|
|
521
305
|
env: {
|
|
522
306
|
...process.env,
|
|
523
307
|
CC_CONNECT_TOKEN: ccTokens.managementToken,
|
|
@@ -526,9 +310,9 @@ if (!skipCcConnect) {
|
|
|
526
310
|
},
|
|
527
311
|
stdio: 'inherit',
|
|
528
312
|
});
|
|
529
|
-
const ready = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken,
|
|
313
|
+
const ready = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 30_000);
|
|
530
314
|
if (!ready) {
|
|
531
|
-
console.warn('[openHermit] cc-connect did not become ready within
|
|
315
|
+
console.warn('[openHermit] cc-connect did not become ready within 30s; openHermit will keep trying via API.');
|
|
532
316
|
}
|
|
533
317
|
}
|
|
534
318
|
}
|
|
@@ -570,42 +354,39 @@ if (!existsSync(distRenderererDir) || !existsSync(path.join(distRenderererDir, '
|
|
|
570
354
|
// Start the server
|
|
571
355
|
console.log('[openHermit] Launching server...\n');
|
|
572
356
|
|
|
573
|
-
const serverProcess = spawn(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
CC_CONNECT_CONFIG: ccConnectConfigPath,
|
|
589
|
-
},
|
|
590
|
-
stdio: 'inherit',
|
|
591
|
-
}
|
|
592
|
-
);
|
|
357
|
+
const serverProcess = spawn(process.execPath, [resolveTsxCli(), 'src/main/server.ts'], {
|
|
358
|
+
cwd: repoRoot,
|
|
359
|
+
env: {
|
|
360
|
+
...process.env,
|
|
361
|
+
PORT: port,
|
|
362
|
+
HOST: process.env.HOST || '127.0.0.1',
|
|
363
|
+
NODE_ENV: 'production',
|
|
364
|
+
HERMIT_HOME: hermitHome,
|
|
365
|
+
CC_CONNECT_TOKEN: ccTokens.managementToken,
|
|
366
|
+
CC_CONNECT_MANAGEMENT_TOKEN: ccTokens.managementToken,
|
|
367
|
+
CC_CONNECT_BRIDGE_TOKEN: ccTokens.bridgeToken,
|
|
368
|
+
CC_CONNECT_CONFIG: ccConnectConfigPath,
|
|
369
|
+
},
|
|
370
|
+
stdio: 'inherit',
|
|
371
|
+
});
|
|
593
372
|
|
|
594
373
|
serverProcess.on('exit', (code) => {
|
|
595
|
-
if (shuttingDown) return;
|
|
596
|
-
terminateProcessGroup(ccConnectProcess, 'SIGTERM');
|
|
597
374
|
if (code !== 0) {
|
|
598
375
|
console.error(`[openHermit] Server exited with code ${code}`);
|
|
599
376
|
process.exit(code ?? 1);
|
|
600
377
|
}
|
|
601
|
-
process.exit(0);
|
|
602
378
|
});
|
|
603
379
|
|
|
604
|
-
process.on('SIGINT', () =>
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
380
|
+
process.on('SIGINT', () => {
|
|
381
|
+
console.log('\n[openHermit] Shutting down...');
|
|
382
|
+
serverProcess.kill('SIGINT');
|
|
383
|
+
ccConnectProcess?.kill('SIGINT');
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
process.on('SIGTERM', () => {
|
|
387
|
+
console.log('\n[openHermit] Shutting down...');
|
|
388
|
+
serverProcess.kill('SIGTERM');
|
|
389
|
+
ccConnectProcess?.kill('SIGTERM');
|
|
609
390
|
});
|
|
610
391
|
|
|
611
392
|
console.log(`[openHermit] Server starting on http://127.0.0.1:${port}`);
|
package/dist-renderer/assets/{ProjectEditorOverlay-BcjkdR8y.js → ProjectEditorOverlay-14yC9eQy.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{bD as de,bE as w,bF as Re,r as s,bG as Ce,bH as As,bI as Ls,E as pe,bJ as Os,bK as zs,q as e,bL as $s,bM as Ks,bN as Kt,bO as Gt,bP as Gs,bQ as Bs,bR as Us,bS as Vs,bT as H,bU as X,bV as sn,bW as rn,bX as q,bY as mt,bZ as he,b_ as on,b$ as P,c0 as Ws,c1 as tt,c2 as Hs,c3 as Xs,c4 as qs,c5 as Ys,c6 as Qs,c7 as Zs,c8 as Js,c9 as er,ca as tr,cb as nr,cc as sr,cd as rr,ce as or,cf as ar,cg as Bt,ch as ir,ci as an,cj as cn,ck as cr,cl as lr,cm as dr,cn as ur,co as xr,cp as je,cq as nt,cr as fr,cs as Ee,ct as hr,cu as gt,cv as ln,cw as mr,cx as dn,cy as un,cz as bt,cA as pr,cB as gr,cC as br,cD as st,cE as vr,cF as Be,cG as $,cH as Ie,cI as Cr,cJ as jr,cK as yr,cL as Nr,cM as wr,cN as Sr,cO as kr,cP as Mr,cQ as Tr,cR as Er,cS as Rr,cT as Ir,cU as Ze,cV as Dr,cW as Pr,cX as vt,cY as xn,cZ as fn,c_ as hn,c$ as _r,d0 as Fr,d1 as Ar,d2 as mn,d3 as pn,d4 as gn,d5 as Le,d6 as Oe,d7 as ze,d8 as $e,d9 as Ye,da as Qe,db as bn,dc as vn,dd as Lr,de as Ue,df as Or,dg as zr,dh as $r,di as Kr,dj as Gr,dk as Br,dl as Ur,dm as Vr,dn as Wr,dp as Hr,dq as Xr,dr as qr,ds as Yr,dt as Qr,du as Zr,dv as Fe,dw as Jr,dx as Ut,dy as eo,dz as to,dA as Vt,dB as no,dC as so,dD as ro,dE as Wt}from"./index-
|
|
1
|
+
import{bD as de,bE as w,bF as Re,r as s,bG as Ce,bH as As,bI as Ls,E as pe,bJ as Os,bK as zs,q as e,bL as $s,bM as Ks,bN as Kt,bO as Gt,bP as Gs,bQ as Bs,bR as Us,bS as Vs,bT as H,bU as X,bV as sn,bW as rn,bX as q,bY as mt,bZ as he,b_ as on,b$ as P,c0 as Ws,c1 as tt,c2 as Hs,c3 as Xs,c4 as qs,c5 as Ys,c6 as Qs,c7 as Zs,c8 as Js,c9 as er,ca as tr,cb as nr,cc as sr,cd as rr,ce as or,cf as ar,cg as Bt,ch as ir,ci as an,cj as cn,ck as cr,cl as lr,cm as dr,cn as ur,co as xr,cp as je,cq as nt,cr as fr,cs as Ee,ct as hr,cu as gt,cv as ln,cw as mr,cx as dn,cy as un,cz as bt,cA as pr,cB as gr,cC as br,cD as st,cE as vr,cF as Be,cG as $,cH as Ie,cI as Cr,cJ as jr,cK as yr,cL as Nr,cM as wr,cN as Sr,cO as kr,cP as Mr,cQ as Tr,cR as Er,cS as Rr,cT as Ir,cU as Ze,cV as Dr,cW as Pr,cX as vt,cY as xn,cZ as fn,c_ as hn,c$ as _r,d0 as Fr,d1 as Ar,d2 as mn,d3 as pn,d4 as gn,d5 as Le,d6 as Oe,d7 as ze,d8 as $e,d9 as Ye,da as Qe,db as bn,dc as vn,dd as Lr,de as Ue,df as Or,dg as zr,dh as $r,di as Kr,dj as Gr,dk as Br,dl as Ur,dm as Vr,dn as Wr,dp as Hr,dq as Xr,dr as qr,ds as Yr,dt as Qr,du as Zr,dv as Fe,dw as Jr,dx as Ut,dy as eo,dz as to,dA as Vt,dB as no,dC as so,dD as ro,dE as Wt}from"./index-3JdA9Dab.js";import"./splashScene-C8lWNnm4.js";/**
|
|
2
2
|
* @license lucide-react v0.577.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as R,m as V,n as q,o as D,r as n,q as a,T as P,G as W,v as I,w as Z,x as z,y as O,z as B}from"./index-
|
|
1
|
+
import{u as R,m as V,n as q,o as D,r as n,q as a,T as P,G as W,v as I,w as Z,x as z,y as O,z as B}from"./index-3JdA9Dab.js";import"./splashScene-C8lWNnm4.js";const M=({teamName:s,onClose:c,onPinAsTab:h,sidebarVisible:C,onToggleSidebar:v,onSendMessage:u,onOpenTaskDetail:i,onOpenMemberProfile:d})=>{const T=R(s),{openTeamPage:k,commitOwnerSlotDrop:w,commitOwnerGridOrderDrop:S,setLayoutMode:x}=V(s),{sidebarVisible:m,toggleSidebarVisible:f}=q(),{dialog:G,openCreateTaskDialog:g}=D(s),b=C??m,y=v??f,t=n.useCallback(o=>e=>window.dispatchEvent(new CustomEvent(`graph:${o}`,{detail:{teamName:s,taskId:e}})),[s]),A=n.useMemo(()=>({onStartTask:t("start-task"),onCompleteTask:t("complete-task"),onApproveTask:t("approve-task"),onRequestReview:t("request-review"),onRequestChanges:t("request-changes"),onCancelTask:t("cancel-task"),onMoveBackToDone:t("move-back-to-done"),onDeleteTask:t("delete-task")}),[t]),j=n.useCallback(()=>{k(),c()},[c,k]),H=n.useCallback(()=>{g("")},[g]),E={onNodeDoubleClick:n.useCallback(o=>{o.kind==="task"?i?.(o.taskId):o.kind==="member"&&d?.(o.memberName)},[i,d]),onSendMessage:n.useCallback(o=>u?.(o),[u]),onOpenTaskDetail:n.useCallback(o=>i?.(o),[i]),onOpenMemberProfile:n.useCallback(o=>d?.(o),[d])};return a.jsxs("div",{className:"fixed inset-0 z-50 flex overflow-hidden",style:{background:"#050510"},children:[b?a.jsx(P,{teamName:s,surface:"graph-overlay",isActive:!0,isFocused:!0}):null,a.jsx(W,{data:T,events:E,isSurfaceActive:!0,onRequestClose:c,onRequestPinAsTab:h,onOpenTeamPage:j,onCreateTask:H,onToggleSidebar:y,isSidebarVisible:b,renderTopToolbarContent:()=>a.jsx(B,{teamName:s}),onLayoutModeChange:x,onOwnerSlotDrop:w,onOwnerGridOrderDrop:S,className:"team-graph-view min-w-0 flex-1",renderHud:o=>{const e=o,{getViewportSize:r,focusNodeIds:l,filters:p}=e;return a.jsxs(a.Fragment,{children:[a.jsx(z,{teamName:s,getTransientHandoffSnapshot:e.getTransientHandoffSnapshot,getCameraZoom:e.getCameraZoom,worldToScreen:e.worldToScreen,getNodeWorldPosition:e.getNodeWorldPosition,focusNodeIds:l,focusEdgeIds:e.focusEdgeIds??null}),a.jsx(O,{teamName:s,nodes:T.nodes,getActivityWorldRect:e.getActivityWorldRect,getCameraZoom:e.getCameraZoom,worldToScreen:e.worldToScreen,getNodeWorldPosition:e.getNodeWorldPosition,getViewportSize:r,focusNodeIds:l,enabled:p?.showActivity??!0,onOpenTaskDetail:i,onOpenMemberProfile:d})]})},renderEdgeOverlay:({edge:o,sourceNode:e,targetNode:r,onClose:l,onSelectNode:p})=>a.jsx(Z,{teamName:s,edge:o,sourceNode:e,targetNode:r,onClose:l,onSelectNode:p,onOpenTaskDetail:i}),renderOverlay:({node:o,onClose:e})=>a.jsx(I,{node:o,teamName:s,onClose:e,onSendMessage:r=>{u?.(r),e()},onCreateTask:g,onOpenTaskDetail:r=>{i?.(r),e()},onOpenMemberProfile:r=>{d?.(r),e()},...A})}),G]})};export{M as TeamGraphOverlay};
|