ccpanel 0.1.3 → 0.1.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 +20 -78
- package/dist/{chunk-SZFZF7CH.js → chunk-LZFMCC5M.js} +241 -74
- package/dist/cli.js +1087 -144
- package/dist/server/app.js +1 -1
- package/package.json +29 -12
- package/web/dist/assets/index-B5vT_l-U.css +1 -0
- package/web/dist/assets/{index-BGhhpb6e.js → index-XKaR9SwA.js} +27 -30
- package/web/dist/index.html +3 -3
- package/web/dist/assets/index-DZh_OBPf.css +0 -1
package/README.md
CHANGED
|
@@ -1,78 +1,20 @@
|
|
|
1
|
-
# ccpanel
|
|
2
|
-
|
|
3
|
-
本地图形化管理 Claude Code 配置:MCP 服务器、Skills、子 Agent、Commands、配置文件、Memory、Sessions、Plugins(全局级 + 项目级)。
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## 构建
|
|
24
|
-
|
|
25
|
-
修改代码后需重新编译,构建产物供 `npx claude-config-manager` 生产运行:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm run build # 全量:前端 web/dist + 后端 dist
|
|
29
|
-
npm run build:web # 仅前端,产物 web/dist(改动 web/ 下代码时)
|
|
30
|
-
npm run build:server # 仅后端,产物 dist/(改动 src/ 下代码时)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
- 改完**前端**:`npm run build:web` 后刷新浏览器即可生效(后端静态托管 `web/dist`,无需重启)。
|
|
34
|
-
- 改完**后端**:`npm run build:server`;若用 `npm run dev`(tsx)运行后端,改动后需重启该进程才生效(tsx 不热重载)。
|
|
35
|
-
- 开发模式下前端 `npm --prefix web run dev` 自带热更新(HMR),免手动构建。
|
|
36
|
-
|
|
37
|
-
## 测试
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
npm test
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 发布 / 分发
|
|
44
|
-
|
|
45
|
-
让别人也能使用,有两种方式。两者都会自动包含前端产物 `web/dist`,发布前请确保已执行过 `npm run build`(`npm publish` 会通过 `prepublishOnly` 自动构建)。
|
|
46
|
-
|
|
47
|
-
### 方式一:发布到 npm(推荐,别人 `npx` 即用)
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm login # 首次需要 npm 账号
|
|
51
|
-
npm publish # prepublishOnly 会自动先 npm run build
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
发布后任何人可直接运行:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npx ccpanel
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
> 包名 `ccpanel` 目前在 npm 上可用;如后续被占用,可改用作用域名(如 `@你的用户名/ccpanel`),并以 `npm publish --access public` 发布。
|
|
61
|
-
|
|
62
|
-
### 方式二:分发 tarball(内网 / 小范围,无需发布)
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
npm run build # 先构建
|
|
66
|
-
npm pack # 生成 ccpanel-<version>.tgz
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
把生成的 `.tgz` 发给对方,对方安装运行:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
npm install -g ./ccpanel-0.1.0.tgz
|
|
73
|
-
ccpanel
|
|
74
|
-
# 或免安装直接运行:
|
|
75
|
-
npx ./ccpanel-0.1.0.tgz
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
> 使用者需 Node.js ≥ 20。工具为本地服务,启动后在 `localhost:7788` 读写本机 `~/.claude` 下的配置。
|
|
1
|
+
# ccpanel
|
|
2
|
+
|
|
3
|
+
本地图形化管理 Claude Code 与 Codex 配置:MCP 服务器、Skills、子 Agent、Commands、配置文件、Memory、Sessions、Plugins(全局级 + 项目级)。
|
|
4
|
+
|
|
5
|
+
- **Claude**:管理 `~/.claude` 下全部配置(全局级 + 项目级)
|
|
6
|
+
- **Codex**:管理 `~/.codex` 下的 MCP、Skills、插件、配置文件、记忆和会话(配置仅全局级;记忆/会话只读)
|
|
7
|
+
- **公共 Skills**:展示 `~/.agents/skills`,一键同步到 Claude / Codex(创建目录链接),也可把本地技能「转公共」
|
|
8
|
+
|
|
9
|
+
## 快速开始
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx ccpanel
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
启动后自动打开浏览器(默认 http://localhost:7788),顶部可切换 Claude / Codex / 公共三个视图。需 Node.js ≥ 20。
|
|
16
|
+
|
|
17
|
+
## 文档
|
|
18
|
+
|
|
19
|
+
- 技术栈、开发/构建/测试、发布分发:[docs/development.md](docs/development.md)
|
|
20
|
+
- Claude Code / Codex / 公共 Skills 目录结构与 ccpanel 读写范围:[docs/agent-directories.md](docs/agent-directories.md)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/server/app.ts
|
|
2
|
-
import { Hono as
|
|
2
|
+
import { Hono as Hono11 } from "hono";
|
|
3
3
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
4
4
|
import { ZodError } from "zod";
|
|
5
5
|
|
|
@@ -37,7 +37,8 @@ var ConfigStore = class {
|
|
|
37
37
|
throw new ConfigCorruptError(file);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
// 原子写 + 写前同目录 .bak 备份(仅当原文件存在)。内容原样落盘。
|
|
41
|
+
async writeText(file, content) {
|
|
41
42
|
await mkdir(dirname(file), { recursive: true });
|
|
42
43
|
try {
|
|
43
44
|
await copyFile(file, file + ".bak");
|
|
@@ -45,9 +46,12 @@ var ConfigStore = class {
|
|
|
45
46
|
if (err.code !== "ENOENT") throw err;
|
|
46
47
|
}
|
|
47
48
|
const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
48
|
-
await writeFile(tmp,
|
|
49
|
+
await writeFile(tmp, content, "utf8");
|
|
49
50
|
await rename(tmp, file);
|
|
50
51
|
}
|
|
52
|
+
async writeJson(file, data) {
|
|
53
|
+
await this.writeText(file, JSON.stringify(data, null, 2) + "\n");
|
|
54
|
+
}
|
|
51
55
|
};
|
|
52
56
|
|
|
53
57
|
// src/core/errors.ts
|
|
@@ -79,21 +83,36 @@ import { createHash } from "crypto";
|
|
|
79
83
|
import { join, resolve } from "path";
|
|
80
84
|
function resolvePaths(opts) {
|
|
81
85
|
const { home, projectPath } = opts;
|
|
86
|
+
const provider = opts.provider ?? "claude";
|
|
82
87
|
const panelDir = join(home, ".ccpanel");
|
|
88
|
+
const codexDir = join(home, ".codex");
|
|
89
|
+
const agentsSkillsDir = join(home, ".agents", "skills");
|
|
90
|
+
const userSkillsDir = provider === "codex" ? join(codexDir, "skills") : provider === "agents" ? agentsSkillsDir : join(home, ".claude", "skills");
|
|
91
|
+
const userDisabledSkillsDir = provider === "codex" ? join(panelDir, "codex-disabled-skills") : provider === "agents" ? join(panelDir, "agents-disabled-skills") : join(panelDir, "disabled-skills");
|
|
83
92
|
const paths = {
|
|
93
|
+
provider,
|
|
84
94
|
userClaudeJson: join(home, ".claude.json"),
|
|
85
|
-
|
|
95
|
+
userMcpFile: provider === "codex" ? join(codexDir, "config.toml") : join(home, ".claude.json"),
|
|
96
|
+
userSkillsDir,
|
|
86
97
|
userAgentsDir: join(home, ".claude", "agents"),
|
|
87
98
|
userCommandsDir: join(home, ".claude", "commands"),
|
|
88
|
-
userDisabledMcp: join(panelDir, "disabled-mcp.json"),
|
|
89
|
-
userDisabledSkillsDir
|
|
99
|
+
userDisabledMcp: provider === "codex" ? join(panelDir, "codex-disabled-mcp.json") : join(panelDir, "disabled-mcp.json"),
|
|
100
|
+
userDisabledSkillsDir,
|
|
90
101
|
userSettings: join(home, ".claude", "settings.json"),
|
|
91
102
|
userClaudeMd: join(home, ".claude", "CLAUDE.md"),
|
|
92
103
|
userProjectsDir: join(home, ".claude", "projects"),
|
|
93
104
|
pluginsDir: join(home, ".claude", "plugins"),
|
|
94
105
|
installedPluginsJson: join(home, ".claude", "plugins", "installed_plugins.json"),
|
|
95
106
|
knownMarketplacesJson: join(home, ".claude", "plugins", "known_marketplaces.json"),
|
|
96
|
-
panelDir
|
|
107
|
+
panelDir,
|
|
108
|
+
agentsSkillsDir,
|
|
109
|
+
codexConfigToml: join(codexDir, "config.toml"),
|
|
110
|
+
codexAgentsMd: join(codexDir, "AGENTS.md"),
|
|
111
|
+
codexPluginsDir: join(codexDir, "plugins"),
|
|
112
|
+
codexMemoriesDir: join(codexDir, "memories"),
|
|
113
|
+
codexMemoriesDb: join(codexDir, "memories_1.sqlite"),
|
|
114
|
+
codexSessionsDir: join(codexDir, "sessions"),
|
|
115
|
+
codexSessionIndex: join(codexDir, "session_index.jsonl")
|
|
97
116
|
};
|
|
98
117
|
if (projectPath) {
|
|
99
118
|
paths.projectMcpJson = join(projectPath, ".mcp.json");
|
|
@@ -111,17 +130,30 @@ function resolvePaths(opts) {
|
|
|
111
130
|
// src/shared/schemas.ts
|
|
112
131
|
import { z } from "zod";
|
|
113
132
|
var ScopeSchema = z.enum(["user", "project"]);
|
|
133
|
+
var ProviderSchema = z.enum(["claude", "codex", "agents"]);
|
|
134
|
+
var McpProviderSchema = z.enum(["claude", "codex"]);
|
|
135
|
+
var ProviderScopeQuerySchema = z.object({
|
|
136
|
+
provider: ProviderSchema.default("claude"),
|
|
137
|
+
scope: ScopeSchema,
|
|
138
|
+
projectPath: z.string().optional()
|
|
139
|
+
}).refine((v) => v.scope === "user" || !!v.projectPath, {
|
|
140
|
+
message: "project scope \u9700\u8981 projectPath",
|
|
141
|
+
path: ["projectPath"]
|
|
142
|
+
}).refine((v) => v.provider === "claude" || v.scope === "user", {
|
|
143
|
+
message: "\u8BE5 provider \u4EC5\u652F\u6301\u7528\u6237\u7EA7\u4F5C\u7528\u57DF\uFF08scope=user\uFF09",
|
|
144
|
+
path: ["provider"]
|
|
145
|
+
});
|
|
114
146
|
var StdioMcpServerSchema = z.object({
|
|
115
147
|
type: z.literal("stdio").optional(),
|
|
116
148
|
command: z.string().min(1),
|
|
117
149
|
args: z.array(z.string()).optional(),
|
|
118
150
|
env: z.record(z.string()).optional()
|
|
119
|
-
});
|
|
151
|
+
}).passthrough();
|
|
120
152
|
var RemoteMcpServerSchema = z.object({
|
|
121
153
|
type: z.enum(["sse", "http"]),
|
|
122
154
|
url: z.string().url(),
|
|
123
155
|
headers: z.record(z.string()).optional()
|
|
124
|
-
});
|
|
156
|
+
}).passthrough();
|
|
125
157
|
var McpServerSchema = z.union([
|
|
126
158
|
RemoteMcpServerSchema,
|
|
127
159
|
StdioMcpServerSchema
|
|
@@ -140,7 +172,12 @@ var MemoryWriteSchema = z.object({
|
|
|
140
172
|
content: z.string(),
|
|
141
173
|
baseMtime: BaseMtimeSchema
|
|
142
174
|
});
|
|
143
|
-
var ConfigFileIdSchema = z.enum([
|
|
175
|
+
var ConfigFileIdSchema = z.enum([
|
|
176
|
+
"settings",
|
|
177
|
+
"claude-json",
|
|
178
|
+
"codex-config",
|
|
179
|
+
"codex-agents"
|
|
180
|
+
]);
|
|
144
181
|
var ConfigFileWriteSchema = z.object({
|
|
145
182
|
content: z.string(),
|
|
146
183
|
baseMtime: BaseMtimeSchema
|
|
@@ -157,11 +194,20 @@ var ScopeQuerySchema = z.object({
|
|
|
157
194
|
function mcpRoutes(deps) {
|
|
158
195
|
const app = new Hono();
|
|
159
196
|
const ctx = (c) => {
|
|
160
|
-
const q =
|
|
197
|
+
const q = ProviderScopeQuerySchema.parse({
|
|
198
|
+
provider: c.req.query("provider"),
|
|
161
199
|
scope: c.req.query("scope"),
|
|
162
200
|
projectPath: c.req.query("projectPath")
|
|
163
201
|
});
|
|
164
|
-
|
|
202
|
+
McpProviderSchema.parse(q.provider);
|
|
203
|
+
return {
|
|
204
|
+
q,
|
|
205
|
+
paths: resolvePaths({
|
|
206
|
+
home: deps.home,
|
|
207
|
+
projectPath: q.projectPath,
|
|
208
|
+
provider: q.provider
|
|
209
|
+
})
|
|
210
|
+
};
|
|
165
211
|
};
|
|
166
212
|
app.get("/", async (c) => {
|
|
167
213
|
const { q, paths } = ctx(c);
|
|
@@ -205,11 +251,19 @@ var SkillBodySchema = z2.object({
|
|
|
205
251
|
function skillRoutes(deps) {
|
|
206
252
|
const app = new Hono2();
|
|
207
253
|
const ctx = (c) => {
|
|
208
|
-
const q =
|
|
254
|
+
const q = ProviderScopeQuerySchema.parse({
|
|
255
|
+
provider: c.req.query("provider"),
|
|
209
256
|
scope: c.req.query("scope"),
|
|
210
257
|
projectPath: c.req.query("projectPath")
|
|
211
258
|
});
|
|
212
|
-
return {
|
|
259
|
+
return {
|
|
260
|
+
q,
|
|
261
|
+
paths: resolvePaths({
|
|
262
|
+
home: deps.home,
|
|
263
|
+
projectPath: q.projectPath,
|
|
264
|
+
provider: q.provider
|
|
265
|
+
})
|
|
266
|
+
};
|
|
213
267
|
};
|
|
214
268
|
app.get("/", async (c) => {
|
|
215
269
|
const { q, paths } = ctx(c);
|
|
@@ -266,34 +320,76 @@ function skillRoutes(deps) {
|
|
|
266
320
|
});
|
|
267
321
|
app.post("/:name/toggle", async (c) => {
|
|
268
322
|
const { q, paths } = ctx(c);
|
|
323
|
+
if (q.provider === "agents") {
|
|
324
|
+
return c.json({ error: "\u516C\u5171 skills \u4E0D\u652F\u6301\u542F\u7528/\u7981\u7528" }, 400);
|
|
325
|
+
}
|
|
269
326
|
const { enabled } = await c.req.json();
|
|
270
327
|
await deps.skills.toggle(paths, q.scope, c.req.param("name"), enabled);
|
|
271
328
|
return c.json(await deps.skills.list(paths, q.scope));
|
|
272
329
|
});
|
|
330
|
+
app.post("/:name/make-shared", async (c) => {
|
|
331
|
+
const { q } = ctx(c);
|
|
332
|
+
if (q.provider === "agents") {
|
|
333
|
+
return c.json({ error: "\u8F6C\u516C\u5171\u4EC5\u652F\u6301 claude/codex \u4E0B\u7684\u6280\u80FD" }, 400);
|
|
334
|
+
}
|
|
335
|
+
await deps.sync.makeShared(q.provider, c.req.param("name"));
|
|
336
|
+
return c.json({ ok: true });
|
|
337
|
+
});
|
|
273
338
|
return app;
|
|
274
339
|
}
|
|
275
340
|
|
|
276
|
-
// src/server/routes/
|
|
341
|
+
// src/server/routes/shared-skills.ts
|
|
277
342
|
import { Hono as Hono3 } from "hono";
|
|
278
343
|
import { z as z3 } from "zod";
|
|
279
|
-
|
|
344
|
+
var SyncBodySchema = z3.object({ target: z3.enum(["claude", "codex"]) });
|
|
345
|
+
function sharedSkillRoutes(deps) {
|
|
280
346
|
const app = new Hono3();
|
|
347
|
+
app.get("/", async (c) => {
|
|
348
|
+
const paths = resolvePaths({ home: deps.home, provider: "agents" });
|
|
349
|
+
const base = await deps.skills.list(paths, "user");
|
|
350
|
+
const withSync = await Promise.all(
|
|
351
|
+
base.map(async (s) => ({ ...s, sync: await deps.sync.status(s.name) }))
|
|
352
|
+
);
|
|
353
|
+
return c.json(withSync);
|
|
354
|
+
});
|
|
355
|
+
app.post("/:name/sync", async (c) => {
|
|
356
|
+
const { target } = SyncBodySchema.parse(await c.req.json());
|
|
357
|
+
await deps.sync.syncTo(c.req.param("name"), target);
|
|
358
|
+
return c.json({ ok: true });
|
|
359
|
+
});
|
|
360
|
+
app.delete("/:name/sync", async (c) => {
|
|
361
|
+
const { target } = SyncBodySchema.parse(await c.req.json());
|
|
362
|
+
await deps.sync.unsync(c.req.param("name"), target);
|
|
363
|
+
return c.json({ ok: true });
|
|
364
|
+
});
|
|
365
|
+
return app;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/server/routes/projects.ts
|
|
369
|
+
import { Hono as Hono4 } from "hono";
|
|
370
|
+
import { z as z4 } from "zod";
|
|
371
|
+
function projectRoutes(deps) {
|
|
372
|
+
const app = new Hono4();
|
|
281
373
|
app.get("/", async (c) => c.json({ projects: await deps.projects.list() }));
|
|
374
|
+
app.get(
|
|
375
|
+
"/known",
|
|
376
|
+
async (c) => c.json({ projects: await deps.projects.known() })
|
|
377
|
+
);
|
|
282
378
|
app.post("/", async (c) => {
|
|
283
|
-
const { path } =
|
|
379
|
+
const { path } = z4.object({ path: z4.string().min(1) }).parse(await c.req.json());
|
|
284
380
|
return c.json({ projects: await deps.projects.add(path) });
|
|
285
381
|
});
|
|
286
382
|
app.delete("/", async (c) => {
|
|
287
|
-
const { path } =
|
|
383
|
+
const { path } = z4.object({ path: z4.string().min(1) }).parse(await c.req.json());
|
|
288
384
|
return c.json({ projects: await deps.projects.remove(path) });
|
|
289
385
|
});
|
|
290
386
|
return app;
|
|
291
387
|
}
|
|
292
388
|
|
|
293
389
|
// src/server/routes/memory.ts
|
|
294
|
-
import { Hono as
|
|
390
|
+
import { Hono as Hono5 } from "hono";
|
|
295
391
|
function memoryRoutes(deps) {
|
|
296
|
-
const app = new
|
|
392
|
+
const app = new Hono5();
|
|
297
393
|
const ctx = (c) => {
|
|
298
394
|
const q = ScopeQuerySchema.parse({
|
|
299
395
|
scope: c.req.query("scope"),
|
|
@@ -315,96 +411,164 @@ function memoryRoutes(deps) {
|
|
|
315
411
|
}
|
|
316
412
|
|
|
317
413
|
// src/server/routes/memory-files.ts
|
|
318
|
-
import { Hono as
|
|
319
|
-
import { z as
|
|
320
|
-
var
|
|
414
|
+
import { Hono as Hono6 } from "hono";
|
|
415
|
+
import { z as z5 } from "zod";
|
|
416
|
+
var ProviderQuerySchema = z5.object({
|
|
417
|
+
provider: z5.enum(["claude", "codex"]).default("claude")
|
|
418
|
+
});
|
|
419
|
+
var ClaudeQuerySchema = z5.object({ projectPath: z5.string().min(1) });
|
|
321
420
|
function memoryFilesRoutes(deps) {
|
|
322
|
-
const app = new
|
|
421
|
+
const app = new Hono6();
|
|
323
422
|
const ctx = (c) => {
|
|
324
|
-
const {
|
|
325
|
-
|
|
423
|
+
const { provider } = ProviderQuerySchema.parse({
|
|
424
|
+
provider: c.req.query("provider") ?? "claude"
|
|
425
|
+
});
|
|
426
|
+
if (provider === "codex") {
|
|
427
|
+
return {
|
|
428
|
+
provider,
|
|
429
|
+
projectPath: void 0,
|
|
430
|
+
paths: resolvePaths({ home: deps.home, provider })
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const { projectPath } = ClaudeQuerySchema.parse({
|
|
434
|
+
projectPath: c.req.query("projectPath")
|
|
435
|
+
});
|
|
436
|
+
return {
|
|
437
|
+
provider,
|
|
438
|
+
projectPath,
|
|
439
|
+
paths: resolvePaths({ home: deps.home, projectPath, provider })
|
|
440
|
+
};
|
|
326
441
|
};
|
|
327
442
|
app.get("/", async (c) => {
|
|
328
|
-
const { projectPath, paths } = ctx(c);
|
|
329
|
-
return c.json(
|
|
443
|
+
const { provider, projectPath, paths } = ctx(c);
|
|
444
|
+
return c.json(
|
|
445
|
+
provider === "codex" ? await deps.codexMemory.overview(paths) : await deps.memoryFiles.overview(paths, projectPath)
|
|
446
|
+
);
|
|
330
447
|
});
|
|
331
448
|
app.get("/:file", async (c) => {
|
|
332
|
-
const { projectPath, paths } = ctx(c);
|
|
333
|
-
return c.json(
|
|
449
|
+
const { provider, projectPath, paths } = ctx(c);
|
|
450
|
+
return c.json(
|
|
451
|
+
provider === "codex" ? await deps.codexMemory.read(paths, c.req.param("file")) : await deps.memoryFiles.read(paths, projectPath, c.req.param("file"))
|
|
452
|
+
);
|
|
334
453
|
});
|
|
335
454
|
return app;
|
|
336
455
|
}
|
|
337
456
|
|
|
338
457
|
// src/server/routes/sessions.ts
|
|
339
|
-
import { Hono as
|
|
340
|
-
import { z as
|
|
341
|
-
var
|
|
458
|
+
import { Hono as Hono7 } from "hono";
|
|
459
|
+
import { z as z6 } from "zod";
|
|
460
|
+
var QuerySchema = z6.object({
|
|
461
|
+
provider: z6.enum(["claude", "codex"]).default("claude"),
|
|
462
|
+
projectPath: z6.string().min(1)
|
|
463
|
+
});
|
|
342
464
|
function sessionRoutes(deps) {
|
|
343
|
-
const app = new
|
|
465
|
+
const app = new Hono7();
|
|
344
466
|
const ctx = (c) => {
|
|
345
|
-
const { projectPath } =
|
|
346
|
-
|
|
467
|
+
const { provider, projectPath } = QuerySchema.parse({
|
|
468
|
+
provider: c.req.query("provider") ?? "claude",
|
|
469
|
+
projectPath: c.req.query("projectPath")
|
|
470
|
+
});
|
|
471
|
+
return {
|
|
472
|
+
provider,
|
|
473
|
+
projectPath,
|
|
474
|
+
paths: resolvePaths({ home: deps.home, projectPath, provider })
|
|
475
|
+
};
|
|
347
476
|
};
|
|
348
477
|
app.get("/", async (c) => {
|
|
349
|
-
const { projectPath, paths } = ctx(c);
|
|
350
|
-
return c.json(
|
|
478
|
+
const { provider, projectPath, paths } = ctx(c);
|
|
479
|
+
return c.json(
|
|
480
|
+
provider === "codex" ? await deps.codexSessions.list(paths, projectPath) : await deps.sessions.list(paths, projectPath)
|
|
481
|
+
);
|
|
351
482
|
});
|
|
352
483
|
app.get("/:id", async (c) => {
|
|
353
|
-
const { projectPath, paths } = ctx(c);
|
|
354
|
-
return c.json(
|
|
484
|
+
const { provider, projectPath, paths } = ctx(c);
|
|
485
|
+
return c.json(
|
|
486
|
+
provider === "codex" ? await deps.codexSessions.read(paths, projectPath, c.req.param("id")) : await deps.sessions.read(paths, projectPath, c.req.param("id"))
|
|
487
|
+
);
|
|
355
488
|
});
|
|
356
489
|
return app;
|
|
357
490
|
}
|
|
358
491
|
|
|
359
492
|
// src/server/routes/plugins.ts
|
|
360
|
-
import { Hono as
|
|
361
|
-
import { z as
|
|
362
|
-
var
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
493
|
+
import { Hono as Hono8 } from "hono";
|
|
494
|
+
import { z as z7 } from "zod";
|
|
495
|
+
var ProviderQuerySchema2 = z7.object({
|
|
496
|
+
provider: z7.enum(["claude", "codex"]).default("claude")
|
|
497
|
+
});
|
|
498
|
+
var EnabledBodySchema = z7.object({
|
|
499
|
+
enabled: z7.boolean(),
|
|
500
|
+
baseMtime: z7.number().optional()
|
|
501
|
+
});
|
|
502
|
+
var ComponentQuerySchema = z7.object({
|
|
503
|
+
type: z7.enum(["skill", "command", "agent", "hook", "mcp"]),
|
|
504
|
+
name: z7.string().min(1)
|
|
366
505
|
});
|
|
367
506
|
function pluginRoutes(deps) {
|
|
368
|
-
const app = new
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
507
|
+
const app = new Hono8();
|
|
508
|
+
const ctx = (c) => {
|
|
509
|
+
const { provider } = ProviderQuerySchema2.parse({
|
|
510
|
+
provider: c.req.query("provider") ?? "claude"
|
|
511
|
+
});
|
|
512
|
+
return { provider, paths: resolvePaths({ home: deps.home, provider }) };
|
|
513
|
+
};
|
|
514
|
+
app.get("/", async (c) => {
|
|
515
|
+
const { provider, paths } = ctx(c);
|
|
516
|
+
return c.json(
|
|
517
|
+
provider === "codex" ? await deps.codexPlugins.list(paths) : await deps.plugins.list(paths)
|
|
518
|
+
);
|
|
519
|
+
});
|
|
520
|
+
app.get("/:id", async (c) => {
|
|
521
|
+
const { provider, paths } = ctx(c);
|
|
522
|
+
return c.json(
|
|
523
|
+
provider === "codex" ? await deps.codexPlugins.detail(paths, c.req.param("id")) : await deps.plugins.detail(paths, c.req.param("id"))
|
|
524
|
+
);
|
|
525
|
+
});
|
|
375
526
|
app.get("/:id/component", async (c) => {
|
|
527
|
+
const { provider, paths } = ctx(c);
|
|
376
528
|
const q = ComponentQuerySchema.parse({
|
|
377
529
|
type: c.req.query("type"),
|
|
378
530
|
name: c.req.query("name")
|
|
379
531
|
});
|
|
532
|
+
const svc = provider === "codex" ? deps.codexPlugins : deps.plugins;
|
|
380
533
|
return c.json(
|
|
381
|
-
await
|
|
382
|
-
paths(),
|
|
383
|
-
c.req.param("id"),
|
|
384
|
-
q.type,
|
|
385
|
-
q.name
|
|
386
|
-
)
|
|
534
|
+
await svc.readComponent(paths, c.req.param("id"), q.type, q.name)
|
|
387
535
|
);
|
|
388
536
|
});
|
|
389
537
|
app.patch("/:id", async (c) => {
|
|
390
|
-
const {
|
|
391
|
-
|
|
392
|
-
|
|
538
|
+
const { provider, paths } = ctx(c);
|
|
539
|
+
const { enabled, baseMtime } = EnabledBodySchema.parse(await c.req.json());
|
|
540
|
+
if (provider === "codex") {
|
|
541
|
+
await deps.codexPlugins.setEnabled(
|
|
542
|
+
paths,
|
|
543
|
+
c.req.param("id"),
|
|
544
|
+
enabled,
|
|
545
|
+
baseMtime
|
|
546
|
+
);
|
|
547
|
+
return c.json(await deps.codexPlugins.list(paths));
|
|
548
|
+
}
|
|
549
|
+
await deps.plugins.setEnabled(paths, c.req.param("id"), enabled);
|
|
550
|
+
return c.json(await deps.plugins.list(paths));
|
|
393
551
|
});
|
|
394
552
|
return app;
|
|
395
553
|
}
|
|
396
554
|
|
|
397
555
|
// src/server/routes/config-files.ts
|
|
398
|
-
import { Hono as
|
|
556
|
+
import { Hono as Hono9 } from "hono";
|
|
399
557
|
function configFileRoutes(deps) {
|
|
400
|
-
const app = new
|
|
558
|
+
const app = new Hono9();
|
|
401
559
|
const ctx = (c) => {
|
|
402
560
|
const fileId = ConfigFileIdSchema.parse(c.req.param("file"));
|
|
403
|
-
const q =
|
|
561
|
+
const q = ProviderScopeQuerySchema.parse({
|
|
562
|
+
provider: c.req.query("provider"),
|
|
404
563
|
scope: c.req.query("scope"),
|
|
405
564
|
projectPath: c.req.query("projectPath")
|
|
406
565
|
});
|
|
407
|
-
|
|
566
|
+
McpProviderSchema.parse(q.provider);
|
|
567
|
+
return {
|
|
568
|
+
fileId,
|
|
569
|
+
q,
|
|
570
|
+
paths: resolvePaths({ home: deps.home, projectPath: q.projectPath, provider: q.provider })
|
|
571
|
+
};
|
|
408
572
|
};
|
|
409
573
|
app.get("/:file", async (c) => {
|
|
410
574
|
const { fileId, q, paths } = ctx(c);
|
|
@@ -420,15 +584,15 @@ function configFileRoutes(deps) {
|
|
|
420
584
|
}
|
|
421
585
|
|
|
422
586
|
// src/server/routes/agent-commands.ts
|
|
423
|
-
import { Hono as
|
|
424
|
-
import { z as
|
|
425
|
-
var CreateSchema =
|
|
426
|
-
var WriteSchema =
|
|
427
|
-
content:
|
|
428
|
-
baseMtime:
|
|
587
|
+
import { Hono as Hono10 } from "hono";
|
|
588
|
+
import { z as z8 } from "zod";
|
|
589
|
+
var CreateSchema = z8.object({ name: z8.string().min(1), content: z8.string() });
|
|
590
|
+
var WriteSchema = z8.object({
|
|
591
|
+
content: z8.string(),
|
|
592
|
+
baseMtime: z8.number().nullable().optional()
|
|
429
593
|
});
|
|
430
594
|
function agentCommandRoutes(deps, kind) {
|
|
431
|
-
const app = new
|
|
595
|
+
const app = new Hono10();
|
|
432
596
|
const ctx = (c) => {
|
|
433
597
|
const q = ScopeQuerySchema.parse({
|
|
434
598
|
scope: c.req.query("scope"),
|
|
@@ -466,7 +630,7 @@ function agentCommandRoutes(deps, kind) {
|
|
|
466
630
|
|
|
467
631
|
// src/server/app.ts
|
|
468
632
|
function createApp(deps) {
|
|
469
|
-
const app = new
|
|
633
|
+
const app = new Hono11();
|
|
470
634
|
app.onError((err, c) => {
|
|
471
635
|
if (err instanceof ZodError) {
|
|
472
636
|
return c.json({ error: "\u6821\u9A8C\u5931\u8D25", issues: err.issues }, 400);
|
|
@@ -484,6 +648,7 @@ function createApp(deps) {
|
|
|
484
648
|
});
|
|
485
649
|
app.route("/api/mcp", mcpRoutes(deps));
|
|
486
650
|
app.route("/api/skills", skillRoutes(deps));
|
|
651
|
+
app.route("/api/shared-skills", sharedSkillRoutes(deps));
|
|
487
652
|
app.route("/api/projects", projectRoutes(deps));
|
|
488
653
|
app.route("/api/memory", memoryRoutes(deps));
|
|
489
654
|
app.route("/api/memory-files", memoryFilesRoutes(deps));
|
|
@@ -497,8 +662,10 @@ function createApp(deps) {
|
|
|
497
662
|
}
|
|
498
663
|
|
|
499
664
|
export {
|
|
665
|
+
ConfigCorruptError,
|
|
500
666
|
InvalidJsonError,
|
|
501
667
|
ConfigStore,
|
|
668
|
+
ConflictError,
|
|
502
669
|
currentMtime,
|
|
503
670
|
assertNoConflict,
|
|
504
671
|
SkillFrontmatterSchema,
|