aws-runtime-bridge 1.3.0 → 1.3.2

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.
Files changed (73) hide show
  1. package/README.md +75 -77
  2. package/dist/config.d.ts +1 -7
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +2 -18
  5. package/dist/index.js +0 -0
  6. package/dist/middleware/auth.d.ts +2 -2
  7. package/dist/middleware/auth.d.ts.map +1 -1
  8. package/dist/middleware/auth.js +4 -10
  9. package/dist/routes/instance.d.ts.map +1 -1
  10. package/dist/routes/instance.js +8 -2
  11. package/dist/routes/properties.test.js +4 -4
  12. package/dist/routes/runtime-binding.d.ts.map +1 -1
  13. package/dist/routes/runtime-binding.js +3 -11
  14. package/dist/services/auto-register.d.ts.map +1 -1
  15. package/dist/services/auto-register.js +15 -8
  16. package/dist/services/auto-register.test.js +4 -0
  17. package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
  18. package/dist/services/aws-client-agent-mcp.js +1 -6
  19. package/dist/services/aws-client-agent-mcp.test.js +0 -2
  20. package/dist/services/orphan-monitor.d.ts.map +1 -1
  21. package/dist/services/orphan-monitor.js +8 -2
  22. package/dist/services/runtime-binding.d.ts +1 -0
  23. package/dist/services/runtime-binding.d.ts.map +1 -1
  24. package/dist/services/runtime-binding.js +31 -3
  25. package/dist/services/runtime-binding.test.js +42 -2
  26. package/dist/services/session-output.d.ts.map +1 -1
  27. package/dist/services/session-output.js +21 -4
  28. package/dist/utils/file-utils.d.ts.map +1 -1
  29. package/dist/utils/file-utils.js +7 -2
  30. package/dist/utils/file-utils.test.js +3 -1
  31. package/dist/utils/yaml-utils.test.js +129 -129
  32. package/node_modules/@cc-switch/sdk/README.md +540 -540
  33. package/node_modules/@cc-switch/sdk/package.json +31 -31
  34. package/package/aws-client-agent-mcp/README.md +287 -288
  35. package/package/aws-client-agent-mcp/dist/agent-client.test.js +24 -1
  36. package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
  37. package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -1
  38. package/package/aws-client-agent-mcp/dist/config.js +0 -1
  39. package/package/aws-client-agent-mcp/dist/config.js.map +1 -1
  40. package/package/aws-client-agent-mcp/dist/config.test.js +0 -7
  41. package/package/aws-client-agent-mcp/dist/config.test.js.map +1 -1
  42. package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -1
  43. package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
  44. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
  45. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +0 -3
  46. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
  47. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +14 -5
  48. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +1 -1
  49. package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
  50. package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
  51. package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
  52. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +3 -0
  53. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
  54. package/package/aws-client-agent-mcp/dist/websocket-client.js +64 -5
  55. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
  56. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +218 -0
  57. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
  58. package/package/aws-client-agent-mcp/package.json +52 -52
  59. package/package/cc-switch-sdk/README.md +540 -540
  60. package/package/cc-switch-sdk/package.json +31 -31
  61. package/package.json +78 -78
  62. package/dist/routes/runtime-mcp-proxy.d.ts +0 -3
  63. package/dist/routes/runtime-mcp-proxy.d.ts.map +0 -1
  64. package/dist/routes/runtime-mcp-proxy.js +0 -102
  65. package/dist/routes/runtime-mcp-proxy.test.d.ts +0 -2
  66. package/dist/routes/runtime-mcp-proxy.test.d.ts.map +0 -1
  67. package/dist/routes/runtime-mcp-proxy.test.js +0 -111
  68. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts +0 -2
  69. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.d.ts.map +0 -1
  70. package/node_modules/@cc-switch/sdk/dist/sdk-import.test.js +0 -119
  71. package/package/cc-switch-sdk/dist/sdk-import.test.d.ts +0 -2
  72. package/package/cc-switch-sdk/dist/sdk-import.test.d.ts.map +0 -1
  73. package/package/cc-switch-sdk/dist/sdk-import.test.js +0 -119
@@ -99,15 +99,26 @@ function normalizeServerIp(serverBaseUrl) {
99
99
  .toLowerCase();
100
100
  }
101
101
  }
