mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-04 08:11:14 +08:00
74 lines
3.8 KiB
TypeScript
74 lines
3.8 KiB
TypeScript
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
export const DEFAULT_PORT = 17371;
|
|
export const CONFIG_DIR = path.join(os.homedir(), ".infinite-canvas");
|
|
export const CONFIG_FILE = path.join(CONFIG_DIR, "canvas-agent.json");
|
|
export const VERSION = readPackageVersion();
|
|
export const AGENT_PROMPT = "你正在帮助用户操作 Infinite Canvas 网页画布。需要改动画布时优先使用已配置的 infinite-canvas MCP 工具:先 canvas_get_state 读取当前画布,再根据任务使用 canvas_create_text_node、canvas_generate_text、canvas_generate_image、canvas_generate_video、canvas_generate_audio、canvas_create_generation_flow、canvas_create_config_node、canvas_run_generation、canvas_update_node、canvas_connect_nodes 等通用工具;复杂批量改动再用 canvas_apply_ops,删除连线可用 delete_connections。需要生成内容时直接调用对应生成工具,不要绑定特定业务场景。不要模拟鼠标点击,不要要求用户手动复制 JSON。";
|
|
|
|
export type CanvasWorkspaceConfig = { workspacePath: string; activeThreadId?: string; pinnedThreadIds?: string[] };
|
|
export type CanvasAgentConfig = { url: string; token: string; origins?: string[]; canvases?: Record<string, CanvasWorkspaceConfig> };
|
|
|
|
export function loadConfig(create = false): CanvasAgentConfig {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8")) as CanvasAgentConfig;
|
|
} catch {
|
|
const config = { url: `http://127.0.0.1:${Number(process.env.PORT) || DEFAULT_PORT}`, token: crypto.randomBytes(18).toString("hex") };
|
|
if (create) saveConfig(config);
|
|
return config;
|
|
}
|
|
}
|
|
|
|
export function saveConfig(config: CanvasAgentConfig) {
|
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
}
|
|
|
|
export function ensureCanvasWorkspace(config: CanvasAgentConfig, canvasId: string) {
|
|
const id = safeSegment(canvasId || "default");
|
|
config.canvases ||= {};
|
|
const current = config.canvases[id];
|
|
if (current?.workspacePath) {
|
|
fs.mkdirSync(resolveWorkspacePath(current.workspacePath), { recursive: true });
|
|
return { canvasId: id, ...current, workspacePath: resolveWorkspacePath(current.workspacePath) };
|
|
}
|
|
const workspacePath = path.join(CONFIG_DIR, "codex-workspaces", id);
|
|
config.canvases[id] = { workspacePath };
|
|
fs.mkdirSync(workspacePath, { recursive: true });
|
|
saveConfig(config);
|
|
return { canvasId: id, workspacePath };
|
|
}
|
|
|
|
export function updateCanvasWorkspace(config: CanvasAgentConfig, canvasId: string, patch: Partial<CanvasWorkspaceConfig>) {
|
|
const current = ensureCanvasWorkspace(config, canvasId);
|
|
const workspacePath = patch.workspacePath ? resolveWorkspacePath(patch.workspacePath) : current.workspacePath;
|
|
const next = { ...current, ...patch, workspacePath };
|
|
config.canvases ||= {};
|
|
config.canvases[current.canvasId] = { workspacePath: next.workspacePath, activeThreadId: next.activeThreadId, pinnedThreadIds: next.pinnedThreadIds };
|
|
fs.mkdirSync(workspacePath, { recursive: true });
|
|
saveConfig(config);
|
|
return { canvasId: current.canvasId, ...config.canvases[current.canvasId] };
|
|
}
|
|
|
|
function resolveWorkspacePath(value: string) {
|
|
if (value === "~") return os.homedir();
|
|
if (value.startsWith("~/")) return path.join(os.homedir(), value.slice(2));
|
|
return path.resolve(value);
|
|
}
|
|
|
|
function safeSegment(value: string) {
|
|
return value.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 120) || "default";
|
|
}
|
|
|
|
function readPackageVersion() {
|
|
try {
|
|
const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { version?: string };
|
|
return pkg.version || "0.0.0";
|
|
} catch {
|
|
return "0.0.0";
|
|
}
|
|
}
|