junis 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +150 -30
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
9
12
  var __copyProps = (to, from, except, desc) => {
10
13
  if (from && typeof from === "object" || typeof from === "function") {
11
14
  for (let key of __getOwnPropNames(from))
@@ -23,6 +26,48 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
26
  mod
24
27
  ));
25
28
 
29
+ // package.json
30
+ var require_package = __commonJS({
31
+ "package.json"(exports2, module2) {
32
+ module2.exports = {
33
+ name: "junis",
34
+ version: "0.1.5",
35
+ description: "One-line device control for AI agents",
36
+ bin: {
37
+ junis: "dist/cli/index.js"
38
+ },
39
+ scripts: {
40
+ build: "tsup src/cli/index.ts --format cjs --dts --out-dir dist/cli && tsup src/server/mcp.ts --format cjs --out-dir dist/server && tsup src/server/stdio.ts --format cjs --out-dir dist/server",
41
+ dev: "tsx src/cli/index.ts",
42
+ "type-check": "tsc --noEmit",
43
+ prepublishOnly: "npm run build"
44
+ },
45
+ dependencies: {
46
+ "@modelcontextprotocol/sdk": "^1.0.0",
47
+ commander: "^12.0.0",
48
+ glob: "^11.0.0",
49
+ open: "^10.1.0",
50
+ playwright: "^1.49.0",
51
+ ws: "^8.18.0",
52
+ zod: "^4.3.6"
53
+ },
54
+ devDependencies: {
55
+ "@types/node": "^20.0.0",
56
+ "@types/ws": "^8.5.0",
57
+ tsup: "^8.0.0",
58
+ tsx: "^4.0.0",
59
+ typescript: "^5.0.0"
60
+ },
61
+ engines: {
62
+ node: ">=18.0.0"
63
+ },
64
+ files: [
65
+ "dist/"
66
+ ]
67
+ };
68
+ }
69
+ });
70
+
26
71
  // src/cli/index.ts
27
72
  var import_commander = require("commander");
28
73
 
