@greatlhd/ailo-desktop 1.0.0 → 1.0.1

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.js CHANGED
@@ -1,12 +1,26 @@
1
1
  /**
2
2
  * CLI init command for ailo-desktop.
3
- * Usage: ailo-desktop init [--defaults]
3
+ * Usage: ailo-desktop init [--defaults] [--config-dir <path>]
4
4
  */
5
5
  import { createInterface } from "readline";
6
- import { join } from "path";
6
+ import { join, resolve } from "path";
7
+ import { mkdirSync } from "fs";
7
8
  import { writeConfig } from "@greatlhd/ailo-endpoint-sdk";
8
9
  import { CONFIG_FILENAME } from "./constants.js";
9
- const CONFIG_PATH = join(process.cwd(), CONFIG_FILENAME);
10
+ function parseCliArgs() {
11
+ const args = process.argv.slice(2);
12
+ let useDefaults = false;
13
+ let configDir = process.cwd();
14
+ for (let i = 0; i < args.length; i++) {
15
+ if (args[i] === "--defaults") {
16
+ useDefaults = true;
17
+ }
18
+ else if ((args[i] === "--config-dir" || args[i] === "-c") && args[i + 1]) {
19
+ configDir = resolve(args[++i]);
20
+ }
21
+ }
22
+ return { useDefaults, configDir };
23
+ }
10
24
  async function prompt(question, defaultVal = "") {
11
25
  const rl = createInterface({ input: process.stdin, output: process.stdout });
12
26
  return new Promise((resolve) => {
@@ -17,8 +31,14 @@ async function prompt(question, defaultVal = "") {
17
31
  });
18
32
  });
19
33
  }
20
- export async function runInit(useDefaults = false) {
34
+ export async function runInit(useDefaultsArg, configDirArg) {
35
+ const { useDefaults, configDir } = useDefaultsArg !== undefined && configDirArg !== undefined
36
+ ? { useDefaults: useDefaultsArg, configDir: configDirArg }
37
+ : parseCliArgs();
21
38
  console.log("=== Ailo Desktop 初始化 ===\n");
39
+ console.log(`配置目录: ${configDir}\n`);
40
+ mkdirSync(configDir, { recursive: true });
41
+ const configPath = join(configDir, CONFIG_FILENAME);
22
42
  const wsUrl = useDefaults ? "ws://127.0.0.1:19800/ws" : await prompt("Ailo WebSocket URL", "ws://127.0.0.1:19800/ws");
23
43
  const apiKey = useDefaults ? "" : await prompt("API Key (留空稍后配置)");
24
44
  const endpointId = useDefaults ? "desktop-01" : await prompt("端点 ID", "desktop-01");
@@ -29,8 +49,11 @@ export async function runInit(useDefaults = false) {
29
49
  endpointId,
30
50
  },
31
51
  };
32
- writeConfig(CONFIG_PATH, config);
33
- console.log(`\n已写入 ${CONFIG_PATH}`);
34
- console.log("\n初始化完成!运行 ailo-desktop 启动桌面端点。");
35
- console.log("配置界面端口:启动时用 --port <端口> 指定,或运行后在控制台按提示输入。");
52
+ writeConfig(configPath, config);
53
+ console.log(`\n已写入 ${configPath}`);
54
+ console.log("\n初始化完成!运行以下命令启动桌面端点:");
55
+ console.log(` ailo-desktop --config-dir ${configDir} --port 3000`);
56
+ }
57
+ if (import.meta.url === `file://${process.argv[1]}`) {
58
+ runInit().catch(console.error);
36
59
  }
@@ -1,6 +1,6 @@
1
1
  import { createServer } from "http";
2
2
  import { readFileSync, existsSync } from "fs";
3
- import { join, resolve, dirname, basename } from "path";
3
+ import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { spawnSync } from "child_process";
6
6
  import { WebSocketServer } from "ws";
