hocai-connect 0.18.0 → 0.18.2

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 +241 -68
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42302,6 +42302,27 @@ var ReorderPlatformSchema = external_exports.object({
42302
42302
  });
42303
42303
  var SetPlatformActiveSchema = external_exports.object({ active: external_exports.boolean() });
42304
42304
 
42305
+ // packages/types/project.ts
42306
+ var ProjectAgentSchema = external_exports.enum(["auto", "claude", "codex"]);
42307
+ var ProjectFolderRoleSchema = external_exports.enum(["SOURCE", "ASSIGNMENTS", "WORK"]);
42308
+ var ProjectBaseSchema = external_exports.object({
42309
+ name: external_exports.string().trim().min(1, "\u0110\u1EB7t t\xEAn cho d\u1EF1 \xE1n").max(120, "T\xEAn d\u1EF1 \xE1n qu\xE1 d\xE0i"),
42310
+ description: external_exports.string().trim().max(1e3, "M\xF4 t\u1EA3 t\u1ED1i \u0111a 1000 k\xFD t\u1EF1").optional(),
42311
+ defaultAgent: ProjectAgentSchema.default("auto")
42312
+ });
42313
+ var UpdateProjectSchema = ProjectBaseSchema.partial();
42314
+ var ProjectFilterSchema = external_exports.object({
42315
+ page: external_exports.coerce.number().int().min(1).default(1),
42316
+ perPage: external_exports.coerce.number().int().min(1).max(100).default(24),
42317
+ q: external_exports.string().trim().max(80).optional(),
42318
+ sort: external_exports.string().max(30).optional(),
42319
+ dir: external_exports.enum(["asc", "desc"]).optional()
42320
+ });
42321
+ var AttachProjectFolderSchema = external_exports.object({
42322
+ folderId: external_exports.string().min(1, "Ch\u1ECDn m\u1ED9t th\u01B0 m\u1EE5c"),
42323
+ role: ProjectFolderRoleSchema
42324
+ });
42325
+
42305
42326
  // packages/types/machine.ts
42306
42327
  var TerminalJobStatusSchema = external_exports.enum(["DONE", "FAILED", "CANCELLED"]);
42307
42328
  var JobLineSchema = external_exports.object({ seq: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER), line: external_exports.string().max(1024 * 1024) });
