@a2hmarket/a2hmarket 1.3.2 → 1.3.4

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/index.ts CHANGED
@@ -46,7 +46,7 @@ export default {
46
46
  description:
47
47
  "A2H Market — AI agent marketplace with self-managed A2A messaging and human notification.",
48
48
 
49
- async register(api: OpenClawPluginApi) {
49
+ register(api: OpenClawPluginApi) {
50
50
  // ── Init runtime & credentials ───────────────────────────────
51
51
  setA2HRuntime(api.runtime);
52
52
  initCredentials(api.pluginConfig as Record<string, unknown> | undefined);
@@ -103,8 +103,9 @@ export default {
103
103
  } else {
104
104
  tools.alsoAllow = [PLUGIN_ID];
105
105
  }
106
- await api.runtime.config.writeConfigFile(cfg as any);
107
- api.logger.info(`a2hmarket: added plugin ID to tools allowlist for profile "${profile}"`);
106
+ api.runtime.config.writeConfigFile(cfg as any).then(() => {
107
+ api.logger.info(`a2hmarket: added plugin ID to tools allowlist for profile "${profile}"`);
108
+ }).catch(() => {});
108
109
  }
109
110
  }
110
111
  } catch {
@@ -202,40 +203,6 @@ export default {
202
203
  return { cancel: true };
203
204
  });
204
205
 
205
- // ── Welcome message on first boot after install ────────────────
206
- api.on("gateway_start", async () => {
207
- try {
208
- const { readPendingWelcome, deletePendingWelcome, sendWelcome } =
209
- await import("./src/pending-welcome.js");
210
- const pending = readPendingWelcome();
211
- if (!pending) return;
212
-
213
- const cfg = api.runtime.config.loadConfig() as Record<string, unknown>;
214
- const channels = (cfg.channels ?? {}) as Record<string, Record<string, unknown>>;
215
- const channelCfg = channels[pending.channel];
216
- if (!channelCfg) {
217
- api.logger.warn(
218
- `a2hmarket: welcome skipped, no config for channel "${pending.channel}"`,
219
- );
220
- deletePendingWelcome();
221
- return;
222
- }
223
-
224
- const sent = await sendWelcome(pending, channelCfg, {
225
- info: (m) => api.logger.info(`a2hmarket: ${m}`),
226
- warn: (m) => api.logger.warn(`a2hmarket: ${m}`),
227
- });
228
- deletePendingWelcome();
229
- if (sent) {
230
- api.logger.info("a2hmarket: welcome message sent");
231
- }
232
- } catch (err) {
233
- api.logger.warn(
234
- `a2hmarket: welcome failed: ${err instanceof Error ? err.message : String(err)}`,
235
- );
236
- }
237
- });
238
-
239
206
  // ── Register agent service ───────────────────────────────────
240
207
  let serviceAbort: AbortController | null = null;
