avenic 1.0.0
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/LICENSE +21 -0
- package/README.md +234 -0
- package/package.json +30 -0
- package/scripts/skills.mjs +13 -0
- package/scripts/watchdog.mjs +50 -0
- package/src/cli/dispatcher.mjs +439 -0
- package/src/cli/self-update.mjs +27 -0
- package/src/cli/skills-cli.mjs +1060 -0
- package/src/cli/watchdog.mjs +30 -0
- package/vendor/core-src/index.mjs +143 -0
- package/vendor/core-src/runtime/adapters/claude.mjs +81 -0
- package/vendor/core-src/runtime/adapters/codex.mjs +142 -0
- package/vendor/core-src/runtime/adapters/index.mjs +9 -0
- package/vendor/core-src/runtime/adapters/opencode.mjs +76 -0
- package/vendor/core-src/runtime/agents.mjs +39 -0
- package/vendor/core-src/runtime/config.mjs +227 -0
- package/vendor/core-src/runtime/gitignore.mjs +69 -0
- package/vendor/core-src/runtime/process.mjs +41 -0
- package/vendor/core-src/runtime/project-root.mjs +42 -0
- package/vendor/core-src/runtime/sessions.mjs +312 -0
- package/vendor/core-src/skills/catalog.mjs +130 -0
- package/vendor/core-src/skills/direct.mjs +209 -0
- package/vendor/core-src/skills/git.mjs +93 -0
- package/vendor/core-src/skills/ids.mjs +40 -0
- package/vendor/core-src/skills/install.mjs +357 -0
- package/vendor/core-src/skills/packs.mjs +256 -0
- package/vendor/core-src/skills/paths.mjs +92 -0
- package/vendor/core-src/skills/sources.mjs +246 -0
- package/vendor/core-src/skills/ui.mjs +21 -0
- package/vendor/core-src/skills/vendor.mjs +57 -0
- package/vendor/core-src/util/fail.mjs +3 -0
- package/vendor/core-src/util/fs.mjs +14 -0
- package/vendor/core-src/util/json.mjs +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Echo Kang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Avenic CLI
|
|
2
|
+
|
|
3
|
+
Avenic 是一个命令行工具,用于统一管理编码 Agent(Claude Code、Codex、OpenCode)的运行时配置与 Skills,支持 Windows、macOS 和 Linux。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
要求 Node.js ≥ 18.17。
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g avenic
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
安装提供两个命令:`avenic` 与简写 `ave`,二者等价。
|
|
14
|
+
|
|
15
|
+
卸载:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm uninstall -g avenic
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
avenic claude init # 初始化 Claude Code(默认全局认证 + 项目便携会话)
|
|
25
|
+
avenic claude init --auth project # 项目级认证(凭据随项目,不进 Git)
|
|
26
|
+
avenic codex init --sessions global # Codex 会话留在全局原生存储
|
|
27
|
+
avenic claude sessions import # 把本机会话复制进项目便携存储
|
|
28
|
+
avenic claude sessions writeback # 把项目便携会话显式回写本机
|
|
29
|
+
avenic sessions git off # 会话不进 Git
|
|
30
|
+
avenic catalog add <owner/repo> # 导入 catalog(可导入多个,会打印 Pack 预览树)
|
|
31
|
+
avenic catalog select # 上下键切换当前 catalog
|
|
32
|
+
avenic skills install # 安装默认 Pack(common)
|
|
33
|
+
avenic skills add <owner/repo> # 从任意 GitHub 仓库直接安装 Skills
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Agent 运行时
|
|
37
|
+
|
|
38
|
+
### 命令
|
|
39
|
+
|
|
40
|
+
`<agent>` 为 `claude`、`codex`、`opencode` 之一。
|
|
41
|
+
|
|
42
|
+
| 命令 | 说明 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `avenic <agent> init [--auth global\|project] [--sessions global\|project]` | 初始化运行时 |
|
|
45
|
+
| `avenic <agent> deinit [--purge]` | 移除运行时;`--purge` 一并删除数据 |
|
|
46
|
+
| `avenic <agent> auth [global\|project\|reset]` | 设置认证作用域;不带参数时查看当前状态 |
|
|
47
|
+
| `avenic <agent> status` | 查看该 Agent 的配置与状态 |
|
|
48
|
+
| `avenic <agent> sessions import\|writeback\|status` | 管理便携会话 |
|
|
49
|
+
| `avenic <agent> [args...]` | 启动 Agent,其余参数透传给官方 CLI |
|
|
50
|
+
| `avenic status` | 三个 Agent 一览 |
|
|
51
|
+
| `avenic doctor` | 环境自检 |
|
|
52
|
+
| `avenic sessions git on\|off\|status` | 便携会话的 Git 同步开关 |
|
|
53
|
+
|
|
54
|
+
`init` 的输出会列出实际创建或修改的内容(`.agents/runtime.json`、`.agents/sessions/<agent>/`、`.agents/local/<agent>/`、`.gitignore`)及使用方法。`init` 可重复执行:结构已符合时不作修改,有缺失时只增量补齐。
|
|
55
|
+
|
|
56
|
+
### 认证作用域
|
|
57
|
+
|
|
58
|
+
- `global`(默认):使用 Agent 的本机全局凭据(如 `~/.claude`、`~/.codex`)。
|
|
59
|
+
- `project`:凭据与配置保存在项目 `.agents/local/<agent>/`(自动 gitignore)。同一 Agent 在不同项目可使用不同账号。
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
avenic claude auth project # 切换到项目认证
|
|
63
|
+
avenic claude auth global # 切回全局
|
|
64
|
+
avenic claude auth reset # 清除本项目覆盖,恢复默认
|
|
65
|
+
avenic claude auth # 查看当前生效的认证作用域
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 会话记录
|
|
69
|
+
|
|
70
|
+
- `project`(默认):启动前把项目内的会话记录提供给 Agent,退出后把本次会话写回 `.agents/sessions/<agent>/`(可跨设备迁移),并把本机原生存储恢复到启动前的状态。会话只更新在项目里,全局存储完全不受影响;删除项目后,会话随项目消失。
|
|
71
|
+
- `global`:会话直接留在 Agent 的原生全局存储,不产生项目副本。
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
avenic codex sessions import # 全局会话 → 项目会话记录(复制不删除)
|
|
75
|
+
avenic codex sessions writeback # 项目会话记录 → 原生存储(显式回写)
|
|
76
|
+
avenic codex sessions status
|
|
77
|
+
avenic sessions git on|off|status # 项目会话记录的 Git 同步开关
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
若同一会话在本机原生存储与项目内都有记录,`avenic <agent>` 启动时以项目内的会话记录为准(覆盖本机副本)。运行 `avenic claude` 优先使用项目内的会话记录;要用全局会话记录时,直接运行 `claude`(其他 Agent 同理直接运行官方 CLI)即可。
|
|
81
|
+
|
|
82
|
+
项目内的会话记录不会自动回写本机原生存储;需要回写时显式执行 `avenic <agent> sessions writeback`:原生存储中该项目的会话记录会被项目内记录覆盖;原生存储中没有该项目的会话记录时,则按 Agent 的原生目录结构创建后放入会话,效果与直接用官方 CLI 产生的会话一致。
|
|
83
|
+
|
|
84
|
+
同一项目同一 Agent 可同时启动多个 `avenic` 会话:第一个启动时保存原生存储快照,最后一个退出时回滚。每次启动还会派一个脱离终端的后台看门狗进程监视本次会话:直接关闭终端、关闭 VS Code、强杀进程都不会影响收尾——看门狗检测到 CLI 进程消失后自动把会话收进项目并恢复原生存储原状。看门狗自身被终止(断电、强制重启)且系统临时目录被清理时无法自动补救,残留会话留在原生存储里,可手动执行 `avenic <agent> sessions import` 收进项目;临时目录还在时,下次启动 `avenic` 会自动补救。
|
|
85
|
+
|
|
86
|
+
> OpenCode 例外:其会话存储由官方 CLI 自行管理,`avenic opencode` 启动后原生存储仍保留本次运行产生的会话,不受上述回滚保护。
|
|
87
|
+
|
|
88
|
+
> 项目会话可能包含提示词、源码、命令输出、路径与密钥;仅在可信仓库中提交会话。
|
|
89
|
+
|
|
90
|
+
## Skills
|
|
91
|
+
|
|
92
|
+
### Catalog
|
|
93
|
+
|
|
94
|
+
Catalog 是一个 git 仓库,公开或私有均可;私有仓库使用本机 git 认证(gh、SSH 或 credential helper),CLI 不接触 token。标准结构:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
my-catalog/
|
|
98
|
+
├── sources.lock.json # 上游源登记:id、仓库地址、锁定 commit、Skill 根目录、许可证
|
|
99
|
+
├── packs/
|
|
100
|
+
│ ├── common.json # Pack 定义(common 为默认 Pack,安装时自动包含)
|
|
101
|
+
│ └── development.json
|
|
102
|
+
├── skills/ # 按源归档的 Skill 副本
|
|
103
|
+
│ └── <source-id>/<skill-name>/SKILL.md
|
|
104
|
+
└── licenses/ # 上游许可证(登记源时自动保存)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Pack 定义示例(`packs/development.json`):
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"schemaVersion": 1,
|
|
112
|
+
"id": "development",
|
|
113
|
+
"name": "Development",
|
|
114
|
+
"description": "Research, coding, and review workflows.",
|
|
115
|
+
"sources": [{ "source": "example-source", "skills": ["beta", "gamma"] }]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
一个 catalog 可聚合多个上游源;Pack 从这些源挑选 Skill(可跨源),`description` 是 Pack 的用途说明,会显示在 `catalog add` 的预览树中。
|
|
120
|
+
|
|
121
|
+
#### 使用
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
avenic catalog add <owner/repo> # 导入 catalog(owner/repo[#ref]、URL 或本地路径),成功后打印 Pack 预览树
|
|
125
|
+
avenic skills install # 安装默认 Pack(common)
|
|
126
|
+
avenic skills install development # 安装多个 Pack;common 自动包含
|
|
127
|
+
avenic skills uninstall development # 卸载 Pack(不带参数移除全部受管理 Skills)
|
|
128
|
+
avenic skills -g development # 安装到全局作用域(skills <pack> 是 install 的简写)
|
|
129
|
+
avenic skills tree [pack...] # 查看 catalog 内容树
|
|
130
|
+
avenic skills packs # 列出可用 Packs
|
|
131
|
+
avenic skills status [-g] # 当前安装状态
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`catalog add` 拉取失败不影响源保存,之后 `avenic catalog sync` 重试。可反复 `add` 注册多个 catalog,同一时间生效一个(该 catalog 聚合的多个上游源共享所有 Pack):
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
avenic catalog select [name|spec] # ↑/↓ 选择当前 catalog(无终端时打印列表)
|
|
138
|
+
avenic catalog list # 列出已注册 catalog(> 标记当前)
|
|
139
|
+
avenic catalog default # 查看当前 catalog
|
|
140
|
+
avenic catalog sync # 拉取或更新缓存(~/.config/avenic/catalog/)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
每次安装把 catalog commit 写入项目锁 `.avenic.lock.json`,跨设备可复现。
|
|
144
|
+
|
|
145
|
+
> 默认 catalog 为维护者提供的示例;使用前请通过 `avenic catalog add <owner/repo>` 指向自己的 catalog。
|
|
146
|
+
|
|
147
|
+
#### 构造与维护 catalog
|
|
148
|
+
|
|
149
|
+
初始化骨架、登记上游、建 Pack、校验后推送:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
mkdir my-catalog && cd my-catalog
|
|
153
|
+
git init
|
|
154
|
+
mkdir -p packs skills
|
|
155
|
+
echo '{"schemaVersion":1,"sources":[]}' > sources.lock.json
|
|
156
|
+
avenic catalog pack-add common --name Common # 新建 Pack
|
|
157
|
+
avenic catalog skill-add <owner/repo> --pack common # 登记第一个上游源并收录其全部 Skill
|
|
158
|
+
avenic catalog pack-add development --name Development
|
|
159
|
+
avenic catalog skill-add <owner/repo> skill-a skill-b --pack development
|
|
160
|
+
avenic catalog doctor # 校验结构
|
|
161
|
+
git add -A && git commit -m "catalog" && git push
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`skill-add` 自动登记未收录的上游源(锁定 commit、保存许可证);省略 `[skill...]` 收录该源全部 Skill;可反复 `skill-add` 聚合多个上游源。
|
|
165
|
+
|
|
166
|
+
维护命令(在 catalog 克隆内运行):
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
avenic catalog skill-add <source-id|owner/repo> [skill...] [--pack <pack,pack>]
|
|
170
|
+
avenic catalog remove <source-id|owner/repo> <skill...> [--pack <pack,pack>] # 从 Pack 移除 Skill;无 Pack 引用时删除副本
|
|
171
|
+
avenic catalog pack-add <id> [--name <name>] [--description <text>]
|
|
172
|
+
avenic catalog pack-remove <pack...> # 删除 Pack(common 不可删),无引用 Skill 一并清理
|
|
173
|
+
avenic catalog source-add <id> <repo> [--name <name>] [--skill-root <path>] [--license <path>]
|
|
174
|
+
avenic catalog update [source] [--check] # 跟进上游更新,锁定新 commit
|
|
175
|
+
avenic catalog doctor # 校验 catalog
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### 连接私有 Skills 仓库
|
|
179
|
+
|
|
180
|
+
私有仓库不需要额外配置:CLI 不接触 token,clone 与 fetch 全部由本机 git 完成。以连接私有 catalog `Echo-Kang-hub/avenic-catalog` 为例:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
gh auth login # 1. 登录 GitHub(或改用 SSH key,二选一,只需一次)
|
|
184
|
+
avenic catalog add Echo-Kang-hub/avenic-catalog # 2. 设置 catalog 源(换成 <你的用户名>/<你的仓库>),终端会打印 Pack 预览树
|
|
185
|
+
avenic catalog sync # 3. 验证可拉取(输出 40 位 commit 即成功)
|
|
186
|
+
avenic skills install # 4. 安装默认 Pack(common)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- Windows 上 HTTPS 方式默认使用 Git Credential Manager(首次自动弹窗登录);也可以使用 SSH 地址:`avenic catalog add git@github.com:<owner>/<repo>.git`
|
|
190
|
+
- `avenic skills add <owner/repo>` 从单个私有仓库安装 Skill,认证方式相同
|
|
191
|
+
|
|
192
|
+
| 现象 | 处理 |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `schannel: failed to receive handshake / SSL/TLS connection failed` | 网络或代理阻断了到 github.com 的 TLS 连接,与认证无关;检查代理/VPN,或改用 SSH 地址 |
|
|
195
|
+
| `Unable to fetch catalog` + `Check your GitHub authentication` | git 没有该私有仓库的访问权限;先运行 `gh auth status` 或 `ssh -T git@github.com` |
|
|
196
|
+
| 换回其他 catalog | 已注册的直接 `avenic catalog select` 切换;未注册的再次 `avenic catalog add <spec>` |
|
|
197
|
+
|
|
198
|
+
### 直接源
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
avenic skills add <owner/repo> [skill...] [-g]
|
|
202
|
+
avenic skills remove <skill...> # 撤回:移除通过 add 安装的 Skills
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
从任意 GitHub 仓库直接安装 Skill(递归发现),锁定 commit 并保存许可证。公开仓库直接可用;私有仓库使用本机 git 认证(`gh auth login` 或 SSH)。与 Pack 管理的 Skill 重名会被拒绝。
|
|
206
|
+
|
|
207
|
+
## 撤回操作
|
|
208
|
+
|
|
209
|
+
| 操作 | 撤回 |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `avenic <agent> init` | `avenic <agent> deinit`(加 `--purge` 连会话数据一起删除) |
|
|
212
|
+
| `avenic <agent> auth project` / `auth global` | 执行相反设置,或 `auth reset` 恢复默认 |
|
|
213
|
+
| `avenic <agent> sessions import` | 只复制不删除;清除项目副本:`avenic <agent> deinit --purge` 后重新 `init` |
|
|
214
|
+
| `avenic sessions git off` | `avenic sessions git on` |
|
|
215
|
+
| `avenic skills install [pack...]`(简写 `avenic skills [pack...]`) | `avenic skills uninstall`(全部)或 `avenic skills uninstall <pack>` |
|
|
216
|
+
| `avenic skills add <owner/repo>` | `avenic skills remove <skill...>` |
|
|
217
|
+
| `avenic catalog add <spec>` | `avenic catalog select` 选回已注册 catalog,或再次 `avenic catalog add <原 spec>` |
|
|
218
|
+
| `avenic self-update` | `npm install -g avenic@<旧版本>` |
|
|
219
|
+
|
|
220
|
+
## 自更新
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
avenic self-update
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
从 npm 更新 Avenic 到最新版本。
|
|
227
|
+
|
|
228
|
+
## 开发者
|
|
229
|
+
|
|
230
|
+
构建、测试与发布流程见 [docs/development.md](docs/development.md)。
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
[MIT](LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avenic",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Portable Skills, runtime configuration, and sessions for coding agents",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"imports": {
|
|
9
|
+
"#core": "./vendor/core-src/index.mjs",
|
|
10
|
+
"#core/*": "./vendor/core-src/*"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"avenic": "scripts/skills.mjs",
|
|
14
|
+
"ave": "scripts/skills.mjs"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"prepack": "node ../../scripts/sync-core.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"scripts/",
|
|
21
|
+
"src/",
|
|
22
|
+
"vendor/core-src/"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.17"
|
|
26
|
+
},
|
|
27
|
+
"avenic": {
|
|
28
|
+
"packageSpec": "avenic@latest"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { runCli } from "../src/cli/dispatcher.mjs";
|
|
5
|
+
|
|
6
|
+
runCli({ io: console, cwd: process.cwd(), environment: process.env })
|
|
7
|
+
.then((status) => {
|
|
8
|
+
process.exitCode = status;
|
|
9
|
+
})
|
|
10
|
+
.catch((error) => {
|
|
11
|
+
console.error(`Error: ${error.message}`);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Detached helper for `avenic <agent>` launches. When the launching CLI
|
|
2
|
+
// process disappears without a normal exit (terminal closed, killed), it
|
|
3
|
+
// finishes the launch's cleanup: the run's sessions are captured into the
|
|
4
|
+
// project and, for the last launch of the group, the agent's native storage
|
|
5
|
+
// is restored to its pre-launch state.
|
|
6
|
+
import { appendFile, readFile } from "node:fs/promises";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import process from "node:process";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
11
|
+
import {
|
|
12
|
+
getSessionAdapter,
|
|
13
|
+
processAlive,
|
|
14
|
+
releaseSessionLease,
|
|
15
|
+
sessionLeasePath,
|
|
16
|
+
} from "#core";
|
|
17
|
+
|
|
18
|
+
const stateDir = process.argv[2];
|
|
19
|
+
const config = JSON.parse(await readFile(path.join(stateDir, "watchdog.json"), "utf8"));
|
|
20
|
+
const adapter = getSessionAdapter(config.agentId);
|
|
21
|
+
|
|
22
|
+
async function finish() {
|
|
23
|
+
// Capture first so the interrupted session is not lost, then leave the
|
|
24
|
+
// launch group; the last one to leave restores the native storage.
|
|
25
|
+
await adapter.capture(config.projectRoot, { environment: config.environment });
|
|
26
|
+
await releaseSessionLease(config.agentId, config.projectRoot, config.member, {
|
|
27
|
+
onLast: async () => {
|
|
28
|
+
// A snapshot marker means the group's snapshot completed: only then is
|
|
29
|
+
// a revert meaningful (the launch may have died mid-snapshot).
|
|
30
|
+
if (existsSync(path.join(stateDir, "snapshot.ok"))) {
|
|
31
|
+
await adapter.revertNative(path.join(stateDir, "snapshot"), config.projectRoot, {
|
|
32
|
+
environment: config.environment,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Wait for the launching CLI to go away. The group state directory is
|
|
41
|
+
// removed on a normal exit, which also ends the watch.
|
|
42
|
+
while (processAlive(config.parentPid) && existsSync(stateDir)) {
|
|
43
|
+
await delay(1000);
|
|
44
|
+
}
|
|
45
|
+
if (!processAlive(config.parentPid) && existsSync(stateDir)) {
|
|
46
|
+
await finish();
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
await appendFile(`${stateDir}.watchdog.log`, `${new Date().toISOString()} ${error?.stack ?? error}\n`).catch(() => {});
|
|
50
|
+
}
|