@@ -42319,6 +42340,8 @@ function terminalJobStatus(line) {
42319
42340
  var CreateRemoteJobSchema = external_exports.object({
42320
42341
  /** MÃ thư mục đã đăng ký, KHÔNG phải đường dẫn — xem apps/mcp/src/folder.ts. */
42321
42342
  folderId: external_exports.string().min(1, "Ch\u1ECDn m\u1ED9t th\u01B0 m\u1EE5c"),
42343
+ projectId: external_exports.string().min(1).optional(),
42344
+ agent: ProjectAgentSchema.optional(),
42322
42345
  prompt: external_exports.string().min(1, "G\xF5 vi\u1EC7c b\u1EA1n mu\u1ED1n m\xE1y l\xE0m").max(4e3, "Y\xEAu c\u1EA7u qu\xE1 d\xE0i \u2014 r\xFAt g\u1ECDn c\xF2n d\u01B0\u1EDBi 4000 k\xFD t\u1EF1")
42323
42346
  });
42324
42347
  var AddFolderSchema = external_exports.object({
@@ -45788,7 +45811,7 @@ var TOOLS = {
45788
45811
  // không phải là lời mời đọc ổ đĩa của học viên.
45789
45812
  CHAT: []
45790
45813
  };
45791
- var VERSION = "0.18.0";
45814
+ var VERSION = "0.18.2";
45792
45815
  function claudeArgs(job, o, port) {
45793
45816
  const mcp = {
45794
45817
  mcpServers: {
@@ -45881,12 +45904,12 @@ function buildHttpServer(o) {
45881
45904
  dir: o.jobsDir ?? (0, import_node_path9.join)((0, import_node_os5.homedir)(), ".hocai", "jobs"),
45882
45905
  maxRunning: o.maxRunning,
45883
45906
  run: (job, onLine) => {
45884
- if (job.kind === "CHAT") {
45907
+ if (job.kind === "CHAT" || job.kind === "WORK" && job.agent) {
45885
45908
  const controller = new AbortController();
45886
45909
  let streamed = "";
45887
45910
  const done = Promise.resolve().then(() => runAi({
45888
45911
  agent: job.agent ?? "claude",
45889
- mode: "chat",
45912
+ mode: job.kind === "CHAT" ? "chat" : "work",
45890
45913
  prompt: job.prompt,
45891
45914
  cwd: job.dir,
45892
45915
  signal: controller.signal,
@@ -46253,6 +46276,7 @@ function handleServerMessage(msg, deps) {
46253
46276
  if (jobs.has(jobId)) return;
46254
46277
  const folderId = typeof msg.folderId === "string" ? msg.folderId : "";
46255
46278
  const prompt = typeof msg.prompt === "string" ? msg.prompt : "";
46279
+ const agent = msg.agent === "codex" ? "codex" : "claude";
46256
46280
  const dir = deps.folders.pathOf(folderId);
46257
46281
  if (!dir) {
46258
46282
  deps.send({
@@ -46263,7 +46287,7 @@ function handleServerMessage(msg, deps) {
46263
46287
  });
46264
46288
  return;
46265
46289
  }
46266
- const job = deps.store.create({ kind: "WORK", dir, prompt });
46290
+ const job = deps.store.create({ kind: "WORK", dir, prompt, agent });
46267
46291
  const record3 = { localId: job.id, finished: false };
46268
46292
  jobs.set(jobId, record3);
46269
46293
  let off = () => {
@@ -47823,6 +47847,83 @@ async function setupMcp(options) {
47823
47847
  return { script, protocolVerified: true, tools, clients };
47824
47848
  }
47825
47849
 
47850
+ // apps/mcp/src/terminal-progress.ts
47851
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
47852
+ var CLEAR_LINE = "\r\x1B[2K";
47853
+ function createTerminalProgress(options = {}) {
47854
+ const stream = options.stream ?? process.stderr;
47855
+ const intervalMs = options.intervalMs ?? 80;
47856
+ const interactive = options.interactive ?? (stream.isTTY === true && process.env.TERM !== "dumb");
47857
+ let timer = null;
47858
+ let frame = 0;
47859
+ let label = "";
47860
+ let active = false;
47861
+ let lastPlain = "";
47862
+ const clearTimer = () => {
47863
+ if (!timer) return;
47864
+ clearInterval(timer);
47865
+ timer = null;
47866
+ };
47867
+ const draw = () => {
47868
+ if (!active || !interactive) return;
47869
+ stream.write(`${CLEAR_LINE}${FRAMES[frame % FRAMES.length]} ${label}`);
47870
+ frame++;
47871
+ };
47872
+ const plain = (icon, text) => {
47873
+ stream.write(`${icon} ${text}
47874
+ `);
47875
+ lastPlain = text;
47876
+ };
47877
+ const stop = () => {
47878
+ if (!active) return;
47879
+ clearTimer();
47880
+ if (interactive) stream.write(CLEAR_LINE);
47881
+ active = false;
47882
+ };
47883
+ const settle = (icon, text) => {
47884
+ const finalLabel = text ?? label;
47885
+ clearTimer();
47886
+ if (interactive) stream.write(`${CLEAR_LINE}${icon} ${finalLabel}
47887
+ `);
47888
+ else plain(icon, finalLabel);
47889
+ active = false;
47890
+ };
47891
+ return {
47892
+ get interactive() {
47893
+ return interactive;
47894
+ },
47895
+ start(text) {
47896
+ stop();
47897
+ label = text;
47898
+ frame = 0;
47899
+ active = true;
47900
+ lastPlain = "";
47901
+ if (interactive) {
47902
+ draw();
47903
+ timer = setInterval(draw, intervalMs);
47904
+ timer.unref?.();
47905
+ } else {
47906
+ plain("\xB7", text);
47907
+ }
47908
+ },
47909
+ update(text) {
47910
+ if (!active) return;
47911
+ label = text;
47912
+ if (interactive) draw();
47913
+ else if (text !== lastPlain) plain("\xB7", text);
47914
+ },
47915
+ succeed(text) {
47916
+ if (!active && !text) return;
47917
+ settle("\u2713", text);
47918
+ },
47919
+ fail(text) {
47920
+ if (!active && !text) return;
47921
+ settle("\u2715", text);
47922
+ },
47923
+ stop
47924
+ };
47925
+ }
47926
+
47826
47927
  // apps/mcp/src/cli.ts
47827
47928
  var HOCAI_DIR = (0, import_node_path17.join)((0, import_node_os11.homedir)(), ".hocai");
47828
47929
  var CREDS = (0, import_node_path17.join)(HOCAI_DIR, "config.json");
@@ -47867,18 +47968,35 @@ async function login() {
47867
47968
  say2(`N\u1EBFu kh\xF4ng th\u1EA5y g\xEC m\u1EDF ra, d\xE1n \u0111\u1ECBa ch\u1EC9 n\xE0y v\xE0o tr\xECnh duy\u1EC7t:
47868
47969
  ${url2}`);
47869
47970
  openBrowser(url2);
47870
- const creds = await l.token;
47871
- await l.close();
47872
- saveCreds({ token: creds.token, api: absoluteApiUrl(creds.api, WEB(), API_DEFAULT()) });
47873
- say2("\u2713 \u0110\xE3 nh\u1EADn token v\xE0 l\u01B0u v\xE0o ~/.hocai/config.json");
47874
- return loadCreds();
47971
+ const progress = createTerminalProgress();
47972
+ progress.start("\u0110ang ch\u1EDD b\u1EA1n cho ph\xE9p trong tr\xECnh duy\u1EC7t\u2026");
47973
+ try {
47974
+ const creds = await l.token;
47975
+ await l.close();
47976
+ saveCreds({ token: creds.token, api: absoluteApiUrl(creds.api, WEB(), API_DEFAULT()) });
47977
+ progress.succeed("\u0110\xE3 \u0111\u0103ng nh\u1EADp v\xE0 l\u01B0u k\u1EBFt n\u1ED1i hocAI.");
47978
+ return loadCreds();
47979
+ } catch (error51) {
47980
+ progress.fail("Ch\u01B0a nh\u1EADn \u0111\u01B0\u1EE3c \u0111\u0103ng nh\u1EADp t\u1EEB tr\xECnh duy\u1EC7t.");
47981
+ await l.close().catch(() => {
47982
+ });
47983
+ throw error51;
47984
+ }
47875
47985
  }
47876
47986
  async function configureMcp(json3 = false) {
47877
47987
  const creds = workCredentials(loadCreds());
47878
- const result = await setupMcp({ root: HOCAI_DIR, source: __filename, authenticated: !!creds });
47988
+ const progress = json3 ? null : createTerminalProgress();
47989
+ progress?.start("\u0110ang c\u1EA5u h\xECnh MCP cho c\xE1c tr\u1EE3 l\xFD AI tr\xEAn m\xE1y\u2026");
47990
+ let result;
47991
+ try {
47992
+ result = await setupMcp({ root: HOCAI_DIR, source: __filename, authenticated: !!creds });
47993
+ } catch (error51) {
47994
+ progress?.fail("C\u1EA5u h\xECnh MCP ch\u01B0a ho\xE0n t\u1EA5t.");
47995
+ throw error51;
47996
+ }
47879
47997
  if (json3) process.stdout.write(JSON.stringify(result) + "\n");
47880
47998
  else {
47881
- say2("\u2713 MCP \u0111\xE3 b\u1EAFt tay v\xE0 kh\xE1m ph\xE1 c\xF4ng c\u1EE5 th\xE0nh c\xF4ng.");
47999
+ progress?.succeed("MCP \u0111\xE3 b\u1EAFt tay v\xE0 kh\xE1m ph\xE1 c\xF4ng c\u1EE5 th\xE0nh c\xF4ng.");
47882
48000
  for (const client of result.clients) {
47883
48001
  const status = { configured: "\u0111\xE3 c\u1EA5u h\xECnh", unchanged: "\u0111\xE3 c\xF3 c\u1EA5u h\xECnh \u0111\xFAng", skipped: "ch\u01B0a th\u1EA5y \u1EE9ng d\u1EE5ng", failed: "ch\u01B0a c\u1EA5u h\xECnh \u0111\u01B0\u1EE3c" }[client.status];
47884
48002
  say2(` ${client.client}: ${status}${client.message ? ` \u2014 ${client.message}` : ""}`);
@@ -47989,31 +48107,41 @@ function caiDichVu() {
47989
48107
  say2("\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.");
47990
48108
  return false;
47991
48109
  }
47992
- (0, import_node_fs10.mkdirSync)(APP_DIR, { recursive: true });
47993
- (0, import_node_fs10.mkdirSync)(LOG_DIR, { recursive: true });
47994
- (0, import_node_fs10.copyFileSync)(__filename, APP_CLI);
47995
- if (p.file && p.content) {
47996
- (0, import_node_fs10.mkdirSync)((0, import_node_path17.dirname)(p.file), { recursive: true });
47997
- (0, import_node_fs10.writeFileSync)(p.file, p.content);
47998
- }
47999
- p.install.forEach((c, i) => {
48000
- if (i === 0) {
48001
- chay(c, true);
48002
- if ((0, import_node_os11.platform)() === "darwin") {
48003
- const nhan = `gui/${process.getuid?.() ?? 501}/${p.label}`;
48004
- for (let lan = 0; lan < 20; lan++) {
48005
- if (!chay(["launchctl", "print", nhan], true)) break;
48006
- nghi(250);
48110
+ const progress = createTerminalProgress();
48111
+ progress.start("\u0110ang chu\u1EA9n b\u1ECB d\u1ECBch v\u1EE5 n\u1EC1n\u2026");
48112
+ try {
48113
+ (0, import_node_fs10.mkdirSync)(APP_DIR, { recursive: true });
48114
+ (0, import_node_fs10.mkdirSync)(LOG_DIR, { recursive: true });
48115
+ (0, import_node_fs10.copyFileSync)(__filename, APP_CLI);
48116
+ progress.update("\u0110ang ghi c\u1EA5u h\xECnh d\u1ECBch v\u1EE5 n\u1EC1n\u2026");
48117
+ if (p.file && p.content) {
48118
+ (0, import_node_fs10.mkdirSync)((0, import_node_path17.dirname)(p.file), { recursive: true });
48119
+ (0, import_node_fs10.writeFileSync)(p.file, p.content);
48120
+ }
48121
+ progress.update("\u0110ang \u0111\u0103ng k\xFD d\u1ECBch v\u1EE5 v\u1EDBi h\u1EC7 \u0111i\u1EC1u h\xE0nh\u2026");
48122
+ p.install.forEach((c, i) => {
48123
+ if (i === 0) {
48124
+ chay(c, true);
48125
+ if ((0, import_node_os11.platform)() === "darwin") {
48126
+ const nhan = `gui/${process.getuid?.() ?? 501}/${p.label}`;
48127
+ for (let lan = 0; lan < 20; lan++) {
48128
+ if (!chay(["launchctl", "print", nhan], true)) break;
48129
+ nghi(250);
48130
+ }
48007
48131
  }
48132
+ return;
48008
48133
  }
48009
- return;
48010
- }
48011
- for (let lan = 0; lan < 5; lan++) {
48012
- if (chay(c, true)) return;
48013
- nghi(700);
48014
- }
48015
- });
48016
- return true;
48134
+ for (let lan = 0; lan < 5; lan++) {
48135
+ if (chay(c, true)) return;
48136
+ nghi(700);
48137
+ }
48138
+ });
48139
+ progress.succeed("D\u1ECBch v\u1EE5 n\u1EC1n \u0111\xE3 \u0111\u01B0\u1EE3c c\xE0i v\xE0 b\u1EADt.");
48140
+ return true;
48141
+ } catch (error51) {
48142
+ progress.fail("Kh\xF4ng c\xE0i \u0111\u01B0\u1EE3c d\u1ECBch v\u1EE5 n\u1EC1n.");
48143
+ throw error51;
48144
+ }
48017
48145
  }
48018
48146
  function goDichVu() {
48019
48147
  const p = plan();
@@ -48058,19 +48186,41 @@ async function main() {
48058
48186
  if (cmd === "sync-reports") {
48059
48187
  const flags = process.argv.slice(3);
48060
48188
  if (flags.some((flag) => flag !== "--json")) throw new Error("sync-reports ch\u1EC9 nh\u1EADn --json.");
48189
+ const json3 = flags.includes("--json");
48061
48190
  const creds2 = workCredentials(loadCreds());
48062
48191
  if (!creds2) throw new Error("Ch\u01B0a n\u1ED1i hocAI. Ch\u1EA1y hocai-connect tr\u01B0\u1EDBc.");
48063
- const result = await createWorkOutbox({ ...creds2, root: (0, import_node_path17.join)(HOCAI_DIR, "work-outbox") }).flush();
48064
- if (flags.includes("--json")) process.stdout.write(JSON.stringify(result) + "\n");
48065
- else say2(`\u0110\xE3 x\xE1c nh\u1EADn ${result.sent} b\xE1o c\xE1o; c\xF2n ch\u1EDD ${result.pending}.${result.lastError ? ` ${result.lastError}` : ""}`);
48192
+ const progress = json3 ? null : createTerminalProgress();
48193
+ progress?.start("\u0110ang \u0111\u1ED3ng b\u1ED9 c\xE1c b\xE1o c\xE1o c\xF2n ch\u1EDD\u2026");
48194
+ let result;
48195
+ try {
48196
+ result = await createWorkOutbox({ ...creds2, root: (0, import_node_path17.join)(HOCAI_DIR, "work-outbox") }).flush();
48197
+ } catch (error51) {
48198
+ progress?.fail("\u0110\u1ED3ng b\u1ED9 b\xE1o c\xE1o ch\u01B0a ho\xE0n t\u1EA5t.");
48199
+ throw error51;
48200
+ }
48201
+ if (json3) process.stdout.write(JSON.stringify(result) + "\n");
48202
+ else {
48203
+ progress?.succeed(`\u0110\xE3 \u0111\u1ED3ng b\u1ED9 b\xE1o c\xE1o \xB7 g\u1EEDi ${result.sent} \xB7 c\xF2n ${result.pending}`);
48204
+ if (result.lastError) say2(` ${result.lastError}`);
48205
+ }
48066
48206
  process.exitCode = result.pending ? 2 : 0;
48067
48207
  return;
48068
48208
  }
48069
48209
  if (cmd === "report" || cmd === "doctor") {
48070
48210
  const flags = process.argv.slice(3);
48071
48211
  if (flags.some((flag) => flag !== "--json")) throw new Error("L\u1EC7nh report ch\u1EC9 nh\u1EADn tu\u1EF3 ch\u1ECDn --json.");
48072
- const report = await collectMachineReport({ helperVersion: VERSION });
48073
- process.stdout.write((flags.includes("--json") ? JSON.stringify(report, null, 2) : formatMachineReport(report)) + "\n");
48212
+ const json3 = flags.includes("--json");
48213
+ const progress = json3 ? null : createTerminalProgress();
48214
+ progress?.start("\u0110ang d\xF2 Claude, Codex v\xE0 tr\u1EA1ng th\xE1i m\xE1y\u2026");
48215
+ let report;
48216
+ try {
48217
+ report = await collectMachineReport({ helperVersion: VERSION });
48218
+ progress?.succeed("\u0110\xE3 d\xF2 xong tr\u1EA1ng th\xE1i m\xE1y v\xE0 c\xE1c tr\u1EE3 l\xFD AI.");
48219
+ } catch (error51) {
48220
+ progress?.fail("Kh\xF4ng d\xF2 xong tr\u1EA1ng th\xE1i m\xE1y.");
48221
+ throw error51;
48222
+ }
48223
+ process.stdout.write((json3 ? JSON.stringify(report, null, 2) : formatMachineReport(report)) + "\n");
48074
48224
  return;
48075
48225
  }
48076
48226
  if (cmd === "enable-usage") {
@@ -48111,25 +48261,37 @@ async function main() {
48111
48261
  return;
48112
48262
  }
48113
48263
  if (cmd === "quen") {
48264
+ const progress = createTerminalProgress();
48265
+ progress.start("\u0110ang ng\u1EAFt m\xE1y n\xE0y kh\u1ECFi hocAI\u2026");
48114
48266
  goDichVu();
48115
48267
  (0, import_node_fs10.rmSync)(CREDS, { force: true });
48116
- say2("\u0110\xE3 ng\u1EAFt k\u1EBFt n\u1ED1i v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n tr\xEAn m\xE1y n\xE0y.");
48268
+ progress.succeed("\u0110\xE3 ng\u1EAFt k\u1EBFt n\u1ED1i v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n tr\xEAn m\xE1y n\xE0y.");
48117
48269
  say2("Ch\u1EA1y l\u1EA1i `npx hocai-connect` \u0111\u1EC3 k\u1EBFt n\u1ED1i l\u1EA1i.");
48118
48270
  return;
48119
48271
  }
48120
48272
  if (cmd === "update") {
48121
48273
  const stored = loadCreds();
48122
- const [moi, running] = await Promise.all([
48123
- latestVersion(),
48124
- stored ? askPortOwner(PORT, stored.api.replace(/\/api$/, "")) : Promise.resolve(null)
48125
- ]);
48274
+ const progress = createTerminalProgress();
48275
+ progress.start("\u0110ang ki\u1EC3m tra phi\xEAn b\u1EA3n m\u1EDBi v\xE0 d\u1ECBch v\u1EE5 \u0111ang ch\u1EA1y\u2026");
48276
+ let moi;
48277
+ let running;
48278
+ try {
48279
+ ;
48280
+ [moi, running] = await Promise.all([
48281
+ latestVersion(),
48282
+ stored ? askPortOwner(PORT, stored.api.replace(/\/api$/, "")) : Promise.resolve(null)
48283
+ ]);
48284
+ } catch (error51) {
48285
+ progress.fail("Kh\xF4ng ki\u1EC3m tra \u0111\u01B0\u1EE3c phi\xEAn b\u1EA3n hi\u1EC7n t\u1EA1i.");
48286
+ throw error51;
48287
+ }
48126
48288
  const action = planHelperUpdate(VERSION, moi, running?.phienBan ?? null);
48127
48289
  if (action === "current") {
48128
- say2(`\u2713 D\u1ECBch v\u1EE5 \u0111ang ch\u1EA1y b\u1EA3n ${running?.phienBan}; kh\xF4ng c\u1EA7n c\xE0i l\u1EA1i.`);
48290
+ progress.succeed(`D\u1ECBch v\u1EE5 \u0111ang ch\u1EA1y b\u1EA3n ${running?.phienBan}; kh\xF4ng c\u1EA7n c\xE0i l\u1EA1i.`);
48129
48291
  return;
48130
48292
  }
48131
48293
  if (action === "download" && moi) {
48132
- say2(`C\xF3 b\u1EA3n m\u1EDBi: ${VERSION} \u2192 ${moi}. \u0110ang c\u1EADp nh\u1EADt\u2026`);
48294
+ progress.succeed(`C\xF3 b\u1EA3n m\u1EDBi ${VERSION} \u2192 ${moi}. B\u1EAFt \u0111\u1EA7u c\u1EADp nh\u1EADt.`);
48133
48295
  say2("");
48134
48296
  (0, import_node_fs10.mkdirSync)(NPM_CACHE, { recursive: true });
48135
48297
  const r = (0, import_node_child_process9.spawnSync)("npx", updateNpxArgs(moi, NPM_CACHE), {
@@ -48138,12 +48300,15 @@ async function main() {
48138
48300
  });
48139
48301
  process.exit(r.status ?? 1);
48140
48302
  }
48141
- say2(`\u0110ang c\xE0i bundle ${VERSION} v\xE0o d\u1ECBch v\u1EE5 (b\u1EA3n \u0111ang ch\u1EA1y: ${running?.phienBan ?? "ch\u01B0a c\xF3"}).`);
48303
+ progress.succeed(`\u0110\xE3 ch\u1ECDn bundle ${VERSION}; \u0111ang c\xE0i v\xE0o d\u1ECBch v\u1EE5 n\u1EC1n.`);
48142
48304
  cmd = void 0;
48143
48305
  }
48144
48306
  if (cmd === "stop") {
48307
+ const progress = createTerminalProgress();
48308
+ progress.start("\u0110ang t\u1EAFt v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n\u2026");
48145
48309
  goDichVu();
48146
- say2("\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.");
48310
+ progress.succeed("\u0110\xE3 t\u1EAFt v\xE0 g\u1EE1 d\u1ECBch v\u1EE5 n\u1EC1n.");
48311
+ say2("Ch\xECa kho\xE1 v\u1EABn gi\u1EEF \u2014 ch\u1EA1y `npx hocai-connect` l\xE0 n\u1ED1i l\u1EA1i ngay.");
48147
48312
  return;
48148
48313
  }
48149
48314
  if (cmd === "status") {
@@ -48152,15 +48317,19 @@ async function main() {
48152
48317
  say2("M\xE1y n\xE0y ch\u01B0a n\u1ED1i. Ch\u1EA1y `npx hocai-connect`.");
48153
48318
  process.exit(1);
48154
48319
  }
48155
- const info = await askPortOwner(PORT, c.api.replace(/\/api$/, ""));
48320
+ const progress = createTerminalProgress();
48321
+ progress.start("\u0110ang ki\u1EC3m tra d\u1ECBch v\u1EE5 v\xE0 phi\xEAn b\u1EA3n hocai-connect\u2026");
48322
+ const [info, moi] = await Promise.all([
48323
+ askPortOwner(PORT, c.api.replace(/\/api$/, "")),
48324
+ latestVersion()
48325
+ ]);
48156
48326
  if (!info) {
48157
- say2(`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.`);
48327
+ progress.fail(`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.`);
48158
48328
  process.exit(1);
48159
48329
  }
48160
- say2(`\u2713 \u0110ang ch\u1EA1y \xB7 ${info.user?.ten ?? "(ch\xECa \u0111\xE3 b\u1ECB thu h\u1ED3i)"} \xB7 b\u1EA3n ${info.phienBan}`);
48330
+ progress.succeed(`\u0110ang ch\u1EA1y \xB7 ${info.user?.ten ?? "(ch\xECa \u0111\xE3 b\u1ECB thu h\u1ED3i)"} \xB7 b\u1EA3n ${info.phienBan}`);
48161
48331
  say2(` Nh\u1EADt k\xFD: ${plan().log}`);
48162
48332
  warnLines(doPerms());
48163
- const moi = await latestVersion();
48164
48333
  if (moi && info.phienBan && newerVersion(moi, info.phienBan)) {
48165
48334
  say2("");
48166
48335
  say2(` C\xF3 b\u1EA3n m\u1EDBi ${moi}. C\u1EADp nh\u1EADt: npx hocai-connect@latest update`);
@@ -48189,18 +48358,20 @@ async function main() {
48189
48358
  }
48190
48359
  const creds = loadCreds() ?? await login();
48191
48360
  process.env.HOCAI_API_URL = creds.api;
48361
+ const identityProgress = cmd === "run" ? null : createTerminalProgress();
48362
+ identityProgress?.start("\u0110ang x\xE1c minh t\xE0i kho\u1EA3n hocAI\u2026");
48192
48363
  const me = await fetchIdentity(creds.api, creds.token).catch(() => {
48193
- say2("");
48194
- say2("Kh\xF4ng n\u1ED1i \u0111\u01B0\u1EE3c t\u1EDBi hocAI \u0111\u1EC3 ki\u1EC3m tra t\xE0i kho\u1EA3n. Ki\u1EC3m tra m\u1EA1ng r\u1ED3i ch\u1EA1y l\u1EA1i \u2014");
48195
- say2("kh\xF4ng c\xF3 m\u1EA1ng th\xEC trang hocAI c\u0169ng kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c, n\xEAn ch\u1EA1y ti\u1EBFp c\u0169ng kh\xF4ng l\xE0m g\xEC \u0111\u01B0\u1EE3c.");
48364
+ identityProgress?.fail("Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c t\u1EDBi hocAI \u0111\u1EC3 ki\u1EC3m tra t\xE0i kho\u1EA3n.");
48365
+ say2("Ki\u1EC3m tra m\u1EA1ng r\u1ED3i ch\u1EA1y l\u1EA1i \u2014 kh\xF4ng c\xF3 m\u1EA1ng th\xEC trang hocAI c\u0169ng kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c.");
48196
48366
  process.exit(1);
48197
48367
  });
48198
48368
  if (!me) {
48199
- say2("");
48200
- say2("Token tr\xEAn m\xE1y n\xE0y kh\xF4ng d\xF9ng \u0111\u01B0\u1EE3c n\u1EEFa \u2014 \u0111\xE3 b\u1ECB thu h\u1ED3i, ho\u1EB7c t\xE0i kho\u1EA3n \u0111\xE3 b\u1ECB kho\xE1.");
48369
+ identityProgress?.fail("Token tr\xEAn m\xE1y n\xE0y kh\xF4ng d\xF9ng \u0111\u01B0\u1EE3c n\u1EEFa.");
48370
+ say2("Token c\xF3 th\u1EC3 \u0111\xE3 b\u1ECB thu h\u1ED3i, ho\u1EB7c t\xE0i kho\u1EA3n \u0111\xE3 b\u1ECB kho\xE1.");
48201
48371
  say2("Ch\u1EA1y `npx hocai-connect quen` r\u1ED3i ch\u1EA1y l\u1EA1i `npx hocai-connect` \u0111\u1EC3 k\u1EBFt n\u1ED1i l\u1EA1i.");
48202
48372
  process.exit(1);
48203
48373
  }
48374
+ identityProgress?.succeed(`\u0110\xE3 x\xE1c minh t\xE0i kho\u1EA3n \xB7 ${me.ten}`);
48204
48375
  const identity = createIdentityCache({
48205
48376
  fetchIdentity: () => fetchIdentity(creds.api, creds.token),
48206
48377
  seed: me,
@@ -48210,6 +48381,19 @@ async function main() {
48210
48381
  if (cmd !== "run") {
48211
48382
  const xong = caiDichVu();
48212
48383
  const sanSang = doTroLy();
48384
+ if (xong) {
48385
+ const startupProgress = createTerminalProgress();
48386
+ startupProgress.start("\u0110ang ch\u1EDD d\u1ECBch v\u1EE5 n\u1EC1n kh\u1EDFi \u0111\u1ED9ng v\xE0 ph\u1EA3n h\u1ED3i\u2026");
48387
+ await new Promise((r) => setTimeout(r, 2500));
48388
+ const info = await askPortOwner(PORT, WEB());
48389
+ if (!info) {
48390
+ startupProgress.fail("D\u1ECBch v\u1EE5 \u0111\xE3 c\xE0i nh\u01B0ng ch\u01B0a ph\u1EA3n h\u1ED3i \u1EDF c\u1ED5ng " + PORT + ".");
48391
+ say2(` Xem nh\u1EADt k\xFD: npx hocai-connect logs`);
48392
+ process.exit(1);
48393
+ }
48394
+ startupProgress.succeed(`D\u1ECBch v\u1EE5 n\u1EC1n \u0111\xE3 s\u1EB5n s\xE0ng \xB7 b\u1EA3n ${info.phienBan}`);
48395
+ warnLines(doPerms());
48396
+ }
48213
48397
  say2("");
48214
48398
  say2("\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");
48215
48399
  say2(sanSang ? ` \u2713 \u0110\xE3 n\u1ED1i m\xE1y \xB7 ${me.ten}` : ` \u2713 \u0110\xE3 n\u1ED1i m\xE1y \xB7 ${me.ten} \u2014 nh\u01B0ng xem c\u1EA3nh b\xE1o \u1EDF tr\xEAn`);
@@ -48221,17 +48405,6 @@ async function main() {
48221
48405
  say2(" Ng\u1EAFt k\u1EBFt n\u1ED1i: npx hocai-connect stop");
48222
48406
  }
48223
48407
  say2("\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");
48224
- if (xong) {
48225
- await new Promise((r) => setTimeout(r, 2500));
48226
- const info = await askPortOwner(PORT, WEB());
48227
- if (!info) {
48228
- say2("");
48229
- say2("\u26A0 D\u1ECBch v\u1EE5 \u0111\xE3 c\xE0i nh\u01B0ng ch\u01B0a th\u1EA5y tr\u1EA3 l\u1EDDi \u1EDF c\u1ED5ng " + PORT + ".");
48230
- say2(` Xem nh\u1EADt k\xFD: npx hocai-connect logs`);
48231
- process.exit(1);
48232
- }
48233
- warnLines(doPerms());
48234
- }
48235
48408
  if (quyen && quyen.fixCache.length) {
48236
48409
  say2("");
48237
48410
  say2("C\xF2n m\u1ED9t vi\u1EC7c: cache npm tr\xEAn m\xE1y n\xE0y c\xF3 t\u1EC7p thu\u1ED9c ng\u01B0\u1EDDi d\xF9ng kh\xE1c,");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hocai-connect",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Nối AI trên máy vào hocAI, chat, làm việc và gửi báo cáo qua MCP",
5
5
  "bin": {
6
6
  "hocai-connect": "bin.js"