junis 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "junis",
34
- version: "0.1.8",
34
+ version: "0.2.0",
35
35
  description: "One-line device control for AI agents",
36
36
  bin: {
37
37
  junis: "dist/cli/index.js"
@@ -182,9 +182,11 @@ var RelayClient = class {
182
182
  destroyed = false;
183
183
  async connect() {
184
184
  if (this.destroyed) return;
185
- const url = `${JUNIS_WS}/ws/devices/${this.config.device_key}?token=${this.config.token}`;
185
+ const url = `${JUNIS_WS}/ws/devices/${this.config.device_key}`;
186
186
  console.log(`\u{1F517} \uB9B4\uB808\uC774 \uC11C\uBC84 \uC5F0\uACB0 \uC911...`);
187
- this.ws = new import_ws.default(url);
187
+ this.ws = new import_ws.default(url, {
188
+ headers: { Authorization: `Bearer ${this.config.token}` }
189
+ });
188
190
  this.ws.on("open", () => {
189
191
  console.log("\u2705 \uB9B4\uB808\uC774 \uC11C\uBC84 \uC5F0\uACB0\uB428");
190
192
  this.reconnectDelay = 1e3;
@@ -447,6 +449,46 @@ ${error.stderr ?? ""}`
447
449
  };
448
450
  }
449
451
  );
452
+ server.tool(
453
+ "edit_block",
454
+ "\uD30C\uC77C\uC758 \uD2B9\uC815 \uD14D\uC2A4\uD2B8 \uBE14\uB85D\uC744 \uC0C8 \uD14D\uC2A4\uD2B8\uB85C \uAD50\uCCB4 (diff \uAE30\uBC18 \uBD80\uBD84 \uC218\uC815)",
455
+ {
456
+ path: import_zod.z.string().describe("\uD30C\uC77C \uACBD\uB85C"),
457
+ old_string: import_zod.z.string().describe("\uAD50\uCCB4\uD560 \uAE30\uC874 \uD14D\uC2A4\uD2B8 (\uC815\uD655\uD788 \uC77C\uCE58\uD574\uC57C \uD568)"),
458
+ new_string: import_zod.z.string().describe("\uC0C8 \uD14D\uC2A4\uD2B8"),
459
+ replace_all: import_zod.z.boolean().optional().default(false).describe("true\uBA74 \uBAA8\uB4E0 \uB9E4\uCE6D \uAD50\uCCB4, false\uBA74 \uCCAB \uBC88\uC9F8\uB9CC")
460
+ },
461
+ async ({ path: filePath, old_string, new_string, replace_all }) => {
462
+ const content = await import_promises.default.readFile(filePath, "utf-8");
463
+ if (!content.includes(old_string)) {
464
+ throw new Error(`old_string\uC744 \uD30C\uC77C\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}`);
465
+ }
466
+ let count = 0;
467
+ let pos = 0;
468
+ while ((pos = content.indexOf(old_string, pos)) !== -1) {
469
+ count++;
470
+ pos += old_string.length;
471
+ }
472
+ if (!replace_all && count > 1) {
473
+ throw new Error(
474
+ `\uB9E4\uCE6D\uC774 ${count}\uAC1C\uC785\uB2C8\uB2E4. replace_all\uC744 true\uB85C \uD558\uAC70\uB098 \uB354 \uB113\uC740 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uD3EC\uD568\uD558\uC138\uC694.`
475
+ );
476
+ }
477
+ let result;
478
+ let replaced;
479
+ if (replace_all) {
480
+ result = content.split(old_string).join(new_string);
481
+ replaced = count;
482
+ } else {
483
+ result = content.replace(old_string, new_string);
484
+ replaced = 1;
485
+ }
486
+ await import_promises.default.writeFile(filePath, result, "utf-8");
487
+ return {
488
+ content: [{ type: "text", text: `\uAD50\uCCB4 \uC644\uB8CC (${replaced}\uAC1C \uBCC0\uACBD\uB428)` }]
489
+ };
490
+ }
491
+ );
450
492
  }
451
493
  };
452
494
 
@@ -660,6 +702,53 @@ var NotebookTools = class {
660
702
  throw new Error("jupyter\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC124\uCE58 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694: pip install jupyter");
661
703
  }
662
704
  );
705
+ server.tool(
706
+ "notebook_add_cell",
707
+ "\uB178\uD2B8\uBD81\uC5D0 \uC0C8 \uC140 \uCD94\uAC00",
708
+ {
709
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
710
+ cell_type: import_zod3.z.enum(["code", "markdown"]).describe("\uC140 \uD0C0\uC785"),
711
+ source: import_zod3.z.string().describe("\uC140 \uC18C\uC2A4 \uB0B4\uC6A9"),
712
+ position: import_zod3.z.number().optional().describe("\uC0BD\uC785 \uC704\uCE58(0-based). \uC5C6\uC73C\uBA74 \uB9E8 \uB05D\uC5D0 \uCD94\uAC00")
713
+ },
714
+ async ({ path: filePath, cell_type: cellType, source, position }) => {
715
+ const nb = await readNotebook(filePath);
716
+ const newCell = {
717
+ cell_type: cellType,
718
+ source: source.split("\n").map((l, i, arr) => i < arr.length - 1 ? l + "\n" : l),
719
+ metadata: {},
720
+ outputs: cellType === "code" ? [] : void 0,
721
+ execution_count: cellType === "code" ? null : void 0
722
+ };
723
+ let actualIndex;
724
+ if (position === void 0 || position === null) {
725
+ nb.cells.push(newCell);
726
+ actualIndex = nb.cells.length - 1;
727
+ } else {
728
+ nb.cells.splice(position, 0, newCell);
729
+ actualIndex = position;
730
+ }
731
+ await writeNotebook(filePath, nb);
732
+ return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
733
+ }
734
+ );
735
+ server.tool(
736
+ "notebook_delete_cell",
737
+ "\uB178\uD2B8\uBD81 \uD2B9\uC815 \uC140 \uC0AD\uC81C",
738
+ {
739
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
740
+ cell_index: import_zod3.z.number().describe("\uC0AD\uC81C\uD560 \uC140 \uC778\uB371\uC2A4 (0-based)")
741
+ },
742
+ async ({ path: filePath, cell_index }) => {
743
+ const nb = await readNotebook(filePath);
744
+ if (cell_index < 0 || cell_index >= nb.cells.length) {
745
+ throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC140 \uC778\uB371\uC2A4: ${cell_index}`);
746
+ }
747
+ nb.cells.splice(cell_index, 1);
748
+ await writeNotebook(filePath, nb);
749
+ return { content: [{ type: "text", text: `\uC140 \uC0AD\uC81C \uC644\uB8CC (index: ${cell_index})` }] };
750
+ }
751
+ );
663
752
  }
