junis 0.2.2 → 0.2.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 +397 -104
- package/dist/server/mcp.js +70 -25
- package/dist/server/stdio.js +70 -25
- package/package.json +1 -1
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.2.
|
|
34
|
+
version: "0.2.4",
|
|
35
35
|
description: "One-line device control for AI agents",
|
|
36
36
|
bin: {
|
|
37
37
|
junis: "dist/cli/index.js"
|
|
@@ -91,7 +91,10 @@ function saveConfig(config) {
|
|
|
91
91
|
import_fs.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
92
92
|
import_fs.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
93
93
|
} catch (err) {
|
|
94
|
-
|
|
94
|
+
console.error(`
|
|
95
|
+
\u274C \uC124\uC815 \uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${err.message}`);
|
|
96
|
+
console.error(` \uC218\uB3D9\uC73C\uB85C ${CONFIG_FILE} \uC5D0 \uC800\uC7A5\uD574\uC8FC\uC138\uC694.`);
|
|
97
|
+
console.error(` \uB0B4\uC6A9: ${JSON.stringify(config, null, 2)}`);
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
function clearConfig() {
|
|
@@ -117,11 +120,19 @@ var JUNIS_WEB = (() => {
|
|
|
117
120
|
return null;
|
|
118
121
|
})();
|
|
119
122
|
async function authenticate(deviceName, platform2, onBrowserOpen, onWaiting) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
let startRes;
|
|
124
|
+
try {
|
|
125
|
+
startRes = await fetch(`${JUNIS_API}/api/auth/device/start`, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: { "Content-Type": "application/json" },
|
|
128
|
+
body: JSON.stringify({ device_name: deviceName, platform: platform2 })
|
|
129
|
+
});
|
|
130
|
+
} catch (err) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`\uC11C\uBC84\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uAC70\uB098 \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.
|
|
133
|
+
(${err.message})`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
125
136
|
if (!startRes.ok) {
|
|
126
137
|
const body = await startRes.text().catch(() => "");
|
|
127
138
|
throw new Error(`Auth \uC2DC\uC791 \uC2E4\uD328: ${startRes.status} ${body}`);
|
|
@@ -129,15 +140,29 @@ async function authenticate(deviceName, platform2, onBrowserOpen, onWaiting) {
|
|
|
129
140
|
const startData = await startRes.json();
|
|
130
141
|
const verificationUri = JUNIS_WEB ? startData.verification_uri.replace(/^https?:\/\/[^/]+/, JUNIS_WEB) : startData.verification_uri;
|
|
131
142
|
onBrowserOpen?.(verificationUri);
|
|
132
|
-
|
|
143
|
+
try {
|
|
144
|
+
await (0, import_open.default)(verificationUri);
|
|
145
|
+
} catch {
|
|
146
|
+
console.warn(`
|
|
147
|
+
\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uB798 URL\uC744 \uC9C1\uC811 \uC5F4\uC5B4\uC8FC\uC138\uC694:
|
|
148
|
+
|
|
149
|
+
${verificationUri}
|
|
150
|
+
`);
|
|
151
|
+
}
|
|
133
152
|
const deadline = Date.now() + startData.expires_in * 1e3;
|
|
134
153
|
const intervalMs = startData.interval * 1e3;
|
|
135
154
|
let waitingCallbackCalled = false;
|
|
136
155
|
while (Date.now() < deadline) {
|
|
137
156
|
await sleep(intervalMs);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
let pollRes;
|
|
158
|
+
try {
|
|
159
|
+
pollRes = await fetch(
|
|
160
|
+
`${JUNIS_API}/api/auth/device/poll?code=${startData.device_code}`
|
|
161
|
+
);
|
|
162
|
+
} catch {
|
|
163
|
+
onWaiting?.();
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
141
166
|
if (pollRes.status === 202) {
|
|
142
167
|
if (!waitingCallbackCalled) {
|
|
143
168
|
waitingCallbackCalled = true;
|
|
@@ -188,15 +213,18 @@ var RelayClient = class {
|
|
|
188
213
|
if (this.destroyed) return;
|
|
189
214
|
const url = `${JUNIS_WS}/ws/devices/${this.config.device_key}`;
|
|
190
215
|
console.log(`\u{1F517} \uB9B4\uB808\uC774 \uC11C\uBC84 \uC5F0\uACB0 \uC911...`);
|
|
191
|
-
|
|
216
|
+
const ws = new import_ws.default(url, {
|
|
192
217
|
headers: { Authorization: `Bearer ${this.config.token}` }
|
|
193
218
|
});
|
|
194
|
-
this.ws
|
|
219
|
+
this.ws = ws;
|
|
220
|
+
ws.on("open", () => {
|
|
221
|
+
if (this.ws !== ws) return;
|
|
195
222
|
console.log("\u2705 \uB9B4\uB808\uC774 \uC11C\uBC84 \uC5F0\uACB0\uB428");
|
|
196
223
|
this.reconnectDelay = 1e3;
|
|
197
224
|
this.startHeartbeat();
|
|
198
225
|
});
|
|
199
|
-
|
|
226
|
+
ws.on("message", async (raw) => {
|
|
227
|
+
if (this.ws !== ws) return;
|
|
200
228
|
try {
|
|
201
229
|
const msg = JSON.parse(raw.toString());
|
|
202
230
|
if (msg.type === "pong") return;
|
|
@@ -215,9 +243,10 @@ var RelayClient = class {
|
|
|
215
243
|
} catch {
|
|
216
244
|
}
|
|
217
245
|
});
|
|
218
|
-
|
|
219
|
-
this.stopHeartbeat();
|
|
246
|
+
ws.on("close", async (code) => {
|
|
220
247
|
if (this.destroyed) return;
|
|
248
|
+
if (this.ws !== ws) return;
|
|
249
|
+
this.stopHeartbeat();
|
|
221
250
|
if (code === 4001) {
|
|
222
251
|
this.destroyed = true;
|
|
223
252
|
if (this.onAuthExpired) {
|
|
@@ -234,7 +263,7 @@ var RelayClient = class {
|
|
|
234
263
|
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
235
264
|
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
|
|
236
265
|
});
|
|
237
|
-
|
|
266
|
+
ws.on("error", (err) => {
|
|
238
267
|
console.error(`\uB9B4\uB808\uC774 \uC624\uB958: ${err.message}`);
|
|
239
268
|
});
|
|
240
269
|
}
|
|
@@ -326,8 +355,16 @@ ${error.stderr ?? ""}`
|
|
|
326
355
|
encoding: import_zod.z.enum(["utf-8", "base64"]).optional().default("utf-8").describe("\uC778\uCF54\uB529")
|
|
327
356
|
},
|
|
328
357
|
async ({ path: filePath, encoding }) => {
|
|
329
|
-
|
|
330
|
-
|
|
358
|
+
try {
|
|
359
|
+
const content = await import_promises.default.readFile(filePath, encoding);
|
|
360
|
+
return { content: [{ type: "text", text: content }] };
|
|
361
|
+
} catch (err) {
|
|
362
|
+
const e = err;
|
|
363
|
+
if (e.code === "ENOENT") {
|
|
364
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}` }], isError: true };
|
|
365
|
+
}
|
|
366
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
367
|
+
}
|
|
331
368
|
}
|
|
332
369
|
);
|
|
333
370
|
server.tool(
|
|
@@ -350,9 +387,17 @@ ${error.stderr ?? ""}`
|
|
|
350
387
|
path: import_zod.z.string().describe("\uB514\uB809\uD1A0\uB9AC \uACBD\uB85C")
|
|
351
388
|
},
|
|
352
389
|
async ({ path: dirPath }) => {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
390
|
+
try {
|
|
391
|
+
const entries = await import_promises.default.readdir(dirPath, { withFileTypes: true });
|
|
392
|
+
const lines = entries.map((e) => `${e.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}"} ${e.name}`);
|
|
393
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
394
|
+
} catch (err) {
|
|
395
|
+
const e = err;
|
|
396
|
+
if (e.code === "ENOENT") {
|
|
397
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dirPath}` }], isError: true };
|
|
398
|
+
}
|
|
399
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
400
|
+
}
|
|
356
401
|
}
|
|
357
402
|
);
|
|
358
403
|
server.tool(
|
|
@@ -372,12 +417,13 @@ ${error.stderr ?? ""}`
|
|
|
372
417
|
);
|
|
373
418
|
return { content: [{ type: "text", text: stdout || "\uACB0\uACFC \uC5C6\uC74C" }] };
|
|
374
419
|
} catch {
|
|
375
|
-
const
|
|
420
|
+
const safeDirectory = import_path2.default.resolve(directory);
|
|
421
|
+
const files = await (0, import_glob.glob)(file_pattern, { cwd: safeDirectory });
|
|
376
422
|
const results = [];
|
|
377
423
|
for (const file of files.slice(0, 100)) {
|
|
378
424
|
try {
|
|
379
425
|
const content = await import_promises.default.readFile(
|
|
380
|
-
import_path2.default.join(
|
|
426
|
+
import_path2.default.join(safeDirectory, file),
|
|
381
427
|
"utf-8"
|
|
382
428
|
);
|
|
383
429
|
const lines = content.split("\n");
|
|
@@ -576,14 +622,14 @@ var BrowserTools = class {
|
|
|
576
622
|
path: import_zod2.z.string().optional().describe("\uC800\uC7A5 \uACBD\uB85C (\uC5C6\uC73C\uBA74 base64 \uBC18\uD658)"),
|
|
577
623
|
full_page: import_zod2.z.boolean().optional().default(false)
|
|
578
624
|
},
|
|
579
|
-
({ path:
|
|
625
|
+
({ path: path4, full_page }) => this.withLock(async () => {
|
|
580
626
|
const page = requirePage();
|
|
581
627
|
const screenshot = await page.screenshot({
|
|
582
|
-
path:
|
|
628
|
+
path: path4 ?? void 0,
|
|
583
629
|
fullPage: full_page
|
|
584
630
|
});
|
|
585
|
-
if (
|
|
586
|
-
return { content: [{ type: "text", text: `\uC800\uC7A5 \uC644\uB8CC: ${
|
|
631
|
+
if (path4) {
|
|
632
|
+
return { content: [{ type: "text", text: `\uC800\uC7A5 \uC644\uB8CC: ${path4}` }] };
|
|
587
633
|
}
|
|
588
634
|
return {
|
|
589
635
|
content: [
|
|
@@ -615,21 +661,28 @@ var BrowserTools = class {
|
|
|
615
661
|
"JavaScript \uC2E4\uD589",
|
|
616
662
|
{ code: import_zod2.z.string().describe("\uC2E4\uD589\uD560 JavaScript \uCF54\uB4DC") },
|
|
617
663
|
({ code }) => this.withLock(async () => {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
664
|
+
try {
|
|
665
|
+
const result = await requirePage().evaluate(code);
|
|
666
|
+
return {
|
|
667
|
+
content: [
|
|
668
|
+
{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2) }
|
|
669
|
+
]
|
|
670
|
+
};
|
|
671
|
+
} catch (err) {
|
|
672
|
+
return {
|
|
673
|
+
content: [{ type: "text", text: `\u274C JavaScript \uC2E4\uD589 \uC624\uB958: ${err.message}` }],
|
|
674
|
+
isError: true
|
|
675
|
+
};
|
|
676
|
+
}
|
|
624
677
|
})
|
|
625
678
|
);
|
|
626
679
|
server.tool(
|
|
627
680
|
"browser_pdf",
|
|
628
681
|
"\uD604\uC7AC \uD398\uC774\uC9C0 PDF \uC800\uC7A5",
|
|
629
682
|
{ path: import_zod2.z.string().describe("\uC800\uC7A5 \uACBD\uB85C (.pdf)") },
|
|
630
|
-
({ path:
|
|
631
|
-
await requirePage().pdf({ path:
|
|
632
|
-
return { content: [{ type: "text", text: `PDF \uC800\uC7A5 \uC644\uB8CC: ${
|
|
683
|
+
({ path: path4 }) => this.withLock(async () => {
|
|
684
|
+
await requirePage().pdf({ path: path4 });
|
|
685
|
+
return { content: [{ type: "text", text: `PDF \uC800\uC7A5 \uC644\uB8CC: ${path4}` }] };
|
|
633
686
|
})
|
|
634
687
|
);
|
|
635
688
|
}
|
|
@@ -646,7 +699,7 @@ async function readNotebook(filePath) {
|
|
|
646
699
|
try {
|
|
647
700
|
return JSON.parse(raw);
|
|
648
701
|
} catch {
|
|
649
|
-
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740
|
|
702
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 Jupyter \uB178\uD2B8\uBD81 \uD30C\uC77C\uC785\uB2C8\uB2E4: ${filePath}`);
|
|
650
703
|
}
|
|
651
704
|
}
|
|
652
705
|
async function writeNotebook(filePath, nb) {
|
|
@@ -742,16 +795,21 @@ var NotebookTools = class {
|
|
|
742
795
|
execution_count: cellType === "code" ? null : void 0
|
|
743
796
|
};
|
|
744
797
|
let actualIndex;
|
|
798
|
+
let warning = "";
|
|
745
799
|
if (position === void 0 || position === null) {
|
|
746
800
|
nb.cells.push(newCell);
|
|
747
801
|
actualIndex = nb.cells.length - 1;
|
|
802
|
+
} else if (position > nb.cells.length) {
|
|
803
|
+
nb.cells.push(newCell);
|
|
804
|
+
actualIndex = nb.cells.length - 1;
|
|
805
|
+
warning = ` (\uACBD\uACE0: position ${position}\uC774 \uBC94\uC704\uB97C \uCD08\uACFC\uD558\uC5EC \uB05D(index: ${actualIndex})\uC5D0 \uCD94\uAC00\uB428)`;
|
|
748
806
|
} else {
|
|
749
|
-
const clamped = Math.max(0,
|
|
807
|
+
const clamped = Math.max(0, position);
|
|
750
808
|
nb.cells.splice(clamped, 0, newCell);
|
|
751
809
|
actualIndex = clamped;
|
|
752
810
|
}
|
|
753
811
|
await writeNotebook(filePath, nb);
|
|
754
|
-
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
|
|
812
|
+
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})${warning}` }] };
|
|
755
813
|
}
|
|
756
814
|
);
|
|
757
815
|
server.tool(
|
|
@@ -809,9 +867,11 @@ var DeviceTools = class {
|
|
|
809
867
|
}
|
|
810
868
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
811
869
|
const data = readFileSync(tmpPath).toString("base64");
|
|
812
|
-
if (isTmp)
|
|
813
|
-
|
|
814
|
-
|
|
870
|
+
if (isTmp) {
|
|
871
|
+
try {
|
|
872
|
+
unlinkSync(tmpPath);
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
815
875
|
}
|
|
816
876
|
return {
|
|
817
877
|
content: [{ type: "image", data, mimeType: "image/png" }]
|
|
@@ -836,7 +896,14 @@ var DeviceTools = class {
|
|
|
836
896
|
try {
|
|
837
897
|
await execAsync3(cmd);
|
|
838
898
|
} catch (err) {
|
|
839
|
-
|
|
899
|
+
const e = err;
|
|
900
|
+
return {
|
|
901
|
+
content: [{ type: "text", text: `\u274C \uCE74\uBA54\uB77C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
902
|
+
\uC6D0\uC778: ${e.message}
|
|
903
|
+
|
|
904
|
+
\uCE74\uBA54\uB77C\uAC00 \uC5F0\uACB0\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.` }],
|
|
905
|
+
isError: true
|
|
906
|
+
};
|
|
840
907
|
}
|
|
841
908
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
842
909
|
const data = readFileSync(tmpPath).toString("base64");
|
|
@@ -858,11 +925,17 @@ var DeviceTools = class {
|
|
|
858
925
|
},
|
|
859
926
|
async ({ title, message }) => {
|
|
860
927
|
const p = platform();
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
928
|
+
let cmd;
|
|
929
|
+
if (p === "win") {
|
|
930
|
+
const script = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${message.replace(/'/g, "''")}', '${title.replace(/'/g, "''")}')`;
|
|
931
|
+
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
932
|
+
cmd = `powershell -NoProfile -EncodedCommand ${encoded}`;
|
|
933
|
+
} else {
|
|
934
|
+
cmd = {
|
|
935
|
+
mac: `osascript -e 'display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"'`,
|
|
936
|
+
linux: `notify-send "${title.replace(/"/g, '\\"')}" "${message.replace(/"/g, '\\"')}"`
|
|
937
|
+
}[p] ?? "";
|
|
938
|
+
}
|
|
866
939
|
await execAsync3(cmd);
|
|
867
940
|
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
868
941
|
}
|
|
@@ -907,9 +980,9 @@ var DeviceTools = class {
|
|
|
907
980
|
return { content: [{ type: "text", text: "\uC774\uBBF8 \uB179\uD654 \uC911\uC785\uB2C8\uB2E4." }] };
|
|
908
981
|
}
|
|
909
982
|
const tmpPath = output_path ?? `/tmp/junis_record_${Date.now()}.mp4`;
|
|
910
|
-
const { spawn } = await import("child_process");
|
|
983
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
911
984
|
const cmd = p === "mac" ? ["screencapture", ["-v", tmpPath]] : ["ffmpeg", ["-f", p === "win" ? "gdigrab" : "x11grab", "-i", p === "win" ? "desktop" : ":0.0", tmpPath]];
|
|
912
|
-
const child =
|
|
985
|
+
const child = spawn2(cmd[0], cmd[1], { detached: true, stdio: "ignore" });
|
|
913
986
|
child.unref();
|
|
914
987
|
screenRecordPid = child.pid ?? null;
|
|
915
988
|
return { content: [{ type: "text", text: `\uB179\uD654 \uC2DC\uC791\uB428. \uC800\uC7A5 \uACBD\uB85C: ${tmpPath} (PID: ${screenRecordPid})` }] };
|
|
@@ -919,6 +992,7 @@ var DeviceTools = class {
|
|
|
919
992
|
}
|
|
920
993
|
try {
|
|
921
994
|
process.kill(screenRecordPid, "SIGINT");
|
|
995
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
922
996
|
} catch {
|
|
923
997
|
}
|
|
924
998
|
screenRecordPid = null;
|
|
@@ -1221,6 +1295,182 @@ async function handleMCPRequest(id, payload) {
|
|
|
1221
1295
|
return null;
|
|
1222
1296
|
}
|
|
1223
1297
|
|
|
1298
|
+
// src/cli/daemon.ts
|
|
1299
|
+
var import_fs2 = __toESM(require("fs"));
|
|
1300
|
+
var import_path3 = __toESM(require("path"));
|
|
1301
|
+
var import_os2 = __toESM(require("os"));
|
|
1302
|
+
var import_child_process4 = require("child_process");
|
|
1303
|
+
var CONFIG_DIR2 = import_path3.default.join(import_os2.default.homedir(), ".junis");
|
|
1304
|
+
var PID_FILE = import_path3.default.join(CONFIG_DIR2, "junis.pid");
|
|
1305
|
+
var LOG_DIR = import_path3.default.join(CONFIG_DIR2, "logs");
|
|
1306
|
+
var LOG_FILE = import_path3.default.join(LOG_DIR, "junis.log");
|
|
1307
|
+
var PLIST_PATH = import_path3.default.join(
|
|
1308
|
+
import_os2.default.homedir(),
|
|
1309
|
+
"Library/LaunchAgents/ai.junis.plist"
|
|
1310
|
+
);
|
|
1311
|
+
var SYSTEMD_PATH = import_path3.default.join(
|
|
1312
|
+
import_os2.default.homedir(),
|
|
1313
|
+
".config/systemd/user/junis.service"
|
|
1314
|
+
);
|
|
1315
|
+
function isRunning() {
|
|
1316
|
+
try {
|
|
1317
|
+
if (!import_fs2.default.existsSync(PID_FILE)) return { running: false };
|
|
1318
|
+
const pid = parseInt(import_fs2.default.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1319
|
+
if (isNaN(pid)) return { running: false };
|
|
1320
|
+
process.kill(pid, 0);
|
|
1321
|
+
return { running: true, pid };
|
|
1322
|
+
} catch {
|
|
1323
|
+
try {
|
|
1324
|
+
import_fs2.default.unlinkSync(PID_FILE);
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
return { running: false };
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function writePid(pid) {
|
|
1331
|
+
import_fs2.default.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
1332
|
+
import_fs2.default.writeFileSync(PID_FILE, String(pid), "utf-8");
|
|
1333
|
+
}
|
|
1334
|
+
function startDaemon(port) {
|
|
1335
|
+
import_fs2.default.mkdirSync(LOG_DIR, { recursive: true });
|
|
1336
|
+
const nodePath = process.execPath;
|
|
1337
|
+
const scriptPath = process.argv[1];
|
|
1338
|
+
const out = import_fs2.default.openSync(LOG_FILE, "a");
|
|
1339
|
+
const err = import_fs2.default.openSync(LOG_FILE, "a");
|
|
1340
|
+
const child = (0, import_child_process4.spawn)(nodePath, [scriptPath, "start", "--daemon", "--port", String(port)], {
|
|
1341
|
+
detached: true,
|
|
1342
|
+
stdio: ["ignore", out, err],
|
|
1343
|
+
env: { ...process.env }
|
|
1344
|
+
// JUNIS_API_URL, JUNIS_WS_URL 등 현재 env 상속
|
|
1345
|
+
});
|
|
1346
|
+
child.unref();
|
|
1347
|
+
const pid = child.pid;
|
|
1348
|
+
writePid(pid);
|
|
1349
|
+
}
|
|
1350
|
+
function stopDaemon() {
|
|
1351
|
+
const { running, pid } = isRunning();
|
|
1352
|
+
if (!running || !pid) return false;
|
|
1353
|
+
try {
|
|
1354
|
+
process.kill(pid, "SIGTERM");
|
|
1355
|
+
try {
|
|
1356
|
+
import_fs2.default.unlinkSync(PID_FILE);
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
return true;
|
|
1360
|
+
} catch {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
var ServiceManager = class {
|
|
1365
|
+
get platform() {
|
|
1366
|
+
if (process.platform === "darwin") return "mac";
|
|
1367
|
+
if (process.platform === "win32") return "win";
|
|
1368
|
+
return "linux";
|
|
1369
|
+
}
|
|
1370
|
+
async install() {
|
|
1371
|
+
const nodePath = process.execPath;
|
|
1372
|
+
const scriptPath = process.argv[1];
|
|
1373
|
+
if (this.platform === "mac") {
|
|
1374
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1375
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1376
|
+
<plist version="1.0">
|
|
1377
|
+
<dict>
|
|
1378
|
+
<key>Label</key>
|
|
1379
|
+
<string>ai.junis</string>
|
|
1380
|
+
<key>ProgramArguments</key>
|
|
1381
|
+
<array>
|
|
1382
|
+
<string>${nodePath}</string>
|
|
1383
|
+
<string>${scriptPath}</string>
|
|
1384
|
+
<string>start</string>
|
|
1385
|
+
<string>--daemon</string>
|
|
1386
|
+
</array>
|
|
1387
|
+
<key>EnvironmentVariables</key>
|
|
1388
|
+
<dict>
|
|
1389
|
+
<key>HOME</key>
|
|
1390
|
+
<string>${import_os2.default.homedir()}</string>
|
|
1391
|
+
<key>PATH</key>
|
|
1392
|
+
<string>${process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin"}</string>
|
|
1393
|
+
${process.env.JUNIS_API_URL ? `<key>JUNIS_API_URL</key>
|
|
1394
|
+
<string>${process.env.JUNIS_API_URL}</string>` : ""}
|
|
1395
|
+
${process.env.JUNIS_WS_URL ? `<key>JUNIS_WS_URL</key>
|
|
1396
|
+
<string>${process.env.JUNIS_WS_URL}</string>` : ""}
|
|
1397
|
+
${process.env.JUNIS_WEB_URL ? `<key>JUNIS_WEB_URL</key>
|
|
1398
|
+
<string>${process.env.JUNIS_WEB_URL}</string>` : ""}
|
|
1399
|
+
</dict>
|
|
1400
|
+
<key>RunAtLoad</key>
|
|
1401
|
+
<true/>
|
|
1402
|
+
<key>KeepAlive</key>
|
|
1403
|
+
<true/>
|
|
1404
|
+
<key>StandardOutPath</key>
|
|
1405
|
+
<string>${LOG_FILE}</string>
|
|
1406
|
+
<key>StandardErrorPath</key>
|
|
1407
|
+
<string>${LOG_FILE}</string>
|
|
1408
|
+
</dict>
|
|
1409
|
+
</plist>`;
|
|
1410
|
+
import_fs2.default.mkdirSync(import_path3.default.dirname(PLIST_PATH), { recursive: true });
|
|
1411
|
+
import_fs2.default.mkdirSync(LOG_DIR, { recursive: true });
|
|
1412
|
+
import_fs2.default.writeFileSync(PLIST_PATH, plist, "utf-8");
|
|
1413
|
+
try {
|
|
1414
|
+
(0, import_child_process4.execSync)(`launchctl unload "${PLIST_PATH}" 2>/dev/null || true`);
|
|
1415
|
+
(0, import_child_process4.execSync)(`launchctl load "${PLIST_PATH}"`);
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
throw new Error(`launchctl load \uC2E4\uD328: ${e.message}`);
|
|
1418
|
+
}
|
|
1419
|
+
} else if (this.platform === "linux") {
|
|
1420
|
+
const unit = `[Unit]
|
|
1421
|
+
Description=Junis Device Agent
|
|
1422
|
+
After=network.target
|
|
1423
|
+
|
|
1424
|
+
[Service]
|
|
1425
|
+
ExecStart=${nodePath} ${scriptPath} start --daemon
|
|
1426
|
+
Restart=always
|
|
1427
|
+
RestartSec=5
|
|
1428
|
+
Environment=HOME=${import_os2.default.homedir()}
|
|
1429
|
+
Environment=PATH=${process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin"}
|
|
1430
|
+
${process.env.JUNIS_API_URL ? `Environment=JUNIS_API_URL=${process.env.JUNIS_API_URL}` : ""}
|
|
1431
|
+
${process.env.JUNIS_WS_URL ? `Environment=JUNIS_WS_URL=${process.env.JUNIS_WS_URL}` : ""}
|
|
1432
|
+
${process.env.JUNIS_WEB_URL ? `Environment=JUNIS_WEB_URL=${process.env.JUNIS_WEB_URL}` : ""}
|
|
1433
|
+
StandardOutput=append:${LOG_FILE}
|
|
1434
|
+
StandardError=append:${LOG_FILE}
|
|
1435
|
+
|
|
1436
|
+
[Install]
|
|
1437
|
+
WantedBy=default.target`;
|
|
1438
|
+
import_fs2.default.mkdirSync(import_path3.default.dirname(SYSTEMD_PATH), { recursive: true });
|
|
1439
|
+
import_fs2.default.mkdirSync(LOG_DIR, { recursive: true });
|
|
1440
|
+
import_fs2.default.writeFileSync(SYSTEMD_PATH, unit, "utf-8");
|
|
1441
|
+
(0, import_child_process4.execSync)("systemctl --user daemon-reload");
|
|
1442
|
+
(0, import_child_process4.execSync)("systemctl --user enable junis");
|
|
1443
|
+
(0, import_child_process4.execSync)("systemctl --user start junis");
|
|
1444
|
+
} else {
|
|
1445
|
+
(0, import_child_process4.execSync)(
|
|
1446
|
+
`schtasks /Create /F /TN "Junis" /TR "${nodePath} ${scriptPath} start --daemon" /SC ONLOGON /RL HIGHEST`
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
async uninstall() {
|
|
1451
|
+
if (this.platform === "mac") {
|
|
1452
|
+
try {
|
|
1453
|
+
(0, import_child_process4.execSync)(`launchctl unload "${PLIST_PATH}" 2>/dev/null || true`);
|
|
1454
|
+
if (import_fs2.default.existsSync(PLIST_PATH)) import_fs2.default.unlinkSync(PLIST_PATH);
|
|
1455
|
+
} catch {
|
|
1456
|
+
}
|
|
1457
|
+
} else if (this.platform === "linux") {
|
|
1458
|
+
try {
|
|
1459
|
+
(0, import_child_process4.execSync)("systemctl --user stop junis 2>/dev/null || true");
|
|
1460
|
+
(0, import_child_process4.execSync)("systemctl --user disable junis 2>/dev/null || true");
|
|
1461
|
+
if (import_fs2.default.existsSync(SYSTEMD_PATH)) import_fs2.default.unlinkSync(SYSTEMD_PATH);
|
|
1462
|
+
(0, import_child_process4.execSync)("systemctl --user daemon-reload 2>/dev/null || true");
|
|
1463
|
+
} catch {
|
|
1464
|
+
}
|
|
1465
|
+
} else {
|
|
1466
|
+
try {
|
|
1467
|
+
(0, import_child_process4.execSync)('schtasks /Delete /F /TN "Junis" 2>nul || true');
|
|
1468
|
+
} catch {
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1224
1474
|
// src/cli/index.ts
|
|
1225
1475
|
var { version } = require_package();
|
|
1226
1476
|
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);
|
|
@@ -1228,9 +1478,9 @@ function getSystemInfo() {
|
|
|
1228
1478
|
const platform2 = process.platform;
|
|
1229
1479
|
if (platform2 === "darwin") {
|
|
1230
1480
|
try {
|
|
1231
|
-
const { execSync } = require("child_process");
|
|
1232
|
-
const sw =
|
|
1233
|
-
const hw =
|
|
1481
|
+
const { execSync: execSync2 } = require("child_process");
|
|
1482
|
+
const sw = execSync2("sw_vers -productVersion", { encoding: "utf8" }).trim();
|
|
1483
|
+
const hw = execSync2("sysctl -n machdep.cpu.brand_string", { encoding: "utf8" }).trim();
|
|
1234
1484
|
return `macOS ${sw} (${hw})`;
|
|
1235
1485
|
} catch {
|
|
1236
1486
|
return "macOS";
|
|
@@ -1272,22 +1522,68 @@ function printStep1(port) {
|
|
|
1272
1522
|
console.log(` \u25C9 Local MCP endpoint ........... http://localhost:${port}/mcp`);
|
|
1273
1523
|
console.log("");
|
|
1274
1524
|
}
|
|
1275
|
-
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) => {
|
|
1525
|
+
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").option("--daemon", "\uB370\uBAAC \uBAA8\uB4DC\uB85C \uC2E4\uD589 (\uB0B4\uBD80\uC6A9, launchd/systemd\uC5D0\uC11C \uC0AC\uC6A9)").action(async (options) => {
|
|
1276
1526
|
const port = parseInt(options.port, 10);
|
|
1527
|
+
if (options.daemon) {
|
|
1528
|
+
writePid(process.pid);
|
|
1529
|
+
if (options.local) {
|
|
1530
|
+
await startMCPServer(port);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
let config2 = options.reset ? null : loadConfig();
|
|
1534
|
+
if (!config2) {
|
|
1535
|
+
console.error("\u274C \uC778\uC99D \uC815\uBCF4 \uC5C6\uC74C. npx junis \uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1536
|
+
process.exit(1);
|
|
1537
|
+
}
|
|
1538
|
+
const deviceName2 = config2.device_name;
|
|
1539
|
+
const platformName2 = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
|
|
1540
|
+
const actualPort = await startMCPServer(port);
|
|
1541
|
+
console.log(`[junis daemon] MCP server started on port ${actualPort}`);
|
|
1542
|
+
const relay = new RelayClient(config2, handleMCPRequest, async () => {
|
|
1543
|
+
console.log("[junis daemon] \uC138\uC158 \uB9CC\uB8CC - \uC7AC\uC778\uC99D \uD544\uC694");
|
|
1544
|
+
try {
|
|
1545
|
+
let waitingPrinted = false;
|
|
1546
|
+
const authResult = await authenticate(
|
|
1547
|
+
deviceName2,
|
|
1548
|
+
platformName2,
|
|
1549
|
+
(uri) => {
|
|
1550
|
+
console.log(`[junis daemon] \uBE0C\uB77C\uC6B0\uC800 \uC7AC\uC778\uC99D: ${uri}`);
|
|
1551
|
+
},
|
|
1552
|
+
() => {
|
|
1553
|
+
if (!waitingPrinted) waitingPrinted = true;
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1556
|
+
config2.token = authResult.token;
|
|
1557
|
+
saveConfig(config2);
|
|
1558
|
+
relay.restart();
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
console.error("[junis daemon] \uC7AC\uC778\uC99D \uC2E4\uD328:", e);
|
|
1561
|
+
process.exit(1);
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
await relay.connect();
|
|
1565
|
+
console.log("[junis daemon] relay connected");
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1277
1568
|
printBanner();
|
|
1278
1569
|
if (options.local) {
|
|
1279
|
-
const
|
|
1280
|
-
printStep1(
|
|
1570
|
+
const actualPort = await startMCPServer(port);
|
|
1571
|
+
printStep1(actualPort);
|
|
1281
1572
|
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");
|
|
1282
1573
|
console.log(" \u2705 ALL SET \u2014 Local MCP server is running");
|
|
1283
1574
|
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");
|
|
1284
1575
|
return;
|
|
1285
1576
|
}
|
|
1577
|
+
const { running, pid } = isRunning();
|
|
1578
|
+
if (running) {
|
|
1579
|
+
console.log(`\u2705 Junis \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4. (PID: ${pid})`);
|
|
1580
|
+
console.log(" \uC885\uB8CC\uD558\uB824\uBA74: npx junis stop");
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1286
1583
|
let config = options.reset ? null : loadConfig();
|
|
1287
1584
|
const deviceName = config?.device_name ?? `${process.env["USER"] ?? "user"}'s ${getDeviceName()}`;
|
|
1288
1585
|
const platformName = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
|
|
1289
|
-
|
|
1290
|
-
printStep1(actualPort);
|
|
1586
|
+
printStep1(port);
|
|
1291
1587
|
if (!config) {
|
|
1292
1588
|
let waitingPrinted = false;
|
|
1293
1589
|
const authResult = await authenticate(
|
|
@@ -1348,53 +1644,47 @@ import_commander.program.command("start", { isDefault: true }).description("Juni
|
|
|
1348
1644
|
console.log(" \u25C9 Status ....................... \u{1F7E2} online");
|
|
1349
1645
|
console.log("");
|
|
1350
1646
|
}
|
|
1351
|
-
const relay = new RelayClient(config, handleMCPRequest, async () => {
|
|
1352
|
-
console.log("\n\u{1F511} \uC138\uC158\uC774 \uB9CC\uB8CC\uB410\uC2B5\uB2C8\uB2E4. \uC790\uB3D9\uC73C\uB85C \uC7AC\uC778\uC99D\uD569\uB2C8\uB2E4...");
|
|
1353
|
-
try {
|
|
1354
|
-
let waitingPrinted = false;
|
|
1355
|
-
const authResult = await authenticate(
|
|
1356
|
-
deviceName,
|
|
1357
|
-
platformName,
|
|
1358
|
-
(uri) => {
|
|
1359
|
-
console.log(" Opening browser for re-auth...");
|
|
1360
|
-
console.log(` \u2192 ${uri}`);
|
|
1361
|
-
process.stdout.write(" Waiting for login \xB7");
|
|
1362
|
-
},
|
|
1363
|
-
() => {
|
|
1364
|
-
process.stdout.write("\xB7");
|
|
1365
|
-
}
|
|
1366
|
-
);
|
|
1367
|
-
console.log(`
|
|
1368
|
-
\u2705 \uC7AC\uC778\uC99D \uC644\uB8CC`);
|
|
1369
|
-
config.token = authResult.token;
|
|
1370
|
-
saveConfig(config);
|
|
1371
|
-
relay.restart();
|
|
1372
|
-
} catch (e) {
|
|
1373
|
-
console.error("\n\u274C \uC7AC\uC778\uC99D \uC2E4\uD328:", e);
|
|
1374
|
-
process.exit(1);
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
|
-
await relay.connect();
|
|
1378
1647
|
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");
|
|
1379
|
-
console.log("
|
|
1648
|
+
console.log(" STEP 5 \xB7 Starting Background Service");
|
|
1380
1649
|
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");
|
|
1650
|
+
const svc = new ServiceManager();
|
|
1651
|
+
try {
|
|
1652
|
+
await svc.install();
|
|
1653
|
+
console.log(" \u25C9 Service registered ........... \u2705");
|
|
1654
|
+
console.log(" \u25C9 Auto-start on boot ........... \u2705");
|
|
1655
|
+
} catch (e) {
|
|
1656
|
+
console.warn(` \u26A0\uFE0F \uC11C\uBE44\uC2A4 \uB4F1\uB85D \uC2E4\uD328: ${e.message}`);
|
|
1657
|
+
console.warn(" \uBC31\uADF8\uB77C\uC6B4\uB4DC \uD504\uB85C\uC138\uC2A4\uB85C\uB9CC \uC2E4\uD589\uD569\uB2C8\uB2E4.");
|
|
1658
|
+
startDaemon(port);
|
|
1659
|
+
}
|
|
1381
1660
|
const webUrl = process.env.JUNIS_WEB_URL ?? "https://junis.ai";
|
|
1382
1661
|
console.log("");
|
|
1383
|
-
console.log("
|
|
1662
|
+
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");
|
|
1663
|
+
console.log(" \u2705 ALL SET \u2014 Junis\uAC00 \uBC31\uADF8\uB77C\uC6B4\uB4DC\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4.");
|
|
1664
|
+
console.log(" \uBD80\uD305 \uC2DC \uC790\uB3D9\uC73C\uB85C \uC2DC\uC791\uB429\uB2C8\uB2E4.");
|
|
1665
|
+
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");
|
|
1666
|
+
console.log("");
|
|
1384
1667
|
console.log(` \u2192 ${webUrl}`);
|
|
1385
|
-
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");
|
|
1386
|
-
console.log("\u2502 \u2502");
|
|
1387
|
-
console.log("\u2502 Try asking your AI: \u2502");
|
|
1388
|
-
console.log("\u2502 \u2502");
|
|
1389
|
-
console.log('\u2502 "\uB370\uC2A4\uD06C\uD1B1 \uD30C\uC77C \uBAA9\uB85D \uBCF4\uC5EC\uC918" \u2502');
|
|
1390
|
-
console.log('\u2502 "\uD06C\uB86C\uC5D0\uC11C \uC624\uB298 \uB274\uC2A4 \uAC80\uC0C9\uD574\uC918" \u2502');
|
|
1391
|
-
console.log('\u2502 "\uCE74\uBA54\uB77C\uB85C \uC0AC\uC9C4 \uD55C \uC7A5 \uCC0D\uC5B4\uC918" \u2502');
|
|
1392
|
-
console.log('\u2502 "\uC774 \uC8FC\uD53C\uD130 \uB178\uD2B8\uBD81 3\uBC88 \uC140 \uC218\uC815\uD574\uC918" \u2502');
|
|
1393
|
-
console.log("\u2502 \u2502");
|
|
1394
|
-
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");
|
|
1395
1668
|
console.log("");
|
|
1396
|
-
console.log("
|
|
1669
|
+
console.log(" \uC885\uB8CC\uD558\uB824\uBA74: npx junis stop");
|
|
1397
1670
|
console.log("");
|
|
1671
|
+
process.exit(0);
|
|
1672
|
+
});
|
|
1673
|
+
import_commander.program.command("stop").description("\uBC31\uADF8\uB77C\uC6B4\uB4DC \uC11C\uBE44\uC2A4 \uC911\uC9C0 \uBC0F \uC790\uB3D9\uC2DC\uC791 \uD574\uC81C").action(async () => {
|
|
1674
|
+
const stopped = stopDaemon();
|
|
1675
|
+
const svc = new ServiceManager();
|
|
1676
|
+
let serviceUninstalled = false;
|
|
1677
|
+
try {
|
|
1678
|
+
await svc.uninstall();
|
|
1679
|
+
serviceUninstalled = true;
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
if (stopped || serviceUninstalled) {
|
|
1683
|
+
console.log("\u2705 Junis \uC11C\uBE44\uC2A4\uAC00 \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
1684
|
+
console.log(" \uC790\uB3D9\uC2DC\uC791\uC774 \uD574\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
1685
|
+
} else {
|
|
1686
|
+
console.log("\u2139\uFE0F \uC2E4\uD589 \uC911\uC778 Junis \uD504\uB85C\uC138\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1687
|
+
}
|
|
1398
1688
|
});
|
|
1399
1689
|
import_commander.program.command("logout").description("\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C").action(() => {
|
|
1400
1690
|
clearConfig();
|
|
@@ -1402,14 +1692,17 @@ import_commander.program.command("logout").description("\uC778\uC99D \uC815\uBCF
|
|
|
1402
1692
|
});
|
|
1403
1693
|
import_commander.program.command("status").description("\uD604\uC7AC \uC0C1\uD0DC \uD655\uC778").action(() => {
|
|
1404
1694
|
const config = loadConfig();
|
|
1695
|
+
const { running, pid } = isRunning();
|
|
1405
1696
|
if (!config) {
|
|
1406
1697
|
console.log("\u274C \uC778\uC99D \uC5C6\uC74C (npx junis \uC2E4\uD589 \uD544\uC694)");
|
|
1698
|
+
} else if (running) {
|
|
1699
|
+
console.log(`\u2705 \uC2E4\uD589 \uC911 (PID: ${pid})`);
|
|
1700
|
+
console.log(` \uB514\uBC14\uC774\uC2A4: ${config.device_name}`);
|
|
1701
|
+
console.log(` \uB4F1\uB85D\uC77C: ${config.created_at}`);
|
|
1407
1702
|
} else {
|
|
1408
|
-
console.log(
|
|
1409
|
-
|
|
1410
|
-
\
|
|
1411
|
-
\uB4F1\uB85D\uC77C: ${config.created_at}`
|
|
1412
|
-
);
|
|
1703
|
+
console.log("\u26A0\uFE0F \uC778\uC99D\uB428, \uC11C\uBE44\uC2A4 \uC911\uC9C0 \uC0C1\uD0DC");
|
|
1704
|
+
console.log(` \uB514\uBC14\uC774\uC2A4: ${config.device_name}`);
|
|
1705
|
+
console.log(" \uC2DC\uC791\uD558\uB824\uBA74: npx junis");
|
|
1413
1706
|
}
|
|
1414
1707
|
});
|
|
1415
1708
|
import_commander.program.parse();
|
package/dist/server/mcp.js
CHANGED
|
@@ -92,8 +92,16 @@ ${error.stderr ?? ""}`
|
|
|
92
92
|
encoding: import_zod.z.enum(["utf-8", "base64"]).optional().default("utf-8").describe("\uC778\uCF54\uB529")
|
|
93
93
|
},
|
|
94
94
|
async ({ path: filePath, encoding }) => {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
try {
|
|
96
|
+
const content = await import_promises.default.readFile(filePath, encoding);
|
|
97
|
+
return { content: [{ type: "text", text: content }] };
|
|
98
|
+
} catch (err) {
|
|
99
|
+
const e = err;
|
|
100
|
+
if (e.code === "ENOENT") {
|
|
101
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}` }], isError: true };
|
|
102
|
+
}
|
|
103
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
);
|
|
99
107
|
server.tool(
|
|
@@ -116,9 +124,17 @@ ${error.stderr ?? ""}`
|
|
|
116
124
|
path: import_zod.z.string().describe("\uB514\uB809\uD1A0\uB9AC \uACBD\uB85C")
|
|
117
125
|
},
|
|
118
126
|
async ({ path: dirPath }) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
try {
|
|
128
|
+
const entries = await import_promises.default.readdir(dirPath, { withFileTypes: true });
|
|
129
|
+
const lines = entries.map((e) => `${e.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}"} ${e.name}`);
|
|
130
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const e = err;
|
|
133
|
+
if (e.code === "ENOENT") {
|
|
134
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dirPath}` }], isError: true };
|
|
135
|
+
}
|
|
136
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
137
|
+
}
|
|
122
138
|
}
|
|
123
139
|
);
|
|
124
140
|
server.tool(
|
|
@@ -138,12 +154,13 @@ ${error.stderr ?? ""}`
|
|
|
138
154
|
);
|
|
139
155
|
return { content: [{ type: "text", text: stdout || "\uACB0\uACFC \uC5C6\uC74C" }] };
|
|
140
156
|
} catch {
|
|
141
|
-
const
|
|
157
|
+
const safeDirectory = import_path.default.resolve(directory);
|
|
158
|
+
const files = await (0, import_glob.glob)(file_pattern, { cwd: safeDirectory });
|
|
142
159
|
const results = [];
|
|
143
160
|
for (const file of files.slice(0, 100)) {
|
|
144
161
|
try {
|
|
145
162
|
const content = await import_promises.default.readFile(
|
|
146
|
-
import_path.default.join(
|
|
163
|
+
import_path.default.join(safeDirectory, file),
|
|
147
164
|
"utf-8"
|
|
148
165
|
);
|
|
149
166
|
const lines = content.split("\n");
|
|
@@ -381,12 +398,19 @@ var BrowserTools = class {
|
|
|
381
398
|
"JavaScript \uC2E4\uD589",
|
|
382
399
|
{ code: import_zod2.z.string().describe("\uC2E4\uD589\uD560 JavaScript \uCF54\uB4DC") },
|
|
383
400
|
({ code }) => this.withLock(async () => {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
401
|
+
try {
|
|
402
|
+
const result = await requirePage().evaluate(code);
|
|
403
|
+
return {
|
|
404
|
+
content: [
|
|
405
|
+
{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2) }
|
|
406
|
+
]
|
|
407
|
+
};
|
|
408
|
+
} catch (err) {
|
|
409
|
+
return {
|
|
410
|
+
content: [{ type: "text", text: `\u274C JavaScript \uC2E4\uD589 \uC624\uB958: ${err.message}` }],
|
|
411
|
+
isError: true
|
|
412
|
+
};
|
|
413
|
+
}
|
|
390
414
|
})
|
|
391
415
|
);
|
|
392
416
|
server.tool(
|
|
@@ -412,7 +436,7 @@ async function readNotebook(filePath) {
|
|
|
412
436
|
try {
|
|
413
437
|
return JSON.parse(raw);
|
|
414
438
|
} catch {
|
|
415
|
-
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740
|
|
439
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 Jupyter \uB178\uD2B8\uBD81 \uD30C\uC77C\uC785\uB2C8\uB2E4: ${filePath}`);
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
442
|
async function writeNotebook(filePath, nb) {
|
|
@@ -508,16 +532,21 @@ var NotebookTools = class {
|
|
|
508
532
|
execution_count: cellType === "code" ? null : void 0
|
|
509
533
|
};
|
|
510
534
|
let actualIndex;
|
|
535
|
+
let warning = "";
|
|
511
536
|
if (position === void 0 || position === null) {
|
|
512
537
|
nb.cells.push(newCell);
|
|
513
538
|
actualIndex = nb.cells.length - 1;
|
|
539
|
+
} else if (position > nb.cells.length) {
|
|
540
|
+
nb.cells.push(newCell);
|
|
541
|
+
actualIndex = nb.cells.length - 1;
|
|
542
|
+
warning = ` (\uACBD\uACE0: position ${position}\uC774 \uBC94\uC704\uB97C \uCD08\uACFC\uD558\uC5EC \uB05D(index: ${actualIndex})\uC5D0 \uCD94\uAC00\uB428)`;
|
|
514
543
|
} else {
|
|
515
|
-
const clamped = Math.max(0,
|
|
544
|
+
const clamped = Math.max(0, position);
|
|
516
545
|
nb.cells.splice(clamped, 0, newCell);
|
|
517
546
|
actualIndex = clamped;
|
|
518
547
|
}
|
|
519
548
|
await writeNotebook(filePath, nb);
|
|
520
|
-
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
|
|
549
|
+
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})${warning}` }] };
|
|
521
550
|
}
|
|
522
551
|
);
|
|
523
552
|
server.tool(
|
|
@@ -575,9 +604,11 @@ var DeviceTools = class {
|
|
|
575
604
|
}
|
|
576
605
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
577
606
|
const data = readFileSync(tmpPath).toString("base64");
|
|
578
|
-
if (isTmp)
|
|
579
|
-
|
|
580
|
-
|
|
607
|
+
if (isTmp) {
|
|
608
|
+
try {
|
|
609
|
+
unlinkSync(tmpPath);
|
|
610
|
+
} catch {
|
|
611
|
+
}
|
|
581
612
|
}
|
|
582
613
|
return {
|
|
583
614
|
content: [{ type: "image", data, mimeType: "image/png" }]
|
|
@@ -602,7 +633,14 @@ var DeviceTools = class {
|
|
|
602
633
|
try {
|
|
603
634
|
await execAsync3(cmd);
|
|
604
635
|
} catch (err) {
|
|
605
|
-
|
|
636
|
+
const e = err;
|
|
637
|
+
return {
|
|
638
|
+
content: [{ type: "text", text: `\u274C \uCE74\uBA54\uB77C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
639
|
+
\uC6D0\uC778: ${e.message}
|
|
640
|
+
|
|
641
|
+
\uCE74\uBA54\uB77C\uAC00 \uC5F0\uACB0\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.` }],
|
|
642
|
+
isError: true
|
|
643
|
+
};
|
|
606
644
|
}
|
|
607
645
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
608
646
|
const data = readFileSync(tmpPath).toString("base64");
|
|
@@ -624,11 +662,17 @@ var DeviceTools = class {
|
|
|
624
662
|
},
|
|
625
663
|
async ({ title, message }) => {
|
|
626
664
|
const p = platform();
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
665
|
+
let cmd;
|
|
666
|
+
if (p === "win") {
|
|
667
|
+
const script = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${message.replace(/'/g, "''")}', '${title.replace(/'/g, "''")}')`;
|
|
668
|
+
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
669
|
+
cmd = `powershell -NoProfile -EncodedCommand ${encoded}`;
|
|
670
|
+
} else {
|
|
671
|
+
cmd = {
|
|
672
|
+
mac: `osascript -e 'display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"'`,
|
|
673
|
+
linux: `notify-send "${title.replace(/"/g, '\\"')}" "${message.replace(/"/g, '\\"')}"`
|
|
674
|
+
}[p] ?? "";
|
|
675
|
+
}
|
|
632
676
|
await execAsync3(cmd);
|
|
633
677
|
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
634
678
|
}
|
|
@@ -685,6 +729,7 @@ var DeviceTools = class {
|
|
|
685
729
|
}
|
|
686
730
|
try {
|
|
687
731
|
process.kill(screenRecordPid, "SIGINT");
|
|
732
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
688
733
|
} catch {
|
|
689
734
|
}
|
|
690
735
|
screenRecordPid = null;
|
package/dist/server/stdio.js
CHANGED
|
@@ -81,8 +81,16 @@ ${error.stderr ?? ""}`
|
|
|
81
81
|
encoding: import_zod.z.enum(["utf-8", "base64"]).optional().default("utf-8").describe("\uC778\uCF54\uB529")
|
|
82
82
|
},
|
|
83
83
|
async ({ path: filePath, encoding }) => {
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
try {
|
|
85
|
+
const content = await import_promises.default.readFile(filePath, encoding);
|
|
86
|
+
return { content: [{ type: "text", text: content }] };
|
|
87
|
+
} catch (err) {
|
|
88
|
+
const e = err;
|
|
89
|
+
if (e.code === "ENOENT") {
|
|
90
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}` }], isError: true };
|
|
91
|
+
}
|
|
92
|
+
return { content: [{ type: "text", text: `\u274C \uD30C\uC77C \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
93
|
+
}
|
|
86
94
|
}
|
|
87
95
|
);
|
|
88
96
|
server.tool(
|
|
@@ -105,9 +113,17 @@ ${error.stderr ?? ""}`
|
|
|
105
113
|
path: import_zod.z.string().describe("\uB514\uB809\uD1A0\uB9AC \uACBD\uB85C")
|
|
106
114
|
},
|
|
107
115
|
async ({ path: dirPath }) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
try {
|
|
117
|
+
const entries = await import_promises.default.readdir(dirPath, { withFileTypes: true });
|
|
118
|
+
const lines = entries.map((e) => `${e.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}"} ${e.name}`);
|
|
119
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const e = err;
|
|
122
|
+
if (e.code === "ENOENT") {
|
|
123
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dirPath}` }], isError: true };
|
|
124
|
+
}
|
|
125
|
+
return { content: [{ type: "text", text: `\u274C \uB514\uB809\uD1A0\uB9AC \uC77D\uAE30 \uC2E4\uD328: ${e.message}` }], isError: true };
|
|
126
|
+
}
|
|
111
127
|
}
|
|
112
128
|
);
|
|
113
129
|
server.tool(
|
|
@@ -127,12 +143,13 @@ ${error.stderr ?? ""}`
|
|
|
127
143
|
);
|
|
128
144
|
return { content: [{ type: "text", text: stdout || "\uACB0\uACFC \uC5C6\uC74C" }] };
|
|
129
145
|
} catch {
|
|
130
|
-
const
|
|
146
|
+
const safeDirectory = import_path.default.resolve(directory);
|
|
147
|
+
const files = await (0, import_glob.glob)(file_pattern, { cwd: safeDirectory });
|
|
131
148
|
const results = [];
|
|
132
149
|
for (const file of files.slice(0, 100)) {
|
|
133
150
|
try {
|
|
134
151
|
const content = await import_promises.default.readFile(
|
|
135
|
-
import_path.default.join(
|
|
152
|
+
import_path.default.join(safeDirectory, file),
|
|
136
153
|
"utf-8"
|
|
137
154
|
);
|
|
138
155
|
const lines = content.split("\n");
|
|
@@ -370,12 +387,19 @@ var BrowserTools = class {
|
|
|
370
387
|
"JavaScript \uC2E4\uD589",
|
|
371
388
|
{ code: import_zod2.z.string().describe("\uC2E4\uD589\uD560 JavaScript \uCF54\uB4DC") },
|
|
372
389
|
({ code }) => this.withLock(async () => {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
390
|
+
try {
|
|
391
|
+
const result = await requirePage().evaluate(code);
|
|
392
|
+
return {
|
|
393
|
+
content: [
|
|
394
|
+
{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2) }
|
|
395
|
+
]
|
|
396
|
+
};
|
|
397
|
+
} catch (err) {
|
|
398
|
+
return {
|
|
399
|
+
content: [{ type: "text", text: `\u274C JavaScript \uC2E4\uD589 \uC624\uB958: ${err.message}` }],
|
|
400
|
+
isError: true
|
|
401
|
+
};
|
|
402
|
+
}
|
|
379
403
|
})
|
|
380
404
|
);
|
|
381
405
|
server.tool(
|
|
@@ -401,7 +425,7 @@ async function readNotebook(filePath) {
|
|
|
401
425
|
try {
|
|
402
426
|
return JSON.parse(raw);
|
|
403
427
|
} catch {
|
|
404
|
-
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740
|
|
428
|
+
throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 Jupyter \uB178\uD2B8\uBD81 \uD30C\uC77C\uC785\uB2C8\uB2E4: ${filePath}`);
|
|
405
429
|
}
|
|
406
430
|
}
|
|
407
431
|
async function writeNotebook(filePath, nb) {
|
|
@@ -497,16 +521,21 @@ var NotebookTools = class {
|
|
|
497
521
|
execution_count: cellType === "code" ? null : void 0
|
|
498
522
|
};
|
|
499
523
|
let actualIndex;
|
|
524
|
+
let warning = "";
|
|
500
525
|
if (position === void 0 || position === null) {
|
|
501
526
|
nb.cells.push(newCell);
|
|
502
527
|
actualIndex = nb.cells.length - 1;
|
|
528
|
+
} else if (position > nb.cells.length) {
|
|
529
|
+
nb.cells.push(newCell);
|
|
530
|
+
actualIndex = nb.cells.length - 1;
|
|
531
|
+
warning = ` (\uACBD\uACE0: position ${position}\uC774 \uBC94\uC704\uB97C \uCD08\uACFC\uD558\uC5EC \uB05D(index: ${actualIndex})\uC5D0 \uCD94\uAC00\uB428)`;
|
|
503
532
|
} else {
|
|
504
|
-
const clamped = Math.max(0,
|
|
533
|
+
const clamped = Math.max(0, position);
|
|
505
534
|
nb.cells.splice(clamped, 0, newCell);
|
|
506
535
|
actualIndex = clamped;
|
|
507
536
|
}
|
|
508
537
|
await writeNotebook(filePath, nb);
|
|
509
|
-
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
|
|
538
|
+
return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})${warning}` }] };
|
|
510
539
|
}
|
|
511
540
|
);
|
|
512
541
|
server.tool(
|
|
@@ -564,9 +593,11 @@ var DeviceTools = class {
|
|
|
564
593
|
}
|
|
565
594
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
566
595
|
const data = readFileSync(tmpPath).toString("base64");
|
|
567
|
-
if (isTmp)
|
|
568
|
-
|
|
569
|
-
|
|
596
|
+
if (isTmp) {
|
|
597
|
+
try {
|
|
598
|
+
unlinkSync(tmpPath);
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
570
601
|
}
|
|
571
602
|
return {
|
|
572
603
|
content: [{ type: "image", data, mimeType: "image/png" }]
|
|
@@ -591,7 +622,14 @@ var DeviceTools = class {
|
|
|
591
622
|
try {
|
|
592
623
|
await execAsync3(cmd);
|
|
593
624
|
} catch (err) {
|
|
594
|
-
|
|
625
|
+
const e = err;
|
|
626
|
+
return {
|
|
627
|
+
content: [{ type: "text", text: `\u274C \uCE74\uBA54\uB77C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
628
|
+
\uC6D0\uC778: ${e.message}
|
|
629
|
+
|
|
630
|
+
\uCE74\uBA54\uB77C\uAC00 \uC5F0\uACB0\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.` }],
|
|
631
|
+
isError: true
|
|
632
|
+
};
|
|
595
633
|
}
|
|
596
634
|
const { readFileSync, unlinkSync } = await import("fs");
|
|
597
635
|
const data = readFileSync(tmpPath).toString("base64");
|
|
@@ -613,11 +651,17 @@ var DeviceTools = class {
|
|
|
613
651
|
},
|
|
614
652
|
async ({ title, message }) => {
|
|
615
653
|
const p = platform();
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
654
|
+
let cmd;
|
|
655
|
+
if (p === "win") {
|
|
656
|
+
const script = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${message.replace(/'/g, "''")}', '${title.replace(/'/g, "''")}')`;
|
|
657
|
+
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
658
|
+
cmd = `powershell -NoProfile -EncodedCommand ${encoded}`;
|
|
659
|
+
} else {
|
|
660
|
+
cmd = {
|
|
661
|
+
mac: `osascript -e 'display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"'`,
|
|
662
|
+
linux: `notify-send "${title.replace(/"/g, '\\"')}" "${message.replace(/"/g, '\\"')}"`
|
|
663
|
+
}[p] ?? "";
|
|
664
|
+
}
|
|
621
665
|
await execAsync3(cmd);
|
|
622
666
|
return { content: [{ type: "text", text: "\uC54C\uB9BC \uC804\uC1A1 \uC644\uB8CC" }] };
|
|
623
667
|
}
|
|
@@ -674,6 +718,7 @@ var DeviceTools = class {
|
|
|
674
718
|
}
|
|
675
719
|
try {
|
|
676
720
|
process.kill(screenRecordPid, "SIGINT");
|
|
721
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
677
722
|
} catch {
|
|
678
723
|
}
|
|
679
724
|
screenRecordPid = null;
|