hocai-connect 0.5.0 → 0.6.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.
Files changed (2) hide show
  1. package/dist/cli.js +185 -6
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -29147,6 +29147,96 @@ var StdioServerTransport = class {
29147
29147
  }
29148
29148
  };
29149
29149
 
29150
+ // apps/mcp/src/daemon.ts
29151
+ var LABEL_MAC = "vn.hocai.connect";
29152
+ var TASK_NAME = "hocai-connect";
29153
+ function daemonPlan(platform2, o) {
29154
+ const log = `${o.home}/.hocai/log/connect.log`;
29155
+ if (platform2 === "darwin") {
29156
+ return {
29157
+ label: LABEL_MAC,
29158
+ file: `${o.home}/Library/LaunchAgents/${LABEL_MAC}.plist`,
29159
+ // `KeepAlive` để launchd dựng lại khi tiến trình sập; `RunAtLoad` để nó
29160
+ // lên ngay lúc đăng nhập, không đợi ai gọi.
29161
+ content: `<?xml version="1.0" encoding="UTF-8"?>
29162
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
29163
+ <plist version="1.0">
29164
+ <dict>
29165
+ <key>Label</key><string>${LABEL_MAC}</string>
29166
+ <key>ProgramArguments</key>
29167
+ <array>
29168
+ <string>${o.node}</string>
29169
+ <string>${o.script}</string>
29170
+ <string>run</string>
29171
+ </array>
29172
+ <key>RunAtLoad</key><true/>
29173
+ <key>KeepAlive</key><true/>
29174
+ <key>StandardOutPath</key><string>${log}</string>
29175
+ <key>StandardErrorPath</key><string>${log}</string>
29176
+ </dict>
29177
+ </plist>
29178
+ `,
29179
+ // `bootout` trước `bootstrap`: nạp đè lên một nhãn đang chạy thì launchd
29180
+ // từ chối, và người dùng nhận "Load failed: 5: Input/output error".
29181
+ install: [
29182
+ ["launchctl", "bootout", `gui/${process.getuid?.() ?? 501}/${LABEL_MAC}`],
29183
+ ["launchctl", "bootstrap", `gui/${process.getuid?.() ?? 501}`, `${o.home}/Library/LaunchAgents/${LABEL_MAC}.plist`]
29184
+ ],
29185
+ uninstall: [["launchctl", "bootout", `gui/${process.getuid?.() ?? 501}/${LABEL_MAC}`]],
29186
+ log
29187
+ };
29188
+ }
29189
+ if (platform2 === "linux") {
29190
+ return {
29191
+ label: TASK_NAME,
29192
+ file: `${o.home}/.config/systemd/user/${TASK_NAME}.service`,
29193
+ content: `[Unit]
29194
+ Description=hocAI connect \u2014 n\u1ED1i Claude tr\xEAn m\xE1y n\xE0y v\xE0o hocAI
29195
+
29196
+ [Service]
29197
+ ExecStart=${o.node} ${o.script} run
29198
+ Restart=always
29199
+ RestartSec=5
29200
+ StandardOutput=append:${log}
29201
+ StandardError=append:${log}
29202
+
29203
+ [Install]
29204
+ WantedBy=default.target
29205
+ `,
29206
+ install: [
29207
+ ["systemctl", "--user", "daemon-reload"],
29208
+ ["systemctl", "--user", "enable", "--now", `${TASK_NAME}.service`]
29209
+ ],
29210
+ uninstall: [["systemctl", "--user", "disable", "--now", `${TASK_NAME}.service`]],
29211
+ log
29212
+ };
29213
+ }
29214
+ if (platform2 === "win32") {
29215
+ return {
29216
+ label: TASK_NAME,
29217
+ file: null,
29218
+ content: null,
29219
+ install: [
29220
+ [
29221
+ "schtasks",
29222
+ "/Create",
29223
+ "/F",
29224
+ "/SC",
29225
+ "ONLOGON",
29226
+ "/TN",
29227
+ TASK_NAME,
29228
+ "/TR",
29229
+ `"${o.node}" "${o.script}" run`
29230
+ ],
29231
+ ["schtasks", "/Run", "/TN", TASK_NAME]
29232
+ ],
29233
+ uninstall: [["schtasks", "/Delete", "/F", "/TN", TASK_NAME]],
29234
+ log
29235
+ };
29236
+ }
29237
+ return { label: TASK_NAME, file: null, content: null, install: [], uninstall: [], log };
29238
+ }
29239
+
29150
29240
  // apps/mcp/src/server.ts