664
753
  };
665
754
 
@@ -219,6 +219,46 @@ ${error.stderr ?? ""}`
219
219
  };
220
220
  }
221
221
  );
222
+ server.tool(
223
+ "edit_block",
224
+ "\uD30C\uC77C\uC758 \uD2B9\uC815 \uD14D\uC2A4\uD2B8 \uBE14\uB85D\uC744 \uC0C8 \uD14D\uC2A4\uD2B8\uB85C \uAD50\uCCB4 (diff \uAE30\uBC18 \uBD80\uBD84 \uC218\uC815)",
225
+ {
226
+ path: import_zod.z.string().describe("\uD30C\uC77C \uACBD\uB85C"),
227
+ old_string: import_zod.z.string().describe("\uAD50\uCCB4\uD560 \uAE30\uC874 \uD14D\uC2A4\uD2B8 (\uC815\uD655\uD788 \uC77C\uCE58\uD574\uC57C \uD568)"),
228
+ new_string: import_zod.z.string().describe("\uC0C8 \uD14D\uC2A4\uD2B8"),
229
+ replace_all: import_zod.z.boolean().optional().default(false).describe("true\uBA74 \uBAA8\uB4E0 \uB9E4\uCE6D \uAD50\uCCB4, false\uBA74 \uCCAB \uBC88\uC9F8\uB9CC")
230
+ },
231
+ async ({ path: filePath, old_string, new_string, replace_all }) => {
232
+ const content = await import_promises.default.readFile(filePath, "utf-8");
233
+ if (!content.includes(old_string)) {
234
+ throw new Error(`old_string\uC744 \uD30C\uC77C\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}`);
235
+ }
236
+ let count = 0;
237
+ let pos = 0;
238
+ while ((pos = content.indexOf(old_string, pos)) !== -1) {
239
+ count++;
240
+ pos += old_string.length;
241
+ }
242
+ if (!replace_all && count > 1) {
243
+ throw new Error(
244
+ `\uB9E4\uCE6D\uC774 ${count}\uAC1C\uC785\uB2C8\uB2E4. replace_all\uC744 true\uB85C \uD558\uAC70\uB098 \uB354 \uB113\uC740 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uD3EC\uD568\uD558\uC138\uC694.`
245
+ );
246
+ }
247
+ let result;
248
+ let replaced;
249
+ if (replace_all) {
250
+ result = content.split(old_string).join(new_string);
251
+ replaced = count;
252
+ } else {
253
+ result = content.replace(old_string, new_string);
254
+ replaced = 1;
255
+ }
256
+ await import_promises.default.writeFile(filePath, result, "utf-8");
257
+ return {
258
+ content: [{ type: "text", text: `\uAD50\uCCB4 \uC644\uB8CC (${replaced}\uAC1C \uBCC0\uACBD\uB428)` }]
259
+ };
260
+ }
261
+ );
222
262
  }
223
263
  };
224
264
 
@@ -432,6 +472,53 @@ var NotebookTools = class {
432
472
  throw new Error("jupyter\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC124\uCE58 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694: pip install jupyter");
433
473
  }
434
474
  );
475
+ server.tool(
476
+ "notebook_add_cell",
477
+ "\uB178\uD2B8\uBD81\uC5D0 \uC0C8 \uC140 \uCD94\uAC00",
478
+ {
479
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
480
+ cell_type: import_zod3.z.enum(["code", "markdown"]).describe("\uC140 \uD0C0\uC785"),
481
+ source: import_zod3.z.string().describe("\uC140 \uC18C\uC2A4 \uB0B4\uC6A9"),
482
+ position: import_zod3.z.number().optional().describe("\uC0BD\uC785 \uC704\uCE58(0-based). \uC5C6\uC73C\uBA74 \uB9E8 \uB05D\uC5D0 \uCD94\uAC00")
483
+ },
484
+ async ({ path: filePath, cell_type: cellType, source, position }) => {
485
+ const nb = await readNotebook(filePath);
486
+ const newCell = {
487
+ cell_type: cellType,
488
+ source: source.split("\n").map((l, i, arr) => i < arr.length - 1 ? l + "\n" : l),
489
+ metadata: {},
490
+ outputs: cellType === "code" ? [] : void 0,
491
+ execution_count: cellType === "code" ? null : void 0
492
+ };
493
+ let actualIndex;
494
+ if (position === void 0 || position === null) {
495
+ nb.cells.push(newCell);
496
+ actualIndex = nb.cells.length - 1;
497
+ } else {
498
+ nb.cells.splice(position, 0, newCell);
499
+ actualIndex = position;
500
+ }
501
+ await writeNotebook(filePath, nb);
502
+ return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
503
+ }
504
+ );
505
+ server.tool(
506
+ "notebook_delete_cell",
507
+ "\uB178\uD2B8\uBD81 \uD2B9\uC815 \uC140 \uC0AD\uC81C",
508
+ {
509
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
510
+ cell_index: import_zod3.z.number().describe("\uC0AD\uC81C\uD560 \uC140 \uC778\uB371\uC2A4 (0-based)")
511
+ },
512
+ async ({ path: filePath, cell_index }) => {
513
+ const nb = await readNotebook(filePath);
514
+ if (cell_index < 0 || cell_index >= nb.cells.length) {
515
+ throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC140 \uC778\uB371\uC2A4: ${cell_index}`);
516
+ }
517
+ nb.cells.splice(cell_index, 1);
518
+ await writeNotebook(filePath, nb);
519
+ return { content: [{ type: "text", text: `\uC140 \uC0AD\uC81C \uC644\uB8CC (index: ${cell_index})` }] };
520
+ }
521
+ );
435
522
  }
