shine-code-submit 0.2.6 → 0.2.7
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/.claude-plugin/plugin.json +1 -1
- package/README.md +29 -64
- package/bin/launcher.cjs +71 -44
- package/dist/install.cjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Claude Code Hook → 本地常驻 Daemon 的状态/持久化底座。Hook 只做「采集 + 落盘 + 转发」立即退出,重活交给后台 Daemon 异步处理,不拖慢 Claude Code。详见 [`设计文档.md`](./设计文档.md)。更新日志见 [`CHANGELOG.md`](./CHANGELOG.md)。
|
|
4
4
|
|
|
5
|
-
以 **Claude Code Plugin** 形式分发——`npx shine-code-submit install` 一键安装(也支持 `/plugin marketplace add
|
|
5
|
+
以 **Claude Code Plugin** 形式分发——`npx shine-code-submit install` 一键安装(也支持 `/plugin marketplace add` 从 GitHub 装),跨平台(Windows/macOS/Linux × x64/arm64)。
|
|
6
6
|
|
|
7
7
|
## 架构
|
|
8
8
|
|
|
@@ -18,17 +18,15 @@ Claude Code ──事件──▶ node launcher.cjs ──spawn──▶ hook(
|
|
|
18
18
|
|
|
19
19
|
### hook / daemon / cli 分工
|
|
20
20
|
|
|
21
|
-
|
|
|
21
|
+
| 组件 | 生命周期 | 职责 |
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| **hook** | 短命(每次事件 spawn 后立即退出) | Claude Code 经 hooks.json 调它;采集事件 → POST 给 daemon(热路径)+ spool 落盘(兜底)→ 退出。绝不拖慢 Claude Code |
|
|
24
24
|
| **daemon** | 常驻后台(首次被 hook 拉起,自愈) | 收事件存 SQLite(幂等去重)、WebSocket 推送查看页、提供 HTTP API、内嵌并服务查看页 UI |
|
|
25
25
|
| **cli** | 按需(用户手动跑) | 管理命令:`status` / `start` / `stop` / `restart` / `ui`。读 pid 文件取 token → 调 daemon API |
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
三者共享 `src/`;hook/cli 跑在 Bun 下,用 `process.execPath`(= bun)执行 `bun run src/daemon/main.ts` 拉起 daemon,零配置。
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
每个二进制 ~94MB(Windows x64),`bin/` 共 ~1.5GB(6 平台)。`bun build --compile` 把**整个 Bun 运行时**(JavaScriptCore + sqlite + 系统 API)静态链入每个二进制——源码本身很小(hook 9KB / daemon 33KB / cli 6KB),运行时占 ~90MB 且三个二进制各自独立含一份、不共享。这是 `--compile` 的固有代价,减重只能换方案 B(Release 下载)或 C(源码跑,运行时全局只装一份)。
|
|
29
|
+
> **源码直跑,不分发二进制**——仓库只含源码,launcher 与 daemon 直接 `bun run src/...`。仓库小、改源码即时生效;代价是用户机器要有 Bun,装不上时安装器 / launcher 会自动装(见下)。
|
|
32
30
|
|
|
33
31
|
## Hook 事件覆盖
|
|
34
32
|
|
|
@@ -50,7 +48,9 @@ Claude Code 共 9 个 hook 事件([官方清单](https://docs.claude.com/en/do
|
|
|
50
48
|
|
|
51
49
|
## 安装(用户)
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
两种方式,任选其一。
|
|
52
|
+
|
|
53
|
+
### 方式一(推荐):npx 一键安装
|
|
54
54
|
|
|
55
55
|
```
|
|
56
56
|
npx shine-code-submit install
|
|
@@ -70,22 +70,22 @@ npx shine-code-submit install
|
|
|
70
70
|
|
|
71
71
|
卸载:`npx shine-code-submit uninstall`(⚠️ 不要 `sudo` —— sudo 没有 nvm 的 PATH,会 `npx: command not found`)。
|
|
72
72
|
|
|
73
|
-
###
|
|
73
|
+
### 方式二:`/plugin marketplace add`(从 GitHub)
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
源码直跑,需要 Bun 运行时——**没装也行**:首次 SessionStart 时 `launcher.cjs` 会自动装:先提示「未检测到 Bun」,再流式显示安装进度,约 10-30s(SessionStart 已配 200s 超时兜底;要真·实时进度另开终端 `tail -f ~/.local/share/shine-code-submit/log/bun-install.log`)。想首次更快可先手装 `npm install -g bun`,或官方脚本——Windows `powershell -c "irm bun.sh/install.ps1 | iex"`,macOS/Linux `curl -fsSL https://bun.sh/install | bash`。
|
|
76
76
|
|
|
77
77
|
**从 GitHub:**
|
|
78
78
|
|
|
79
79
|
```
|
|
80
|
-
/plugin marketplace add
|
|
80
|
+
/plugin marketplace add renguifeng/shine-code-submit
|
|
81
81
|
/plugin install shine-code-submit@shine-code-submit
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
clone
|
|
84
|
+
clone 后只有源码;首次 hook 事件时 `bin/launcher.cjs`(node)自动 `bun run src/hook/main.ts`,daemon 同理 `bun run src/daemon/main.ts`。
|
|
85
85
|
|
|
86
86
|
> 需机器能访问 github.com(国内通常要走代理);`marketplace add` 走 git,代理配好即可。
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
**从本地目录(开发自测):**
|
|
89
89
|
|
|
90
90
|
```
|
|
91
91
|
/plugin marketplace add <本仓库本地路径>
|
|
@@ -116,76 +116,45 @@ daemon 默认绑 `0.0.0.0`(所有网卡),打印的 Dashboard 链接自动
|
|
|
116
116
|
|
|
117
117
|
> ⚠️ 绑非回环后,`token`(UI 链接 `?t=` 里明文)成为数据接口唯一防线。仅可信网络下如此配,勿外泄带 token 的链接。
|
|
118
118
|
|
|
119
|
-
## 分发方案:三种取舍
|
|
120
|
-
|
|
121
|
-
本项目支持三种分发方案(A、B 为二进制,C 为源码直跑):
|
|
122
|
-
|
|
123
|
-
### 方案 A:二进制 commit 进 `bin/`(备选)
|
|
124
|
-
|
|
125
|
-
18 个二进制 commit 进 `bin/<plat>-<arch>/`,经 Git LFS 纳管。clone/安装即自带,launcher 直接 spawn 本地 hook,**装即用、无需联网**。
|
|
126
|
-
|
|
127
|
-
- ✅ 安装最简单(装即用,无首次下载)
|
|
128
|
-
- ✅ 本地/自建 git 仓库无 LFS 配额限制
|
|
129
|
-
- ⚠️ GitHub 公开时占 LFS 配额(免费约 1GB 存储 + 1GB/月流量;776MB 二进制几人 clone 即超额)
|
|
130
|
-
- ⚠️ clone 需拉 LFS 对象(首次稍慢)
|
|
131
|
-
|
|
132
|
-
### 方案 B:GitHub Releases 下载(仓库更小,备选)
|
|
133
|
-
|
|
134
|
-
二进制移出 git,作为 GitHub Release 资产。plugin 仓库只留源码 + `launcher.cjs`(几 MB,clone 快、零 LFS)。launcher 首次运行(SessionStart)按平台从 Release 下载 hook/cli/daemon 到 `%LOCALAPPDATA%/shine-code-submit/bin/<plat>-<arch>/`。
|
|
135
|
-
|
|
136
|
-
- ✅ 仓库小、clone 快、GitHub 无 LFS 配额压力
|
|
137
|
-
- ⚠️ 首次安装需联网下载约 300MB(SessionStart 的 `timeout` 调到 300s)
|
|
138
|
-
- ⚠️ 需额外发 Release(`gh release create`)+ launcher 下载自举逻辑
|
|
139
|
-
|
|
140
|
-
> 切换到方案 B 的实现见 git 历史 commit `fc30665`(launcher 下载自举 + `scripts/release.sh`)。
|
|
141
|
-
|
|
142
|
-
### 方案 C:源码直跑(**本仓库采用** ✅,需 Bun)
|
|
143
|
-
|
|
144
|
-
不产出/不分发二进制,launcher 与 daemon 直接用 `bun run src/...` 跑源码。plugin 仓库只留源码 + `launcher.cjs`(几 MB,无 LFS、无 Release)。
|
|
145
|
-
|
|
146
|
-
- ✅ 仓库最小、零 LFS 配额、无需发 Release
|
|
147
|
-
- ✅ 改源码即时生效(无 build/commit 二进制步骤)
|
|
148
|
-
- ⚠️ 用户机器须装 Bun 1.3+(开发者向,非通用)
|
|
149
|
-
- ⚠️ hook/daemon 启动比二进制慢几百 ms(bun run 源码)
|
|
150
|
-
|
|
151
|
-
> 实施需:`bin/launcher.cjs` 与 `src/shared/daemonctl.ts` 加「二进制优先、否则 `bun run src/...`」分支;`bin/<plat>-<arch>/` 移除并加入 `.gitignore`;cli 用 `bun run src/cli/main.ts`。
|
|
152
|
-
|
|
153
119
|
## 开发(贡献者)
|
|
154
120
|
|
|
155
121
|
依赖 [Bun](https://bun.sh) 1.3+:
|
|
156
122
|
|
|
157
123
|
```bash
|
|
158
124
|
bun install
|
|
159
|
-
bun run
|
|
160
|
-
bun run build:
|
|
161
|
-
bun run
|
|
125
|
+
bun run typecheck # tsc --noEmit
|
|
126
|
+
bun run build:install # 编译 install CLI → dist/install.cjs(npm bin 入口)
|
|
127
|
+
bun run build # 编译本机平台 daemon/hook/cli 到 bin/<plat>-<arch>/(本地自测)
|
|
162
128
|
```
|
|
163
129
|
|
|
164
|
-
|
|
130
|
+
- `dist/install.cjs`:`npx shine-code-submit` 的入口,发布到 npm。`scripts/build-install.ts` 把 `src/install/*` 打成单文件 cjs bundle。
|
|
131
|
+
- `bin/<plat>-<arch>/`:`bun build --compile` 产出的**本机**二进制,开发自测用、**gitignored、不入库、不发布**。launcher 优先用它、没有则 `bun run src/...`,所以不 build 也能跑。
|
|
132
|
+
- 发版到 npm:`bash scripts/publish.sh`(build → `npm pack` → `fix-tarball-mode.py` 修 `+x` → `npm publish <tgz>`)。详见 [`CHANGELOG.md`](./CHANGELOG.md)。
|
|
165
133
|
|
|
166
134
|
## 目录
|
|
167
135
|
|
|
168
136
|
```
|
|
169
137
|
.claude-plugin/ plugin.json、marketplace.json(plugin 元信息 + 自托管市场)
|
|
170
|
-
hooks/ hooks.json(plugin hook 注册,
|
|
171
|
-
bin/ launcher.cjs
|
|
172
|
-
src/ shared/ daemon/ hook/ cli
|
|
173
|
-
ui/
|
|
174
|
-
|
|
138
|
+
hooks/ hooks.json(plugin hook 注册,command 调 node launcher.cjs)
|
|
139
|
+
bin/ launcher.cjs(hook 分发器);<plat>-<arch>/ 本机编译产物(gitignored,不入库)
|
|
140
|
+
src/ shared/ daemon/ hook/ cli/ install/(多端共用源码)
|
|
141
|
+
ui/ 查看页(React/TSX,由 daemon 内嵌 HTTP 服务)
|
|
142
|
+
dist/ install.cjs(npm 发布产物,gitignored)
|
|
143
|
+
scripts/ build.ts、build-install.ts、publish.sh、fix-tarball-mode.py
|
|
175
144
|
```
|
|
176
145
|
|
|
177
146
|
## 环境变量
|
|
178
147
|
|
|
179
148
|
| 变量 | 作用 | 默认 |
|
|
180
149
|
| --- | --- | --- |
|
|
181
|
-
| `SHINE_CODE_SUBMIT_DAEMON_CMD` | 拉起 daemon 的完整命令(开发期,如 `bun run src/daemon/main.ts`) | 同目录 daemon 二进制 |
|
|
182
|
-
| `SHINE_CODE_SUBMIT_DAEMON` | 仅 `bun run` 入口路径 | 无 |
|
|
183
|
-
| `SHINE_CODE_SUBMIT_DEBUG` | 开启 daemon DEBUG 日志 | 无 |
|
|
184
150
|
| `SHINE_CODE_SUBMIT_HOST` | daemon 监听地址。默认 `0.0.0.0`(绑所有网卡,局域网可访问);仅本机回环用时设 `127.0.0.1` | `0.0.0.0` |
|
|
151
|
+
| `SHINE_CODE_SUBMIT_DAEMON_CMD` | 拉起 daemon 的完整命令(开发期覆盖) | `bun run src/daemon/main.ts` |
|
|
152
|
+
| `SHINE_CODE_SUBMIT_DAEMON` | 仅 `bun run` 入口路径 | `src/daemon/main.ts` |
|
|
153
|
+
| `SHINE_CODE_SUBMIT_DEBUG` | 开启 daemon DEBUG 日志 | 无 |
|
|
185
154
|
|
|
186
155
|
## 数据位置
|
|
187
156
|
|
|
188
|
-
`%LOCALAPPDATA%/shine-code-submit
|
|
157
|
+
`%LOCALAPPDATA%/shine-code-submit/`(Windows)或 `~/.local/share/shine-code-submit/`(macOS/Linux):
|
|
189
158
|
|
|
190
159
|
```
|
|
191
160
|
daemon.pid pid/port/token/startedAt
|
|
@@ -194,10 +163,6 @@ log/daemon.log 日志(按大小轮转)
|
|
|
194
163
|
db/events.sqlite 事件库(按 cwd 隔离,幂等去重)
|
|
195
164
|
```
|
|
196
165
|
|
|
197
|
-
二进制在 plugin 安装目录的 `bin/<plat>-<arch>/`(不在数据目录)。
|
|
198
|
-
|
|
199
|
-
> 旧版本(livesetting)的数据目录 `%LOCALAPPDATA%/livesetting/` 不会自动迁移,可手动删除。
|
|
200
|
-
|
|
201
166
|
## 关键设计点
|
|
202
167
|
|
|
203
168
|
- **目录式 spool + 原子 rename**:每事件一文件,规避多进程并发 append 损坏。
|
|
@@ -208,4 +173,4 @@ db/events.sqlite 事件库(按 cwd 隔离,幂等去重)
|
|
|
208
173
|
- **监听/连接地址分离**:daemon 监听用 `LISTEN_HOST`(默认 0.0.0.0,env 可配);hook POST / cli / 探活 连接 daemon 固定走 `127.0.0.1` 回环(daemon 即使绑 0.0.0.0 也含回环),最快最稳。
|
|
209
174
|
- **打印链接用真实网卡 IP**:`PUBLIC_BASE_URL` 取第一个非虚拟网卡的 IPv4(跳过 vEthernet/VMware/docker),局域网设备可直接访问;本机打开浏览器则用 `localhost`(WSL2 转发友好)。
|
|
210
175
|
- **自启动 + 自愈**:任意事件故障路径都能拉起;重复实例启动时自检退出,crash 只删属于自己的 pid。
|
|
211
|
-
-
|
|
176
|
+
- **hook 永不阻断**:launcher 与 hook 退出码恒 0,Bun 缺失时自动安装或静默跳过,绝不影响 Claude Code 主进程。
|
package/bin/launcher.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// shine-code-submit hook 平台分发器(.cjs 强制 CommonJS,兼容所有 node,不依赖 package.json)。
|
|
3
3
|
// Claude Code 经 hooks.json 以 `node launcher.cjs <Event>` 调用(exec form,不经 shell)。
|
|
4
|
-
// 优先 spawn 同目录 bin/<plat>-<arch>/hook[.exe]
|
|
4
|
+
// 优先 spawn 同目录 bin/<plat>-<arch>/hook[.exe](二进制模式,本机 build 产物);
|
|
5
5
|
// 不存在则 bun run src/hook/main.ts(源码模式)。
|
|
6
|
-
// 源码模式需要 Bun——若没装,首次自动安装(npm i -g bun →
|
|
7
|
-
//
|
|
6
|
+
// 源码模式需要 Bun——若没装,首次自动安装(npm i -g bun → 失败回退官方脚本),
|
|
7
|
+
// 安装过程逐行流式输出(hook stdout + 日志文件,可 tail -f 看实时进度)。退出码恒 0。
|
|
8
8
|
const { spawn, spawnSync } = require("node:child_process");
|
|
9
|
+
const readline = require("node:readline");
|
|
9
10
|
const { existsSync, mkdirSync, appendFileSync } = require("node:fs");
|
|
10
11
|
const { join } = require("node:path");
|
|
11
12
|
const { homedir } = require("node:os");
|
|
@@ -36,53 +37,79 @@ function findBun() {
|
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const log = (s) => { try { appendFileSync(logFile, `[${new Date().toISOString()}] ${s}\n`); } catch {} };
|
|
46
|
-
const opts = { shell: true, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 180000 };
|
|
40
|
+
/** 日志文件路径(创建目录)。安装过程逐行写这里,可 `tail -f` 看实时进度。 */
|
|
41
|
+
function logFile() {
|
|
42
|
+
const dir = join(homedir(), ".local", "share", "shine-code-submit", "log");
|
|
43
|
+
try { mkdirSync(dir, { recursive: true }); } catch {}
|
|
44
|
+
return join(dir, "bun-install.log");
|
|
45
|
+
}
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
/** 跑一条 shell 命令,stdout/stderr 逐行流式:写日志 +(仅 SessionStart)转发到 hook stdout。 */
|
|
48
|
+
function streamCmd(cmd, file, toStdout) {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const w = (s) => { try { appendFileSync(file, s); } catch {} };
|
|
51
|
+
w(`\n[${new Date().toISOString()}] $ ${cmd}\n`);
|
|
52
|
+
let child;
|
|
53
|
+
try {
|
|
54
|
+
child = spawn(cmd, { shell: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
55
|
+
} catch {
|
|
56
|
+
w("[spawn error]\n");
|
|
57
|
+
resolve(1);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
61
|
+
if (!stream) continue;
|
|
62
|
+
readline.createInterface({ input: stream, crlfDelay: Infinity }).on("line", (line) => {
|
|
63
|
+
w(line + "\n");
|
|
64
|
+
if (toStdout) process.stdout.write(line + "\n");
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
child.on("error", () => { w("[child error]\n"); resolve(1); });
|
|
68
|
+
child.on("exit", (code) => { w(`[exit ${code}]\n`); resolve(code ?? 1); });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
/** 装 bun:npm i -g bun → 失败回退官方脚本。每步逐行流式输出。返回 bun 路径或 null。 */
|
|
73
|
+
async function installBun(toStdout) {
|
|
74
|
+
const file = logFile();
|
|
75
|
+
const step = async (cmd) => { await streamCmd(cmd, file, toStdout); return findBun(); };
|
|
76
|
+
let b = await step("npm install -g bun");
|
|
77
|
+
if (b) return b;
|
|
78
|
+
const official = process.platform === "win32"
|
|
79
|
+
? 'powershell -c "irm bun.sh/install.ps1 | iex"'
|
|
80
|
+
: "curl -fsSL https://bun.sh/install | bash";
|
|
81
|
+
return step(official);
|
|
63
82
|
}
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
function runChild(cmd, args, opts) {
|
|
85
|
+
const child = spawn(cmd, args, opts);
|
|
86
|
+
child.on("error", () => process.exit(0));
|
|
87
|
+
child.on("exit", () => process.exit(0));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
(async () => {
|
|
91
|
+
try {
|
|
92
|
+
if (existsSync(hookBin)) {
|
|
93
|
+
// 二进制模式:spawn 本地已 build 的 hook
|
|
94
|
+
runChild(hookBin, argv, { stdio: "inherit" });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
71
97
|
// 源码模式:bun run src/hook/main.ts(Windows 需 shell 找 bun.exe)
|
|
72
98
|
let bun = findBun();
|
|
73
99
|
if (!bun) {
|
|
74
|
-
// 只在 SessionStart
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
// 只在 SessionStart 把进度打到 stdout(其它 hook 的 stdout 可能被 Claude Code 按 JSON 解析)
|
|
101
|
+
const show = event === "SessionStart";
|
|
102
|
+
if (show) {
|
|
103
|
+
console.log("");
|
|
104
|
+
console.log("⏳ shine-code-submit: 未检测到 Bun 运行时,首次自动安装中(约 10-30s)");
|
|
105
|
+
console.log(" 实时进度可另开终端: tail -f " + logFile());
|
|
106
|
+
}
|
|
107
|
+
bun = await installBun(show);
|
|
108
|
+
if (show) console.log(bun ? "✅ Bun 就绪,继续启动…" : "❌ Bun 自动安装失败。请手动装: https://bun.sh (装完重开会话即可,事件不丢)");
|
|
81
109
|
}
|
|
82
|
-
|
|
110
|
+
if (!bun) process.exit(0);
|
|
111
|
+
runChild(bun, ["run", hookSrc, ...argv], { stdio: "inherit", shell: SHELL });
|
|
112
|
+
} catch {
|
|
113
|
+
process.exit(0);
|
|
83
114
|
}
|
|
84
|
-
|
|
85
|
-
child.on("exit", () => process.exit(0));
|
|
86
|
-
} catch {
|
|
87
|
-
process.exit(0);
|
|
88
|
-
}
|
|
115
|
+
})();
|
package/dist/install.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var w=require("node:fs"),Xb=require("node:path"),Yb=require("node:child_process");var f=require("node:fs"),m=require("node:os"),y=require("node:path"),j=require("node:child_process"),Gb=[1,1,0],Hb=300000;function D(){let b=j.spawnSync("bun",["--version"],{shell:process.platform==="win32",encoding:"utf8"});if(b.status===0&&(b.stdout??"").trim())return"bun";let q=m.homedir(),z=process.platform==="win32"?[y.join(q,".bun","bin","bun.exe")]:[y.join(q,".bun","bin","bun"),"/usr/local/bin/bun","/opt/homebrew/bin/bun"];for(let Q of z)if(f.existsSync(Q))return Q;return null}function Wb(b){return b.trim().split(".").map((q)=>parseInt(q,10)||0)}function Jb(b,q){let z=Wb(b);for(let Q=0;Q<q.length;Q++){let X=z[Q]??0,J=q[Q]??0;if(X>J)return!0;if(X<J)return!1}return!0}function Kb(){let b=process.env.npm_config_registry??"";return/npmmirror|taobao/i.test(b)}function M(b){return j.spawnSync(b,{shell:!0,encoding:"utf8",timeout:Hb,stdio:"inherit"}).status??1}async function E(){let b=D();if(b){let z=j.spawnSync(b,["--version"],{shell:process.platform==="win32",encoding:"utf8"}).stdout?.trim()??"";if(z&&Jb(z,Gb))return console.log(`[shine-code-submit] bun ${z} detected`),b}if(console.log("[shine-code-submit] bun 未找到或版本过低,开始自动安装..."),Kb()){if(console.log("[shine-code-submit] 检测到国内 npm 镜像,先尝试 npm install -g bun"),M("npm install -g bun")===0){let z=D();if(z)return console.log("[shine-code-submit] ✓ bun 安装成功(via npm 镜像)"),z}console.log("[shine-code-submit] npm 镜像方式失败,回退官方脚本")}if(process.platform==="win32")M('powershell -c "irm bun.sh/install.ps1 | iex"');else M("curl -fsSL https://bun.sh/install | bash");let q=D();if(!q)throw console.error("[shine-code-submit] bun 自动安装失败。请手动安装后重试:"),console.error(" Windows: winget install Oven-sh.Bun 或 npm install -g bun"),console.error(" macOS: brew install oven-sh/bun/bun"),console.error(" Linux: curl -fsSL https://bun.sh/install | bash"),Error("bun installation failed");return console.log("[shine-code-submit] ✓ bun 安装成功"),q}var Z=require("node:fs"),Y=require("node:path"),g=require("node:child_process");var S=require("node:os"),V=require("node:path");function p(){return process.env.CLAUDE_CONFIG_DIR||V.join(S.homedir(),".claude")}function u(){return V.join(p(),"plugins")}function x(){return V.join(u(),"known_marketplaces.json")}function O(){return V.join(u(),"installed_plugins.json")}function v(){return V.join(p(),"settings.json")}var A={name:"shine-code-submit",version:"0.2.6",private:!1,type:"module",description:"Claude Code Hook -> 本地常驻 Daemon 的状态/持久化底座",author:"renguifeng",license:"MIT",repository:{type:"git",url:"https://github.com/renguifeng/shine-code-submit.git"},homepage:"https://github.com/renguifeng/shine-code-submit",bugs:{url:"https://github.com/renguifeng/shine-code-submit/issues"},keywords:["claude","claude-code","plugin","hooks","daemon","dashboard"],bin:{"shine-code-submit":"dist/install.cjs"},files:["dist/install.cjs",".claude-plugin","hooks/hooks.json","bin/launcher.cjs","src","ui","package.json","bun.lock","README.md"],engines:{node:">=18"},publishConfig:{access:"public",registry:"https://registry.npmjs.org/"},scripts:{daemon:"bun run src/daemon/main.ts",hook:"bun run src/hook/main.ts",cli:"bun run src/cli/main.ts",build:"bun run scripts/build.ts","build:all":"bun run scripts/build.ts --all","build:install":"bun run scripts/build-install.ts","build:dist":"bun run scripts/build.ts && bun run scripts/build-install.ts",prepublishOnly:"bun run build:dist",typecheck:"tsc --noEmit"},devDependencies:{"@types/bun":"^1.3.14","@types/react":"^19.2.17","@types/react-dom":"^19.2.3",typescript:"^6.0.3"},dependencies:{marked:"^18.0.5",react:"^19.2.7","react-dom":"^19.2.7"}};var P=require("node:os"),h="shine-code-submit",W=A.version,Rb=process.env.SHINE_CODE_SUBMIT_HOST??"0.0.0.0",Vb="127.0.0.1",c=36666,B=`http://${Vb}:${c}`;function Ub(){let b=["vethernet","vmware","virtualbox","docker","veth","br-","virbr","vnet","utun"],q=(z)=>{let Q=z.toLowerCase();return b.some((X)=>Q.includes(X))};try{let z=P.networkInterfaces();for(let Q of Object.keys(z)){if(q(Q))continue;for(let X of z[Q]??[])if(X.family==="IPv4"&&!X.internal)return X.address}for(let Q of Object.keys(z))for(let X of z[Q]??[])if(X.family==="IPv4"&&!X.internal)return X.address}catch{}return"localhost"}var N=`http://${Ub()}:${c}`;var G="shine-code-submit",I="shine-code-submit";function L(b=W){return Y.join(u(),"cache",G,I,b)}function Cb(){let b=process.argv[1],q=process.cwd();if(b)try{q=Y.dirname(Z.realpathSync(Y.resolve(b)))}catch{q=Y.dirname(Y.resolve(b))}let z=q;for(let Q=0;Q<12;Q++){if(Z.existsSync(Y.join(z,"package.json"))&&Z.existsSync(Y.join(z,".claude-plugin")))return z;let X=Y.dirname(z);if(X===z)break;z=X}return Y.dirname(q)}var jb=[".claude-plugin","hooks","bin","src","ui","package.json","bun.lock","README.md"];function l(b){let q=L();if(Z.existsSync(q))Z.rmSync(q,{recursive:!0,force:!0});Z.mkdirSync(q,{recursive:!0});let z=Cb();console.log(`[shine-code-submit] 部署源:${z}`);for(let X of jb){let J=Y.join(z,X);if(!Z.existsSync(J))continue;Z.cpSync(J,Y.join(q,X),{recursive:!0})}console.log("[shine-code-submit] 安装运行时依赖(bun install)...");let Q=g.spawnSync(b,["install","--frozen-lockfile"],{cwd:q,shell:process.platform==="win32",encoding:"utf8",stdio:"inherit"}).status;if(Q!==0){if(console.log("[shine-code-submit] --frozen-lockfile 失败,重试普通 bun install"),Q=g.spawnSync(b,["install"],{cwd:q,shell:process.platform==="win32",encoding:"utf8",stdio:"inherit"}).status,Q!==0)throw Error(`bun install 失败(exit ${Q})。请手动在 ${q} 跑 bun install`)}return Z.writeFileSync(Y.join(q,".install-version"),JSON.stringify({version:W,installedAt:Date.now()}),"utf8"),console.log(`[shine-code-submit] 已部署到 ${q}`),q}var $=require("node:fs"),d=require("node:path");function K(b,q){if(!$.existsSync(b))return q;let z;try{z=$.readFileSync(b,"utf8")}catch{return q}try{return JSON.parse(z)}catch{let Q=`${b}.bak-corrupt-${Date.now()}`;try{$.copyFileSync(b,Q),console.error(`[shine-code-submit] WARNING: ${b} JSON 损坏,已备份到 ${Q},用默认值继续`)}catch{}return q}}function F(b,q){let z=`${b}.bak-pre-install`;if($.existsSync(b)&&!$.existsSync(z))try{$.copyFileSync(b,z)}catch{}$.mkdirSync(d.dirname(b),{recursive:!0});let Q=`${b}.tmp-${process.pid}`;$.writeFileSync(Q,JSON.stringify(q,null,2),"utf8"),$.renameSync(Q,b)}function R(){return`${I}@${G}`}function o(b){let q=x(),z=K(q,{}),Q=z[G];if(Q?.source&&Q.source.source!=="directory")console.log(`[shine-code-submit] WARNING: marketplace "${G}" 已存在(source=${Q.source.source}),将覆盖为 directory 源(原文件已备份)`);z[G]={source:{source:"directory",path:b},installLocation:b,lastUpdated:new Date().toISOString(),autoUpdate:!1},F(q,z),console.log(`[shine-code-submit] marketplace 已注册 → ${q}`)}function s(b){let q=O(),z=K(q,{version:2,plugins:{}});if(!z.version)z.version=2;if(!z.plugins)z.plugins={};let Q=R(),X=new Date().toISOString(),J=z.plugins[Q]?.[0];z.plugins[Q]=[{scope:"user",installPath:b,version:W,installedAt:J?.installedAt??X,lastUpdated:X}],F(q,z),console.log(`[shine-code-submit] plugin 已注册 → ${q}`)}function r(b){let q=v(),z=K(q,{});if(!z.enabledPlugins)z.enabledPlugins={};let Q=R();if(z.enabledPlugins[Q]=!0,!z.extraKnownMarketplaces)z.extraKnownMarketplaces={};if(!z.extraKnownMarketplaces[G])z.extraKnownMarketplaces[G]={source:{source:"directory",path:b}};F(q,z),console.log(`[shine-code-submit] 已启用(enabledPlugins)→ ${q}`)}function i(){let b=R(),q=K(x(),{});if(q[G])delete q[G],F(x(),q);let z=K(O(),{plugins:{}});if(z.plugins&&z.plugins[b])delete z.plugins[b],F(O(),z);let Q=K(v(),{}),X=!1;if(Q.enabledPlugins&&Q.enabledPlugins[b])delete Q.enabledPlugins[b],X=!0;if(Q.extraKnownMarketplaces&&Q.extraKnownMarketplaces[G])delete Q.extraKnownMarketplaces[G],X=!0;if(X)F(v(),Q);console.log("[shine-code-submit] 已从三处 JSON 移除注册")}var t=require("node:child_process"),_=require("node:fs");async function U(b=400){try{let q=await fetch(`${B}/api/health`,{signal:AbortSignal.timeout(b)});if(!q.ok)return!1;return(await q.json())?.service===h}catch{return!1}}function n(b){let q=process.platform,z,Q;if(q==="win32")z="cmd",Q=["/c","start","",b];else if(q==="darwin")z="open",Q=[b];else if(ub())z="cmd.exe",Q=["/c","start","",b];else z="xdg-open",Q=[b];try{t.spawn(z,Q,{detached:!0,stdio:"ignore"}).unref()}catch{}}function ub(){if(process.platform!=="linux")return!1;try{return _.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")}catch{return!1}}var H=require("node:path"),a=require("node:fs"),e=require("node:os"),xb=process.env.LOCALAPPDATA??H.join(e.homedir(),".local","share"),C=H.join(xb,"shine-code-submit"),Ob=H.join(C,"spool"),bb=H.join(C,"log"),qb=H.join(C,"db"),zb=H.join(C,"daemon.pid"),rb=H.join(bb,"daemon.log"),ib=H.join(qb,"events.sqlite");function Qb(){for(let b of[C,Ob,bb,qb])a.mkdirSync(b,{recursive:!0})}var k=require("node:fs");function T(){try{let b=k.readFileSync(zb,"utf8");return JSON.parse(b)}catch{return null}}var[,,Zb]=process.argv;vb().catch((b)=>{console.error(`[shine-code-submit] ${b instanceof Error?b.message:String(b)}`),process.exit(1)});async function vb(){switch(Zb){case void 0:case"install":await Bb();break;case"uninstall":await Nb();break;case"status":await kb();break;case"--version":case"-v":console.log(W);break;default:Mb()}}async function Bb(){console.log(`=== shine-code-submit installer v${W} ===`);let b=await E(),q=l(b);o(q),s(q),r(q),Qb(),await Tb(b,q),Db(),console.log(""),console.log("✓ 安装完成。"),console.log(" · 重启 Claude Code 后,/plugin 列表会显示 shine-code-submit(已启用)。"),console.log(" · 开新会话即触发 SessionStart hook,事件出现在 dashboard。")}async function Nb(){console.log("=== shine-code-submit uninstaller ==="),await wb(),i();let b=L();if(w.existsSync(b))w.rmSync(b,{recursive:!0,force:!0}),console.log(`[shine-code-submit] 已删除 ${b}`);console.log("✓ 已卸载。重启 Claude Code 后 /plugin 不再显示。")}async function kb(){let b=await U(),q=T();if(b&&q)console.log(`daemon: running pid=${q.pid} ${N}`);else console.log("daemon: not running")}async function Tb(b,q){if(await U()){console.log("[shine-code-submit] daemon 已在运行,跳过启动");return}let z=Xb.join(q,"src","daemon","main.ts");console.log("[shine-code-submit] 启动 daemon...");try{Yb.spawn(b,["run",z],{detached:!0,stdio:"ignore",windowsHide:!0,cwd:q,shell:process.platform==="win32"}).unref()}catch(X){console.error(`[shine-code-submit] 启动 daemon 失败:${X instanceof Error?X.message:X}`),console.error(" plugin 已注册,Claude Code 重启后 hook 会自动拉起 daemon");return}let Q=Date.now()+1e4;while(Date.now()<Q)if(await $b(200),await U()){console.log("[shine-code-submit] daemon 已就绪");return}console.error("[shine-code-submit] daemon 启动超时(10s)。plugin 已注册,可稍后手动 `shine-code-submit start` 或重启 claude。")}async function wb(){let b=T();if(!b){console.log("[shine-code-submit] daemon 未运行(无 pid 文件)");return}if(await U()){try{await fetch(`${B}/api/shutdown`,{method:"POST",headers:{authorization:`Bearer ${b.token}`}})}catch{}if(await $b(1000),await U())try{process.kill(b.pid)}catch{}}console.log("[shine-code-submit] daemon 已停止")}function Db(){let b=T(),q=b?`${N}/ui?t=${b.token}`:`${N}/ui`;console.log(`[shine-code-submit] Dashboard: ${q}`);try{n(q)}catch{}}function $b(b){return new Promise((q)=>setTimeout(q,b))}function Mb(){console.log(`shine-code-submit <command>
|
|
2
|
+
var w=require("node:fs"),Xb=require("node:path"),Yb=require("node:child_process");var f=require("node:fs"),m=require("node:os"),y=require("node:path"),j=require("node:child_process"),Gb=[1,1,0],Hb=300000;function D(){let b=j.spawnSync("bun",["--version"],{shell:process.platform==="win32",encoding:"utf8"});if(b.status===0&&(b.stdout??"").trim())return"bun";let q=m.homedir(),z=process.platform==="win32"?[y.join(q,".bun","bin","bun.exe")]:[y.join(q,".bun","bin","bun"),"/usr/local/bin/bun","/opt/homebrew/bin/bun"];for(let Q of z)if(f.existsSync(Q))return Q;return null}function Wb(b){return b.trim().split(".").map((q)=>parseInt(q,10)||0)}function Jb(b,q){let z=Wb(b);for(let Q=0;Q<q.length;Q++){let X=z[Q]??0,J=q[Q]??0;if(X>J)return!0;if(X<J)return!1}return!0}function Kb(){let b=process.env.npm_config_registry??"";return/npmmirror|taobao/i.test(b)}function M(b){return j.spawnSync(b,{shell:!0,encoding:"utf8",timeout:Hb,stdio:"inherit"}).status??1}async function E(){let b=D();if(b){let z=j.spawnSync(b,["--version"],{shell:process.platform==="win32",encoding:"utf8"}).stdout?.trim()??"";if(z&&Jb(z,Gb))return console.log(`[shine-code-submit] bun ${z} detected`),b}if(console.log("[shine-code-submit] bun 未找到或版本过低,开始自动安装..."),Kb()){if(console.log("[shine-code-submit] 检测到国内 npm 镜像,先尝试 npm install -g bun"),M("npm install -g bun")===0){let z=D();if(z)return console.log("[shine-code-submit] ✓ bun 安装成功(via npm 镜像)"),z}console.log("[shine-code-submit] npm 镜像方式失败,回退官方脚本")}if(process.platform==="win32")M('powershell -c "irm bun.sh/install.ps1 | iex"');else M("curl -fsSL https://bun.sh/install | bash");let q=D();if(!q)throw console.error("[shine-code-submit] bun 自动安装失败。请手动安装后重试:"),console.error(" Windows: winget install Oven-sh.Bun 或 npm install -g bun"),console.error(" macOS: brew install oven-sh/bun/bun"),console.error(" Linux: curl -fsSL https://bun.sh/install | bash"),Error("bun installation failed");return console.log("[shine-code-submit] ✓ bun 安装成功"),q}var Z=require("node:fs"),Y=require("node:path"),g=require("node:child_process");var S=require("node:os"),V=require("node:path");function p(){return process.env.CLAUDE_CONFIG_DIR||V.join(S.homedir(),".claude")}function u(){return V.join(p(),"plugins")}function x(){return V.join(u(),"known_marketplaces.json")}function O(){return V.join(u(),"installed_plugins.json")}function v(){return V.join(p(),"settings.json")}var A={name:"shine-code-submit",version:"0.2.7",private:!1,type:"module",description:"Claude Code Hook -> 本地常驻 Daemon 的状态/持久化底座",author:"renguifeng",license:"MIT",repository:{type:"git",url:"https://github.com/renguifeng/shine-code-submit.git"},homepage:"https://github.com/renguifeng/shine-code-submit",bugs:{url:"https://github.com/renguifeng/shine-code-submit/issues"},keywords:["claude","claude-code","plugin","hooks","daemon","dashboard"],bin:{"shine-code-submit":"dist/install.cjs"},files:["dist/install.cjs",".claude-plugin","hooks/hooks.json","bin/launcher.cjs","src","ui","package.json","bun.lock","README.md"],engines:{node:">=18"},publishConfig:{access:"public",registry:"https://registry.npmjs.org/"},scripts:{daemon:"bun run src/daemon/main.ts",hook:"bun run src/hook/main.ts",cli:"bun run src/cli/main.ts",build:"bun run scripts/build.ts","build:all":"bun run scripts/build.ts --all","build:install":"bun run scripts/build-install.ts","build:dist":"bun run scripts/build.ts && bun run scripts/build-install.ts",prepublishOnly:"bun run build:dist",typecheck:"tsc --noEmit"},devDependencies:{"@types/bun":"^1.3.14","@types/react":"^19.2.17","@types/react-dom":"^19.2.3",typescript:"^6.0.3"},dependencies:{marked:"^18.0.5",react:"^19.2.7","react-dom":"^19.2.7"}};var P=require("node:os"),h="shine-code-submit",W=A.version,Rb=process.env.SHINE_CODE_SUBMIT_HOST??"0.0.0.0",Vb="127.0.0.1",c=36666,B=`http://${Vb}:${c}`;function Ub(){let b=["vethernet","vmware","virtualbox","docker","veth","br-","virbr","vnet","utun"],q=(z)=>{let Q=z.toLowerCase();return b.some((X)=>Q.includes(X))};try{let z=P.networkInterfaces();for(let Q of Object.keys(z)){if(q(Q))continue;for(let X of z[Q]??[])if(X.family==="IPv4"&&!X.internal)return X.address}for(let Q of Object.keys(z))for(let X of z[Q]??[])if(X.family==="IPv4"&&!X.internal)return X.address}catch{}return"localhost"}var N=`http://${Ub()}:${c}`;var G="shine-code-submit",I="shine-code-submit";function L(b=W){return Y.join(u(),"cache",G,I,b)}function Cb(){let b=process.argv[1],q=process.cwd();if(b)try{q=Y.dirname(Z.realpathSync(Y.resolve(b)))}catch{q=Y.dirname(Y.resolve(b))}let z=q;for(let Q=0;Q<12;Q++){if(Z.existsSync(Y.join(z,"package.json"))&&Z.existsSync(Y.join(z,".claude-plugin")))return z;let X=Y.dirname(z);if(X===z)break;z=X}return Y.dirname(q)}var jb=[".claude-plugin","hooks","bin","src","ui","package.json","bun.lock","README.md"];function l(b){let q=L();if(Z.existsSync(q))Z.rmSync(q,{recursive:!0,force:!0});Z.mkdirSync(q,{recursive:!0});let z=Cb();console.log(`[shine-code-submit] 部署源:${z}`);for(let X of jb){let J=Y.join(z,X);if(!Z.existsSync(J))continue;Z.cpSync(J,Y.join(q,X),{recursive:!0})}console.log("[shine-code-submit] 安装运行时依赖(bun install)...");let Q=g.spawnSync(b,["install","--frozen-lockfile"],{cwd:q,shell:process.platform==="win32",encoding:"utf8",stdio:"inherit"}).status;if(Q!==0){if(console.log("[shine-code-submit] --frozen-lockfile 失败,重试普通 bun install"),Q=g.spawnSync(b,["install"],{cwd:q,shell:process.platform==="win32",encoding:"utf8",stdio:"inherit"}).status,Q!==0)throw Error(`bun install 失败(exit ${Q})。请手动在 ${q} 跑 bun install`)}return Z.writeFileSync(Y.join(q,".install-version"),JSON.stringify({version:W,installedAt:Date.now()}),"utf8"),console.log(`[shine-code-submit] 已部署到 ${q}`),q}var $=require("node:fs"),d=require("node:path");function K(b,q){if(!$.existsSync(b))return q;let z;try{z=$.readFileSync(b,"utf8")}catch{return q}try{return JSON.parse(z)}catch{let Q=`${b}.bak-corrupt-${Date.now()}`;try{$.copyFileSync(b,Q),console.error(`[shine-code-submit] WARNING: ${b} JSON 损坏,已备份到 ${Q},用默认值继续`)}catch{}return q}}function F(b,q){let z=`${b}.bak-pre-install`;if($.existsSync(b)&&!$.existsSync(z))try{$.copyFileSync(b,z)}catch{}$.mkdirSync(d.dirname(b),{recursive:!0});let Q=`${b}.tmp-${process.pid}`;$.writeFileSync(Q,JSON.stringify(q,null,2),"utf8"),$.renameSync(Q,b)}function R(){return`${I}@${G}`}function o(b){let q=x(),z=K(q,{}),Q=z[G];if(Q?.source&&Q.source.source!=="directory")console.log(`[shine-code-submit] WARNING: marketplace "${G}" 已存在(source=${Q.source.source}),将覆盖为 directory 源(原文件已备份)`);z[G]={source:{source:"directory",path:b},installLocation:b,lastUpdated:new Date().toISOString(),autoUpdate:!1},F(q,z),console.log(`[shine-code-submit] marketplace 已注册 → ${q}`)}function s(b){let q=O(),z=K(q,{version:2,plugins:{}});if(!z.version)z.version=2;if(!z.plugins)z.plugins={};let Q=R(),X=new Date().toISOString(),J=z.plugins[Q]?.[0];z.plugins[Q]=[{scope:"user",installPath:b,version:W,installedAt:J?.installedAt??X,lastUpdated:X}],F(q,z),console.log(`[shine-code-submit] plugin 已注册 → ${q}`)}function r(b){let q=v(),z=K(q,{});if(!z.enabledPlugins)z.enabledPlugins={};let Q=R();if(z.enabledPlugins[Q]=!0,!z.extraKnownMarketplaces)z.extraKnownMarketplaces={};if(!z.extraKnownMarketplaces[G])z.extraKnownMarketplaces[G]={source:{source:"directory",path:b}};F(q,z),console.log(`[shine-code-submit] 已启用(enabledPlugins)→ ${q}`)}function i(){let b=R(),q=K(x(),{});if(q[G])delete q[G],F(x(),q);let z=K(O(),{plugins:{}});if(z.plugins&&z.plugins[b])delete z.plugins[b],F(O(),z);let Q=K(v(),{}),X=!1;if(Q.enabledPlugins&&Q.enabledPlugins[b])delete Q.enabledPlugins[b],X=!0;if(Q.extraKnownMarketplaces&&Q.extraKnownMarketplaces[G])delete Q.extraKnownMarketplaces[G],X=!0;if(X)F(v(),Q);console.log("[shine-code-submit] 已从三处 JSON 移除注册")}var t=require("node:child_process"),_=require("node:fs");async function U(b=400){try{let q=await fetch(`${B}/api/health`,{signal:AbortSignal.timeout(b)});if(!q.ok)return!1;return(await q.json())?.service===h}catch{return!1}}function n(b){let q=process.platform,z,Q;if(q==="win32")z="cmd",Q=["/c","start","",b];else if(q==="darwin")z="open",Q=[b];else if(ub())z="cmd.exe",Q=["/c","start","",b];else z="xdg-open",Q=[b];try{t.spawn(z,Q,{detached:!0,stdio:"ignore"}).unref()}catch{}}function ub(){if(process.platform!=="linux")return!1;try{return _.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")}catch{return!1}}var H=require("node:path"),a=require("node:fs"),e=require("node:os"),xb=process.env.LOCALAPPDATA??H.join(e.homedir(),".local","share"),C=H.join(xb,"shine-code-submit"),Ob=H.join(C,"spool"),bb=H.join(C,"log"),qb=H.join(C,"db"),zb=H.join(C,"daemon.pid"),rb=H.join(bb,"daemon.log"),ib=H.join(qb,"events.sqlite");function Qb(){for(let b of[C,Ob,bb,qb])a.mkdirSync(b,{recursive:!0})}var k=require("node:fs");function T(){try{let b=k.readFileSync(zb,"utf8");return JSON.parse(b)}catch{return null}}var[,,Zb]=process.argv;vb().catch((b)=>{console.error(`[shine-code-submit] ${b instanceof Error?b.message:String(b)}`),process.exit(1)});async function vb(){switch(Zb){case void 0:case"install":await Bb();break;case"uninstall":await Nb();break;case"status":await kb();break;case"--version":case"-v":console.log(W);break;default:Mb()}}async function Bb(){console.log(`=== shine-code-submit installer v${W} ===`);let b=await E(),q=l(b);o(q),s(q),r(q),Qb(),await Tb(b,q),Db(),console.log(""),console.log("✓ 安装完成。"),console.log(" · 重启 Claude Code 后,/plugin 列表会显示 shine-code-submit(已启用)。"),console.log(" · 开新会话即触发 SessionStart hook,事件出现在 dashboard。")}async function Nb(){console.log("=== shine-code-submit uninstaller ==="),await wb(),i();let b=L();if(w.existsSync(b))w.rmSync(b,{recursive:!0,force:!0}),console.log(`[shine-code-submit] 已删除 ${b}`);console.log("✓ 已卸载。重启 Claude Code 后 /plugin 不再显示。")}async function kb(){let b=await U(),q=T();if(b&&q)console.log(`daemon: running pid=${q.pid} ${N}`);else console.log("daemon: not running")}async function Tb(b,q){if(await U()){console.log("[shine-code-submit] daemon 已在运行,跳过启动");return}let z=Xb.join(q,"src","daemon","main.ts");console.log("[shine-code-submit] 启动 daemon...");try{Yb.spawn(b,["run",z],{detached:!0,stdio:"ignore",windowsHide:!0,cwd:q,shell:process.platform==="win32"}).unref()}catch(X){console.error(`[shine-code-submit] 启动 daemon 失败:${X instanceof Error?X.message:X}`),console.error(" plugin 已注册,Claude Code 重启后 hook 会自动拉起 daemon");return}let Q=Date.now()+1e4;while(Date.now()<Q)if(await $b(200),await U()){console.log("[shine-code-submit] daemon 已就绪");return}console.error("[shine-code-submit] daemon 启动超时(10s)。plugin 已注册,可稍后手动 `shine-code-submit start` 或重启 claude。")}async function wb(){let b=T();if(!b){console.log("[shine-code-submit] daemon 未运行(无 pid 文件)");return}if(await U()){try{await fetch(`${B}/api/shutdown`,{method:"POST",headers:{authorization:`Bearer ${b.token}`}})}catch{}if(await $b(1000),await U())try{process.kill(b.pid)}catch{}}console.log("[shine-code-submit] daemon 已停止")}function Db(){let b=T(),q=b?`${N}/ui?t=${b.token}`:`${N}/ui`;console.log(`[shine-code-submit] Dashboard: ${q}`);try{n(q)}catch{}}function $b(b){return new Promise((q)=>setTimeout(q,b))}function Mb(){console.log(`shine-code-submit <command>
|
|
3
3
|
|
|
4
4
|
install 安装插件(自动装 bun + 部署 + 注册 + 启 daemon + 开 dashboard)
|
|
5
5
|
uninstall 卸载(停 daemon + 反注册 + 删文件)
|