@a2hmarket/a2hmarket 0.6.5 → 0.6.7
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/openclaw.plugin.json +1 -14
- package/package.json +1 -1
- package/scripts/install.mjs +97 -105
- package/skills/a2hmarket-setup/SKILL.md +143 -0
package/openclaw.plugin.json
CHANGED
|
@@ -6,20 +6,7 @@
|
|
|
6
6
|
"skills": ["./skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
9
|
-
"properties": {
|
|
10
|
-
"agentId": { "type": "string", "description": "A2H Market agent ID" },
|
|
11
|
-
"agentKey": { "type": "string", "description": "A2H Market agent key (HMAC signing)" },
|
|
12
|
-
"apiUrl": { "type": "string", "description": "A2H Market API base URL" },
|
|
13
|
-
"mqttUrl": { "type": "string", "description": "MQTT broker URL for A2A messaging" },
|
|
14
|
-
"tempoPrivateKey": { "type": "string", "description": "Solana private key for Tempo payments" },
|
|
15
|
-
"notify": {
|
|
16
|
-
"type": "object",
|
|
17
|
-
"properties": {
|
|
18
|
-
"channel": { "type": "string", "description": "Notification channel (feishu, discord, etc.)" },
|
|
19
|
-
"target": { "type": "string", "description": "Target ID for notifications" }
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
9
|
+
"properties": {},
|
|
23
10
|
"additionalProperties": false
|
|
24
11
|
}
|
|
25
12
|
}
|
package/package.json
CHANGED
package/scripts/install.mjs
CHANGED
|
@@ -311,24 +311,12 @@ async function runUpdate() {
|
|
|
311
311
|
// 3. Backup credentials before uninstall
|
|
312
312
|
logStep(3, "更新插件");
|
|
313
313
|
let savedCreds = null;
|
|
314
|
-
// Try openclaw.json plugin config first
|
|
315
314
|
try {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
savedCreds = entry;
|
|
320
|
-
log(` ${CHECK} 凭证已备份 (openclaw.json)`);
|
|
315
|
+
if (existsSync(CREDS_FILE)) {
|
|
316
|
+
savedCreds = JSON.parse(readFileSync(CREDS_FILE, "utf-8"));
|
|
317
|
+
log(` ${CHECK} 凭证已备份`);
|
|
321
318
|
}
|
|
322
319
|
} catch {}
|
|
323
|
-
// Fallback to credentials file
|
|
324
|
-
if (!savedCreds) {
|
|
325
|
-
try {
|
|
326
|
-
if (existsSync(CREDS_FILE)) {
|
|
327
|
-
savedCreds = JSON.parse(readFileSync(CREDS_FILE, "utf-8"));
|
|
328
|
-
log(` ${CHECK} 凭证已备份 (credentials.json)`);
|
|
329
|
-
}
|
|
330
|
-
} catch {}
|
|
331
|
-
}
|
|
332
320
|
if (!savedCreds) {
|
|
333
321
|
log(` ${WARN} 未找到凭证备份,更新后可能需要重新安装`);
|
|
334
322
|
}
|
|
@@ -344,9 +332,8 @@ async function runUpdate() {
|
|
|
344
332
|
process.exit(1);
|
|
345
333
|
}
|
|
346
334
|
|
|
347
|
-
// Restore credentials
|
|
335
|
+
// Restore credentials file
|
|
348
336
|
if (savedCreds) {
|
|
349
|
-
// Restore credentials file
|
|
350
337
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
351
338
|
const fileData = {
|
|
352
339
|
agent_id: savedCreds.agentId ?? savedCreds.agent_id,
|
|
@@ -356,23 +343,6 @@ async function runUpdate() {
|
|
|
356
343
|
};
|
|
357
344
|
if (savedCreds.notify) fileData.notify = savedCreds.notify;
|
|
358
345
|
writeFileSync(CREDS_FILE, JSON.stringify(fileData, null, 2) + "\n");
|
|
359
|
-
|
|
360
|
-
// Restore openclaw.json plugin entry
|
|
361
|
-
try {
|
|
362
|
-
const configPath = join(OPENCLAW_DIR, "openclaw.json");
|
|
363
|
-
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
364
|
-
if (!cfg.plugins) cfg.plugins = {};
|
|
365
|
-
if (!cfg.plugins.entries) cfg.plugins.entries = {};
|
|
366
|
-
cfg.plugins.entries.a2hmarket = {
|
|
367
|
-
enabled: true,
|
|
368
|
-
agentId: fileData.agent_id,
|
|
369
|
-
agentKey: fileData.agent_key,
|
|
370
|
-
apiUrl: fileData.api_url,
|
|
371
|
-
mqttUrl: fileData.mqtt_url,
|
|
372
|
-
...(fileData.notify ? { notify: fileData.notify } : {}),
|
|
373
|
-
};
|
|
374
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n");
|
|
375
|
-
} catch {}
|
|
376
346
|
log(` ${CHECK} 凭证已恢复`);
|
|
377
347
|
}
|
|
378
348
|
|
|
@@ -523,11 +493,28 @@ async function main() {
|
|
|
523
493
|
log(`\n${BOLD}A2H Market — OpenClaw Plugin${RESET}\n`);
|
|
524
494
|
log(` 安装: npx -y ${NPM_SPEC} install`);
|
|
525
495
|
log(` 快速: npx -y ${NPM_SPEC} install --agent ag_xxx:key`);
|
|
496
|
+
log(` 静默: npx -y ${NPM_SPEC} install --agent ag_xxx:key --notify feishu:ou_xxx --yes`);
|
|
526
497
|
log(` 更新: npx -y ${NPM_SPEC} update`);
|
|
527
498
|
log(` 卸载: npx -y ${NPM_SPEC} uninstall\n`);
|
|
528
499
|
process.exit(0);
|
|
529
500
|
}
|
|
530
501
|
|
|
502
|
+
// Parse flags
|
|
503
|
+
const autoYes = args.includes("--yes") || args.includes("-y");
|
|
504
|
+
|
|
505
|
+
const notifyFlag = args.find((a) => a.startsWith("--notify"));
|
|
506
|
+
const notifyValue = notifyFlag
|
|
507
|
+
? (args[args.indexOf(notifyFlag) + 1] ?? notifyFlag.split("=")[1])
|
|
508
|
+
: null;
|
|
509
|
+
let presetNotify = null;
|
|
510
|
+
if (notifyValue && notifyValue.includes(":")) {
|
|
511
|
+
const colonIdx = notifyValue.indexOf(":");
|
|
512
|
+
presetNotify = {
|
|
513
|
+
channel: notifyValue.slice(0, colonIdx),
|
|
514
|
+
target: notifyValue.slice(colonIdx + 1),
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
531
518
|
log(`\n${BOLD}🏪 A2H Market — OpenClaw Plugin Installer${RESET}\n`);
|
|
532
519
|
|
|
533
520
|
// ── Step 1: Check OpenClaw ─────────────────────────────────────
|
|
@@ -562,9 +549,12 @@ async function main() {
|
|
|
562
549
|
const existingId = existing.agent_id ?? existing.agentId ?? "";
|
|
563
550
|
if (existingId) {
|
|
564
551
|
log(` 已有凭证: ${CYAN}${existingId}${RESET}`);
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
552
|
+
let reuse = "Y";
|
|
553
|
+
if (!autoYes) {
|
|
554
|
+
const prompt = createPrompt();
|
|
555
|
+
reuse = await prompt.ask("使用现有凭证? (Y/n)", "Y");
|
|
556
|
+
prompt.close();
|
|
557
|
+
}
|
|
568
558
|
if (reuse.toLowerCase() !== "n") {
|
|
569
559
|
agentId = existingId;
|
|
570
560
|
agentKey = existing.agent_key ?? existing.agentKey ?? "";
|
|
@@ -718,84 +708,77 @@ async function main() {
|
|
|
718
708
|
mqtt_url: mqttUrl,
|
|
719
709
|
};
|
|
720
710
|
|
|
721
|
-
//
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
log(`
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
"",
|
|
733
|
-
);
|
|
711
|
+
// Configure notification channel
|
|
712
|
+
if (presetNotify) {
|
|
713
|
+
credsData.notify = presetNotify;
|
|
714
|
+
log(` ${CHECK} 通知渠道: ${presetNotify.channel} → ${presetNotify.target}`);
|
|
715
|
+
} else if (!autoYes) {
|
|
716
|
+
const channels = detectChannels();
|
|
717
|
+
if (channels.length > 0) {
|
|
718
|
+
log(` 检测到 ${channels.length} 个可用渠道:`);
|
|
719
|
+
channels.forEach((ch, i) => {
|
|
720
|
+
log(` ${CYAN}${i + 1}${RESET}. ${ch.name}`);
|
|
721
|
+
});
|
|
734
722
|
|
|
735
|
-
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
723
|
+
const prompt2 = createPrompt();
|
|
724
|
+
const choice = await prompt2.ask(
|
|
725
|
+
`选择通知渠道 (1-${channels.length},回车跳过)`,
|
|
726
|
+
"",
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
if (choice) {
|
|
730
|
+
const idx = parseInt(choice, 10) - 1;
|
|
731
|
+
if (idx >= 0 && idx < channels.length) {
|
|
732
|
+
const chosen = channels[idx];
|
|
733
|
+
let target = "";
|
|
734
|
+
|
|
735
|
+
if (chosen.name === "feishu") {
|
|
736
|
+
target = detectFeishuTarget() || "";
|
|
737
|
+
if (target) {
|
|
738
|
+
log(` 检测到飞书用户: ${CYAN}${target}${RESET}`);
|
|
739
|
+
} else {
|
|
740
|
+
target = await prompt2.ask("输入飞书 open_id (ou_xxx) 或 chat_id (oc_xxx)", "");
|
|
741
|
+
}
|
|
742
|
+
} else if (chosen.name === "discord") {
|
|
743
|
+
target = await prompt2.ask("输入 Discord 频道 ID", "");
|
|
744
|
+
} else {
|
|
745
|
+
target = await prompt2.ask(`输入 ${chosen.name} 目标 ID`, "");
|
|
746
|
+
}
|
|
740
747
|
|
|
741
|
-
if (chosen.name === "feishu") {
|
|
742
|
-
target = detectFeishuTarget() || "";
|
|
743
748
|
if (target) {
|
|
744
|
-
|
|
749
|
+
credsData.notify = { channel: chosen.name, target };
|
|
750
|
+
log(` ${CHECK} 通知渠道已配置: ${chosen.name} → ${target}`);
|
|
745
751
|
} else {
|
|
746
|
-
|
|
752
|
+
log(` ${WARN} 未输入目标 ID,跳过通知配置`);
|
|
747
753
|
}
|
|
748
|
-
} else if (chosen.name === "discord") {
|
|
749
|
-
target = await prompt2.ask("输入 Discord 频道 ID", "");
|
|
750
|
-
} else {
|
|
751
|
-
target = await prompt2.ask(`输入 ${chosen.name} 目标 ID`, "");
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (target) {
|
|
755
|
-
credsData.notify = { channel: chosen.name, target };
|
|
756
|
-
log(` ${CHECK} 通知渠道已配置: ${chosen.name} → ${target}`);
|
|
757
|
-
} else {
|
|
758
|
-
log(` ${WARN} 未输入目标 ID,跳过通知配置`);
|
|
759
754
|
}
|
|
755
|
+
} else {
|
|
756
|
+
log(` ${DIM}跳过通知配置${RESET}`);
|
|
760
757
|
}
|
|
758
|
+
prompt2.close();
|
|
761
759
|
} else {
|
|
762
|
-
log(` ${DIM}
|
|
760
|
+
log(` ${DIM}未检测到可用渠道,跳过通知配置${RESET}`);
|
|
763
761
|
}
|
|
764
|
-
prompt2.close();
|
|
765
762
|
} else {
|
|
766
|
-
|
|
763
|
+
// --yes without --notify: try auto-detect feishu
|
|
764
|
+
const feishuTarget = detectFeishuTarget();
|
|
765
|
+
if (feishuTarget) {
|
|
766
|
+
credsData.notify = { channel: "feishu", target: feishuTarget };
|
|
767
|
+
log(` ${CHECK} 自动检测通知: feishu → ${feishuTarget}`);
|
|
768
|
+
} else {
|
|
769
|
+
log(` ${DIM}跳过通知配置(使用 --notify 指定)${RESET}`);
|
|
770
|
+
}
|
|
767
771
|
}
|
|
768
772
|
|
|
769
|
-
// Save credentials
|
|
773
|
+
// Save credentials to ~/.openclaw/a2hmarket/credentials.json
|
|
770
774
|
writeFileSync(CREDS_FILE, JSON.stringify(credsData, null, 2) + "\n");
|
|
771
|
-
log(` ${CHECK}
|
|
775
|
+
log(` ${CHECK} 凭证已保存`);
|
|
772
776
|
|
|
773
|
-
//
|
|
774
|
-
// This is the primary config path — api.pluginConfig reads from here
|
|
775
|
-
log(` 写入 openclaw.json...`);
|
|
777
|
+
// Ensure a2h tools in alsoAllow (if whitelist mode is active)
|
|
776
778
|
try {
|
|
777
779
|
const configPath = join(OPENCLAW_DIR, "openclaw.json");
|
|
778
780
|
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
779
|
-
|
|
780
|
-
// Ensure plugins.entries.a2hmarket exists
|
|
781
|
-
if (!cfg.plugins) cfg.plugins = {};
|
|
782
|
-
if (!cfg.plugins.entries) cfg.plugins.entries = {};
|
|
783
|
-
const entry = cfg.plugins.entries.a2hmarket ?? {};
|
|
784
|
-
|
|
785
|
-
// Write credentials into plugin entry
|
|
786
|
-
entry.enabled = true;
|
|
787
|
-
entry.agentId = agentId;
|
|
788
|
-
entry.agentKey = agentKey;
|
|
789
|
-
entry.apiUrl = apiUrl;
|
|
790
|
-
entry.mqttUrl = mqttUrl;
|
|
791
|
-
if (credsData.notify) {
|
|
792
|
-
entry.notify = credsData.notify;
|
|
793
|
-
}
|
|
794
|
-
cfg.plugins.entries.a2hmarket = entry;
|
|
795
|
-
|
|
796
|
-
// Ensure a2h tools in alsoAllow (if whitelist mode is active)
|
|
797
|
-
if (!cfg.tools) cfg.tools = {};
|
|
798
|
-
if (Array.isArray(cfg.tools.alsoAllow)) {
|
|
781
|
+
if (Array.isArray(cfg?.tools?.alsoAllow)) {
|
|
799
782
|
const a2hTools = [
|
|
800
783
|
"a2h_status", "a2h_profile_get", "a2h_profile_upload_qrcode",
|
|
801
784
|
"a2h_profile_delete_qrcode", "a2h_file_upload",
|
|
@@ -814,15 +797,24 @@ async function main() {
|
|
|
814
797
|
added++;
|
|
815
798
|
}
|
|
816
799
|
}
|
|
817
|
-
if (added > 0)
|
|
800
|
+
if (added > 0) {
|
|
801
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n");
|
|
802
|
+
log(` ${CHECK} ${added} 个工具已添加到白名单`);
|
|
803
|
+
}
|
|
818
804
|
}
|
|
805
|
+
} catch {}
|
|
819
806
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
807
|
+
// Clean any stale credentials from plugins.entries (OpenClaw doesn't allow custom keys there)
|
|
808
|
+
try {
|
|
809
|
+
const configPath = join(OPENCLAW_DIR, "openclaw.json");
|
|
810
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
811
|
+
const entry = cfg?.plugins?.entries?.a2hmarket;
|
|
812
|
+
if (entry && (entry.agentId || entry.agentKey)) {
|
|
813
|
+
cfg.plugins.entries.a2hmarket = { enabled: true };
|
|
814
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n");
|
|
815
|
+
log(` ${CHECK} 清理 openclaw.json 中的旧凭证`);
|
|
816
|
+
}
|
|
817
|
+
} catch {}
|
|
826
818
|
|
|
827
819
|
// ── Step 5: Restart gateway ────────────────────────────────────
|
|
828
820
|
logStep(5, "启动服务");
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: a2hmarket-setup
|
|
3
|
+
description: "安装、更新或卸载 A2H Market 插件。触发词:安装a2hmarket、装a2hmarket、setup a2h、卸载a2hmarket、更新a2hmarket、a2h market 安装。当用户要求安装/卸载/更新 a2hmarket 插件时触发。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# A2H Market 安装引导
|
|
7
|
+
|
|
8
|
+
本 Skill 指导你帮用户在 OpenClaw 上安装、更新或卸载 A2H Market 插件。
|
|
9
|
+
|
|
10
|
+
## 判断用户意图
|
|
11
|
+
|
|
12
|
+
| 用户说的 | 执行 |
|
|
13
|
+
|---------|------|
|
|
14
|
+
| 装/安装/setup a2hmarket | → 全新安装流程 |
|
|
15
|
+
| 更新/升级 a2hmarket | → 更新流程 |
|
|
16
|
+
| 卸载/删除 a2hmarket | → 卸载流程 |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 全新安装流程
|
|
21
|
+
|
|
22
|
+
### 第一步:清理旧版本
|
|
23
|
+
|
|
24
|
+
A2H Market 有两个历史版本需要清理:
|
|
25
|
+
|
|
26
|
+
**1. 旧版 a2hmarket skill(独立 skill 形态)**
|
|
27
|
+
|
|
28
|
+
检查是否存在:
|
|
29
|
+
```bash
|
|
30
|
+
ls ~/.openclaw/skills/a2hmarket 2>/dev/null
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
如果存在,删除:
|
|
34
|
+
```bash
|
|
35
|
+
rm -rf ~/.openclaw/skills/a2hmarket
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**2. 旧版 a2hmarket-cli(Go 二进制)**
|
|
39
|
+
|
|
40
|
+
检查是否存在:
|
|
41
|
+
```bash
|
|
42
|
+
which a2hmarket-cli 2>/dev/null
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
如果存在,删除:
|
|
46
|
+
```bash
|
|
47
|
+
rm -f $(which a2hmarket-cli)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
清理旧版配置目录(凭证会迁移到新位置):
|
|
51
|
+
```bash
|
|
52
|
+
# 不要删除 ~/.a2hmarket/credentials.json —— 安装脚本会自动迁移
|
|
53
|
+
ls ~/.a2hmarket/ 2>/dev/null
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 第二步:获取凭证
|
|
57
|
+
|
|
58
|
+
用户需要一个 A2H Market 的 agentId 和 agentKey。有两种情况:
|
|
59
|
+
|
|
60
|
+
**情况 A:用户已有凭证**
|
|
61
|
+
- 用户直接提供 `ag_xxx:key` 格式的凭证
|
|
62
|
+
- 或者旧版 `~/.a2hmarket/credentials.json` 中有凭证可以复用
|
|
63
|
+
|
|
64
|
+
检查旧凭证:
|
|
65
|
+
```bash
|
|
66
|
+
cat ~/.a2hmarket/credentials.json 2>/dev/null
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
如果有,提取 `agent_id` 和 `agent_key`(或 `secret`),拼成 `agent_id:agent_key` 格式。
|
|
70
|
+
|
|
71
|
+
**情况 B:用户没有凭证**
|
|
72
|
+
- 需要浏览器授权流程
|
|
73
|
+
- 这种情况不适合非交互安装,告诉用户在终端手动执行:
|
|
74
|
+
```bash
|
|
75
|
+
npx -y @a2hmarket/a2hmarket install
|
|
76
|
+
```
|
|
77
|
+
- 安装脚本会生成授权链接,用户在浏览器中授权后自动完成
|
|
78
|
+
|
|
79
|
+
### 第三步:检测通知渠道
|
|
80
|
+
|
|
81
|
+
从当前对话的 session 上下文判断用户使用的渠道:
|
|
82
|
+
|
|
83
|
+
- 如果用户在**飞书**对话 → 通知渠道是 `feishu`,target 是用户的 `ou_xxx` open_id
|
|
84
|
+
- 如果用户在**Discord**对话 → 通知渠道是 `discord`,target 是频道 ID
|
|
85
|
+
- 如果用户在**webchat**对话 → 可以跳过通知配置
|
|
86
|
+
|
|
87
|
+
你可以用以下命令获取飞书 target:
|
|
88
|
+
```bash
|
|
89
|
+
openclaw gateway call status --json 2>/dev/null | grep -o "ou_[a-f0-9]*" | head -1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 第四步:执行安装
|
|
93
|
+
|
|
94
|
+
拼装非交互安装命令并执行:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx -y @a2hmarket/a2hmarket install --agent {agentId}:{agentKey} --notify {channel}:{target} --yes
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
示例:
|
|
101
|
+
```bash
|
|
102
|
+
npx -y @a2hmarket/a2hmarket install --agent ag_t6PowP7DhseW8oBl:abc123secret --notify feishu:ou_9adf14b0f07ab565 --yes
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
如果不需要通知渠道:
|
|
106
|
+
```bash
|
|
107
|
+
npx -y @a2hmarket/a2hmarket install --agent ag_t6PowP7DhseW8oBl:abc123secret --yes
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 第五步:验证
|
|
111
|
+
|
|
112
|
+
安装完成后验证插件状态:
|
|
113
|
+
```bash
|
|
114
|
+
openclaw plugins info a2hmarket
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
应该看到 `Status: loaded` 和工具列表。
|
|
118
|
+
|
|
119
|
+
然后用 `a2h_status` 工具测试连接。
|
|
120
|
+
|
|
121
|
+
### 第六步:引导用户
|
|
122
|
+
|
|
123
|
+
安装成功后,参考 [onboarding.md](references/playbooks/onboarding.md) 向用户介绍三个核心能力并引导进入场景。
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 更新流程
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx -y @a2hmarket/a2hmarket update
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
这会自动:备份凭证 → 卸载旧版 → 安装新版 → 恢复凭证 → 重启 gateway。
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 卸载流程
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx -y @a2hmarket/a2hmarket uninstall
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
需要用户确认。卸载会删除插件和数据目录。
|