436
523
  };
437
524
 
@@ -208,6 +208,46 @@ ${error.stderr ?? ""}`
208
208
  };
209
209
  }
210
210
  );
211
+ server.tool(
212
+ "edit_block",
213
+ "\uD30C\uC77C\uC758 \uD2B9\uC815 \uD14D\uC2A4\uD2B8 \uBE14\uB85D\uC744 \uC0C8 \uD14D\uC2A4\uD2B8\uB85C \uAD50\uCCB4 (diff \uAE30\uBC18 \uBD80\uBD84 \uC218\uC815)",
214
+ {
215
+ path: import_zod.z.string().describe("\uD30C\uC77C \uACBD\uB85C"),
216
+ old_string: import_zod.z.string().describe("\uAD50\uCCB4\uD560 \uAE30\uC874 \uD14D\uC2A4\uD2B8 (\uC815\uD655\uD788 \uC77C\uCE58\uD574\uC57C \uD568)"),
217
+ new_string: import_zod.z.string().describe("\uC0C8 \uD14D\uC2A4\uD2B8"),
218
+ replace_all: import_zod.z.boolean().optional().default(false).describe("true\uBA74 \uBAA8\uB4E0 \uB9E4\uCE6D \uAD50\uCCB4, false\uBA74 \uCCAB \uBC88\uC9F8\uB9CC")
219
+ },
220
+ async ({ path: filePath, old_string, new_string, replace_all }) => {
221
+ const content = await import_promises.default.readFile(filePath, "utf-8");
222
+ if (!content.includes(old_string)) {
223
+ throw new Error(`old_string\uC744 \uD30C\uC77C\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}`);
224
+ }
225
+ let count = 0;
226
+ let pos = 0;
227
+ while ((pos = content.indexOf(old_string, pos)) !== -1) {
228
+ count++;
229
+ pos += old_string.length;
230
+ }
231
+ if (!replace_all && count > 1) {
232
+ throw new Error(
233
+ `\uB9E4\uCE6D\uC774 ${count}\uAC1C\uC785\uB2C8\uB2E4. replace_all\uC744 true\uB85C \uD558\uAC70\uB098 \uB354 \uB113\uC740 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uD3EC\uD568\uD558\uC138\uC694.`
234
+ );
235
+ }
236
+ let result;
237
+ let replaced;
238
+ if (replace_all) {
239
+ result = content.split(old_string).join(new_string);
240
+ replaced = count;
241
+ } else {
242
+ result = content.replace(old_string, new_string);
243
+ replaced = 1;
244
+ }
245
+ await import_promises.default.writeFile(filePath, result, "utf-8");
246
+ return {
247
+ content: [{ type: "text", text: `\uAD50\uCCB4 \uC644\uB8CC (${replaced}\uAC1C \uBCC0\uACBD\uB428)` }]
248
+ };
249
+ }
250
+ );
211
251
  }
212
252
  };
213
253
 
@@ -421,6 +461,53 @@ var NotebookTools = class {
421
461
  throw new Error("jupyter\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC124\uCE58 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694: pip install jupyter");
422
462
  }
423
463
  );
464
+ server.tool(
465
+ "notebook_add_cell",
466
+ "\uB178\uD2B8\uBD81\uC5D0 \uC0C8 \uC140 \uCD94\uAC00",
467
+ {
468
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
469
+ cell_type: import_zod3.z.enum(["code", "markdown"]).describe("\uC140 \uD0C0\uC785"),
470
+ source: import_zod3.z.string().describe("\uC140 \uC18C\uC2A4 \uB0B4\uC6A9"),
471
+ position: import_zod3.z.number().optional().describe("\uC0BD\uC785 \uC704\uCE58(0-based). \uC5C6\uC73C\uBA74 \uB9E8 \uB05D\uC5D0 \uCD94\uAC00")
472
+ },
473
+ async ({ path: filePath, cell_type: cellType, source, position }) => {
474
+ const nb = await readNotebook(filePath);
475
+ const newCell = {
476
+ cell_type: cellType,
477
+ source: source.split("\n").map((l, i, arr) => i < arr.length - 1 ? l + "\n" : l),
478
+ metadata: {},
479
+ outputs: cellType === "code" ? [] : void 0,
480
+ execution_count: cellType === "code" ? null : void 0
481
+ };
482
+ let actualIndex;
483
+ if (position === void 0 || position === null) {
484
+ nb.cells.push(newCell);
485
+ actualIndex = nb.cells.length - 1;
486
+ } else {
487
+ nb.cells.splice(position, 0, newCell);
488
+ actualIndex = position;
489
+ }
490
+ await writeNotebook(filePath, nb);
491
+ return { content: [{ type: "text", text: `\uC140 \uCD94\uAC00 \uC644\uB8CC (index: ${actualIndex})` }] };
492
+ }
493
+ );
494
+ server.tool(
495
+ "notebook_delete_cell",
496
+ "\uB178\uD2B8\uBD81 \uD2B9\uC815 \uC140 \uC0AD\uC81C",
497
+ {
498
+ path: import_zod3.z.string().describe(".ipynb \uD30C\uC77C \uACBD\uB85C"),
499
+ cell_index: import_zod3.z.number().describe("\uC0AD\uC81C\uD560 \uC140 \uC778\uB371\uC2A4 (0-based)")
500
+ },
501
+ async ({ path: filePath, cell_index }) => {
502
+ const nb = await readNotebook(filePath);
503
+ if (cell_index < 0 || cell_index >= nb.cells.length) {
504
+ throw new Error(`\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC140 \uC778\uB371\uC2A4: ${cell_index}`);
505
+ }
506
+ nb.cells.splice(cell_index, 1);
507
+ await writeNotebook(filePath, nb);
508
+ return { content: [{ type: "text", text: `\uC140 \uC0AD\uC81C \uC644\uB8CC (index: ${cell_index})` }] };
509
+ }
510
+ );
424
511
  }
425
512
  };
426
513
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junis",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "One-line device control for AI agents",
5
5
  "bin": {
6
6
  "junis": "dist/cli/index.js"