102
+ function normalizeRuntimeTokenServerUrl(serverBaseUrl) {
103
+ const normalized = normalizeSchedulerBaseUrl(serverBaseUrl);
104
+ if (normalized) {
105
+ return normalized;
106
+ }
107
+ const raw = String(serverBaseUrl || "").trim();
108
+ if (!raw) {
109
+ return "";
110
+ }
111
+ return raw.replace(/\/+$/, "").toLowerCase();
112
+ }
102
113
  export function buildRuntimeTokenKey(userId, serverBaseUrl) {
103
114
  const normalizedUserId = String(userId || "").trim();
104
- const serverIp = normalizeServerIp(serverBaseUrl);
105
- if (!normalizedUserId || !serverIp) {
115
+ const serverUrl = normalizeRuntimeTokenServerUrl(serverBaseUrl);
116
+ if (!normalizedUserId || !serverUrl) {
106
117
  throw new Error("userId and serverBaseUrl are required to build runtime token key");
107
118
  }
108
119
  return crypto
109
120
  .createHash("md5")
110
- .update(`${normalizedUserId}:${serverIp}`, "utf8")
121
+ .update(`${normalizedUserId}:${serverUrl}`, "utf8")
111
122
  .digest("hex");
112
123
  }
113
124
  function loadRuntimeTokenStore() {
@@ -168,6 +179,23 @@ export function getScopedRuntimeAccessToken(userId, serverBaseUrl) {
168
179
  return undefined;
169
180
  }
170
181
  }
182
+ export function clearScopedRuntimeAccessToken(userId, serverBaseUrl) {
183
+ if (!userId || !serverBaseUrl) {
184
+ return false;
185
+ }
186
+ try {
187
+ const key = buildRuntimeTokenKey(userId, serverBaseUrl);
188
+ const tokens = loadRuntimeTokenStore();
189
+ const removed = tokens.delete(key);
190
+ if (removed) {
191
+ saveRuntimeTokenStore(tokens);
192
+ }
193
+ return removed;
194
+ }
195
+ catch {
196
+ return false;
197
+ }
198
+ }
171
199
  function validateRuntimeTokenStore(token) {
172
200
  if (!token) {
173
201
  return false;
@@ -1,5 +1,25 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { normalizeSchedulerBaseUrl } from './runtime-binding.js';
1
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { buildRuntimeTokenKey, clearScopedRuntimeAccessToken, getScopedRuntimeAccessToken, normalizeSchedulerBaseUrl, saveScopedRuntimeAccessToken, } from "./runtime-binding.js";
6
+ const originalEnv = { ...process.env };
7
+ const tempRoots = [];
8
+ function useRuntimeHome() {
9
+ const runtimeHome = mkdtempSync(path.join(os.tmpdir(), "aws-runtime-binding-"));
10
+ tempRoots.push(runtimeHome);
11
+ mkdirSync(runtimeHome, { recursive: true });
12
+ process.env.AWS_RUNTIME_HOME_DIR = runtimeHome;
13
+ process.env.HOME = runtimeHome;
14
+ process.env.USERPROFILE = runtimeHome;
15
+ return runtimeHome;
16
+ }
17
+ afterEach(() => {
18
+ process.env = { ...originalEnv };
19
+ for (const root of tempRoots.splice(0)) {
20
+ rmSync(root, { recursive: true, force: true });
21
+ }
22
+ });
3
23
  describe('runtime binding scheduler URL normalization', () => {
4
24
  it('repairs duplicated scheduler ports', () => {
5
25
  expect(normalizeSchedulerBaseUrl('http://127.0.0.1:8080:8080')).toBe('http://127.0.0.1:8080');
@@ -8,4 +28,24 @@ describe('runtime binding scheduler URL normalization', () => {
8
28
  expect(normalizeSchedulerBaseUrl('ws://127.0.0.1:8080/ws/agent')).toBe('http://127.0.0.1:8080');
9
29
  expect(normalizeSchedulerBaseUrl('wss://example.com/ws/agent')).toBe('https://example.com');
10
30
  });
31
+ it("scopes runtime tokens by user and full scheduler origin", () => {
32
+ useRuntimeHome();
33
+ const key7380 = saveScopedRuntimeAccessToken({
34
+ userId: "user-a",
35
+ serverBaseUrl: "http://scheduler.local:7380",
36
+ accessToken: "runtime-token-7380-123456",
37
+ });
38
+ const key8080 = saveScopedRuntimeAccessToken({
39
+ userId: "user-a",
40
+ serverBaseUrl: "http://scheduler.local:8080",
41
+ accessToken: "runtime-token-8080-123456",
42
+ });
43
+ expect(key7380).not.toBe(key8080);
44
+ expect(buildRuntimeTokenKey("user-a", "http://scheduler.local:7380/api")).toBe(key7380);
45
+ expect(getScopedRuntimeAccessToken("user-a", "http://scheduler.local:7380")).toBe("runtime-token-7380-123456");
46
+ expect(getScopedRuntimeAccessToken("user-a", "http://scheduler.local:8080")).toBe("runtime-token-8080-123456");
47
+ expect(clearScopedRuntimeAccessToken("user-a", "http://scheduler.local:7380")).toBe(true);
48
+ expect(getScopedRuntimeAccessToken("user-a", "http://scheduler.local:7380")).toBeUndefined();
49
+ expect(getScopedRuntimeAccessToken("user-a", "http://scheduler.local:8080")).toBe("runtime-token-8080-123456");
50
+ });
11
51
  });
@@ -1 +1 @@
1
- {"version":3,"file":"session-output.d.ts","sourceRoot":"","sources":["../../src/services/session-output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,aAAa;AACb,eAAO,MAAM,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAa,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,KAAK,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAqBnC;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCzE"}
1
+ {"version":3,"file":"session-output.d.ts","sourceRoot":"","sources":["../../src/services/session-output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAO3C,aAAa;AACb,eAAO,MAAM,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAa,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,KAAK,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAyBnC;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa3D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCzE"}
@@ -4,7 +4,12 @@
4
4
  * 管理终端会话的输出缓冲和刷新机制
5
5
  */
6
6
  import axios from 'axios';
7
- import { schedulerBaseUrl, runtimeToken } from '../config.js';
7
+ import { schedulerBaseUrl } from '../config.js';
8
+ import { getRuntimeAccessToken } from './runtime-binding.js';
9
+ function runtimeAuthHeaders() {
10
+ const token = getRuntimeAccessToken(undefined, schedulerBaseUrl) || getRuntimeAccessToken();
11
+ return token ? { 'X-Runtime-Token': token } : undefined;
12
+ }
8
13
  /** 活跃会话存储 */
9
14
  export const sessions = new Map();
10
15
  /**
@@ -31,7 +36,11 @@ export async function sendStatus(agentId, sessionId, status, actionInfo, usage)
31
36
  payload.inputTokens = usage.inputTokens;
32
37
  payload.outputTokens = usage.outputTokens;
33
38
  }
34
- return axios.post(`${schedulerBaseUrl}/api/runtime/callback/status`, payload, { headers: { 'X-Runtime-Token': runtimeToken } }).catch(() => undefined);
39
+ const headers = runtimeAuthHeaders();
40
+ if (!headers) {
41
+ return undefined;
42
+ }
43
+ return axios.post(`${schedulerBaseUrl}/api/runtime/callback/status`, payload, { headers }).catch(() => undefined);
35
44
  }
36
45
  /**
37
46
  * 发送输出到调度器
@@ -43,7 +52,11 @@ export async function sendStatus(agentId, sessionId, status, actionInfo, usage)
43
52
  */
44
53
  export async function sendOutput(agentId, output, sessionId, seq) {
45
54
  try {
46
- await axios.post(`${schedulerBaseUrl}/api/runtime/callback/output`, { agentId, output, sessionId, seq }, { headers: { 'X-Runtime-Token': runtimeToken } });
55
+ const headers = runtimeAuthHeaders();
56
+ if (!headers) {
57
+ return;
58
+ }
59
+ await axios.post(`${schedulerBaseUrl}/api/runtime/callback/output`, { agentId, output, sessionId, seq }, { headers });
47
60
  }
48
61
  catch {
49
62
  // ignore transient callback failures
@@ -58,7 +71,11 @@ export async function sendOutput(agentId, output, sessionId, seq) {
58
71
  */
59
72
  export async function sendQuestionRequest(agentId, sessionId, questions) {
60
73
  try {
61
- await axios.post(`${schedulerBaseUrl}/api/runtime/callback/question`, { agentId, sessionId, questions }, { headers: { 'X-Runtime-Token': runtimeToken } });
74
+ const headers = runtimeAuthHeaders();
75
+ if (!headers) {
76
+ return;
77
+ }
78
+ await axios.post(`${schedulerBaseUrl}/api/runtime/callback/question`, { agentId, sessionId, questions }, { headers });
62
79
  }
63
80
  catch {
64
81
  // ignore transient callback failures
@@ -1 +1 @@
1
- {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI5E,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBjG;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxF;AAED,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0ChC;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhG;AAED,wBAAsB,oCAAoC,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASlG;AAED,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,QAAQ,CAW1E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAM1D;AAED,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhF;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAyBhC"}
1
+ {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI5E,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBjG;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxF;AAED,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA+ChC;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhG;AAED,wBAAsB,oCAAoC,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASlG;AAED,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,QAAQ,CAW1E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAM1D;AAED,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhF;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAyBhC"}
@@ -3,7 +3,8 @@ import { spawn } from 'node:child_process';
3
3
  import { promises as fs } from 'node:fs';
4
4
  import { createHash, randomUUID } from 'node:crypto';
5
5
  import axios from 'axios';
6
- import { schedulerBaseUrl, runtimeToken } from '../config.js';
6
+ import { schedulerBaseUrl } from '../config.js';
7
+ import { getRuntimeAccessToken } from '../services/runtime-binding.js';
7
8
  const fileLocks = new Map();
8
9
  export async function ensureDir(targetDir) {
9
10
  await fs.mkdir(targetDir, { recursive: true });
@@ -56,10 +57,14 @@ export async function downloadSkillZip(skillPackage, targetDir) {
56
57
  ? String(skillPackage.fileName).trim()
57
58
  : 'skill-package.zip';
58
59
  const outputPath = path.join(targetDir, fileName);
60
+ const runtimeAccessToken = getRuntimeAccessToken(undefined, schedulerBaseUrl) || getRuntimeAccessToken();
61
+ if (!runtimeAccessToken) {
62
+ throw new Error('runtime access token is required to download skill package');
63
+ }
59
64
  const response = await axios.get(absoluteUrl, {
60
65
  responseType: 'arraybuffer',
61
66
  headers: {
62
- 'X-Runtime-Token': runtimeToken,
67
+ 'X-Runtime-Token': runtimeAccessToken,
63
68
  },
64
69
  });
65
70
  const bytes = Buffer.from(response.data);
@@ -13,7 +13,9 @@ vi.mock('axios', () => ({
13
13
  // Mock config
14
14
  vi.mock('../config.js', () => ({
15
15
  schedulerBaseUrl: 'http://localhost:8080',
16
- runtimeToken: 'test-token',
16
+ }));
17
+ vi.mock('../services/runtime-binding.js', () => ({
18
+ getRuntimeAccessToken: vi.fn(() => 'test-token'),
17
19
  }));
18
20
  describe('file-utils', () => {
19
21
  describe('ensureDir', () => {
@@ -12,95 +12,95 @@ describe('yaml-utils', () => {
12
12
  expect(hasMatchingAgentPath('', 'test')).toBeNull();
13
13
  });
14
14
  it('should return null when no agent key exists', () => {
15
- const yaml = `
16
- other:
17
- - name: test
15
+ const yaml = `
16
+ other:
17
+ - name: test
18
18
  `;
19
19
  expect(hasMatchingAgentPath(yaml, 'test')).toBeNull();
20
20
  });
21
21
  it('should return null when agent is not an array', () => {
22
- const yaml = `
23
- agent:
24
- name: single
22
+ const yaml = `
23
+ agent:
24
+ name: single
25
25
  `;
26
26
  expect(hasMatchingAgentPath(yaml, 'test')).toBeNull();
27
27
  });
28
28
  it('should return null when agent has no path', () => {
29
- const yaml = `
30
- agent:
31
- - name: test
32
- description: no path
29
+ const yaml = `
30
+ agent:
31
+ - name: test
32
+ description: no path
33
33
  `;
34
34
  expect(hasMatchingAgentPath(yaml, 'test')).toBeNull();
35
35
  });
36
36
  it('should match exact path', () => {
37
- const yaml = `
38
- agent:
39
- - name: test
40
- path: src/modules
37
+ const yaml = `
38
+ agent:
39
+ - name: test
40
+ path: src/modules
41
41
  `;
42
42
  expect(hasMatchingAgentPath(yaml, 'src/modules')).toBe('src/modules');
43
43
  });
44
44
  it('should match path with different separators (backslash)', () => {
45
- const yaml = `
46
- agent:
47
- - name: test
48
- path: src/modules
45
+ const yaml = `
46
+ agent:
47
+ - name: test
48
+ path: src/modules
49
49
  `;
50
50
  expect(hasMatchingAgentPath(yaml, 'src\\modules')).toBe('src/modules');
51
51
  });
52
52
  it('should match path with forward slash when yaml has backslash', () => {
53
- const yaml = `
54
- agent:
55
- - name: test
56
- path: src\\modules
53
+ const yaml = `
54
+ agent:
55
+ - name: test
56
+ path: src\\modules
57
57
  `;
58
58
  expect(hasMatchingAgentPath(yaml, 'src/modules')).toBe('src\\modules');
59
59
  });
60
60
  it('should match current directory "." path', () => {
61
- const yaml = `
62
- agent:
63
- - name: test
64
- path: .
61
+ const yaml = `
62
+ agent:
63
+ - name: test
64
+ path: .
65
65
  `;
66
66
  expect(hasMatchingAgentPath(yaml, '.')).toBe('.');
67
67
  });
68
68
  it('should match empty path when target is "."', () => {
69
- const yaml = `
70
- agent:
71
- - name: test
72
- path: ''
69
+ const yaml = `
70
+ agent:
71
+ - name: test
72
+ path: ''
73
73
  `;
74
74
  expect(hasMatchingAgentPath(yaml, '.')).toBe('');
75
75
  });
76
76
  it('should return null when no matching path found', () => {
77
- const yaml = `
78
- agent:
79
- - name: test
80
- path: other/path
77
+ const yaml = `
78
+ agent:
79
+ - name: test
80
+ path: other/path
81
81
  `;
82
82
  expect(hasMatchingAgentPath(yaml, 'different/path')).toBeNull();
83
83
  });
84
84
  it('should return first matching path from multiple agents', () => {
85
- const yaml = `
86
- agent:
87
- - name: first
88
- path: first/path
89
- - name: second
90
- path: second/path
85
+ const yaml = `
86
+ agent:
87
+ - name: first
88
+ path: first/path
89
+ - name: second
90
+ path: second/path
91
91
  `;
92
92
  expect(hasMatchingAgentPath(yaml, 'first/path')).toBe('first/path');
93
93
  expect(hasMatchingAgentPath(yaml, 'second/path')).toBe('second/path');
94
94
  });
95
95
  it('should handle complex YAML with nested objects', () => {
96
- const yaml = `
97
- agent:
98
- - name: complex
99
- path: complex/path
100
- project: my-project
101
- prompt: |
102
- This is a multi-line
103
- prompt
96
+ const yaml = `
97
+ agent:
98
+ - name: complex
99
+ path: complex/path
100
+ project: my-project
101
+ prompt: |
102
+ This is a multi-line
103
+ prompt
104
104
  `;
105
105
  expect(hasMatchingAgentPath(yaml, 'complex/path')).toBe('complex/path');
106
106
  });
@@ -113,12 +113,12 @@ agent:
113
113
  expect(result.isTemplate).toBe(false);
114
114
  });
115
115
  it('should return matching agent config', () => {
116
- const yaml = `
117
- agent:
118
- - name: my-agent
119
- path: src/modules
120
- project: my-project
121
- prompt: test prompt
116
+ const yaml = `
117
+ agent:
118
+ - name: my-agent
119
+ path: src/modules
120
+ project: my-project
121
+ prompt: test prompt
122
122
  `;
123
123
  const result = extractAgentConfigOrTemplate(yaml, 'src/modules');
124
124
  expect(result.isTemplate).toBe(false);
@@ -127,11 +127,11 @@ agent:
127
127
  expect(result.content).toContain('my-project');
128
128
  });
129
129
  it('should generate template when no matching agent found', () => {
130
- const yaml = `
131
- agent:
132
- - name: other-agent
133
- path: other/path
134
- project: existing-project
130
+ const yaml = `
131
+ agent:
132
+ - name: other-agent
133
+ path: other/path
134
+ project: existing-project
135
135
  `;
136
136
  const result = extractAgentConfigOrTemplate(yaml, 'new/path');
137
137
  expect(result.isTemplate).toBe(true);
@@ -139,10 +139,10 @@ agent:
139
139
  expect(result.content).toContain('new/path');
140
140
  });
141
141
  it('should generate template with empty project when no agents have project', () => {
142
- const yaml = `
143
- agent:
144
- - name: agent1
145
- path: path1
142
+ const yaml = `
143
+ agent:
144
+ - name: agent1
145
+ path: path1
146
146
  `;
147
147
  const result = extractAgentConfigOrTemplate(yaml, 'new/path');
148
148
  expect(result.isTemplate).toBe(true);
@@ -150,21 +150,21 @@ agent:
150
150
  expect(result.content).toContain('project: ""');
151
151
  });
152
152
  it('should match current directory "." path', () => {
153
- const yaml = `
154
- agent:
155
- - name: root-agent
156
- path: .
157
- project: root-project
153
+ const yaml = `
154
+ agent:
155
+ - name: root-agent
156
+ path: .
157
+ project: root-project
158
158
  `;
159
159
  const result = extractAgentConfigOrTemplate(yaml, '.');
160
160
  expect(result.isTemplate).toBe(false);
161
161
  expect(result.content).toContain('root-agent');
162
162
  });
163
163
  it('should normalize path separators in output', () => {
164
- const yaml = `
165
- agent:
166
- - name: test
167
- path: src\\modules
164
+ const yaml = `
165
+ agent:
166
+ - name: test
167
+ path: src\\modules
168
168
  `;
169
169
  const result = extractAgentConfigOrTemplate(yaml, 'src/modules');
170
170
  expect(result.isTemplate).toBe(false);
@@ -192,78 +192,78 @@ agent:
192
192
  expect(autoFillPathInYml(invalidYaml, 'test/path')).toBe(invalidYaml);
193
193
  });
194
194
  it('should return original content when no agent key', () => {
195
- const yaml = `
196
- other:
197
- - name: test
195
+ const yaml = `
196
+ other:
197
+ - name: test
198
198
  `;
199
199
  expect(autoFillPathInYml(yaml, 'test/path')).toBe(yaml);
200
200
  });
201
201
  it('should fill empty path', () => {
202
- const yaml = `
203
- agent:
204
- - name: test
205
- path: ''
206
- project: my-project
202
+ const yaml = `
203
+ agent:
204
+ - name: test
205
+ path: ''
206
+ project: my-project
207
207
  `;
208
208
  const result = autoFillPathInYml(yaml, 'new/path');
209
209
  expect(result).toContain('new/path');
210
210
  expect(result).not.toContain("path: ''");
211
211
  });
212
212
  it('should fill undefined path', () => {
213
- const yaml = `
214
- agent:
215
- - name: test
216
- project: my-project
213
+ const yaml = `
214
+ agent:
215
+ - name: test
216
+ project: my-project
217
217
  `;
218
218
  const result = autoFillPathInYml(yaml, 'new/path');
219
219
  expect(result).toContain('new/path');
220
220
  });
221
221
  it('should fill null path', () => {
222
- const yaml = `
223
- agent:
224
- - name: test
225
- path: null
222
+ const yaml = `
223
+ agent:
224
+ - name: test
225
+ path: null
226
226
  `;
227
227
  const result = autoFillPathInYml(yaml, 'new/path');
228
228
  expect(result).toContain('new/path');
229
229
  });
230
230
  it('should fill whitespace-only path', () => {
231
- const yaml = `
232
- agent:
233
- - name: test
234
- path: ' '
231
+ const yaml = `
232
+ agent:
233
+ - name: test
234
+ path: ' '
235
235
  `;
236
236
  const result = autoFillPathInYml(yaml, 'new/path');
237
237
  expect(result).toContain('new/path');
238
238
  });
239
239
  it('should not modify existing non-empty path', () => {
240
- const yaml = `
241
- agent:
242
- - name: test
243
- path: existing/path
240
+ const yaml = `
241
+ agent:
242
+ - name: test
243
+ path: existing/path
244
244
  `;
245
245
  const result = autoFillPathInYml(yaml, 'new/path');
246
246
  expect(result).toContain('existing/path');
247
247
  expect(result).not.toContain('new/path');
248
248
  });
249
249
  it('should handle multiple agents', () => {
250
- const yaml = `
251
- agent:
252
- - name: agent1
253
- path: ''
254
- - name: agent2
255
- path: existing/path
256
- - name: agent3
250
+ const yaml = `
251
+ agent:
252
+ - name: agent1
253
+ path: ''
254
+ - name: agent2
255
+ path: existing/path
256
+ - name: agent3
257
257
  `;
258
258
  const result = autoFillPathInYml(yaml, 'new/path');
259
259
  // agent1 should have path filled
260
260
  expect(result).toContain('new/path');
261
261
  });
262
262
  it('should return original content when no modification needed', () => {
263
- const yaml = `
264
- agent:
265
- - name: test
266
- path: existing/path
263
+ const yaml = `
264
+ agent:
265
+ - name: test
266
+ path: existing/path
267
267
  `;
268
268
  const result = autoFillPathInYml(yaml, 'new/path');
269
269
  expect(result).toBe(yaml);
@@ -296,11 +296,11 @@ agent:
296
296
  it('should load config from workspace directory with matching path "."', async () => {
297
297
  const configPath = path.join(workspacePath, relativePath);
298
298
  await fs.mkdir(path.dirname(configPath), { recursive: true });
299
- await fs.writeFile(configPath, `
300
- agent:
301
- - name: local-agent
302
- path: .
303
- project: local-project
299
+ await fs.writeFile(configPath, `
300
+ agent:
301
+ - name: local-agent
302
+ path: .
303
+ project: local-project
304
304
  `, 'utf-8');
305
305
  const result = await loadYmlWithUpwardSearch(workspacePath, relativePath);
306
306
  expect(result.content).not.toBeNull();
@@ -312,11 +312,11 @@ agent:
312
312
  // 在 project/module 目录创建配置,匹配 src 路径
313
313
  const parentConfigPath = path.join(tempRoot, 'project', 'module', relativePath);
314
314
  await fs.mkdir(path.dirname(parentConfigPath), { recursive: true });
315
- await fs.writeFile(parentConfigPath, `
316
- agent:
317
- - name: module-agent
318
- path: src
319
- project: module-project
315
+ await fs.writeFile(parentConfigPath, `
316
+ agent:
317
+ - name: module-agent
318
+ path: src
319
+ project: module-project
320
320
  `, 'utf-8');
321
321
  const result = await loadYmlWithUpwardSearch(workspacePath, relativePath);
322
322
  expect(result.content).not.toBeNull();
@@ -328,10 +328,10 @@ agent:
328
328
  // 在 project/module 目录创建配置,但不匹配 src 路径
329
329
  const parentConfigPath = path.join(tempRoot, 'project', 'module', relativePath);
330
330
  await fs.mkdir(path.dirname(parentConfigPath), { recursive: true });
331
- await fs.writeFile(parentConfigPath, `
332
- agent:
333
- - name: other-agent
334
- path: other/path
331
+ await fs.writeFile(parentConfigPath, `
332
+ agent:
333
+ - name: other-agent
334
+ path: other/path
335
335
  `, 'utf-8');
336
336
  const result = await loadYmlWithUpwardSearch(workspacePath, relativePath);
337
337
  expect(result.content).not.toBeNull();
@@ -342,17 +342,17 @@ agent:
342
342
  // 在 workspace 目录创建匹配的配置
343
343
  const localConfigPath = path.join(workspacePath, relativePath);
344
344
  await fs.mkdir(path.dirname(localConfigPath), { recursive: true });
345
- await fs.writeFile(localConfigPath, `
346
- agent:
347
- - name: local-agent
348
- path: .
345
+ await fs.writeFile(localConfigPath, `
346
+ agent:
347
+ - name: local-agent
348
+ path: .
349
349
  `, 'utf-8');
350
350
  // 在 parent 目录也创建配置
351
351
  const parentConfigPath = path.join(tempRoot, 'project', 'module', relativePath);
352
- await fs.writeFile(parentConfigPath, `
353
- agent:
354
- - name: parent-agent
355
- path: src
352
+ await fs.writeFile(parentConfigPath, `
353
+ agent:
354
+ - name: parent-agent
355
+ path: src
356
356
  `, 'utf-8');
357
357
  const result = await loadYmlWithUpwardSearch(workspacePath, relativePath);
358
358
  // 应该使用 local 的配置,因为更近且匹配