168 lines
5.7 KiB
JavaScript

import { tool } from "@opencode-ai/plugin/tool";
import https from "node:https";
import fs from "node:fs";
import path from "node:path";
const NC_URL = "https://nc.hibbhome.com";
const NC_USER = "opencode_memgpt";
const NC_PASS = "ioH2o-QnQJx-8z7Dx-edPyx-pmxLA";
const DAV_BASE = `${NC_URL}/remote.php/dav/files/${NC_USER}`;
function authHeader() {
return "Basic " + Buffer.from(`${NC_USER}:${NC_PASS}`).toString("base64");
}
function davRequest(method, remotePath, body = null) {
return new Promise((resolve, reject) => {
const url = new URL(`${DAV_BASE}${remotePath}`);
const opts = {
hostname: url.hostname,
port: 443,
path: url.pathname,
method,
headers: {
Authorization: authHeader(),
},
};
if (body) {
opts.headers["Content-Length"] = Buffer.byteLength(body);
}
const req = https.request(opts, (res) => {
const chunks = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () => {
resolve({
status: res.statusCode,
headers: res.headers,
body: Buffer.concat(chunks),
});
});
});
req.on("error", reject);
if (body) req.write(body);
req.end();
});
}
export const NextcloudPlugin = async (_ctx) => {
return {
tool: {
nextcloud_upload: tool({
description: "Upload a local file to NextCloud",
args: {
local_path: tool.schema.string().describe("Local file path to upload"),
remote_path: tool.schema.string().describe("Remote path on NextCloud (e.g., /Documents/file.txt)"),
},
async execute(args) {
const content = fs.readFileSync(args.local_path);
const res = davRequest("PUT", args.remote_path, content);
return (await res).status === 201 || (await res).status === 204
? `Uploaded ${args.local_path} to ${args.remote_path}`
: `Upload failed with status ${(await res).status}`;
},
}),
nextcloud_download: tool({
description: "Download a file from NextCloud to local disk",
args: {
remote_path: tool.schema.string().describe("Remote path on NextCloud"),
local_path: tool.schema.string().describe("Local file path to save to"),
},
async execute(args) {
const res = await davRequest("GET", args.remote_path);
if (res.status === 200) {
const dir = path.dirname(args.local_path);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(args.local_path, res.body);
return `Downloaded ${args.remote_path} to ${args.local_path}`;
}
return `Download failed with status ${res.status}`;
},
}),
nextcloud_list: tool({
description: "List files in a NextCloud directory",
args: {
remote_path: tool.schema.string().describe("Remote directory path on NextCloud (e.g., /Documents)"),
},
async execute(args) {
const body = `<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:">
<D:prop><D:displayname/><D:getcontentlength/><D:getlastmodified/><D:resourcetype/></D:prop>
</D:propfind>`;
const res = await davRequest("PROPFIND", args.remote_path, body);
if (res.status === 207) {
const xml = res.body.toString();
const items = [];
const regex = /<d:displayname>(.*?)<\/d:displayname>/g;
let match;
while ((match = regex.exec(xml)) !== null) {
if (match[1]) items.push(match[1]);
}
return items.length > 0 ? items.join("\n") : "Empty directory";
}
return `List failed with status ${res.status}`;
},
}),
nextcloud_mkdir: tool({
description: "Create a directory on NextCloud",
args: {
remote_path: tool.schema.string().describe("Remote directory path to create"),
},
async execute(args) {
const res = await davRequest("MKCOL", args.remote_path);
return res.status === 201
? `Created directory ${args.remote_path}`
: `mkdir failed with status ${res.status}`;
},
}),
nextcloud_delete: tool({
description: "Delete a file or directory on NextCloud",
args: {
remote_path: tool.schema.string().describe("Remote path to delete"),
},
async execute(args) {
const res = await davRequest("DELETE", args.remote_path);
return res.status === 204
? `Deleted ${args.remote_path}`
: `Delete failed with status ${res.status}`;
},
}),
nextcloud_copy: tool({
description: "Copy a file within NextCloud",
args: {
source_path: tool.schema.string().describe("Source path on NextCloud"),
dest_path: tool.schema.string().describe("Destination path on NextCloud"),
},
async execute(args) {
const url = new URL(`${DAV_BASE}${args.source_path}`);
const opts = {
hostname: url.hostname,
port: 443,
path: url.pathname,
method: "COPY",
headers: {
Authorization: authHeader(),
Destination: `${DAV_BASE}${args.dest_path}`,
},
};
const res = await new Promise((resolve, reject) => {
const req = https.request(opts, (r) => {
r.on("data", () => {});
r.on("end", () => resolve(r.statusCode));
});
req.on("error", reject);
req.end();
});
return res === 201 || res === 204
? `Copied ${args.source_path} to ${args.dest_path}`
: `Copy failed with status ${res}`;
},
}),
},
};
};