@manturhub/cli 0.9.10-dev.20260810.6 → 0.9.10-dev.20260810.8
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/lib/config.js +31 -31
- package/lib/skill-install.js +10 -6
- package/package.json +1 -1
package/lib/config.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
import { homedir } from "node:os"
|
|
2
|
-
import { join } from "node:path"
|
|
1
|
+
import { homedir } from "node:os"
|
|
2
|
+
import { join } from "node:path"
|
|
3
3
|
import {
|
|
4
4
|
readFileSync,
|
|
5
5
|
writeFileSync,
|
|
6
6
|
mkdirSync,
|
|
7
7
|
existsSync,
|
|
8
8
|
chmodSync,
|
|
9
|
-
} from "node:fs"
|
|
9
|
+
} from "node:fs"
|
|
10
10
|
|
|
11
11
|
// Local config lives at ~/.manturhub/config.json (chmod 600). Env vars win over it.
|
|
12
|
-
const DIR = join(homedir(), ".manturhub")
|
|
13
|
-
const FILE = join(DIR, "config.json")
|
|
14
|
-
const
|
|
15
|
-
const PRODUCTION_ORIGIN = "https://hub.mantur.ai"
|
|
12
|
+
const DIR = join(homedir(), ".manturhub")
|
|
13
|
+
const FILE = join(DIR, "config.json")
|
|
14
|
+
const DEVELOPMENT_ORIGIN = "https://gateway-dev.guiyi.cn"
|
|
15
|
+
const PRODUCTION_ORIGIN = "https://hub.mantur.ai"
|
|
16
16
|
const PACKAGE_VERSION = JSON.parse(
|
|
17
17
|
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
18
|
-
).version
|
|
18
|
+
).version
|
|
19
19
|
|
|
20
20
|
export function defaultBaseForVersion(version) {
|
|
21
|
-
return /-dev(?:\.|$)/.test(String(version)) ?
|
|
21
|
+
return /-dev(?:\.|$)/.test(String(version)) ? DEVELOPMENT_ORIGIN : PRODUCTION_ORIGIN
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// dev 预发布包默认连接开发网关,稳定包默认连接生产网关。MANTURHUB_BASE
|
|
25
25
|
// 仍可显式覆盖;历史配置中保存过的官方默认地址不应覆盖当前发布通道。
|
|
26
|
-
const DEFAULT_BASE = defaultBaseForVersion(PACKAGE_VERSION)
|
|
26
|
+
const DEFAULT_BASE = defaultBaseForVersion(PACKAGE_VERSION)
|
|
27
27
|
const KNOWN_DEFAULT_BASES = new Set([
|
|
28
|
-
|
|
28
|
+
DEVELOPMENT_ORIGIN,
|
|
29
29
|
PRODUCTION_ORIGIN,
|
|
30
30
|
"https://hub.mantur.cn",
|
|
31
31
|
"https://manturhub.leisurecat.cloud",
|
|
32
32
|
"https://api.ophub.com",
|
|
33
|
-
])
|
|
33
|
+
])
|
|
34
34
|
|
|
35
35
|
export function isAllowedHttpUrl(url) {
|
|
36
36
|
const loopback =
|
|
37
|
-
url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]"
|
|
38
|
-
return url.protocol === "http:" &&
|
|
37
|
+
url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]"
|
|
38
|
+
return url.protocol === "http:" && loopback
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function loadConfig() {
|
|
42
42
|
try {
|
|
43
|
-
return JSON.parse(readFileSync(FILE, "utf8"))
|
|
43
|
+
return JSON.parse(readFileSync(FILE, "utf8"))
|
|
44
44
|
} catch {
|
|
45
|
-
return {}
|
|
45
|
+
return {}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function saveConfig(cfg) {
|
|
50
|
-
if (!existsSync(DIR)) mkdirSync(DIR, { recursive: true })
|
|
51
|
-
writeFileSync(FILE, JSON.stringify(cfg, null, 2), { mode: 0o600 })
|
|
50
|
+
if (!existsSync(DIR)) mkdirSync(DIR, { recursive: true })
|
|
51
|
+
writeFileSync(FILE, JSON.stringify(cfg, null, 2), { mode: 0o600 })
|
|
52
52
|
try {
|
|
53
|
-
chmodSync(FILE, 0o600)
|
|
53
|
+
chmodSync(FILE, 0o600)
|
|
54
54
|
} catch {
|
|
55
55
|
/* best-effort on platforms without chmod */
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export function getKey() {
|
|
60
|
-
return process.env.MANTURHUB_KEY || loadConfig().key || null
|
|
60
|
+
return process.env.MANTURHUB_KEY || loadConfig().key || null
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function validateBaseUrl(value) {
|
|
64
|
-
let url
|
|
64
|
+
let url
|
|
65
65
|
try {
|
|
66
|
-
url = new URL(value)
|
|
66
|
+
url = new URL(value)
|
|
67
67
|
} catch {
|
|
68
|
-
throw new Error(`ManturHub 网关地址不合法: ${value}`)
|
|
68
|
+
throw new Error(`ManturHub 网关地址不合法: ${value}`)
|
|
69
69
|
}
|
|
70
70
|
if (url.protocol !== "https:" && !isAllowedHttpUrl(url)) {
|
|
71
71
|
throw new Error(
|
|
72
|
-
"MANTURHUB_BASE 必须使用 HTTPS
|
|
73
|
-
)
|
|
72
|
+
"MANTURHUB_BASE 必须使用 HTTPS(仅本机地址可使用 HTTP)"
|
|
73
|
+
)
|
|
74
74
|
}
|
|
75
|
-
if (url.username || url.password) throw new Error("MANTURHUB_BASE 不得包含用户名或密码")
|
|
76
|
-
return url.href.replace(/\/$/, "")
|
|
75
|
+
if (url.username || url.password) throw new Error("MANTURHUB_BASE 不得包含用户名或密码")
|
|
76
|
+
return url.href.replace(/\/$/, "")
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function getBaseUrl() {
|
|
80
|
-
if (process.env.MANTURHUB_BASE) return validateBaseUrl(process.env.MANTURHUB_BASE)
|
|
81
|
-
const storedBase = loadConfig().baseUrl?.replace(/\/$/, "")
|
|
82
|
-
if (storedBase && !KNOWN_DEFAULT_BASES.has(storedBase)) return validateBaseUrl(storedBase)
|
|
83
|
-
return DEFAULT_BASE
|
|
80
|
+
if (process.env.MANTURHUB_BASE) return validateBaseUrl(process.env.MANTURHUB_BASE)
|
|
81
|
+
const storedBase = loadConfig().baseUrl?.replace(/\/$/, "")
|
|
82
|
+
if (storedBase && !KNOWN_DEFAULT_BASES.has(storedBase)) return validateBaseUrl(storedBase)
|
|
83
|
+
return DEFAULT_BASE
|
|
84
84
|
}
|
package/lib/skill-install.js
CHANGED
|
@@ -53,9 +53,6 @@ async function fetchSkillDetail(slug) {
|
|
|
53
53
|
}
|
|
54
54
|
if (!response.ok) throw new Error(`Skill 信息获取失败(HTTP ${response.status})`);
|
|
55
55
|
const detail = unwrapGatewayData(response.json) || {};
|
|
56
|
-
if (!detail?.version) {
|
|
57
|
-
throw new Error(`线上 Skill「${slug}」缺少 version,平台需先修正后才能安装`);
|
|
58
|
-
}
|
|
59
56
|
return { ...detail, slug: detail.code };
|
|
60
57
|
}
|
|
61
58
|
|
|
@@ -234,6 +231,7 @@ export async function skillAdd(
|
|
|
234
231
|
const tempDir = mkdtempSync(join(tmpdir(), "manturhub-skill-"));
|
|
235
232
|
const bundle = join(tempDir, `${slug}.zip`);
|
|
236
233
|
const staged = join(tempDir, "staged");
|
|
234
|
+
let installedVersion;
|
|
237
235
|
let installError = null;
|
|
238
236
|
try {
|
|
239
237
|
await downloadSkillBundle(slug, bundle);
|
|
@@ -244,11 +242,17 @@ export async function skillAdd(
|
|
|
244
242
|
`安装包 name 与请求不一致(请求 ${slug},安装包 ${bundleMetadata.name || "缺失"})`
|
|
245
243
|
);
|
|
246
244
|
}
|
|
247
|
-
|
|
245
|
+
// 兼容公开详情未返回版本的旧网关,以实际安装包版本记录安装状态。
|
|
246
|
+
// 接口已返回版本时仍严格比对,避免安装过程中发布切换造成版本错配。
|
|
247
|
+
if (detail.version && bundleMetadata.version !== detail.version) {
|
|
248
248
|
throw new Error(
|
|
249
249
|
`安装包 version 与线上元数据不一致(线上 ${detail.version},安装包 ${bundleMetadata.version || "缺失"})`
|
|
250
250
|
);
|
|
251
251
|
}
|
|
252
|
+
if (!bundleMetadata.version) {
|
|
253
|
+
throw new Error(`Skill「${slug}」的接口和安装包均缺少 version,无法记录安装版本`);
|
|
254
|
+
}
|
|
255
|
+
installedVersion = bundleMetadata.version;
|
|
252
256
|
const contentSha256 = hashSkillDirectory(staged);
|
|
253
257
|
const bundleSha256 = createHash("sha256").update(readFileSync(bundle)).digest("hex");
|
|
254
258
|
const installedAt = new Date().toISOString();
|
|
@@ -259,7 +263,7 @@ export async function skillAdd(
|
|
|
259
263
|
setSkillRecord(state, {
|
|
260
264
|
slug,
|
|
261
265
|
client: destination.client,
|
|
262
|
-
version:
|
|
266
|
+
version: installedVersion,
|
|
263
267
|
base_url: baseUrl,
|
|
264
268
|
bundle_sha256: bundleSha256,
|
|
265
269
|
content_sha256: contentSha256,
|
|
@@ -283,7 +287,7 @@ export async function skillAdd(
|
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
for (const destination of destinations) {
|
|
286
|
-
console.log(`✓ 已${action === "update" ? "更新" : "安装"} Skill「${slug}」v${
|
|
290
|
+
console.log(`✓ 已${action === "update" ? "更新" : "安装"} Skill「${slug}」v${installedVersion} → ${destination.path}`);
|
|
287
291
|
}
|
|
288
292
|
console.log(" 重启对应 Agent 后,用自然语言描述任务即可使用。");
|
|
289
293
|
}
|