fix: companion 改用 $.fs,修正 /companion 無法載入 #13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "companion",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"description": "小說角色陪伴:人設注入、語氣、心情與好感、互動記憶、主動關心與待辦,並可用 ingest skill 解析小說建立角色",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { Fs } from './fs'
|
||||
|
||||
// hooks 沙箱沒有 node:fs,改用引擎的 $.fs;它沒有 rename,所以不提供(writeAtomic 會直接覆寫)
|
||||
type EngineFs = {
|
||||
read(path: string): Promise<string>
|
||||
write(path: string, text: string): Promise<void>
|
||||
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 []
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -2,7 +2,8 @@
|
||||
export interface Fs {
|
||||
read(path: string): Promise<string | null>
|
||||
write(path: string, data: string): Promise<void>
|
||||
rename(from: string, to: string): Promise<void>
|
||||
/** 沒有 rename 的環境(hooks 沙箱)會直接覆寫 */
|
||||
rename?(from: string, to: string): Promise<void>
|
||||
list(dir: string): Promise<string[]>
|
||||
}
|
||||
|
||||
@@ -10,6 +11,7 @@ export type Clock = { now(): number }
|
||||
|
||||
// 先寫暫存檔再 rename,當機時不會留下寫到一半的檔案
|
||||
export async function writeAtomic(fs: Fs, path: string, data: string): Promise<void> {
|
||||
if (!fs.rename) return fs.write(path, data)
|
||||
const tmp = `${path}.tmp`
|
||||
await fs.write(tmp, data)
|
||||
await fs.rename(tmp, path)
|
||||
|
||||
@@ -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 []
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -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<typeof createCompanion> | 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 可關閉`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user