junis 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/dist/cli/index.js CHANGED
@@ -55,9 +55,11 @@ function clearConfig() {
55
55
  var import_open = __toESM(require("open"));
56
56
  var JUNIS_API = process.env.JUNIS_API_URL ?? "https://api.junis.ai";
57
57
  var JUNIS_WEB = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
58
- async function authenticate() {
58
+ async function authenticate(deviceName, platform2, onBrowserOpen, onWaiting) {
59
59
  const startRes = await fetch(`${JUNIS_API}/api/auth/device/start`, {
60
- method: "POST"
60
+ method: "POST",
61
+ headers: { "Content-Type": "application/json" },
62
+ body: JSON.stringify({ device_name: deviceName, platform: platform2 })
61
63
  });
62
64
  if (!startRes.ok) {
63
65
  throw new Error(`Auth \uC2DC\uC791 \uC2E4\uD328: ${startRes.status}`);
@@ -67,24 +69,27 @@ async function authenticate() {
67
69
  /^https?:\/\/[^/]+/,
68
70
  JUNIS_WEB
69
71
  );
70
- console.log("\n\u{1F310} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uD6C4 \uC2B9\uC778 \uBC84\uD2BC\uC744 \uD074\uB9AD\uD558\uC138\uC694.");
71
- console.log(` URL: ${verificationUri}
72
- `);
72
+ onBrowserOpen?.(verificationUri);
73
73
  await (0, import_open.default)(verificationUri);
74
74
  const deadline = Date.now() + startData.expires_in * 1e3;
75
75
  const intervalMs = startData.interval * 1e3;
76
+ let waitingCallbackCalled = false;
76
77
  while (Date.now() < deadline) {
77
78
  await sleep(intervalMs);
78
79
  const pollRes = await fetch(
79
80
  `${JUNIS_API}/api/auth/device/poll?code=${startData.device_code}`
80
81
  );
81
82
  if (pollRes.status === 202) {
82
- process.stdout.write(".");
83
+ if (!waitingCallbackCalled) {
84
+ waitingCallbackCalled = true;
85
+ onWaiting?.();
86
+ } else {
87
+ onWaiting?.();
88
+ }
83
89
  continue;
84
90
  }
85
91
  if (pollRes.status === 200) {
86
92
  const result = await pollRes.json();
87
- console.log("\n\u2705 \uC778\uC99D \uC644\uB8CC!\n");
88
93
  return result;
89
94
  }
90
95
  if (pollRes.status === 410) {
@@ -885,7 +890,6 @@ async function startMCPServer(port) {
885
890
  const actualPort = await tryListen(httpServer, port, 10);
886
891
  resolvedPort = actualPort;
887
892
  mcpPort = actualPort;
888
- console.log(`MCP \uC11C\uBC84 \uC2DC\uC791: http://localhost:${actualPort}/mcp`);
889
893
  const cleanup = async () => {
890
894
  if (globalBrowserTools) {
891
895
  await globalBrowserTools.cleanup();
@@ -929,38 +933,137 @@ async function handleMCPRequest(id, payload) {
929
933
  }
930
934
 
931
935
  // src/cli/index.ts
932
- import_commander.program.name("junis").description("AI\uAC00 \uB0B4 \uB514\uBC14\uC774\uC2A4\uB97C \uC644\uC804 \uC81C\uC5B4\uD558\uB294 MCP \uC11C\uBC84").version("0.1.0");
936
+ import_commander.program.name("junis").description("AI\uAC00 \uB0B4 \uB514\uBC14\uC774\uC2A4\uB97C \uC644\uC804 \uC81C\uC5B4\uD558\uB294 MCP \uC11C\uBC84").version("0.1.2");
937
+ function getSystemInfo() {
938
+ const platform2 = process.platform;
939
+ if (platform2 === "darwin") {
940
+ try {
941
+ const { execSync } = require("child_process");
942
+ const sw = execSync("sw_vers -productVersion", { encoding: "utf8" }).trim();
943
+ const hw = execSync("sysctl -n machdep.cpu.brand_string", { encoding: "utf8" }).trim();
944
+ return `macOS ${sw} (${hw})`;
945
+ } catch {
946
+ return "macOS";
947
+ }
948
+ }
949
+ if (platform2 === "win32") return "Windows";
950
+ return "Linux";
951
+ }
952
+ function getDeviceName() {
953
+ const platform2 = process.platform;
954
+ if (platform2 === "darwin") return "Mac";
955
+ if (platform2 === "win32") return "Windows PC";
956
+ return "Linux PC";
957
+ }
958
+ function printBanner() {
959
+ console.log("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
960
+ console.log("\u2551 \u2551");
961
+ console.log("\u2551 \u{1F99E} J U N I S v0.1.2 \u2551");
962
+ console.log("\u2551 \u2551");
963
+ console.log("\u2551 Device Agent for AI Teams \u2551");
964
+ console.log("\u2551 \u2551");
965
+ console.log("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
966
+ console.log("");
967
+ }
968
+ function printStep1(port) {
969
+ const sysInfo = getSystemInfo();
970
+ 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");
971
+ console.log(" STEP 1 \xB7 Initializing Device");
972
+ 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");
973
+ console.log(` \u25C9 Scanning system ............... ${sysInfo}`);
974
+ console.log(" \u25C9 Loading MCP tools");
975
+ console.log(" \u251C\u2500\u2500 file_system files, directories, search \u2705 ready");
976
+ console.log(" \u251C\u2500\u2500 shell terminal commands, processes \u2705 ready");
977
+ console.log(" \u251C\u2500\u2500 browser web automation via Playwright \u2705 ready");
978
+ console.log(" \u251C\u2500\u2500 notebook Jupyter cell editing \u2705 ready");
979
+ console.log(" \u2514\u2500\u2500 device camera, screen, alerts \u2705 ready");
980
+ console.log(` \u25C9 Local MCP endpoint ........... http://localhost:${port}/mcp`);
981
+ console.log("");
982
+ }
933
983
  import_commander.program.command("start", { isDefault: true }).description("Junis \uC5D0\uC774\uC804\uD2B8\uC640 \uC5F0\uACB0 \uC2DC\uC791").option("--local", "\uB85C\uCEEC MCP \uC11C\uBC84\uB9CC \uC2E4\uD589 (\uD074\uB77C\uC6B0\uB4DC \uC5F0\uACB0 \uC5C6\uC74C)").option("--port <number>", "\uD3EC\uD2B8 \uBC88\uD638", "3000").option("--reset", "\uAE30\uC874 \uC778\uC99D \uCD08\uAE30\uD654 \uD6C4 \uC7AC\uB85C\uADF8\uC778").action(async (options) => {
934
984
  const port = parseInt(options.port, 10);
985
+ printBanner();
935
986
  if (options.local) {
936
- console.log("\u{1F3E0} \uB85C\uCEEC \uBAA8\uB4DC: \uD074\uB77C\uC6B0\uB4DC \uC5F0\uACB0 \uC5C6\uC774 MCP \uC11C\uBC84\uB9CC \uC2E4\uD589\uD569\uB2C8\uB2E4.");
937
- const actualPort2 = await startMCPServer(port);
987
+ const actualPort = await startMCPServer(port);
988
+ printStep1(actualPort);
989
+ 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");
990
+ console.log(" \u2705 ALL SET \u2014 Local MCP server is running");
991
+ 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");
938
992
  return;
939
993
  }
940
994
  let config = options.reset ? null : loadConfig();
941
995
  if (!config) {
942
- console.log("\u{1F511} Junis \uACC4\uC815 \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
943
- const authResult = await authenticate();
996
+ const deviceName = `${process.env["USER"] ?? "user"}'s ${getDeviceName()}`;
997
+ const platformName = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
998
+ const actualPort = await startMCPServer(port);
999
+ printStep1(actualPort);
1000
+ let waitingPrinted = false;
1001
+ const authResult = await authenticate(
1002
+ deviceName,
1003
+ platformName,
1004
+ (uri) => {
1005
+ 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");
1006
+ console.log(" STEP 2 \xB7 Connect to Junis Cloud");
1007
+ 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");
1008
+ console.log(" Opening browser...");
1009
+ console.log(` \u2192 ${uri}`);
1010
+ process.stdout.write(" Waiting for login \xB7");
1011
+ },
1012
+ () => {
1013
+ if (!waitingPrinted) {
1014
+ waitingPrinted = true;
1015
+ } else {
1016
+ process.stdout.write("\xB7");
1017
+ }
1018
+ }
1019
+ );
1020
+ console.log("");
1021
+ console.log(` \u2705 Authenticated as ${authResult.email ?? "your account"}`);
1022
+ console.log("");
944
1023
  config = {
945
1024
  device_key: authResult.device_key,
946
1025
  token: authResult.token,
947
- device_name: `${process.env["USER"] ?? "user"}'s ${getDeviceName()}`,
1026
+ device_name: deviceName,
948
1027
  created_at: (/* @__PURE__ */ new Date()).toISOString()
949
1028
  };
950
1029
  saveConfig(config);
951
- console.log(`\u{1F4BE} \uC778\uC99D \uC815\uBCF4 \uC800\uC7A5: ~/.junis/config.json`);
1030
+ 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");
1031
+ console.log(" STEP 3 \xB7 Register Device");
1032
+ 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");
1033
+ console.log(` \u25C9 Device name .................. ${deviceName}`);
1034
+ console.log(` \u25C9 Device key ................... ${authResult.device_key}`);
1035
+ console.log(` \u25C9 Cloud relay connected ........ wss://api.junis.ai/ws/devices/${authResult.device_key}`);
1036
+ console.log(" \u25C9 Status ....................... \u{1F7E2} online");
1037
+ console.log("");
1038
+ if (authResult.agent_id) {
1039
+ 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");
1040
+ console.log(" STEP 4 \xB7 Create AI Team");
1041
+ 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");
1042
+ console.log(" No existing team found. Creating your first AI team...");
1043
+ console.log(` \u25C9 Agent created ................ ${authResult.agent_name}`);
1044
+ console.log(` \u25C9 Device linked ................ ${deviceName} \u2192 ${authResult.agent_name}`);
1045
+ console.log(" \u25C9 Tools assigned ............... file_system, shell, browser, notebook, device");
1046
+ console.log("");
1047
+ }
1048
+ const relay = new RelayClient(config, handleMCPRequest);
1049
+ await relay.connect();
952
1050
  } else {
953
- console.log(`\u{1F511} \uAE30\uC874 \uC778\uC99D \uC0AC\uC6A9: ${config.device_name}`);
1051
+ const actualPort = await startMCPServer(port);
1052
+ printStep1(actualPort);
1053
+ 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");
1054
+ console.log(" STEP 3 \xB7 Register Device");
1055
+ 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");
1056
+ console.log(` \u25C9 Device name .................. ${config.device_name}`);
1057
+ console.log(` \u25C9 Device key ................... ${config.device_key}`);
1058
+ console.log(` \u25C9 Cloud relay connected ........ wss://api.junis.ai/ws/devices/${config.device_key}`);
1059
+ console.log(" \u25C9 Status ....................... \u{1F7E2} online");
1060
+ console.log("");
1061
+ const relay = new RelayClient(config, handleMCPRequest);
1062
+ await relay.connect();
954
1063
  }
955
- const actualPort = await startMCPServer(port);
956
- const relay = new RelayClient(config, handleMCPRequest);
957
- await relay.connect();
958
- console.log(`
959
- \u2728 Junis \uC2DC\uC791 \uC644\uB8CC!
960
- \uB85C\uCEEC MCP: http://localhost:${actualPort}/mcp
961
- \uB514\uBC14\uC774\uC2A4: ${config.device_name}
962
- \uC0C1\uD0DC: \uD074\uB77C\uC6B0\uB4DC \uB9B4\uB808\uC774 \uC5F0\uACB0\uB428
963
- `);
1064
+ 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");
1065
+ console.log(" \u2705 ALL SET \u2014 Your AI can now control this device");
1066
+ 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");
964
1067
  });
965
1068
  import_commander.program.command("logout").description("\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C").action(() => {
966
1069
  clearConfig();
@@ -978,10 +1081,4 @@ import_commander.program.command("status").description("\uD604\uC7AC \uC0C1\uD0D
978
1081
  );
979
1082
  }
980
1083
  });
981
- function getDeviceName() {
982
- const platform2 = process.platform;
983
- if (platform2 === "darwin") return "Mac";
984
- if (platform2 === "win32") return "Windows PC";
985
- return "Linux PC";
986
- }
987
1084
  import_commander.program.parse();
@@ -744,7 +744,6 @@ async function startMCPServer(port) {
744
744
  const actualPort = await tryListen(httpServer, port, 10);
745
745
  resolvedPort = actualPort;
746
746
  mcpPort = actualPort;
747
- console.log(`MCP \uC11C\uBC84 \uC2DC\uC791: http://localhost:${actualPort}/mcp`);
748
747
  const cleanup = async () => {
749
748
  if (globalBrowserTools) {
750
749
  await globalBrowserTools.cleanup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junis",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "One-line device control for AI agents",
5
5
  "bin": {
6
6
  "junis": "dist/cli/index.js"