@@ -53,8 +98,20 @@ function clearConfig() {
53
98
 
54
99
  // src/cli/auth.ts
55
100
  var import_open = __toESM(require("open"));
56
- var JUNIS_API = process.env.JUNIS_API_URL ?? "https://api.junis.ai";
57
- var JUNIS_WEB = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
101
+ var JUNIS_API = process.env.JUNIS_API_URL ?? "https://junis.ai";
102
+ var JUNIS_WEB = (() => {
103
+ if (process.env.JUNIS_WEB_URL) return process.env.JUNIS_WEB_URL;
104
+ if (process.env.JUNIS_API_URL) {
105
+ try {
106
+ const u = new URL(process.env.JUNIS_API_URL);
107
+ if (u.hostname === "localhost" || u.hostname === "127.0.0.1") {
108
+ return `${u.protocol}//${u.hostname}:3000`;
109
+ }
110
+ } catch {
111
+ }
112
+ }
113
+ return null;
114
+ })();
58
115
  async function authenticate(deviceName, platform2, onBrowserOpen, onWaiting) {
59
116
  const startRes = await fetch(`${JUNIS_API}/api/auth/device/start`, {
60
117
  method: "POST",
@@ -65,10 +122,7 @@ async function authenticate(deviceName, platform2, onBrowserOpen, onWaiting) {
65
122
  throw new Error(`Auth \uC2DC\uC791 \uC2E4\uD328: ${startRes.status}`);
66
123
  }
67
124
  const startData = await startRes.json();
68
- const verificationUri = startData.verification_uri.replace(
69
- /^https?:\/\/[^/]+/,
70
- JUNIS_WEB
71
- );
125
+ const verificationUri = JUNIS_WEB ? startData.verification_uri.replace(/^https?:\/\/[^/]+/, JUNIS_WEB) : startData.verification_uri;
72
126
  onBrowserOpen?.(verificationUri);
73
127
  await (0, import_open.default)(verificationUri);
74
128
  const deadline = Date.now() + startData.expires_in * 1e3;
@@ -105,11 +159,21 @@ function sleep(ms) {
105
159
 
106
160
  // src/relay/client.ts
107
161
  var import_ws = __toESM(require("ws"));
108
- var JUNIS_WS = process.env.JUNIS_WS_URL ?? "wss://api.junis.ai";
162
+ var JUNIS_WS = (() => {
163
+ if (process.env.JUNIS_WS_URL) return process.env.JUNIS_WS_URL;
164
+ const apiUrl = process.env.JUNIS_API_URL ?? "https://junis.ai";
165
+ try {
166
+ const u = new URL(apiUrl);
167
+ return `${u.protocol.replace("http", "ws")}//${u.host}`;
168
+ } catch {
169
+ return "wss://junis.ai";
170
+ }
171
+ })();
109
172
  var RelayClient = class {
110
- constructor(config, onMCPRequest) {
173
+ constructor(config, onMCPRequest, onAuthExpired) {
111
174
  this.config = config;
112
175
  this.onMCPRequest = onMCPRequest;
176
+ this.onAuthExpired = onAuthExpired;
113
177
  }
114
178
  ws = null;
115
179
  reconnectDelay = 1e3;
@@ -117,7 +181,7 @@ var RelayClient = class {
117
181
  destroyed = false;
118
182
  async connect() {
119
183
  if (this.destroyed) return;
120
- const url = `${JUNIS_WS}/api/ws/devices/${this.config.device_key}?token=${this.config.token}`;
184
+ const url = `${JUNIS_WS}/ws/devices/${this.config.device_key}?token=${this.config.token}`;
121
185
  console.log(`\u{1F517} \uB9B4\uB808\uC774 \uC11C\uBC84 \uC5F0\uACB0 \uC911...`);
122
186
  this.ws = new import_ws.default(url);
123
187
  this.ws.on("open", () => {
@@ -144,18 +208,35 @@ var RelayClient = class {
144
208
  } catch {
145
209
  }
146
210
  });
147
- this.ws.on("close", () => {
211
+ this.ws.on("close", async (code) => {
148
212
  this.stopHeartbeat();
149
- if (!this.destroyed) {
150
- console.log(`\u26A0\uFE0F \uC5F0\uACB0 \uB04A\uAE40. ${this.reconnectDelay / 1e3}\uCD08 \uD6C4 \uC7AC\uC811\uC18D...`);
151
- setTimeout(() => this.connect(), this.reconnectDelay);
152
- this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
213
+ if (this.destroyed) return;
214
+ if (code === 4001) {
215
+ this.destroyed = true;
216
+ if (this.onAuthExpired) {
217
+ await this.onAuthExpired();
218
+ } else {
219
+ console.error(
220
+ "\n\u274C \uC778\uC99D \uD1A0\uD070\uC774 \uB9CC\uB8CC\uB410\uC2B5\uB2C8\uB2E4. `npx junis --reset` \uC73C\uB85C \uC7AC\uC778\uC99D\uD558\uC138\uC694."
221
+ );
222
+ process.exit(1);
223
+ }
224
+ return;
153
225
  }
226
+ console.log(`\u26A0\uFE0F \uC5F0\uACB0 \uB04A\uAE40. ${this.reconnectDelay / 1e3}\uCD08 \uD6C4 \uC7AC\uC811\uC18D...`);
227
+ setTimeout(() => this.connect(), this.reconnectDelay);
228
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
154
229
  });
155
230
  this.ws.on("error", (err) => {
156
231
  console.error(`\uB9B4\uB808\uC774 \uC624\uB958: ${err.message}`);
157
232
  });
158
233
  }
234
+ /** 재인증 완료 후 재연결 */
235
+ restart() {
236
+ this.destroyed = false;
237
+ this.reconnectDelay = 1e3;
238
+ this.connect();
239
+ }
159
240
  send(data) {
160
241
  if (this.ws?.readyState === import_ws.default.OPEN) {
161
242
  this.ws.send(JSON.stringify(data));
@@ -933,7 +1014,8 @@ async function handleMCPRequest(id, payload) {
933
1014
  }
934
1015
 
935
1016
  // src/cli/index.ts
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");
1017
+ var { version } = require_package();
1018
+ import_commander.program.name("junis").description("AI\uAC00 \uB0B4 \uB514\uBC14\uC774\uC2A4\uB97C \uC644\uC804 \uC81C\uC5B4\uD558\uB294 MCP \uC11C\uBC84").version(version);
937
1019
  function getSystemInfo() {
938
1020
  const platform2 = process.platform;
939
1021
  if (platform2 === "darwin") {
@@ -958,7 +1040,7 @@ function getDeviceName() {
958
1040
  function printBanner() {
959
1041
  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
1042
  console.log("\u2551 \u2551");
961
- console.log("\u2551 \u{1F99E} J U N I S v0.1.2 \u2551");
1043
+ console.log(`\u2551 \u{1F99E} J U N I S v${version} \u2551`);
962
1044
  console.log("\u2551 \u2551");
963
1045
  console.log("\u2551 Device Agent for AI Teams \u2551");
964
1046
  console.log("\u2551 \u2551");
@@ -984,19 +1066,19 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
984
1066
  const port = parseInt(options.port, 10);
985
1067
  printBanner();
986
1068
  if (options.local) {
987
- const actualPort = await startMCPServer(port);
988
- printStep1(actualPort);
1069
+ const actualPort2 = await startMCPServer(port);
1070
+ printStep1(actualPort2);
989
1071
  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
1072
  console.log(" \u2705 ALL SET \u2014 Local MCP server is running");
991
1073
  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");
992
1074
  return;
993
1075
  }
994
1076
  let config = options.reset ? null : loadConfig();
1077
+ const deviceName = config?.device_name ?? `${process.env["USER"] ?? "user"}'s ${getDeviceName()}`;
1078
+ const platformName = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
1079
+ const actualPort = await startMCPServer(port);
1080
+ printStep1(actualPort);
995
1081
  if (!config) {
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
1082
  let waitingPrinted = false;
1001
1083
  const authResult = await authenticate(
1002
1084
  deviceName,
@@ -1032,7 +1114,7 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
1032
1114
  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
1115
  console.log(` \u25C9 Device name .................. ${deviceName}`);
1034
1116
  console.log(` \u25C9 Device key ................... ${authResult.device_key}`);
1035
- console.log(` \u25C9 Cloud relay connected ........ wss://api.junis.ai/ws/devices/${authResult.device_key}`);
1117
+ console.log(` \u25C9 Cloud relay connected ........ wss://junis.ai/ws/devices/${authResult.device_key}`);
1036
1118
  console.log(" \u25C9 Status ....................... \u{1F7E2} online");
1037
1119
  console.log("");
1038
1120
  if (authResult.agent_id) {
@@ -1045,25 +1127,63 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
1045
1127
  console.log(" \u25C9 Tools assigned ............... file_system, shell, browser, notebook, device");
1046
1128
  console.log("");
1047
1129
  }
1048
- const relay = new RelayClient(config, handleMCPRequest);
1049
- await relay.connect();
1050
1130
  } else {
1051
- const actualPort = await startMCPServer(port);
1052
- printStep1(actualPort);
1053
1131
  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
1132
  console.log(" STEP 3 \xB7 Register Device");
1055
1133
  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
1134
  console.log(` \u25C9 Device name .................. ${config.device_name}`);
1057
1135
  console.log(` \u25C9 Device key ................... ${config.device_key}`);
1058
- console.log(` \u25C9 Cloud relay connected ........ wss://api.junis.ai/ws/devices/${config.device_key}`);
1136
+ console.log(` \u25C9 Cloud relay connected ........ wss://junis.ai/ws/devices/${config.device_key}`);
1059
1137
  console.log(" \u25C9 Status ....................... \u{1F7E2} online");
1060
1138
  console.log("");
1061
- const relay = new RelayClient(config, handleMCPRequest);
1062
- await relay.connect();
1063
1139
  }
1140
+ const relay = new RelayClient(config, handleMCPRequest, async () => {
1141
+ console.log("\n\u{1F511} \uC138\uC158\uC774 \uB9CC\uB8CC\uB410\uC2B5\uB2C8\uB2E4. \uC790\uB3D9\uC73C\uB85C \uC7AC\uC778\uC99D\uD569\uB2C8\uB2E4...");
1142
+ try {
1143
+ let waitingPrinted = false;
1144
+ const authResult = await authenticate(
1145
+ deviceName,
1146
+ platformName,
1147
+ (uri) => {
1148
+ console.log(" Opening browser for re-auth...");
1149
+ console.log(` \u2192 ${uri}`);
1150
+ process.stdout.write(" Waiting for login \xB7");
1151
+ },
1152
+ () => {
1153
+ process.stdout.write("\xB7");
1154
+ }
1155
+ );
1156
+ console.log(`
1157
+ \u2705 \uC7AC\uC778\uC99D \uC644\uB8CC`);
1158
+ config.token = authResult.token;
1159
+ saveConfig(config);
1160
+ relay.restart();
1161
+ } catch (e) {
1162
+ console.error("\n\u274C \uC7AC\uC778\uC99D \uC2E4\uD328:", e);
1163
+ process.exit(1);
1164
+ }
1165
+ });
1166
+ await relay.connect();
1064
1167
  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
1168
  console.log(" \u2705 ALL SET \u2014 Your AI can now control this device");
1066
1169
  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");
1170
+ const webUrl = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
1171
+ console.log("");
1172
+ console.log(" Opening your AI team...");
1173
+ console.log(` \u2192 ${webUrl}`);
1174
+ console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
1175
+ console.log("\u2502 \u2502");
1176
+ console.log("\u2502 Try asking your AI: \u2502");
1177
+ console.log("\u2502 \u2502");
1178
+ console.log('\u2502 "\uB370\uC2A4\uD06C\uD1B1 \uD30C\uC77C \uBAA9\uB85D \uBCF4\uC5EC\uC918" \u2502');
1179
+ console.log('\u2502 "\uD06C\uB86C\uC5D0\uC11C \uC624\uB298 \uB274\uC2A4 \uAC80\uC0C9\uD574\uC918" \u2502');
1180
+ console.log('\u2502 "\uCE74\uBA54\uB77C\uB85C \uC0AC\uC9C4 \uD55C \uC7A5 \uCC0D\uC5B4\uC918" \u2502');
1181
+ console.log('\u2502 "\uC774 \uC8FC\uD53C\uD130 \uB178\uD2B8\uBD81 3\uBC88 \uC140 \uC218\uC815\uD574\uC918" \u2502');
1182
+ console.log("\u2502 \u2502");
1183
+ console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
1184
+ console.log("");
1185
+ console.log(" Device agent running. Press Ctrl+C to disconnect.");
1186
+ console.log("");
1067
1187
  });
1068
1188
  import_commander.program.command("logout").description("\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C").action(() => {
1069
1189
  clearConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junis",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "One-line device control for AI agents",
5
5
  "bin": {
6
6
  "junis": "dist/cli/index.js"