@lightcone-ai/daemon 0.10.1 → 0.10.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.
@@ -3,7 +3,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
5
 
6
- const SERVER_URL = process.env.PLATFORM_SERVER_URL ?? process.env.SERVER_URL ?? 'http://localhost:3001';
6
+ const SERVER_URL = process.env.PLATFORM_SERVER_URL ?? process.env.SERVER_URL ?? 'http://localhost:9779';
7
7
  const MACHINE_API_KEY = process.env.PLATFORM_MACHINE_API_KEY ?? process.env.MACHINE_API_KEY ?? '';
8
8
  const AGENT_ID = process.env.PLATFORM_AGENT_ID ?? process.env.AGENT_ID ?? '';
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,7 +1,16 @@
1
1
  import { spawn } from 'child_process';
2
- import { mkdirSync, writeFileSync, readdirSync, unlinkSync } from 'fs';
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ readdirSync,
7
+ statSync,
8
+ unlinkSync,
9
+ writeFileSync,
10
+ } from 'fs';
3
11
  import { homedir } from 'os';
4
12
  import path from 'path';
13
+ import { fileURLToPath } from 'url';
5
14
  import { parseCodexLine, adaptCodexSystemPrompt } from './drivers/codex.js';
6
15
  import { parseKimiLine, encodeKimiStdin } from './drivers/kimi.js';
7
16
  import { startSession, stopSession, stopAllSessions } from './browser-login.js';
@@ -10,6 +19,95 @@ import { markInvalidatedLeases } from './governance-state.js';
10
19
  const KIMI_SYSTEM_PROMPT_FILE = '.lightcone-kimi-system.md';
11
20
  const KIMI_AGENT_FILE = '.lightcone-kimi-agent.yaml';
12
21
  const KIMI_MCP_FILE = '.lightcone-kimi-mcp.json';
