From 94143a1f515a15e59413ff7271f572a9d76c4a2d Mon Sep 17 00:00:00 2001 From: root Date: Mon, 12 Oct 2026 14:06:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20companion=20=E6=94=B9=E7=94=A8=20$.fs?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=AD=A3=20hooks=20=E6=A8=A1=E7=B5=84?= =?UTF-8?q?=E8=BC=89=E5=85=A5=E5=A4=B1=E6=95=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node-fs.ts 引用 node:fs/promises,hooks 沙箱不允許,導致 /companion 指令未註冊。 改為以引擎的 $.fs 包成 Fs adapter;rename 改為選用,無 rename 時直接覆寫。 版本號調整為 0.0.1。 Co-Authored-By: Claude Sonnet 5.5 --- plugins/companion/.claude-plugin/plugin.json | 2 +- .../companion/hooks/companion/engine-fs.ts | 26 +++++++++++++++++++ plugins/companion/hooks/companion/fs.ts | 4 ++- plugins/companion/hooks/companion/node-fs.ts | 26 ------------------- plugins/companion/hooks/register.tsx | 4 +-- 5 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 plugins/companion/hooks/companion/engine-fs.ts delete mode 100644 plugins/companion/hooks/companion/node-fs.ts diff --git a/plugins/companion/.claude-plugin/plugin.json b/plugins/companion/.claude-plugin/plugin.json index 9f378db..322856c 100644 --- a/plugins/companion/.claude-plugin/plugin.json +++ b/plugins/companion/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "companion", - "version": "0.1.0", + "version": "0.0.1", "description": "小說角色陪伴:人設注入、語氣、心情與好感、互動記憶、主動關心與待辦,並可用 ingest skill 解析小說建立角色", "license": "MIT", "author": { diff --git a/plugins/companion/hooks/companion/engine-fs.ts b/plugins/companion/hooks/companion/engine-fs.ts new file mode 100644 index 0000000..48335bb --- /dev/null +++ b/plugins/companion/hooks/companion/engine-fs.ts @@ -0,0 +1,26 @@ +import type { Fs } from './fs' + +// hooks 沙箱沒有 node:fs,改用引擎的 $.fs;它沒有 rename,所以不提供(writeAtomic 會直接覆寫) +type EngineFs = { + read(path: string): Promise + write(path: string, text: string): Promise + list(path?: string): Promise<{ name: string }[]> +} + +export const engineFs = (fs: EngineFs): Fs => ({ + async read(path) { + try { + return await fs.read(path) + } catch { + return null + } + }, + write: (path, data) => fs.write(path, data), + async list(dir) { + try { + return (await fs.list(dir)).map(e => e.name).filter(n => !n.endsWith('.tmp')).sort() + } catch { + return [] + } + }, +}) diff --git a/plugins/companion/hooks/companion/fs.ts b/plugins/companion/hooks/companion/fs.ts index fc277dc..061dcfb 100644 --- a/plugins/companion/hooks/companion/fs.ts +++ b/plugins/companion/hooks/companion/fs.ts @@ -2,7 +2,8 @@ export interface Fs { read(path: string): Promise write(path: string, data: string): Promise - rename(from: string, to: string): Promise + /** 沒有 rename 的環境(hooks 沙箱)會直接覆寫 */ + rename?(from: string, to: string): Promise list(dir: string): Promise } @@ -10,6 +11,7 @@ export type Clock = { now(): number } // 先寫暫存檔再 rename,當機時不會留下寫到一半的檔案 export async function writeAtomic(fs: Fs, path: string, data: string): Promise { + if (!fs.rename) return fs.write(path, data) const tmp = `${path}.tmp` await fs.write(tmp, data) await fs.rename(tmp, path) diff --git a/plugins/companion/hooks/companion/node-fs.ts b/plugins/companion/hooks/companion/node-fs.ts deleted file mode 100644 index a06a9a2..0000000 --- a/plugins/companion/hooks/companion/node-fs.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' - -import type { Fs } from './fs' - -export const nodeFs: Fs = { - async read(path) { - try { - return await readFile(path, 'utf8') - } catch { - return null - } - }, - async write(path, data) { - await mkdir(dirname(path), { recursive: true }) - await writeFile(path, data, 'utf8') - }, - rename, - async list(dir) { - try { - return (await readdir(dir)).filter(n => !n.endsWith('.tmp')).sort() - } catch { - return [] - } - }, -} diff --git a/plugins/companion/hooks/register.tsx b/plugins/companion/hooks/register.tsx index 67b421e..316b545 100644 --- a/plugins/companion/hooks/register.tsx +++ b/plugins/companion/hooks/register.tsx @@ -2,7 +2,7 @@ import type { Register } from 'claude-code' import { fitCompanion } from './companion/band' import { createCompanion } from './companion/core' -import { nodeFs } from './companion/node-fs' +import { engineFs } from './companion/engine-fs' let companion: ReturnType | undefined @@ -14,7 +14,7 @@ export const register: Register = on => { argumentHint: 'status | switch <角色> | off | setup', }) const home = await $.env.get('HOME') - companion = createCompanion({ fs: nodeFs, clock: { now: Date.now }, root: `${home ?? ''}/.companion` }) + companion = createCompanion({ fs: engineFs({ read: p => $.fs.read(p), write: (p, t) => $.fs.write(p, t), list: p => $.fs.list(p) }), clock: { now: Date.now }, root: `${home ?? ''}/.companion` }) const first = await companion.ensureActivated() if (first.first) $.ui.toast(`已啟用 ${first.name}。用 /companion off 可關閉`) -- 2.53.0