@@ -165,10 +165,8 @@ export function startConfigServer(deps) {
165
165
  return json(res, await runEnvInstall());
166
166
  if (path === "/api/tools" && req.method === "GET")
167
167
  return json(res, await getReportedTools(deps));
168
- if (path === "/api/blueprint" && req.method === "GET")
169
- return json(res, await getBlueprintInfo(deps));
170
- if (path === "/api/blueprints" && req.method === "GET")
171
- return json(res, await getAllBlueprintsInfo(deps));
168
+ if (path === "/api/skills" && req.method === "GET")
169
+ return json(res, await getReportedSkills(deps));
172
170
  // Ailo 连接配置(仅当 configPath 存在时,供桌面端在界面填写并保存)
173
171
  if (deps.configPath) {
174
172
  if (path === "/api/connection" && req.method === "GET")
@@ -176,13 +174,6 @@ export function startConfigServer(deps) {
176
174
  if (path === "/api/connection" && req.method === "POST")
177
175
  return json(res, await saveConnectionConfig(deps.configPath, await body(req), deps.onConnectionConfigSaved));
178
176
  }
179
- // 邮件配置
180
- if (deps.configPath) {
181
- if (path === "/api/email/config" && req.method === "GET")
182
- return json(res, getEmailConfig(deps.configPath, deps.getEmailStatus));
183
- if (path === "/api/email/config" && req.method === "POST")
184
- return json(res, await saveEmailConfig(deps.configPath, await body(req), deps.onEmailConfigSaved));
185
- }
186
177
  // 飞书配置
187
178
  if (deps.configPath) {
188
179
  if (path === "/api/feishu/config" && req.method === "GET")
@@ -190,20 +181,6 @@ export function startConfigServer(deps) {
190
181
  if (path === "/api/feishu/config" && req.method === "POST")
191
182
  return json(res, await savePlatformConfig(deps.configPath, "feishu", ["appId", "appSecret"], await body(req), deps.onFeishuConfigSaved));
192
183
  }
193
- // 钉钉配置
194
- if (deps.configPath) {
195
- if (path === "/api/dingtalk/config" && req.method === "GET")
196
- return json(res, getPlatformConfig(deps.configPath, "dingtalk", ["clientId", "clientSecret"], deps.getDingtalkStatus));
197
- if (path === "/api/dingtalk/config" && req.method === "POST")
198
- return json(res, await savePlatformConfig(deps.configPath, "dingtalk", ["clientId", "clientSecret"], await body(req), deps.onDingtalkConfigSaved));
199
- }
200
- // QQ 配置
201
- if (deps.configPath) {
202
- if (path === "/api/qq/config" && req.method === "GET")
203
- return json(res, getPlatformConfig(deps.configPath, "qq", ["appId", "appSecret", "apiBase"], deps.getQQStatus));
204
- if (path === "/api/qq/config" && req.method === "POST")
205
- return json(res, await savePlatformConfig(deps.configPath, "qq", ["appId", "appSecret", "apiBase"], await body(req), deps.onQQConfigSaved));
206
- }
207
184
  // MCP
208
185
  if (path === "/api/mcp" && req.method === "GET")
209
186
  return json(res, getMCPList(deps.mcpManager));
@@ -392,114 +369,28 @@ async function runEnvInstall() {
392
369
  }
393
370
  return { installed, errors };
394
371
  }
395
- /** 从蓝图正文中解析 tools 列表(仅提取 name、description) */
396
- function parseBlueprintTools(md) {
397
- const tools = [];
398
- const frontmatterMatch = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
399
- const yaml = frontmatterMatch?.[1] ?? "";
400
- let inToolsSection = false;
401
- let current = null;
402
- for (const line of yaml.split(/\r?\n/)) {
403
- if (line.trim() === "tools:") {
404
- inToolsSection = true;
405
- continue;
406
- }
407
- if (!inToolsSection)
408
- continue;
409
- const itemMatch = line.match(/^\s+-\s+name:\s*(.+)$/);
410
- const descMatch = line.match(/^\s+description:\s*(.+)$/);
411
- if (itemMatch) {
412
- if (current)
413
- tools.push(current);
414
- current = { name: itemMatch[1].trim(), description: "" };
415
- }
416
- else if (current && descMatch) {
417
- current.description = descMatch[1].trim();
418
- }
419
- }
420
- if (current)
421
- tools.push(current);
422
- return tools;
423
- }
424
- async function fetchBlueprintContent(url, localFallbackPath) {
425
- if (url.startsWith("file://")) {
426
- const filePath = url.slice(7);
427
- return readFileSync(filePath, "utf-8");
428
- }
429
- try {
430
- const res = await fetch(url);
431
- if (!res.ok)
432
- throw new Error(`Blueprint fetch failed: ${res.status}`);
433
- return res.text();
434
- }
435
- catch (e) {
436
- if (localFallbackPath && existsSync(localFallbackPath)) {
437
- return readFileSync(localFallbackPath, "utf-8");
438
- }
439
- throw e;
440
- }
441
- }
442
372
  async function getReportedTools(deps) {
443
373
  const out = [];
444
- if (deps.blueprintUrl) {
445
- try {
446
- const localPath = deps.blueprintLocalPath
447
- ? resolve(process.cwd(), deps.blueprintLocalPath)
448
- : undefined;
449
- const md = await fetchBlueprintContent(deps.blueprintUrl, localPath);
450
- for (const t of parseBlueprintTools(md)) {
451
- out.push({ name: t.name, description: t.description, source: "builtin" });
452
- }
453
- }
454
- catch (e) {
455
- console.error("[config] 解析蓝图工具失败:", e);
374
+ // 内置工具
375
+ if (deps.getEndpointTools) {
376
+ for (const t of deps.getEndpointTools()) {
377
+ out.push({ name: t.name, description: t.description, source: "builtin" });
456
378
  }
457
379
  }
380
+ // MCP 工具
458
381
  for (const t of deps.mcpManager.getAllPrivateTools()) {
459
382
  out.push({ name: t.name, description: t.description ?? "", source: "mcp" });
460
383
  }
461
384
  return out;
462
385
  }
463
- async function getBlueprintInfo(deps) {
464
- if (!deps.blueprintUrl)
465
- return { url: null, content: null };
466
- try {
467
- const localPath = deps.blueprintLocalPath ? resolve(process.cwd(), deps.blueprintLocalPath) : undefined;
468
- const content = await fetchBlueprintContent(deps.blueprintUrl, localPath);
469
- return { url: deps.blueprintUrl, content };
470
- }
471
- catch {
472
- return { url: deps.blueprintUrl, content: null };
473
- }
474
- }
475
- async function getAllBlueprintsInfo(deps) {
476
- const paths = deps.getBlueprintPaths?.() ?? [];
477
- if (paths.length === 0) {
478
- // fallback:如果没有传 getBlueprintPaths,至少返回主蓝图
479
- if (deps.blueprintUrl) {
480
- try {
481
- const localPath = deps.blueprintLocalPath ? resolve(process.cwd(), deps.blueprintLocalPath) : undefined;
482
- const content = await fetchBlueprintContent(deps.blueprintUrl, localPath);
483
- const name = basename(deps.blueprintLocalPath ?? deps.blueprintUrl, ".blueprint.md");
484
- return [{ name, path: deps.blueprintUrl, content }];
485
- }
486
- catch {
487
- return [];
488
- }
386
+ async function getReportedSkills(deps) {
387
+ const out = [];
388
+ if (deps.getEndpointSkills) {
389
+ for (const s of deps.getEndpointSkills()) {
390
+ out.push({ name: s.name, description: s.description, source: "builtin" });
489
391
  }
490
- return [];
491
392
  }
492
- return Promise.all(paths.map(async (p) => {
493
- const filePath = p.startsWith("file://") ? p.slice(7) : p;
494
- const name = basename(filePath, ".blueprint.md");
495
- try {
496
- const content = readFileSync(filePath, "utf-8");
497
- return { name, path: p, content };
498
- }
499
- catch {
500
- return { name, path: p, content: null };
501
- }
502
- }));
393
+ return out;
503
394
  }
504
395
  function getMCPList(mgr) {
505
396
  const configs = mgr.getConfigs();
@@ -585,45 +476,6 @@ async function saveConnectionConfig(configPath, bodyStr, onSaved) {
585
476
  return { ok: false, error: errMsg(e) };
586
477
  }
587
478
  }
588
- function getEmailConfig(configPath, getStatus) {
589
- const cfg = readConfig(configPath);
590
- const email = (cfg.email ?? {});
591
- const status = getStatus?.() ?? { configured: false, running: false };
592
- return {
593
- imapHost: email.imapHost ?? "",
594
- imapUser: email.imapUser ?? "",
595
- imapPassword: email.imapPassword ?? "",
596
- imapPort: email.imapPort ?? 993,
597
- smtpHost: email.smtpHost ?? "",
598
- smtpPort: email.smtpPort ?? 465,
599
- smtpUser: email.smtpUser ?? "",
600
- smtpPassword: email.smtpPassword ?? "",
601
- ...status,
602
- };
603
- }
604
- async function saveEmailConfig(configPath, bodyStr, onSaved) {
605
- try {
606
- const bodyTrimmed = (bodyStr ?? "").trim();
607
- if (!bodyTrimmed)
608
- return { ok: false, error: "请求体为空" };
609
- const existing = readConfig(configPath);
610
- const b = JSON.parse(bodyTrimmed);
611
- const emailFields = ["imapHost", "imapUser", "imapPassword", "imapPort", "smtpHost", "smtpPort", "smtpUser", "smtpPassword"];
612
- for (const field of emailFields) {
613
- if (b[field] !== undefined)
614
- setNestedValue(existing, `email.${field}`, b[field]);
615
- }
616
- writeConfig(configPath, existing);
617
- if (onSaved) {
618
- await onSaved();
619
- return { ok: true, message: "已保存,邮件通道正在重启…" };
620
- }
621
- return { ok: true, message: "已保存。" };
622
- }
623
- catch (e) {
624
- return { ok: false, error: errMsg(e) };
625
- }
626
- }
627
479
  function getPlatformConfig(configPath, platform, fields, getStatus) {
628
480
  const cfg = readConfig(configPath);
629
481
  const section = (cfg[platform] ?? {});