ofiere-openclaw-plugin 4.14.0 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/tools.ts +66 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM - 11 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, and space file management",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
package/src/tools.ts
CHANGED
|
@@ -3232,6 +3232,23 @@ function registerFileOps(
|
|
|
3232
3232
|
});
|
|
3233
3233
|
}
|
|
3234
3234
|
|
|
3235
|
+
// ── File Ops helpers ─────────────────────────────────────────────────────────
|
|
3236
|
+
|
|
3237
|
+
/**
|
|
3238
|
+
* Sanitize a filename for safe use as a Supabase storage object key.
|
|
3239
|
+
* Strips characters that break storage APIs (parens, em-dashes, exclamation marks, etc.)
|
|
3240
|
+
* while preserving extension and readability. The original filename is kept in DB metadata.
|
|
3241
|
+
*/
|
|
3242
|
+
function sanitizeStorageFileName(fileName: string): string {
|
|
3243
|
+
// Replace spaces with hyphens, strip anything not alphanumeric, hyphen, underscore, or dot
|
|
3244
|
+
return fileName
|
|
3245
|
+
.replace(/\s+/g, "-")
|
|
3246
|
+
.replace(/[^a-zA-Z0-9._-]/g, "")
|
|
3247
|
+
.replace(/-{2,}/g, "-") // collapse multiple hyphens
|
|
3248
|
+
.replace(/^-+|-+$/g, "") // trim leading/trailing hyphens
|
|
3249
|
+
|| "file"; // fallback if everything was stripped
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3235
3252
|
// ── File Ops handlers ────────────────────────────────────────────────────────
|
|
3236
3253
|
|
|
3237
3254
|
async function handleListFiles(
|
|
@@ -3339,8 +3356,9 @@ async function handleCreateTextFile(
|
|
|
3339
3356
|
};
|
|
3340
3357
|
const mimeType = mimeMap[ext] || "text/plain";
|
|
3341
3358
|
|
|
3342
|
-
// Upload to storage
|
|
3343
|
-
const
|
|
3359
|
+
// Upload to storage — sanitize filename for storage key safety
|
|
3360
|
+
const safeFileName = sanitizeStorageFileName(fileName);
|
|
3361
|
+
const storagePath = `${userId}/${spaceId}/${Date.now()}-${safeFileName}`;
|
|
3344
3362
|
const blob = new Blob([content], { type: mimeType });
|
|
3345
3363
|
const buffer = await blob.arrayBuffer();
|
|
3346
3364
|
|
|
@@ -3388,8 +3406,32 @@ async function handleUploadFile(
|
|
|
3388
3406
|
const bytes = new Uint8Array(binaryStr.length);
|
|
3389
3407
|
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
3390
3408
|
|
|
3391
|
-
|
|
3392
|
-
|
|
3409
|
+
// Auto-detect MIME from extension if not explicitly provided
|
|
3410
|
+
let mimeType = params.file_type as string;
|
|
3411
|
+
if (!mimeType) {
|
|
3412
|
+
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
3413
|
+
const binaryMimeMap: Record<string, string> = {
|
|
3414
|
+
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif",
|
|
3415
|
+
webp: "image/webp", bmp: "image/bmp", ico: "image/x-icon",
|
|
3416
|
+
svg: "image/svg+xml", avif: "image/avif",
|
|
3417
|
+
pdf: "application/pdf", zip: "application/zip", gz: "application/gzip",
|
|
3418
|
+
tar: "application/x-tar", "7z": "application/x-7z-compressed",
|
|
3419
|
+
mp3: "audio/mpeg", wav: "audio/wav", ogg: "audio/ogg", flac: "audio/flac",
|
|
3420
|
+
mp4: "video/mp4", webm: "video/webm", avi: "video/x-msvideo", mov: "video/quicktime",
|
|
3421
|
+
woff: "font/woff", woff2: "font/woff2", ttf: "font/ttf", otf: "font/otf",
|
|
3422
|
+
doc: "application/msword", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
3423
|
+
xls: "application/vnd.ms-excel", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
3424
|
+
ppt: "application/vnd.ms-powerpoint", pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
3425
|
+
md: "text/markdown", txt: "text/plain", json: "application/json",
|
|
3426
|
+
csv: "text/csv", html: "text/html", css: "text/css",
|
|
3427
|
+
js: "text/javascript", ts: "text/typescript", yaml: "text/yaml",
|
|
3428
|
+
yml: "text/yaml", xml: "text/xml",
|
|
3429
|
+
};
|
|
3430
|
+
mimeType = binaryMimeMap[ext] || "application/octet-stream";
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
const safeFileName = sanitizeStorageFileName(fileName);
|
|
3434
|
+
const storagePath = `${userId}/${spaceId}/${Date.now()}-${safeFileName}`;
|
|
3393
3435
|
|
|
3394
3436
|
const { error: uploadErr } = await supabase.storage
|
|
3395
3437
|
.from("space-files")
|
|
@@ -3463,13 +3505,16 @@ async function handleRenameFile(
|
|
|
3463
3505
|
if (!fileId) return err("Missing required field: file_id");
|
|
3464
3506
|
if (!newName) return err("Missing required field: new_name");
|
|
3465
3507
|
|
|
3466
|
-
const { error } = await supabase
|
|
3508
|
+
const { data, error } = await supabase
|
|
3467
3509
|
.from("pm_space_files")
|
|
3468
3510
|
.update({ file_name: newName, updated_at: new Date().toISOString() })
|
|
3469
3511
|
.eq("id", fileId)
|
|
3470
|
-
.eq("user_id", userId)
|
|
3512
|
+
.eq("user_id", userId)
|
|
3513
|
+
.select("id")
|
|
3514
|
+
.maybeSingle();
|
|
3471
3515
|
|
|
3472
3516
|
if (error) return err(error.message);
|
|
3517
|
+
if (!data) return err(`File not found: ${fileId}`);
|
|
3473
3518
|
return ok({ message: `File renamed to "${newName}"` });
|
|
3474
3519
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
3475
3520
|
}
|
|
@@ -3483,13 +3528,16 @@ async function handleRenameSpaceFolder(
|
|
|
3483
3528
|
if (!folderId) return err("Missing required field: folder_id");
|
|
3484
3529
|
if (!newName) return err("Missing required field: new_name");
|
|
3485
3530
|
|
|
3486
|
-
const { error } = await supabase
|
|
3531
|
+
const { data, error } = await supabase
|
|
3487
3532
|
.from("pm_space_folders")
|
|
3488
3533
|
.update({ name: newName, updated_at: new Date().toISOString() })
|
|
3489
3534
|
.eq("id", folderId)
|
|
3490
|
-
.eq("user_id", userId)
|
|
3535
|
+
.eq("user_id", userId)
|
|
3536
|
+
.select("id")
|
|
3537
|
+
.maybeSingle();
|
|
3491
3538
|
|
|
3492
3539
|
if (error) return err(error.message);
|
|
3540
|
+
if (!data) return err(`Folder not found: ${folderId}`);
|
|
3493
3541
|
return ok({ message: `Folder renamed to "${newName}"` });
|
|
3494
3542
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
3495
3543
|
}
|
|
@@ -3505,13 +3553,16 @@ async function handleMoveFile(
|
|
|
3505
3553
|
? (params.target_folder_id as string | null)
|
|
3506
3554
|
: null;
|
|
3507
3555
|
|
|
3508
|
-
const { error } = await supabase
|
|
3556
|
+
const { data, error } = await supabase
|
|
3509
3557
|
.from("pm_space_files")
|
|
3510
3558
|
.update({ folder_id: targetFolderId, updated_at: new Date().toISOString() })
|
|
3511
3559
|
.eq("id", fileId)
|
|
3512
|
-
.eq("user_id", userId)
|
|
3560
|
+
.eq("user_id", userId)
|
|
3561
|
+
.select("id")
|
|
3562
|
+
.maybeSingle();
|
|
3513
3563
|
|
|
3514
3564
|
if (error) return err(error.message);
|
|
3565
|
+
if (!data) return err(`File not found: ${fileId}`);
|
|
3515
3566
|
return ok({ message: `File moved to ${targetFolderId || "root"}` });
|
|
3516
3567
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
3517
3568
|
}
|
|
@@ -3527,13 +3578,16 @@ async function handleMoveSpaceFolder(
|
|
|
3527
3578
|
? (params.target_parent_id as string | null)
|
|
3528
3579
|
: null;
|
|
3529
3580
|
|
|
3530
|
-
const { error } = await supabase
|
|
3581
|
+
const { data, error } = await supabase
|
|
3531
3582
|
.from("pm_space_folders")
|
|
3532
3583
|
.update({ parent_folder_id: targetParentId, updated_at: new Date().toISOString() })
|
|
3533
3584
|
.eq("id", folderId)
|
|
3534
|
-
.eq("user_id", userId)
|
|
3585
|
+
.eq("user_id", userId)
|
|
3586
|
+
.select("id")
|
|
3587
|
+
.maybeSingle();
|
|
3535
3588
|
|
|
3536
3589
|
if (error) return err(error.message);
|
|
3590
|
+
if (!data) return err(`Folder not found: ${folderId}`);
|
|
3537
3591
|
return ok({ message: `Folder moved to ${targetParentId || "root"}` });
|
|
3538
3592
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
3539
3593
|
}
|