junis 0.1.5 → 0.1.7

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 CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "junis",
34
- version: "0.1.5",
34
+ version: "0.1.7",
35
35
  description: "One-line device control for AI agents",
36
36
  bin: {
37
37
  junis: "dist/cli/index.js"
@@ -673,6 +673,7 @@ function platform() {
673
673
  return "linux";
674
674
  }
675
675
  var DeviceTools = class {
676
+ screenRecordPid = null;
676
677
  register(server) {
677
678
  server.tool(
678
679
  "screen_capture",
@@ -762,6 +763,80 @@ var DeviceTools = class {
762
763
  return { content: [{ type: "text", text: "\uD074\uB9BD\uBCF4\uB4DC \uC800\uC7A5 \uC644\uB8CC" }] };
763
764
  }
764
765
  );
766
+ server.tool(
767
+ "screen_record",
768
+ "\uD654\uBA74 \uB179\uD654 \uC2DC\uC791/\uC911\uC9C0 (macOS: screencapture -v, \uAE30\uD0C0: ffmpeg)",
769
+ {
770
+ action: import_zod4.z.enum(["start", "stop"]).describe("start: \uB179\uD654 \uC2DC\uC791, stop: \uB179\uD654 \uC911\uC9C0"),
771
+ output_path: import_zod4.z.string().optional().describe("\uC800\uC7A5 \uACBD\uB85C (start \uC2DC \uC0AC\uC6A9, \uAE30\uBCF8: /tmp/junis_record_<timestamp>.mp4)")
772
+ },
773
+ async ({ action, output_path }) => {
774
+ const p = platform();
775
+ if (action === "start") {
776
+ if (this.screenRecordPid) {
777
+ return { content: [{ type: "text", text: "\uC774\uBBF8 \uB179\uD654 \uC911\uC785\uB2C8\uB2E4." }] };
778
+ }
779
+ const tmpPath = output_path ?? `/tmp/junis_record_${Date.now()}.mp4`;
780
+ const { spawn } = await import("child_process");
781
+ const cmd = p === "mac" ? ["screencapture", ["-v", tmpPath]] : ["ffmpeg", ["-f", p === "win" ? "gdigrab" : "x11grab", "-i", p === "win" ? "desktop" : ":0.0", tmpPath]];
782
+ const child = spawn(cmd[0], cmd[1], { detached: true, stdio: "ignore" });
783
+ child.unref();
784
+ this.screenRecordPid = child.pid ?? null;
785
+ return { content: [{ type: "text", text: `\uB179\uD654 \uC2DC\uC791\uB428. \uC800\uC7A5 \uACBD\uB85C: ${tmpPath} (PID: ${this.screenRecordPid})` }] };
786
+ } else {
787
+ if (!this.screenRecordPid) {
788
+ return { content: [{ type: "text", text: "\uD604\uC7AC \uB179\uD654 \uC911\uC774 \uC544\uB2D9\uB2C8\uB2E4." }] };
789
+ }
790
+ try {
791
+ process.kill(this.screenRecordPid, "SIGINT");
792
+ } catch {
793
+ }
794
+ this.screenRecordPid = null;
795
+ return { content: [{ type: "text", text: "\uB179\uD654 \uC911\uC9C0\uB428." }] };
796
+ }
797
+ }
798
+ );
799
+ server.tool(
800
+ "location_get",
801
+ "\uD604\uC7AC \uC704\uCE58 \uC870\uD68C (macOS: CoreLocation CLI, \uAE30\uD0C0: IP \uAE30\uBC18 fallback)",
802
+ {},
803
+ async () => {
804
+ const p = platform();
805
+ if (p === "mac") {
806
+ try {
807
+ const { stdout } = await execAsync3("CoreLocationCLI -once -format '%latitude,%longitude'", { timeout: 1e4 });
808
+ const [lat, lon] = stdout.trim().split(",");
809
+ return { content: [{ type: "text", text: `\uC704\uB3C4: ${lat}, \uACBD\uB3C4: ${lon}` }] };
810
+ } catch {
811
+ }
812
+ }
813
+ const res = await fetch("https://ipapi.co/json/");
814
+ const data = await res.json();
815
+ return {
816
+ content: [{
817
+ type: "text",
818
+ text: `\uC704\uB3C4: ${data.latitude}, \uACBD\uB3C4: ${data.longitude}, \uB3C4\uC2DC: ${data.city}, \uAD6D\uAC00: ${data.country_name} (IP \uAE30\uBC18 \uCD94\uC815)`
819
+ }]
820
+ };
821
+ }
822
+ );
823
+ server.tool(
824
+ "audio_play",
825
+ "\uC624\uB514\uC624 \uD30C\uC77C \uC7AC\uC0DD (macOS: afplay, \uAE30\uD0C0: ffplay)",
826
+ {
827
+ file_path: import_zod4.z.string().describe("\uC7AC\uC0DD\uD560 \uC624\uB514\uC624 \uD30C\uC77C \uACBD\uB85C")
828
+ },
829
+ async ({ file_path }) => {
830
+ const p = platform();
831
+ const cmd = {
832
+ mac: `afplay "${file_path}"`,
833
+ win: `ffplay -nodisp -autoexit "${file_path}"`,
834
+ linux: `ffplay -nodisp -autoexit "${file_path}"`
835
+ }[p];
836
+ await execAsync3(cmd);
837
+ return { content: [{ type: "text", text: `\uC7AC\uC0DD \uC644\uB8CC: ${file_path}` }] };
838
+ }
839
+ );
765
840
  }
766
841
  };
767
842
 
@@ -1118,13 +1193,14 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
1118
1193
  console.log(" \u25C9 Status ....................... \u{1F7E2} online");
1119
1194
  console.log("");
1120
1195
  if (authResult.agent_id) {
1196
+ const workspaceName = `${deviceName} Workspace`;
1121
1197
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1122
1198
  console.log(" STEP 4 \xB7 Create AI Team");
1123
1199
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1124
- console.log(" No existing team found. Creating your first AI team...");
1200
+ console.log(` \u25C9 Workspace created ............ ${workspaceName}`);
1125
1201
  console.log(` \u25C9 Agent created ................ ${authResult.agent_name}`);
1126
1202
  console.log(` \u25C9 Device linked ................ ${deviceName} \u2192 ${authResult.agent_name}`);
1127
- console.log(" \u25C9 Tools assigned ............... file_system, shell, browser, notebook, device");
1203
+ console.log(" \u25C9 Tools assigned ............... call_device_mcp, list_device_mcp_tools");
1128
1204
  console.log("");
1129
1205
  }
1130
1206
  } else {
@@ -446,6 +446,7 @@ function platform() {
446
446
  return "linux";
447
447
  }
448
448
  var DeviceTools = class {
449
+ screenRecordPid = null;
449
450
  register(server) {
450
451
  server.tool(
451
452
  "screen_capture",
@@ -535,6 +536,80 @@ var DeviceTools = class {
535
536
  return { content: [{ type: "text", text: "\uD074\uB9BD\uBCF4\uB4DC \uC800\uC7A5 \uC644\uB8CC" }] };
536
537
  }
537
538
  );
539
+ server.tool(
540
+ "screen_record",
541
+ "\uD654\uBA74 \uB179\uD654 \uC2DC\uC791/\uC911\uC9C0 (macOS: screencapture -v, \uAE30\uD0C0: ffmpeg)",
542
+ {
543
+ action: import_zod4.z.enum(["start", "stop"]).describe("start: \uB179\uD654 \uC2DC\uC791, stop: \uB179\uD654 \uC911\uC9C0"),
544
+ output_path: import_zod4.z.string().optional().describe("\uC800\uC7A5 \uACBD\uB85C (start \uC2DC \uC0AC\uC6A9, \uAE30\uBCF8: /tmp/junis_record_<timestamp>.mp4)")
545
+ },
546
+ async ({ action, output_path }) => {
547
+ const p = platform();
548
+ if (action === "start") {
549
+ if (this.screenRecordPid) {
550
+ return { content: [{ type: "text", text: "\uC774\uBBF8 \uB179\uD654 \uC911\uC785\uB2C8\uB2E4." }] };
551
+ }
552
+ const tmpPath = output_path ?? `/tmp/junis_record_${Date.now()}.mp4`;
553
+ const { spawn } = await import("child_process");
554
+ const cmd = p === "mac" ? ["screencapture", ["-v", tmpPath]] : ["ffmpeg", ["-f", p === "win" ? "gdigrab" : "x11grab", "-i", p === "win" ? "desktop" : ":0.0", tmpPath]];
555
+ const child = spawn(cmd[0], cmd[1], { detached: true, stdio: "ignore" });
556
+ child.unref();
557
+ this.screenRecordPid = child.pid ?? null;
558
+ return { content: [{ type: "text", text: `\uB179\uD654 \uC2DC\uC791\uB428. \uC800\uC7A5 \uACBD\uB85C: ${tmpPath} (PID: ${this.screenRecordPid})` }] };
559
+ } else {
560
+ if (!this.screenRecordPid) {
561
+ return { content: [{ type: "text", text: "\uD604\uC7AC \uB179\uD654 \uC911\uC774 \uC544\uB2D9\uB2C8\uB2E4." }] };
562
+ }
563
+ try {
564
+ process.kill(this.screenRecordPid, "SIGINT");
565
+ } catch {
566
+ }
567
+ this.screenRecordPid = null;
568
+ return { content: [{ type: "text", text: "\uB179\uD654 \uC911\uC9C0\uB428." }] };
569
+ }
570
+ }
571
+ );
572
+ server.tool(
573
+ "location_get",
574
+ "\uD604\uC7AC \uC704\uCE58 \uC870\uD68C (macOS: CoreLocation CLI, \uAE30\uD0C0: IP \uAE30\uBC18 fallback)",
575
+ {},
576
+ async () => {
577
+ const p = platform();
578
+ if (p === "mac") {
579
+ try {
580
+ const { stdout } = await execAsync3("CoreLocationCLI -once -format '%latitude,%longitude'", { timeout: 1e4 });
581
+ const [lat, lon] = stdout.trim().split(",");
582
+ return { content: [{ type: "text", text: `\uC704\uB3C4: ${lat}, \uACBD\uB3C4: ${lon}` }] };
583
+ } catch {
584
+ }
585
+ }
586
+ const res = await fetch("https://ipapi.co/json/");
587
+ const data = await res.json();
588
+ return {
589
+ content: [{
590
+ type: "text",
591
+ text: `\uC704\uB3C4: ${data.latitude}, \uACBD\uB3C4: ${data.longitude}, \uB3C4\uC2DC: ${data.city}, \uAD6D\uAC00: ${data.country_name} (IP \uAE30\uBC18 \uCD94\uC815)`
592
+ }]
593
+ };
594
+ }
595
+ );
596
+ server.tool(
597
+ "audio_play",
598
+ "\uC624\uB514\uC624 \uD30C\uC77C \uC7AC\uC0DD (macOS: afplay, \uAE30\uD0C0: ffplay)",
599
+ {
600
+ file_path: import_zod4.z.string().describe("\uC7AC\uC0DD\uD560 \uC624\uB514\uC624 \uD30C\uC77C \uACBD\uB85C")
601
+ },
602
+ async ({ file_path }) => {
603
+ const p = platform();
604
+ const cmd = {
605
+ mac: `afplay "${file_path}"`,
606
+ win: `ffplay -nodisp -autoexit "${file_path}"`,
607
+ linux: `ffplay -nodisp -autoexit "${file_path}"`
608
+ }[p];
609
+ await execAsync3(cmd);
610
+ return { content: [{ type: "text", text: `\uC7AC\uC0DD \uC644\uB8CC: ${file_path}` }] };
611
+ }
612
+ );
538
613
  }
539
614
  };
540
615
 
@@ -435,6 +435,7 @@ function platform() {
435
435
  return "linux";
436
436
  }
437
437
  var DeviceTools = class {
438
+ screenRecordPid = null;
438
439
  register(server) {
439
440
  server.tool(
440
441
  "screen_capture",
@@ -524,6 +525,80 @@ var DeviceTools = class {
524
525
  return { content: [{ type: "text", text: "\uD074\uB9BD\uBCF4\uB4DC \uC800\uC7A5 \uC644\uB8CC" }] };
525
526
  }
526
527
  );
528
+ server.tool(
529
+ "screen_record",
530
+ "\uD654\uBA74 \uB179\uD654 \uC2DC\uC791/\uC911\uC9C0 (macOS: screencapture -v, \uAE30\uD0C0: ffmpeg)",
531
+ {
532
+ action: import_zod4.z.enum(["start", "stop"]).describe("start: \uB179\uD654 \uC2DC\uC791, stop: \uB179\uD654 \uC911\uC9C0"),
533
+ output_path: import_zod4.z.string().optional().describe("\uC800\uC7A5 \uACBD\uB85C (start \uC2DC \uC0AC\uC6A9, \uAE30\uBCF8: /tmp/junis_record_<timestamp>.mp4)")
534
+ },
535
+ async ({ action, output_path }) => {
536
+ const p = platform();
537
+ if (action === "start") {
538
+ if (this.screenRecordPid) {
539
+ return { content: [{ type: "text", text: "\uC774\uBBF8 \uB179\uD654 \uC911\uC785\uB2C8\uB2E4." }] };
540
+ }
541
+ const tmpPath = output_path ?? `/tmp/junis_record_${Date.now()}.mp4`;
542
+ const { spawn } = await import("child_process");
543
+ const cmd = p === "mac" ? ["screencapture", ["-v", tmpPath]] : ["ffmpeg", ["-f", p === "win" ? "gdigrab" : "x11grab", "-i", p === "win" ? "desktop" : ":0.0", tmpPath]];
544
+ const child = spawn(cmd[0], cmd[1], { detached: true, stdio: "ignore" });
545
+ child.unref();
546
+ this.screenRecordPid = child.pid ?? null;
547
+ return { content: [{ type: "text", text: `\uB179\uD654 \uC2DC\uC791\uB428. \uC800\uC7A5 \uACBD\uB85C: ${tmpPath} (PID: ${this.screenRecordPid})` }] };
548
+ } else {
549
+ if (!this.screenRecordPid) {
550
+ return { content: [{ type: "text", text: "\uD604\uC7AC \uB179\uD654 \uC911\uC774 \uC544\uB2D9\uB2C8\uB2E4." }] };
551
+ }
552
+ try {
553
+ process.kill(this.screenRecordPid, "SIGINT");
554
+ } catch {
555
+ }
556
+ this.screenRecordPid = null;
557
+ return { content: [{ type: "text", text: "\uB179\uD654 \uC911\uC9C0\uB428." }] };
558
+ }
559
+ }
560
+ );
561
+ server.tool(
562
+ "location_get",
563
+ "\uD604\uC7AC \uC704\uCE58 \uC870\uD68C (macOS: CoreLocation CLI, \uAE30\uD0C0: IP \uAE30\uBC18 fallback)",
564
+ {},
565
+ async () => {
566
+ const p = platform();
567
+ if (p === "mac") {
568
+ try {
569
+ const { stdout } = await execAsync3("CoreLocationCLI -once -format '%latitude,%longitude'", { timeout: 1e4 });
570
+ const [lat, lon] = stdout.trim().split(",");
571
+ return { content: [{ type: "text", text: `\uC704\uB3C4: ${lat}, \uACBD\uB3C4: ${lon}` }] };
572
+ } catch {
573
+ }
574
+ }
575
+ const res = await fetch("https://ipapi.co/json/");
576
+ const data = await res.json();
577
+ return {
578
+ content: [{
579
+ type: "text",
580
+ text: `\uC704\uB3C4: ${data.latitude}, \uACBD\uB3C4: ${data.longitude}, \uB3C4\uC2DC: ${data.city}, \uAD6D\uAC00: ${data.country_name} (IP \uAE30\uBC18 \uCD94\uC815)`
581
+ }]
582
+ };
583
+ }
584
+ );
585
+ server.tool(
586
+ "audio_play",
587
+ "\uC624\uB514\uC624 \uD30C\uC77C \uC7AC\uC0DD (macOS: afplay, \uAE30\uD0C0: ffplay)",
588
+ {
589
+ file_path: import_zod4.z.string().describe("\uC7AC\uC0DD\uD560 \uC624\uB514\uC624 \uD30C\uC77C \uACBD\uB85C")
590
+ },
591
+ async ({ file_path }) => {
592
+ const p = platform();
593
+ const cmd = {
594
+ mac: `afplay "${file_path}"`,
595
+ win: `ffplay -nodisp -autoexit "${file_path}"`,
596
+ linux: `ffplay -nodisp -autoexit "${file_path}"`
597
+ }[p];
598
+ await execAsync3(cmd);
599
+ return { content: [{ type: "text", text: `\uC7AC\uC0DD \uC644\uB8CC: ${file_path}` }] };
600
+ }
601
+ );
527
602
  }
528
603
  };
529
604
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junis",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "One-line device control for AI agents",
5
5
  "bin": {
6
6
  "junis": "dist/cli/index.js"