ccpanel 0.1.2 → 0.1.4

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.
@@ -1,5 +1,5 @@
1
1
  // src/server/app.ts
2
- import { Hono as Hono10 } from "hono";
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
- async writeJson(file, data) {
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,11 +46,35 @@ 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, JSON.stringify(data, null, 2) + "\n", "utf8");
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
 
57
+ // src/core/errors.ts
58
+ import { stat } from "fs/promises";
59
+ var ConflictError = class extends Error {
60
+ constructor(message = "\u6587\u4EF6\u5DF2\u88AB\u5916\u90E8\u4FEE\u6539\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u540E\u518D\u4FDD\u5B58") {
61
+ super(message);
62
+ this.name = "ConflictError";
63
+ }
64
+ };
65
+ async function currentMtime(file) {
66
+ try {
67
+ return (await stat(file)).mtimeMs;
68
+ } catch (err) {
69
+ if (err.code === "ENOENT") return null;
70
+ throw err;
71
+ }
72
+ }
73
+ async function assertNoConflict(file, baseMtime) {
74
+ if (baseMtime === void 0) return;
75
+ if (await currentMtime(file) !== baseMtime) throw new ConflictError();
76
+ }
77
+
53
78
  // src/server/routes/mcp.ts
54
79
  import { Hono } from "hono";
55
80
 
@@ -58,21 +83,36 @@ import { createHash } from "crypto";
58
83
  import { join, resolve } from "path";
