huaweicloud-devkit 1.1.4-next.2 → 1.1.4-next.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
3
  "mcpName": "io.github.huaweicloud/huaweicloud-devkit",
4
- "version": "1.1.4-next.2",
4
+ "version": "1.1.4-next.3",
5
5
  "kooCliVersion": "7.2.12",
6
6
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
7
7
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.4-next.2",
20
+ "version": "1.1.4-next.3",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -20,7 +20,7 @@
20
20
  "mcpServers": "./.mcp.json",
21
21
  "description": "Agent toolkit that helps coding agents use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities safely and accurately.",
22
22
  "skills": "./skills/",
23
- "version": "1.1.4-next.2",
23
+ "version": "1.1.4-next.3",
24
24
  "author": {
25
25
  "name": "HuaweiCloud Mate",
26
26
  "url": "https://github.com/huaweicloud"
@@ -17,7 +17,7 @@
17
17
  "mcpServers": "./.mcp.json",
18
18
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
19
19
  "skills": "./skills/",
20
- "version": "1.1.4-next.2",
20
+ "version": "1.1.4-next.3",
21
21
  "author": {
22
22
  "name": "HuaweiCloud Mate",
23
23
  "url": "https://github.com/huaweicloud"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.4-next.2",
3
+ "version": "1.1.4-next.3",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huaweicloud-devkit",
3
- "version": "1.1.4-next.2",
3
+ "version": "1.1.4-next.3",
4
4
  "description": "Guide coding agents to use Huawei Cloud Skills, KooCLI, APIs, SDKs, and future MCP capabilities with safer execution and less context.",
5
5
  "author": {
6
6
  "name": "HuaweiCloud Mate",
@@ -2,7 +2,7 @@
2
2
  "name": "huaweicloud-devkit",
3
3
  "id": "huaweicloud-devkit",
4
4
  "displayName": "HuaweiCloud DevKit",
5
- "version": "1.1.4-next.2",
5
+ "version": "1.1.4-next.3",
6
6
  "family": "bundle-plugin",
7
7
  "bundleFormat": "codex",
8
8
  "description": "Guide coding agents to use Huawei Cloud safely — KooCLI, APIs, SDKs, 28 MCP tools, skills, and safety guardrails.",
@@ -22,21 +22,29 @@ for (const base of [pluginRoot, packageRoot]) {
22
22
  }
23
23
 
24
24
  // 会话内首个非 check/upgrade 工具调用附加 _updateInfo,只消费一次。
25
- let hintConsumed = false;
26
- function decorateResult(name, result) {
27
- if (hintConsumed) return result;
25
+ // 按会话隔离:同进程内不同会话(A/B)各自首次提示;stdio 用固定 'stdin'。
26
+ const consumedBySession = new Map();
27
+ export function _decorateResult(sessionId, name, result) {
28
+ if (consumedBySession.get(sessionId)) return result;
28
29
  try {
29
30
  const hint = peekCachedUpdateInfo();
30
31
  if (!hint) return result;
31
32
  const decorated = applyUpdateHint(result, name, hint);
32
- if (decorated !== result) hintConsumed = true;
33
+ if (decorated !== result) consumedBySession.set(sessionId, true);
33
34
  return decorated;
34
35
  } catch {
35
36
  return result; // 兜底装饰失败绝不影响工具调用
36
37
  }
37
38
  }
39
+ export function _resetHintConsumption() {
40
+ consumedBySession.clear();
41
+ }
42
+ export function _isHintConsumed(sessionId) {
43
+ return Boolean(consumedBySession.get(sessionId));
44
+ }
38
45
 
39
- export async function dispatch(method, params) {
46
+ export async function dispatch(method, params, opts = {}) {
47
+ const sessionId = opts?.sessionId || 'default';
40
48
  if (method === 'initialize') {
41
49
  const ci = params.clientInfo || {};
42
50
 
@@ -68,7 +76,7 @@ export async function dispatch(method, params) {
68
76
 
69
77
  if (method === 'tools/call') {
70
78
  const result = await callTool(params.name, params.arguments || {});
71
- const decorated = decorateResult(params.name, result);
79
+ const decorated = _decorateResult(sessionId, params.name, result);
72
80
  return {
73
81
  content: [
74
82
  {
@@ -50,7 +50,8 @@ export async function startRemoteServer({ port = DEFAULT_PORT, host = DEFAULT_HO
50
50
 
51
51
  let response;
52
52
  try {
53
- const result = await dispatch(message.method, message.params || {});
53
+ const sessionId = (req.headers['mcp-session-id'] || '').trim() || 'default';
54
+ const result = await dispatch(message.method, message.params || {}, { sessionId });
54
55
  response = { jsonrpc: '2.0', id: message.id, result };
55
56
  if (message.method === 'initialize') {
56
57
  res.setHeader('MCP-Protocol-Version', result.protocolVersion || '2024-11-05');
@@ -159,7 +159,7 @@ function runStdioServer() {
159
159
  return;
160
160
  }
161
161
  try {
162
- const result = await dispatch(message.method, message.params || {});
162
+ const result = await dispatch(message.method, message.params || {}, { sessionId: 'stdin' });
163
163
  writeMessage({ jsonrpc: '2.0', id: message.id, result });
164
164
  } catch (error) {
165
165
  writeMessage({
@@ -1049,7 +1049,7 @@ function normalizeNumericArgs(args) {
1049
1049
  return out;
1050
1050
  }
1051
1051
 
1052
- export async function callTool(name, rawArgs = {}) {
1052
+ export async function callTool(name, rawArgs = {}, opts = {}) {
1053
1053
  const args = normalizeNumericArgs(rawArgs);
1054
1054
  const toolValue = toolInvokeValue(name, args);
1055
1055
  trackToolInvoke(name, toolValue);
@@ -1448,33 +1448,35 @@ export async function callTool(name, rawArgs = {}) {
1448
1448
  case 'huaweicloud_voucher_claim':
1449
1449
  return await hdkitVoucherClaim(args.domain_id);
1450
1450
  case 'huaweicloud_check_update':
1451
- return await handleCheckUpdate(args);
1451
+ return await handleCheckUpdate(args, { sessionId: opts?.sessionId });
1452
1452
  case 'huaweicloud_upgrade':
1453
- return await handleUpgrade(args);
1453
+ return await handleUpgrade(args, { sessionId: opts?.sessionId });
1454
1454
  default:
1455
1455
  throw new Error(`Unknown tool: ${name}`);
1456
1456
  }
1457
1457
  }
1458
1458
 
1459
- async function handleCheckUpdate(args = {}) {
1459
+ async function handleCheckUpdate(args = {}, opts = {}) {
1460
+ const sessionId = opts?.sessionId || null;
1460
1461
  const current = readInstalledVersion() || '0.0.0';
1461
1462
  if (args.dismiss === true) {
1462
- const distTags = await getUpdateDistTags(current);
1463
+ const distTags = await getUpdateDistTags(current, { sessionId });
1463
1464
  const target = determineTarget(current, distTags);
1464
1465
  const dismissedVersion =
1465
1466
  typeof args.dismissVersion === 'string' && args.dismissVersion ? args.dismissVersion : target || current;
1466
- const state = writeSkipState(resolveSkipFilePath(), dismissedVersion);
1467
+ const state = writeSkipState(resolveSkipFilePath(sessionId), dismissedVersion);
1467
1468
  invalidateUpdateCache();
1468
1469
  return judgeUpdate(current, distTags, state);
1469
1470
  }
1470
- return getCachedUpdateInfo(current);
1471
+ return getCachedUpdateInfo(current, { sessionId });
1471
1472
  }
1472
1473
 
1473
- async function handleUpgrade(args = {}) {
1474
+ async function handleUpgrade(args = {}, opts = {}) {
1475
+ const sessionId = opts?.sessionId || null;
1474
1476
  const target = typeof args.target === 'string' && args.target ? args.target : 'all';
1475
1477
  const version = typeof args.version === 'string' && args.version ? args.version : 'latest';
1476
1478
  const current = readInstalledVersion() || '0.0.0';
1477
- const info = await getCachedUpdateInfo(current);
1479
+ const info = await getCachedUpdateInfo(current, { sessionId });
1478
1480
  if (info && info.result === 'up_to_date') {
1479
1481
  return {
1480
1482
  success: false,
@@ -152,12 +152,26 @@ export function fallbackSkipFilePath() {
152
152
  return join(base, '.config', 'huaweicloud', 'devkit-skip.json');
153
153
  }
154
154
 
155
+ // 会话化 skip 文件:remote 多会话按 sessionId 拆分,stdio/默认保持原文件(向后兼容)。
156
+ function sanitizeSessionId(sessionId) {
157
+ return String(sessionId || '').replace(/[^0-9a-zA-Z-]/g, '_');
158
+ }
159
+
155
160
  // A1 定稿: 标准 agent 用插件目录副本(有 package.json); codex 等无副本时回退共享文件
156
- export function resolveSkipFilePath() {
161
+ export function resolveSkipFilePath(sessionId = null) {
162
+ let base;
157
163
  try {
158
- if (existsSync(join(dirname(skipFilePath()), 'package.json'))) return skipFilePath();
159
- } catch {}
160
- return fallbackSkipFilePath();
164
+ if (existsSync(join(dirname(skipFilePath()), 'package.json'))) {
165
+ base = skipFilePath();
166
+ } else {
167
+ base = fallbackSkipFilePath();
168
+ }
169
+ } catch {
170
+ base = fallbackSkipFilePath();
171
+ }
172
+ // stdio / 默认 / 无 session → 原文件(保持向后兼容)
173
+ if (!sessionId || sessionId === 'default' || sessionId === 'stdin') return base;
174
+ return `${base}.${sanitizeSessionId(sessionId)}`;
161
175
  }
162
176
 
163
177
  export function readSkipState(file) {
@@ -293,12 +307,15 @@ function cacheValid(now = Date.now()) {
293
307
  return Boolean(cachedDistTags) && now - cachedAt <= TTL_MS;
294
308
  }
295
309
 
296
- export async function getCachedUpdateInfo(current, { doQuery = queryDistTags, now = Date.now() } = {}) {
310
+ export async function getCachedUpdateInfo(
311
+ current,
312
+ { doQuery = queryDistTags, now = Date.now(), sessionId = null } = {},
313
+ ) {
297
314
  if (process.env.HUAWEICLOUD_DEVKIT_SKIP_UPDATE === '1') {
298
315
  lastHint = judgeUpdate(current, null, undefined, now);
299
316
  return lastHint;
300
317
  }
301
- const skipState = readSkipState(resolveSkipFilePath());
318
+ const skipState = readSkipState(resolveSkipFilePath(sessionId));
302
319
  if (!cacheValid(now)) {
303
320
  if (!cachedDistTags && now - failedAt < FAIL_THROTTLE_MS) {
304
321
  lastHint = judgeUpdate(current, null, skipState, now);
@@ -331,8 +348,8 @@ export function peekCachedUpdateInfo() {
331
348
  return lastHint && lastHint.updateAvailable && lastHint.targetVersion ? lastHint : null;
332
349
  }
333
350
 
334
- export async function getUpdateDistTags(current) {
335
- const result = await getCachedUpdateInfo(current);
351
+ export async function getUpdateDistTags(current, { sessionId = null } = {}) {
352
+ const result = await getCachedUpdateInfo(current, { sessionId });
336
353
  if (!result) return null;
337
354
  return { latest: result.latestStable ?? null, next: result.latestNext ?? null };
338
355
  }