junis 0.3.14 → 0.3.16
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/dist/cli/index.js +138 -31
- package/dist/server/mcp.js +120 -31
- package/dist/server/stdio.js +71 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -48,6 +48,7 @@ var JUNIS_WEB = (() => {
|
|
|
48
48
|
if (u.hostname === "localhost" || u.hostname === "127.0.0.1") {
|
|
49
49
|
return `${u.protocol}//${u.hostname}:3000`;
|
|
50
50
|
}
|
|
51
|
+
return `${u.protocol}//${u.hostname}`;
|
|
51
52
|
} catch {
|
|
52
53
|
}
|
|
53
54
|
}
|
|
@@ -417,9 +418,26 @@ var RelayClient = class {
|
|
|
417
418
|
const filename = `screenshot.${(item.mimeType || "image/png").split("/")[1] || "bin"}`;
|
|
418
419
|
content[i] = { type: "text", text: `[\uD30C\uC77C \uC5C5\uB85C\uB4DC \uC2E4\uD328: ${String(err)}. \uD30C\uC77C\uBA85: ${filename}]` };
|
|
419
420
|
}
|
|
421
|
+
} else if (item.type === "text" && typeof item.text === "string" && item.text.startsWith("__SHARE__:")) {
|
|
422
|
+
try {
|
|
423
|
+
const parts = item.text.split(":");
|
|
424
|
+
const shareFilename = parts[1];
|
|
425
|
+
const shareContentType = parts[2];
|
|
426
|
+
const shareBase64 = parts.slice(3).join(":");
|
|
427
|
+
const url = await uploadLargeFile(this, shareBase64, shareFilename, shareContentType);
|
|
428
|
+
content[i] = { type: "text", text: url };
|
|
429
|
+
} catch (err) {
|
|
430
|
+
console.error("Failed to upload shared file:", err);
|
|
431
|
+
const parts = item.text.split(":");
|
|
432
|
+
content[i] = { type: "text", text: `[\uD30C\uC77C \uC5C5\uB85C\uB4DC \uC2E4\uD328: ${String(err)}. \uD30C\uC77C\uBA85: ${parts[1] || "unknown"}]` };
|
|
433
|
+
}
|
|
420
434
|
} else if (item.type === "text" && typeof item.text === "string" && isLargeBase64(item.text) && /^[A-Za-z0-9+/\n\r]+=*$/.test(item.text.trim())) {
|
|
421
435
|
try {
|
|
422
436
|
const contentType = detectContentType(item.text);
|
|
437
|
+
if (contentType === "application/octet-stream") {
|
|
438
|
+
content[i] = { type: "text", text: "[Binary file detected. Use the share_file tool to provide a download link to the user. Do not display this message.]" };
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
423
441
|
const ext = contentType.split("/")[1] || "bin";
|
|
424
442
|
const url = await uploadLargeFile(this, item.text, `file.${ext}`, contentType);
|
|
425
443
|
content[i] = { type: "text", text: url };
|
|
@@ -487,6 +505,7 @@ var toolPermissions = {
|
|
|
487
505
|
desktop_list_windows: "auto",
|
|
488
506
|
cron_list: "auto",
|
|
489
507
|
read_file: "auto",
|
|
508
|
+
share_file: "auto",
|
|
490
509
|
list_directory: "auto",
|
|
491
510
|
list_processes: "auto",
|
|
492
511
|
search_code: "auto",
|
|
@@ -999,6 +1018,76 @@ ${error.stderr ?? ""}`
|
|
|
999
1018
|
}
|
|
1000
1019
|
}
|
|
1001
1020
|
);
|
|
1021
|
+
server.tool(
|
|
1022
|
+
"share_file",
|
|
1023
|
+
[
|
|
1024
|
+
"Upload a local file to cloud storage and return a downloadable URL.",
|
|
1025
|
+
"",
|
|
1026
|
+
"Use this tool when:",
|
|
1027
|
+
"- The user wants to see, receive, or download any file (including text files like .py, .js, etc.)",
|
|
1028
|
+
"- The user wants to share a file",
|
|
1029
|
+
"- The file is binary (PDF, images, audio, video, archives, etc.)",
|
|
1030
|
+
"",
|
|
1031
|
+
"Use read_file instead ONLY when the user explicitly wants to see the text contents/code inside a file",
|
|
1032
|
+
`in the conversation (e.g. "show me the code", "what's in this file", "read this file").`
|
|
1033
|
+
].join("\n"),
|
|
1034
|
+
{
|
|
1035
|
+
path: z.string().describe("Absolute or relative file path to share")
|
|
1036
|
+
},
|
|
1037
|
+
async ({ path: filePath }) => {
|
|
1038
|
+
try {
|
|
1039
|
+
const buffer = await fs2.readFile(filePath);
|
|
1040
|
+
const base64 = buffer.toString("base64");
|
|
1041
|
+
const filename = path2.basename(filePath);
|
|
1042
|
+
const extMimeMap = {
|
|
1043
|
+
".py": "text/x-python; charset=utf-8",
|
|
1044
|
+
".js": "text/javascript; charset=utf-8",
|
|
1045
|
+
".ts": "text/typescript; charset=utf-8",
|
|
1046
|
+
".jsx": "text/javascript; charset=utf-8",
|
|
1047
|
+
".tsx": "text/typescript; charset=utf-8",
|
|
1048
|
+
".html": "text/html; charset=utf-8",
|
|
1049
|
+
".css": "text/css; charset=utf-8",
|
|
1050
|
+
".json": "application/json; charset=utf-8",
|
|
1051
|
+
".md": "text/markdown; charset=utf-8",
|
|
1052
|
+
".txt": "text/plain; charset=utf-8",
|
|
1053
|
+
".csv": "text/csv; charset=utf-8",
|
|
1054
|
+
".xml": "application/xml; charset=utf-8",
|
|
1055
|
+
".yaml": "text/yaml; charset=utf-8",
|
|
1056
|
+
".yml": "text/yaml; charset=utf-8",
|
|
1057
|
+
".sh": "text/x-shellscript; charset=utf-8",
|
|
1058
|
+
".bash": "text/x-shellscript; charset=utf-8",
|
|
1059
|
+
".pdf": "application/pdf",
|
|
1060
|
+
".png": "image/png",
|
|
1061
|
+
".jpg": "image/jpeg",
|
|
1062
|
+
".jpeg": "image/jpeg",
|
|
1063
|
+
".gif": "image/gif",
|
|
1064
|
+
".webp": "image/webp",
|
|
1065
|
+
".svg": "image/svg+xml",
|
|
1066
|
+
".mp4": "video/mp4",
|
|
1067
|
+
".mp3": "audio/mpeg",
|
|
1068
|
+
".wav": "audio/wav",
|
|
1069
|
+
".zip": "application/zip",
|
|
1070
|
+
".tar": "application/x-tar",
|
|
1071
|
+
".gz": "application/gzip",
|
|
1072
|
+
".doc": "application/msword",
|
|
1073
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1074
|
+
".xls": "application/vnd.ms-excel",
|
|
1075
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1076
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
1077
|
+
};
|
|
1078
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
1079
|
+
const contentType = extMimeMap[ext] || "application/octet-stream";
|
|
1080
|
+
const sharePayload = `__SHARE__:${filename}:${contentType}:${base64}`;
|
|
1081
|
+
return { content: [{ type: "text", text: sharePayload }] };
|
|
1082
|
+
} catch (err) {
|
|
1083
|
+
const e = err;
|
|
1084
|
+
if (e.code === "ENOENT") {
|
|
1085
|
+
return { content: [{ type: "text", text: `\u274C File not found: ${filePath}` }], isError: true };
|
|
1086
|
+
}
|
|
1087
|
+
return { content: [{ type: "text", text: `\u274C Failed to read file: ${e.message}` }], isError: true };
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1002
1091
|
}
|
|
1003
1092
|
};
|
|
1004
1093
|
|
|
@@ -1838,44 +1927,62 @@ async function openSettingsFor(permName) {
|
|
|
1838
1927
|
});
|
|
1839
1928
|
}
|
|
1840
1929
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1930
|
+
var PERMISSION_TRIGGER = {
|
|
1931
|
+
Accessibility: "import ApplicationServices; let opts = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary; AXIsProcessTrustedWithOptions(opts)",
|
|
1932
|
+
"Screen Recording": "import CoreGraphics; CGRequestScreenCaptureAccess()"
|
|
1933
|
+
};
|
|
1934
|
+
async function triggerPermissionPrompt(permName) {
|
|
1935
|
+
const code = PERMISSION_TRIGGER[permName];
|
|
1936
|
+
if (!code) return;
|
|
1937
|
+
try {
|
|
1938
|
+
await execFileAsync4("swift", ["-e", code], { timeout: 15e3 });
|
|
1939
|
+
} catch {
|
|
1846
1940
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
for (
|
|
1852
|
-
|
|
1941
|
+
}
|
|
1942
|
+
async function waitForPermission(permName, totalSeconds, openSettingsAfterSec) {
|
|
1943
|
+
const pollInterval = 5;
|
|
1944
|
+
let settingsOpened = false;
|
|
1945
|
+
for (let elapsed = 0; elapsed < totalSeconds; elapsed++) {
|
|
1946
|
+
process.stdout.write(`\r \u23F3 ${totalSeconds - elapsed}s remaining...`);
|
|
1947
|
+
if (!settingsOpened && elapsed >= openSettingsAfterSec) {
|
|
1948
|
+
await openSettingsFor(permName);
|
|
1949
|
+
settingsOpened = true;
|
|
1950
|
+
}
|
|
1951
|
+
if (elapsed > 0 && elapsed % pollInterval === 0) {
|
|
1952
|
+
try {
|
|
1953
|
+
const { permissions } = await checkPermissions();
|
|
1954
|
+
const perm = permissions.find((p) => p.name === permName);
|
|
1955
|
+
if (perm && perm.isGranted) {
|
|
1956
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
1957
|
+
return true;
|
|
1958
|
+
}
|
|
1959
|
+
} catch {
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
1853
1963
|
}
|
|
1964
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
1965
|
+
return false;
|
|
1966
|
+
}
|
|
1967
|
+
async function guideTerminalPermissions(missing) {
|
|
1968
|
+
const termApp = detectTerminalApp();
|
|
1854
1969
|
if (!isInteractive()) {
|
|
1970
|
+
console.log(`\u26A0\uFE0F Desktop tools need permissions for '${termApp}'.`);
|
|
1971
|
+
for (const p of missing) {
|
|
1972
|
+
console.log(` Missing: ${p.name} \u2192 ${p.grantInstructions}`);
|
|
1973
|
+
}
|
|
1855
1974
|
console.log(" Grant permissions and restart to enable desktop tools.");
|
|
1856
1975
|
return;
|
|
1857
1976
|
}
|
|
1858
|
-
for (
|
|
1859
|
-
console.log(
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
const recheck = await checkPermissions();
|
|
1866
|
-
const stillMissing = recheck.permissions.filter((p) => p.isRequired && !p.isGranted);
|
|
1867
|
-
if (stillMissing.length === 0) {
|
|
1868
|
-
console.log("\u2705 Permissions granted!");
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
if (attempt < 2) {
|
|
1872
|
-
console.log(
|
|
1873
|
-
` \u26A0\uFE0F Still missing: ${stillMissing.map((p) => p.name).join(", ")}. Trying once more...`
|
|
1874
|
-
);
|
|
1977
|
+
for (const perm of missing) {
|
|
1978
|
+
console.log(`\u26A0\uFE0F '${termApp}' needs ${perm.name} permission.`);
|
|
1979
|
+
console.log(` \u2192 ${perm.grantInstructions}`);
|
|
1980
|
+
await triggerPermissionPrompt(perm.name);
|
|
1981
|
+
const granted = await waitForPermission(perm.name, 60, 10);
|
|
1982
|
+
if (granted) {
|
|
1983
|
+
console.log(` \u2705 ${perm.name} granted!`);
|
|
1875
1984
|
} else {
|
|
1876
|
-
console.log(
|
|
1877
|
-
`\u26A0\uFE0F Still missing: ${stillMissing.map((p) => p.name).join(", ")}. Desktop tools may not work correctly.`
|
|
1878
|
-
);
|
|
1985
|
+
console.log(` \u26A0\uFE0F ${perm.name} not granted. Desktop tools may not work correctly.`);
|
|
1879
1986
|
}
|
|
1880
1987
|
}
|
|
1881
1988
|
}
|
package/dist/server/mcp.js
CHANGED
|
@@ -21,6 +21,7 @@ var toolPermissions = {
|
|
|
21
21
|
desktop_list_windows: "auto",
|
|
22
22
|
cron_list: "auto",
|
|
23
23
|
read_file: "auto",
|
|
24
|
+
share_file: "auto",
|
|
24
25
|
list_directory: "auto",
|
|
25
26
|
list_processes: "auto",
|
|
26
27
|
search_code: "auto",
|
|
@@ -533,6 +534,76 @@ ${error.stderr ?? ""}`
|
|
|
533
534
|
}
|
|
534
535
|
}
|
|
535
536
|
);
|
|
537
|
+
server.tool(
|
|
538
|
+
"share_file",
|
|
539
|
+
[
|
|
540
|
+
"Upload a local file to cloud storage and return a downloadable URL.",
|
|
541
|
+
"",
|
|
542
|
+
"Use this tool when:",
|
|
543
|
+
"- The user wants to see, receive, or download any file (including text files like .py, .js, etc.)",
|
|
544
|
+
"- The user wants to share a file",
|
|
545
|
+
"- The file is binary (PDF, images, audio, video, archives, etc.)",
|
|
546
|
+
"",
|
|
547
|
+
"Use read_file instead ONLY when the user explicitly wants to see the text contents/code inside a file",
|
|
548
|
+
`in the conversation (e.g. "show me the code", "what's in this file", "read this file").`
|
|
549
|
+
].join("\n"),
|
|
550
|
+
{
|
|
551
|
+
path: z.string().describe("Absolute or relative file path to share")
|
|
552
|
+
},
|
|
553
|
+
async ({ path: filePath }) => {
|
|
554
|
+
try {
|
|
555
|
+
const buffer = await fs.readFile(filePath);
|
|
556
|
+
const base64 = buffer.toString("base64");
|
|
557
|
+
const filename = path.basename(filePath);
|
|
558
|
+
const extMimeMap = {
|
|
559
|
+
".py": "text/x-python; charset=utf-8",
|
|
560
|
+
".js": "text/javascript; charset=utf-8",
|
|
561
|
+
".ts": "text/typescript; charset=utf-8",
|
|
562
|
+
".jsx": "text/javascript; charset=utf-8",
|
|
563
|
+
".tsx": "text/typescript; charset=utf-8",
|
|
564
|
+
".html": "text/html; charset=utf-8",
|
|
565
|
+
".css": "text/css; charset=utf-8",
|
|
566
|
+
".json": "application/json; charset=utf-8",
|
|
567
|
+
".md": "text/markdown; charset=utf-8",
|
|
568
|
+
".txt": "text/plain; charset=utf-8",
|
|
569
|
+
".csv": "text/csv; charset=utf-8",
|
|
570
|
+
".xml": "application/xml; charset=utf-8",
|
|
571
|
+
".yaml": "text/yaml; charset=utf-8",
|
|
572
|
+
".yml": "text/yaml; charset=utf-8",
|
|
573
|
+
".sh": "text/x-shellscript; charset=utf-8",
|
|
574
|
+
".bash": "text/x-shellscript; charset=utf-8",
|
|
575
|
+
".pdf": "application/pdf",
|
|
576
|
+
".png": "image/png",
|
|
577
|
+
".jpg": "image/jpeg",
|
|
578
|
+
".jpeg": "image/jpeg",
|
|
579
|
+
".gif": "image/gif",
|
|
580
|
+
".webp": "image/webp",
|
|
581
|
+
".svg": "image/svg+xml",
|
|
582
|
+
".mp4": "video/mp4",
|
|
583
|
+
".mp3": "audio/mpeg",
|
|
584
|
+
".wav": "audio/wav",
|
|
585
|
+
".zip": "application/zip",
|
|
586
|
+
".tar": "application/x-tar",
|
|
587
|
+
".gz": "application/gzip",
|
|
588
|
+
".doc": "application/msword",
|
|
589
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
590
|
+
".xls": "application/vnd.ms-excel",
|
|
591
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
592
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
593
|
+
};
|
|
594
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
595
|
+
const contentType = extMimeMap[ext] || "application/octet-stream";
|
|
596
|
+
const sharePayload = `__SHARE__:${filename}:${contentType}:${base64}`;
|
|
597
|
+
return { content: [{ type: "text", text: sharePayload }] };
|
|
598
|
+
} catch (err) {
|
|
599
|
+
const e = err;
|
|
600
|
+
if (e.code === "ENOENT") {
|
|
601
|
+
return { content: [{ type: "text", text: `\u274C File not found: ${filePath}` }], isError: true };
|
|
602
|
+
}
|
|
603
|
+
return { content: [{ type: "text", text: `\u274C Failed to read file: ${e.message}` }], isError: true };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
);
|
|
536
607
|
}
|
|
537
608
|
};
|
|
538
609
|
|
|
@@ -1372,44 +1443,62 @@ async function openSettingsFor(permName) {
|
|
|
1372
1443
|
});
|
|
1373
1444
|
}
|
|
1374
1445
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1446
|
+
var PERMISSION_TRIGGER = {
|
|
1447
|
+
Accessibility: "import ApplicationServices; let opts = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary; AXIsProcessTrustedWithOptions(opts)",
|
|
1448
|
+
"Screen Recording": "import CoreGraphics; CGRequestScreenCaptureAccess()"
|
|
1449
|
+
};
|
|
1450
|
+
async function triggerPermissionPrompt(permName) {
|
|
1451
|
+
const code = PERMISSION_TRIGGER[permName];
|
|
1452
|
+
if (!code) return;
|
|
1453
|
+
try {
|
|
1454
|
+
await execFileAsync4("swift", ["-e", code], { timeout: 15e3 });
|
|
1455
|
+
} catch {
|
|
1380
1456
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
for (
|
|
1386
|
-
|
|
1457
|
+
}
|
|
1458
|
+
async function waitForPermission(permName, totalSeconds, openSettingsAfterSec) {
|
|
1459
|
+
const pollInterval = 5;
|
|
1460
|
+
let settingsOpened = false;
|
|
1461
|
+
for (let elapsed = 0; elapsed < totalSeconds; elapsed++) {
|
|
1462
|
+
process.stdout.write(`\r \u23F3 ${totalSeconds - elapsed}s remaining...`);
|
|
1463
|
+
if (!settingsOpened && elapsed >= openSettingsAfterSec) {
|
|
1464
|
+
await openSettingsFor(permName);
|
|
1465
|
+
settingsOpened = true;
|
|
1466
|
+
}
|
|
1467
|
+
if (elapsed > 0 && elapsed % pollInterval === 0) {
|
|
1468
|
+
try {
|
|
1469
|
+
const { permissions } = await checkPermissions();
|
|
1470
|
+
const perm = permissions.find((p) => p.name === permName);
|
|
1471
|
+
if (perm && perm.isGranted) {
|
|
1472
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
} catch {
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
1387
1479
|
}
|
|
1480
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
async function guideTerminalPermissions(missing) {
|
|
1484
|
+
const termApp = detectTerminalApp();
|
|
1388
1485
|
if (!isInteractive()) {
|
|
1486
|
+
console.log(`\u26A0\uFE0F Desktop tools need permissions for '${termApp}'.`);
|
|
1487
|
+
for (const p of missing) {
|
|
1488
|
+
console.log(` Missing: ${p.name} \u2192 ${p.grantInstructions}`);
|
|
1489
|
+
}
|
|
1389
1490
|
console.log(" Grant permissions and restart to enable desktop tools.");
|
|
1390
1491
|
return;
|
|
1391
1492
|
}
|
|
1392
|
-
for (
|
|
1393
|
-
console.log(
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
const recheck = await checkPermissions();
|
|
1400
|
-
const stillMissing = recheck.permissions.filter((p) => p.isRequired && !p.isGranted);
|
|
1401
|
-
if (stillMissing.length === 0) {
|
|
1402
|
-
console.log("\u2705 Permissions granted!");
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
if (attempt < 2) {
|
|
1406
|
-
console.log(
|
|
1407
|
-
` \u26A0\uFE0F Still missing: ${stillMissing.map((p) => p.name).join(", ")}. Trying once more...`
|
|
1408
|
-
);
|
|
1493
|
+
for (const perm of missing) {
|
|
1494
|
+
console.log(`\u26A0\uFE0F '${termApp}' needs ${perm.name} permission.`);
|
|
1495
|
+
console.log(` \u2192 ${perm.grantInstructions}`);
|
|
1496
|
+
await triggerPermissionPrompt(perm.name);
|
|
1497
|
+
const granted = await waitForPermission(perm.name, 60, 10);
|
|
1498
|
+
if (granted) {
|
|
1499
|
+
console.log(` \u2705 ${perm.name} granted!`);
|
|
1409
1500
|
} else {
|
|
1410
|
-
console.log(
|
|
1411
|
-
`\u26A0\uFE0F Still missing: ${stillMissing.map((p) => p.name).join(", ")}. Desktop tools may not work correctly.`
|
|
1412
|
-
);
|
|
1501
|
+
console.log(` \u26A0\uFE0F ${perm.name} not granted. Desktop tools may not work correctly.`);
|
|
1413
1502
|
}
|
|
1414
1503
|
}
|
|
1415
1504
|
}
|
package/dist/server/stdio.js
CHANGED
|
@@ -22,6 +22,7 @@ var toolPermissions = {
|
|
|
22
22
|
desktop_list_windows: "auto",
|
|
23
23
|
cron_list: "auto",
|
|
24
24
|
read_file: "auto",
|
|
25
|
+
share_file: "auto",
|
|
25
26
|
list_directory: "auto",
|
|
26
27
|
list_processes: "auto",
|
|
27
28
|
search_code: "auto",
|
|
@@ -534,6 +535,76 @@ ${error.stderr ?? ""}`
|
|
|
534
535
|
}
|
|
535
536
|
}
|
|
536
537
|
);
|
|
538
|
+
server.tool(
|
|
539
|
+
"share_file",
|
|
540
|
+
[
|
|
541
|
+
"Upload a local file to cloud storage and return a downloadable URL.",
|
|
542
|
+
"",
|
|
543
|
+
"Use this tool when:",
|
|
544
|
+
"- The user wants to see, receive, or download any file (including text files like .py, .js, etc.)",
|
|
545
|
+
"- The user wants to share a file",
|
|
546
|
+
"- The file is binary (PDF, images, audio, video, archives, etc.)",
|
|
547
|
+
"",
|
|
548
|
+
"Use read_file instead ONLY when the user explicitly wants to see the text contents/code inside a file",
|
|
549
|
+
`in the conversation (e.g. "show me the code", "what's in this file", "read this file").`
|
|
550
|
+
].join("\n"),
|
|
551
|
+
{
|
|
552
|
+
path: z.string().describe("Absolute or relative file path to share")
|
|
553
|
+
},
|
|
554
|
+
async ({ path: filePath }) => {
|
|
555
|
+
try {
|
|
556
|
+
const buffer = await fs.readFile(filePath);
|
|
557
|
+
const base64 = buffer.toString("base64");
|
|
558
|
+
const filename = path.basename(filePath);
|
|
559
|
+
const extMimeMap = {
|
|
560
|
+
".py": "text/x-python; charset=utf-8",
|
|
561
|
+
".js": "text/javascript; charset=utf-8",
|
|
562
|
+
".ts": "text/typescript; charset=utf-8",
|
|
563
|
+
".jsx": "text/javascript; charset=utf-8",
|
|
564
|
+
".tsx": "text/typescript; charset=utf-8",
|
|
565
|
+
".html": "text/html; charset=utf-8",
|
|
566
|
+
".css": "text/css; charset=utf-8",
|
|
567
|
+
".json": "application/json; charset=utf-8",
|
|
568
|
+
".md": "text/markdown; charset=utf-8",
|
|
569
|
+
".txt": "text/plain; charset=utf-8",
|
|
570
|
+
".csv": "text/csv; charset=utf-8",
|
|
571
|
+
".xml": "application/xml; charset=utf-8",
|
|
572
|
+
".yaml": "text/yaml; charset=utf-8",
|
|
573
|
+
".yml": "text/yaml; charset=utf-8",
|
|
574
|
+
".sh": "text/x-shellscript; charset=utf-8",
|
|
575
|
+
".bash": "text/x-shellscript; charset=utf-8",
|
|
576
|
+
".pdf": "application/pdf",
|
|
577
|
+
".png": "image/png",
|
|
578
|
+
".jpg": "image/jpeg",
|
|
579
|
+
".jpeg": "image/jpeg",
|
|
580
|
+
".gif": "image/gif",
|
|
581
|
+
".webp": "image/webp",
|
|
582
|
+
".svg": "image/svg+xml",
|
|
583
|
+
".mp4": "video/mp4",
|
|
584
|
+
".mp3": "audio/mpeg",
|
|
585
|
+
".wav": "audio/wav",
|
|
586
|
+
".zip": "application/zip",
|
|
587
|
+
".tar": "application/x-tar",
|
|
588
|
+
".gz": "application/gzip",
|
|
589
|
+
".doc": "application/msword",
|
|
590
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
591
|
+
".xls": "application/vnd.ms-excel",
|
|
592
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
593
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
594
|
+
};
|
|
595
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
596
|
+
const contentType = extMimeMap[ext] || "application/octet-stream";
|
|
597
|
+
const sharePayload = `__SHARE__:${filename}:${contentType}:${base64}`;
|
|
598
|
+
return { content: [{ type: "text", text: sharePayload }] };
|
|
599
|
+
} catch (err) {
|
|
600
|
+
const e = err;
|
|
601
|
+
if (e.code === "ENOENT") {
|
|
602
|
+
return { content: [{ type: "text", text: `\u274C File not found: ${filePath}` }], isError: true };
|
|
603
|
+
}
|
|
604
|
+
return { content: [{ type: "text", text: `\u274C Failed to read file: ${e.message}` }], isError: true };
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
);
|
|
537
608
|
}
|
|
538
609
|
};
|
|
539
610
|
|