59
84
  function resolvePaths(opts) {
60
85
  const { home, projectPath } = opts;
86
+ const provider = opts.provider ?? "claude";
61
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");
62
92
  const paths = {
93
+ provider,
63
94
  userClaudeJson: join(home, ".claude.json"),
64
- userSkillsDir: join(home, ".claude", "skills"),
95
+ userMcpFile: provider === "codex" ? join(codexDir, "config.toml") : join(home, ".claude.json"),
96
+ userSkillsDir,
65
97
  userAgentsDir: join(home, ".claude", "agents"),
66
98
  userCommandsDir: join(home, ".claude", "commands"),
67
- userDisabledMcp: join(panelDir, "disabled-mcp.json"),
68
- userDisabledSkillsDir: join(panelDir, "disabled-skills"),
99
+ userDisabledMcp: provider === "codex" ? join(panelDir, "codex-disabled-mcp.json") : join(panelDir, "disabled-mcp.json"),
100
+ userDisabledSkillsDir,
69
101
  userSettings: join(home, ".claude", "settings.json"),
70
102
  userClaudeMd: join(home, ".claude", "CLAUDE.md"),
71
103
  userProjectsDir: join(home, ".claude", "projects"),
72
104
  pluginsDir: join(home, ".claude", "plugins"),
73
105
  installedPluginsJson: join(home, ".claude", "plugins", "installed_plugins.json"),
74
106
  knownMarketplacesJson: join(home, ".claude", "plugins", "known_marketplaces.json"),
75
- 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")
76
116
  };
77
117
  if (projectPath) {
78
118
  paths.projectMcpJson = join(projectPath, ".mcp.json");
@@ -90,17 +130,30 @@ function resolvePaths(opts) {
90
130
  // src/shared/schemas.ts
91
131
  import { z } from "zod";
92
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
+ });
93
146
  var StdioMcpServerSchema = z.object({
94
147
  type: z.literal("stdio").optional(),
95
148
  command: z.string().min(1),
96
149
  args: z.array(z.string()).optional(),
97
150
  env: z.record(z.string()).optional()
98
- });
151
+ }).passthrough();
99
152
  var RemoteMcpServerSchema = z.object({
100
153
  type: z.enum(["sse", "http"]),
101
154
  url: z.string().url(),
102
155
  headers: z.record(z.string()).optional()
103
- });
156
+ }).passthrough();
104
157
  var McpServerSchema = z.union([
105
158
  RemoteMcpServerSchema,
106
159
  StdioMcpServerSchema
@@ -110,10 +163,25 @@ var McpServerInputSchema = z.intersection(
110
163
  McpServerSchema
111
164
  );
112
165
  var SkillFrontmatterSchema = z.object({ name: z.string().min(1), description: z.string().min(1) }).passthrough();
113
- var SkillFileWriteSchema = z.object({ content: z.string() });
114
- var MemoryWriteSchema = z.object({ content: z.string() });
115
- var ConfigFileIdSchema = z.enum(["settings", "claude-json"]);
116
- var ConfigFileWriteSchema = z.object({ content: z.string() });
166
+ var BaseMtimeSchema = z.number().nullable().optional();
167
+ var SkillFileWriteSchema = z.object({
168
+ content: z.string(),
169
+ baseMtime: BaseMtimeSchema
170
+ });
171
+ var MemoryWriteSchema = z.object({
172
+ content: z.string(),
173
+ baseMtime: BaseMtimeSchema
174
+ });
175
+ var ConfigFileIdSchema = z.enum([
176
+ "settings",
177
+ "claude-json",
178
+ "codex-config",
179
+ "codex-agents"
180
+ ]);
181
+ var ConfigFileWriteSchema = z.object({
182
+ content: z.string(),
183
+ baseMtime: BaseMtimeSchema
184
+ });
117
185
  var ScopeQuerySchema = z.object({
118
186
  scope: ScopeSchema,
119
187
  projectPath: z.string().optional()
@@ -126,11 +194,20 @@ var ScopeQuerySchema = z.object({
126
194
  function mcpRoutes(deps) {
127
195
  const app = new Hono();
128
196
  const ctx = (c) => {
129
- const q = ScopeQuerySchema.parse({
197
+ const q = ProviderScopeQuerySchema.parse({
198
+ provider: c.req.query("provider"),
130
199
  scope: c.req.query("scope"),
131
200
  projectPath: c.req.query("projectPath")
132
201
  });
133
- return { q, paths: resolvePaths({ home: deps.home, projectPath: q.projectPath }) };
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
+ };
134
211
  };
135
212
  app.get("/", async (c) => {
136
213
  const { q, paths } = ctx(c);
@@ -174,11 +251,19 @@ var SkillBodySchema = z2.object({
174
251
  function skillRoutes(deps) {
175
252
  const app = new Hono2();
176
253
  const ctx = (c) => {
177
- const q = ScopeQuerySchema.parse({
254
+ const q = ProviderScopeQuerySchema.parse({
255
+ provider: c.req.query("provider"),
178
256
  scope: c.req.query("scope"),
179
257
  projectPath: c.req.query("projectPath")
180
258
  });
181
- return { q, paths: resolvePaths({ home: deps.home, projectPath: q.projectPath }) };
259
+ return {
260
+ q,
261
+ paths: resolvePaths({
262
+ home: deps.home,
263
+ projectPath: q.projectPath,
264
+ provider: q.provider
265
+ })
266
+ };
182
267
  };
183
268
  app.get("/", async (c) => {
184
269
  const { q, paths } = ctx(c);
@@ -205,13 +290,14 @@ function skillRoutes(deps) {
205
290
  });
206
291
  app.put("/:name/files/:filePath{.+}", async (c) => {
207
292
  const { q, paths } = ctx(c);
208
- const { content } = SkillFileWriteSchema.parse(await c.req.json());
293
+ const { content, baseMtime } = SkillFileWriteSchema.parse(await c.req.json());
209
294
  await deps.skills.writeFileRaw(
210
295
  paths,
211
296
  q.scope,
212
297
  c.req.param("name"),
213
298
  c.req.param("filePath"),
214
- content
299
+ content,
300
+ baseMtime
215
301
  );
216
302
  return c.json({ ok: true });
217
303
  });
@@ -234,34 +320,72 @@ function skillRoutes(deps) {
234
320
  });
235
321
  app.post("/:name/toggle", async (c) => {
236
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
+ }
237
326
  const { enabled } = await c.req.json();
238
327
  await deps.skills.toggle(paths, q.scope, c.req.param("name"), enabled);
239
328
  return c.json(await deps.skills.list(paths, q.scope));
240
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
+ });
241
338
  return app;
242
339
  }
243
340
 
244
- // src/server/routes/projects.ts
341
+ // src/server/routes/shared-skills.ts
245
342
  import { Hono as Hono3 } from "hono";
246
343
  import { z as z3 } from "zod";
247
- function projectRoutes(deps) {
344
+ var SyncBodySchema = z3.object({ target: z3.enum(["claude", "codex"]) });
345
+ function sharedSkillRoutes(deps) {
248
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();
249
373
  app.get("/", async (c) => c.json({ projects: await deps.projects.list() }));
250
374
  app.post("/", async (c) => {
251
- const { path } = z3.object({ path: z3.string().min(1) }).parse(await c.req.json());
375
+ const { path } = z4.object({ path: z4.string().min(1) }).parse(await c.req.json());
252
376
  return c.json({ projects: await deps.projects.add(path) });
253
377
  });
254
378
  app.delete("/", async (c) => {
255
- const { path } = z3.object({ path: z3.string().min(1) }).parse(await c.req.json());
379
+ const { path } = z4.object({ path: z4.string().min(1) }).parse(await c.req.json());
256
380
  return c.json({ projects: await deps.projects.remove(path) });
257
381
  });
258
382
  return app;
259
383
  }
260
384
 
261
385
  // src/server/routes/memory.ts
262
- import { Hono as Hono4 } from "hono";
386
+ import { Hono as Hono5 } from "hono";
263
387
  function memoryRoutes(deps) {
264
- const app = new Hono4();
388
+ const app = new Hono5();
265
389
  const ctx = (c) => {
266
390
  const q = ScopeQuerySchema.parse({
267
391
  scope: c.req.query("scope"),
@@ -275,104 +399,172 @@ function memoryRoutes(deps) {
275
399
  });
276
400
  app.put("/", async (c) => {
277
401
  const { q, paths } = ctx(c);
278
- const { content } = MemoryWriteSchema.parse(await c.req.json());
279
- await deps.memory.write(paths, q.scope, content);
402
+ const { content, baseMtime } = MemoryWriteSchema.parse(await c.req.json());
403
+ await deps.memory.write(paths, q.scope, content, baseMtime);
280
404
  return c.json({ ok: true });
281
405
  });
282
406
  return app;
283
407
  }
284
408
 
285
409
  // src/server/routes/memory-files.ts
286
- import { Hono as Hono5 } from "hono";
287
- import { z as z4 } from "zod";
288
- var QuerySchema = z4.object({ projectPath: z4.string().min(1) });
410
+ import { Hono as Hono6 } from "hono";
411
+ import { z as z5 } from "zod";
412
+ var ProviderQuerySchema = z5.object({
413
+ provider: z5.enum(["claude", "codex"]).default("claude")
414
+ });
415
+ var ClaudeQuerySchema = z5.object({ projectPath: z5.string().min(1) });
289
416
  function memoryFilesRoutes(deps) {
290
- const app = new Hono5();
417
+ const app = new Hono6();
291
418
  const ctx = (c) => {
292
- const { projectPath } = QuerySchema.parse({ projectPath: c.req.query("projectPath") });
293
- return { projectPath, paths: resolvePaths({ home: deps.home, projectPath }) };
419
+ const { provider } = ProviderQuerySchema.parse({
420
+ provider: c.req.query("provider") ?? "claude"
421
+ });
422
+ if (provider === "codex") {
423
+ return {
424
+ provider,
425
+ projectPath: void 0,
426
+ paths: resolvePaths({ home: deps.home, provider })
427
+ };
428
+ }
429
+ const { projectPath } = ClaudeQuerySchema.parse({
430
+ projectPath: c.req.query("projectPath")
431
+ });
432
+ return {
433
+ provider,
434
+ projectPath,
435
+ paths: resolvePaths({ home: deps.home, projectPath, provider })
436
+ };
294
437
  };
295
438
  app.get("/", async (c) => {
296
- const { projectPath, paths } = ctx(c);
297
- return c.json(await deps.memoryFiles.overview(paths, projectPath));
439
+ const { provider, projectPath, paths } = ctx(c);
440
+ return c.json(
441
+ provider === "codex" ? await deps.codexMemory.overview(paths) : await deps.memoryFiles.overview(paths, projectPath)
442
+ );
298
443
  });
299
444
  app.get("/:file", async (c) => {
300
- const { projectPath, paths } = ctx(c);
301
- return c.json(await deps.memoryFiles.read(paths, projectPath, c.req.param("file")));
445
+ const { provider, projectPath, paths } = ctx(c);
446
+ return c.json(
447
+ provider === "codex" ? await deps.codexMemory.read(paths, c.req.param("file")) : await deps.memoryFiles.read(paths, projectPath, c.req.param("file"))
448
+ );
302
449
  });
303
450
  return app;
304
451
  }
305
452
 
306
453
  // src/server/routes/sessions.ts
307
- import { Hono as Hono6 } from "hono";
308
- import { z as z5 } from "zod";
309
- var QuerySchema2 = z5.object({ projectPath: z5.string().min(1) });
454
+ import { Hono as Hono7 } from "hono";
455
+ import { z as z6 } from "zod";
456
+ var QuerySchema = z6.object({
457
+ provider: z6.enum(["claude", "codex"]).default("claude"),
458
+ projectPath: z6.string().min(1)
459
+ });
310
460
  function sessionRoutes(deps) {
311
- const app = new Hono6();
461
+ const app = new Hono7();
312
462
  const ctx = (c) => {
313
- const { projectPath } = QuerySchema2.parse({ projectPath: c.req.query("projectPath") });
314
- return { projectPath, paths: resolvePaths({ home: deps.home, projectPath }) };
463
+ const { provider, projectPath } = QuerySchema.parse({
464
+ provider: c.req.query("provider") ?? "claude",
465
+ projectPath: c.req.query("projectPath")
466
+ });
467
+ return {
468
+ provider,
469
+ projectPath,
470
+ paths: resolvePaths({ home: deps.home, projectPath, provider })
471
+ };
315
472
  };
316
473
  app.get("/", async (c) => {
317
- const { projectPath, paths } = ctx(c);
318
- return c.json(await deps.sessions.list(paths, projectPath));
474
+ const { provider, projectPath, paths } = ctx(c);
475
+ return c.json(
476
+ provider === "codex" ? await deps.codexSessions.list(paths, projectPath) : await deps.sessions.list(paths, projectPath)
477
+ );
319
478
  });
320
479
  app.get("/:id", async (c) => {
321
- const { projectPath, paths } = ctx(c);
322
- return c.json(await deps.sessions.read(paths, projectPath, c.req.param("id")));
480
+ const { provider, projectPath, paths } = ctx(c);
481
+ return c.json(
482
+ provider === "codex" ? await deps.codexSessions.read(paths, projectPath, c.req.param("id")) : await deps.sessions.read(paths, projectPath, c.req.param("id"))
483
+ );
323
484
  });
324
485
  return app;
325
486
  }
326
487
 
327
488
  // src/server/routes/plugins.ts
328
- import { Hono as Hono7 } from "hono";
329
- import { z as z6 } from "zod";
330
- var EnabledBodySchema = z6.object({ enabled: z6.boolean() });
331
- var ComponentQuerySchema = z6.object({
332
- type: z6.enum(["skill", "command", "agent", "hook", "mcp"]),
333
- name: z6.string().min(1)
489
+ import { Hono as Hono8 } from "hono";
490
+ import { z as z7 } from "zod";
491
+ var ProviderQuerySchema2 = z7.object({
492
+ provider: z7.enum(["claude", "codex"]).default("claude")
493
+ });
494
+ var EnabledBodySchema = z7.object({
495
+ enabled: z7.boolean(),
496
+ baseMtime: z7.number().optional()
497
+ });
498
+ var ComponentQuerySchema = z7.object({
499
+ type: z7.enum(["skill", "command", "agent", "hook", "mcp"]),
500
+ name: z7.string().min(1)
334
501
  });
335
502
  function pluginRoutes(deps) {
336
- const app = new Hono7();
337
- const paths = () => resolvePaths({ home: deps.home });
338
- app.get("/", async (c) => c.json(await deps.plugins.list(paths())));
339
- app.get(
340
- "/:id",
341
- async (c) => c.json(await deps.plugins.detail(paths(), c.req.param("id")))
342
- );
503
+ const app = new Hono8();
504
+ const ctx = (c) => {
505
+ const { provider } = ProviderQuerySchema2.parse({
506
+ provider: c.req.query("provider") ?? "claude"
507
+ });
508
+ return { provider, paths: resolvePaths({ home: deps.home, provider }) };
509
+ };
510
+ app.get("/", async (c) => {
511
+ const { provider, paths } = ctx(c);
512
+ return c.json(
513
+ provider === "codex" ? await deps.codexPlugins.list(paths) : await deps.plugins.list(paths)
514
+ );
515
+ });
516
+ app.get("/:id", async (c) => {
517
+ const { provider, paths } = ctx(c);
518
+ return c.json(
519
+ provider === "codex" ? await deps.codexPlugins.detail(paths, c.req.param("id")) : await deps.plugins.detail(paths, c.req.param("id"))
520
+ );
521
+ });
343
522
  app.get("/:id/component", async (c) => {
523
+ const { provider, paths } = ctx(c);
344
524
  const q = ComponentQuerySchema.parse({
345
525
  type: c.req.query("type"),
346
526
  name: c.req.query("name")
347
527
  });
528
+ const svc = provider === "codex" ? deps.codexPlugins : deps.plugins;
348
529
  return c.json(
349
- await deps.plugins.readComponent(
350
- paths(),
351
- c.req.param("id"),
352
- q.type,
353
- q.name
354
- )
530
+ await svc.readComponent(paths, c.req.param("id"), q.type, q.name)
355
531
  );
356
532
  });
357
533
  app.patch("/:id", async (c) => {
358
- const { enabled } = EnabledBodySchema.parse(await c.req.json());
359
- await deps.plugins.setEnabled(paths(), c.req.param("id"), enabled);
360
- return c.json(await deps.plugins.list(paths()));
534
+ const { provider, paths } = ctx(c);
535
+ const { enabled, baseMtime } = EnabledBodySchema.parse(await c.req.json());
536
+ if (provider === "codex") {
537
+ await deps.codexPlugins.setEnabled(
538
+ paths,
539
+ c.req.param("id"),
540
+ enabled,
541
+ baseMtime
542
+ );
543
+ return c.json(await deps.codexPlugins.list(paths));
544
+ }
545
+ await deps.plugins.setEnabled(paths, c.req.param("id"), enabled);
546
+ return c.json(await deps.plugins.list(paths));
361
547
  });
362
548
  return app;
363
549
  }
364
550
 
365
551
  // src/server/routes/config-files.ts
366
- import { Hono as Hono8 } from "hono";
552
+ import { Hono as Hono9 } from "hono";
367
553
  function configFileRoutes(deps) {
368
- const app = new Hono8();
554
+ const app = new Hono9();
369
555
  const ctx = (c) => {
370
556
  const fileId = ConfigFileIdSchema.parse(c.req.param("file"));
371
- const q = ScopeQuerySchema.parse({
557
+ const q = ProviderScopeQuerySchema.parse({
558
+ provider: c.req.query("provider"),
372
559
  scope: c.req.query("scope"),
373
560
  projectPath: c.req.query("projectPath")
374
561
  });
375
- return { fileId, q, paths: resolvePaths({ home: deps.home, projectPath: q.projectPath }) };
562
+ McpProviderSchema.parse(q.provider);
563
+ return {
564
+ fileId,
565
+ q,
566
+ paths: resolvePaths({ home: deps.home, projectPath: q.projectPath, provider: q.provider })
567
+ };
376
568
  };
377
569
  app.get("/:file", async (c) => {
378
570
  const { fileId, q, paths } = ctx(c);
@@ -380,20 +572,23 @@ function configFileRoutes(deps) {
380
572
  });
381
573
  app.put("/:file", async (c) => {
382
574
  const { fileId, q, paths } = ctx(c);
383
- const { content } = ConfigFileWriteSchema.parse(await c.req.json());
384
- await deps.configFiles.write(paths, fileId, q.scope, content);
575
+ const { content, baseMtime } = ConfigFileWriteSchema.parse(await c.req.json());
576
+ await deps.configFiles.write(paths, fileId, q.scope, content, baseMtime);
385
577
  return c.json({ ok: true });
386
578
  });
387
579
  return app;
388
580
  }
389
581
 
390
582
  // src/server/routes/agent-commands.ts
391
- import { Hono as Hono9 } from "hono";
392
- import { z as z7 } from "zod";
393
- var CreateSchema = z7.object({ name: z7.string().min(1), content: z7.string() });
394
- var WriteSchema = z7.object({ content: z7.string() });
583
+ import { Hono as Hono10 } from "hono";
584
+ import { z as z8 } from "zod";
585
+ var CreateSchema = z8.object({ name: z8.string().min(1), content: z8.string() });
586
+ var WriteSchema = z8.object({
587
+ content: z8.string(),
588
+ baseMtime: z8.number().nullable().optional()
589
+ });
395
590
  function agentCommandRoutes(deps, kind) {
396
- const app = new Hono9();
591
+ const app = new Hono10();
397
592
  const ctx = (c) => {
398
593
  const q = ScopeQuerySchema.parse({
399
594
  scope: c.req.query("scope"),
@@ -417,8 +612,8 @@ function agentCommandRoutes(deps, kind) {
417
612
  });
418
613
  app.put("/:name", async (c) => {
419
614
  const { q, paths } = ctx(c);
420
- const { content } = WriteSchema.parse(await c.req.json());
421
- await deps.agentCommands.write(paths, kind, q.scope, c.req.param("name"), content);
615
+ const { content, baseMtime } = WriteSchema.parse(await c.req.json());
616
+ await deps.agentCommands.write(paths, kind, q.scope, c.req.param("name"), content, baseMtime);
422
617
  return c.json({ ok: true });
423
618
  });
424
619
  app.delete("/:name", async (c) => {
@@ -431,7 +626,7 @@ function agentCommandRoutes(deps, kind) {
431
626
 
432
627
  // src/server/app.ts
433
628
  function createApp(deps) {
434
- const app = new Hono10();
629
+ const app = new Hono11();
435
630
  app.onError((err, c) => {
436
631
  if (err instanceof ZodError) {
437
632
  return c.json({ error: "\u6821\u9A8C\u5931\u8D25", issues: err.issues }, 400);
@@ -442,10 +637,14 @@ function createApp(deps) {
442
637
  if (err instanceof InvalidJsonError) {
443
638
  return c.json({ error: err.message }, 400);
444
639
  }
640
+ if (err instanceof ConflictError) {
641
+ return c.json({ error: err.message, code: "conflict" }, 409);
642
+ }
445
643
  return c.json({ error: err.message }, 500);
446
644
  });
447
645
  app.route("/api/mcp", mcpRoutes(deps));
448
646
  app.route("/api/skills", skillRoutes(deps));
647
+ app.route("/api/shared-skills", sharedSkillRoutes(deps));
449
648
  app.route("/api/projects", projectRoutes(deps));
450
649
  app.route("/api/memory", memoryRoutes(deps));
451
650
  app.route("/api/memory-files", memoryFilesRoutes(deps));
@@ -459,8 +658,12 @@ function createApp(deps) {
459
658
  }
460
659
 
461
660
  export {
661
+ ConfigCorruptError,
462
662
  InvalidJsonError,
463
663
  ConfigStore,
664
+ ConflictError,
665
+ currentMtime,
666
+ assertNoConflict,
464
667
  SkillFrontmatterSchema,
465
668
  createApp
466
669
  };