@ystemsrx/cfshare 0.1.1 → 0.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ystemsrx/cfshare",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin to safely expose local ports/files via Cloudflare Quick Tunnel",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@ Expose local services, ports, files, or directories as temporary public `https:/
22
22
 
23
23
  **This tool is useful when you need to:**
24
24
 
25
- 1. Share files;
25
+ 1. Share or send files;
26
26
  2. Present web pages, services, lengthy Markdown content, PDFs, images, audio, video, and other media.
27
27
 
28
28
  ---
package/src/manager.ts CHANGED
@@ -282,7 +282,22 @@ function normalizeWorkspaceRelativePath(input: string | undefined): string | und
282
282
  }
283
283
 
284
284
  function sanitizeFilename(input: string): string {
285
- return input.replace(/[^a-zA-Z0-9._-]/g, "_").replace(/_+/g, "_");
285
+ // Keep Unicode to preserve original filenames, but replace characters that are
286
+ // invalid or problematic across common filesystems (Windows in particular).
287
+ // We only sanitize a single path segment (basename), not a full path.
288
+ //
289
+ // Reference set:
290
+ // - ASCII control chars: 0x00-0x1F and 0x7F
291
+ // - Windows reserved: <>:"/\\|?*
292
+ // - Trailing dots/spaces are invalid on Windows
293
+ const cleaned = input
294
+ .replace(/[\u0000-\u001F\u007F]/g, "_")
295
+ .replace(/[<>:"/\\|?*]/g, "_")
296
+ .replace(/_+/g, "_")
297
+ .replace(/[. ]+$/g, "_");
298
+
299
+ const trimmed = cleaned.trim();
300
+ return trimmed || "item";
286
301
  }
287
302
 
288
303
  function ensureString(input: unknown): string | undefined {
@@ -1097,10 +1112,12 @@ export class CfshareManager {
1097
1112
  zip.outputStream.pipe(out);
1098
1113
 
1099
1114
  for (const relPath of files) {
1100
- if (relPath === path.basename(zipPath)) {
1115
+ // Zip entries should use "/" separators regardless of OS.
1116
+ const zipEntry = relPath.split(path.sep).join("/");
1117
+ if (zipEntry === path.basename(zipPath)) {
1101
1118
  continue;
1102
1119
  }
1103
- zip.addFile(path.join(workspaceDir, relPath), relPath);
1120
+ zip.addFile(path.join(workspaceDir, relPath), zipEntry);
1104
1121
  }
1105
1122
  zip.end();
1106
1123
  });
@@ -1316,6 +1333,7 @@ export class CfshareManager {
1316
1333
  res,
1317
1334
  session: params.session,
1318
1335
  filePath: zipBundle.zipPath,
1336
+ downloadName: "download.zip",
1319
1337
  presentation: "download",
1320
1338
  countAsDownload: true,
1321
1339
  });
@@ -1421,10 +1439,23 @@ export class CfshareManager {
1421
1439
 
1422
1440
  const sourceStat = await fs.stat(real);
1423
1441
  const baseName = sanitizeFilename(path.basename(real) || "item");
1424
- let target = path.join(workspaceDir, baseName);
1442
+ const makeCandidate = (n: number) => {
1443
+ if (n === 0) {
1444
+ return baseName;
1445
+ }
1446
+ // For directories, treat dots as part of the name (do not split extension).
1447
+ if (sourceStat.isDirectory()) {
1448
+ return `${baseName}_${n}`;
1449
+ }
1450
+ // For files, keep extension stable: "a.txt" -> "a_1.txt".
1451
+ const parsed = path.parse(baseName);
1452
+ return `${parsed.name || "item"}_${n}${parsed.ext || ""}`;
1453
+ };
1454
+
1455
+ let target = path.join(workspaceDir, makeCandidate(0));
1425
1456
  let seq = 1;
1426
1457
  while (await fileExists(target)) {
1427
- target = path.join(workspaceDir, `${baseName}_${seq}`);
1458
+ target = path.join(workspaceDir, makeCandidate(seq));
1428
1459
  seq += 1;
1429
1460
  }
1430
1461