shine-code-submit 0.2.4 → 0.2.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/.claude-plugin/plugin.json +3 -3
- package/README.md +28 -30
- package/dist/install.cjs +2 -2
- package/package.json +4 -4
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shine-code-submit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Claude Code Hook → 本地常驻 Daemon 的状态/持久化底座。Hook 只做采集+落盘+转发立即退出,重活交给后台 Daemon 异步处理,不拖慢 Claude Code。",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "renguifeng"
|
|
7
7
|
},
|
|
8
|
-
"homepage": "
|
|
9
|
-
"repository": "
|
|
8
|
+
"homepage": "https://github.com/renguifeng/shine-code-submit",
|
|
9
|
+
"repository": "https://github.com/renguifeng/shine-code-submit.git"
|
|
10
10
|
}
|
package/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# Shine Code Submit
|
|
2
2
|
|
|
3
|
-
Claude Code Hook → 本地常驻 Daemon 的状态/持久化底座。Hook 只做「采集 + 落盘 + 转发」立即退出,重活交给后台 Daemon 异步处理,不拖慢 Claude Code。详见 [`设计文档.md`](./设计文档.md)。
|
|
3
|
+
Claude Code Hook → 本地常驻 Daemon 的状态/持久化底座。Hook 只做「采集 + 落盘 + 转发」立即退出,重活交给后台 Daemon 异步处理,不拖慢 Claude Code。详见 [`设计文档.md`](./设计文档.md)。更新日志见 [`CHANGELOG.md`](./CHANGELOG.md)。
|
|
4
4
|
|
|
5
|
-
以 **Claude Code Plugin**
|
|
5
|
+
以 **Claude Code Plugin** 形式分发——`npx shine-code-submit install` 一键安装(也支持 `/plugin marketplace add`),跨平台(Windows/macOS/Linux × x64/arm64)。
|
|
6
6
|
|
|
7
|
-
claude --resume 964154b8-3a68-4b4d-ae43-10205ff50978
|
|
8
7
|
## 架构
|
|
9
8
|
|
|
10
9
|
```
|
|
11
|
-
Claude Code ──事件──▶ node launcher.
|
|
10
|
+
Claude Code ──事件──▶ node launcher.cjs ──spawn──▶ hook(短命) ──┬── POST(热路径) ──▶ Daemon(常驻)
|
|
12
11
|
└── spool 落盘 ──▶ (回捞兜底)
|
|
13
12
|
├── SQLite(幂等)
|
|
14
13
|
├── WS 推送
|
|
@@ -51,42 +50,42 @@ Claude Code 共 9 个 hook 事件([官方清单](https://docs.claude.com/en/do
|
|
|
51
50
|
|
|
52
51
|
## 安装(用户)
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### 前置:安装 Bun
|
|
57
|
-
|
|
58
|
-
机器上已有 node/npm 的话最省事(国内可复用 npm mirror):
|
|
53
|
+
### 推荐:npx 一键安装
|
|
59
54
|
|
|
60
55
|
```
|
|
61
|
-
|
|
56
|
+
npx shine-code-submit install
|
|
62
57
|
```
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
> 国内 npm 若默认走镜像(npmmirror),新版同步有延迟;拉不到最新版时加 `--registry=https://registry.npmjs.org/` 指官方源。
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
68
|
-
```
|
|
61
|
+
一条命令完成:
|
|
69
62
|
|
|
70
|
-
|
|
63
|
+
1. 自动检测并安装运行时 **Bun**(1.1+,国内镜像优先 `npm i -g bun`,否则走官方脚本);
|
|
64
|
+
2. 部署 plugin 到 `~/.claude/plugins/cache/shine-code-submit/shine-code-submit/<version>/`;
|
|
65
|
+
3. `bun install` 装运行时依赖(marked / react / react-dom);
|
|
66
|
+
4. 注册 marketplace + plugin + 启用(写 `known_marketplaces.json` / `installed_plugins.json` / `settings.json` 三处 JSON);
|
|
67
|
+
5. 拉起 daemon、打印 Dashboard 链接。
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
装完**重启 Claude Code**,`/plugin` 列表会显示 `shine-code-submit`(✔ enabled);开新会话即触发 SessionStart hook,事件出现在 Dashboard。
|
|
70
|
+
|
|
71
|
+
卸载:`npx shine-code-submit uninstall`(⚠️ 不要 `sudo` —— sudo 没有 nvm 的 PATH,会 `npx: command not found`)。
|
|
72
|
+
|
|
73
|
+
### 手动安装(开发自测)
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
> 手动方式(源码直跑)需机器先装 [Bun](https://bun.sh) 1.3+(npx 安装器会自动装,手动才需自己装):`npm install -g bun` 最省事;或官方脚本——Windows `powershell -c "irm bun.sh/install.ps1 | iex"`,macOS/Linux `curl -fsSL https://bun.sh/install | bash`。装完重开终端,`bun --version` 确认。
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
**从 GitHub:**
|
|
79
78
|
|
|
80
79
|
```
|
|
81
|
-
/plugin marketplace add
|
|
80
|
+
/plugin marketplace add https://github.com/renguifeng/shine-code-submit.git
|
|
82
81
|
/plugin install shine-code-submit@shine-code-submit
|
|
83
82
|
```
|
|
84
83
|
|
|
85
|
-
clone 后无二进制;首次 hook 事件时 `bin/launcher.
|
|
84
|
+
clone 后无二进制;首次 hook 事件时 `bin/launcher.cjs`(node)自动 `bun run src/hook/main.ts`,daemon 同理 `bun run src/daemon/main.ts`。
|
|
86
85
|
|
|
87
|
-
>
|
|
86
|
+
> 需机器能访问 github.com(国内通常要走代理);`marketplace add` 走 git,代理配好即可。
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
**从本地目录:**
|
|
90
89
|
|
|
91
90
|
```
|
|
92
91
|
/plugin marketplace add <本仓库本地路径>
|
|
@@ -132,25 +131,24 @@ daemon 默认绑 `0.0.0.0`(所有网卡),打印的 Dashboard 链接自动
|
|
|
132
131
|
|
|
133
132
|
### 方案 B:GitHub Releases 下载(仓库更小,备选)
|
|
134
133
|
|
|
135
|
-
二进制移出 git,作为 GitHub Release 资产。plugin 仓库只留源码 + `launcher.
|
|
134
|
+
二进制移出 git,作为 GitHub Release 资产。plugin 仓库只留源码 + `launcher.cjs`(几 MB,clone 快、零 LFS)。launcher 首次运行(SessionStart)按平台从 Release 下载 hook/cli/daemon 到 `%LOCALAPPDATA%/shine-code-submit/bin/<plat>-<arch>/`。
|
|
136
135
|
|
|
137
136
|
- ✅ 仓库小、clone 快、GitHub 无 LFS 配额压力
|
|
138
137
|
- ⚠️ 首次安装需联网下载约 300MB(SessionStart 的 `timeout` 调到 300s)
|
|
139
138
|
- ⚠️ 需额外发 Release(`gh release create`)+ launcher 下载自举逻辑
|
|
140
139
|
|
|
141
140
|
> 切换到方案 B 的实现见 git 历史 commit `fc30665`(launcher 下载自举 + `scripts/release.sh`)。
|
|
142
|
-
claude --resume 61a9beba-9bc9-4407-a41e-270e3d2efed3
|
|
143
141
|
|
|
144
142
|
### 方案 C:源码直跑(**本仓库采用** ✅,需 Bun)
|
|
145
143
|
|
|
146
|
-
不产出/不分发二进制,launcher 与 daemon 直接用 `bun run src/...` 跑源码。plugin 仓库只留源码 + `launcher.
|
|
144
|
+
不产出/不分发二进制,launcher 与 daemon 直接用 `bun run src/...` 跑源码。plugin 仓库只留源码 + `launcher.cjs`(几 MB,无 LFS、无 Release)。
|
|
147
145
|
|
|
148
146
|
- ✅ 仓库最小、零 LFS 配额、无需发 Release
|
|
149
147
|
- ✅ 改源码即时生效(无 build/commit 二进制步骤)
|
|
150
148
|
- ⚠️ 用户机器须装 Bun 1.3+(开发者向,非通用)
|
|
151
149
|
- ⚠️ hook/daemon 启动比二进制慢几百 ms(bun run 源码)
|
|
152
150
|
|
|
153
|
-
> 实施需:`bin/launcher.
|
|
151
|
+
> 实施需:`bin/launcher.cjs` 与 `src/shared/daemonctl.ts` 加「二进制优先、否则 `bun run src/...`」分支;`bin/<plat>-<arch>/` 移除并加入 `.gitignore`;cli 用 `bun run src/cli/main.ts`。
|
|
154
152
|
|
|
155
153
|
## 开发(贡献者)
|
|
156
154
|
|
|
@@ -170,7 +168,7 @@ bun run typecheck
|
|
|
170
168
|
```
|
|
171
169
|
.claude-plugin/ plugin.json、marketplace.json(plugin 元信息 + 自托管市场)
|
|
172
170
|
hooks/ hooks.json(plugin hook 注册,exec form 调 node launcher)
|
|
173
|
-
bin/ launcher.
|
|
171
|
+
bin/ launcher.cjs + <plat>-<arch>/{hook,cli,daemon}(编译产物,commit 入库,Git LFS)
|
|
174
172
|
src/ shared/ daemon/ hook/ cli/(三端共用源码)
|
|
175
173
|
ui/ 查看页(原生 JS,编译期嵌入 daemon 二进制)
|
|
176
174
|
scripts/ build.ts
|
|
@@ -210,4 +208,4 @@ db/events.sqlite 事件库(按 cwd 隔离,幂等去重)
|
|
|
210
208
|
- **监听/连接地址分离**:daemon 监听用 `LISTEN_HOST`(默认 0.0.0.0,env 可配);hook POST / cli / 探活 连接 daemon 固定走 `127.0.0.1` 回环(daemon 即使绑 0.0.0.0 也含回环),最快最稳。
|
|
211
209
|
- **打印链接用真实网卡 IP**:`PUBLIC_BASE_URL` 取第一个非虚拟网卡的 IPv4(跳过 vEthernet/VMware/docker),局域网设备可直接访问;本机打开浏览器则用 `localhost`(WSL2 转发友好)。
|
|
212
210
|
- **自启动 + 自愈**:任意事件故障路径都能拉起;重复实例启动时自检退出,crash 只删属于自己的 pid。
|
|
213
|
-
- **跨平台分发(方案 A)**:plugin 的 hooks.json 静态、无平台变量,靠 `bin/launcher.
|
|
211
|
+
- **跨平台分发(方案 A)**:plugin 的 hooks.json 静态、无平台变量,靠 `bin/launcher.cjs` 按 `process.platform/arch` 选 `bin/<plat>-<arch>/` 里的二进制;hook/cli/daemon 同目录,hook 用 `process.execPath` 定位 daemon,零额外配置、装即用。
|
package/dist/install.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var w=require("node:fs"),X1=require("node:path"),Y1=require("node:child_process");var f=require("node:fs"),m=require("node:os"),y=require("node:path"),j=require("node:child_process"),G1=[1,1,0],H1=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 W1(b){return b.trim().split(".").map((q)=>parseInt(q,10)||0)}function J1(b,q){let z=W1(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 K1(){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:H1,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&&J1(z,G1))return console.log(`[shine-code-submit] bun ${z} detected`),b}if(console.log("[shine-code-submit] bun 未找到或版本过低,开始自动安装..."),K1()){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"),I=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 x(){return V.join(p(),"plugins")}function O(){return V.join(x(),"known_marketplaces.json")}function v(){return V.join(x(),"installed_plugins.json")}function B(){return V.join(p(),"settings.json")}var A={name:"shine-code-submit",version:"0.2.4",private:!1,type:"module",description:"Claude Code Hook -> 本地常驻 Daemon 的状态/持久化底座",author:"renguifeng",license:"MIT",repository:{type:"git",url:"http://8.130.168.121:9001/AI/shine-code-submit.git"},homepage:"http://8.130.168.121:9001/AI/shine-code-submit",bugs:{url:"http://8.130.168.121:9001/AI/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,g1=process.env.SHINE_CODE_SUBMIT_HOST??"0.0.0.0",V1="127.0.0.1",c=36666,N=`http://${V1}:${c}`;function U1(){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 u=`http://${U1()}:${c}`;var G="shine-code-submit",L="shine-code-submit";function R(b=W){return Y.join(x(),"cache",G,L,b)}function C1(){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 j1=[".claude-plugin","hooks","bin","src","ui","package.json","bun.lock","README.md"];function l(b){let q=R();if(Z.existsSync(q))Z.rmSync(q,{recursive:!0,force:!0});Z.mkdirSync(q,{recursive:!0});let z=C1();console.log(`[shine-code-submit] 部署源:${z}`);for(let X of j1){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=I.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=I.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 g(){return`${L}@${G}`}function o(b){let q=O(),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=v(),z=K(q,{version:2,plugins:{}});if(!z.version)z.version=2;if(!z.plugins)z.plugins={};let Q=g(),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=B(),z=K(q,{});if(!z.enabledPlugins)z.enabledPlugins={};let Q=g();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=g(),q=K(O(),{});if(q[G])delete q[G],F(O(),q);let z=K(v(),{plugins:{}});if(z.plugins&&z.plugins[b])delete z.plugins[b],F(v(),z);let Q=K(B(),{}),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(B(),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(`${N}/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(x1())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 x1(){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"),O1=process.env.LOCALAPPDATA??H.join(e.homedir(),".local","share"),C=H.join(O1,"shine-code-submit"),v1=H.join(C,"spool"),b1=H.join(C,"log"),q1=H.join(C,"db"),z1=H.join(C,"daemon.pid"),r1=H.join(b1,"daemon.log"),i1=H.join(q1,"events.sqlite");function Q1(){for(let b of[C,v1,b1,q1])a.mkdirSync(b,{recursive:!0})}var k=require("node:fs");function T(){try{let b=k.readFileSync(z1,"utf8");return JSON.parse(b)}catch{return null}}var[,,Z1]=process.argv;B1().catch((b)=>{console.error(`[shine-code-submit] ${b instanceof Error?b.message:String(b)}`),process.exit(1)});async function B1(){switch(Z1){case void 0:case"install":await N1();break;case"uninstall":await u1();break;case"status":await k1();break;case"--version":case"-v":console.log(W);break;default:M1()}}async function N1(){console.log(`=== shine-code-submit installer v${W} ===`);let b=await E(),q=l(b);o(q),s(q),r(q),Q1(),await T1(b,q),D1(),console.log(""),console.log("✓ 安装完成。"),console.log(" · 重启 Claude Code 后,/plugin 列表会显示 shine-code-submit(已启用)。"),console.log(" · 开新会话即触发 SessionStart hook,事件出现在 dashboard。")}async function u1(){console.log("=== shine-code-submit uninstaller ==="),await w1(),i();let b=R();if(w.existsSync(b))w.rmSync(b,{recursive:!0,force:!0}),console.log(`[shine-code-submit] 已删除 ${b}`);console.log("✓ 已卸载。重启 Claude Code 后 /plugin 不再显示。")}async function k1(){let b=await U(),q=T();if(b&&q)console.log(`daemon: running pid=${q.pid} ${u}`);else console.log("daemon: not running")}async function T1(b,q){if(await U()){console.log("[shine-code-submit] daemon 已在运行,跳过启动");return}let z=X1.join(q,"src","daemon","main.ts");console.log("[shine-code-submit] 启动 daemon...");try{Y1.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 $1(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 w1(){let b=T();if(!b){console.log("[shine-code-submit] daemon 未运行(无 pid 文件)");return}if(await U()){try{await fetch(`${N}/api/shutdown`,{method:"POST",headers:{authorization:`Bearer ${b.token}`}})}catch{}if(await $1(1000),await U())try{process.kill(b.pid)}catch{}}console.log("[shine-code-submit] daemon 已停止")}function D1(){let b=T(),q=b?`${u}/ui?t=${b.token}`:`${u}/ui`;console.log(`[shine-code-submit] Dashboard: ${q}`);try{n(q)}catch{}}function $1(b){return new Promise((q)=>setTimeout(q,b))}function M1(){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.5",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 + 反注册 + 删文件)
|
|
6
6
|
status 显示 daemon 状态
|
|
7
7
|
|
|
8
|
-
通常通过 npx 跑:npx shine-code-submit install`),process.exit(
|
|
8
|
+
通常通过 npx 跑:npx shine-code-submit install`),process.exit(Zb?1:0)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shine-code-submit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Claude Code Hook -> 本地常驻 Daemon 的状态/持久化底座",
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "
|
|
11
|
+
"url": "https://github.com/renguifeng/shine-code-submit.git"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "
|
|
13
|
+
"homepage": "https://github.com/renguifeng/shine-code-submit",
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "
|
|
15
|
+
"url": "https://github.com/renguifeng/shine-code-submit/issues"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
18
|
"claude",
|