junis 0.1.3 → 0.1.4
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 +133 -30
- 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.4",
|
|
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://
|
|
57
|
-
var JUNIS_WEB =
|
|
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 =
|
|
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}/
|
|
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 (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
988
|
-
printStep1(
|
|
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://
|
|
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,22 +1127,43 @@ 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://
|
|
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");
|