168 lines
5.7 KiB
JavaScript
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}`;
|
|
},
|
|
}),
|
|
},
|
|
};
|
|
};
|