22
+ const LOCAL_FILE_DIR = path.dirname(fileURLToPath(import.meta.url));
23
+ const LOCAL_MCP_ROOTS = Object.freeze([
24
+ path.resolve(LOCAL_FILE_DIR, '../mcp-servers'),
25
+ path.resolve(LOCAL_FILE_DIR, '../../mcp-servers'),
26
+ ]);
27
+
28
+ function isPlainObject(value) {
29
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
30
+ }
31
+
32
+ function normalizeMcpServerId(value, fallback = '') {
33
+ const normalized = String(value ?? fallback).trim().toLowerCase();
34
+ if (!normalized) return '';
35
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(normalized)) return '';
36
+ return normalized;
37
+ }
38
+
39
+ function isFile(filePath) {
40
+ try {
41
+ return statSync(filePath).isFile();
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ function collectMcpPathsFromRoot(rootDir, output) {
48
+ if (!existsSync(rootDir)) return;
49
+
50
+ const pending = [rootDir];
51
+ const visited = new Set();
52
+ const fallbackCandidates = new Map();
53
+
54
+ while (pending.length > 0) {
55
+ const currentDir = pending.pop();
56
+ if (visited.has(currentDir)) continue;
57
+ visited.add(currentDir);
58
+
59
+ const manifestPath = path.join(currentDir, 'manifest.json');
60
+ if (isFile(manifestPath)) {
61
+ try {
62
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
63
+ const serverId = normalizeMcpServerId(manifest?.id, path.basename(currentDir));
64
+ const entrypointRaw = String(manifest?.entrypoint ?? 'index.js').trim() || 'index.js';
65
+ const entrypointPath = path.resolve(currentDir, entrypointRaw);
66
+ if (serverId && isFile(entrypointPath) && !Object.hasOwn(output, serverId)) {
67
+ output[serverId] = entrypointPath;
68
+ }
69
+ } catch {
70
+ // Ignore malformed manifest and keep scanning sibling directories.
71
+ }
72
+ } else {
73
+ const fallbackId = normalizeMcpServerId(path.basename(currentDir));
74
+ const fallbackEntrypoint = path.join(currentDir, 'index.js');
75
+ if (
76
+ currentDir !== rootDir
77
+ && fallbackId
78
+ && isFile(fallbackEntrypoint)
79
+ && !Object.hasOwn(output, fallbackId)
80
+ && !fallbackCandidates.has(fallbackId)
81
+ ) {
82
+ fallbackCandidates.set(fallbackId, fallbackEntrypoint);
83
+ }
84
+ }
85
+
86
+ let entries = [];
87
+ try {
88
+ entries = readdirSync(currentDir, { withFileTypes: true });
89
+ } catch {
90
+ continue;
91
+ }
92
+ for (const entry of entries) {
93
+ if (!entry.isDirectory()) continue;
94
+ pending.push(path.join(currentDir, entry.name));
95
+ }
96
+ }
97
+
98
+ for (const [serverId, entrypointPath] of fallbackCandidates.entries()) {
99
+ if (Object.hasOwn(output, serverId)) continue;
100
+ output[serverId] = entrypointPath;
101
+ }
102
+ }
103
+
104
+ function collectLocalMcpPaths() {
105
+ const output = {};
106
+ for (const rootDir of LOCAL_MCP_ROOTS) {
107
+ collectMcpPathsFromRoot(rootDir, output);
108
+ }
109
+ return output;
110
+ }
13
111
 
14
112
  function runtimeMissingDetail(runtime) {
15
113
  return `cli_missing:${runtime}`;
@@ -25,7 +123,7 @@ function resolveExitOfflineDetail({ code, signal, stopCause }) {
25
123
  }
26
124
 
27
125
  function normalizeObject(value) {
28
- return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
126
+ return isPlainObject(value) ? value : {};
29
127
  }
30
128
 
31
129
  function replacePlaceholders(text, replacements) {
@@ -434,6 +532,7 @@ export class AgentManager {
434
532
  session_id: config?.sessionId ?? null,
435
533
  workspace_dir: workspaceDir,
436
534
  chat_bridge_path: chatBridgePath,
535
+ mcp_paths: collectLocalMcpPaths(),
437
536
  },
438
537
  agent_profile: {
439
538
  name: config?.name ?? null,
@@ -14,7 +14,7 @@ function getArg(name) {
14
14
  return idx !== -1 && cliArgs[idx + 1] ? cliArgs[idx + 1] : '';
15
15
  }
16
16
 
17
- const SERVER_URL = process.env.SERVER_URL || getArg('--server-url') || 'http://localhost:8777';
17
+ const SERVER_URL = process.env.SERVER_URL || getArg('--server-url') || 'http://localhost:9779';
18
18
  const MACHINE_API_KEY = process.env.MACHINE_API_KEY || getArg('--auth-token') || '';
19
19
  const AGENT_ID = process.env.AGENT_ID || getArg('--agent-id') || '';
20
20
  const WORKSPACE_ID = process.env.WORKSPACE_ID || getArg('--workspace-id') || ''; // injected per-workspace at spawn time
@@ -169,9 +169,9 @@ When referencing a workspace or mentioning someone, write them as plain text wit
169
169
 
170
170
  When writing a URL next to non-ASCII punctuation (Chinese, Japanese, etc.), always wrap the URL in angle brackets or use markdown link syntax. Otherwise the punctuation may be rendered as part of the URL.
171
171
 
172
- - **Wrong**: \`测试环境:http://localhost:3000,请查看\` (the \`,\` gets swallowed into the link)
173
- - **Correct**: \`测试环境:<http://localhost:3000>,请查看\`
174
- - **Also correct**: \`测试环境:[http://localhost:3000](http://localhost:3000),请查看\`
172
+ - **Wrong**: \`测试环境:http://localhost:9779,请查看\` (the \`,\` gets swallowed into the link)
173
+ - **Correct**: \`测试环境:<http://localhost:9779>,请查看\`
174
+ - **Also correct**: \`测试环境:[http://localhost:9779](http://localhost:9779),请查看\`
175
175
 
176
176
  ## Filesystem Access
177
177
 
package/src/index.js CHANGED
@@ -39,7 +39,7 @@ if (opts['--help'] || opts['-h']) {
39
39
  process.exit(0);
40
40
  }
41
41
 
42
- const SERVER_URL = String(opts['--server-url'] || process.env.SERVER_URL || 'http://localhost:8779').trim();
42
+ const SERVER_URL = String(opts['--server-url'] || process.env.SERVER_URL || 'http://localhost:9779').trim();
43
43
  const MACHINE_API_KEY = String(opts['--api-key'] || process.env.MACHINE_API_KEY || '').trim();
44
44
 
45
45
  if (!MACHINE_API_KEY) {
package/src/mcp-config.js CHANGED
@@ -16,7 +16,7 @@ const LEGACY_MCP_PATH_TOKENS = Object.freeze({
16
16
  '{portfolio_analysis_mcp_path}': 'portfolio-analysis',
17
17
  });
18
18
 
19
- function resolveMcpPathToken(arg) {
19
+ function resolveMcpPathToken(arg, mcpPaths = {}) {
20
20
  if (typeof arg !== 'string') return null;
21
21
 
22
22
  const trimmed = arg.trim();
@@ -27,6 +27,13 @@ function resolveMcpPathToken(arg) {
27
27
  const serverId = legacyId ?? (dynamicMatch ? dynamicMatch[1].toLowerCase() : null);
28
28
  if (!serverId) return null;
29
29
 
30
+ const runtimeMcpPath = typeof mcpPaths?.[serverId] === 'string'
31
+ ? mcpPaths[serverId].trim()
32
+ : '';
33
+ if (runtimeMcpPath) {
34
+ return runtimeMcpPath;
35
+ }
36
+
30
37
  const entrypointPath = resolveMcpServerEntrypoint(serverId, { strict: true });
31
38
  if (!entrypointPath) {
32
39
  throw new Error(`MCP server '${serverId}' is not registered in manifest registry`);
@@ -35,7 +42,7 @@ function resolveMcpPathToken(arg) {
35
42
  }
36
43
 
37
44
  function resolveSkillArg(arg, config) {
38
- const resolvedMcpPath = resolveMcpPathToken(arg);
45
+ const resolvedMcpPath = resolveMcpPathToken(arg, config?.mcpPaths);
39
46
  if (resolvedMcpPath) return resolvedMcpPath;
40
47
  if (arg === '{xhs_profile_dir}')
41
48
  return profileDir('xhs', config.userId ?? 'default');