minio: full nox module — client, tools, provisioner and events (ADR 0044/0045/0046)
Object store, an s3-bucket provider. Client ported with no npm deps: S3 data plane over fetch + SigV4 (node:crypto), scoped access keys via the mc CLI (the admin API needs an Argon2 payload node built-ins can't make — the honest port hal also used). Tools: list buckets/objects, bucket info, presigned url. The provisioner makes a bucket + scoped key per grant and emits module.minio.bucket.created/removed (the secret stays off the bus). Typechecks; manifest parses. (Trimmed the generated self-consuming ledger: a provider need not subscribe to its own emits.)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
// minio's tools — ported here from the shared sdk (novox/hq ADR 0044), importing minio's own client.
|
||||
// They return structured data; the mesh serves them through the sdk's tool harness. These are the
|
||||
// read/inspect operations useful to an operator; creating storage for a consumer is the provisioner's
|
||||
// job, not a tool's.
|
||||
|
||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||
import { MinioClient } from "../client.js";
|
||||
|
||||
export function getMinioTools(minio: MinioClient): ToolDefinition[] {
|
||||
return [
|
||||
{
|
||||
name: "minio_list_buckets",
|
||||
description: "List every S3 bucket in the object store, with creation dates.",
|
||||
input: {},
|
||||
run: async () => {
|
||||
const buckets = await minio.listBuckets();
|
||||
return {
|
||||
count: buckets.length,
|
||||
buckets: buckets.map((b) => ({ name: b.name, createdAt: b.creationDate?.toISOString() })),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minio_list_objects",
|
||||
description: "List objects in a bucket, optionally under a prefix.",
|
||||
input: {
|
||||
bucket: { type: "string", description: "the bucket name" },
|
||||
prefix: { type: "string", description: "only keys under this prefix (e.g. 'photos/')" },
|
||||
recursive: { type: "boolean", description: "descend into nested prefixes (default false)" },
|
||||
limit: { type: "number", description: "max keys to return (default 100)" },
|
||||
},
|
||||
run: async (args) => {
|
||||
const bucket = String(args.bucket);
|
||||
const objects = await minio.listObjects(
|
||||
bucket,
|
||||
args.prefix ? String(args.prefix) : "",
|
||||
Boolean(args.recursive),
|
||||
args.limit ? Number(args.limit) : 100,
|
||||
);
|
||||
return {
|
||||
bucket,
|
||||
count: objects.length,
|
||||
objects: objects.map((o) => ({ key: o.name, size: o.size, lastModified: o.lastModified.toISOString(), etag: o.etag })),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "minio_bucket_info",
|
||||
description: "Summary of one bucket: whether it exists, its region, and a sampled object count and size.",
|
||||
input: { bucket: { type: "string", description: "the bucket name" } },
|
||||
run: async (args) => minio.bucketInfo(String(args.bucket)),
|
||||
},
|
||||
{
|
||||
name: "minio_presigned_url",
|
||||
description: "A time-limited URL to download (GET) or upload (PUT) one object without credentials.",
|
||||
input: {
|
||||
bucket: { type: "string", description: "the bucket name" },
|
||||
object: { type: "string", description: "the object key" },
|
||||
method: { type: "string", description: "'GET' to download (default) or 'PUT' to upload" },
|
||||
expires: { type: "number", description: "seconds until the URL expires (default 86400 = 24h)" },
|
||||
},
|
||||
run: async (args) => {
|
||||
const method = String(args.method ?? "GET").toUpperCase() === "PUT" ? "PUT" : "GET";
|
||||
const expires = args.expires ? Number(args.expires) : 86400;
|
||||
const url = minio.presignedUrl(method, String(args.bucket), String(args.object), expires);
|
||||
return { method, bucket: String(args.bucket), object: String(args.object), expiresInSeconds: expires, url };
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// The tools exist only when the object store is configured and reachable; without it minio
|
||||
// contributes none rather than failing the whole tool runtime.
|
||||
registerModuleTools("minio", (env) => {
|
||||
try {
|
||||
return getMinioTools(MinioClient.fromEnv(env));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user