29151
29241
  var import_promises = require("node:fs/promises");
29152
29242
  var import_node_os = require("node:os");
@@ -39587,7 +39677,7 @@ var TOOLS = {
39587
39677
  ],
39588
39678
  WORK: ["Read", "Write", "Edit", "Glob", "Grep"]
39589
39679
  };
39590
- var VERSION = "0.5.0";
39680
+ var VERSION = "0.6.0";
39591
39681
  function claudeArgs(job, o, port) {
39592
39682
  const mcp = {
39593
39683
  mcpServers: {
@@ -40074,6 +40164,9 @@ async function askPortOwner(port, origin) {
40074
40164
  // apps/mcp/src/cli.ts
40075
40165
  var HOCAI_DIR = (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".hocai");
40076
40166
  var CREDS = (0, import_node_path5.join)(HOCAI_DIR, "config.json");
40167
+ var APP_DIR = (0, import_node_path5.join)(HOCAI_DIR, "app");
40168
+ var APP_CLI = (0, import_node_path5.join)(APP_DIR, "cli.js");
40169
+ var LOG_DIR = (0, import_node_path5.join)(HOCAI_DIR, "log");
40077
40170
  var WEB = () => process.env.HOCAI_WEB_URL ?? "http://103.154.62.225:8090";
40078
40171
  var API_DEFAULT = () => process.env.HOCAI_API_URL ?? `${WEB()}/api`;
40079
40172
  var PORT = Number(process.env.MCP_HTTP_PORT ?? 4100);
@@ -40167,6 +40260,37 @@ function claudeBin() {
40167
40260
  return "claude";
40168
40261
  }
40169
40262
  }
40263
+ var plan = () => daemonPlan((0, import_node_os3.platform)(), { node: process.execPath, script: APP_CLI, home: (0, import_node_os3.homedir)() });
40264
+ function chay(cmd, im = false) {
40265
+ try {
40266
+ (0, import_node_child_process3.execFileSync)(cmd[0], cmd.slice(1), { stdio: "ignore", shell: (0, import_node_os3.platform)() === "win32" });
40267
+ return true;
40268
+ } catch {
40269
+ if (!im) return false;
40270
+ return false;
40271
+ }
40272
+ }
40273
+ function caiDichVu() {
40274
+ const p = plan();
40275
+ if (p.install.length === 0) {
40276
+ say("\xB7 H\u1EC7 \u0111i\u1EC1u h\xE0nh n\xE0y ch\u01B0a h\u1ED7 tr\u1EE3 ch\u1EA1y \u1EA9n. D\xF9ng `npx hocai-connect run` v\xE0 \u0111\u1EC3 c\u1EEDa s\u1ED5 m\u1EDF.");
40277
+ return false;
40278
+ }
40279
+ (0, import_node_fs3.mkdirSync)(APP_DIR, { recursive: true });
40280
+ (0, import_node_fs3.mkdirSync)(LOG_DIR, { recursive: true });
40281
+ (0, import_node_fs3.copyFileSync)(process.argv[1], APP_CLI);
40282
+ if (p.file && p.content) {
40283
+ (0, import_node_fs3.mkdirSync)((0, import_node_path5.dirname)(p.file), { recursive: true });
40284
+ (0, import_node_fs3.writeFileSync)(p.file, p.content);
40285
+ }
40286
+ p.install.forEach((c, i) => chay(c, i === 0));
40287
+ return true;
40288
+ }
40289
+ function goDichVu() {
40290
+ const p = plan();
40291
+ for (const c of p.uninstall) chay(c, true);
40292
+ if (p.file) (0, import_node_fs3.rmSync)(p.file, { force: true });
40293
+ }
40170
40294
  async function main() {
40171
40295
  const cmd = process.argv[2];
40172
40296
  if (cmd === "mcp") {
@@ -40174,8 +40298,39 @@ async function main() {
40174
40298
  return;
40175
40299
  }
40176
40300
  if (cmd === "quen") {
40301
+ goDichVu();
40177
40302
  (0, import_node_fs3.rmSync)(CREDS, { force: true });
40178
- say("\u0110\xE3 qu\xEAn token tr\xEAn m\xE1y n\xE0y. Ch\u1EA1y l\u1EA1i `npx hocai-connect` \u0111\u1EC3 k\u1EBFt n\u1ED1i l\u1EA1i.");
40303
+ say("\u0110\xE3 ng\u1EAFt k\u1EBFt n\u1ED1i v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n tr\xEAn m\xE1y n\xE0y.");
40304
+ say("Ch\u1EA1y l\u1EA1i `npx hocai-connect` \u0111\u1EC3 k\u1EBFt n\u1ED1i l\u1EA1i.");
40305
+ return;
40306
+ }
40307
+ if (cmd === "stop") {
40308
+ goDichVu();
40309
+ say("\u0110\xE3 t\u1EAFt v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n. Ch\xECa kho\xE1 v\u1EABn gi\u1EEF \u2014 ch\u1EA1y `npx hocai-connect` l\xE0 n\u1ED1i l\u1EA1i ngay.");
40310
+ return;
40311
+ }
40312
+ if (cmd === "status") {
40313
+ const c = loadCreds();
40314
+ if (!c) {
40315
+ say("M\xE1y n\xE0y ch\u01B0a n\u1ED1i. Ch\u1EA1y `npx hocai-connect`.");
40316
+ process.exit(1);
40317
+ }
40318
+ const info = await askPortOwner(PORT, c.api.replace(/\/api$/, ""));
40319
+ if (!info) {
40320
+ say(`Kh\xF4ng c\xF3 g\xEC tr\u1EA3 l\u1EDDi \u1EDF c\u1ED5ng ${PORT} \u2014 d\u1ECBch v\u1EE5 n\u1EC1n ch\u01B0a ch\u1EA1y.`);
40321
+ process.exit(1);
40322
+ }
40323
+ say(`\u2713 \u0110ang ch\u1EA1y \xB7 ${info.user?.ten ?? "(ch\xECa \u0111\xE3 b\u1ECB thu h\u1ED3i)"} \xB7 b\u1EA3n ${info.phienBan}`);
40324
+ say(` Nh\u1EADt k\xFD: ${plan().log}`);
40325
+ return;
40326
+ }
40327
+ if (cmd === "logs") {
40328
+ const f = plan().log;
40329
+ if (!(0, import_node_fs3.existsSync)(f)) {
40330
+ say(`Ch\u01B0a c\xF3 nh\u1EADt k\xFD n\xE0o \u1EDF ${f}`);
40331
+ return;
40332
+ }
40333
+ say((0, import_node_fs3.readFileSync)(f, "utf8").split("\n").slice(-40).join("\n"));
40179
40334
  return;
40180
40335
  }
40181
40336
  const creds = loadCreds() ?? await login();
@@ -40198,6 +40353,31 @@ async function main() {
40198
40353
  });
40199
40354
  writeDesktop(creds);
40200
40355
  addClaudeCode(creds);
40356
+ if (cmd !== "run") {
40357
+ const xong = caiDichVu();
40358
+ say("");
40359
+ say("\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\u2550\u2550\u2550\u2550\u2550");
40360
+ say(` \u2713 \u0110\xE3 n\u1ED1i m\xE1y \xB7 ${me.ten}`);
40361
+ if (xong) {
40362
+ say(" M\xE1y n\xE0y s\u1EBD T\u1EF0 n\u1ED1i l\u1EA1i m\u1ED7i l\u1EA7n b\u1EA1n b\u1EADt m\xE1y.");
40363
+ say(" Kh\xF4ng c\u1EA7n m\u1EDF Terminal n\u1EEFa, kh\xF4ng c\u1EA7n gi\u1EEF c\u1EEDa s\u1ED5 n\xE0o.");
40364
+ say("");
40365
+ say(" Xem tr\u1EA1ng th\xE1i: npx hocai-connect status");
40366
+ say(" Ng\u1EAFt k\u1EBFt n\u1ED1i: npx hocai-connect stop");
40367
+ }
40368
+ say("\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\u2550\u2550\u2550\u2550\u2550");
40369
+ if (xong) {
40370
+ await new Promise((r) => setTimeout(r, 2500));
40371
+ const info = await askPortOwner(PORT, WEB());
40372
+ if (!info) {
40373
+ say("");
40374
+ say("\u26A0 D\u1ECBch v\u1EE5 \u0111\xE3 c\xE0i nh\u01B0ng ch\u01B0a th\u1EA5y tr\u1EA3 l\u1EDDi \u1EDF c\u1ED5ng " + PORT + ".");
40375
+ say(` Xem nh\u1EADt k\xFD: npx hocai-connect logs`);
40376
+ process.exit(1);
40377
+ }
40378
+ }
40379
+ return;
40380
+ }
40201
40381
  const srv = buildHttpServer({
40202
40382
  token: creds.token,
40203
40383
  origins: (process.env.HOCAI_WEB_ORIGINS ?? `${WEB()},http://localhost:5173,http://localhost:5174`).split(","),
@@ -40216,10 +40396,9 @@ async function main() {
40216
40396
  srv.listen(PORT, "127.0.0.1", () => {
40217
40397
  say("");
40218
40398
  say("\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\u2550\u2550\u2550\u2550\u2550");
40219
- say(` \u2713 \u0110\xE3 k\u1EBFt n\u1ED1i \xB7 ${me.ten}`);
40220
- say(" \u0110\u1EC2 C\u1EECA S\u1ED4 N\xC0Y M\u1EDE trong l\xFAc l\xE0m b\xE0i.");
40221
- say(` Trang hocAI s\u1EBD g\u1ECDi Claude tr\xEAn m\xE1y b\u1EA1n qua c\u1ED5ng ${PORT}.`);
40222
- say(" \u0110\xF3ng c\u1EEDa s\u1ED5 n\xE0y (Ctrl+C) l\xE0 ng\u1EAFt k\u1EBFt n\u1ED1i.");
40399
+ say(` \u2713 \u0110ang ch\u1EA1y \xB7 ${me.ten}`);
40400
+ say(` Trang hocAI g\u1ECDi Claude tr\xEAn m\xE1y b\u1EA1n qua c\u1ED5ng ${PORT}.`);
40401
+ say(" (\u0110\xE2y l\xE0 ch\u1EBF \u0111\u1ED9 ti\u1EC1n c\u1EA3nh \u2014 d\u1ECBch v\u1EE5 n\u1EC1n g\u1ECDi ch\xEDnh \u0111\u01B0\u1EDDng n\xE0y.)");
40223
40402
  say("\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\u2550\u2550\u2550\u2550\u2550");
40224
40403
  });
40225
40404
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hocai-connect",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Nối Claude trên máy học viên vào hocAI, và chạy việc AI ngay tại đó",
5
5
  "bin": {
6
6
  "hocai-connect": "bin.js"