feat: 实现 Workspace 对话持久化
This commit is contained in:
parent
c2b10ea5c0
commit
2f7d5b2e39
97
src/extensions/shared/workspace/index.ts
Normal file
97
src/extensions/shared/workspace/index.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { createHash } from "node:crypto";
|
||||||
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
|
import { homedir } from "node:os";
|
||||||
|
import { join, resolve } from "node:path";
|
||||||
|
|
||||||
|
import type { Extension } from "../../../kernel";
|
||||||
|
import { ExtensionId, Hook } from "../../catalog";
|
||||||
|
|
||||||
|
export type MessageRole = "system" | "user" | "assistant";
|
||||||
|
|
||||||
|
export interface ChatMessage {
|
||||||
|
role: MessageRole;
|
||||||
|
content: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkspaceService {
|
||||||
|
home: string;
|
||||||
|
projectPath: string;
|
||||||
|
workspaceId: string;
|
||||||
|
conversationId: string;
|
||||||
|
messages(): Promise<ChatMessage[]>;
|
||||||
|
append(role: MessageRole, content: string): Promise<ChatMessage>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkspaceOptions {
|
||||||
|
home?: string;
|
||||||
|
projectPath?: string;
|
||||||
|
conversationId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createWorkspaceExtension(options: WorkspaceOptions = {}): Extension {
|
||||||
|
const home = resolve(
|
||||||
|
options.home ?? process.env.LLM_TO_AGENT_HOME ?? join(homedir(), ".llm-to-agent"),
|
||||||
|
);
|
||||||
|
const projectPath = resolve(options.projectPath ?? process.cwd());
|
||||||
|
const workspaceId = createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
|
||||||
|
const conversationId = options.conversationId ?? "default";
|
||||||
|
const workspaceDirectory = join(home, "workspaces", workspaceId);
|
||||||
|
const conversationDirectory = join(workspaceDirectory, "conversations", conversationId);
|
||||||
|
const messagesFile = join(conversationDirectory, "messages.jsonl");
|
||||||
|
|
||||||
|
const workspace: WorkspaceService = {
|
||||||
|
home,
|
||||||
|
projectPath,
|
||||||
|
workspaceId,
|
||||||
|
conversationId,
|
||||||
|
|
||||||
|
async messages() {
|
||||||
|
try {
|
||||||
|
const content = await readFile(messagesFile, "utf8");
|
||||||
|
return content
|
||||||
|
.split("\n")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((line) => JSON.parse(line) as ChatMessage);
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async append(role, content) {
|
||||||
|
const message = {
|
||||||
|
role,
|
||||||
|
content,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await appendFile(messagesFile, `${JSON.stringify(message)}\n`, "utf8");
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: ExtensionId.Workspace,
|
||||||
|
|
||||||
|
async setup(context) {
|
||||||
|
await mkdir(conversationDirectory, { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
join(workspaceDirectory, "workspace.json"),
|
||||||
|
`${JSON.stringify(
|
||||||
|
{
|
||||||
|
id: workspaceId,
|
||||||
|
kind: "linked",
|
||||||
|
projectPath,
|
||||||
|
activeConversationId: conversationId,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}\n`,
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
context.add(Hook.Workspace, workspace);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user