241
208
  api.registerService({
@@ -265,6 +232,30 @@ export default {
265
232
  warn: (m: string) => ctx.logger.warn(`[a2hmarket] ${m}`),
266
233
  };
267
234
 
235
+ // ── Welcome message on first boot after install ──────────
236
+ try {
237
+ const { readPendingWelcome, deletePendingWelcome, sendWelcome } =
238
+ await import("./src/pending-welcome.js");
239
+ const pending = readPendingWelcome();
240
+ if (pending) {
241
+ const cfg = ctx.config as Record<string, unknown>;
242
+ const channels = (cfg.channels ?? {}) as Record<string, Record<string, unknown>>;
243
+ const channelCfg = channels[pending.channel];
244
+ if (channelCfg) {
245
+ const sent = await sendWelcome(pending, channelCfg, {
246
+ info: (m) => serviceLog.info(m),
247
+ warn: (m) => serviceLog.warn(m),
248
+ });
249
+ if (sent) serviceLog.info("welcome message sent");
250
+ } else {
251
+ serviceLog.warn(`welcome skipped, no config for channel "${pending.channel}"`);
252
+ }
253
+ deletePendingWelcome();
254
+ }
255
+ } catch (err) {
256
+ serviceLog.warn(`welcome failed: ${err instanceof Error ? err.message : String(err)}`);
257
+ }
258
+
268
259
  serviceAbort = new AbortController();
269
260
  try {
270
261
  await startAgentService({
@@ -2,7 +2,7 @@
2
2
  "id": "a2hmarket",
3
3
  "name": "A2H Market",
4
4
  "description": "A2H Market \u2014 AI agent marketplace with self-managed A2A messaging via MQTT.",
5
- "version": "1.3.2",
5
+ "version": "1.3.4",
6
6
  "hosts": [
7
7
  "openclaw"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2hmarket/a2hmarket",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "index.ts",
@@ -49,32 +49,93 @@ export function deletePendingWelcome(): void {
49
49
 
50
50
  const WELCOME_TITLE = "🎉 A2H Market 插件已就绪";
51
51
 
52
- const WELCOME_FEISHU_ELEMENTS = [
53
- {
54
- tag: "markdown" as const,
55
- content:
56
- "我是你的 **A2H Market** AI 助手,可以帮你:\n" +
57
- "📦 发布商品、浏览市场\n" +
58
- "💬 自动与其他 Agent 谈判协商\n" +
59
- "📋 管理订单、处理支付",
60
- },
61
- {
62
- tag: "markdown" as const,
63
- content: "---\n🔐 首次使用请发送 **\"登录 A2H Market\"** 完成授权",
64
- },
65
- ];
66
-
67
- const WELCOME_TEXT = [
68
- `**${WELCOME_TITLE}**`,
69
- "",
70
- "我是你的 A2H Market AI 助手,可以帮你:",
71
- "📦 发布商品、浏览市场",
72
- "💬 自动与其他 Agent 谈判协商",
73
- "📋 管理订单、处理支付",
74
- "",
75
- "---",
76
- '🔐 首次使用请发送 "登录 A2H Market" 完成授权',
77
- ].join("\n");
52
+ const AUTH_API_URL = "https://web.a2hmarket.ai";
53
+ const LOGIN_URL = "https://a2hmarket.ai";
54
+
55
+ async function generateAuthUrl(): Promise<string | null> {
56
+ try {
57
+ const { networkInterfaces } = await import("node:os");
58
+ const { createHash, randomBytes } = await import("node:crypto");
59
+
60
+ let mac = "00:00:00:00:00:00";
61
+ const ifaces = networkInterfaces();
62
+ for (const name of Object.keys(ifaces)) {
63
+ for (const iface of ifaces[name] ?? []) {
64
+ if (!iface.internal && iface.mac && iface.mac !== "00:00:00:00:00:00") {
65
+ mac = iface.mac;
66
+ break;
67
+ }
68
+ }
69
+ if (mac !== "00:00:00:00:00:00") break;
70
+ }
71
+
72
+ const timestamp = String(Math.floor(Date.now() / 1000));
73
+ try {
74
+ const formData = new URLSearchParams({ timestamp, mac });
75
+ const resp = await fetch(`${AUTH_API_URL}/v1/auth/init-login`, {
76
+ method: "POST",
77
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
78
+ body: formData.toString(),
79
+ });
80
+ const data = (await resp.json()) as { code?: string; url?: string };
81
+ if (data.url) return data.url;
82
+ } catch {
83
+ // fallback to local generation
84
+ }
85
+
86
+ const randomHex = randomBytes(32).toString("hex");
87
+ const macClean = mac.replace(/:/g, "");
88
+ const raw = `${randomHex}_${timestamp}_${macClean}`;
89
+ const code = createHash("md5").update(raw).digest("hex");
90
+ return `${LOGIN_URL}/authcode?code=${code}`;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ function buildFeishuElements(authUrl: string | null) {
97
+ const elements = [
98
+ {
99
+ tag: "markdown" as const,
100
+ content:
101
+ "我是你的 **A2H Market** AI 助手,可以帮你:\n" +
102
+ "📦 发布商品、浏览市场\n" +
103
+ "💬 自动与其他 Agent 谈判协商\n" +
104
+ "📋 管理订单、处理支付",
105
+ },
106
+ ];
107
+ if (authUrl) {
108
+ elements.push({
109
+ tag: "markdown" as const,
110
+ content: `---\n🔐 **[点击这里完成授权登录](${authUrl})**`,
111
+ });
112
+ } else {
113
+ elements.push({
114
+ tag: "markdown" as const,
115
+ content: '---\n🔐 首次使用请发送 **"登录 A2H Market"** 完成授权',
116
+ });
117
+ }
118
+ return elements;
119
+ }
120
+
121
+ function buildWelcomeText(authUrl: string | null): string {
122
+ const lines = [
123
+ `**${WELCOME_TITLE}**`,
124
+ "",
125
+ "我是你的 A2H Market AI 助手,可以帮你:",
126
+ "📦 发布商品、浏览市场",
127
+ "💬 自动与其他 Agent 谈判协商",
128
+ "📋 管理订单、处理支付",
129
+ "",
130
+ "---",
131
+ ];
132
+ if (authUrl) {
133
+ lines.push(`🔐 点击完成授权登录: ${authUrl}`);
134
+ } else {
135
+ lines.push('🔐 首次使用请发送 "登录 A2H Market" 完成授权');
136
+ }
137
+ return lines.join("\n");
138
+ }
78
139
 
79
140
  // ── Send Welcome ───────────────────────────────────────────────────────
80
141
 
@@ -92,6 +153,12 @@ export async function sendWelcome(
92
153
  channelCfg: Record<string, unknown>,
93
154
  log: WelcomeLog,
94
155
  ): Promise<boolean> {
156
+ // Generate auth URL (best-effort, falls back to text prompt)
157
+ const authUrl = await generateAuthUrl();
158
+ if (authUrl) {
159
+ log.info(`welcome: auth url generated`);
160
+ }
161
+
95
162
  if (pending.channel === "feishu") {
96
163
  if (!channelCfg.appId || !channelCfg.appSecret) {
97
164
  log.warn("welcome: feishu channel credentials not configured, skipped");
@@ -104,7 +171,7 @@ export async function sendWelcome(
104
171
  target: pending.target,
105
172
  title: WELCOME_TITLE,
106
173
  titleColor: "green",
107
- elements: WELCOME_FEISHU_ELEMENTS,
174
+ elements: buildFeishuElements(authUrl),
108
175
  });
109
176
  return true;
110
177
  }
@@ -137,7 +204,7 @@ export async function sendWelcome(
137
204
  const resp = await fetch(`https://discord.com/api/v10/channels/${channelId}/messages`, {
138
205
  method: "POST",
139
206
  headers,
140
- body: JSON.stringify({ content: WELCOME_TEXT }),
207
+ body: JSON.stringify({ content: buildWelcomeText(authUrl) }),
141
208
  });
142
209
  if (!resp.ok) {
143
210
  const data = (await resp.json()) as { message?: string };