@whyshu/argent 0.2.7 → 0.2.9
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/mcp/gateway.mjs +14 -3
- package/package.json +1 -1
- package/src/index.js +7 -2
- package/src/tunnel.mjs +24 -9
package/mcp/gateway.mjs
CHANGED
|
@@ -32,6 +32,16 @@ import { fileURLToPath } from 'node:url'
|
|
|
32
32
|
|
|
33
33
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
34
34
|
const PKG_ROOT = resolve(HERE, '..')
|
|
35
|
+
/**
|
|
36
|
+
* 引擎根(**可选**):只认宿主显式传入的 `ARGENT_ENGINE_DIR`(`@whyshu/argent` 注册时会给),
|
|
37
|
+
* ⛔ 不按仓库相对路径猜 —— 插件安装后没有"仓库根",这条口径由
|
|
38
|
+
* `scripts/tests/argent-mcp-vendor-sync.test.mjs` 钉住。
|
|
39
|
+
* 2026-11-08 修:本文件从 `core/mcp/gateway.mjs` 抄来时把原来的 `REPO` 丢了、只剩 `PKG_ROOT`,
|
|
40
|
+
* 而 ml 分支还在用 `REPO`(`tools/agentic/mcp_server.py` / `scripts`)⇒ 只要
|
|
41
|
+
* `ARGENT_GATEWAY_ML=1` 就抛 `ReferenceError: REPO is not defined`,**而且死在装 stdio 前端之前**
|
|
42
|
+
* ⇒ 宿主侧 `dsh-mcp-client` 永远等不到 initialize 响应(表现为"MCP 网关握手挂起")。
|
|
43
|
+
*/
|
|
44
|
+
const ENGINE_DIR = process.env.ARGENT_ENGINE_DIR || ''
|
|
35
45
|
const DATA_DIR = process.env.ARGENT_DATA_DIR || join(homedir(), '.dsh', 'argent')
|
|
36
46
|
const GATEWAY_DIR = join(DATA_DIR, 'gateway')
|
|
37
47
|
const NO_PREFIX = process.env.ARGENT_GATEWAY_NOPREFIX === '1'
|
|
@@ -147,15 +157,16 @@ export class Gateway {
|
|
|
147
157
|
|
|
148
158
|
static defaultUpstreams() {
|
|
149
159
|
// 引擎上游可选:插件独立安装(没有 engine 包)时只挂 erp.*,不因缺引擎而整个网关启动失败
|
|
150
|
-
const engineDir =
|
|
160
|
+
const engineDir = ENGINE_DIR
|
|
151
161
|
const engineServer = engineDir ? join(engineDir, 'core', 'mcp', 'server.mjs') : ''
|
|
152
162
|
const ups = []
|
|
153
163
|
if (engineServer && existsSync(engineServer)) ups.push(new Upstream('engine', process.execPath, [engineServer]))
|
|
154
164
|
ups.push(new Upstream('erp', process.execPath, [join(HERE, 'erp-server.mjs')]))
|
|
155
165
|
if (process.env.ARGENT_GATEWAY_ML === '1') {
|
|
166
|
+
// ml 上游在**引擎根**下面(tools/agentic/mcp_server.py + scripts);没有引擎根就跳过,⛔ 不猜路径
|
|
156
167
|
const py = process.env.ARGENT_ENGINE_PY || join(DATA_DIR, 'venv', 'bin', 'python3')
|
|
157
|
-
const srv = join(
|
|
158
|
-
if (existsSync(py) && existsSync(srv)) ups.push(new Upstream('ml', py, [srv], { PYTHONPATH: join(
|
|
168
|
+
const srv = engineDir ? join(engineDir, 'tools', 'agentic', 'mcp_server.py') : ''
|
|
169
|
+
if (py && srv && existsSync(py) && existsSync(srv)) ups.push(new Upstream('ml', py, [srv], { PYTHONPATH: join(engineDir, 'scripts') }))
|
|
159
170
|
}
|
|
160
171
|
return ups
|
|
161
172
|
}
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -227,8 +227,13 @@ export function apply(ctx) {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
//
|
|
231
|
-
//
|
|
230
|
+
// 真根因(2026-11-08 用 V8 inspector 抓栈确诊):**不是** dsh-mcp-client/网关握手挂起,而是
|
|
231
|
+
// MCP 工具在 boot 之后才注册 ⇒ 触发 `@whyshu/argent-erp` 的 office-guard 在
|
|
232
|
+
// 「dispose 旧 mask → restrict 新 mask」过程中的**同步重入风暴**(主线程 100% CPU + 堆 OOM,
|
|
233
|
+
// 端口 bound 但 HTTP 不响应)。已在 office-guard 加 reconcile 重入闸修好;见
|
|
234
|
+
// scripts/tests/argent-office-guard-reentrancy.test.mjs。
|
|
235
|
+
// 这里仍保留「延后注册 + ARGENT_MCP_DISABLE + 握手 20s 上限」作为兜底:宿主侧第三方 listeners
|
|
236
|
+
// 若还有同类重入,最多拖慢 MCP 自身,不把 boot 与 HTTP 服务拖下水。
|
|
232
237
|
if (process.env.ARGENT_MCP_DISABLE === '1') {
|
|
233
238
|
console.log('[argent] MCP 网关已禁用(ARGENT_MCP_DISABLE=1)')
|
|
234
239
|
} else setTimeout(() => { void (async () => {
|
package/src/tunnel.mjs
CHANGED
|
@@ -530,18 +530,33 @@ export function startTunnel(options = {}) {
|
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
// ── dsh 准入 cookie(仅内存;401/403 重换一次)──────────────────────────
|
|
533
|
+
// dsh 准入 token 的三条来源(优先级:env → 启动脚本写出的 token 文件 → 本机 /api/argent-state/access)
|
|
534
|
+
// 为什么需要文件来源:`/api/argent-state/access` 由 argent-core 插件提供;单插件(@whyshu/argent)装配下
|
|
535
|
+
// 该端点 401 ⇒ 隧道拿不到 token ⇒ App 侧表现为 access-unavailable(2026-11-07 实测)。
|
|
536
|
+
const readTokenFile = () => {
|
|
537
|
+
try {
|
|
538
|
+
const t = readFileSync(join(String(options.dataDir || ''), 'dsh-web.token'), 'utf8').trim()
|
|
539
|
+
return t || ''
|
|
540
|
+
} catch { return '' }
|
|
541
|
+
}
|
|
533
542
|
const acquireCookie = async ({ refresh = false } = {}) => {
|
|
534
543
|
if (!refresh && dsh.cookie) return dsh.cookie
|
|
535
|
-
let access
|
|
536
|
-
try {
|
|
537
|
-
access = await fetchImpl(`${dshBase}${DSH_ACCESS_PATH}`, {
|
|
538
|
-
headers: { accept: 'application/json' },
|
|
539
|
-
signal: AbortSignal.timeout(5000),
|
|
540
|
-
})
|
|
541
|
-
} catch { throw err('access-unreachable') }
|
|
542
544
|
let body = null
|
|
543
|
-
|
|
544
|
-
|
|
545
|
+
const envToken = String(env.ARGENT_DSH_TOKEN || '').trim()
|
|
546
|
+
const fileToken = envToken ? '' : readTokenFile()
|
|
547
|
+
if (envToken || fileToken) {
|
|
548
|
+
body = { ok: true, token: envToken || fileToken }
|
|
549
|
+
} else {
|
|
550
|
+
let access
|
|
551
|
+
try {
|
|
552
|
+
access = await fetchImpl(`${dshBase}${DSH_ACCESS_PATH}`, {
|
|
553
|
+
headers: { accept: 'application/json' },
|
|
554
|
+
signal: AbortSignal.timeout(5000),
|
|
555
|
+
})
|
|
556
|
+
} catch { throw err('access-unreachable') }
|
|
557
|
+
try { body = await access.json() } catch { body = null }
|
|
558
|
+
if (!access.ok || !body || body.ok !== true || !body.token) throw err('access-unavailable', `HTTP ${access.status}`)
|
|
559
|
+
}
|
|
545
560
|
let res
|
|
546
561
|
try {
|
|
547
562
|
res = await fetchImpl(`${dshBase}/?token=${encodeURIComponent(String(body.token))}`, {
|